生命不息,折腾不止。这一篇开个新坑: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,但每个字段都是为「模型」设计的,不是为「人写代码」设计的:

  1. 工具的 description 是写给模型看的。模型靠读你写的 docstring 决定「这个工具是不是我要的、该怎么传参」,而不是靠人肉读 API 文档。你写「Add two integers」,模型就知道这是加法。
  2. inputSchema 自动从类型标注生成。你的 Python 函数 def add(a: int, b: int),int 类型标注直接变成 JSON Schema,模型照着 schema 传参,不会传错类型。
  3. 模型自己「发现」而不是人写死。客户端连上来先 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
2
3
pip install "mcp[cli]"
# 或者用 uv:
# uv add "mcp[cli]"

[cli] 这个 extra 会顺带装上 mcp 命令行工具(后面 mcp dev、mcp run、mcp install 全靠它)。装完可以先看一眼版本确认装对了:

1
2
mcp version
# MCP version 2.3.0

第 2 步:写 server.py,三个原语(工具 / 资源 / 提示词)各来一个,一次看全:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from mcp.server import MCPServer

mcp = MCPServer("Demo")


@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two integers."""
return a + b


@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
"""Greet someone by name."""
return f"Hello, {name}!"


@mcp.prompt()
def review(code: str) -> str:
"""Ask the model to review a code snippet."""
return f"Review this code for bugs and style: {code}"


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

注意你没写的东西:没有 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
2
mcp install server.py --name "Demo"
# 需要环境变量时:-v API_KEY=xxx 或 -f .env

其它宿主也认同一套东西:Claude Code 用 claude mcp add、Cursor / VS Code 在各自的 MCP 配置里填同样的启动命令,只是格式各项目自己定。核心就一句:你写的是 MCP Server,谁都能接。

第 5 步(可选):同一个 Server,一行改成远程 HTTP

代码一个字不动,只改最后一行,就能从本地子进程变成 HTTP 服务:

1
2
if __name__ == "__main__":
mcp.run(transport="streamable-http", port=3001)

客户端连 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
2
from mcp.server.fastmcp import FastMCP
# ModuleNotFoundError: No module named 'mcp.server.fastmcp'

我写这篇时实机装了最新的 mcp(2.3.0)验证过:from mcp.server import MCPServer 正常,老的 from mcp.server.fastmcp import FastMCP 就是 ModuleNotFoundError。网上大量 2026 年 7 月之前的教程还在教 FastMCP,照抄必翻车。

迁移其实就一句话:

1
2
3
4
5
6
7
# 旧(v1.x,已失效)
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("Demo")

# 新(v2.x,当前)
from mcp.server import MCPServer
mcp = MCPServer("Demo")

装饰器那套 @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 客户端安全共享同一个工具集。