The MCP Client is what allows our application code to communicate with the MCP server and access its functionality.
The MCP client consists of two main components:
• MCP Client - A custom class we create to make using the session easier • Client Session - The actual connection to the server (part of the MCP Python SDK)
The client session requires careful resource management - we need to properly clean up connections when we're done.
MCP clients are instantiated by host applications to communicate with particular MCP servers.
• The host application, like Claude.ai or an IDE, manages the overall user experience and coordinates multiple clients. • Each MCP client handles one direct communication with one server.
Understanding the distinction is important: the host is the application users interact with, while clients are the protocol-level components that enable server connections.
We can create a new Python project with uv to implement the MCP client:
$ uv init mcp-client$ cd mcp-client$ uv vent$ source .vent/bin/activate$ uv add mcp anthropic python-dotenv$ rm main.py$ touch client.pyWe need to set the API key to call the LLM:
$ echo"ANTHROPIC_API_KEY=api-key" > .env$ echo".env" >> .gitignoreOur CLI code uses the MCP client to:
• Get a list of available tools to send to agent • Execute tools when agent requests them
Let's setup the MCP client with Python:
import asynciofrom typing importOptionalfrom contextlib import AsyncExitStackfrom mcp import ClientSession, StdioServerParametersfrom mcp.client.stdio import stdio_clientfrom anthropic import Anthropicfrom dotenv import load_dotenvload_dotenv() # load environment variables from .envclass MCPClient:def __init__(self):# Initialize session and client objectsself.session: Optional[ClientSession] = Noneself.exit_stack = AsyncExitStack()self.anthropic = Anthropic()# methods will go hereThe MCPClient class initializes with session management and API clients.
• Uses AsyncExitStackfor proper resource management• Configures the Anthropicclient for interactions
MCP Client workflow
When we submit a query:
1. The MCP client gets the list of available tools from the MCP server 2. Our query is sent to LLM along with tool descriptions 3. LLM decides which tools (if any) to use 4. The MCP client executes any requested tool calls through the MCP server 5. Results are sent back to agent 6. LLM provides a natural language response 7. The response is displayed to us
# Relative pathuv run client.py ./server/weather.py# Absolute pathuv run client.py /Users/username/projects/mcp-server/weather.py# Windows path (either format works)uv run client.py C:/projects/mcp-server/weather.pyuv run client.py C:\\projects\\mcp-server\\weather.pyThe first response might take up to 30 seconds to return, this is normal and happens while:
• The MCP server initializes • LLM processes the query • Tools are being executed
Subsequent responses are typically faster, don’t interrupt the process during this initial waiting period.
If we got an error message "Tool execution failed", we should verify the tool’s required environment variables are set.
Implementing Core Client Functions
We need to implement two essential functions: list_tools() and call_tool().
List Tools function
This list_tools() function gets all available tools from the MCP server:
asyncdeflist_tools(self) -> list[types.Tool]: result = awaitself.session().list_tools()return result.toolsWe access our session (the connection to the server), call the built-in list_tools() method, and return the tools from the result.
Call Tool function
This call_tool() function executes a specific tool on the server:
asyncdefcall_tool( self, tool_name: str, tool_input: dict) -> types.CallToolResult | None:returnawaitself.session().call_tool(tool_name, tool_input)We pass the tool name and input parameters (provided by agent) to the server and return the result.
The MCP client file includes a simple test harness at the bottom. We can run it directly to verify everything works:
$ uv run mcp_client.pyThis will connect to the MCP server and print out the available tools, we should see output showing our tool definitions, including descriptions and input schemas.
We can test the complete flow by running the main application:
$ uv run main.pyWhen we ask a question: "What is the contents of the report.pdf document?", here's what happens behind the scenes:
1. The application uses the client to get available tools 2. These tools are sent to agent along with the question 3. Agent decides to use the read_doc_contentstool4. The application uses the client to execute that tool 5. The result is returned to agent, who then responds to us
The MCP client acts as the bridge between our application logic and the MCP server's functionality, making it easy to integrate powerful tools into the AI workflows.
夜雨聆风