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 is an AI-powered code editor built on VS Code with built-in MCP support. Its configuration format is similar to Claude Desktop.
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"]
}
}
}
{
"mcpServers": {
"remote-tools": {
"url": "https://my-mcp-server.example.com/sse"
}
}
}
After adding the config:
Windsurf is another AI code editor with MCP support. Configuration lives at:
~/.codeium/windsurf/mcp_settings.jsonFormat:
{
"mcpServers": {
"my-server": {
"command": "python",
"args": ["/path/to/server.py"],
"env": {
"API_KEY": "your-key"
}
}
}
}
| 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.
The Python SDK includes a low-level client you can use to build your own MCP-powered application. This is useful for:
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
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)
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.
.cursor/mcp.json with the same mcpServers format as other clientsClientSession for building custom MCP clients| ← Chapter 8: Connecting to Claude Code | Table of Contents | Chapter 10: Advanced Topics → |