MCP 实战第一课:一个协议,通吃所有 Agent 工具
生命不息,折腾不止。这一篇开个新坑:MCP 实战系列第一课,先把「MCP 到底解决什么问题、它凭啥不是又一个 API 规范」讲透,再手把手跑通你人生中第一个 MCP Server,让任意 Agent 客户端都能调用你本地暴露出来的工具。
前面折腾 DeepSeek Harness 的时候,我在十几篇里反复提到一个词:MCP。装插件接 MCP、给 Claude Code 接 MCP 外接工具……但每次都只用了它的一小块,从没停下来认真问一句:这东西到底是个啥,为什么现在 Claude、Cursor、DeepSeek Harness、各种 Agent 框架都在抢着认它?
今天把这个问题彻底掰开,然后自己动手写一个,跑通为止。看完这篇你会发现,MCP 真不是又一个「会过时的 API 规范」,而是给 AI 工具定了根「USB-C 线」。
一、先搞清楚:MCP 在解决「N×M 对接地狱」
先看没有 MCP 的时候,世界长什么样。
你有 N 个 AI 客户端:Claude Desktop、Claude Code、Cursor、dsh、各种自研 Agent……每个都挺能干,但都「手不够长」,碰不到你本地的东西。你又有 M 个数据源和工具:自己的笔记、数据库、内部 API、GitHub、飞书文档……
按老办法,你得给「每个客户端 × 每个数据源」写一套对接:Claude Code 接数据库写一遍,Cursor 接同一个数据库再写一遍,dsh 接还得写第三遍。这就是 N×M 爆炸——5 个客户端 × 5 个数据源 = 25 套适配代码,还各自维护、各自踩坑。
MCP(Model Context Protocol,模型上下文协议)干的事就是把这 25 套压缩成 N + M:你只要为「每个数据源」写一次 MCP Server,所有支持 MCP 的客户端就都能发现它、调用它。写一遍,处处可用。
打个比方:MCP 之于 AI 工具,就像 USB-C 之于各种设备——以前每台设备一种接口一堆线,现在一个口通吃。它由 Anthropic 在 2024 年 11 月开源,现在已经被捐给了 Linux Foundation 旗下的 Agentic AI Foundation(Anthropic、Block、OpenAI 共同发起,Google、微软、AWS、Cloudflare、Bloomberg 背书),成了中立的公共标准。这也是为什么「大厂都在认它」——因为没人想再回到 N×M 的时代。
二、它凭啥不是「又一个 API 规范」?三个原语 + 一个设计哲学
这是最关键的一节。很多人第一眼看 MCP,觉得「不就是个 RPC 协议嘛,跟 OpenAPI/gRPC 有啥区别」。区别大了。
先记住:MCP 底层确实是 JSON-RPC 2.0,这点没跑。但 MCP 真正值钱的是它定义的那套「给模型看」的语义层。它只规定了三种服务器可以暴露的原语(primitive):
| 原语 | 干的事 | 类比 REST | 一句话 |
|---|---|---|---|
| 工具 Tools | 可执行的函数,模型可以调用来做动作 | POST 端点 | 「能干点啥」 |
| 资源 Resources | 只读数据,按 URI 暴露给模型当上下文 | GET 端点 | 「能看点啥」 |
| 提示词 Prompts | 可复用的模板,帮模型组织交互 | 模板 | 「怎么开场」 |
看出来了吗——它确实像 REST,但每个字段都是为「模型」设计的,不是为「人写代码」设计的:
- 工具的 description 是写给模型看的。模型靠读你写的 docstring 决定「这个工具是不是我要的、该怎么传参」,而不是靠人肉读 API 文档。你写「Add two integers」,模型就知道这是加法。
- inputSchema 自动从类型标注生成。你的 Python 函数
def add(a: int, b: int),int类型标注直接变成 JSON Schema,模型照着 schema 传参,不会传错类型。 - 模型自己「发现」而不是人写死。客户端连上来先
tools/list问「你有哪些工具」,拿到清单后模型再决定调哪个、传什么。这套「发现 → 调用」是动态的,服务端加个新工具,客户端下次就能看到。
所以一句话:OpenAPI 是给「人」对接用的,MCP 是给「模型」对接用的。 这才是它「不是又一个 API 规范」的底气——它解决的不是「机器怎么通信」,而是「模型怎么安全、标准地拿到上下文和动手能力」。
三、三块积木 + 一个架构,架构先看明白再动手
动手前花两分钟看清架构,后面不迷糊。MCP 是典型的 客户端-服务器 结构,三个角色:
- 宿主(Host):你正在用的 AI 应用,比如 Claude Desktop、Claude Code、Cursor、dsh 的 Web 界面。它负责跑模型、管权限。
- 客户端(Client):宿主内部为「每个 MCP Server」单独开的一条连接,一个 Server 一个 Client,隔离得干干净净。
- 服务器(Server):你自己写的、暴露工具/资源/提示词的那个进程。
连接方式(传输层)在实战里就两种,够用了:
- stdio:把 Server 当成本地子进程跑,通过标准输入输出对话。桌面客户端(Claude Desktop)就是这么拉起它的。
- Streamable HTTP:把 Server 跑成一个 HTTP 端口,给远程/多客户端用。要联网、要多端共享,用这个。
再记一个点:MCP 的版本号是日期式的,当前最新协议修订是 2026-07-28。SDK 会自动协商版本,你不用手动管,但看到这个日期别慌,知道它是「今天最新的协议版」就行。
四、动手:10 分钟跑通你的第一个 MCP Server
理论够了,上代码。环境要求:Python 3.10+,装包用 uv 或 pip 都行。全程本地跑,不需要 API key、不需要指定聊天 App。
第 1 步:装 SDK(带 CLI 工具)
1 | pip install "mcp[cli]" |
[cli] 这个 extra 会顺带装上 mcp 命令行工具(后面 mcp dev、mcp run、mcp install 全靠它)。装完可以先看一眼版本确认装对了:
1 | mcp version |
第 2 步:写 server.py,三个原语(工具 / 资源 / 提示词)各来一个,一次看全:
1 | from mcp.server import MCPServer |
注意你没写的东西:没有 JSON Schema、没有请求解析、没有协议握手。你的类型标注自动变成工具的 inputSchema,docstring 自动变成模型读的描述。这就是 SDK 存在的意义——两个装饰器 + 一段 docstring,就是一个完整的接入面。
第 3 步:用 Inspector 验证它真的能跑
1 | mcp dev server.py |
这条命令会把你的文件当 stdio 子进程拉起来,并开一个网页版 MCP Inspector。在里面你能:列出所有工具、查看自动生成的 schema、给 add 传参调一把、读取 greeting 资源。全程没让你指定端口——因为 stdio 模式下本来就没有端口,标准输入输出就是那根线。
第 4 步:注册给宿主,让它每次都自动拉起
想让 Claude Desktop 每次对话都带着这个 Server,一条命令:
1 | mcp install server.py --name "Demo" |
其它宿主也认同一套东西:Claude Code 用 claude mcp add、Cursor / VS Code 在各自的 MCP 配置里填同样的启动命令,只是格式各项目自己定。核心就一句:你写的是 MCP Server,谁都能接。
第 5 步(可选):同一个 Server,一行改成远程 HTTP
代码一个字不动,只改最后一行,就能从本地子进程变成 HTTP 服务:
1 | if __name__ == "__main__": |
客户端连 http://127.0.0.1:3001/mcp。也可以不改代码,直接让 CLI 帮跑:
1 | mcp run server.py --transport streamable-http |
stdio 和 HTTP 是同一个 Server 的两种「接线方式」,写一次,两种都能上。
五、一个必须躲开的坑:FastMCP 已经改名 MCPServer
写教程最怕的就是「照着网上老文章抄,一跑就报错」。这里有个上个月刚发生的、影响巨大的改名,必须单独拎出来讲:
2026 年 7 月 28 日,Python SDK 随协议修订一起发了 v2.0.0,把高层服务器类从 FastMCP 改名成了 MCPServer。而且不是「弃用」,是直接删掉——老代码的这条 import 现在会当场报错:
1 | from mcp.server.fastmcp import FastMCP |
我写这篇时实机装了最新的 mcp(2.3.0)验证过:from mcp.server import MCPServer 正常,老的 from mcp.server.fastmcp import FastMCP 就是 ModuleNotFoundError。网上大量 2026 年 7 月之前的教程还在教 FastMCP,照抄必翻车。
迁移其实就一句话:
1 | # 旧(v1.x,已失效) |
装饰器那套 @mcp.tool() / @mcp.resource() / @mcp.prompt() 完全没变,所以大多数迁移就是改这一行 import。老项目如果暂时不想动,就在依赖里钉死 mcp>=1.28,<2,先止血再慢慢搬。
顺带把几个常见的连带坑也记下来:异常基类 McpError 改名成了 MCPError;协议模型字段从驼峰改成了蛇形(inputSchema → input_schema);host/port 从构造函数挪到了 run() 里。真要用到低级 API 时再翻官方迁移指南,日常写教程级的 Server 基本碰不到。
六、一句话总结 + 下一步
- MCP 解决 N×M 对接地狱:写一次 Server,所有 Agent 客户端都能接。
- 它给「模型」而非「人」设计:Tools / Resources / Prompts 三个原语,description 和 inputSchema 直接喂给模型。
- 底层是 JSON-RPC 2.0,传输就两种:stdio(本地)和 Streamable HTTP(远程)。
- 上手就三样:
pip install "mcp[cli]"→ 写MCPServer+ 三个装饰器 →mcp dev/mcp install验证接入。 - 躲坑:
FastMCP已改名MCPServer,老 import 直接报 ModuleNotFoundError。
这一课先让你「看懂 + 跑通一个会算加法的玩具」。下一课我们把它升级成真正有用的 Server——接上你自己的数据(笔记、数据库、内部接口),把 Resources 的动态 URI、模板化、分页一次讲透,再把 Streamable HTTP + 鉴权搭起来,让远程客户端也能安全连上。
生命不息,折腾不止。下一篇「MCP 实战第二课」:把玩具 Server 换成能干活的——手把手接上你真实的数据库/文件/内部 API,讲透 Resources 的动态 URI 与分页,再用 Streamable HTTP + 鉴权把它挂到远程,让多个 Agent 客户端安全共享同一个工具集。