
FastMCP å ¥é (7) - FastMCPã¯ã©ã€ã¢ã³ã
ãFastMCPãã®ãã¯ã©ã€ã¢ã³ããã«ã€ããŠãŸãšããŸããã
åå
1. FastMCPã¯ã©ã€ã¢ã³ã
1-1. FastMCPã¯ã©ã€ã¢ã³ã
ãfastmcp.Clientãã¯ãMCPãµãŒããšããåãããããã®é«æ°Žæºã®éåæã€ã³ã¿ãŒãã§ãŒã¹ãæäŸããŸãããããã³ã«ã®è©³çŽ°ãšæ¥ç¶ç®¡çãåŠçããããšã§ãéä¿¡ãç°¡çŽ åããŸãã
FastMCPã¯ã©ã€ã¢ã³ãã¢ãŒããã¯ãã£ã¯ããããã³ã«ããžã㯠(ã¯ã©ã€ã¢ã³ã) ãšæ¥ç¶ã¡ã«ããºã (ãã©ã³ã¹ããŒã) ãåé¢ããŸãã
ã»ã¯ã©ã€ã¢ã³ã
MCPãªã¯ãšã¹ãïŒtools/callãresources/readãªã©ïŒã®éä¿¡ãã¬ã¹ãã³ã¹ã®åä¿¡ãã³ãŒã«ããã¯ã®ç®¡çãåŠçããŸãã
ã»ãã©ã³ã¹ããŒã
ãµãŒããžã®æ¥ç¶ïŒWebSocketãSSEãStdioãã¡ã¢ãªå ãªã©ïŒã®ç¢ºç«ãšç¶æãæ åœããŸãã
1-2. ãã©ã³ã¹ããŒã
ã¯ã©ã€ã¢ã³ãã¯ãã©ã³ã¹ããŒãã䜿çšããŠåæåããå¿ èŠããããŸããã€ã³ã¹ã¿ã³ã¹åããããã©ã³ã¹ããŒããªããžã§ã¯ããæå®ãããããã©ã³ã¹ããŒããœãŒã¹ãæå®ããŠFastMCP ã«é©åãªãã©ã³ã¹ããŒããæšæž¬ãããããéžæã§ããŸãã
å ¥åã¿ã€ãã«åºã¥ããŠé©å㪠ClientTransport ãæ±ºå®ããããã«ã以äžã®æšè«èŠåã䜿çšãããŸãã
ã»ClientTransport ã€ã³ã¹ã¿ã³ã¹
æ¢ã«ã€ã³ã¹ã¿ã³ã¹åãããŠãããã©ã³ã¹ããŒããªããžã§ã¯ããæå®ããå ŽåããããçŽæ¥äœ¿çšãããŸãã
ã»FastMCP ã€ã³ã¹ã¿ã³ã¹
å¹ççãªã¡ã¢ãªå éä¿¡ (ãã¹ãã«æé©) ã®ããã« FastMCPTransport ãäœæããŸãã
ã»æ¢åãã¡ã€ã«ãæã path ãŸã㯠str
ãã».py ã§çµããå Žå : PythonStdioTransport ãäœæããPython ã䜿çšããŠã¹ã¯ãªãããå®è¡ããŸãã
ãã».js ã§çµããå Žå : NodeStdioTransport ãäœæããNode ã䜿çšããŠã¹ã¯ãªãããå®è¡ããŸãã
ã»URL ãæã AnyUrl ãŸã㯠str
ãã»http:// ãŸã㯠https:// ã§å§ãŸãå Žå: SSETransport ãäœæããŸãã
ãã»ws:// ãŸã㯠wss:// ã§å§ãŸãå Žå: WSTransport ãäœæããŸãã
ã»ãã®ä»
åãæšè«ã§ããªãå Žå㯠ValueError ãçºçããŸãã
import asyncio
from fastmcp import Client, FastMCP
# ãã©ã³ã¹ããŒãã®äŸ (ãã©ã³ã¹ããŒãããŒãžãåç
§)
server_instance = FastMCP(name="TestServer") # ã€ã³ã¡ã¢ãªãµãŒã
sse_url = "http://localhost:8000/sse" # SSEãµãŒãã®URL
ws_url = "ws://localhost:9000" # WebSocketãµãŒãã®URL
server_script = "my_mcp_server.py" # PythonãµãŒããã¡ã€ã«ãžã®ãã¹
# ã¯ã©ã€ã¢ã³ãã¯ãã©ã³ã¹ããŒãã®çš®é¡ãèªåçã«æšæž¬
client_in_memory = Client(server_instance)
client_sse = Client(sse_url)
client_ws = Client(ws_url)
client_stdio = Client(server_script)
print(client_in_memory.transport)
print(client_sse.transport)
print(client_ws.transport)
print(client_stdio.transport)
# äºæ³ãããåºå (ç°å¢ã«ãã£ãŠã¿ã€ããè¥å¹²ç°ãªãå Žåããã)
# <FastMCP(server='TestServer')>
# <SSE(url='http://localhost:8000/sse')>
# <WebSocket(url='ws://localhost:9000')>
# <PythonStdioTransport(command='python', args=['/path/to/your/my_mcp_server.py'])>2. ã¯ã©ã€ã¢ã³ãã®äœ¿çš
2-1. æ¥ç¶ã©ã€ããµã€ã¯ã«
ã¯ã©ã€ã¢ã³ãã¯éåæã§åäœããasync with ãããã¯å ã§äœ¿çšããå¿ èŠããããŸãããã®ã³ã³ããã¹ããããŒãžã£ã¯ãæ¥ç¶ã®ç¢ºç«ãMCPã»ãã·ã§ã³ã®åæåãçµäºæã®ãªãœãŒã¹ã®ã¯ãªãŒã³ã¢ãããåŠçããŸãã
import asyncio
from fastmcp import Client
client = Client("my_mcp_server.py") # Assumes my_mcp_server.py exists
async def main():
# ããã§æ¥ç¶ã確ç«
async with client:
print(f"Client connected: {client.is_connected()}")
# ã³ã³ããã¹ãå
ã§MCPåŒã³åºããè¡ã
tools = await client.list_tools()
print(f"Available tools: {tools}")
if any(tool.name == "greet" for tool in tools):
result = await client.call_tool("greet", {"name": "World"})
print(f"Greet result: {result}")
# ããã§æ¥ç¶ã¯èªåçã«ã¯ããŒãº
print(f"Client connected: {client.is_connected()}")
if __name__ == "__main__":
asyncio.run(main())確ç«ãããã»ãã·ã§ã³ã䜿çšããŠãåã async with ãããã¯å ã§ãµãŒããžã®è€æ°ã®åŒã³åºããè¡ãããšãã§ããŸãã
2-2. ã¯ã©ã€ã¢ã³ãã¡ãœãã
ã¯ã©ã€ã¢ã³ãã¯ãæšæºã®MCPãªã¯ãšã¹ãã«å¯Ÿå¿ããã¡ãœãããæäŸããŸãã
2-3. ããŒã«æäœ
ã»list_tools() : ãµãŒãäžã§å©çšå¯èœãªããŒã«ã®ãªã¹ããååŸããŸãã
tools = await client.list_tools()
# tools -> list[mcp.types.Tool]ã»call_tool(name: str, argument: dict[str, Any] | None = None) : ãµãŒãäžã§ããŒã«ãå®è¡ããŸãã
result = await client.call_tool("add", {"a": 5, "b": 3})
# result -> list[mcp.types.TextContent | mcp.types.ImageContent | ...]
print(result[0].text) # Assuming TextContent, e.g., '8'ã»åŒæ°ã¯èŸæžãšããŠæž¡ãããŸããFastMCPãµãŒãã¯ãå¿ èŠã«å¿ããŠè€éãªåã®JSONæååè§£æãèªåçã«åŠçããŸãã
ã»ã³ã³ãã³ããªããžã§ã¯ã (éåžžã¯TextContentãŸãã¯ImageContent) ã®ãªã¹ããè¿ããŸãã
2-4. ãªãœãŒã¹æäœ
ã»list_resources() : éçãªãœãŒã¹ã®ãªã¹ããååŸããŸãã
resources = await client.list_resources()
# resources -> list[mcp.types.Resource]ã»list_resource_templates() : ãªãœãŒã¹ãã³ãã¬ãŒãã®ãªã¹ããååŸããŸãã
templates = await client.list_resource_templates()
# templates -> list[mcp.types.ResourceTemplate]ã»read_resource(uri: str | AnyUrl) : ãªãœãŒã¹ãŸãã¯è§£æ±ºããããã³ãã¬ãŒãã®ã³ã³ãã³ããèªã¿åããŸãã
# éçãªãœãŒã¹ãèªã¿åã
readme_content = await client.read_resource("file:///path/to/README.md")
# readme_content -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents]
print(readme_content[0].text) # ããã¹ããæ³å®
# ãã³ãã¬ãŒãããçæããããªãœãŒã¹ãèªã¿åã
weather_content = await client.read_resource("data://weather/london")
print(weather_content[0].text) # ããã¹ãJSONãæ³å®2-5. ããã³ããæäœ
ã»list_prompts() : å©çšå¯èœãªããã³ãããã³ãã¬ãŒããååŸããŸãã
ã»get_prompt(name: str, argument: dict[str, Any] | None = None) : ã¬ã³ããªã³ã°ãããããã³ããã¡ãã»ãŒãžãªã¹ããååŸããŸãã
3. çã®MCPãããã³ã«ãªããžã§ã¯ã
FastMCPã¯ã©ã€ã¢ã³ãã¯ãMCPãããã³ã«ãžã®ããã¬ã³ããªãŒãªãã€ã³ã¿ãŒãã§ãŒã¹ãæäŸããããšããŸãããçã®MCPãããã³ã«ãªããžã§ã¯ããžã®ã¢ã¯ã»ã¹ãå¿ èŠã«ãªãå ŽåããããŸããããŒã¿ãè¿ãäž»èŠãªã¯ã©ã€ã¢ã³ãã¡ãœããã«ã¯ãããããçã®MCPãããã³ã«ãªããžã§ã¯ããçŽæ¥è¿ã察å¿ãã*_mcp ã¡ãœããããããŸãã
# æšæºã¡ãœãã - ããŒã«ã®ãªã¹ãã®ã¿ãè¿ããŸã
tools = await client.list_tools()
# tools -> list[mcp.types.Tool]
# çã®MCPã¡ãœãã - å®å
šãªãããã³ã«ãªããžã§ã¯ããè¿ã
result = await client.list_tools_mcp()
# result -> mcp.types.ListToolsResult
tools = result.toolsã»list_tools_mcp() : mcp.types.ListToolsResult ãè¿ããŸã
ã»call_tool_mcp(name, argument) : mcp.types.CallToolResult ãè¿ããŸã
ã»list_resources_mcp() : mcp.types.ListResourcesResult ãè¿ããŸã
ã»list_resource_templates_mcp() : mcp.types.ListResourceTemplatesResult ãè¿ããŸã
ã»read_resource_mcp(uri) : mcp.types.ReadResourceResult ãè¿ããŸã
ã»list_prompts_mcp() : mcp.types.ListPromptsResult ãè¿ããŸã
ã»get_prompt_mcp(name, argument) : mcp.types.GetPromptResult ãè¿ããŸã
ã»complete_mcp(ref, argument) : mcp.types.CompleteResult ãè¿ããŸã
ãããã®ã¡ãœããã¯ããããã°æããç°¡ç¥åãããã¡ãœããã§ã¯å ¬éãããªãã¡ã¿ããŒã¿ããã£ãŒã«ãã«ã¢ã¯ã»ã¹ããå¿ èŠãããå Žåã«ç¹ã«åœ¹ç«ã¡ãŸãã
4. é«åºŠãªæ©èœ
MCPã䜿çšãããšããµãŒãã¯ã¯ã©ã€ã¢ã³ããšå¯Ÿè©±ããŠè¿œå æ©èœãæäŸã§ããŸããClientã³ã³ã¹ãã©ã¯ã¿ã¯ããããã®ãµãŒããªã¯ãšã¹ããåŠçããããã®è¿œå èšå®ãåãå ¥ããŸãã
4-1. LLM ãµã³ããªã³ã°
MCPãµãŒãã¯ã¯ã©ã€ã¢ã³ããã LLM è£å®ãèŠæ±ã§ããŸããã¯ã©ã€ã¢ã³ãã¯ãããã®èŠæ±ãåŠçããããã®ãµã³ããªã³ã°ãã³ãã©ãæäŸã§ããŸãããµã³ããªã³ã°ãã³ãã©ã¯ãµãŒãããã¡ãã»ãŒãžã®ãªã¹ããšãã®ä»ã®ãã©ã¡ãŒã¿ãåãåããæååè£å®ãè¿ããŸãã
次ã®äŸã§ã¯ãmarvin ã©ã€ãã©ãªã䜿çšããŠè£å®ãçæããŸãã
import marvin
from fastmcp import Client
from fastmcp.client.sampling import (
SamplingMessage,
SamplingParams,
RequestContext,
)
async def sampling_handler(
messages: list[SamplingMessage],
params: SamplingParams,
context: RequestContext
) -> str:
return await marvin.say_async(
message=[m.content.text for m in messages],
instructions=params.systemPrompt,
)
client = Client(
...,
sampling_handler=sampling_handler,
)4-2. ãã°èšé²
MCPãµãŒãã¯ã¯ã©ã€ã¢ã³ãã«ãã°ãåºåã§ããŸããã¯ã©ã€ã¢ã³ãã¯ããããã®ãã°ãåä¿¡ããããã®ãã°èšé²ã³ãŒã«ããã¯ãèšå®ã§ããŸãã
from fastmcp import Client
from fastmcp.client.logging import LogHandler, LogMessage
async def my_log_handler(params: LogMessage):
print(f"[Server Log - {params.level.upper()}] {params.logger or 'default'}: {params.data}")
client_with_logging = Client(
...,
log_handler=my_log_handler,
)4-3. ã«ãŒã
ã«ãŒãã¯ãã¯ã©ã€ã¢ã³ããã¢ã¯ã»ã¹ã§ãããªãœãŒã¹ãã¢ã¯ã»ã¹ã®å¢çããµãŒãã«éç¥ããææ®µã§ãããµãŒãã¯ãã®æ å ±ã䜿çšããŠãåäœã調æŽããããããæ£ç¢ºãªã¬ã¹ãã³ã¹ãæäŸãããã§ããŸãã
ãµãŒãã¯ã¯ã©ã€ã¢ã³ãã«ã«ãŒããèŠæ±ã§ããã¯ã©ã€ã¢ã³ãã¯ã«ãŒãã倿Žãããéã«ãµãŒãã«éç¥ã§ããŸãã
ã¯ã©ã€ã¢ã³ãäœææã«ã«ãŒããèšå®ããã«ã¯ãã«ãŒãã®ãªã¹ã (æååã®ãªã¹ããå¯) ãæå®ããããã«ãŒãã®ãªã¹ããè¿ãéåæé¢æ°ã䜿çšããŸãã
from fastmcp import Client
client = Client(
...,
roots=["/path/to/root1", "/path/to/root2"],
)5. ãŠãŒãã£ãªãã£ã¡ãœãã
ã»ping() : æ¥ç¶ã確èªããããã«ãµãŒãã«pingèŠæ±ãéä¿¡ããŸãã
async def check_connection():
async with client:
await client.ping()
print("Server is reachable")6. ãšã©ãŒåŠç
call_tool ãªã¯ãšã¹ãããµãŒãåŽã§ãšã©ãŒãåŒãèµ·ãããå Žå (äŸïŒããŒã«é¢æ°ãäŸå€ãçæããå Žå)ãclient.call_tool() ã¡ãœãã㯠fastmcp.client.ClientError ãçºçãããŸãã
async def safe_call_tool():
async with client:
try:
# ãdivideãããŒã«ãååšãããšä»®å®ããZeroDivisionError ãçºçããå¯èœæ§ããã
result = await client.call_tool("divide", {"a": 10, "b": 0})
print(f"Result: {result}")
except ClientError as e:
print(f"Tool call failed: {e}")
except ConnectionError as e:
print(f"Connection failed: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
# ãŒãé€ç®ãçºçããå Žåã®åºåäŸ:
# ããŒã«ã®åŒã³åºãã«å€±æããŸãã: ãŒãé€ç®ã¯èš±å¯ãããŠããŸããæ¥ç¶å€±æãªã©ã®ãã®ä»ã®ãšã©ãŒã§ã¯ãæšæºã® PythonäŸå€Â (ConnectionErrorãTimeoutError ãªã©) ãçºçããŸãã