🔌

速習・MCP

に公開

先週くらいから各所のホットエントリーに立ち並ぶ"MCP"。最近のAI関連のインプットを怠ってきた(僕のような)人向けの、MCPの速習記事です。
MCPの概要を押さえて、応用や事例の記事で言っていることをイメージできるようになることがゴールです。

MCPとは

一言でいうと、MCPはLLMが外部システムと連携するための統一的なプロトコルで、お互いがこれをサポートすることで、LLMにとって外部システムがプラグアンドプレイなモジュールになることを目指すものです。

LLMと外部システムの連携の課題とMCPが提示する解決策

LLMの発展を原動力に、ChatGPTやClineなどのAIアシスタントアプリケーションが急速に浸透してきましたが、既存のシステム(SlackやGitHubなど)との接続については、アプリケーションごとに独自の方法を定めていました。そのため、この接続の実装はAIアプリケーション毎に必要となり、データソースとの統合の足かせとなっていました。

MCP(Model Context Protocol)は、AIアプリケーションと多様なデータソースを接続するためのオープンな標準で、Claudeなどを開発するAnthropic社によって2024年11月に公開されました。2025/4/11現時点では、2025-03-26が最新版となっています。
https://www.anthropic.com/news/model-context-protocol

既存システムはMCPサーバーを実装・公開するだけで、MCPに準拠したAIアプリケーションであれば、どれでも組み込むことができるようになります。AIアプリケーション側から見ると、MCPに準拠したクライアント実装にしておけば、MCPサーバーが提供する広範なデータソースを自由に選択・接続できるようになります。AIアプリケーションとデータソースの統合がお互いに進むようになるという意味で、大きなインパクトがあります。(なので、今これだけMCPが騒がれてるんだと理解してます。)

現在では、多くのAIアプリケーション、多くのシステム(データソース)で、MCPをサポートし始めています。Google Drive、Slack、GitHubなどと連携できるのは、ビジネスユースケースで特に可能性を感じますね。また、OpenAI Agent SDKでもMCPがサポートされ、今後さらにOpenAI APIなどにもMCPをサポートしていく[1]とのことなので、デファクトスタンダードとしての地位を着実に築きつつあります。

MCPのコンポーネントと大まかな流れ

最初にコンポーネントと大まかな流れを掴んでおきましょう。Toolについては後述しますが、LLMから外部システムを呼び出すためのインタフェースと思ってください。

  1. アプリケーションで実行されるMCPクライアントにて、MCPサーバーからアクセス可能なTool一覧を取得
  2. ユーザーは、アプリケーション(例えばClaude Desktop)にクエリを送る
  3. アプリケーションは、クエリに加えて、アクセス可能なToolの一覧をLLM(例えばClaude)へ送る
  4. LLMは、Toolを使うか・どれを使うかを選択して、アプリケーションへ返す
  5. クライアントは、選択されたToolに対応するサーバーへパラメータを送り、結果を受け取る
  6. アプリケーションは、結果をLLMへ渡す
  7. LLMは、自然言語に変換して返す
  8. アプリケーションがユーザーに表示する

alt text
https://modelcontextprotocol.io/introduction#general-architecture 抜粋(2025/4/12時点)

MCPの仕様的な特徴

MCPの仕様的な特徴を見ていきます。MCPは日付でバージョニングされており[2]、投稿時点での最新は2025-03-26です。
https://modelcontextprotocol.io/specification/2025-03-26

JSON-RPC 2.0がベース

MCPはJSON-RPC 2.0[3]をベースにしたプロトコルです。
RPC(Remote Procedure Call)[4]は、他のアドレス空間で手続き(プログラム)を実行するためのルールのことです。RPCの標準としてはJSON-RPCの他に、gRPCやSOAPなどが有名です。
JSON-RPC 2.0は、シンプルなJSON形式のメッセージを取り決めています。以下に例を示します。リクエストでは、$.methodで呼び出す手続き、$.paramsで手続きに渡すパラメータを指定し、レスポンスでは、$.result$.errorに実行やエラーの結果を詰めています。

// リクエスト
{"jsonrpc": "2.0", "id": 123, "method": "hello", "params": {"name": "ohke"}}
// レスポンス(成功時)
{"jsonrpc": "2.0", "id": 123, "result": {"greeting": "Hello, ohke!"}}
// レスポンス(失敗時)
{"jsonrpc": "2.0", "id": 123, "error": {"code": -1, "message": "Unknown param, `name`."}}

MCPではこのJSON-RPC 2.0に則り、Toolなどのメッセージを構成しています。後ほどMCPサーバーにログを見ますが、以下のようなメッセージがやり取りされます。

// Toolへのリクエスト
{"jsonrpc":"2.0","id":35,"method":"tools/call","params":{"name":"add","arguments":{"a":88,"b":102}}}
// Toolからのレスポンス
{"jsonrpc":"2.0","id":35,"result":{"content":[{"type":"text","text":"190"}],"isError":false}}

通信方法はstdioまたはSSE/Streamable HTTPから選択

JSON-RPC 2.0では、通信方法について具体的な定めがありません。MCPでは、stdioまたはSSE/Streamable HTTPで通信できます[5]。AIアプリケーションの多くはstdioはサポートしています。

  • stdioは、ローカルプロセスと効率的に通信したい場合に有効
  • SSE/Streamable HTTPは、HTTP互換が必要なケースに用いる
    • SSEは2025-03-26では非推奨

サーバーの機能

MCPサーバーで提供すべき機能は3つあります。外部のシステムとの連携では、Toolsが主に用いられます。

  • Resources
    • サーバーから公開するデータで、LLMのコンテキストとして利用する
  • Prompts
    • 予めサーバーで定義したプロンプトテンプレートで、LLMのプロンプトエンジニアリングを助ける
  • Tools
    • LLMが呼び出す関数

クライアントの機能

  • Sampling
    • サーバーからクライアントのLLMを実行する
    • クライアント側でLLMへの入力や生成物をコントロールできる
  • Roots
    • クライアントからサーバーに参照してほしいリソース(ルート)を示唆するURIを与える

MCPサーバーを作ってみよう

MCPの仕様の策定に加えて、各プログラミング言語に向けたSDKの提供など、MCP利用にあたっての周辺整備も進められています。
本投稿ではPythonのSDKを使ってMCPサーバーを実装し、Claude Desktopと接続してみます。

python-sdkでMCPサーバーの実装

MCPのサーバーやクライアントの開発のために、各プログラミング言語でSDKが開発されています。
https://github.com/modelcontextprotocol

Python SDKはこちらで、pip install "mcp[cli]"や、uvを使う場合はuv add "mcp[cli]"でインストールします。
https://github.com/modelcontextprotocol/python-sdk

Quickstartから、MCPサーバーでTools(2つの値を足し算するadd)を実装する例を抜粋します。
FastMCPインスタンスを作り、定義した関数add()にデコレータで@mcp.tool()とするだけで、add()がToolsに登録されます。
これを~/python-mcp/server.pyとして作成します。

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Demo")

@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers"""
    return a + b

MCP Inspectorで動作確認

Claude Desktopに接続する前に、まずは単体で起動してみましょう。
uvでインストールした場合はuv run mcp dev server.pyとすると、MCP Inspectorというサービスが http://127.0.0.1:6274 で立ち上がります。
ブラウザからアクセスしてConnectすると、作成したMCPサーバーを起動・接続し、作成したものが公開されているか(tools/list)を確認したり、作成したToolsに実行したり(tools/call)できます。

alt text

Claude Desktopでの設定

動作確認ができたら、Claude Desktopと繋いでLLMから呼び出してもらいましょう。

Claude Desktopをインストール・起動したら、メニューから 設定 > 開発者 > 構成を編集 を選んで、claude_desktop_config.jsonをテキストエディタで開きます。Macの場合は、~/Library/Application Support/Claude/claude_desktop_config.jsonにあるかと思います。

先ほど作成したMCPサーバーを$.mcpServersに、"Demo"という名前で追加します。また、MCPサーバーを実行するコマンドと引数を記述します。

{
  "mcpServers": {
    "Demo": {
      "command": "/Users/ohke/.local/bin/uv",
      "args": [
        "--directory",
        "/Users/ohke/python-mcp",
        "run",
        "mcp",
        "run",
        "server.py"
      ]
    }
  }
}

編集を保存したら、Claude Desktopを再起動してください。
チャットフォームのハンマーマークを押すと、追加したToolを呼び出せるか確認できます。

Alt text

Claude DesktopでMCPサーバーを呼び出す

ここでチャットを新規作成して、試しに"88 + 102"でポストしてみます。追加したDemoのTool実行を許可するか聞かれますので、これを許可するとaddが実行され、さらに結果をLLMで自然言語にして返答します。LLMと僕のPythonコードがめでたく融合しました。

Alt text

一方で、同じ足し算でも値を変えるとaddが実行されないことがあります。試しに先ほどよりも簡単な値にしてみると、addは実行されずに、結果が返されています。LLMがToolsを呼び出すかどうかをジャッジしていることがわかりますね。

Alt text

MCPの通信ログを見てみる

ここで通信ログを見てみましょう。MacのClaude Desktopでは、~/Library/Logs/Claude/mcp.logに出力されているかと思います。
以下の順番で実行されていることがわかります。

  1. Claude Desktopを立ち上げると、MCPサーバーが起動
  2. "method":"initialize"で、双方の情報を交換(id=0)
  3. "method":"tools/list"で、選択可能なToolとそれぞれのパラメータスキーマを取得(id=1)
  4. チャットで "88 + 102" を実行
  5. {"method":"tools/call","params":{"name":"add","arguments":{"a":88,"b":102}}でリクエスト、{"result":{"content":[{"type":"text","text":"190"}],"isError":false}でレスポンスされており、MCPサーバーで実行されいていることを確認できます(id=35)
  6. チャットで "190" が返ってくる
2025-04-10T09:47:02.184Z [info] [Demo] Initializing server...
2025-04-10T09:47:02.263Z [info] [Demo] Server started and connected successfully
2025-04-10T09:47:02.283Z [info] [Demo] Message from client: {"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"claude-ai","version":"0.1.0"}},"jsonrpc":"2.0","id":0}
2025-04-10T09:47:03.001Z [info] [Demo] Message from server: {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2024-11-05","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"Demo","version":"1.6.0"}}}
2025-04-10T09:47:03.002Z [info] [Demo] Message from client: {"method":"notifications/initialized","jsonrpc":"2.0"}
2025-04-10T09:47:03.004Z [info] [Demo] Message from client: {"method":"tools/list","params":{},"jsonrpc":"2.0","id":1}
2025-04-10T09:47:03.007Z [info] [Demo] Message from server: {"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"add","description":"Add two numbers","inputSchema":{"properties":{"a":{"title":"A","type":"integer"},"b":{"title":"B","type":"integer"}},"required":["a","b"],"title":"addArguments","type":"object"}}]}}
2025-04-10T09:47:04.438Z [info] [Demo] Message from client: {"method":"resources/list","params":{},"jsonrpc":"2.0","id":2}
2025-04-10T09:47:04.441Z [info] [Demo] Message from client: {"method":"tools/list","params":{},"jsonrpc":"2.0","id":3}
2025-04-10T09:47:04.444Z [info] [Demo] Message from server: {"jsonrpc":"2.0","id":2,"result":{"resources":[]}}
2025-04-10T09:47:04.444Z [info] [Demo] Message from server: {"jsonrpc":"2.0","id":3,"result":{"tools":[{"name":"add","description":"Add two numbers","inputSchema":{"properties":{"a":{"title":"A","type":"integer"},"b":{"title":"B","type":"integer"}},"required":["a","b"],"title":"addArguments","type":"object"}}]}}
2025-04-10T09:47:04.577Z [info] [Demo] Message from client: {"method":"prompts/list","params":{},"jsonrpc":"2.0","id":4}
2025-04-10T09:47:04.580Z [info] [Demo] Message from server: {"jsonrpc":"2.0","id":4,"result":{"prompts":[]}}
...
2025-04-10T09:48:21.823Z [info] [Demo] Message from client: {"method":"tools/call","params":{"name":"add","arguments":{"a":88,"b":102}},"jsonrpc":"2.0","id":35}
2025-04-10T09:48:21.828Z [info] [Demo] Message from server: {"jsonrpc":"2.0","id":35,"result":{"content":[{"type":"text","text":"190"}],"isError":false}}
...

まとめ

"MCPなんもわからん"人向けに、MCPの概要についてまとめました。

参考

脚注
  1. https://x.com/OpenAIDevs/status/1904957755829481737 ↩︎

  2. https://modelcontextprotocol.io/specification/versioning ↩︎

  3. https://www.jsonrpc.org/specification ↩︎

  4. https://en.wikipedia.org/wiki/Remote_procedure_call ↩︎

  5. https://modelcontextprotocol.io/docs/concepts/architecture#transport-selection ↩︎

Discussion