はじめに
長崎、いいね! 暖房入ってるね👍 nikkieです
今回もMCPの素振りです。
私はClaude Desktopを使う気がなく1、クライアントスクリプトにサーバのスクリプトを渡さない方法を探しました。
目次
前回のMCP! QuickstartをClaude、GPT、Geminiで動かす
MCPのドキュメントのQuickstartに沿って、Pythonでサーバとクライアントを実装しました。
- サーバ
- 天気の警報取得と天気予報取得の2つのtoolを提供
- クライアント
- Claude、GPT、Geminiにtoolsを渡して動かせています!
動かし方は uv run client.py ../weather/weather.py
です。
サーバのスクリプトをクライアントに渡しています
最初の素振りの記事(上の2つのうち1つ目)にて
また、「MCPを採用して構築したアプリケーションってサーバのスクリプトを渡してはいないのでは?」という思いがあり、スクリプトを渡す以外にサーバを立ててHTTP通信する方法もあるのかも疑問に思っています
と書きました。
この記事ではスクリプトを渡す以外の動かし方を模索します。
結論としては、SSEなるもの2が提供されていました
MCPサーバとクライアントをSSEで動かす
2種類のtransport
このドキュメントの中のBuilt-in Transport Typesより
MCP includes two standard transport implementations:
- stdio (Standard Input/Output)
- SSE (Server-Sent Events)
- この記事で扱う方法です
- 文字通りサーバとクライアントです(スクリプトを渡すというようなことはしません)
- サーバからクライアントにstreamingな通信
QuickstartのMCPサーバとクライアントを、SSEをサポートするように書き換えていきます。
SSEをサポートしたMCPサーバ
実はめちゃめちゃ簡単です。
if __name__ == "__main__": - mcp.run(transport="stdio") + mcp.run(transport="sse")
FastMCP
インスタンスのrun
メソッドのtransport
引数の値を変えるだけです。
このサーバのスクリプトをuv run
すると
% uv run weather.py INFO: Started server process [72290] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
uvicornが立ち上がるんですね5!!
- Server-Sent Events (SSE)の「Python (Server)」の実装!(Starlette)
- さらに、uvicornでStarletteアプリをサーブ
この実装によってuvicornが立ち上がったのでした。
これでMCPサーバのSSEサポートは完了です。
uv run
でサーバを立ち上げておきます。
SSEをサポートしたMCPクライアント
Server-Sent Events (SSE)の「Python (Client)」の実装を参考にすると、以下のようになります
from mcp import ClientSession from mcp.client.sse import sse_client async def main(): async with sse_client("http://localhost:8000/sse") as streams: async with ClientSession(streams[0], streams[1]) as session: await session.initialize() tools = await session.list_tools() print(tools) if __name__ == "__main__": import asyncio asyncio.run(main())
このスクリプトをuv run
すると、ツールの一覧が取得できます!
Quickstartのclientと同様の実装に落とし込みます。
この機にcontextlib.AsyncExitStack
のドキュメントを見ましたが、なかなか興味深かったです。
(非同期)コンテキストマネージャのstack、内部的にはwith
文のネストなんですね
Pythonのcontextlib、ExitStackなんてものがあるですね〜https://t.co/0NN4jD92Sq
— nikkie(にっきー) / にっP (@ftnext) 2025年3月11日
>例えば、複数のファイルを1つの with 文で簡単に扱うことができます:
with ExitStack() as stack:
files = [stack.enter_context(open(fname)) for fname in filenames]
動作確認
gemini-2.0-flashとgpt-4oで動作確認しました(今回Claudeは省略)。
SSEでも動きます!
gemini-2.0-flashの例(uv run client.py
)
MCPサーバのログ(SSEの様子)はこちら
https://gist.github.com/ftnext/d8276f058b4e8c4d0b76fc0d9743fc0a
終わりに
SSEでMCPサーバとクライアントを動かせました!🙌
stdio transportでサーバのスクリプトを渡していたときと比べて、組合せやすくなるのではと期待しています。
SSEをサポートしさえすれば、サーバの実装の詳細は考えなくてよくなっています。
MCPサーバをDockerコンテナで立てるようなこともできるのではないでしょうか(要検証)
なおSSEで立てたMCPサーバですがCtrl+C
1回では終了せず、force quitになっちゃってます。
私なにか見落としているかもしれません
INFO: Waiting for background tasks to complete. (CTRL+C to force quit)
ソースコードの全容はこちらです
- だって、LLMもMCPサーバも任意の組合せができるんですよ! Claudeだけに固執することないですよ↩
- 出会いは初めてではなく、GitHub Copilot Extensionsにて出会っていました ↩
-
command = "python" if is_python else "node"
ref:https://modelcontextprotocol.io/quickstart/client↩ - https://github.com/modelcontextprotocol/python-sdk/tree/main/examples/servers/simple-tool#example↩
- mcpパッケージはuvicornに依存しています https://github.com/modelcontextprotocol/python-sdk/blob/v1.3.0/pyproject.toml#L32↩