MCP协议实战从零写个Agent工具摘要MCP 就是 AI 世界的 REST API——你写好服务Claude 来调。前一篇聊了 AI Agent 的学习路线这篇落到具体动作用 Python 写一个 MCP Server把自己的 API 包装成 Agent 能认识的标准工具。不讲协议规范讲后端最关心的事——怎么让 AI 调你的服务。每个大模型的工具调用方式都不一样。OpenAI 是 function callingClaude 是 tool use国产模型各有各的格式。做后端的人最烦这种事——同一个查天气的接口接三个模型要写三套适配层。举个例子。你用 Spring Boot 写了个天气查询接口想让 AI 能调用。接 OpenAI你得按它的 function calling 格式写 JSON Schema字段名、参数类型全得按它的规范来。接 Claude换一套 tool use 的定义格式描述文案的写法也不一样。接国内的模型又是一套。最崩溃的不是第一次适配——是模型升级之后。OpenAI 改了个参数格式你的适配层报错了。Claude 更新了 tool use 的限制你之前能跑的工具现在调不起来了。三个模型三个迭代节奏你一个人维护三套适配代码。说实话适配到想吐。后来 Anthropic 搞了个 MCPModel Context Protocol把这事统一了。2024 年底提出2025 年生态炸开到 2026 年已经成了 Agent 工具调用的标配来源Anthropic 官方博客2024年11月。跟 HTTP 之于 Web 一样——大家都认这个协议www.ycsjb.com你写一次所有支持 MCP 的模型都能调。上篇聊了 AI Agent 的学习路线这篇不聊概念聊代码。MCP 是什么用后端的话说你用 Spring Boot 写过一个 REST 接口。bash 代码解读复制代码GET /api/weather?city北京→ { temp: 26, humidity: 60 }前端发起 HTTP 请求你的 Controller 处理逻辑返回 JSON——这条路你走了十年闭着眼睛都能写。MCP 干的事一模一样只是调用方从浏览器换成了 AI。arduino 代码解读复制代码Claude 想查天气 → MCP 协议 → 你的 MCP Server → 返回天气数据架构拆开来看arduino 代码解读复制代码┌──────────────────┐ ┌──────────────────┐│ MCP Client │ JSON-RPC 2.0 │ MCP Server ││ │ ◄──────────────────► │ ││ Claude Desktop │ stdio / HTTP/SSE │ 你写的 Python ││ Claude Code │ │ 服务 ││ Cursor / Cline │ │ │└──────────────────┘ └─────────┬────────┘│▼┌─────────────────┐│ 你的业务 API ││ ││ 天气接口 ││ 数据库查询 ││ 搜索引擎 │└─────────────────┘三个要点用后端的类比就够了MCP Server 你写的微服务。 用 server.tool() 装饰器注册工具函数跟 Spring 的 RestController 一样——声明这个方法对外暴露MCP 框架自动处理协议细节。MCP Client 调用方。 Claude Desktop、Claude Code、Cursor 这些 AI 工具都内置了 MCP 客户端。你配好 Server 地址它们自动发现你的工具、看懂工具描述、在需要的时候调用。通信协议 JSON-RPC 2.0。 跟 REST 的 HTTP JSON 一样MCP 用 JSON-RPC 做序列化支持 stdio本地进程通信和 HTTP/SSE远程部署。本机调试用 stdio部署到服务器换成 SSE。一句话以前你写 REST API 给浏览器调现在写 MCP Server 给 AI 调。换的是调用方不换的是定义接口 → 处理请求 → 返回结果这套逻辑。为什么要用 MCP 而不是直接把 API 丢给 AI两个原因。第一MCP 是标准协议。AI 客户端不用知道你的 API 地址、认证方式、参数格式——它只认 MCP 协议你的 Server 负责把内部 API 包装成标准工具。第二工具发现是自动的。你加了新工具AI 自动感知你改了工具描述AI 下次调用就用新的描述。不用通知任何一个客户端。动手写天气查询 MCP Server理论够了写代码。环境准备Python 3.103.10 以下跑不了后面踩坑会说装一个包bash 代码解读复制代码pip install mcp就一个依赖。FastMCP 把协议细节全封好了你只关心工具函数怎么写。核心代码python 代码解读复制代码from mcp.server.fastmcp import FastMCPmcp FastMCP(weather-server)mcp.tool()async def get_weather(city: str) - str:查询指定城市的实时天气信息返回温度、湿度、风向和天气概况# 这里接真实的天气 API示例用模拟数据return f{city}晴26°C湿度 60%北风 3 级if __name__ __main__:mcp.run(transportstdio)就这么几行。拆一下FastMCP(weather-server) —— 创建一个 MCP Server 实例名字随便起。mcp.tool() —— 装饰器注册工具跟 Spring 的 GetMapping 一个意思。函数签名 (city: str) - str —— 参数类型和返回值 MCP 框架会自动转成 JSON SchemaAI 客户端用它来判断什么时候调用。函数的 docstring —— 这个不是注释是 AI 看的东西。写什么、怎么写直接决定 AI 调不调用你的工具。后面专门说。mcp.run(transportstdio) —— 启动服务。stdio 走标准输入输出本机调试用部署到服务器换成 transportsse。三十行不到一个能跑的 MCP Server 就出来了。再加一个工具网页搜索只有一个工具太单薄。再加一个搜索工具让 AI 能查实时信息python 代码解读复制代码import httpxmcp.tool()async def search_web(query: str) - str:在互联网上搜索指定关键词返回前 3 条搜索结果摘要和链接。适用于需要实时信息的问题比如新闻、股价、最新动态。async with httpx.AsyncClient() as client:resp await client.get(https://api.search.example.com/v1/search,params{q: query, limit: 3},)data resp.json()return \n.join(f- {r[title]}: {r[snippet]} ({r[url]}) for r in data[results])两个工具注册完你的 MCP Server 就有了两样本事查天气、搜网页。AI 拿到这两个工具碰到今天北京热不热会调天气工具碰到最近 AI 圈有什么新闻会调搜索工具。先自己测一下接 Claude 之前先确认 Server 本身能跑。FastMCP 内置了调试模式bash 代码解读复制代码python server.py --debug或者在代码里用 mcp dev 命令启动一个本地测试界面直接在浏览器里手动调工具、看返回结果。这个调试界面还显示了 AI 看到的工具 Schema——你会看到你的 docstring 是怎么被转成 JSON Schema 的参数类型、返回值描述一目了然。这时候如果发现 Schema 不对回去改 docstring 就行。测通了再接 Claude省得配完配置才发现 Server 起不来两头排查更费劲。接 Claude让 AI 真正用起来服务写好了接下来让 Claude 认识它。Claude Desktop 配置macOS 上配置文件在这里javascript 代码解读复制代码~/Library/Application Support/Claude/claude_desktop_config.json打开加上这一段json 代码解读复制代码{mcpServers: {weather-server: {command: python,args: [/Users/tangyuewei/mcp-server/server.py]}}}command 是启动命令args 是脚本路径。保存重启 Claude Desktop。重启后Claude 对话框左下角会出现一个锤子图标——点开能看到你的两个工具get_weather 和 search_web描述就是你写的 docstring。这时候你问 Claude 北京今天天气怎么样它会自动调你的 get_weather。Claude Code 配置如果你用的是 Claude Code终端里的 Claude配法更简单。在项目根目录加一个 .mcp.jsonjson 代码解读复制代码{mcpServers: {weather-server: {command: python,args: [server.py]}}}然后在 Claude Code 里 /mcp 就能看到连接状态。绿点代表连上了可以调。HTTP 部署如果你想把服务部署到服务器上让团队共用把 stdio 换成 SSEpython 代码解读复制代码if __name__ __main__:mcp.run(transportsse, host0.0.0.0, port8080)客户端配置对应改成 URLjson 代码解读复制代码{mcpServers: {weather-server: {url: http://your-server:8080/sse}}}跟 REST 服务的部署套路一样——本机开发用 stdio上线换成 HTTP。踩过的坑写了几个 MCP Server 之后我把踩过的坑说三个。坑一工具描述写模糊了AI 不调用这个是最大的坑。mcp.tool() 的 docstringAI 是当工具说明书来读的。你说得越清楚AI 越知道什么时候调。我做过对比。描述写查询天气信息——调用率 70% 左右。改成了查询指定城市的实时天气信息返回温度、湿度、风向和天气概况——调用率跳到 95% 以上。加了适用于查询天气相关问题这句使用场景描述后误调用也少了。为什么差这么多因为 LLM 靠语义匹配决定调哪个工具。你写查询天气它只在用户明确说天气的时候调你写了温度、湿度、风向用户说今天热不热它也能匹配上。经验docstring 写三行——第一行说功能第二行说输入输出第三行说适用场景。别省这个功夫这是你跟 AI 之间唯一的接口文档。还有一个容易被忽略的点工具名也是有语义的。get_weather 比 fetch_wx 好因为 AI 能直接从名字推断功能。如果你用缩写或项目内部代号当工具名AI 看不懂调用率就低。工具名 描述一起决定了 AI 的调用意愿。坑二Python 版本MCP 官方依赖要求 Python 3.10。macOS 自带的 Python 3.9 跑不了。我第一次装了之后启动报 TypeError查了半小时才反应过来是版本问题。装个 pyenv 切到 3.12 解决bash 代码解读复制代码pyenv install 3.12pyenv local 3.12pip install mcp坑三配置文件路径Claude Desktop 的配置路径 macOS 和 Windows 不一样。macOS 是 ~/Library/Application Support/Claude/claude_desktop_config.jsonWindows 在 %APPDATA%\Claude\ 下面。网上很多教程只贴 mac 路径用 Windows 的同事照着找半天找不到。还有一个点配置文件里的 args 必须是绝对路径写相对路径 Claude Desktop 找不到。用 pwd 看完整路径再填进去。后端看 MCP写了一圈回到一个朴素的问题MCP 对后端工程师到底意味着什么。它不是什么新东西。本质上跟你写了十年的 REST API 一样——定义接口、处理请求、返回结果。MCP 不过是把请求方从浏览器换成了 AI。但换个角度想这意味着你的代码能力直接延伸到了 AI 的世界。以前你写的接口只有前端能调——用户点按钮 → 前端发请求 → 后端处理。现在你写的接口 AI 也能调——用户说句话 → AI 理解意图 → AI 调你的工具 → 返回结果。这条链路上你的代码是最后一公里——它连接着 AI 的理解能力和真实世界的业务数据。回头看那个类比MCP 就是 AI 世界的 REST API。 十年前后端学 REST现在后端学 MCP——逻辑没变能力范围变了。有一件事值得多想一步。REST API 的时代你的接口能力边界是前端决定的——前端能调什么你的系统就能做什么。MCP 时代你的接口能力边界是 AI 的理解能力决定的——AI 能理解用户的意图、能组合调用你的工具、能在多个工具之间做决策。同样是你的后端代码在 REST 时代是被动等调用在 MCP 时代是主动参与 AI 的推理链。这不只是换个调用方的事。它意味着你写的每一个工具都可能被 AI 以一种你没想到的方式组合使用——今天它拿天气工具查天气明天可能用天气工具 搜索工具 数据库工具自动生成一份出差报告。工具的排列组合效应是 MCP 区别于 REST 最本质的地方。上篇说关注 MCP 和 A2A 两个协议这篇算是把 MCP 从概念到代码走了一遍。A2AAgent-to-Agent是 Agent 之间的通信协议场景不一样后面再写。
MCP协议实战从零写个Agent工具
MCP协议实战从零写个Agent工具摘要MCP 就是 AI 世界的 REST API——你写好服务Claude 来调。前一篇聊了 AI Agent 的学习路线这篇落到具体动作用 Python 写一个 MCP Server把自己的 API 包装成 Agent 能认识的标准工具。不讲协议规范讲后端最关心的事——怎么让 AI 调你的服务。每个大模型的工具调用方式都不一样。OpenAI 是 function callingClaude 是 tool use国产模型各有各的格式。做后端的人最烦这种事——同一个查天气的接口接三个模型要写三套适配层。举个例子。你用 Spring Boot 写了个天气查询接口想让 AI 能调用。接 OpenAI你得按它的 function calling 格式写 JSON Schema字段名、参数类型全得按它的规范来。接 Claude换一套 tool use 的定义格式描述文案的写法也不一样。接国内的模型又是一套。最崩溃的不是第一次适配——是模型升级之后。OpenAI 改了个参数格式你的适配层报错了。Claude 更新了 tool use 的限制你之前能跑的工具现在调不起来了。三个模型三个迭代节奏你一个人维护三套适配代码。说实话适配到想吐。后来 Anthropic 搞了个 MCPModel Context Protocol把这事统一了。2024 年底提出2025 年生态炸开到 2026 年已经成了 Agent 工具调用的标配来源Anthropic 官方博客2024年11月。跟 HTTP 之于 Web 一样——大家都认这个协议www.ycsjb.com你写一次所有支持 MCP 的模型都能调。上篇聊了 AI Agent 的学习路线这篇不聊概念聊代码。MCP 是什么用后端的话说你用 Spring Boot 写过一个 REST 接口。bash 代码解读复制代码GET /api/weather?city北京→ { temp: 26, humidity: 60 }前端发起 HTTP 请求你的 Controller 处理逻辑返回 JSON——这条路你走了十年闭着眼睛都能写。MCP 干的事一模一样只是调用方从浏览器换成了 AI。arduino 代码解读复制代码Claude 想查天气 → MCP 协议 → 你的 MCP Server → 返回天气数据架构拆开来看arduino 代码解读复制代码┌──────────────────┐ ┌──────────────────┐│ MCP Client │ JSON-RPC 2.0 │ MCP Server ││ │ ◄──────────────────► │ ││ Claude Desktop │ stdio / HTTP/SSE │ 你写的 Python ││ Claude Code │ │ 服务 ││ Cursor / Cline │ │ │└──────────────────┘ └─────────┬────────┘│▼┌─────────────────┐│ 你的业务 API ││ ││ 天气接口 ││ 数据库查询 ││ 搜索引擎 │└─────────────────┘三个要点用后端的类比就够了MCP Server 你写的微服务。 用 server.tool() 装饰器注册工具函数跟 Spring 的 RestController 一样——声明这个方法对外暴露MCP 框架自动处理协议细节。MCP Client 调用方。 Claude Desktop、Claude Code、Cursor 这些 AI 工具都内置了 MCP 客户端。你配好 Server 地址它们自动发现你的工具、看懂工具描述、在需要的时候调用。通信协议 JSON-RPC 2.0。 跟 REST 的 HTTP JSON 一样MCP 用 JSON-RPC 做序列化支持 stdio本地进程通信和 HTTP/SSE远程部署。本机调试用 stdio部署到服务器换成 SSE。一句话以前你写 REST API 给浏览器调现在写 MCP Server 给 AI 调。换的是调用方不换的是定义接口 → 处理请求 → 返回结果这套逻辑。为什么要用 MCP 而不是直接把 API 丢给 AI两个原因。第一MCP 是标准协议。AI 客户端不用知道你的 API 地址、认证方式、参数格式——它只认 MCP 协议你的 Server 负责把内部 API 包装成标准工具。第二工具发现是自动的。你加了新工具AI 自动感知你改了工具描述AI 下次调用就用新的描述。不用通知任何一个客户端。动手写天气查询 MCP Server理论够了写代码。环境准备Python 3.103.10 以下跑不了后面踩坑会说装一个包bash 代码解读复制代码pip install mcp就一个依赖。FastMCP 把协议细节全封好了你只关心工具函数怎么写。核心代码python 代码解读复制代码from mcp.server.fastmcp import FastMCPmcp FastMCP(weather-server)mcp.tool()async def get_weather(city: str) - str:查询指定城市的实时天气信息返回温度、湿度、风向和天气概况# 这里接真实的天气 API示例用模拟数据return f{city}晴26°C湿度 60%北风 3 级if __name__ __main__:mcp.run(transportstdio)就这么几行。拆一下FastMCP(weather-server) —— 创建一个 MCP Server 实例名字随便起。mcp.tool() —— 装饰器注册工具跟 Spring 的 GetMapping 一个意思。函数签名 (city: str) - str —— 参数类型和返回值 MCP 框架会自动转成 JSON SchemaAI 客户端用它来判断什么时候调用。函数的 docstring —— 这个不是注释是 AI 看的东西。写什么、怎么写直接决定 AI 调不调用你的工具。后面专门说。mcp.run(transportstdio) —— 启动服务。stdio 走标准输入输出本机调试用部署到服务器换成 transportsse。三十行不到一个能跑的 MCP Server 就出来了。再加一个工具网页搜索只有一个工具太单薄。再加一个搜索工具让 AI 能查实时信息python 代码解读复制代码import httpxmcp.tool()async def search_web(query: str) - str:在互联网上搜索指定关键词返回前 3 条搜索结果摘要和链接。适用于需要实时信息的问题比如新闻、股价、最新动态。async with httpx.AsyncClient() as client:resp await client.get(https://api.search.example.com/v1/search,params{q: query, limit: 3},)data resp.json()return \n.join(f- {r[title]}: {r[snippet]} ({r[url]}) for r in data[results])两个工具注册完你的 MCP Server 就有了两样本事查天气、搜网页。AI 拿到这两个工具碰到今天北京热不热会调天气工具碰到最近 AI 圈有什么新闻会调搜索工具。先自己测一下接 Claude 之前先确认 Server 本身能跑。FastMCP 内置了调试模式bash 代码解读复制代码python server.py --debug或者在代码里用 mcp dev 命令启动一个本地测试界面直接在浏览器里手动调工具、看返回结果。这个调试界面还显示了 AI 看到的工具 Schema——你会看到你的 docstring 是怎么被转成 JSON Schema 的参数类型、返回值描述一目了然。这时候如果发现 Schema 不对回去改 docstring 就行。测通了再接 Claude省得配完配置才发现 Server 起不来两头排查更费劲。接 Claude让 AI 真正用起来服务写好了接下来让 Claude 认识它。Claude Desktop 配置macOS 上配置文件在这里javascript 代码解读复制代码~/Library/Application Support/Claude/claude_desktop_config.json打开加上这一段json 代码解读复制代码{mcpServers: {weather-server: {command: python,args: [/Users/tangyuewei/mcp-server/server.py]}}}command 是启动命令args 是脚本路径。保存重启 Claude Desktop。重启后Claude 对话框左下角会出现一个锤子图标——点开能看到你的两个工具get_weather 和 search_web描述就是你写的 docstring。这时候你问 Claude 北京今天天气怎么样它会自动调你的 get_weather。Claude Code 配置如果你用的是 Claude Code终端里的 Claude配法更简单。在项目根目录加一个 .mcp.jsonjson 代码解读复制代码{mcpServers: {weather-server: {command: python,args: [server.py]}}}然后在 Claude Code 里 /mcp 就能看到连接状态。绿点代表连上了可以调。HTTP 部署如果你想把服务部署到服务器上让团队共用把 stdio 换成 SSEpython 代码解读复制代码if __name__ __main__:mcp.run(transportsse, host0.0.0.0, port8080)客户端配置对应改成 URLjson 代码解读复制代码{mcpServers: {weather-server: {url: http://your-server:8080/sse}}}跟 REST 服务的部署套路一样——本机开发用 stdio上线换成 HTTP。踩过的坑写了几个 MCP Server 之后我把踩过的坑说三个。坑一工具描述写模糊了AI 不调用这个是最大的坑。mcp.tool() 的 docstringAI 是当工具说明书来读的。你说得越清楚AI 越知道什么时候调。我做过对比。描述写查询天气信息——调用率 70% 左右。改成了查询指定城市的实时天气信息返回温度、湿度、风向和天气概况——调用率跳到 95% 以上。加了适用于查询天气相关问题这句使用场景描述后误调用也少了。为什么差这么多因为 LLM 靠语义匹配决定调哪个工具。你写查询天气它只在用户明确说天气的时候调你写了温度、湿度、风向用户说今天热不热它也能匹配上。经验docstring 写三行——第一行说功能第二行说输入输出第三行说适用场景。别省这个功夫这是你跟 AI 之间唯一的接口文档。还有一个容易被忽略的点工具名也是有语义的。get_weather 比 fetch_wx 好因为 AI 能直接从名字推断功能。如果你用缩写或项目内部代号当工具名AI 看不懂调用率就低。工具名 描述一起决定了 AI 的调用意愿。坑二Python 版本MCP 官方依赖要求 Python 3.10。macOS 自带的 Python 3.9 跑不了。我第一次装了之后启动报 TypeError查了半小时才反应过来是版本问题。装个 pyenv 切到 3.12 解决bash 代码解读复制代码pyenv install 3.12pyenv local 3.12pip install mcp坑三配置文件路径Claude Desktop 的配置路径 macOS 和 Windows 不一样。macOS 是 ~/Library/Application Support/Claude/claude_desktop_config.jsonWindows 在 %APPDATA%\Claude\ 下面。网上很多教程只贴 mac 路径用 Windows 的同事照着找半天找不到。还有一个点配置文件里的 args 必须是绝对路径写相对路径 Claude Desktop 找不到。用 pwd 看完整路径再填进去。后端看 MCP写了一圈回到一个朴素的问题MCP 对后端工程师到底意味着什么。它不是什么新东西。本质上跟你写了十年的 REST API 一样——定义接口、处理请求、返回结果。MCP 不过是把请求方从浏览器换成了 AI。但换个角度想这意味着你的代码能力直接延伸到了 AI 的世界。以前你写的接口只有前端能调——用户点按钮 → 前端发请求 → 后端处理。现在你写的接口 AI 也能调——用户说句话 → AI 理解意图 → AI 调你的工具 → 返回结果。这条链路上你的代码是最后一公里——它连接着 AI 的理解能力和真实世界的业务数据。回头看那个类比MCP 就是 AI 世界的 REST API。 十年前后端学 REST现在后端学 MCP——逻辑没变能力范围变了。有一件事值得多想一步。REST API 的时代你的接口能力边界是前端决定的——前端能调什么你的系统就能做什么。MCP 时代你的接口能力边界是 AI 的理解能力决定的——AI 能理解用户的意图、能组合调用你的工具、能在多个工具之间做决策。同样是你的后端代码在 REST 时代是被动等调用在 MCP 时代是主动参与 AI 的推理链。这不只是换个调用方的事。它意味着你写的每一个工具都可能被 AI 以一种你没想到的方式组合使用——今天它拿天气工具查天气明天可能用天气工具 搜索工具 数据库工具自动生成一份出差报告。工具的排列组合效应是 MCP 区别于 REST 最本质的地方。上篇说关注 MCP 和 A2A 两个协议这篇算是把 MCP 从概念到代码走了一遍。A2AAgent-to-Agent是 Agent 之间的通信协议场景不一样后面再写。