mcp_client_example.py

script

← Back to skill

Content hash: 061107c4ad0bc9bc831f03db611a8bfd303e1c5bd5fb2ee52977add0cb40f36a
#!/usr/bin/env python3
"""Minimal MCP client: connect to a server, list tools, call one.

Uses the mcp Python SDK. Run against any MCP server (stdio or HTTP).
"""

from __future__ import annotations

import asyncio
import sys

try:
    from mcp import ClientSession, StdioServerParameters
    from mcp.client.stdio import stdio_client
except ImportError:
    print("Install: pip install mcp", file=sys.stderr)
    sys.exit(1)


async def connect_and_explore(command: str, args: list[str]) -> None:
    """Connect over stdio, initialize, list tools, call one."""
    params = StdioServerParameters(command=command, args=args)

    async with stdio_client(params) as (read_stream, write_stream):
        async with ClientSession(read_stream, write_stream) as session:
            # 1. Initialize handshake (REQUIRED before any discovery)
            init_result = await session.initialize()
            print(f"Connected. Server: {init_result.server_info.name}")
            print(f"Protocol version: {init_result.protocol_version}")

            # 2. Discover tools (with pagination support)
            tools_result = await session.list_tools()
            tools = tools_result.tools
            print(f"\nTools ({len(tools)}):")
            for t in tools:
                sig = ", ".join(
                    f"{k}: {v.get('type','?')}" for k, v in t.inputSchema.get("properties", {}).items()
                )
                print(f"  • {t.name}({sig}) — {t.description[:60]}")

            # Check for cursor-based pagination
            next_cursor = getattr(tools_result, "nextCursor", None)
            while next_cursor:
                more = await session.list_tools(cursor=next_cursor)
                for t in more.tools:
                    print(f"  • {t.name} (paginated)")
                next_cursor = getattr(more, "nextCursor", None)

            # 3. Call the first tool (if any exist)
            if tools:
                first = tools[0]
                print(f"\n=== Calling: {first.name} ===")
                result = await session.call_tool(first.name, arguments={})
                for item in result.content:
                    if hasattr(item, "text"):
                        print(f"  text: {item.text[:200]}")
                    elif hasattr(item, "type") and item.type == "image":
                        print(f"  [image: {len(item.data)} bytes]")
                    else:
                        print(f"  [content: {item}]")

            print("\n=== Done - session closed ===")


async def main() -> None:
    if len(sys.argv) < 2:
        print("Usage: python mcp_client_example.py <command> [args...]")
        print("Example: python mcp_client_example.py python server.py")
        sys.exit(2)

    command = sys.argv[1]
    args = sys.argv[2:]
    try:
        async with asyncio.timeout(30):
            await connect_and_explore(command, args)
    except TimeoutError:
        print("Timeout: server may not be responding", file=sys.stderr)
    except Exception as e:
        print(f"Error: {e}", file=sys.stderr)


if __name__ == "__main__":
    asyncio.run(main())