Chapter 9: Connecting to Other Clients


MCP is an open standard, and many applications beyond Claude support it. This chapter covers Cursor (the most widely used AI code editor), other popular clients, and how to build a custom MCP client from scratch using the Python SDK.


Cursor

Cursor is an AI-powered code editor built on VS Code with built-in MCP support. Its configuration format is similar to Claude Desktop.

Configuration File

Create or edit .cursor/mcp.json in your project directory, or ~/.cursor/mcp.json for global configuration:

{
  "mcpServers": {
    "my-server": {
      "command": "python",
      "args": ["/absolute/path/to/server.py"]
    }
  }
}

Remote Server

{
  "mcpServers": {
    "remote-tools": {
      "url": "https://my-mcp-server.example.com/sse"
    }
  }
}

Verifying in Cursor

After adding the config:

  1. Open Cursor settings → MCP (or search for “MCP” in the command palette)
  2. You should see your server listed with a green connection status
  3. In a Cursor chat, you can ask the AI to use tools from your server directly

Windsurf (Codeium)

Windsurf is another AI code editor with MCP support. Configuration lives at:

Format:

{
  "mcpServers": {
    "my-server": {
      "command": "python",
      "args": ["/path/to/server.py"],
      "env": {
        "API_KEY": "your-key"
      }
    }
  }
}

Client Comparison

Client Config Location Relative Paths Remote Servers
Claude Desktop claude_desktop_config.json No Yes (SSE URL)
Claude Code .mcp.json / settings.json Yes (project) Yes (SSE URL)
Cursor .cursor/mcp.json No Yes (SSE URL)
Windsurf mcp_settings.json No Yes (SSE URL)

The configuration structure is similar across all clients: a mcpServers object with named server entries, each specifying either a local command or a remote URL.


Building a Custom MCP Client

The Python SDK includes a low-level client you can use to build your own MCP-powered application. This is useful for:

Basic Client Example

import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def run():
    server_params = StdioServerParameters(
        command="python",
        args=["server.py"],
    )
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()

            # List available tools
            tools = await session.list_tools()
            for tool in tools.tools:
                print(f"Tool: {tool.name}{tool.description}")

            # Call a tool
            result = await session.call_tool(
                "greet", arguments={"name": "Alice"}
            )
            print(result.content[0].text)

asyncio.run(run())

Full example: code/06_full_example.py

Connecting to an HTTP/SSE Server

from mcp.client.sse import sse_client

async def run():
    async with sse_client("https://my-mcp-server.example.com/sse") as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool("my_tool", arguments={})
            print(result)

Building an Agent With MCP

Combining the MCP client with the Claude API creates a simple agent loop:

import anthropic
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def agent_loop(user_message: str):
    client = anthropic.Anthropic()
    server_params = StdioServerParameters(command="python", args=["server.py"])

    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            mcp_tools = await session.list_tools()

            # Convert MCP tools to Anthropic tool format
            tools = [
                {
                    "name": t.name,
                    "description": t.description,
                    "input_schema": t.inputSchema,
                }
                for t in mcp_tools.tools
            ]

            messages = [{"role": "user", "content": user_message}]

            while True:
                response = client.messages.create(
                    model="claude-opus-4-6",
                    max_tokens=4096,
                    tools=tools,
                    messages=messages,
                )
                if response.stop_reason == "end_turn":
                    print(response.content[0].text)
                    break
                # Handle tool use and continue loop
                # (see code/06_full_example.py for the full loop)

This pattern — listing MCP tools, converting them to the LLM’s tool format, and running an agent loop — is the foundation for any MCP-powered agent.


Key Takeaways


← Chapter 8: Connecting to Claude Code Table of Contents Chapter 10: Advanced Topics →