本笔记回答一个问题:从零开始怎么写一个MCP工具,第一步干嘛,第二步干嘛,写完之后怎么用?
〇、先建立一句话的心智模型
MCP = 用JSON-RPC消息告诉AI:"我这里有哪些工具、干什么用的、怎么调用",再由AI决定调不调,何时调用。
你写的东西本质是一个"函数清单 + 函数本体",剩下的(协议、传输、握手)全是现成的轮子,MCP框架都帮你写好了。所以写一个MCP工具 == 写几个普通Python函数,加装饰器,完事了。MCP有三种传输方式,怎么选择见下表:
| 场景 | 选什么 |
|---|---|
| 本机自用(接 Claude Code、IDE) | stdio(默认,最简单) |
| 给别人远程用 / 部署到服务器 | Streamable HTTP |
| 老教程里的 SSE | 别用,已弃用 |
一、准备环境
推荐用 uv(Astral 出品的新一代包管理器,官方教程默认用它):
bash
# 1. 安装 uv(只需一次,之后所有项目通用)
pip install uv
# 2. 建项目并进入
mkdir my-mcp-tool
cd my-mcp-tool
# 3. 初始化项目
uv init
uv venv
# 4. 安装官方 SDK(写入 pyproject.toml 并装进 .venv)
# [cli] 是可选依赖组,第三步的 `mcp dev` 调试命令需要它
uv add "mcp[cli]"
二、写服务端
新建 server.py,这是完整可运行的最小例子:
python
# server.py
from mcp.server.mcpserver import MCPServer
mcp = MCPServer("my-first-tool")
@mcp.tool()
def add(a: float, b: float) -> str:
"""把两个数字相加,返回结果""" # ← docstring 会原样给 AI 看,务必写清楚
return f"{a} + {b} = {a + b}"
if __name__ == "__main__":
mcp.run() # 默认 stdio 传输:等客户端来启动我
| 代码 | 作用 |
|---|---|
MCPServer("名字") |
给你的工具集起名,会出现在握手的 serverInfo 里 |
@mcp.tool() |
把函数注册为工具;函数签名自动变成参数 schema(类型注解是契约,别乱写) |
| docstring | 工具描述,AI 靠它判断"什么时候该调我这个工具"------写得越具体,AI 用得越准 |
mcp.run() |
启动 stdio 通道,阻塞等待消息(终端里"卡住"是正常的,它在等人对话) |
写工具的三条纪律:
- **docstring 认真写:**它不是给你看的,是给 AI 看的"使用说明书"。
- 类型注解要准确:
a: float会被生成参数 schema,AI 按它传参,写错了 AI 就传错。 - 绝不在工具里
print(...):因为stdio 模式下 stdout 是协议通道,print 会污染消息流。要输出日志用print(..., file=sys.stderr)或 logging。
三、测试
① 方法A:用MCP库自带的Inspector官方调试器,最推荐
bash
uv run mcp dev server.py
浏览器会打开一个界面,可以点按钮握手、列工具、填参数调用。不用写一行客户端代码。


Inspector 两个常见困惑
-
没有"添加 Server"按钮 :
mcp dev启动的 Inspector 是只读模式(页面顶部会显示 "Read-only session"),只能看不能改。想手动添加
server、自己填命令,用不带参数的方式独立启动:
npx @modelcontextprotocol/inspector@latest -
默认的 uv 命令 Failed / Connection closed :
mcp dev生成的启动命令假设你用 uv。没装 uv 就会这样------不是你的 server 写错了。
装 uv 或换自己的命令即可(见第一步的注意事项)。
② 方法B:写一个客户端自己去调,这个不推荐
四、接入真实的AI客户端使用
工具写好是给 AI 用的。以 Claude Code 为例(也适用于 Claude Desktop、Cursor 等,格式大同小异)。
在项目根目录新建.mcp.json文件写入:
bash
{
"mcpServers": {
"my-tool": {
"command": "e:\\my-mcp-tool\\.venv\\Scripts\\python.exe",
"args": ["e:\\my-mcp-tool\\server.py"]
}
}
}
要点:
command用 venv 里的 python 绝对路径 ,别写python(会找不到 mcp 包)- Windows 路径在 JSON 里要写双反斜杠
\\ - 重启 Claude Code 后,输入
/mcp可以看到连接状态
如果是远程工具(HTTP 版),server 端改成**mcp.run(transport="streamable-http")**,客户端配置改成:
bash
{
"mcpServers": {
"my-tool": {
"type": "http",
"url": "http://your-server:8000/mcp"
}
}
}
三种传输只是这条消息走的管道不同:stdio 走子进程 stdin/stdout;Streamable HTTP 走 HTTP POST(响应可为 SSE 流)。