MCP Client Quick Start
Table of Contents generated with DocToc
MCP Client Quick Start
Background
Usually, compared with MCP Server, MCP Client will be more complex. Here’s a breakdown to clarify why:
MCP Server: Lightweight and Declarative
- Purpose: Exposes and hosts tools with metadata in a standardized format.
- Responsibilities:
- Serves tool definitions
- Provides a registry of capabilities (e.g. tool name, input/output schema).
- Key Traits:
- Stateless, read-only, low logic overhead.
- No need to interact with LLMs or orchestrate execution.
MCP Client: Smart, Interactive, LLM-Aware
- Purpose: Acts as the bridge between the LLM and tool invocation infrastructure.
- Responsibilities:
- Queries the MCP Server to fetch tool metadata.
- Integrates with an LLM, providing it with the tool schema in MCP format.
- Parses LLM outputs to detect tool invocations (tool_use events).
- Calls tools using the provided input schema and passes results back to the LLM.
- Optionally manages conversational context, error handling, fallbacks, and multi-step toolchains.
- Key Traits:
- Stateful, interactive, and logic-heavy.
- Needs tight coordination between LLM-generated plans and real-world tool APIs.
MCP Client Creation Tutorial
Set Up Virtual Env
I was using virtual environment for this tutorial, you can use either uv
or python venv
.
After virtual environment created, create a new file named as client.py
.
Set Up API Key
We are going to use Anthropic for this client, so get your Anthropic API Key from here first and put it to a .env
file.
ANTHROPIC_API_KEY=sk-ant-xxx
Create Basic Client
First, let’s set up our imports and create the basic client class. It will include the following:
- The MCPClient class initializes with session management and API clients
- Uses AsyncExitStack for proper resource management
- Configures the Anthropic client for Claude interactions
import asyncio
from typing import Optional
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from anthropic import Anthropic
from dotenv import load_dotenv
load_dotenv() # load environment variables from .env
class MCPClient:
def __init__(self):
# Initialize session and client objects
self.session: Optional[ClientSession] = None
self.exit_stack = AsyncExitStack()
self.anthropic = Anthropic()
# methods will go here
Create Server Connection
The following code is about how to build connections with an existing MCP Server:
- Validates server script type, support both Python and JS
- Sets up proper communication channels
- Initializes the session and lists available tools
async def connect_to_server(self, server_script_path: str):
"""Connect to an MCP server
Args:
server_script_path: Path to the server script (.py or .js)
"""
is_python = server_script_path.endswith('.py')
is_js = server_script_path.endswith('.js')
if not (is_python or is_js):
raise ValueError("Server script must be a .py or .js file")
command = "python" if is_python else "node"
server_params = StdioServerParameters(
command=command,
args=[server_script_path],
env=None
)
stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
self.stdio, self.write = stdio_transport
self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
await self.session.initialize()
# List available tools
response = await self.session.list_tools()
tools = response.tools
print("\nConnected to server with tools:", [tool.name for tool in tools])
Create Query Processing Logic
This is the key part of the MCP Client, this will be used for processing natural language queries and handling tool calls:
- Sending it to the Claude AI model (Claude 3.5 Sonnet)
- Detecting if any tools are needed to answer the question
- Calling those tools if requested
- Sending the results back to Claude for further reasoning
- Repeating this process if needed
- Finally, returning the composed answer
async def process_query(self, query: str) -> str:
"""Process a query using Claude and available tools"""
messages = [
{
"role": "user",
"content": query
}
]
response = await self.session.list_tools()
available_tools = [{
"name": tool.name,
"description": tool.description,
"input_schema": tool.inputSchema
} for tool in response.tools]
# Initial Claude API call
response = self.anthropic.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1000,
messages=messages,
tools=available_tools
)
# Process response and handle tool calls
final_text = []
assistant_message_content = []
for content in response.content:
if content.type == 'text':
final_text.append(content.text)
assistant_message_content.append(content)
elif content.type == 'tool_use':
tool_name = content.name
tool_args = content.input
# Execute tool call
result = await self.session.call_tool(tool_name, tool_args)
final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")
assistant_message_content.append(content)
messages.append({
"role": "assistant",
"content": assistant_message_content
})
messages.append({
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": content.id,
"content": result.content
}
]
})
# Get next response from Claude
response = self.anthropic.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1000,
messages=messages,
tools=available_tools
)
final_text.append(response.content[0].text)
return "\n".join(final_text)
You can get the full example from here.
Run this MCP Client
You can run this program with any MCP Server that you have as below:
python client.py path/to/server.py
For my case, I was running this client against my weather.py
at here. And weather.py
was located at `` on my local host.
mcp-client % python client.py /Users/gyliu513/github.com/gyliu513/langX101/mcp/weather/weather.py
[04/21/25 09:45:57] INFO Processing request of type ListToolsRequest server.py:534
Connected to server with tools: ['get_alerts', 'get_forecast']
MCP Client Started!
Type your queries or 'quit' to exit.
Query:
Sequence Chart for E2E
The following is the full sequence chart for the e2e use case here.
sequenceDiagram
participant User
participant MCPClient
participant MCPServer
participant ClaudeLLM
participant WeatherService
User->>MCPClient: Enter query
MCPClient->>MCPServer: Request available tool list
MCPServer-->>MCPClient: Return list of available tools
MCPClient->>ClaudeLLM: Send user query + tool metadata
ClaudeLLM->>ClaudeLLM: Analysis available tools
ClaudeLLM-->>MCPClient: Return response (may include tool call)
alt Tool call requested
MCPClient->>MCPServer: Execute weather tool
MCPServer->>WeatherService: Retrieve weather from weather service
WeatherService-->>MCPServer: Return weather info
MCPServer-->>MCPClient: Return weather tool result
MCPClient->>ClaudeLLM: Send weather tool result to ClaudLLM
ClaudeLLM-->>MCPClient: Return follow-up response from ClaudLLM
end
MCPClient->>User: Display combined response