用 Python 从零写一个能跑起来的 MCP Server

MCP Server 不是一套需要先部署的中间件,而是一个遵循 Model Context Protocol 的普通进程:它通过标准输入输出(stdio)或 HTTP 与客户端交换 JSON-RPC 消息,向模型暴露工具、资源或提示词。
想跑通第一个 MCP Server,最短路径是用 Python 写一个只提供单个工具的 stdio 服务,然后让客户端连上它验证 tools/list 和 tools/call 是否正常返回。

下面的做法适合本地开发和学习协议,前提是本机已安装 Python 3.10 及以上版本,并且你能修改客户端的配置文件。

先弄清通信方式,再动手写代码

stdio 模式下,客户端启动你的脚本,双方通过管道通信。
每条消息是一行 JSON,包含 jsonrpc、id、method、params 等字段。
这意味着两点:

  • 你的程序不能往标准输出打印日志或调试信息,否则会污染协议流,客户端直接解析失败。调试信息请写到 stderr。
  • 服务端要循环读取输入,逐行解析并响应,不能启动后就退出。

一个最小可用的服务端至少要响应三个请求:initialize(握手并声明能力)、tools/list(返回工具清单)、tools/call(执行具体工具)。
客户端发来的通知类消息(没有 id 字段)只需处理,不必返回结果。

写一个只含单个工具的服务端

安装官方 Python SDK 可以省去手写协议细节:

pip install mcp

创建 server.py,注册一个把摄氏度换算为华氏度的工具:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("demo-server")

@mcp.tool()
def c_to_f(celsius: float) -> float:
    """把摄氏度转换为华氏度"""
    return celsius * 9 / 5 + 32

if __name__ == "__main__":
    mcp.run()

FastMCP 默认使用 stdio 传输。
函数名、参数类型和 docstring 会被自动转换成工具描述,模型正是依靠这些信息判断何时调用工具。
参数类型尽量写明确,float 比不加注解更容易让模型传对值。

把服务端接入客户端

以 Claude Desktop 为例,
编辑其配置文件(macOS 通常在 ~/Library/Application Support/Claude/claude_desktop_config.json,
Windows 在 %APPDATA%\Claude\claude_desktop_config.json),
加入:

{
  "mcpServers": {
    "demo": {
      "command": "python",
      "args": ["/绝对路径/server.py"]
    }
  }
}

保存后完全退出并重启客户端。
路径必须写绝对路径,用相对路径时客户端的工作目录和你终端里的不同,会找不到脚本。
如果 Python 装在虚拟环境里,command 要指向该环境的解释器,例如 /path/to/venv/bin/python。

常见连不上的原因

服务端在客户端里显示为不可用,多数不是协议写错,而是下面几类问题:

  • 脚本一启动就报错。先用 python server.py 手动运行,确认没有语法错误或缺少依赖;stdio 服务启动后会静默等待输入,这是正常现象。
  • 标准输出被日志污染。检查代码里有没有 print,第三方库也可能默认输出日志,必要时把日志重定向到 stderr。
  • 依赖不在客户端使用的环境里。客户端启动的是你配置的那个解释器,不是你当前 shell 的 python。用绝对路径的解释器执行一次 pip show mcp 确认。
  • 改了配置没重启。多数客户端只在启动时读取 MCP 配置。

验证工具是否真的被调用

连接成功后,在对话中提出一个需要换算的问题,观察客户端是否弹出工具调用提示。
也可以直接测试协议层:向脚本的标准输入逐行写入请求并查看输出,握手请求大致如下:

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0.1"}}}

注意协议版本会随规范更新,实际值应以你安装的 SDK 文档为准,不要照抄后当作所有版本的通用值。
若返回中包含 serverInfo 和 capabilities,说明握手正常;
再发 tools/list 应能看到 c_to_f 及其参数结构。

工具能列出但调用报错,优先检查参数名是否与函数签名一致、类型是否能被转换,以及工具内部是否有未捕获异常。
把异常信息写到 stderr,比在客户端界面上猜原因快得多。

从示例走向可用服务

示例跑通后,扩展方向通常是增加工具数量、接入真实数据源、改用 HTTP 传输供多客户端共享。
工具越多,越要注意每个工具的职责边界和参数校验:模型会按描述选择工具,描述含糊或功能重叠时容易调错。
涉及文件写入、外部请求等有副作用的操作,建议在工具内部做权限检查和输入限制,而不是完全依赖模型的判断。

对第一次开发 MCP Server 的人来说,判断是否成功的标准很简单:客户端能列出你的工具,模型能在合适的问题上选中它,调用结果能正确回到对话里。
这三点都通过,再考虑部署和扩展。

分享到:
上一篇
Agent人工确认配置:关键操作前插入人工审核的落地方法
下一篇
用 Python 编写一个能连接 MCP Server 的客户端
1
系统公告

关于香港云优质服务器IP地址更换的通知

尊敬的客户: 因原IP运营商大幅涨价,为避免成本转嫁至用户,我司决定更换IP服务商。本次更换后,您的续费价格将保持不变,且新IP网络质量与服务标准不受影响。新IP已分配至后台,即刻生效(更换的新IP都是香港原生IPv4)。 涉及 IP 段: 45.192.x.x 旧 IP 停用时间: 2026 年 12 月 1 日
服务中心
客服
在线客服
24小时为您服务
咨询
联系我们
联系我们,为您的业务提供专属服务。
24/7 技术支持
如果您遇到寻求进一步的帮助,请过工单与我们进行联系。
24/7 即时支持
泽御云
售前客服
泽御云
泽御云
售后客服
泽御云
技术支持
评价
您对当前页面的整体感受是否满意?
😞
非常不满意
😕
不满意
😐
一般
🙂
满意
😊
非常满意