MCPについて調べていた時に、FastMCPのコードを見ている時に「サーバーをコマンドラインから起動していないのに、クライアントがサーバーと通信している」ことが不思議だったので調べました。
MCP関連の用語整理
基本用語だけ整理すると以下の通り。
| 用語 | 内容 |
|---|---|
| MCP | AIがツールと通信するためのプロトコル |
| MCPサーバー | 実際にツール処理を行うプログラム |
| FastMCP | MCPサーバー・クライアントを作成するためのPythonフレームワーク |
調査
ネットをざっと見ても、コマンドで起動するやり方しか見つからなかったので、ChatGPTで調査した。
例として挙げられたプログラムは以下だった。
- server.py
from fastmcp import FastMCP
mcp = FastMCP("sample-server")
@mcp.tool()
def add(a: int, b: int) -> int:
return a + b
if __name__ == "__main__":
mcp.run(transport="stdio")
- client.py
import asyncio
from fastmcp import Client
from fastmcp.client.transports import StdioTransport
async def main():
transport = StdioTransport(
command="python",
args=["server.py"],
)
async with Client(transport) as client:
tools = await client.list_tools()
print(tools)
result = await client.call_tool("add", {"a": 10, "b": 20})
print(result)
asyncio.run(main())
サーバー起動コマンドを定義している場所
ここの処理で起動コマンドを定義して
transport = StdioTransport(
command="python",
args=["server.py"],
)
実際にプロセスを生成しているのは以下の処理
async with Client(transport) as client:
通信の仕組み
公式ドキュメントに詳しく書いていました。
https://gofastmcp.com/clients/transports?utm_source=chatgpt.com
自分なりにまとめると・・・
FastMCPのSTDIOトランスポートでは、標準入力(stdin)・標準出力(stdout)を使ってクライアントとサーバーが通信する。
STDIOトランスポートを使用すると、サブプロセスパイプを介してMCPサーバーと通信する。このとき、クライアントがサーバープロセスを起動、管理し、そのライフサイクルを制御する。
さらに調べると、インメモリ転送と呼ばれる仕組みを使うと、同プロセス内でMCPサーバーと接続するので、サブプロセス管理とネットワークオーバーヘッドがいらない。
from fastmcp import FastMCP, Client
import os
mcp = FastMCP("TestServer")
@mcp.tool
def greet(name: str) -> str:
prefix = os.environ.get("GREETING_PREFIX", "Hello")
return f"{prefix}, {name}!"
client = Client(mcp)
async with client:
result = await client.call_tool("greet", {"name": "World"})
まとめ
FastMCPでは、STDIOトランスポートを使用すると、クライアント側がサーバープロセスを起動してくれる。
また、MCPサーバーはクライアントとは別のプロセスでは無く、サブプロセスや同プロセスで動かすことができる。