用 Python 写一个能连上的 MCP Server:本地搭建与验证步骤
MCP Server 搭建的核心不是把服务跑起来,而是让 MCP 客户端能通过约定的传输方式连上它、完成初始化握手、正确列出工具。
本文用 Python 写一个最小可用的 MCP Server,走 stdio 传输,接入 Claude Desktop 验证;
如果你卡在连接失败,文中也给出定位顺序。
先明确传输方式和运行环境
MCP Server 支持多种传输方式,本地开发最省事的是 stdio:客户端把 Server 当作子进程启动,通过标准输入输出收发 JSON-RPC 消息。
这意味着两件事:
- Server 进程不能往 stdout 打印调试信息,否则会污染协议消息流;日志一律走 stderr。
- 客户端配置里写的启动命令必须能在它自己的工作目录下执行成功,路径和环境变量要写绝对路径。
本文环境:Python 3.10 及以上,使用官方 mcp Python SDK。
如果机器上同时有多个 Python 版本,后面所有命令都用 python3 -m 形式,避免 pip 和 python 指向不同解释器。
安装 SDK 并写一个最小 Server
先建独立虚拟环境,把依赖装干净:
python3 -m venv mcp-venv
source mcp-venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install mcp
新建文件 server.py,实现一个加法工具和一个问候资源。
工具函数是客户端真正会调用的部分,参数类型要写清楚:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("demo-server")
@mcp.tool()
def add(a: int, b: int) -> int:
"""返回两个整数之和"""
return a + b
@mcp.resource("greeting://hello")
def hello() -> str:
"""返回一句问候语"""
return "hello from mcp server"
if __name__ == "__main__":
mcp.run(transport="stdio")
这里没有写任何 print 语句。
如果后续要加日志,用 logging 并显式输出到 sys.stderr,不要用默认 stdout 输出。
用 inspect 先验证服务本身
在接客户端之前,先确认 Server 自己能正常响应工具列表。
SDK 自带开发调试命令:
mcp dev server.py
该命令会启动一个本地调试入口,浏览器打开它给出的地址,能看到 add 工具和 greeting://hello 资源。
在调试界面里手动调用一次 add,参数传 1 和 2,应返回 3。
这一步能通过,说明协议层、工具注册和参数解析都没问题,剩下的失败基本都出在客户端配置或启动环境上。
接入 Claude Desktop 并确认连接状态
在 Claude Desktop 的配置文件中加入这段服务定义。
macOS 路径通常是 ~/Library/Application Support/Claude/claude_desktop_config.json,Windows 是 %APPDATA%\Claude\claude_desktop_config.json:
{
"mcpServers": {
"demo-server": {
"command": "/绝对路径/mcp-venv/bin/python3",
"args": ["/绝对路径/server.py"]
}
}
}
两个绝对路径必须写全:command 指向虚拟环境里的解释器,args 指向脚本本身。
写 python3 这种相对命令时,客户端可能找不到虚拟环境里的依赖。
保存后完全退出 Claude Desktop 再重新打开,在对话输入框的工具图标或设置里的 MCP 服务列表查看 demo-server 是否显示为已连接。
连接成功后,直接提问“用 add 工具算 1 加 2”,观察返回结果是否为 3。
连不上时按这个顺序排查
客户端显示未连接或工具列表为空,按下面顺序查,不要一上来改代码:
- 在终端手动执行配置里的
command和args组合,看进程是否直接退出并打印异常。最常见的报错是ModuleNotFoundError: No module named 'mcp',说明客户端调用的 Python 不是装了依赖的那个。 - 检查
server.py里是否有print或第三方库往 stdout 写内容。stdio 模式下 stdout 只允许走 JSON-RPC,任何多余输出都会让客户端解析失败。 - 查看 Claude Desktop 的 MCP 日志,路径在 macOS 的
~/Library/Logs/Claude/mcp*.log,Windows 的%APPDATA%\Claude\logs下。日志里出现Unexpected token或 JSON 解析错误,优先回查上一条。 - 确认配置 JSON 本身合法,末尾不要多逗号,
mcpServers拼写正确。改完配置必须完全重启客户端,只关窗口不算。
什么时候需要换传输方式
stdio 适合本地单机调试和桌面客户端接入。
如果 Server 要部署在远程服务器、供多个客户端同时连接,或者客户端本身不支持拉起子进程,就需要改用 HTTP 类传输,并在服务端处理端口、鉴权和会话管理。
这类场景的配置复杂度明显高于本地 stdio,建议先把本地链路跑通,再迁移部署。
常见疑问:
工具写好了但客户端列表里没有,先看服务是否显示已连接,再看工具函数是否真的带上了 @mcp.tool() 装饰器,最后用 mcp dev 复核。
改完代码需要重启客户端吗?
stdio 模式下 Server 是客户端启动的子进程,改代码后要重启客户端才能加载新进程。
能不能用系统 Python 直接跑?
可以,但依赖必须装在同一个解释器里;
用虚拟环境并写绝对路径是更稳的做法。