2026年最新MCP协议从原理到实战:手写一个MCP Server接入Claude Code全流程踩坑指南
📑 目录
- 一、为什么 2026 年你必须搞懂 MCP 协议?
- 二、MCP 协议核心原理:5 分钟看懂
- 2.1 一句话理解 MCP
- 2.2 三层架构:Host / Client / Server
- 2.3 三类能力:Tools / Resources / Prompts
- 2.4 两种传输方式:stdio vs SSE
- 三、环境准备
- 3.1 Python 环境与 MCP SDK
- 3.2 安装 Claude Code
- 四、实战第一步:手写一个 stdio 版 MCP Server
- 4.1 用 FastMCP 五行代码起一个 Server
- 4.2 定义你的工具:增、查、搜索
- 4.3 本地调试:MCP Inspector 可视化
- 五、接入 Claude Code 实战
- 5.1 方式一:配置文件(推荐,可版本化)
- 5.2 方式二:命令行添加(适合临时调试)
- 5.3 验证:工具是否被识别
- 六、进阶:升级到 SSE 远程传输
- 6.1 改造代码:从 stdio 到 SSE
- 6.2 客户端连接 SSE Server
- 七、踩坑实录:我踩过的 7 个坑(建议收藏)
- 八、MCP vs 传统 Function Calling 横向对比
- 九、总结与互动
- 9.1 本文核心回顾
- 9.2 给你的下一步建议
- 9.3 互动
摘要: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+,建个独立虚拟环境:
# 创建项目目录mkdirmy-mcp-server&&cdmy-mcp-server# 创建虚拟环境(Windows 用 python,Linux/Mac 用 python3)python-mvenv .venv# 激活虚拟环境# Windows (Git Bash):source.venv/Scripts/activate# Linux/Mac:source.venv/bin/activate# 安装 MCP 官方 SDKpipinstallmcp踩坑预警:很多人图省事不建虚拟环境,直接全局
pip install。后面接入 Claude Code 时,command字段要写 Python 解释器的绝对路径,全局环境的路径一旦变动(升级、重装)就全崩。永远用虚拟环境的绝对路径,别偷懒。
3.2 安装 Claude Code
Claude Code 是 Anthropic 官方的命令行 AI 编程助手,对 MCP 支持最完整,本文以它为接入对象。
# 需要 Node.js 18+npminstall-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.pyimportjsonimportosfrommcp.server.fastmcpimportFastMCP# 笔记存储文件NOTE_FILE="notes.json"def_load_notes():ifnotos.path.exists(NOTE_FILE):return[]withopen(NOTE_FILE,"r",encoding="utf-8")asf:returnjson.load(f)def_save_notes(notes):withopen(NOTE_FILE,"w",encoding="utf-8")asf: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()deflist_notes()->str:"""列出所有笔记的标题和编号。无笔记时返回空列表提示。"""notes=_load_notes()ifnotnotes:return"当前没有任何笔记。"returnjson.dumps([{"id":i,"title":n["title"]}fori,ninenumerate(notes)],ensure_ascii=False)@mcp.tool()defadd_note(title:str,content:str)->str:"""添加一条新笔记。 Args: title: 笔记标题,简短概括 content: 笔记正文内容 """notes=_load_notes()notes.append({"title":title,"content":content})_save_notes(notes)returnf"已添加笔记《{title}》,当前共{len(notes)}条。"@mcp.tool()defsearch_notes(keyword:str)->str:"""按关键词搜索笔记标题和正文,返回匹配结果。 Args: keyword: 搜索关键词 """notes=_load_notes()results=[{"id":i,"title":n["title"],"content":n["content"]}fori,ninenumerate(notes)ifkeywordinn["title"]orkeywordinn["content"]]ifnotresults:returnf"没有找到包含「{keyword}」的笔记。"returnjson.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 mcpaddnote-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:87656.2 客户端连接 SSE Server
Claude Code 的.mcp.json配置改为 URL 形式:
{"mcpServers":{"note-server-remote":{"url":"http://your-server-ip:8765/sse"}}}或命令行:
claude mcpaddnote-server-remote--transportsse 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 的正确调试姿势:
importsysimportlogging# 关键:输出到 stderr,不污染 stdout(stdout 是协议通道)logging.basicConfig(level=logging.DEBUG,stream=sys.stderr,format="%(asctime)s [%(levelname)s] %(message)s")@mcp.tool()defadd_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()defsearch(keyword):"""搜索"""...# ✅ 模型能准确判断何时调用、怎么传参@mcp.tool()defsearch_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 可能有细微差异,以官方文档为准。