CodeGym /课程 /ChatGPT Apps /本地调试:日志、MCP 检查与 Dev Mode

本地调试:日志、MCP 检查与 Dev Mode

ChatGPT Apps
第 7 级 , 课程 1
可用

1. 为什么需要一节专讲本地调试

在之前的模块中,我们已经拆解了 Apps SDK 以及 MCP 的栈结构。现在来谈谈,为什么有必要专门讲本地调试。

很多人的路径是这样:“我就打开 ChatGPT,写一句‘使用我的 App’,然后看看它说什么。如果不行——我就随便改点代码试试”。这就像只盯着浏览器里的 HTML 页面来修后端,从不打开服务器日志。

ChatGPT Apps 里尤其容易滑向“魔法思维”:有 GPT,它决定是否调用工具,也有自己的错误处理逻辑。如果你看不到底层发生了什么,调试就会变成“敲锣打鼓求神仙”。

我们的目标:把它变成正常的工程流程:

  • 你知道去哪里看 Next/MCP 的日志;
  • 你会用 Inspector 手动“拉” MCP 服务器;
  • 你明白 Dev Mode 检查的是什么,并能确认 ChatGPT 的确能访问到你的服务器。

最重要的是:你不再用“GPT 猜谜”来调试,而是先检查栈的底层——服务器与协议,然后才看 UI 与模型行为。

2. 心智模型:三层调试

为了不陷入混乱,我们约定用三层来思考调试。这是我们的小“千层蛋糕”:

层级 里面是什么 常见症状 用什么调试
UI(小部件) React 组件、状态、window.openai 小部件空白/灰屏、渲染异常、按钮无响应 浏览器 DevTools
后端 / MCP 服务器 工具、对 DB/API 的访问 500 错误、“工具崩溃”、数据异常 服务器日志、MCP Inspector
MCP 协议 JSON‑RPC、tools/listtools/call、Schema GPT 显示“无法调用工具”、invalid params Inspector + 请求日志

第二层我们关注 MCP 服务器本身在做什么(工具、DB、API),第三层则关注“线路”和 MCP 消息格式(JSON‑RPC、Schema 等)。

这三层就是本讲与整个调试课程的主线。

更直观一点,可以看一下请求的流转:

sequenceDiagram
    participant User as 用户
    participant ChatGPT as ChatGPT (Dev Mode)
    participant Tunnel as 隧道(ngrok/CF)
    participant Next as Next.js + MCP

    User->>ChatGPT: "挑选 50 美元以内的礼物"
    ChatGPT->>Next: tools/call search_gifts (通过隧道)
    Next->>Next: 调用 MCP 工具,访问数据库/API
    Next-->>ChatGPT: JSON-RPC result + ToolOutput
    ChatGPT-->>User: 回复 + 渲染小部件

任何一点都可能出问题:隧道、endpoint、MCP 逻辑、JSON Schema、React 小部件。调试的关键——先定位是哪个层出了错,而不是一上来就全改。

3. Next.js 与 MCP 日志:一切的基础

从最枯燥却最有用的东西开始——日志。

本地开发时日志在哪里

在基于 Next.js 的 Apps SDK 模板中,MCP 服务器通常包在一个 API 路由里(比如 /api/mcp)。你运行 npm run dev,一个终端里会有:

  • Next.js 的开发服务器;
  • MCP 的 endpoint 处理器,接收 tools/listtools/call 等 JSON‑RPC 请求;
  • console.log/console.error 打印的一切“精彩内容”。

如果你把 MCP 拆成了独立进程,会多一个终端,但思路一样:有用的信息都在控制台里。

注意区分:

  • 构建/启动错误——next dev 起不来、TypeScript 崩了、import 不对等;
  • 运行时错误——都起来了,但对 /api/mcp 的某个请求导致工具崩溃。

Next.js 在开发模式下会用覆盖层显示运行时错误,并把 stack trace 写进控制台。

MCP 服务器里该打什么日志

虽然 MCP 用的是 JSON‑RPC 协议,但调试并不需要把完整 JSON 全打出来。更有用的是结构化且简短的日志。

一条不错的 MCP 日志实践:至少记录 timestamprequest_id/traceId、 工具名、 参数(脱敏)、 状态(ok/error) 以及耗时。

GiftGenius 的一个最简单 logger.ts 可能是这样:

// src/lib/logger.ts
export function logToolEvent(
  phase: "start" | "end" | "error",
  data: Record<string, unknown>
) {
  const ts = new Date().toISOString();
  console.log(JSON.stringify({ ts, phase, ...data }));
}

而在工具处理器中:

// src/mcp/tools/searchGifts.ts
import { logToolEvent } from "@/lib/logger";

export async function searchGiftsTool(args: { q: string }) {
  const traceId = crypto.randomUUID();

  logToolEvent("start", { tool: "search_gifts", traceId, args });

  try {
    // ... 实际的礼物搜索 ...
    const results = []; // 占位

    logToolEvent("end", { tool: "search_gifts", traceId, count: results.length });

    return results;
  } catch (err) {
    logToolEvent("error", { tool: "search_gifts", traceId, error: String(err) });
    throw err;
  }
}

有两点很重要。

其一,不要在日志中存完整的邮箱、手机号、卡号、token。这不仅不合适,还与 MCP 的基础安全实践相冲突。

其二,traceId 是你最好的朋友。把 Next.js 和 MCP 日志放一起看时,它能把事件串起来:某次 tools/call 请求、对应的 React 渲染,以及小部件的网络日志。

如何通过日志判断“哪儿崩了”

终端里不断滚着来自 logToolEvent 的 JSON。一种典型的场景:

  • 收到 phase: "start"tool: "search_gifts"
  • 没有 phase: "end",却出现了 phase: "error" 与 stack trace;
  • 这表明工具确实进入了你的业务逻辑,但在里面某处崩了——比如外部 API 请求、解析或 DB 操作。

如果你压根看不到该工具名对应的日志——说明请求根本没到工具。那就顺着栈往上查:隧道、/mcp endpoint、tools/call 的 JSON 请求。

4. MCP Inspector:在 ChatGPT 之前调试 MCP

如果日志是你的“眼睛”,那 MCP Inspector(或 MCPJam Inspector)就是“显微镜”。

MCP Inspector 是什么、为什么要用它

在 MCP 模块里我们已经接过 Inspector 来验证“Hello, MCP” 服务器了。这里把它当作核心调试工具:先确保 MCP 自己正常,再去看 Dev Mode 与 UI。

Inspector 是一个独立应用(多为 Web‑UI + CLI),扮演 MCP 客户端。它通过 HTTP/SSE 或 stdin/stdout 连接到你的服务器,执行 tools/listtools/call,并展示原始 JSON 消息、握手、工具列表、资源等。

核心想法:把 ChatGPT 从等式里移开。如果工具不工作,你要先搞清楚服务器是否存活、协议与 Schema 是否正确,而不是先怀疑 GPT。

使用 Inspector 的迷你流程

本地调试的典型流程:

  1. 启动 npm run dev,让 Next.js + MCP endpoint 跑起来。
  2. 启动 MCP Inspector,例如:
npx @modelcontextprotocol/inspector

(具体命令取决于你使用的工具)。

  1. 在 Inspector 中填入 MCP endpoint 的 URL,比如 http://localhost:3000/api/mcp(若想一并验证隧道,也可填 HTTPS 隧道地址)。
  2. 查看握手是否成功:服务器应返回支持的能力、工具列表、资源等。
  3. 手动调用目标工具:选择 search_gifts,输入参数 {"q": "30 岁以下的女生"},点击 “Call tool”,然后观察:
    • 是否返回了响应;
    • 是否出现 JSON‑RPC 或 MCP 的错误;
    • 服务器针对这次调用打印了哪些日志。

如果在 Inspector 里就已经失败,根本不用打开 ChatGPT:先修 MCP 服务器。

如果在 Inspector 一切正常,而 ChatGPT 仍旧报错——那问题更高一层:Dev Mode URL、鉴权、模型行为。

“故意把工具弄崩”示例

拿我们的 search_gifts 来故意整坏一下:

export async function searchGiftsTool(args: { q: string }) {
  if (args.q === "崩溃") {
    throw new Error("用于演示调试的教学错误");
  }
  // ... 正常逻辑 ...
  return [];
}

接下来:

  1. 在 Inspector 中以参数 {"q": "崩溃"} 调用 search_gifts
  2. 在日志里看到 phase: "error" 与 stack trace。
  3. 确认 MCP 服务器如实返回了错误。

之后,当你把它接到 ChatGPT Dev Mode 并让模型“挑选包含‘崩溃’一词的礼物”时,它会尝试调用工具,并向用户展示类似“I encountered an error running the tool”的消息。可以看出:错误不是模型引起的,而是你明确抛出的异常。

这种方法能训练你的思维:清楚地区分业务错误(我们自己抛了 Error)与协议错误(JSON 结构坏了、工具名不对等)。

5. 小部件调试:DevTools、状态与“调试横幅”

当 MCP 服务器基本清楚后,转到前端——Apps SDK 小部件。

在哪里、怎么查看小部件错误

你的小部件在 ChatGPT 的 iframe 沙箱里渲染。好消息是:这个 iframe 也有完整的浏览器 DevTools。

迷你步骤:

  1. 在浏览器(Chrome/Edge/Firefox)中打开 ChatGPT。
  2. 打开 DevTools(通常是 F12Ctrl+Shift+I)。
  3. 在 Console 标签页选择你的部件所在的 frame 上下文(通常是域名 web-sandbox.oaiusercontent.com)。
  4. 刷新聊天/发送消息,让 GPT 显示你的 App

如果小部件:

  • 根本没出现;
  • 显示为灰色/空白;
  • 在控制台出现红色错误

——几乎可以断定是 React 代码问题:访问了不存在的属性、错误的 import、hook 用错等。

Network 标签页也很有用,你能看到:

  • 你的应用 JS bundle 的加载(如果是 404/500——那是 dev 服务器/隧道侧的问题);
  • 小部件通过 window.fetch 发起的请求以及返回的 4xx/5xx

最简单的调试横幅

一个非常实用的小技巧——在小部件根组件加一个“调试横幅”,在 Dev Mode 显示当前环境与构建版本。

例如:

// src/components/DebugBanner.tsx
export function DebugBanner() {
  if (process.env.NODE_ENV !== "development") return null;

  return (
    <div style={{ padding: 4, background: "#222", color: "#0f0", fontSize: 10 }}>
      ENV: dev | build: local | {new Date().toLocaleTimeString()}
    </div>
  );
}

并在小部件根组件中:

// src/app/widget/page.tsx
import { DebugBanner } from "@/components/DebugBanner";

export default function GiftGeniusWidget() {
  return (
    <div>
      <DebugBanner />
      {/* 其余的礼物搜索 UI */}
    </div>
  );
}

如果你打开了 ChatGPT、启动了 App,却看不到横幅——说明你的 JS 根本没到浏览器:要么构建出错、要么 endpoint 有问题、要么小部件压根没在 MCP 服务器里注册。

本地状态与错误处理

你的小部件应该能展示不同状态:加载、成功、错误。如果还没有——现在就加上。

迷你模式:

const [status, setStatus] = useState<"idle"|"loading"|"error"|"success">("idle");

async function handleSearch(query: string) {
  try {
    setStatus("loading");
    // 通过 window.openai.callTool 或 Apps SDK 的 hook 来调用 MCP 工具
    setStatus("success");
  } catch (e) {
    console.error("Search failed", e);
    setStatus("error");
  }
}

在 JSX 中:

{status === "error" && (
  <div style={{ color: "red" }}>出了点问题,请重试。</div>
)}

对调试来说,务必做到:

  • 不要吞掉异常(否则控制台一片静默,UI 只是“挂住”);
  • 在 UI 中明确展示错误,否则用户会以为 App 死了。

6. 把 Dev Mode 纳入调试:它做什么、何时别冤枉它

现在把 ChatGPT Dev Mode 纳入全景。在此之前我们只看你的代码。但有时本地一切正常、Inspector 里也很好,ChatGPT 依然提示 “Error talking to [AppName]”,甚至根本不提供你的 App

Dev Mode 做什么

Dev Mode 是 ChatGPT 的一个模式,你可以:

  • 创建和编辑自己的 Apps;
  • 指定 MCP 服务器的 endpoint(通常是 https://your-domain/mcp/api/mcp);
  • 无需上架 Store 就能快速更新清单和元数据。

从调试角度看,Dev Mode 只是另一层配置:

  • 如果那里填了错误的 URL;
  • 如果你忘了在末尾加 /mcp
  • 如果隧道换了新域名,你却没更新设置

——ChatGPT 就压根访问不到你的服务器。

Dev Mode 常见“翻车”场景

经典四步:

  1. 你开了一个隧道 https://abcd.ngrok.io,在 Dev Mode 里配置了它,一切正常。
  2. 第二天重启 ngrok,得到 https://efgh.ngrok.io
  3. Dev Mode 里还是 https://abcd.ngrok.io/mcp
  4. ChatGPT 显示“Error talking to GiftGenius”。

这时把 MCP Inspector 指向 http://localhost:3000/api/mcp 会发现一切正常。这意味着 MCP 是活的,但 ChatGPT 看错了地址。

解决:进入 Dev Mode 设置,更新 URL,别忘了末尾的 /mcp

Dev Mode vs Store

本讲只讨论 Dev Mode——你的沙箱。在这里频繁改 URL、重连隧道、调整工具 Schema 都没问题。

将来进入 Store 后,endpoint 会更加固定,这些“花活”就不合适了。但离 Store 还有几个模块,现在先放心地在 Dev Mode 里改和修。

7. 调试小算法:当“一切都不工作”时怎么办

把一切收束为可执行的流程。其实就是开头那三层,只是写成步骤。

假设你打开 ChatGPT,选择 GiftGenius,发:“为极客朋友挑选 30$ 以内的礼物”,然后:

  • GPT 并不提到你的 App
  • 或提示 “Error talking to GiftGenius”;
  • 或小部件空白/灰屏。

如何不绝望?

步骤 1(MCP/服务器层):用 Inspector 与日志验证 MCP

先别管 GPT 与 UI。只看服务器。

  1. 确保 npm run dev 在跑,且 endpoint(/api/mcp)有响应。
  2. MCP Inspector 连到 http://localhost:3000/api/mcp 或你的隧道。
  3. 检查握手——应能看到工具列表。
  4. 手动调用与 GPT 预期相同的工具(比如 search_gifts),参数尽量相似。

如果此处就挂了——修 MCP:Schema、业务逻辑、网络调用。用日志与 traceId 来定位哪儿坏了。

步骤 2(协议/Dev Mode 层):检查 Dev Mode 与 URL

如果 Inspector 一切 OK,但 ChatGPT 仍然看不到你的 App 或报连不上:

  1. 打开你的 App 的 Dev Mode 设置。
  2. 看看为 MCP 填的 URL 是什么。
  3. 与服务器/隧道实际监听的地址对一下(别忘了,如果你的服务器需要,末尾要有 /mcp)。

很多问题就出在这里。

步骤 3(UI 层):用 DevTools 检查小部件

如果 ChatGPT 成功调用了工具(从 MCP 日志可见),但小部件异常:

  1. 在 ChatGPT 页面打开浏览器 DevTools。
  2. Console 标签页选择你的小部件 iframe 上下文。
  3. 查看 JS 错误。
  4. Network 标签页确认:
    • 小部件的 JS bundle 能正常加载,不是 404/500
    • 后续请求(通过 fetch/window.openai.fetch)有合理的响应。

同时看你的 DebugBanner:如果没出现,说明根本没渲染到 React 树。

步骤 4:用 Dev Mode 复现 bug 报告

拿到同事/用户的 bug 报告时,尽量保留触发问题的精确 prompt。用 Dev Mode 可很快复现:

  1. 启动 npm run dev,打开隧道。
  2. 在 Dev Mode 里选择你的 App
  3. 粘贴有问题的 prompt。
  4. 同时:
    • 在日志里看 MCP 收到的 JSON 请求;
    • 必要时,在 Inspector 里用相同参数重复 tools/call

这样你就把“偶尔不工作”变成了可复现场景。

8. 一些便捷调试的代码细节

为巩固内容,给我们的 GiftGenius 再加几段实用代码。

环境配置与日志级别

在服务器配置里显式写出 MCP endpoint 与日志级别很方便:

// src/config.ts
export const config = {
  mcpEndpoint:
    process.env.NODE_ENV === "development"
      ? "http://localhost:3000/api/mcp" // 隧道会覆盖这个
      : "https://api.giftgenius.com/api/mcp",
  logLevel: process.env.NODE_ENV === "development" ? "DEBUG" : "ERROR",
};

同时在 logToolEvent 里可参考 logLevel,避免生产环境刷屏。

记录结构化的 MCP 错误

在处理工具时,尽量捕获预期错误并返回可理解的信息,而不是一股脑 throw

export async function searchGiftsTool(args: { q: string }) {
  const traceId = crypto.randomUUID();
  logToolEvent("start", { tool: "search_gifts", traceId, args });

  try {
    // ... 正常代码 ...
    return { content: [{ type: "text", text: "找到 3 个礼物" }] };
  } catch (err) {
    logToolEvent("error", { tool: "search_gifts", traceId, error: String(err) });

    return {
      content: [{ type: "text", text: "搜索礼物出错。请稍后再试。" }],
      isError: true,
    };
  }
}

这样 ChatGPT 会看到结果被标注为 isError,能更好地向用户说明问题,而你也能从日志里看清发生了什么。

9. 本地调试 ChatGPT App 的常见错误

错误 1:通过“GPT 猜”来调试,而不是通过服务器与 Inspector。
只盯着模型回答来猜 bug 很诱人。但模型是最上层。如果 MCP 服务器本身(通过 Inspector、手工)都不正常,就别指望 GPT 出奇迹。先把 MCP 跑稳,再接 ChatGPT。

错误 2:要么不看日志,要么什么都往里记。
没日志会让你“眼盲”:不知道哪个工具被调用、带了什么参数、结果如何。反过来,过度日志会把控制台变成无关字符串的“矩阵”。更好的做法是简洁、结构化的日志,包含 toolargs(脱敏)、traceIdstatus 与耗时。

错误 3:在日志里存储敏感数据。
记录 token、完整邮箱与卡号是糟糕做法,不论安全还是 OpenAI 政策。日志只应该包含对调试真正有用的信息,个人数据要么脱敏要么不写。

错误 4:把所有问题都怪在 Dev Mode 头上。
Dev Mode 常常被当作“背锅侠”:“OpenAI 又把什么搞坏了”。事实上,很多时候是你忘了在重启隧道后更新 URL,或者路径填错了(/ 写成了 /mcp)。在联系支持前,先去 Dev Mode 设置对一下 endpoint 与服务器的实际地址。

错误 5:忽视 DevTools 与小部件的错误。
小部件空白或灰屏几乎总是客户端 JavaScript 错误。如果你只看 MCP 日志,不在 ChatGPT 里打开 DevTools,你只看到了半张图。养成按 F12、查看 Console/Network 的习惯能省下大量时间。

错误 6:尝试用“魔法延迟”来“修” bug。
有时会想加个 setTimeoutThread.sleep 延迟“让它都加载完”。在 MCP/Next/React 的世界里,这几乎总是错的:问题通常在 Schema、endpoint 或代码错误,不是“服务器没来得及”。与其用延迟糊住,不如找清楚哪里断了(Inspector → Dev Mode → 小部件)。

错误 7:本地未跑通就上 Vercel。
“尽快上生产”的心情可以理解,但把坏掉的 MCP 丢到 Vercel,是制造“两层问题”(本地 + 生产)的绝佳办法。本模块我们刻意要求:先 MCP Jam/Inspector → 一切 OK,Dev Mode → 基本场景跑通,最后再部署。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION