CodeGym /課程 /ChatGPT Apps /本機除錯:日誌、MCP 檢查與 Dev Mode

本機除錯:日誌、MCP 檢查與 Dev Mode

ChatGPT Apps
等級 7 , 課堂 1
開放

1. 為什麼需要一堂關於本機除錯的課

在先前的模組中,我們已經拆解過 Apps SDK 與 MCP 的堆疊。現在來談談,為什麼還需要專門一堂關於本機除錯的課。

很多人的路徑是這樣: 「嗯,我就打開 ChatGPT,寫『使用我的 App』,然後看看它說什麼。如果不行——就隨便改改程式碼。」 這大概就像只看瀏覽器裡的 HTML 頁面來修後端,卻從不打開伺服器日誌。

對於 ChatGPT Apps 特別容易滑向「魔法」:有 GPT,它自己決定要不要呼叫 tool,還有自己的錯誤邏輯。如果你看不到底層在發生什麼,除錯就會變成跳大神。

我們的目標:把這一切變成正規的工程流程:

  • 你知道在哪裡看 Next/MCP 的日誌;
  • 你能用手動方式透過檢查器呼叫 MCP 伺服器;
  • 你明白 Dev Mode 會檢查什麼,以及如何確認 ChatGPT 根本能連到你的伺服器。

最重要的是:你不再用「猜猜看 GPT」來除錯,而是先檢查堆疊的低層——伺服器與通訊協定,再來才是 UI 與模型行為。

2. 心智模型:三個除錯層級

為了不在混亂中沉沒,我們把除錯想成三個層級。可以把它當作我們的小「千層蛋糕」:

層級 裡面是什麼 常見症狀 用什麼除錯
UI(Widget) React 元件、狀態、window.openai Widget 空白/灰屏、渲染異常、按鈕無作用 瀏覽器 DevTools
後端 / MCP 伺服器 tools、存取資料庫/API 500 錯誤、「tool 掛了」、資料怪異 伺服器日誌、MCP Inspector
MCP 通訊協定 JSON‑RPC、tools/listtools/call、Schema GPT 顯示「無法呼叫工具」、invalid params 檢查器 + 請求日誌

在第二層,我們關心 MCP 伺服器本身(tools、資料庫、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 tool,連到資料庫/API
    Next-->>ChatGPT: JSON‑RPC 結果 + ToolOutput
    ChatGPT-->>User: 回覆 + 渲染 Widget

任何一點都可能出錯:隧道、endpoint、MCP 邏輯、JSON Schema、React Widget。你的除錯任務是找出究竟是哪一層出了問題,而不是一股腦兒把所有東西重寫。

3. Next.js 與 MCP 的日誌:一切的基礎

從最無聊也最有用的東西開始——日誌。

本機開發時,日誌在哪裡

在 Next.js 的 Apps SDK 標準樣板中,MCP 伺服器通常包在 API route(例如 /api/mcp)裡。你執行 npm run dev 後,單一終端機裡會同時跑:

  • Next.js 的開發伺服器;
  • MCP endpoint 的處理器,接收 tools/listtools/call 等 JSON‑RPC 請求;
  • console.log/console.error 打印的一切精彩內容。

如果你把 MCP 拆成獨立行程,就會有第二個終端機,但概念一樣:有趣的東西都在主控台看得到。

請分清楚:

  • 建置/啟動錯誤——next dev 起不來、TypeScript 掛掉、匯入錯誤等;
  • 執行時錯誤——都啟動了,但對 /api/mcp 的特定請求導致某個 tool 掛掉。

Next.js 在開發模式會用漂亮的浮層顯示 runtime 錯誤,並在主控台列出 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;
  }
}

有兩個重要細節。

第一,不要在日誌中保留完整的 e‑mail、電話、卡號、token。這不只不妥,也違反 MCP 的基本安全實務。

第二,traceId 是你最好的朋友。當你同時看 Next.js 與 MCP 的日誌時,可以用它把事件串起來:特定的 tools/call 請求、對應的 React 渲染,以及 Widget 的網路日誌。

如何從日誌判斷是哪裡出錯

終端機裡滾著 logToolEvent 打出的 JSON 行。典型情境:

  • 出現 phase: "start" 搭配 tool: "search_gifts"
  • 沒有 phase: "end",但有 phase: "error" 與 stack trace;
  • 由此可見,請求已經進入你的工具邏輯,但內部有東西壞了——例如外部 API 請求、剖析、或資料庫操作。

如果你根本看不到對應 tool 名稱的任何日誌——代表請求甚至沒到工具。那就往上查:隧道、/mcp endpoint、tools/call 的 JSON 請求。

4. MCP Inspector:在接入 MCP 給 ChatGPT 前先把它除錯好

如果日誌是你的眼睛,MCP Inspector(或 MCPJam Inspector)就是顯微鏡。

關於 MCP Inspector 與它的用途

在 MCP 模組中,我們已經用 Inspector 來驗證「Hello, MCP」伺服器。這裡把它當主要除錯工具:先確認 MCP 自己活得好,再碰 Dev Mode 與 UI。

Inspector 是一個獨立應用(多半是網頁 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. 查看握手是否通過:伺服器應回應支援的 capabilities、tools 清單、資源等。
  3. 手動呼叫目標工具:選 search_gifts,填入參數 {"q": "給 30 歲以下女性"},按下「Call tool」,並查看:
    • 是否有回應;
    • 是否回了 JSON‑RPC 或 MCP 錯誤;
    • 伺服器針對這次呼叫,日誌印了什麼。

如果在 Inspector 已經會掛,根本不用打開 ChatGPT:先修好 MCP 伺服器。

如果在 Inspector 一切正常,但 ChatGPT 還是抱怨——問題就在更上層:Dev Mode URL、授權、或模型行為。

「刻意把 tool 弄壞」的示例

拿我們的 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. Widget 除錯:DevTools、狀態與「debug 橫幅」

當 MCP 伺服器大致搞定後,我們轉到前端——Apps SDK 的 Widget。

在哪裡、如何查看 Widget 的錯誤

你的 Widget 在 ChatGPT 內的 iframe 沙箱裡渲染。好消息是:該 iframe 一樣有瀏覽器 DevTools。

迷你步驟:

  1. 在瀏覽器(Chrome/Edge/Firefox)打開 ChatGPT。
  2. 打開 DevTools(通常是 F12Ctrl+Shift+I)。
  3. 在 Console 分頁選擇你的 Widget 所在的 frame 內容(常見網域是 web-sandbox.oaiusercontent.com)。
  4. 重新整理對話/送出訊息,讓 GPT 顯示你的 App

如果 Widget:

  • 根本沒出現;
  • 顯示為灰色/空白;
  • 在主控台出現紅色錯誤

——幾乎可以肯定是 React 端的問題:取用不存在的屬性、錯誤的匯入、歪掉的 hook 等。

Network 分頁也很有用。你會看到:

  • 你的應用 JS bundle 的載入(如果是 404/500——問題在 dev 伺服器/隧道端);
  • 你的 Widget 透過 window.fetch 發出的請求,以及 4xx/5xx 的回應。

最簡單的 debug 橫幅

很實用的小技巧——在 Widget 的根元件加一個小小的「debug 橫幅」,在 Dev Mode 顯示環境與 build 版本。

例如:

// 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>
  );
}

在 Widget 的根元件中:

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

export default function GiftGeniusWidget() {
  return (
    <div>
      <DebugBanner />
      {/* 其餘的禮物搜尋 UI */}
    </div>
  );
}

如果你打開了 ChatGPT、啟動了 App,卻看不到橫幅——代表你的 JS 根本沒到瀏覽器:可能是建置錯誤、endpoint 有問題,或是 Widget 根本沒在 MCP 伺服器裡註冊。

本地狀態與錯誤處理

你的 Widget 應該已經會顯示不同狀態:載入、成功、錯誤。如果沒有——現在正是加上的時候。

迷你樣式:

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

async function handleSearch(query: string) {
  try {
    setStatus("loading");
    // 透過 window.openai.callTool 或 Apps SDK 的 hook 呼叫 MCP tool
    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 裡也都 OK,ChatGPT 卻回「Error talking to [AppName]」或根本不提供你的 App

Dev Mode 做了什麼

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

  • 建立/編輯自己的 Apps;
  • 指定 MCP 伺服器的 endpoint(通常是 https://your-domain/mcp/api/mcp);
  • 在不發佈到 Store 的情況下快速更新 manifest 與中繼資料。

從除錯角度看,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」;
  • 或打開的是空白/灰色的 Widget。

怎麼不陷入絕望?

步驟 1(MCP/伺服器層):用 Inspector 與日誌檢查 MCP

先忽略 GPT 與 UI。我們只看伺服器。

  1. 確認 npm run dev 已啟動,且 endpoint(/api/mcp)有回應。
  2. MCP Inspector 連到 http://localhost:3000/api/mcp 或你的隧道。
  3. 檢查握手——應能看到 tools 清單。
  4. 手動呼叫 GPT 應該會用到的工具(例如 search_gifts),填入類似的參數。

如果在這一步就掛——修 MCP:Schema、業務邏輯、網路呼叫。利用日誌與 traceId 來鎖定壞點。

步驟 2(協定/Dev Mode 層):檢查 Dev Mode 與 URL

若在 Inspector 一切良好,但 ChatGPT 仍然看不到你的 App 或回報連線問題:

  1. 打開該 App 的 Dev Mode 設定。
  2. 看看 MCP 用的是哪個 URL。
  3. 和你的伺服器/隧道實際在聽的位址比對(若伺服器需要,記得最後要有 /mcp)。

很多時候,問題就在這裡。

步驟 3(UI 層):用 DevTools 檢查 Widget

如果 ChatGPT 成功呼叫了工具(從 MCP 日誌可見),但 Widget 行為很怪:

  1. 在 ChatGPT 頁面打開瀏覽器 DevTools。
  2. Console 分頁——選擇你的 Widget 的 iframe 內容。
  3. 查看 JS 錯誤。
  4. Network 分頁——確認:
    • Widget 的 JS bundle 載入沒有 404/500
    • 額外請求(透過 fetch/window.openai.fetch)回來的是有意義的結果。

同時看看你的 DebugBanner:如果沒出現,代表根本沒進到 React 樹。

步驟 4:用 Dev Mode 重現 bug 回報

拿到同事/使用者的 bug 回報時,盡量保留出問題的精確提示詞。在 Dev Mode 可以很快重現:

  1. 啟動 npm run dev,開隧道。
  2. 在 Dev Mode 選你的 App
  3. 貼上有問題的提示詞。
  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 回答」來除錯,而不是用伺服器與檢查器。
直接看模型回什麼、然後猜哪裡有 bug 很誘人。但模型是最上層。如果 MCP 伺服器自己就不工作(用 Inspector 手動也不行)——別指望 GPT 會有奇蹟。先把 MCP 穩住,再接 ChatGPT。

錯誤 2:不看日誌,或把所有東西都打到日誌。
沒有日誌就像瞎了眼:你不知道呼叫了哪個工具、用了什麼參數、最後結果如何。過度日誌又會讓主控台變成無法關聯的「綠雨」。最好擁有精簡、結構化的日誌,包含 toolargs(去識別)、traceIdstatus 與執行時間。

錯誤 3:在日誌中保留敏感資料。
把 token、完整 e‑mail、卡號寫進日誌,是安全與 OpenAI 政策上的雙重地雷。日誌裡只該放真正有助於除錯的資訊,個資要遮罩或乾脆不記。

錯誤 4:把所有問題都怪在 Dev Mode 身上。
Dev Mode 常成代罪羔羊:「一定是 OpenAI 壞了」。其實很多時候是你忘了在隧道重啟後更新 URL,或路徑填錯(/ 寫成了 /mcp)。在寫信求援前,先進 Dev Mode 設定,對一下 endpoint 與實際伺服器地址。

錯誤 5:忽略 DevTools 與 Widget 的錯誤。
空白或灰色的 Widget 幾乎總是前端 JavaScript 錯誤。如果你只看 MCP 日誌,卻不在 ChatGPT 裡打開 DevTools,就只看到了半張圖。養成按 F12 看 Console/Network 的習慣,能省下大把時間。

錯誤 6:用「魔法延遲」來「修」 bug。
有時會想來個 setTimeoutThread.sleep 式延遲「讓東西都載好」。在 MCP/Next/React 的世界幾乎總是錯的:問題通常在 Schema、錯的 endpoint,或程式錯誤,而不是「伺服器來不及」。比起挖洞用延遲填,先找清楚斷點在哪(Inspector → Dev Mode → Widget)。

錯誤 7:還沒確認本機可用就急著部署到 Vercel。
想「快點上線」可以理解,但把壞掉的 MCP 丟上 Vercel,只會得到兩層問題:本機與正式。這個模組我們刻意要求:先 MCP Jam/Inspector → 一切 OK,Dev Mode → 基本情境可跑,最後才部署。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION