运行你的第一个 MCP Python 客户端:mcp-for-beginners 标准 I/O 客户端实战指南
运行你的第一个 MCP Python 客户端:mcp-for-beginners 标准 I/O 客户端实战指南
本篇指南以 mcp-for-beginners 开源课程中 03-GettingStarted/02-client 一课的 Python 示例为核心,带你从零搭建虚拟环境、安装 mcp[cli] 依赖、启动一个基于 stdio 传输的 MCP 客户端,并连接本地的 FastMCP 服务器完成资源发现、工具调用与结果读取。学完本文,你将掌握编写、运行和验证一个 Python MCP 客户端的最小可行流程,并理解 ClientSession、stdio_client、initialize 等核心 API 的调用关系。
一、示例概览:一个最小可运行的 Python MCP 客户端
该示例位于 03-GettingStarted/02-client/solution/python,包含两个核心文件:
整个示例的目标是:客户端通过标准输入/输出(stdio)管道启动服务器进程,建立会话,依次列出资源、列出工具、读取资源、调用工具,并把结果打印到终端。它是 MCP 客户端编程的"Hello World",适合作为后续集成 LLM 客户端(见 03-llm-client)的起点。
前置条件
- 已安装 Python 3 及
pip; - 官方建议(非必须)安装
uv来管理 Python 环境与依赖,具体说明见 uv 官方文档;不安装 uv 也完全可以按下面的 pip 流程执行。
二、逐步运行:四个命令跑通示例
原文档给出了从环境创建到运行客户端的完整命令序列,下面逐一展开说明。
步骤 0:创建虚拟环境
python -m venv venv
venv 会在当前目录下生成一个隔离的 Python 环境目录,避免依赖污染系统级 Python。这是 Python 项目开发的标准做法,尤其适用于依赖版本敏感的 MCP SDK。
步骤 1:激活虚拟环境
venv\Scripts\activate
注意:示例使用 Windows 风格路径。在 Linux/macOS 下请使用
source venv/bin/activate。
激活后,后续的 pip install 与 python 命令都会指向虚拟环境内的解释器。
步骤 2:安装依赖
pip install "mcp[cli]"
这一条命令安装了两部分内容:
mcp:官方 Python SDK,提供ClientSession、StdioServerParameters、types等客户端构造,以及FastMCP服务端构造;[cli]:额外安装mcp命令行工具(即可执行文件mcp),StdioServerParameters中的command="mcp"正是依赖它来拉起服务器。
步骤 3:运行客户端
python client.py
客户端会自动以子进程方式启动服务器(通过 mcp run server.py),然后执行协议交互。运行成功后,你应该看到与原文档一致的输出:
LISTING RESOURCES
Resource: ('meta', None)
Resource: ('nextCursor', None)
Resource: ('resources', [])
INFO Processing request of type ListToolsRequest server.py:534
LISTING TOOLS
Tool: add
READING RESOURCE
INFO Processing request of type ReadResourceRequest server.py:534
CALL TOOL
INFO Processing request of type CallToolRequest server.py:534
[TextContent(type='text', text='8', annotations=None)]
这段输出清晰展示了客户端的完整生命周期:先列出资源,再列出工具,然后读取 greeting://hello 资源,最后调用 add(1, 7) 工具并拿到 text='8' 的结果。中间的 INFO Processing request of type ... server.py:534 行来自 SDK 内部的请求日志,用于说明当前正在处理的协议消息类型(ListToolsRequest、ReadResourceRequest、CallToolRequest)。
三、客户端代码逐段解析
下面结合 client.py 的完整实现,讲解每一段代码的作用。
3.1 导入所需库
from mcp import ClientSession, StdioServerParameters, types
from mcp.client.stdio import stdio_client
ClientSession:高层会话抽象,提供initialize、list_resources、list_tools、read_resource、call_tool等方法;StdioServerParameters:描述如何以子进程方式启动 MCP 服务器(可执行文件、参数、环境变量);stdio_client:负责建立 stdio 传输通道,返回一对读写流;types:协议类型引用(当前示例中主要用于后续的TextContent等类型)。
3.2 配置服务器启动参数
server_params = StdioServerParameters(
command="mcp", # Executable
args=["run", "server.py"], # Optional command line arguments
env=None, # Optional environment variables
)
这里的关键点:客户端并不直接"连接"一个已在运行的进程,而是声明"如何启动服务器"。
command:要执行的可执行文件,这里是mcp(由pip install "mcp[cli]"提供);args:传给可执行文件的参数,run server.py表示用 MCP CLI 运行当前目录下的 server.py;env:可选的额外环境变量,None表示继承当前进程环境。
3.3 建立会话并初始化
async def run():
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
# Initialize the connection
await session.initialize()
stdio_client(server_params)会按照参数启动子进程服务器,并返回(read, write)两条流;ClientSession(read, write)基于这两条流建立 MCP 会话,其__aenter__会自动完成初始化握手;session.initialize()是显式初始化调用,对应 MCP 协议中的initialize请求,用于协商协议版本与能力。
客户端整体通过 asyncio.run(run()) 驱动,这是 mcp Python SDK 基于异步模型设计的标准用法。
3.4 列出资源与工具
resources = await session.list_resources()
print("LISTING RESOURCES")
for resource in resources:
print("Resource: ", resource)
tools = await session.list_tools()
print("LISTING TOOLS")
for tool in tools.tools:
print("Tool: ", tool.name)
list_resources() 返回服务器声明的资源列表;list_tools() 返回 ListToolsResult,其中 tools 字段包含每个工具的名称、描述与输入 schema。在本示例中,服务器通过 @mcp.tool() 暴露了 add 工具,因此输出中可以看到 Tool: add。
3.5 读取资源与调用工具
print("READING RESOURCE")
content, mime_type = await session.read_resource("greeting://hello")
print("CALL TOOL")
result = await session.call_tool("add", arguments={"a": 1, "b": 7})
print(result.content)
read_resource("greeting://hello"):按 URI 读取资源,URI 模板中的{name}会被替换为hello;call_tool("add", arguments={"a": 1, "b": 7}):按名称调用工具并传入参数,返回结果对象;result.content中是TextContent(type='text', text='8', ...)列表,即1 + 7的运算结果。
四、配套服务器端实现
客户端所交互的服务器在 server.py 中定义,它是用 FastMCP 构建的最小示例:
# server.py
from mcp.server.fastmcp import FastMCP
# Create an MCP server
mcp = FastMCP("Demo")
# Add an addition tool
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
# Add a dynamic greeting resource
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
"""Get a personalized greeting"""
return f"Hello, {name}!"
# Main execution block - this is required to run the server
if __name__ == "__main__":
mcp.run()
解读:
FastMCP("Demo")创建名为 "Demo" 的 MCP 服务器;@mcp.tool()装饰器把add(a, b)注册为工具,docstring 即工具描述;@mcp.resource("greeting://{name}")注册一个带 URI 模板的动态资源,模板参数{name}在客户端读取greeting://hello时被解析为name="hello",返回Hello, hello!;mcp.run()是服务器启动入口;客户端通过mcp run server.py以子进程方式把它拉起,双方通过 stdio 管道交换 JSON-RPC 消息。
同样风格的服务器也出现在前一课 01-first-server 的 Python 方案中(后者额外提供了 subtract 工具),可与客户端代码配合扩展练习。
五、客户端与服务器的传输原理:stdio 到底发生了什么
从客户端视角看,整个通信链路可以归纳为:
stdio_client(server_params)根据command与args启动服务器子进程;- 客户端把协议消息(JSON-RPC 格式,如
initialize、tools/list、resources/read、tools/call)写入服务器子进程的 stdin; - 服务器把响应写入自己的 stdout,由客户端从
read流中读取; ClientSession负责把底层字节流包装成结构化请求/响应,并维护会话状态。
这也是为什么服务器代码里的 INFO Processing request of type ListToolsRequest server.py:534 日志会出现在客户端输出中间——它们来自服务器进程的 stderr/stdout,被客户端终端一并捕获展示。stdio 是本地 MCP 通信的推荐传输方式(详见课程 05-stdio-server),它天然具备进程隔离与安全边界;若需远程通信,后续课程还会介绍 HTTP Streaming(06-http-streaming)等传输方案。
六、进阶参考:更完整的客户端示例
如果希望看到覆盖错误处理、结果解析与多工具调用的完整客户端实现,仓库在 03-GettingStarted/02-client/client_example_python.py 提供了 MCPCalculatorClient 类,其亮点包括:
- 通过
try/except包裹会话建立、工具列表、工具调用与资源读取,打印清晰的成功/失败信息; - 内置
extract_text_result()辅助方法,从result.content中稳健提取文本结果(兼容hasattr探测与type == "text"判断,异常时回退为"No result"); - 一次性演示
add、subtract、multiply、divide、help五类工具调用,并读取第一个可用资源。
该示例的配套说明位于 03-GettingStarted/02-client/complete_examples.md,并提供了 C#、Java、TypeScript、Rust 的多语言对照版本(如 client_example_csharp.cs、client_example_java.java),便于跨语言理解同一协议行为。
七、小结与下一步
通过本文,你已经完整掌握了在 mcp-for-beginners 课程中运行 Python MCP 客户端的最小闭环:
- 用
python -m venv venv与pip install "mcp[cli]"搭建隔离环境; - 用
python client.py一行命令启动"客户端 + 子进程服务器"的完整链路; - 理解
initialize→list_resources→list_tools→read_resource→call_tool的协议调用序列; - 知道如何在 client.py 与 server.py 之间对应查找功能实现。
客户端既可以像本例一样在启动时自行拉起服务器,也可以连接到已运行的服务器进程,是测试服务器能力的有效手段(与前一课介绍的 MCP Inspector 互为补充)。下一步建议学习 03-llm-client:为客户端接入大语言模型,让 LLM 代替手工指定工具调用,实现"智能协商"式的自动化交互。