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 のログを見るか分かる;
  • MCP サーバーをインスペクタ経由で手動で叩けるようになる;
  • Dev Mode が何を検証しているか理解し、ChatGPT が自分のサーバーに到達できているか確認できる。

そして何より重要なのは、「GPT 当てずっぽうデバッグ」をやめ、まずは スタックの低いレイヤー(サーバーとプロトコル)を検証し、その後に UI とモデルの挙動を見る、という順序にすることです。

2. メンタルモデル: デバッグの3層

混乱に溺れないため、デバッグを3つのレベルで考えることにします。いわば小さな「ミルフィーユ」です。

レベル そこで動くもの 典型的な症状 使うデバッグ手段
UI(ウィジェット) React コンポーネント、ステート、window.openai ウィジェットが空/グレー、レンダー崩れ、ボタンが効かない ブラウザの DevTools
バックエンド / MCP サーバー ツール、DB/API へのアクセス 500 台、「ツールが落ちる」、おかしなデータ サーバーログ、MCP Inspector
MCP プロトコル JSON‑RPC、tools/listtools/call、スキーマ GPT が「ツールを呼べなかった」と言う、invalid params インスペクタ + リクエストログ

第2層では MCP サーバー自体(ツール、DB、API)が何をしているかに注目し、第3層では「配線」と MCP メッセージ(JSON‑RPC、スキーマなど)のフォーマットを見ます。

この3つが、この講義とデバッグ編の基本方針です。

リクエストの流れを図で見ると分かりやすいでしょう。

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 のツールを呼び出し、DB/API にアクセス
    Next-->>ChatGPT: JSON-RPC result + ToolOutput
    ChatGPT-->>User: 回答 + ウィジェットのレンダリング

故障箇所はどこでもあり得ます。トンネル、エンドポイント、MCP ロジック、JSON スキーマ、React ウィジェット。デバッグの際のあなたの仕事は、どの層でエラーが起きているかを特定することであり、いきなり全部を書き換えることではありません。

3. Next.js と MCP のログ: すべての基本

一番地味で一番役に立つもの――ログから始めましょう。

ローカル開発時のログの場所

Next.js の Apps SDK 標準テンプレートでは、MCP サーバーは多くの場合 API ルート(/api/mcp など)にラップされています。npm run dev を起動すると、1つのターミナル内に:

  • Next.js の dev サーバー;
  • JSON‑RPC の tools/listtools/call などを受ける MCP エンドポイントのハンドラー;
  • console.log/console.error による各種出力。

MCP を別プロセスに分けている場合はターミナルが2つになりますが、要点は同じです。重要なものはすべてコンソールに現れます。

区別が重要:

  • ビルド/起動エラーnext dev が立ち上がらない、TypeScript が落ちる、誤った import など;
  • 実行時エラー — 起動はしたが、特定の /api/mcp リクエストでツールが落ちる。

Next.js の dev モードはランタイムエラーをオーバーレイでも表示し、スタックトレースもコンソールに出します。

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

重要な注意点が2つあります。

第一に、ログにフルの e‑mail、電話番号、カード番号、トークンを保存しないこと。これは見た目が悪いだけでなく、MCP の基本的なセキュリティプラクティスにも反します。

第二に、traceId は最良の友です。Next.js と MCP のログを合わせて見るとき、これでイベントを簡単にひも付けられます。特定の tools/call リクエスト、対応する React レンダー、ウィジェットのネットワークログを関連づけられます。

ログからどこで落ちたかを見極める

ターミナルに logToolEvent の JSON 行が流れているとします。典型的なシナリオ:

  • phase: "start"tool: "search_gifts" が来た;
  • phase: "end" がなく、代わりに phase: "error" とスタックトレースがある;
  • つまり、ツールはあなたのロジックに到達したが、その内部(外部 API 呼び出し、パース、DB 操作など)で壊れたことが分かる。

一方、そのツール名のログ自体がまったく見えない場合は、リクエストがツールに到達していません。スタックを上にたどり、トンネル、/mcp エンドポイント、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 を方程式から外すこと。ツールが動かないときは、まずサーバーが生きているか、プロトコルとスキーマが正しいかを確認し、そのうえで GPT を疑うべきです。

Inspector を使ったミニフロー

ローカルデバッグの典型的な流れは次のとおりです。

  1. npm run dev を起動して、Next.js + MCP エンドポイントを立ち上げる。
  2. MCP Inspector を起動する。例:
npx @modelcontextprotocol/inspector

(具体的なコマンドは使うツールによって異なります)。

  1. Inspector に MCP エンドポイントの URL を指定する。例: http://localhost:3000/api/mcp(トンネルも一緒に検証したい場合は HTTPS トンネルでも可)。
  2. ハンドシェイクが通るか確認する。サーバーは対応する capabilities、ツール一覧、リソースなどを返すはずです。
  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" とスタックトレースを見る。
  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 を開く(通常は F12 または Ctrl+Shift+I)。
  3. Console タブで、ウィジェットが動いているフレームのコンテキストを選ぶ(多くは web-sandbox.oaiusercontent.com ドメイン)。
  4. チャットを更新/メッセージ送信して、GPT に App を表示させる。

もしウィジェットが:

  • まったく表示されない;
  • グレー/空のまま;
  • コンソールに赤いエラーが出る

――それはほぼ間違いなく React コードの問題です。未定義のプロパティ、誤った import、壊れたフックなど。

Network タブも有用です。ここでは次が見えます。

  • アプリの JS バンドルの読み込み(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 はそもそもブラウザに届いていません。ビルドエラー、エンドポイントの問題、あるいはウィジェットが MCP サーバーに登録されていない可能性があります。

ローカルステートとエラー処理

あなたのウィジェットは、読み込み・成功・エラーなどの状態を表示できるはずです。まだなら今が追加の好機です。

最小パターン:

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

async function handleSearch(query: string) {
  try {
    setStatus("loading");
    // MCP ツールを window.openai.callTool または Apps SDK のフック経由で呼ぶ
    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 サーバーのエンドポイント(通常は https://あなたのドメイン/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 Inspectorhttp://localhost:3000/api/mcp に向けると正常に見えるはずです。つまり MCP は生きているが、ChatGPT は見当違いの場所を見ているということです。

解決: Dev Mode の設定を開き、URL を更新し、必要なら末尾の /mcp を忘れない。

Dev Mode と Store の違い

この講義では Dev Mode だけを扱います。ここはあなたのサンドボックスです。URL を頻繁に変えたり、トンネルを付け替えたり、ツールスキーマをいじるのは普通です。

後に Store に進むと、エンドポイントはより厳密に固定され、こうしたお遊びはよくないものになります。しかし Store はまだ先の話。いまは Dev Mode で壊して直してを気楽に繰り返しましょう。

7. ミニ・デバッグ手順: 「何も動かない」ときにやること

ここまでを実用的な手順にまとめます。基本的には冒頭の3レベルデバッグを手順に落とし込んだものです。

たとえば、ChatGPT を開き、GiftGenius を選び、「ギークな友人向けに 30$ 以内でギフトを選んで」と頼んだところ:

  • GPT が App について何も言わない;
  • あるいは「Error talking to GiftGenius」と出る;
  • あるいはウィジェットが空/グレーのまま。

絶望しないための手順は以下。

手順 1(MCP/サーバーレイヤー): Inspector とログで MCP を確認

まず GPT と UI を忘れます。サーバーだけに注目。

  1. npm run dev が動いていて、エンドポイント(/api/mcp)が応答していることを確認。
  2. MCP Inspectorhttp://localhost:3000/api/mcp またはあなたのトンネルに接続。
  3. ハンドシェイクを確認――ツール一覧が表示されるはず。
  4. GPT が呼ぶはずのツール(例: search_gifts)を、似た引数で手動実行。

ここで既に落ちるなら、MCP を直してください。スキーマ、ビジネスロジック、ネットワーク呼び出し。何が壊れているかを掴むのに、ログと traceId を活用します。

手順 2(プロトコル/Dev Mode レイヤー): Dev Mode と URL を確認

Inspector では完璧なのに、ChatGPT が依然として App を見つけられない、または接続問題を訴える場合:

  1. あなたの App の Dev Mode 設定を開く。
  2. MCP 用に指定した URL を確認。
  3. それが実際にサーバー/トンネルでリッスンしている URL と一致するかを照合(サーバーが必要とするなら末尾の /mcp を忘れずに)。

多くの場合、問題はまさにここにあります。

手順 3(UI レイヤー): DevTools でウィジェットを確認

ChatGPT がツール呼び出しに成功している(MCP のログで分かる)のに、ウィジェットの挙動がおかしいなら:

  1. ChatGPT のページでブラウザの DevTools を開く。
  2. Console タブであなたのウィジェットの iframe コンテキストを選択。
  3. JS エラーを確認。
  4. Network タブで次を確認:
    • ウィジェットの JS バンドルが 404/500 なく読み込まれている;
    • fetch/window.openai.fetch による追加リクエストが意味のある応答を返している。

同時に DebugBanner にも注目。表示されないなら、React ツリーに到達していません。

手順 4: バグ再現に Dev Mode を活用

同僚/ユーザーからバグレポートを受けたら、壊れたときの正確なプロンプトを残すようにしましょう。Dev Mode なら素早く再現できます。

  1. npm run dev を起動し、トンネルを立ち上げる。
  2. Dev Mode で App を選ぶ。
  3. 問題のプロンプトを貼り付ける。
  4. 同時に:
    • MCP にどんな JSON リクエストが来ているかログで確認;
    • 必要なら Inspector で同じ引数の tools/call を再現。

こうして「たまに動かない」を再現可能なシナリオに変えられます。

8. 快適なデバッグのための小さなコード工夫

仕上げに、GiftGenius にいくつか便利な断片を足しておきましょう。

環境設定とログレベル

サーバー設定のどこかで、MCP のエンドポイントとログレベルを明示しておくと便利です。

// 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",
};

そして logToolEventlogLevel を考慮すれば、本番で不要なスパムを出さずに済みます。

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,
    };
  }
}

こうして結果に isError を付ければ、ChatGPT は問題を適切にユーザーへ伝えられ、あなたはログで何が起きたかを把握できます。

9. ローカルデバッグでよくあるミス(ChatGPT App

ミス No.1: サーバーとインスペクタではなく「GPT 越し」にデバッグする。
モデルの返答だけを眺めて、どこにバグがあるか当てにいきたくなるものです。しかしモデルは最上位レイヤー。MCP サーバーが(Inspector で手動実行して)単体で動かないなら、GPT に奇跡は起きません。まずは MCP を安定動作させ、その後に ChatGPT をつなぎましょう。

ミス No.2: ログを見ない、または全部を垂れ流す。
ログがないと完全に盲目になります。どのツールがどんな引数で呼ばれ、どう終わったか分かりません。逆に過剰なログは、バラバラの行が並ぶだけの「マトリックス」化を招きます。tool、匿名化した argstraceIdstatus、実行時間を持つ、コンパクトで構造化されたログが最適です。

ミス No.3: 機微情報をログに残す。
トークン、フルの e‑mail やカード番号のロギングは、セキュリティ的にも OpenAI のポリシー的にも悪手です。デバッグに本当に必要な情報だけを残し、個人情報はマスクするか書かないようにしましょう。

ミス No.4: 何でも Dev Mode のせいにする。
「OpenAI が何か壊したせいだ」とされがちですが、実際にはトンネル再起動後に URL を更新し忘れた、あるいはパスを間違えた(/ にしてしまい、/mcp を付け忘れた)といった問題が多いです。サポートに連絡する前に、Dev Mode の設定でエンドポイントが実サーバーのアドレスと一致しているかを確認しましょう。

ミス No.5: DevTools とウィジェットのエラーを無視する。
ウィジェットが空/グレーなのは、たいていクライアント側の JavaScript エラーです。MCP のログだけを見て、ChatGPT 側の DevTools を開かないのは半分しか見ていないのと同じ。F12 で Console/Network を確認する習慣は大きな時間節約になります。

ミス No.6: 魔法の遅延で「修理」しようとする。
ときに setTimeoutThread.sleep 的な遅延を入れて「読み込みが間に合うように」したくなります。しかし MCP/Next/React の世界ではほぼ誤治療です。問題はたいていスキーマ、誤ったエンドポイント、コードエラーであって、「サーバーが間に合っていない」わけではありません。遅延で埋めるより、どこで切れているか(Inspector → Dev Mode → ウィジェット)を理解しましょう。

ミス No.7: ローカルでの健全性確認なしに Vercel へデプロイ。
「早く本番へ」という気持ちは分かりますが、壊れた MCP を Vercel に移すのは、ローカルと本番の二重苦を作る最良の方法です。本モジュールでは意図的にこう求めます。まず MCP Jam/Inspector で OK、次に Dev Mode で基本シナリオが動くことを確認し、それからデプロイです。

コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION