Tasuke Hubのロゴ

ITを中心に困っている人を助けるメディア

分かりやすく解決策を提供することで、あなたの困ったをサポート。 全ての人々がスムーズに生活できる世界を目指します。

MCPを使ったAI連携の実装例!初心者でもわかるModel Context Protocolの基本と活用法

記事のサムネイル
TH

Tasuke Hub管理人

東証プライム市場上場企業エンジニア

情報系修士卒業後、大手IT企業にてフルスタックエンジニアとして活躍。 Webアプリケーション開発からクラウドインフラ構築まで幅広い技術に精通し、 複数のプロジェクトでリードエンジニアを担当。 技術ブログやオープンソースへの貢献を通じて、日本のIT技術コミュニティに積極的に関わっている。

🎓情報系修士🏢東証プライム上場企業💻フルスタックエンジニア📝技術ブログ執筆者

MCPとは?AI連携のための新しい標準プロトコル

生成AIの進化とともに、AIモデルを既存のアプリケーションやサービスと連携させるニーズが急速に高まっています。しかし、各AIプロバイダーがそれぞれ独自の連携方法を提供していると、開発者は複数のインターフェースを学び、実装する必要があります。「二度同じコードを書くな」というプログラミングの格言がありますが、この問題を解決するために登場したのがMCP(Model Context Protocol)です。

MCPは、Anthropicが2024年初頭に公開したオープンな標準プロトコルで、LLM(大規模言語モデル)アプリケーションと外部データソースやツールを統合するための共通言語を提供します。これはまるで、AIアプリケーションのためのUSB-Cポートのようなものです。USBが様々な周辺機器を標準化されたやり方で接続できるように、MCPはAIモデルを様々なデータソースやツールに標準化された方法で接続することを可能にします。

MCPが解決する問題

従来のAI連携では、M個のAIアプリケーション(チャット、RAG、カスタムエージェントなど)とN個のツール/システム(GitHub、Slack、データベースなど)を連携させようとすると、M×N個の異なる統合を構築する必要がありました。これは開発チーム間で重複した作業を引き起こし、一貫性のない実装につながっていました。

MCPはこの問題を「M+N問題」に変換します:

  • ツール作成者はN個のMCPサーバー(各システムに1つ)を構築
  • アプリケーション開発者はM個のMCPクライアント(各AIアプリケーションに1つ)を構築

これにより、開発効率が大幅に向上し、様々なAIアプリケーションとツール間の相互運用性が確保されます。「複雑さは隠し、単純さを表に出す」という設計思想が実現されているのです。

MCPの主な特徴

MCPの主な特徴には以下のようなものがあります:

  • 事前構築された統合リスト: LLMが直接プラグインできる統合が増加中
  • LLMプロバイダー間の切り替え柔軟性: 異なるAIモデルやベンダー間の切り替えが容易
  • データセキュリティ: インフラストラクチャ内でデータを安全に保持
  • 標準化されたインターフェース: 一貫したAPIでツールとデータソースにアクセス

MCPは急速に普及しており、OpenAIを含む主要なAIプロバイダーがサポートを表明しています。これにより、開発者は単一のプロトコルを学ぶだけで、様々なAIモデルと外部システム間の統合を実現できるようになりました。

おすすめの書籍

MCP実装の基本構造:サーバー、クライアント、ホスト

MCPの基本構造を理解することは、実装を始める前に不可欠です。MCPは本質的にクライアント-サーバーアーキテクチャに基づいており、ホストアプリケーションが複数のサーバーに接続できる形態をとっています。レストランでの食事の例えで言えば、ホストはレストラン全体、クライアントはウェイター、サーバーはキッチンのようなものです。

基本コンポーネント

MCPの基本構造は以下の3つの主要コンポーネントから成ります:

  1. MCPホスト: LLMを搭載したアプリケーション(ClaudeデスクトップアプリやIDE、カスタムエージェントなど)で、タスクを完了するためにMCPサーバーが提供するデータやツールを使用します。

  2. MCPクライアント: ホスト内に存在し、特定のMCPサーバーとの接続を管理するプロトコルクライアント。各クライアントは1つのサーバーと1:1の接続を維持します。

  3. MCPサーバー: 標準化されたModel Context Protocolを通じて特定の機能を公開する軽量プログラム。これらはローカルデータソース(ファイル、データベース)や外部サービス(API)とやり取りします。

+---------------+       +---------------+       +---------------+
|    MCPホスト   |       |  MCPクライアント |       |   MCPサーバー  |
|  (Claudeなど)  | <---> |  (接続管理)   | <---> | (ツール・データ) |
+---------------+       +---------------+       +---------------+

サーバーの機能

MCPサーバーは3種類の主要な機能を提供できます:

  1. リソース (Resources): クライアントが読み取れるファイルのようなデータ(APIレスポンスやファイル内容など)

  2. ツール (Tools): LLMが呼び出せる関数(ユーザー承認あり)

  3. プロンプト (Prompts): ユーザーが特定のタスクを達成するのに役立つ事前作成されたテンプレート

通信プロトコル

MCPサーバーとクライアント間の通信には、主に2つの方法があります:

  1. stdio (標準入出力): クライアントとサーバーが同じマシン上で実行されている場合に使用されます。これはローカル統合(ローカルファイルへのアクセスやローカルスクリプトの実行など)に効果的です。
// stdioを使用したサーバー接続の例(TypeScript)
const transport = new StdioServerTransport();
await server.connect(transport);
  1. HTTP/SSE (Server-Sent Events): クライアントはHTTPでサーバーに接続し、初期設定後、サーバーはSSE標準を使用して持続的な接続を通じてクライアントにメッセージ(イベント)をプッシュできます。
// Streamable HTTP transportを使用したサーバー例(TypeScript)
const app = express();
app.use(express.json());
const transport = new StreamableHTTPServerTransport();
app.all('/mcp', async (req, res) => {
  // Streamable HTTP transportの処理
});

MCPの魅力は、そのシンプルさとモジュール性にあります。「一つのことをうまくやる」というUNIXの哲学に従って、各コンポーネントが特定の責任を持ち、全体として効率的に機能するシステムを形成します。この分離は、開発、テスト、スケーリングを容易にします。

おすすめの書籍

PythonでMCPサーバーを実装する方法

Pythonは、その読みやすさと豊富なライブラリのエコシステムから、MCPサーバーの実装に最適な言語の一つです。ここでは、PythonのMCP SDKを使用して、シンプルなMCPサーバーを構築する方法を見ていきましょう。「学ぶための最良の方法は実践である」という言葉の通り、実際のコード例で理解を深めていきます。

環境のセットアップ

まず、必要なパッケージをインストールします。MCPの公式ドキュメントでは、Pythonパッケージ管理にuvを使用することを推奨していますが、ここでは一般的なpipも使えます。

# uvを使用する場合
curl -LsSf https://astral.sh/uv/install.sh | sh  # Mac/Linux
# または
pip install mcp fastmcp

基本的なMCPサーバーの作成

FastMCPを使用すると、短いコードでMCPサーバーを作成できます。以下は、シンプルなMCPサーバーの実装例です:

from fastmcp import FastMCP

# MCPサーバーを作成
mcp = FastMCP("Weather API")

# リソースを追加(情報提供のために使用)
@mcp.resource(mime_type="text/plain")
def get_weather_api_docs():
    return """
    Weather API Documentation
    ========================
    This API provides current weather information for cities worldwide.
    """

# ツールを追加(アクションを実行するために使用)
@mcp.tool
def get_current_weather(city: str):
    """
    現在の天気情報を取得します
    
    Args:
        city: 天気情報を取得したい都市名
    
    Returns:
        指定された都市の現在の天気情報
    """
    # 実際のアプリケーションでは、ここで外部API呼び出しやデータベースクエリを行います
    # この例ではモックデータを返します
    weather_data = {
        "tokyo": {"temp": 22, "condition": "晴れ", "humidity": 65},
        "osaka": {"temp": 24, "condition": "曇り", "humidity": 70},
        "kyoto": {"temp": 23, "condition": "小雨", "humidity": 75},
    }
    
    city = city.lower()
    if city in weather_data:
        return weather_data[city]
    else:
        return {"error": f"{city}の天気情報は利用できません"}

# 実行
if __name__ == "__main__":
    mcp.run()

このシンプルなサーバーは、天気APIのドキュメント(リソース)と、特定の都市の現在の天気を取得するツールを提供します。

リソースの実装

MCPサーバーのリソースは、データをLLMに提供するために使用されます。例えば、ファイルの内容やAPIから取得したデータなどです。

# 様々な種類のリソースを追加する例
@mcp.resource(mime_type="text/plain", name="readme")
def get_readme():
    with open("README.md", "r") as f:
        return f.read()

@mcp.resource(mime_type="application/json")
def get_user_data():
    return {
        "users": [
            {"id": 1, "name": "山田太郎", "role": "管理者"},
            {"id": 2, "name": "鈴木花子", "role": "ユーザー"}
        ]
    }

# 動的パスを持つリソース
@mcp.resource(mime_type="text/plain", path_template="files/{filename}")
def get_file(filename: str):
    try:
        with open(f"files/{filename}", "r") as f:
            return f.read()
    except FileNotFoundError:
        return f"ファイル '{filename}' が見つかりません"

ツールの実装

ツールは、LLMがアクションを実行するために呼び出せる関数です。入力パラメータと出力を明確に定義することが重要です。

@mcp.tool
def search_database(query: str, limit: int = 10):
    """
    データベースを検索します
    
    Args:
        query: 検索クエリ
        limit: 返す結果の最大数(デフォルト: 10)
    
    Returns:
        検索結果のリスト
    """
    # 実際のアプリケーションでは、ここでデータベースクエリを実行します
    results = [f"Result {i} for query: {query}" for i in range(limit)]
    return {"results": results, "count": len(results)}

@mcp.tool
def create_new_user(name: str, email: str, role: str = "user"):
    """
    新しいユーザーを作成します
    
    Args:
        name: ユーザーの名前
        email: ユーザーのメールアドレス
        role: ユーザーの役割(デフォルト: user)
    
    Returns:
        作成されたユーザーの情報
    """
    # 実際のアプリケーションでは、ここでユーザー作成処理を行います
    user_id = 12345  # 通常はデータベースから生成されます
    return {
        "success": True,
        "user": {
            "id": user_id,
            "name": name,
            "email": email,
            "role": role
        }
    }

プロンプトの実装

プロンプトは、LLMが特定のタスクを実行するための定型文を提供します。

@mcp.prompt(name="weather-report")
def weather_report_prompt(city: str):
    """
    天気レポートを生成するためのプロンプト
    
    Args:
        city: 天気情報を取得したい都市名
    """
    return {
        "messages": [
            {"role": "system", "content": "あなたは天気レポートを生成する専門家です。"},
            {"role": "user", "content": f"{city}の天気について詳細に教えてください。"}
        ]
    }

MCPサーバーのテスト

MCPサーバーを実装したら、MCP Inspectorを使用してテストできます。

# MCPサーバーをテストモードで実行
mcp dev weather_server.py

# 依存関係を追加してテスト
mcp dev weather_server.py --with requests --with pandas

認証の実装

より高度なMCPサーバーでは、認証機能を追加することも可能です。

from mcp.server.auth import AuthSettings, OAuthServerProvider, RevocationOptions

class MyOAuthProvider(OAuthServerProvider):
    # 認証プロバイダーの実装(詳細は省略)
    pass

# 認証機能付きのMCPサーバー
mcp = FastMCP(
    "認証付きAPI",
    auth_provider=MyOAuthProvider(),
    auth=AuthSettings(
        issuer_url="https://myapp.com",
        revocation_options=RevocationOptions(enabled=True),
        required_scopes=["read:data"]
    )
)

Python MCPサーバーは、そのシンプルさと強力な機能のおかげで、AIアプリケーションと外部システムを接続する理想的な方法となっています。デコレータを利用した直感的なAPIデザインは、Pythonの哲学「明示的であることは暗黙的であるより優れている」を体現しています。

おすすめの書籍

TypeScriptでMCPクライアントを作成する手順

TypeScriptは、型安全性と優れた開発体験から、MCPクライアントの実装に適した言語です。この章では、TypeScriptを使用してMCPクライアントを作成し、AIモデルからMCPサーバーにアクセスする方法を説明します。「シンプルな方が複雑なものより優れている」というプログラミングの原則に従い、段階的に実装していきましょう。

環境のセットアップ

まず、TypeScriptプロジェクトをセットアップして、必要な依存関係をインストールします。

# プロジェクトディレクトリを作成
mkdir mcp-client-typescript
cd mcp-client-typescript

# npmプロジェクトを初期化
npm init -y

# 依存関係をインストール
npm install @modelcontextprotocol/sdk @anthropic-ai/sdk dotenv

# 開発依存関係をインストール
npm install -D typescript ts-node @types/node

# TypeScriptの設定ファイルを作成
npx tsc --init

次に、tsconfig.jsonを以下のように設定します:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "NodeNext",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,
    "outDir": "./build"
  },
  "include": ["src/**/*"]
}

基本的なMCPクライアントの実装

次に、基本的なMCPクライアントを実装します。以下のようなディレクトリ構造を作成します:

mcp-client-typescript/
  ├── src/
  │    └── index.ts
  ├── package.json
  ├── tsconfig.json
  └── .env

.envファイルには、Anthropic APIキーを設定します:

ANTHROPIC_API_KEY=your_api_key_here

src/index.tsに、基本的なMCPクライアントを実装します:

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { Anthropic } from '@anthropic-ai/sdk';
import * as dotenv from 'dotenv';
import { spawn } from 'child_process';
import * as readline from 'readline';

// 環境変数を読み込む
dotenv.config();

class MCPClient {
  private mcp: Client;
  private transport: StdioClientTransport | null = null;
  private anthropic: Anthropic;
  private tools: any[] = [];

  constructor() {
    this.mcp = new Client();
    this.anthropic = new Anthropic({
      apiKey: process.env.ANTHROPIC_API_KEY,
    });
  }

  // MCPサーバーに接続
  async connectToServer(serverScriptPath: string) {
    try {
      const isPy = serverScriptPath.endsWith(".py");
      const isJs = serverScriptPath.endsWith(".js");

      if (!isPy && !isJs) {
        throw new Error("サーバースクリプトは.pyまたは.jsファイルである必要があります");
      }

      const command = isPy 
        ? process.platform === "win32" ? "python" : "python3" 
        : process.execPath;
      
      this.transport = new StdioClientTransport({
        command,
        args: [serverScriptPath],
      });

      this.mcp.connect(this.transport);

      // 利用可能なツールを取得
      const toolsResult = await this.mcp.listTools();
      this.tools = toolsResult.tools.map((tool) => {
        return {
          name: tool.name,
          description: tool.description,
          input_schema: tool.inputSchema,
        };
      });

      console.log(
        "サーバーに接続しました。利用可能なツール:", 
        this.tools.map(({ name }) => name)
      );
    } catch (e) {
      console.log("MCPサーバーへの接続に失敗しました: ", e);
      throw e;
    }
  }

  // ユーザーのクエリを処理
  async processQuery(query: string) {
    const messages = [
      {
        role: "user",
        content: [
          {
            type: "text",
            text: query
          }
        ]
      }
    ];

    const toolNames = this.tools.map(tool => tool.name);

    try {
      // Claudeに質問を送信
      const response = await this.anthropic.messages.create({
        model: "claude-3-opus-20240229",
        messages,
        tools: this.tools,
        max_tokens: 1000,
      });

      // ツール呼び出しがあれば処理
      if (response.content[0].type === "tool_use") {
        const toolUse = response.content[0].tool_use;
        console.log(`ツール "${toolUse.name}" を呼び出します...`);

        // MCPサーバーでツールを実行
        const toolResult = await this.mcp.callTool(
          toolUse.name,
          toolUse.input
        );

        // ツール実行結果をClaudeに送信して最終的な回答を得る
        const finalResponse = await this.anthropic.messages.create({
          model: "claude-3-opus-20240229",
          messages: [
            ...messages,
            {
              role: "assistant",
              content: [
                {
                  type: "tool_use",
                  tool_use: toolUse
                }
              ]
            },
            {
              role: "user",
              content: [
                {
                  type: "tool_result",
                  tool_result: {
                    tool_use_id: toolUse.id,
                    content: JSON.stringify(toolResult)
                  }
                }
              ]
            }
          ],
          max_tokens: 1000,
        });

        return finalResponse.content[0].text;
      }

      // 通常の応答の場合
      return response.content[0].text;
    } catch (e) {
      console.error("エラーが発生しました:", e);
      return "エラーが発生しました。もう一度お試しください。";
    }
  }

  // チャットループを実行
  async chatLoop() {
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout
    });

    console.log("MCPクライアントが起動しました。終了するには 'exit' と入力してください。");

    while (true) {
      const query = await new Promise<string>(resolve => {
        rl.question("> ", resolve);
      });

      if (query.toLowerCase() === 'exit') {
        break;
      }

      const response = await this.processQuery(query);
      console.log("\n" + response + "\n");
    }

    rl.close();
  }

  // クリーンアップ
  async cleanup() {
    if (this.transport) {
      await this.transport.close();
    }
  }
}

// メイン実行関数
async function main() {
  if (process.argv.length < 3) {
    console.log("使用法: node index.js <サーバースクリプトへのパス>");
    return;
  }

  const mcpClient = new MCPClient();
  
  try {
    await mcpClient.connectToServer(process.argv[2]);
    await mcpClient.chatLoop();
  } finally {
    await mcpClient.cleanup();
    process.exit(0);
  }
}

// スクリプトを実行
main();

このコードは、以下の機能を実装しています:

  1. MCPサーバーへの接続
  2. 利用可能なツールの取得
  3. ユーザークエリの処理
  4. ツール呼び出しの処理
  5. インタラクティブなチャットループ

ビルドと実行

実装したクライアントをビルドして実行するために、package.jsonに以下のスクリプトを追加します:

"scripts": {
  "build": "tsc",
  "start": "node build/index.js"
}

そして、以下のコマンドでビルドと実行を行います:

# ビルド
npm run build

# 実行(PythonのMCPサーバーに接続する例)
npm run start -- path/to/server.py

リソースへのアクセス

MCPサーバーが提供するリソースにアクセスするメソッドを追加しましょう:

// リソースの一覧を取得
async listResources() {
  try {
    const resourcesResult = await this.mcp.listResources();
    return resourcesResult.resources;
  } catch (e) {
    console.error("リソースの一覧取得中にエラーが発生しました:", e);
    throw e;
  }
}

// 特定のリソースを読み取る
async readResource(path: string) {
  try {
    const content = await this.mcp.readResource(path);
    return content;
  } catch (e) {
    console.error(`リソース '${path}' の読み取り中にエラーが発生しました:`, e);
    throw e;
  }
}

プロンプトの利用

MCPサーバーが提供するプロンプトを利用するメソッドも追加できます:

// 利用可能なプロンプトの一覧を取得
async listPrompts() {
  try {
    const promptsResult = await this.mcp.listPrompts();
    return promptsResult.prompts;
  } catch (e) {
    console.error("プロンプトの一覧取得中にエラーが発生しました:", e);
    throw e;
  }
}

// 特定のプロンプトを取得して使用
async usePrompt(name: string, args: Record<string, any>) {
  try {
    const prompt = await this.mcp.getPrompt(name, args);
    // プロンプトを使用してClaudeに質問
    const response = await this.anthropic.messages.create({
      model: "claude-3-opus-20240229",
      messages: prompt.messages,
      max_tokens: 1000,
    });
    return response.content[0].text;
  } catch (e) {
    console.error(`プロンプト '${name}' の使用中にエラーが発生しました:`, e);
    throw e;
  }
}

TypeScriptでMCPクライアントを実装することで、型安全性と優れたコード補完のメリットを享受しながら、AIモデルとMCPサーバー間の通信を効率的に管理できます。「型安全性は、言葉で説明するより示したほうが理解しやすい」という諺がありますが、この実装例はまさにTypeScriptの力を実感できるものです。

おすすめの書籍

実践例:MCPを使った効率的なAI連携アプリ開発

ここまでMCPの基本概念と実装方法を学んできましたが、実際のアプリケーション開発ではどのように活用できるのでしょうか。この章では、MCPを使った実践的なアプリケーション例を紹介します。「理論より実践」という言葉の通り、具体的な例を通じてMCPの有用性を理解しましょう。

GitHubとSlackを連携したPRレビューアシスタント

最初の例として、GitHubのプルリクエスト(PR)をレビューし、結果をSlackに通知するアシスタントを考えてみましょう。このアプリケーションはMCPサーバーを使用して、次のことを実現します:

  1. GitHubからPRの変更内容を取得
  2. AIモデルによるコードレビュー
  3. Slackへのレビュー結果の投稿

アーキテクチャ設計

このアプリケーションのアーキテクチャは以下のようになります:

+---------------+         +---------------+         +---------------+
|  Claude/GPT   |         |  MCPクライアント |         |  MCPサーバー  |
|  (ホスト)      | <-----> |  (接続管理)    | <-----> |  (GitHub/Slack)|
+---------------+         +---------------+         +---------------+

MCPサーバーの実装(Python)

from fastmcp import FastMCP
import requests
import json
import os
from slack_sdk import WebClient
from github import Github

# 環境変数から認証情報を取得
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN")
SLACK_TOKEN = os.environ.get("SLACK_TOKEN")

# MCPサーバーを初期化
mcp = FastMCP("PR Review Assistant")

# GitHub APIクライアント
github_client = Github(GITHUB_TOKEN)
# Slack APIクライアント
slack_client = WebClient(token=SLACK_TOKEN)

@mcp.tool
def get_pull_request(repo_name: str, pr_number: int):
    """
    GitHubからプルリクエストの詳細を取得します
    
    Args:
        repo_name: リポジトリ名(形式: owner/repo)
        pr_number: プルリクエスト番号
    
    Returns:
        プルリクエストの詳細情報(タイトル、説明、変更内容など)
    """
    try:
        repo = github_client.get_repo(repo_name)
        pr = repo.get_pull(pr_number)
        
        # 変更ファイルを取得
        files = []
        for file in pr.get_files():
            files.append({
                "filename": file.filename,
                "status": file.status,
                "additions": file.additions,
                "deletions": file.deletions,
                "patch": file.patch[:2000] if file.patch else None  # サイズ制限
            })
        
        return {
            "title": pr.title,
            "description": pr.body,
            "state": pr.state,
            "user": pr.user.login,
            "created_at": pr.created_at.isoformat(),
            "updated_at": pr.updated_at.isoformat(),
            "files": files,
            "comments_url": pr.comments_url
        }
    except Exception as e:
        return {"error": str(e)}

@mcp.tool
def post_to_slack(channel: str, message: str, blocks: list = None):
    """
    Slackにメッセージを投稿します
    
    Args:
        channel: 投稿先のSlackチャンネル名(例: "#general")
        message: 投稿するテキストメッセージ
        blocks: (オプション) Slackブロックキット形式のメッセージ
    
    Returns:
        投稿結果とタイムスタンプ
    """
    try:
        result = slack_client.chat_postMessage(
            channel=channel,
            text=message,
            blocks=json.dumps(blocks) if blocks else None
        )
        return {
            "success": True,
            "timestamp": result["ts"],
            "channel": result["channel"]
        }
    except Exception as e:
        return {"success": False, "error": str(e)}

@mcp.tool
def add_pr_comment(repo_name: str, pr_number: int, comment: str):
    """
    GitHubのプルリクエストにコメントを追加します
    
    Args:
        repo_name: リポジトリ名(形式: owner/repo)
        pr_number: プルリクエスト番号
        comment: 追加するコメント
    
    Returns:
        コメント追加の結果
    """
    try:
        repo = github_client.get_repo(repo_name)
        pr = repo.get_pull(pr_number)
        comment = pr.create_issue_comment(comment)
        
        return {
            "success": True,
            "comment_id": comment.id,
            "created_at": comment.created_at.isoformat()
        }
    except Exception as e:
        return {"success": False, "error": str(e)}

# プロンプトの実装
@mcp.prompt(name="code-review")
def code_review_prompt(pr_data: dict):
    """
    コードレビューのためのプロンプト
    
    Args:
        pr_data: プルリクエストのデータ
    """
    files_description = ""
    for file in pr_data.get("files", []):
        files_description += f"\nファイル: {file['filename']}\n"
        files_description += f"変更: +{file['additions']} -{file['deletions']}\n"
        if file.get("patch"):
            files_description += f"パッチ:\n```\n{file['patch']}\n```\n"
    
    return {
        "messages": [
            {
                "role": "system",
                "content": "あなたは経験豊富なシニアエンジニアで、コードレビューを担当しています。以下のプルリクエストを分析し、コードの品質、潜在的な問題、改善点を指摘してください。"
            },
            {
                "role": "user",
                "content": f"""
                プルリクエスト「{pr_data.get('title')}」のレビューをお願いします。

                説明:
                {pr_data.get('description')}
                
                変更ファイル:
                {files_description}
                
                以下の点を考慮してレビューしてください:
                1. コードの品質とベストプラクティス
                2. パフォーマンスの問題
                3. セキュリティの懸念
                4. テストカバレッジ
                5. ドキュメントの完全性
                
                レビュー結果は明確で具体的なフィードバックを含め、建設的な提案を行ってください。
                """
            }
        ]
    }

# 実行
if __name__ == "__main__":
    mcp.run()

ホストアプリケーションの実装(TypeScript)

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { Anthropic } from '@anthropic-ai/sdk';
import * as dotenv from 'dotenv';

dotenv.config();

class PRReviewAssistant {
  private mcp: Client;
  private transport: StdioClientTransport | null = null;
  private anthropic: Anthropic;

  constructor() {
    this.mcp = new Client();
    this.anthropic = new Anthropic({
      apiKey: process.env.ANTHROPIC_API_KEY,
    });
  }

  async connectToServer(serverScriptPath: string) {
    // サーバー接続のコード(前章と同様)
    // ...
  }

  async runReview(repoName: string, prNumber: number, slackChannel: string) {
    try {
      console.log(`レポジトリ ${repoName} のPR #${prNumber} のレビューを開始します...`);
      
      // GitHubからPR情報を取得
      const prData = await this.mcp.callTool('get_pull_request', {
        repo_name: repoName,
        pr_number: prNumber
      });
      
      console.log('PRデータを取得しました。AIによるレビューを実行します...');
      
      // プロンプトを取得
      const prompt = await this.mcp.getPrompt('code-review', { pr_data: prData });
      
      // AIでレビューを生成
      const response = await this.anthropic.messages.create({
        model: 'claude-3-opus-20240229',
        messages: prompt.messages,
        max_tokens: 2000,
      });
      
      const reviewContent = response.content[0].text;
      console.log('レビューが完了しました。Slackに投稿します...');
      
      // レビュー結果をSlackに投稿
      const slackResult = await this.mcp.callTool('post_to_slack', {
        channel: slackChannel,
        message: `PR #${prNumber} (${prData.title}) のレビュー結果:`,
        blocks: [
          {
            type: 'header',
            text: {
              type: 'plain_text',
              text: `PR #${prNumber} レビュー`
            }
          },
          {
            type: 'section',
            text: {
              type: 'mrkdwn',
              text: `*リポジトリ:* ${repoName}\n*タイトル:* ${prData.title}\n*作成者:* ${prData.user}`
            }
          },
          {
            type: 'divider'
          },
          {
            type: 'section',
            text: {
              type: 'mrkdwn',
              text: reviewContent
            }
          }
        ]
      });
      
      // GitHubにもコメントを追加
      await this.mcp.callTool('add_pr_comment', {
        repo_name: repoName,
        pr_number: prNumber,
        comment: reviewContent
      });
      
      console.log('レビュープロセスが完了しました!');
      return { success: true, review: reviewContent };
    } catch (error) {
      console.error('レビュープロセスでエラーが発生しました:', error);
      return { success: false, error };
    }
  }
}

// 使用例
async function main() {
  const assistant = new PRReviewAssistant();
  await assistant.connectToServer('./pr_review_server.py');
  await assistant.runReview('owner/repo', 123, '#dev-team');
}

main();

Notionと連携したAI会議アシスタント

2つ目の例として、会議の内容を要約し、Notionに保存するAI会議アシスタントを考えてみましょう。

MCPサーバーの実装(Python)

from fastmcp import FastMCP
import requests
import json
import os
from notion_client import Client as NotionClient

# 環境変数から認証情報を取得
NOTION_TOKEN = os.environ.get("NOTION_TOKEN")
NOTION_DATABASE_ID = os.environ.get("NOTION_DATABASE_ID")

# MCPサーバーを初期化
mcp = FastMCP("Meeting Assistant")

# Notion APIクライアント
notion = NotionClient(auth=NOTION_TOKEN)

@mcp.tool
def create_meeting_note(title: str, summary: str, action_items: list, participants: list):
    """
    Notionに会議ノートを作成します
    
    Args:
        title: 会議のタイトル
        summary: 会議の要約
        action_items: アクションアイテムのリスト
        participants: 参加者のリスト
    
    Returns:
        作成されたNotionページの情報
    """
    try:
        # アクションアイテムをリッチテキスト形式に変換
        action_items_blocks = []
        for item in action_items:
            action_items_blocks.append({
                "object": "block",
                "type": "to_do",
                "to_do": {
                    "rich_text": [{"type": "text", "text": {"content": item}}],
                    "checked": False
                }
            })
        
        # 参加者リストをリッチテキスト形式に変換
        participants_text = "\n".join([f"- {p}" for p in participants])
        
        # Notionページを作成
        page = notion.pages.create(
            parent={"database_id": NOTION_DATABASE_ID},
            properties={
                "タイトル": {"title": [{"text": {"content": title}}]},
                "日付": {"date": {"start": datetime.now().isoformat()}}
            },
            children=[
                {
                    "object": "block",
                    "type": "heading_2",
                    "heading_2": {
                        "rich_text": [{"type": "text", "text": {"content": "参加者"}}]
                    }
                },
                {
                    "object": "block",
                    "type": "paragraph",
                    "paragraph": {
                        "rich_text": [{"type": "text", "text": {"content": participants_text}}]
                    }
                },
                {
                    "object": "block",
                    "type": "heading_2",
                    "heading_2": {
                        "rich_text": [{"type": "text", "text": {"content": "要約"}}]
                    }
                },
                {
                    "object": "block",
                    "type": "paragraph",
                    "paragraph": {
                        "rich_text": [{"type": "text", "text": {"content": summary}}]
                    }
                },
                {
                    "object": "block",
                    "type": "heading_2",
                    "heading_2": {
                        "rich_text": [{"type": "text", "text": {"content": "アクションアイテム"}}]
                    }
                },
                *action_items_blocks
            ]
        )
        
        return {
            "success": True,
            "page_id": page["id"],
            "url": page["url"]
        }
    except Exception as e:
        return {"success": False, "error": str(e)}

@mcp.prompt(name="meeting-summary")
def meeting_summary_prompt(transcript: str):
    """
    会議の要約を生成するためのプロンプト
    
    Args:
        transcript: 会議の文字起こし
    """
    return {
        "messages": [
            {
                "role": "system",
                "content": "あなたは会議の内容を効率的に要約し、重要なポイントとアクションアイテムを抽出する専門家です。"
            },
            {
                "role": "user",
                "content": f"""
                以下の会議の文字起こしを分析し、次の形式で要約してください:

                1. 要約 (200単語以内)
                2. 主要な議論ポイント (箇条書き)
                3. 決定事項 (箇条書き)
                4. アクションアイテム (担当者と期限を含む箇条書き)
                5. フォローアップが必要な未解決の問題 (箇条書き)

                文字起こし:
                {transcript}
                """
            }
        ]
    }

# 実行
if __name__ == "__main__":
    mcp.run()

これらの実践例からわかるように、MCPを活用することで、異なるサービス間の連携がシームレスになります。「適切なツールを適切な仕事に使う」という原則に従って、AIと外部サービスをうまく組み合わせることで、より効率的なワークフローを実現できます。

おすすめの書籍

MCPの将来性と開発者が押さえるべきポイント

ここまでMCPの基本概念、実装方法、実践例を見てきましたが、最後にMCPの将来性と開発者が押さえるべきポイントについて考えてみましょう。「将来を予測する最良の方法は、それを作ることだ」というアラン・ケイの言葉の通り、MCPの将来は開発者コミュニティの取り組み次第で大きく変わる可能性があります。

MCPの成長と普及状況

MCPは2024年初頭にAnthropicによって公開されて以来、AI開発コミュニティで急速に採用されています。現在の状況を見ると、以下のような成長が見られます:

  1. 主要AIプロバイダーのサポート:OpenAIなど主要なAIプロバイダーがMCPのサポートを表明
  2. 豊富なSDK:Python、TypeScript、Java、Kotlin、C#、Swiftなど、様々な言語のSDKが提供されている
  3. コミュニティ主導の発展:数多くのMCPサーバーがGitHubなどで公開され、活発なコミュニティが形成されている
  4. 商用製品への統合:Cursor、WindsurfなどのツールがすでにMCPを統合

開発者が押さえるべき実装のポイント

MCPを実装する際に開発者が押さえるべきポイントは以下の通りです:

  1. 適切なトランスポートの選択

    • ローカルツールにはstdioトランスポート
    • リモートサービスにはStreamable HTTPトランスポート
    • 必要に応じてカスタムトランスポートの実装も検討
  2. セキュリティの考慮

    • 認証・認可の仕組みを導入
    • センシティブな情報へのアクセス制限
    • ツール呼び出し前のユーザー確認機能の実装
  3. エラーハンドリング

    • ツール呼び出しのタイムアウト処理
    • エラーメッセージのユーザーフレンドリーな表示
    • リトライ機構の実装
  4. ステートフル実行

    • 長時間実行されるタスクの状態管理
    • セッション管理の実装
    • 中断・再開機能の考慮
  5. ドキュメンテーション

    • ツールやリソースの明確な説明
    • 入出力パラメータの詳細なドキュメント化
    • サンプルコードやユースケースの提供

今後の発展の可能性

MCPは比較的新しいプロトコルですが、今後の発展には以下のような可能性があります:

  1. 標準化の拡大

    • より多くのAIプロバイダーがMCPを採用
    • AIツールおよびデータソースの標準インターフェースとして確立
    • ベストプラクティスの形成と普及
  2. ツールエコシステムの成長

    • プリビルトMCPサーバーのライブラリ拡大
    • 業界特化型MCPサーバーの出現
    • より洗練されたデバッグおよび開発ツールの登場
  3. AIエージェントとの統合

    • 自律型AIエージェントのための標準ツールアクセスプロトコルとしての位置づけ
    • マルチエージェントシステムでの協調作業の基盤
    • 複雑なワークフローを支援するMCPチェーン
  4. エンタープライズ採用の拡大

    • 企業内システムとの統合
    • コンプライアンスや監査に対応した拡張機能
    • セキュリティ強化のための仕組みの追加

課題と解決すべき問題

MCPの普及には、まだいくつかの課題があります:

  1. 標準認証メカニズム

    • MCPは現在、認証メカニズムを標準化していない
    • クライアントがサーバーと認証する方法やサーバーが第三者APIと認証する方法のフレームワークが必要
  2. ワークフローの標準化

    • 複数のツール呼び出しを順番に管理するための標準的な方法がない
    • 再開可能性や再試行可能性を各クライアントに委ねるのは理想的でない
  3. ツール選択の標準化

    • ツール発見や選択のための標準レイヤーがない
    • RAGツールの標準実装がない
    • 統一的なUI/UXパターンがない

これらの課題は、コミュニティと主要プレイヤーが協力することで解決に向かう可能性があります。

開発者へのアドバイス

MCPを使って開発する際のアドバイスをいくつか紹介します:

  1. 小さく始める

    • 単一の機能を持つシンプルなMCPサーバーから始める
    • 成功したら徐々に機能を追加していく
  2. コミュニティに参加する

    • GitHubのMCPリポジトリをフォロー
    • 既存のMCPサーバー実装を参考にする
    • 疑問点があればディスカッションに参加する
  3. ドキュメントを重視する

    • ツールやリソースの説明を明確に
    • 例示的なプロンプトを提供する
    • エラーケースを丁寧に扱う
  4. セキュリティを最初から考慮する

    • 権限モデルを慎重に設計する
    • センシティブな操作には確認を求める
    • リソースの制限を設ける

「旅は終わりではなく、むしろ始まりなのだ」という言葉がありますが、MCPの旅はまだ始まったばかりです。開発者として、この新しい標準の発展に参加し、貢献することで、AIとアプリケーションの統合の未来を形作る機会があります。

おすすめの書籍

おすすめコンテンツ