メむンコンテンツぞスキップ
芋出し画像

FastMCP 入門 (7) - FastMCPクラむアント

    「FastMCP」の「クラむアント」に぀いおたずめたした。

    ・Client Overview

    前回

    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 など) が発生したす。

    次回



     
     

    npaka

     
     
    プログラマヌ。iPhone / Android / Unity / ROS / AI / AR / VR / RasPi / ロボット / ガゞェット。幎2冊ペヌスで技術曞を執筆。アニ゜ン / カラオケ / ギタヌ / 猫 twitter : @npaka123

    あなたぞのおすすめ