2026年最新MCP协议从原理到实战:手写一个MCP Server接入Claude Code全流程踩坑指南
2026年最新MCP协议从原理到实战:手写一个MCP Server接入Claude Code全流程踩坑指南
📑 目录
- 一、为什么 2026 年你必须搞懂 MCP 协议?
- 二、MCP 协议核心原理:5 分钟看懂
- 三、环境准备
- 四、实战第一步:手写一个 stdio 版 MCP Server
- 五、接入 Claude Code 实战
- 六、进阶:升级到 SSE 远程传输
- 七、踩坑实录:我踩过的 7 个坑(建议收藏)
- 八、MCP vs 传统 Function Calling 横向对比
- 九、总结与互动
摘要:2026 年 6 月微软 Build 大会宣布全面拥抱 MCP 协议,Copilot Studio 原生支持 MCP 工具注册。一时间,MCP 成了 AI 开发圈的"USB 接口"标准。但教程要么停在概念科普,要么一上来就堆 LangGraph + CrewAI 一堆框架,对只想"先跑通一个自己的工具"的开发者极不友好。本文不聊虚的,带你从零手写一个 MCP Server,接入 Claude Code,再把踩过的 7 个坑一次性讲透。读完你就能把自己的任何脚本变成大模型能调用的工具。
关键词:MCP 协议;Model Context Protocol;Claude Code;MCP Server;stdio / SSE 传输;Function Calling;AI Agent;工具调用
一、为什么 2026 年你必须搞懂 MCP 协议?
先说一个很多开发者都遇到过的痛点:
你给大模型接了一个"查询公司数据库"的能力,用 Function Calling 写得好好的。结果换了个 Agent 框架(从 LangChain 换到 CrewAI),接口全得重写;换了家模型(从 GPT 换到 Claude),参数格式又对不上;想让同事复用你的工具,发现他用的框架你压根没听过——工具写了一遍又一遍,像个永远调不通的转接头。
MCP(Model Context Protocol,模型上下文协议)就是来终结这个混乱的。
它由 Anthropic 在 2024 年底提出,核心目标用一句话讲清楚:
让大模型像浏览器访问网页一样,用统一协议安全地访问任何外部工具和数据源。写一次 Server,所有支持 MCP 的客户端都能用。
到 2026 年中,局面已经很明朗:
| 时间节点 | 事件 |
|---|---|
| 2024.11 | Anthropic 发布 MCP 协议规范 |
| 2025 上半年 | OpenAI、Google 相继表态支持 |
| 2025 下半年 | Cursor、Claude Code、Cline 等 AI 编程工具原生集成 |
| 2026.06 | 微软 Build 大会宣布 Copilot Studio 全面拥抱 MCP |
一句话:MCP 已经成了连接 AI 和外部工具的事实标准。学会它,等于拿到了 2026 年 AI 开发的入场券。
二、MCP 协议核心原理:5 分钟看懂
别被"协议"两个字吓到,MCP 的设计相当克制。底层就是 JSON-RPC 2.0,你发一段 JSON、收一段 JSON,仅此而已。
2.1 一句话理解 MCP
把大模型想象成一台电脑,MCP 就是它的 USB 接口。你不用关心接的是硬盘、网卡还是打印机——只要设备符合 USB 规范,插上就能用。MCP Server 就是那些"USB 设备",负责把你的数据库、文件系统、内部 API 包装成统一的接口。
2.2 三层架构:Host / Client / Server
MCP 的运行时由三个角色组成:
┌─────────────────────────────────┐
│ Host(宿主) │
│ 如 Claude Code / Cursor / Cline │
│ ┌───────────┐ ┌───────────┐ │
│ │ MCP Client│ │ MCP Client│ │
│ └─────┬─────┘ └─────┬─────┘ │
└─────────┼──────────────┼────────┘
│ JSON-RPC │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ MCP Server A │ │ MCP Server B │
│ (你的工具) │ │ (别人的工具) │
└──────┬───────┘ └──────┬───────┘
▼ ▼
数据库/文件 GitHub/邮件
- Host(宿主):运行大模型的那个程序,比如 Claude Code。它负责把模型的工具调用请求转发给对应的 Server。
- Client(客户端):嵌在 Host 里,每个 Server 对应一个 Client,负责协议通信。
- Server(服务端):你写的那个程序,真正干活的人——读文件、查数据库、调 API 都在它里面。
2.3 三类能力:Tools / Resources / Prompts
一个 MCP Server 可以向外暴露三类东西:
| 能力 | 作用 | 谁主动 | 典型场景 |
|---|---|---|---|
| Tools(工具) | 可被模型调用的函数 | 模型决定何时调 | 查天气、发邮件、跑 SQL |
| Resources(资源) | 可被读取的数据 | 客户端/用户主动拉 | 读文件内容、读配置 |
| Prompts(提示模板) | 预设的提示词模板 | 用户主动触发 | 代码审查模板、总结模板 |
90% 的实战场景你只需要 Tools。本文也以 Tools 为主线展开,资源和提示模板留作进阶。
2.4 两种传输方式:stdio vs SSE
Server 和 Client 之间怎么传 JSON-RPC 消息?MCP 规范定义了两种主流传输:
| 维度 | stdio(标准输入输出) | SSE(Server-Sent Events) |
|---|---|---|
| 通信方式 | 子进程的标准输入/输出 | HTTP 长连接 + 流式响应 |
| 部署形态 | 本地,Host 启动子进程 | 远程,独立 HTTP 服务 |
| 适用场景 | 本地工具、个人开发 | 团队共享、生产部署 |
| 配置复杂度 | 低,一行命令 | 中,需管端口/CORS |
| 多客户端共享 | 不行,每个 Host 各起一份 | 可以,一个 Server 多端连 |
| 调试难度 | print 会污染协议(见坑1) | 可用浏览器/curl 直接测 |
新手建议:先用 stdio 跑通,再按需升级到 SSE。 本文两种都会手把手演示。
三、环境准备
3.1 Python 环境与 MCP SDK
MCP 官方提供了 Python 和 TypeScript 两套 SDK。本文用 Python,因为上手最快。
建议用 Python 3.10+,建个独立虚拟环境:
# 创建项目目录
mkdir my-mcp-server && cd my-mcp-server
# 创建虚拟环境(Windows 用 python,Linux/Mac 用 python3)
python -m venv .venv
# 激活虚拟环境
# Windows (Git Bash):
source .venv/Scripts/activate
# Linux/Mac:
source .venv/bin/activate
# 安装 MCP 官方 SDK
pip install mcp
踩坑预警:很多人图省事不建虚拟环境,直接全局
pip install。后面接入 Claude Code 时,command字段要写 Python 解释器的绝对路径,全局环境的路径一旦变动(升级、重装)就全崩。永远用虚拟环境的绝对路径,别偷懒。
3.2 安装 Claude Code
Claude Code 是 Anthropic 官方的命令行 AI 编程助手,对 MCP 支持最完整,本文以它为接入对象。
# 需要 Node.js 18+
npm install -g @anthropic-ai/claude-code
# 验证安装
claude --version
安装后运行一次 claude 按提示登录即可。如果你用 Cursor,配置思路完全一致,只是配置文件位置不同,后文会提到。
四、实战第一步:手写一个 stdio 版 MCP Server
需求很实在:写一个"笔记管理"工具,让大模型能帮我们增删查改本地笔记。麻雀虽小,五脏俱全——查、增、改三种工具都有了,足够你举一反三。
4.1 用 FastMCP 五行代码起一个 Server
MCP Python SDK 提供了一个高层封装 FastMCP,用法类似 FastAPI,装饰器一挂就能把普通函数变成 MCP 工具。
新建 note_server.py:
# note_server.py
import json
import os
from mcp.server.fastmcp import FastMCP
# 笔记存储文件
NOTE_FILE = "notes.json"
def _load_notes():
if not os.path.exists(NOTE_FILE):
return []
with open(NOTE_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def _save_notes(notes):
with open(NOTE_FILE, "w", encoding="utf-8") as f:
json.dump(notes, f, ensure_ascii=False, indent=2)
# 创建 MCP Server 实例
mcp = FastMCP("note-server")
if __name__ == "__main__":
mcp.run(transport="stdio")
跑起来什么都不干,但骨架已经有了。接下来加工具。
4.2 定义你的工具:增、查、搜索
@mcp.tool()
def list_notes() -> str:
"""列出所有笔记的标题和编号。无笔记时返回空列表提示。"""
notes = _load_notes()
if not notes:
return "当前没有任何笔记。"
return json.dumps(
[{"id": i, "title": n["title"]} for i, n in enumerate(notes)],
ensure_ascii=False
)
@mcp.tool()
def add_note(title: str, content: str) -> str:
"""添加一条新笔记。
Args:
title: 笔记标题,简短概括
content: 笔记正文内容
"""
notes = _load_notes()
notes.append({"title": title, "content": content})
_save_notes(notes)
return f"已添加笔记《{title}》,当前共 {len(notes)} 条。"
@mcp.tool()
def search_notes(keyword: str) -> str:
"""按关键词搜索笔记标题和正文,返回匹配结果。
Args:
keyword: 搜索关键词
"""
notes = _load_notes()
results = [
{"id": i, "title": n["title"], "content": n["content"]}
for i, n in enumerate(notes)
if keyword in n["title"] or keyword in n["content"]
]
if not results:
return f"没有找到包含「{keyword}」的笔记。"
return json.dumps(results, ensure_ascii=False)
注意几个细节,这些是新手最容易忽略、却直接决定工具能不能被模型正确调用的关键:
- 函数名要语义化:模型靠名字猜用途,
list_notes比get_data好一万倍。 - docstring 是命根子:模型决定"要不要调这个工具"时,主要看 docstring。写得越清楚,调用越准。
- 参数要有类型注解:
title: str比title多了类型信息,SDK 会自动转成 JSON Schema 喂给模型。 - 返回值统一用字符串:复杂结构用
json.dumps,别直接返回 dict,不同客户端兼容性参差。
完整文件保存后,本地先验证语法没问题:
python note_server.py
# 没报错、阻塞等待输入,说明 stdio 服务已就绪(Ctrl+C 退出)
4.3 本地调试:MCP Inspector 可视化
MCP 官方提供了一个调试神器 Inspector,能在浏览器里直接测试你的工具,不用每次都启动 Claude Code。
# 一行命令启动(会自动拉起一个 Web 界面)
npx @modelcontextprotocol/inspector python note_server.py
浏览器打开 http://localhost:5173,能看到:
- 左侧列出所有已注册的 Tools
- 点一个工具,右侧填参数,点 “Run” 直接调用
- 底部显示完整的 JSON-RPC 请求/响应
强烈建议:每次改完代码先在 Inspector 里测一遍,确认工具行为正常,再去接 Claude Code。 这能帮你把"是 Server 的 bug"还是"模型没调用对"区分开,省下大量排查时间。
五、接入 Claude Code 实战
Server 写好了,怎么让 Claude Code 用上它?有两种方式,任选其一。
5.1 方式一:配置文件(推荐,可版本化)
在项目根目录(或用户家目录 ~/.claude/)创建 .mcp.json:
{
"mcpServers": {
"note-server": {
"command": "C:\\\\项目路径\\\\my-mcp-server\\\\.venv\\\\Scripts\\\\python.exe",
"args": ["C:\\\\项目路径\\\\my-mcp-server\\\\note_server.py"]
}
}
}
Windows 路径坑:JSON 里反斜杠要转义成
\\\\。更省心的做法是用正斜杠/,Windows 也认:"C:/项目路径/my-mcp-server/.venv/Scripts/python.exe"。Mac/Linux 用户写正常路径即可。
5.2 方式二:命令行添加(适合临时调试)
claude mcp add note-server -- C:/项目路径/my-mcp-server/.venv/Scripts/python.exe C:/项目路径/my-mcp-server/note_server.py
-- 后面跟的就是启动命令和参数,和配置文件里的 command + args 完全对应。
5.3 验证:工具是否被识别
在项目目录下启动 Claude Code:
claude
进入交互界面后,输入:
/mcp
会列出所有已连接的 MCP Server 及其状态。看到 note-server: connected 且下面列出 list_notes、add_note、search_notes 三个工具,说明接入成功。
然后直接用自然语言测试:
> 帮我加一条笔记,标题"MCP学习计划",内容"本周跑通stdio版本,下周升级SSE"
> 我现在有哪些笔记?
> 搜一下有没有关于"SSE"的笔记
如果模型自主决定调用对应工具并返回正确结果,恭喜,你已经拥有了一个能被大模型驱动的自定义工具系统。
六、进阶:升级到 SSE 远程传输
stdio 模式有个硬伤:每台机器都要装一份 Server,没法团队共享。 当你想让整个团队用同一个"公司内部知识库查询工具"时,就需要 SSE 模式——把 Server 部署成 HTTP 服务,大家远程连。
6.1 改造代码:从 stdio 到 SSE
改动极小,FastMCP 帮你屏蔽了传输层差异:
# note_server_sse.py
# ... 上面的工具定义完全不变 ...
if __name__ == "__main__":
# 只需把 transport 从 "stdio" 改成 "sse",并指定端口
mcp.run(transport="sse", port=8765)
运行:
python note_server_sse.py
# 输出类似:Uvicorn running on http://0.0.0.0:8765
6.2 客户端连接 SSE Server
Claude Code 的 .mcp.json 配置改为 URL 形式:
{
"mcpServers": {
"note-server-remote": {
"url": "http://your-server-ip:8765/sse"
}
}
}
或命令行:
claude mcp add note-server-remote --transport sse http://localhost:8765/sse
这样你的 Server 就能被团队任意成员、任意支持 MCP 的客户端连接了。部署到云服务器后,还能配合 nginx 做反向代理和 HTTPS。
新协议提醒:MCP 规范在 2025 年下半年引入了 Streamable HTTP 传输,逐步取代纯 SSE。它对无状态部署更友好。如果你的 SDK 版本较新,可尝试
transport="streamable-http"。过渡期 SSE 仍广泛兼容,本文以 SSE 为例。
七、踩坑实录:我踩过的 7 个坑(建议收藏)
理论和实战都有了,但真正写的时候,坑往往在细节里。下面这 7 个坑,每一个都让我或社区同学掉过头发。
| # | 坑点 | 现象 | 根因 | 解决方案 |
|---|---|---|---|---|
| 1 | stdio 下用 print 调试 | Claude Code 连接后卡死、报协议解析错误 | stdio 传输靠标准输入输出传 JSON-RPC,print 的内容会被当成协议消息,直接污染通道 | 永远用 logging 输出到 stderr:import logging; logging.basicConfig(stream=sys.stderr) |
| 2 | Windows 中文编码乱码 | 工具返回的中文变成 ??? 或抛 UnicodeDecodeError |
Windows 默认控制台编码是 GBK,Python 写文件/输出时编码不一致 | 文件操作显式指定 encoding="utf-8";脚本开头加 sys.stdout.reconfigure(encoding="utf-8") |
| 3 | command 写了相对路径 | 换个目录启动 Claude Code 就报 “command not found” | Host 启动子进程时的工作目录不固定,相对路径解析失败 | command 和 args 一律写绝对路径,包括 python.exe 和脚本文件 |
| 4 | docstring 太简略 | 模型明明有合适工具却死活不调用,或者乱传参数 | 模型选工具主要靠 docstring 理解语义,写得太短它不知道这工具干嘛 | docstring 写清"做什么 + 参数含义 + 返回什么",像给新人写接口文档一样 |
| 5 | 参数没写类型注解 | 模型把数字传成字符串,或参数顺序混乱 | 没有类型注解,SDK 生成的 JSON Schema 缺类型信息,模型只能猜 | 每个参数都加类型注解 name: str、count: int,复杂结构用 Pydantic Model |
| 6 | SSE 部署后连不上 | 本机 localhost 能连,远程/容器内死活连不上 | 默认监听 127.0.0.1 只接受本机;或防火墙没开端口;或跨域被拦 | 监听地址设为 0.0.0.0;开放对应端口;跨域时配置 CORS |
| 7 | 工具能干危险操作却没护栏 | 模型自作主张删了数据 / 改了不该改的文件 | 给了工具全部权限,模型在"自主"执行时可能越界 | 危险操作(删除/覆盖)加二次确认参数;用 Resources 的只读特性隔离;生产环境加操作日志和回滚 |
重点展开坑 1 和坑 4,这俩是高频翻车点:
坑 1 的正确调试姿势:
import sys
import logging
# 关键:输出到 stderr,不污染 stdout(stdout 是协议通道)
logging.basicConfig(
level=logging.DEBUG,
stream=sys.stderr,
format="%(asctime)s [%(levelname)s] %(message)s"
)
@mcp.tool()
def add_note(title: str, content: str) -> str:
logging.debug(f"调用 add_note: title={title}")
# ... 业务逻辑 ...
return "ok"
Claude Code 启动 Server 时可以把 stderr 重定向到文件查看:在 .mcp.json 里没法直接重定向,但可以在 Python 脚本里把日志写到文件。
坑 4 的 docstring 对比:
# ❌ 模型大概率不会调用,或乱传参
@mcp.tool()
def search(keyword):
"""搜索"""
...
# ✅ 模型能准确判断何时调用、怎么传参
@mcp.tool()
def search_notes(keyword: str) -> str:
"""按关键词搜索笔记标题和正文,返回所有匹配的笔记。
当用户想查找"包含某内容的笔记""有没有关于XX的记录"时使用。
Args:
keyword: 要搜索的关键词,单个词或短语
"""
...
八、MCP vs 传统 Function Calling 横向对比
写到这里你可能会问:我之前用 Function Calling 用得好好的,凭什么要换 MCP?这张表给你答案:
| 维度 | 传统 Function Calling | MCP 协议 |
|---|---|---|
| 标准化程度 | 各家模型接口不一,参数格式各异 | 开放标准,模型无关,一套规范通吃 |
| 工具复用 | 换个框架/模型基本要重写 | 写一次 Server,所有 MCP 客户端通用 |
| 工具发现 | 手动在代码里注册每个工具 | 自动发现,连上 Server 即可用 |
| 本地数据访问 | 需自己处理本地文件/数据库对接 | Server 跑在本地,天然能访问本地资源 |
| 安全控制 | 基本裸奔,全靠 prompt 自觉 | Server 侧可做鉴权、审计、权限隔离 |
| 调试体验 | 只能看日志,黑盒 | MCP Inspector 可视化调试 |
| 生态 | 各自为政 | MCP 工具市场逐渐成型,官方/社区 Server 越来越多 |
| 学习成本 | 低,直接写函数 | 中,需理解协议和传输 |
| 适用场景 | 单一模型、简单工具、快速原型 | 多模型/多框架、团队共享、生产级 Agent |
结论:如果你只是个人小打小闹、只用一个模型,Function Calling 够用;但只要涉及工具复用、团队共享、生产部署,MCP 的标准化红利会越来越值。2026 年的趋势已经很明确——新工具按 MCP 规范写,老工具逐步迁移。
九、总结与互动
9.1 本文核心回顾
- MCP 是什么:连接大模型和外部工具的"USB 协议",2026 年已成事实标准。
- 三层架构:Host(宿主)→ Client(客户端)→ Server(你写的工具服务)。
- 三类能力:Tools(最常用)、Resources、Prompts。
- 两种传输:stdio(本地起步)、SSE/Streamable HTTP(远程共享)。
- 实战路径:FastMCP 写工具 → Inspector 调试 → 接入 Claude Code → 按需升级 SSE。
- 7 个坑:print 污染、中文编码、相对路径、docstring、类型注解、SSE 监听、安全护栏。
9.2 给你的下一步建议
- 本周:照着本文把 stdio 版 Note Server 跑通,接上 Claude Code。
- 下周:把你自己工作中的一个真实脚本(比如查日志、跑 SQL、生成报表)改造成 MCP Server。
- 进阶:研究 Resources 和 Prompts 能力,做只读知识库和提示模板。
- 生产化:升级到 Streamable HTTP,加上鉴权和操作审计。
9.3 互动
MCP 这套东西还在快速演进,坑也还在不断被发现。你在实操中遇到过什么离谱的问题?或者有什么好玩的 Server 想法?欢迎在评论区交流,我会逐条回复。
如果这篇对你有帮助,点赞 + 收藏 + 关注三连是对原创最大的鼓励——下一篇我会写 「MCP Server 生产部署:鉴权、审计与高可用实战」,把这套东西从"能跑"推进到"敢上线",别错过。
本文所有代码均基于 MCP Python SDK 实测,环境为 Windows 11 + Python 3.13 + Claude Code 最新版。不同版本 SDK API 可能有细微差异,以官方文档为准。
更多推荐


所有评论(0)