MCP入門
MCP概要説明
この記事はMCP2025-03-26リビジョンを基に作成しました。
Model Context Protocol (MCP) とは何か?
MCP は、AI アシスタント(チャットボットや自動化エージェントなど)が、さまざまな外部データやツールにアクセスするための 共通のルール(プロトコル) です。
従来は、AI にデータベースやウェブサービス、ローカルのファイルを使わせたいとき、それぞれ違う接続方法をいちいち作り込む必要がありました。すると、AI を拡張するたびに「新しいツール用の独自コード」を用意しなくてはなりません。
MCP を使うと、「AI ⇔ データやツール」 の接続方式を 標準化 できるため、同じ仕組みでいろいろなデータソースや外部サービスとやり取りできます。これは、AI の開発者とデータ管理者双方にとって、大きな手間削減や再利用性の向上につながります。
Anthropic が策定したこと、そして OpenAI を含む他社が採用を始めていることから、MCP は今後の AI アプリケーション連携の業界標準として大きな注目を集めています。
どのような共通ルールなのか?
MCPは、初期化で互いの“立場と機能”を確認し合い、あとは統一フォーマットのリクエスト/レスポンスで自由にやり取りする仕組みです。細かい実装や運用方法は後で詳しく見ますが、まずは「同じ言語(JSON-RPC)」「最初に名刺交換(initialize)」「ツールやリソースを簡単にやり取り」の3点を押さえておけば、MCPの概念をつかめるはずです。
-
共通言語はJSON-RPC
たとえば国際会議で「共通語を英語にしよう」と決めるように、MCPでは「JSON-RPC 2.0」がその“英語”役です。
-
最初の“名刺交換”:initialize
クライアントがサーバに呼びかけるとき、まずは “initialize” というメソッドを送ります。これは「私はこういう機能がある」「あなたはどんな機能を提供できる?」という、いわば名刺交換です。
-
チャンネルが開いたあとは…
一度初期化が済めば、
-
tools(登録された関数の呼び出し)
-
resources(ファイルやデータ取得)
-
prompts(会話テンプレートの取得)
-
logging/completion/ping
などのやり取りを同じルールで行えます。
-
-
“どこで”送るかは自由だが、現在はstdio, Streamable HTTPが標準
JSON-RPCがフォーマットとして決まっているだけで、通信手段(標準入力・SSE・WebSocketなど)は何でもOK。結果的に、サーバ開発者は「MCPさえ守れば」簡単に機能を公開できます。現在はスタンダードなやり方としてstdioとStreamable HTTPが設定されています。
MCP アーキテクチャ概要
MCP は「ホスト(Host)」「クライアント(Client)」「サーバ(Server)」の3つの役割を柱として成り立ちます。1つのホストが複数のクライアントを作成し、それぞれのクライアントが1対1でサーバと通信を行い、AI 機能を拡張するという構造です。イメージとしては、ホストがあって、そこから必要な数だけクライアント(セッション)を生やし、それぞれが特定のサーバと対話している状態です。
“LINE”に例えると
スマホの メッセンジャーアプリ LINE を連想すると分かりやすいです。
- ホスト (Host) = LINE アプリ本体(MCP では LLM などの AI 機能を持った中心的存在)
- クライアント (Client) = 個々の “チャットウィンドウ” や “会話スレッド”。1つのスレッドは1人の相手(サーバ)とのみ通信する
- サーバ (Server) = “友達”や“グループ”など、メッセージを受け取って返事をする相手
1つの LINE アプリ(ホスト)が複数の友達(サーバ)とチャットをする際、内部的には相手ごとに1対1のチャットセッション(クライアント)が存在します。MCP も同じ構造で、ホストがクライアントを通じてサーバと通信し、AI 機能を拡張していきます。
3つの登場人物の役割
1. ホスト (Host)
ホストは MCP 全体の中枢 となる存在で、以下の役割を担います。
-
複数のクライアント(セッション)を生成し管理
サーバごとに必要なセッション(クライアント)を作り、セキュリティや権限を統括します。
-
AI(LLM)との連携・指揮
AI モデルにどのようなデータや機能を活用させるかを制御し、必要に応じてサーバへリクエストを送るよう指示します。
-
複数サーバからの情報を集約し、AI に活用
各クライアントが取得した情報を組み合わせて、AI が最適に利用できる形にまとめます。
2. クライアント (Client)
クライアントは “特定のサーバと通信するための1対1のセッション” を担当します。MCP では以下のように動作します。
-
1つのクライアントは1つのサーバ専用
例えば「DB サーバ用のクライアント」「翻訳サーバ用のクライアント」といった形で、1対1の通信を行います。
-
JSON-RPC でのやり取り
initialize
やcall_tool
,read_resource
などのメソッドを使い、JSON-RPC ベースでサーバと双方向通信を行います。 -
通知やイベントへの対応
ログ通知などの通知がサーバから届いた場合は、クライアント側が受け取り、必要ならホストへフィードバックします。
クライアント実装上のポイント
クライアントはMCPが提供する実装が比較的に少なく、基本的にSession(プロトコル層)と、stdio_client, sse_client, websocket_clientが用意されているようです。
-
低レベル層:
ClientSession
(mcp\client\session.py)- JSON-RPCメッセージを実際に生成し送信、サーバのレスポンスを待つ。
-
initialize
やtools/call
などのコア操作をローレベルで扱う。
-
トランスポート
- 標準入出力(stdio)や SSE(HTTP経由)などを使い、JSON形式のメッセージをやり取り。
3. サーバ (Server)
サーバは AI が活用できる機能やデータを公開 する存在です。
-
ツールやリソースの提供
例えば「翻訳機能」「データベース照会機能」「ファイル読み書き」など、多種多様な機能をエンドポイントとして公開し、クライアント経由で呼び出されます。
-
ホストや他サーバの情報には直接アクセスしない
セキュリティを保つため、自分に与えられた権限とリソースのみを扱い、他サーバやホスト内部データには直接触れません。
-
必要に応じた LLM 活用
場合によってはクライアント経由でホストの LLM(AI)に問い合わせることも可能です(例: 翻訳サーバがさらに別の言語処理をホストに依頼する、など)。
サーバ実装上のポイント
サーバは比較的に色んな実装が提供されていて、低レベルのServerと、それをラッピングしたFastMCPを用いて開発が可能です。
-
低レベル層:
Server
-
@server.call_tool
や@server.list_tools()
などのデコレータでリクエスト種別に対応するハンドラを定義し、 - “どのメソッドが来たらどの関数を動かすか” を管理する。
-
-
内部的な接続管理:
ServerSession
- 新たなクライアント接続ごとにセッションを生成し、JSON-RPCメッセージを実際に受け取って処理する。
- クライアント1つにつき1つの
ServerSession
が対応し、Server
に登録されたハンドラを呼び出して結果を返す。
-
高レベルフレームワーク
-
FastMCP
などを使えば SSE ルートやツール管理が自動セットアップされ、サーバ側の実装がシンプルに済む。
-
-
トランスポート
- クライアントと同様、stdio や SSE を使い、JSON形式のリクエスト/レスポンスをやり取りする。
デザイン原則
-
サーバはできるだけシンプルに
大きなオーケストレーションはホストが担う一方、サーバは必要な機能だけに特化して構築します。
-
高い組み合わせ性
共通プロトコルでつながるため、サーバを積み木のように増やしていくだけで新しい AI 機能を取り込めます。
-
視野を限定
セキュリティ上、各サーバはあくまで自分の権限内で完結し、サーバ同士は直接データを共有しません。全体把握はホスト側に委ねられます。
-
段階的な機能追加
コア機能は最小限に留め、拡張機能は “capability negotiation(機能交渉)” で合意を取りながら追加します。これにより後方互換性を保ちつつ、プロトコルを成長させられます。
登場人物のまとめ
- MCP のアーキテクチャでは、ホストが全体の指揮を執り、必要に応じて複数のクライアント(セッション)を立ち上げ、それぞれがサーバと1対1で通信する形を取っています。
- こうすることでサーバは単機能に絞って実装可能になり、ホストは必要なだけセッションを増やして自由に機能を合体できるため、AI システムを拡張しやすくなります。
サーバとクライアントが話す方法のコア要素
上記では登場人物について調べました。次はその登場人物が話す方法について解説します。
クライアントとサーバはどのようなフォーマットで話すかが決まっていますが、それが「プロトコル層」になります。実際に話すチャンネル、すなわち電話で話すか、メールで話すかを表すのが「トランスポート層」です。そして、話す内容が決められていて、それが「メッセージタイプ」になります。
1. プロトコル層(セッション)
何をするところ?
ここでプロトコルを定義します。
- メッセージの枠組み(JSON-RPC 構造)を定義し、対応するリクエストとレスポンスを結びつける。
-
ServerSession
/ClientSession
などのクラスで、サーバとクライアントそれぞれのやり取りを管理。 - 送受信は「MCPメッセージ(Requests / Results / Errors / Notifications)」として扱われ、セッションがそれを解析・対応付けする。
どう動く?
- クライアント側は
ClientSession
を使い、“リクエストを作って送る/レスポンスを受け取る” フローを整理。 - サーバ側は
ServerSession
を使い、“リクエストを受け取りハンドラを呼ぶ/結果を返す” フローを管理。
2. トランスポート層
何をするところ?
- 実際のバイト列を「どこで」「どう送るか」を担う。
- MCPでは stdio と Streamable HTTP の 2 種類が標準的。
- セッションは “JSON-RPC メッセージを発行” し、トランスポートは “それを実際に送受信” するイメージ。
具体例
- stdio transport: 標準入出力の文字列行を JSON としてセッションに供給。ローカルプロセス間向き。
- HTTP + SSE transport: サーバからクライアントへは SSE イベント、クライアントからサーバへは HTTP POST という形でメッセージ交換。
3. メッセージタイプ
MCPでは、JSON-RPC に基づいて4種類のメッセージがやり取りされる。
-
Requests
「応答が必要」な呼び出し。
method
とparams
、id
を持ち、サーバやクライアントに何らかの処理を要求する。 -
Results
Requests に対する「成功応答」。JSON-RPC上
result
フィールドを返す。 -
Errors
Requests が失敗した際に返るエラー情報。
code
,message
,data
などを持つ。 -
Notifications
応答不要の“一方向メッセージ”。ログ通知や進捗通知などに使われる。
コネクションのライフサイクル
MCPはクライアントとサーバが通信する時にライフサイクルを明記しています。
これを守らないと、エラーになります。
-
Initialization
- クライアントが
initialize
リクエストでバージョンや機能情報を送る - サーバが対応バージョンや機能を返す
- クライアントが
initialized
通知で応答 - これで「初期化済み」状態となり、以降のやり取りが可能になる
- クライアントが
-
Message Exchange
- Request-Response: どちらからでもリクエストを送り、相手がレスポンスを返す
- Notifications: どちらからでも一方向メッセージを送れる
-
Termination
-
Clean shutdown(
close()
など) -
ネットワーク切断
-
エラー状態
などによって接続が終了する
-
以上が MCP を支える主要なコンポーネントの流れです。プロトコル層が中心となり、トランスポートで実際の入出力を行い、サーバ/クライアントがそのうえでツールやリソースなどの機能をやり取りする構造が整えられています。
実際に作ってみよう
以下では、MCP サーバとMCP クライアントを、それぞれ実際に動かすための最小限のコードと、内部コンポーネントのカスタマイズ方法をまとめます。
「MCP にはプロトコル層やトランスポート層など、いろいろある」と言っても、実際に実装する時は色んな便利ツールがあるので、簡単に実装できます
mini_server.py
)
最小サンプルサーバ (from mcp.server.fastmcp import FastMCP
mcp = FastMCP("hello")
@mcp.tool()
async def hello_world(name: str) -> str:
return f"Hello, {name}!"
if __name__ == "__main__":
# stdioで通信
mcp.run(transport="stdio")
ポイント:
-
FastMCP("hello")
でサーバを作成し、@mcp.tool()
で超シンプルなツールを1つ登録。 -
mcp.run(transport="stdio")
により、stdioベースでクライアントとの通信が行われる。
mini_client.py
)
最小サンプルクライアント (import asyncio
from mcp.client.stdio import stdio_client, StdioServerParameters
from mcp import ClientSession
async def main():
# (A) サーバスクリプトを python で起動
server_params = StdioServerParameters(command="python", args=["mini_server.py"])
# (B) async with を使って stdio_client を起動・管理
async with stdio_client(server_params) as (read_stream, write_stream):
async with ClientSession(read_stream, write_stream) as session:
# Initialize the connection
await session.initialize()
# (C) ツール呼び出し: hello_world
result = await session.call_tool("hello_world", {"name": "MCP"})
print("Tool result:", result.content)
if __name__ == "__main__":
asyncio.run(main())
ポイント:
-
(A) で「
mini_server.py
を Python で実行し、stdin/stdout をつないだトランスポート」を確立。 -
(B) で
ClientSession
を使い、initialize()
を呼んでサーバと初期化を完了。 -
(C)
call_tool("hello_world", {"name": "MCP"})
でサーバのhello_world
が動き、「Hello, MCP!」といったレスポンスを取得。
ユーザが実装すべきところ vs. MCPの内部ロジック
実装者が手を入れる部分
-
ツール/リソース/プロンプトの登録
たとえば「このHTTP APIを呼ぶツール」「ローカルファイルを返すリソース」「定型文を返すプロンプト」などを、MCPサーバに登録する。
-
どのトランスポートを使うか
例: 標準入出力 (
stdio
) か Streamable HTTP を使うか、あるいは独自実装するか。 -
サーバの名前やバージョン
weather
サーバなら、name="weather"
のように指定しておくとクライアントへの情報として返される。
MCP内部で自動化されるもの
-
ServerSession(プロトコル層)
リクエストが来たら JSON-RPC として解析→対応するデコレータ付き関数を呼ぶ→レスポンスを構築…という流れ。
普段はあまり意識せずに、
@server.tool()
のようなデコレータだけ書けばOK。 -
通信まわり (Transport)
たとえば
mcp.run(transport="stdio")
と書くだけで、標準入出力を用いた受信→パース→呼び出し→応答 が裏で行われる。
MCPの概要を調べてみました。
概念を理解できると、他のMCPサービスの利用はもちろん、自分自身のMCPサーバを作ったり、いろいろできると思います。
実際の公式サイトのサンプルコードが参考になりますので、参考にしてください。
追加
SSEがオプショナルになりStreamable HTTPが推薦されるようになったらしいです。公式ホームページなどのドキュメントはまだSSEの情報になっています。
進化が早く、毎日のように内容が更新されています。本記事の内容は2025-03-26の内容をベースにしています。
Discussion