1. 为什么要在 Postman 里调 MCP 接口
MCP(Model Context Protocol)是让大模型调用外部工具的一套协议,服务端跑起来之后,它对外暴露的通常是 SSE 或 Streamable HTTP 端点。很多同学第一次写完 MCP Server,浏览器一打开http://localhost:8080/sse看到一堆event: endpoint就懵了——这玩意儿到底怎么发请求、怎么拿工具列表、怎么调工具?
Postman 在这里的价值就体现出来了:它能把 MCP 的握手、初始化、工具列举、工具调用这几步拆开,让你清楚看到每一步的原始报文。相比直接写 Python 客户端,Postman 的好处是所见即所得,鉴权头、Content-Type、session id 都能手动改,出错时能立刻定位是协议层的问题还是业务层的问题。
这篇内容适合三类人:一是刚用 Spring AI 或官方 SDK 写完 MCP Server,想验证接口通不通的后端;二是要对接第三方 MCP 服务,需要先摸清对方返回结构的集成同学;三是排查线上 MCP 调用失败,想复现请求的运维。核心检索词就是 Postman 调试 MCP 接口,我会从请求构造讲到响应校验,把踩过的坑一并说清楚。
需要提前说明的是,MCP 的 SSE 传输是长连接,Postman 对它的支持是"能看能发",但流式刷新的体验不如专门的客户端。所以本文的定位是调试和验证,不是拿 Postman 当生产调用工具。
2. TaoToken 前置准备与 MCP 服务端环境
在动手之前,先把两件事准备好:一个能跑的 MCP Server,以及一个可用的模型调用入口。前者是你要调试的目标,后者是很多 MCP 场景里真正干活的那一环——因为 MCP 工具最终往往要被模型调度。
如果你手上还没有 MCP Server,可以用 Spring AI 的spring-ai-starter-mcp-server-webflux快速起一个。它的依赖很干净,一个 starter 加一个 web 依赖就够了。下面是我实测能跑通的pom.xml关键部分:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.5.7</version> </parent> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-server-webflux</artifactId> <version>1.1.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>6.2.12</version> </dependency> </dependencies>配置文件里最关键的是protocol: sse和两个端点路径。很多人调不通就是因为没注意sse-endpoint和sse-message-endpoint是分开的:前者是建立事件流的入口,后者是客户端回传消息的地址。
server: port: 8080 spring: main: banner-mode: off ai: mcp: server: name: my-weather-server type: ASYNC protocol: sse sse-endpoint: /sse sse-message-endpoint: /mcp logging: level: com.alibaba.cloud.ai.mcp.server: DEBUG io.modelcontextprotocol: DEBUG工具类用@Tool注解声明,参数用@ToolParam描述,这样模型和客户端都能读到语义信息:
@Service public class WeatherService { @Tool(description = "获取指定经纬度的天气预报") public String getWeatherForecastByLocation( @ToolParam(description = "纬度") double latitude, @ToolParam(description = "经度") double longitude) { return "经度:" + latitude + "维度:" + longitude + ",当前天气非常好"; } }最后用MethodToolCallbackProvider把工具注册进去,服务启动后访问http://localhost:8080/sse就能看到事件流。
至于模型侧,如果你后续要把 MCP 工具接到真实对话里,需要一个稳定的 API 入口。TaoToken 提供兼容主流协议的调用地址,Base URL 是https://taotoken.net/api,控制台在https://taotoken.net/console,API Key 在https://taotoken.net/api-keys生成。调试阶段建议先拿模型对话页https://taotoken.net/models验证 Key 是否可用,再去接 MCP。这样分层排查,出问题时能快速判断是模型侧还是 MCP 侧。
3. Postman 请求构造:SSE 握手与工具调用配置
这一节是全文的核心,我会把 Postman 里每一步的配置都写清楚,你可以直接照着填。
3.1 新建请求并选择 SSE 类型
打开 Postman,新建一个 Request。注意不是普通的 HTTP 请求,要在请求类型里选SSE(新版 Postman 在 URL 左侧的下拉里能找到)。如果你用的是旧版本没有 SSE 选项,那就用普通 GET,但流式响应会一次性返回,体验差一些。
URL 填你的 MCP 服务地址:
http://localhost:8080/sseMethod 选 GET。Headers 里加上:
Accept: text/event-stream Cache-Control: no-cache如果服务端配了鉴权,再加一行:
Authorization: Bearer <你的key>点 Send 之后,Postman 下方会持续输出事件流。你会先看到类似这样的内容:
event: endpoint data: /mcp?sessionId=xxxx-xxxx这个sessionId非常关键,后面所有工具调用都要带上它。很多人卡在这一步,就是因为没把 sessionId 记下来。
3.2 构造 initialize 请求
MCP 协议要求客户端先发initialize,服务端返回能力声明后,再发notifications/initialized才算握手完成。在 Postman 里新建一个 POST 请求,URL 用上一步拿到的 message endpoint:
http://localhost:8080/mcp?sessionId=xxxx-xxxxHeaders:
Content-Type: application/json Authorization: Bearer <你的key>Body 选 raw + JSON,内容如下:
{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": { "name": "postman-client", "version": "1.0.0" } } }发送后正常会返回result,里面包含serverInfo和capabilities。如果返回-32600或-32700,多半是 JSON 格式或 method 名写错了。
3.3 列举工具与调用工具
握手完成后,发tools/list拿工具清单:
{ "jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {} }返回里会有tools数组,每个工具带name、description、inputSchema。拿到 name 之后就能调用了:
{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "getWeatherForecastByLocation", "arguments": { "latitude": 39.9, "longitude": 116.4 } } }参数名必须和inputSchema里定义的完全一致,大小写都不能错。我试过把latitude写成lat,服务端直接返回参数校验失败。
3.4 可复制的 Postman Collection 片段
为了省去你手动建请求的麻烦,下面这段 Collection JSON 可以直接导入 Postman(File → Import → Raw text):
{ "info": { "name": "MCP Debug", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [ { "name": "SSE Handshake", "request": { "method": "GET", "header": [ { "key": "Accept", "value": "text/event-stream" }, { "key": "Authorization", "value": "Bearer {{mcp_key}}" } ], "url": { "raw": "{{mcp_base}}/sse", "host": ["{{mcp_base}}"], "path": ["sse"] } } }, { "name": "Initialize", "request": { "method": "POST", "header": [ { "key": "Content-Type", "value": "application/json" }, { "key": "Authorization", "value": "Bearer {{mcp_key}}" } ], "body": { "mode": "raw", "raw": "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{},\"clientInfo\":{\"name\":\"postman\",\"version\":\"1.0.0\"}}}" }, "url": { "raw": "{{mcp_base}}/mcp?sessionId={{session_id}}" } } } ], "variable": [ { "key": "mcp_base", "value": "http://localhost:8080" }, { "key": "mcp_key", "value": "" }, { "key": "session_id", "value": "" } ] }把mcp_key和session_id填成实际值即可。这样每次调试只需要改环境变量,不用重复建请求。
4. 验证请求与成功结果判读
配置好之后,怎么判断一次 MCP 调试是成功的?我总结了三个观察点。
第一,SSE 握手阶段必须收到event: endpoint。如果连接建立后一直空白,说明服务端没推事件,检查sse-endpoint配置和端口是否被占用。如果收到的是event: error,看 data 里的错误码。
第二,initialize的返回里result.protocolVersion要和你请求里的一致。如果服务端返回的版本更高,客户端要按服务端版本重试。这一步成功后再发notifications/initialized,注意它是通知,没有 id,服务端不返回结果:
{ "jsonrpc": "2.0", "method": "notifications/initialized" }第三,tools/call的返回结构是result.content数组,里面每个元素有type和text。比如天气工具返回:
{ "jsonrpc": "2.0", "id": 3, "result": { "content": [ { "type": "text", "text": "经度:39.9维度:116.4,当前天气非常好" } ] } }看到这个结构就说明整条链路通了。如果content为空但没报错,检查工具方法是不是返回了 null。
流式响应方面,Postman 的 SSE 面板会实时追加事件。工具调用如果耗时较长,服务端可能先推event: message再推结果,注意区分事件类型。实测下来,Postman 对多段 SSE 的渲染偶尔会合并显示,建议同时开一个终端用curl -N对照:
curl -N -H "Accept: text/event-stream" http://localhost:8080/sse这样能看到最原始的分包情况,排查流式问题时特别有用。
另外,如果你要把 MCP 工具接到模型做端到端验证,可以在 TaoToken 的模型对话页发一条会触发工具调用的指令,观察模型是否正确选择了工具、参数是否传对。这一步能把 MCP 协议层和模型调度层分开验证,定位问题更快。
5. 常见报错排查对照表
调试 MCP 接口时,报错信息往往比较隐晦。下面这张表是我实际遇到过的错误和对应处理方式。
| 报错现象 | 可能原因 | 处理方式 |
|---|---|---|
| 401 Unauthorized | Authorization 头缺失或格式不对 | 确认是Bearer <key>,key 没过期 |
| local proxy failed | Postman 代理设置拦截了 localhost | 关闭系统代理或把 localhost 加入白名单 |
| reading 'choices' 报错 | 把模型接口和 MCP 接口混用 | MCP 走/mcp,模型走/v1/chat/completions,别搞混 |
| OAuth 相关错误 | 服务端要求 OAuth 但客户端只发了 Bearer | 检查服务端鉴权配置,或改用 OAuth token |
| sessionId 无效 | 握手后没带 sessionId 或已过期 | 重新走 SSE 握手拿新 sessionId |
| -32601 Method not found | method 名拼写错误 | 确认是tools/list不是tool/list |
| -32602 Invalid params | 参数名或类型不匹配 | 对照inputSchema逐个核对 |
| 连接一直 pending | SSE 端点路径不对 | 确认sse-endpoint和实际访问路径一致 |
重点说两个高频坑。第一个是local proxy failed,这个在 Windows 上特别常见,因为 Postman 默认会读系统代理,而 localhost 请求被代理转发后就失败了。解决办法是在 Postman 设置里关掉 "Use System Proxy",或者把localhost,127.0.0.1加到 "Proxy bypass" 列表。
第二个是reading 'choices',这个报错通常出现在你误把 MCP 的 message endpoint 当成 OpenAI 兼容接口来调。MCP 用的是 JSON-RPC 2.0,返回结构里根本没有choices字段。如果你确实需要模型能力,应该走https://taotoken.net/api的对话接口,而不是 MCP 端点。两者协议不同,别混着调。
还有一个容易被忽略的点:MCP 的 SSE 连接是有超时的。如果 Postman 里长时间不发消息,服务端可能主动断开,此时再发tools/call就会失败。建议每次调试前重新握手,或者把服务端的超时时间调大。
6. 把调试链路固化下来
调通一次之后,建议把 Postman 环境变量和 Collection 保存成团队共享的模板。这样别人接手时不用从零摸索,直接改mcp_base和mcp_key就能复现。
如果你后续要做更完整的验证,比如让模型真正调用这些 MCP 工具,可以按这个顺序推进:先用 Postman 确认 MCP 协议层没问题,再用模型对话页确认模型能正确选择工具,最后把两者串起来跑端到端。API Key 在https://taotoken.net/api-keys管理,接入文档在https://taotoken.net/doc,需要长期跑编码或 Agent 任务的话,Coding Plan 页面https://taotoken.net/coding-plan有对应的方案说明。
调试 MCP 最忌讳的就是一上来就接模型,出了问题分不清是协议错还是模型错。把 Postman 这一层打通,后面的事情会顺很多。