CodeGym /课程 /ChatGPT Apps /MCP 的检查与调试:MCP Jam、Inspector、日志

MCP 的检查与调试:MCP Jam、Inspector、日志

ChatGPT Apps
第 6 级 , 课程 4
可用

1. 为什么需要 MCP 检查器

想象你在调试前端,却被禁止打开 DevTools。没有 MCP 检查器的生活大致如此。 MCP 协议在 ChatGPT 和 Apps SDK 的“引擎盖下”运行;如果你只看聊天里的回复并想着:“为什么它看不到我的工具?”,— 本质上就是在盲打。

MCP Inspector(官方)或 MCP Jam 这样的检查器,是面向开发者的专用 MCP 客户端。它们可以:

  • 与 ChatGPT 相同的方式连接到你的 MCP 服务器;
  • 执行握手 / 读取 capabilities;
  • 请求 tools/resources/prompts 列表;
  • 手动调用任意工具并传入自定义参数;
  • 显示原始 JSON‑消息(requests / replies / errors)。

本质上,它是“针对 MCP 的 Postman,但更聪明”。与普通 REST 客户端不同,检查器了解 MCP 的特性: 它理解 tools/listtools/call,能展示参数架构, 有时甚至支持受保护服务器的 OAuth 流程。

如果你没有检查器,调试通常是这样的:你启动 ChatGPT,尝试调用 App,看到“Error talking to app” 或者工具根本没有被调用,于是开始猜:是模型不想调用工具?是你的 MCP 没有启动?还是 JSON‑错误? 借助检查器你可以验证 每一层:先让 MCP 服务器与检查器单独对话,再看 ChatGPT ↔ MCP 的联调。

2. 检查器速览:MCP Inspector、Jam 及其他

在实践中,你最常会用到两类 MCP 检查器。

首先,是 官方 MCP Inspector(Model Context Protocol 仓库)。这是一个 Web 应用 (通常是基于 React 的 SPA),可以在本地启动,或通过 npx/Docker 启动, 并能通过 HTTP/SSE 连接到你的 MCP 服务器。

其次,是 MCP Jam 一类的检查器,它们通常还提供更便利的 OAuth 支持。 它们可以自动读取 .well-known/oauth-protected-resource,从中取出 authorization_endpointtoken_endpoint,完成 PKCE 流程, 并在已授权状态下访问 MCP。

MCP Jam 是在 MCP Inspector 基础上由开发者构建的。如果 MCP Inspector 实现了 最小功能集, 那么 MCP Jam 则实现了 开发者日常所需的一切。 我个人建议 直接使用 MCP Jam,这样后面就不用再切换工具了。

就本课程而言,差别在于:

  • 基础版 Inspector 始终需要,即使是最简单的未受保护 MCP 服务器;
  • MCP Jam(或同类)在你学到认证与授权模块时会变得尤为有用。

但总体思路一致:它就是一个普通的 MCP 客户端,只是能把 ChatGPT 默默进行的操作漂亮地展示出来。

3. 使用 MCP 检查器的典型流程

让我们过一遍典型场景:你在自己的 MCP 服务器中实现了一个新工具,想确认它确实能工作。

在上一讲你已经启动了最小化的 MCP 服务器。现在我们加上一套系统化的验证方法:按步骤跑完 “服务器 → 检查器 → JSON 逻辑”的完整循环。

步骤 1 — 启动 MCP 服务器

你在上一讲已经做过:假设你有一个脚本 npm run mcp-dev

# MCP 服务器启动示例
npm run mcp-dev
# 底层大概是:ts-node src/mcp-server.ts

关键是让服务器监听你选择的传输方式:本课程通常使用 HTTP 端点 /mcp, 端口例如 http://localhost:4001/mcp

步骤 2 — 启动 MCP Jam

第二个终端:

# 启动 MCP Jam 的一种方式
npx @mcpjam/inspector@latest
# 如有需要可添加 --port 4002 等参数

之后检查器会在浏览器中打开,通常是 http://localhost:6274 或类似端口。

在 MCP Jam 的起始页面,它会提示你填写 MCP 服务器的 URL。你输入:

http://localhost:4001/mcp

或者填写你的隧道地址(如果你通过 ngrok 等对外暴露)。

步骤 3 — 握手 / 能力(capabilities)

MCP Jam 一旦连接上,就会自动执行与 ChatGPT 相同的动作:

  1. 发送初始化请求(initialize),包含客户端信息。
  2. 接收包含协议版本和服务器 capabilities 的响应。
  3. 根据 capabilities 判断服务器是否支持 tools、resources、prompts 等特性。

在 UI 中通常显示类似:

Connected
Protocol: mcp/2025-06-18
Capabilities:
- tools: list, call
- resources: list, read
- prompts: list, get

如果在这一步检查器就无法连接(connection refused、CORS、500 等),你可以立即确定: 问题肯定 不在模型 和 ChatGPT,而在你的服务端或网络。

步骤 4 — 发现(discovery):查看 tools/resources/prompts

握手成功后,检查器通常会自动调用 tools/listresources/listprompts/list 来填充侧边栏。你会看到:

  • 带有描述和输入参数 JSON Schema 的工具列表;
  • 按集合/路径分组的资源列表;
  • 带有简短描述的 prompts 列表。

如果你刚添加了新工具却没有出现在列表中,说明它没有在服务器上正确注册, 或者服务器并未用更新后的代码启动。在这里发现要比猜为什么 ChatGPT “不愿意”调用你的工具容易得多。

4. 通过 MCP Jam 手动调用工具

MCP Jam 最有用的功能是 手动调用工具。这是你进行 tools/call 的专属 UI。

选择工具并填写参数

假设在上一模块你写了工具 suggest_gifts

// 位于 src/mcp/tools/suggestGifts.ts 的某处
export const suggestGiftsTool = {
  name: "suggest_gifts",
  description: "根据年龄、预算和兴趣推荐礼物创意",
  inputSchema: {
    type: "object",
    properties: {
      age: { type: "number" },
      budget: { type: "number" },
      interests: {
        type: "array",
        items: { type: "string" }
      }
    },
    required: ["age", "budget"]
  },
  // 处理器在其他位置定义
};

在 MCP Jam 中点击 suggest_gifts。右侧会根据 inputSchema 生成一个表单。 在那里你填写:

{
  "age": 30,
  "budget": 100,
  "interests": ["游戏", "书籍"]
}

然后点击“Call”或类似按钮。

检查器会发送 MCP 请求 tools/call,你会立刻看到:

  • 原始 JSON 请求数据(发送到服务器的具体内容);
  • 原始 JSON 响应数据(resulterror);
  • 可能还有更友好的结果预览。

在检查器中阅读 JSON 日志

通常检查器会显示类似:

// Request
{
  "id": "1",
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "suggest_gifts",
    "arguments": {
      "age": 30,
      "budget": 100,
      "interests": ["游戏", "书籍"]
    }
  }
}
// Reply
{
  "id": "1",
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "1) 桌面游戏 ... 2) 书店礼品卡 ..."
      }
    ]
  }
}

如果你的处理器抛出异常,你会看到 JSON‑RPC 风格的 error

{
  "id": "1",
  "jsonrpc": "2.0",
  "error": {
    "code": -32603,
    "message": "Internal error",
    "data": "TypeError: Cannot read properties of undefined ..."
  }
}

非常重要:你在这里看到的是 协议层。如果响应的格式不是 Apps SDK/ChatGPT 期望的, 你能在抱怨“GPT 的 bug”之前就发现。

5. 资源与提示(prompts)的调试

工具并不是 MCP 的全部。你已经知道,还有 resourcesprompts

通过检查器你可以:

  • 打开资源列表(resources/list)并查看其元数据;
  • 读取具体资源(resources/read)并确认返回数据正确;
  • 执行资源搜索(如果你实现了该能力);
  • 查看预设的 prompts 及其文本。

例如,如果你有一个资源 gift_catalog

// 注册资源的伪代码
registerResource({
  uri: "resource://giftgenius/catalog",
  name: "礼品目录",
  mimeType: "application/json",
  handler: async () => {
    return JSON.stringify(giftCatalogData);
  }
});

在检查器中你会看到该资源,点击即可直接查看 JSON。如果发现 JSON 无效, 或者 MIME 类型异常,— 你会在 ChatGPT 尝试读取或把它嵌入到小部件之前就把问题抓出来。

6. MCP 服务器日志:记录什么、写到哪里、如何记录

MCP Jam 很好,但还不够:你需要 MCP 服务器自身的日志。没有它,任何生产环境都将是一场彩票。

记录哪些内容

最有用的最小集合:

  • 每一条入站 MCP 消息(request/notification),包括:
    • 时间;
    • 方法(tools/calltools/list 等);
    • 工具名(如有);
    • 截断后的参数(不含敏感数据);
  • 每一条出站响应:
    • 状态(成功 / 错误);
    • 执行耗时;
    • 结果的精简版,或至少是类型;
  • 技术性错误:
    • JSON 解析;
    • 处理器中的意外异常。

同时非常重要:不要完整记录 PII 与密钥:token、密码、完整的敏感请求正文。 生产级日志建议通常强调只记录经过截断的 PII。

日志写到哪里:stdout / stderr

MCP 有一条重要要求:JSON‑消息必须走“正确的通道”,而所有调试日志走另一个通道。 比如,当你使用基于 stdout/stderr 的传输时:

  • JSON‑RPC 消息必须写到 stdout
  • 所有 console.logconsole.error 等都应写到 stderr

如果你把 JSON 与文本日志混在一个流中,客户端(MCP Jam 或 ChatGPT)就无法解析消息, 因为 JSON 中会突然出现类似 Server started at http://localhost:4001 的字符串。 这是 MCP 服务器中常见错误之一。

在 HTTP 场景中问题相对简单,但原则相同:HTTP 响应体必须是 纯 JSON, 而所有日志应写到控制台/文件,而不是写进响应体。

TypeScript 版 MCP 服务器的简单日志器

我们给示例 MCP 服务器加一个小日志器:

// src/logger.ts
export function logRequest(method: string, details: unknown) {
  console.error(
    JSON.stringify({
      level: "info",
      type: "request",
      method,
      details,
      ts: new Date().toISOString(),
    })
  );
}

export function logError(method: string, error: unknown) {
  console.error(
    JSON.stringify({
      level: "error",
      type: "error",
      method,
      error: String(error),
      ts: new Date().toISOString(),
    })
  );
}

并在工具的处理逻辑中使用:

// src/mcp-server.ts(片段)
server.setRequestHandler("tools/call", async (req) => {
  logRequest("tools/call", {
    name: req.params?.name,
    // 这里不要记录完整的 payload,只记录安全字段
  });

  try {
    const result = await handleToolCall(req);
    return result;
  } catch (e) {
    logError("tools/call", e);
    throw e;
  }
});

这样你就能在控制台看到结构化的 JSON 日志,之后可以根据 ts 或额外的 requestId 轻松地将它们对应起来。

7. 组合拳:MCP Jam + 日志

正确的 MCP 调试策略通常如下:

  1. 先在检查器中复现问题:看到 tools/list 返回空、 tools/call 报错、JSON‑响应异常等。
  2. 同时查看 MCP 服务器日志:启动时写了什么、每条消息有哪些错误、是否有 stack trace。
  3. 将日志中的 idmethodts 与检查器看到的内容进行对照。

例如,你在检查器中看到:

{
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}

并且在日志中同时看到:

{
  "level": "error",
  "type": "error",
  "method": "tools/call",
  "error": "TypeError: Cannot read properties of undefined (reading 'age')",
  "ts": "2025-11-21T10:15:12.345Z"
}

好了,诊断明确:某处处理器期望 age,但 schema/参数却不一致。

8. 小清单:MCP 服务器是否已准备好接入 App

在把 MCP 服务器接入真实的 ChatGPT App 之前,最好用检查器跑一遍小清单。

首先,握手与 capabilities 必须无错误。MCP Jam 应显示服务器支持你需要的实体: 至少 tools,如有需要,还包括 resources / prompts

其次,检查器中的 tools/resources/prompts 列表应与你认为已实现的工具、资源和提示集合一致。 诸如 name 拼写错误、忘记注册等问题会在这里立刻暴露。

第三,使用有效参数调用工具应稳定返回正确的 result。 建议尝试几类你在生产中确实会依赖的典型场景。

第四,使用无效参数的调用应返回 清晰的 error(JSON‑RPC 风格), 而不是 500。比如缺少必填参数时,最好返回结构化错误,便于 ChatGPT 转换成用户可读的信息。

第五,服务器日志不应在此过程中用海量 stack trace 淹没控制台。错误应结构化, 而敏感数据应被谨慎过滤。

如果这些在检查器中都通过了,就可以更放心地把 MCP 服务器接入 Apps SDK,并在开发者模式下玩转小部件。

9. MCP 服务器常见缺陷,以及如何借助检查器捕获它们

下面我们来看看最常见的问题——以及如何发现。

配置与连接

有时你觉得“服务器不工作”,但根因是它根本没有监听正确的端口或端点。 检查器会如实提示 connection refused 或完全无法连接。 常见原因:URL 填错(例如将 /mcp 写成 /api/mcp), 端口被其他进程占用、隧道未建立,或 CORS 拦截了请求。

无效 JSON / 将日志与协议混用

最棘手的问题之一——当你在 stdout 打印 console.log("Server started"), 而该通道本应承载 JSON‑RPC‑消息。客户端期待纯 JSON,却收到“文本 + JSON”,尝试解析并因格式错误而失败。

解决方法很简单:严格区分协议流(stdout 或 HTTP 响应体)与日志流(stderr 或独立日志文件)。

Schema 与工具实现不一致

另一个常见错误:在 inputSchema 中声明了一套,而代码期望另一套。 例如,schema 指定 age 为数字,interests 为可选的字符串数组, 而代码却调用 arguments.interests.toLowerCase()。模型(以及检查器)会老老实实把 interests 设为 null 或干脆不提供该字段——结果就会崩溃。

检查器可以让你清楚地看到,到底是哪段 JSON 真正被发往 tools/call, 并将其与你的代码一一对应。

tools/resources 的名称不匹配

如果在 capabilities / tools/list 中导出工具名为 suggest_gifts_v2, 而在 Apps 清单或小部件中期待的是 suggest_gifts,那么“找不到工具”会一直困扰你。 在检查器中,通过工具列表及其 name 字段可以立即看出来,无需猜测 GPT 的想法。

工具缓慢或卡死

如果在检查器中调用工具耗时 30 秒,然后因超时而失败,— 别指望 ChatGPT 的表现会更好。 MCP 检查器能帮助你定位到底卡在哪一环:网络调用、数据库,还是外部 API。 在日志中记录每个请求的开始与结束时间会很好,这样可以立刻发现异常值。

10. 进行 MCP 检查与调试时的常见错误

错误 1:试图只通过 ChatGPT 调试 MCP。
很多开发者先把 MCP 接到 App,看到“有些东西不工作”,就开始改提示词、改工具描述, 甚至修改模型版本。而此时 MCP 服务器根本没有启动,或者 tools/list 是空的。 务必从检查器开始:如果检查器里都不行,模型就根本不是问题所在。

错误 2:在同一通道中混用 JSON‑RPC 与日志。
当 MCP 客户端期待纯 JSON,而你在 stdout 打印调试字符串时,结果可想而知——解析失败, Inspector 显示奇怪错误。日志应走独立通道(stderr、日志文件、外部日志系统), 而协议消息必须严格走自己的通道。

错误 3:不查看 capabilities 与工具列表。
工具“消失”的常见原因只是你忘了注册它或开启相应的 capability。 如果不在检查器中查看 capabilitiestools/list, 你可能会一直以为是模型的问题,而不是你的注册代码。

错误 4:忽视 schema 错误与 JSON 不匹配。
inputSchema 与实际 JSON 不一致时,模型和检查器自然会表现异常。 如果你不查看检查器中的原始 JSON 消息,也不做 schema 校验,这些错误会在最意想不到的地方冒出来。

错误 5:把所有内容(包括 PII 和 token)都写入日志。
在调试的兴奋中,很容易把完整的请求体打进日志,其中可能包含个人数据或密钥。 在生产中这会变成一颗定时炸弹:数据泄露、合规问题等。 只记录对诊断确实必要的信息,并进行截断/匿名化。

错误 6:不使用最小化用例复现问题。
有时 Bug 出现在 ChatGPT 的复杂对话中,开发者就按原样去调试。 更有效的方式是在检查器中用一两次 MCP 请求复现实例,排除提示词、对话历史 以及模型“心情”的影响。

1
调查/小测验
MCP 协议第 6 级,课程 4
不可用
MCP 协议
MCP:协议、服务器与检查器
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION