JavaRush /Курсы /ChatGPT Apps /Локальный debug: логи, MCP‑инспекция и Dev Mode

Локальный debug: логи, MCP‑инспекция и Dev Mode

ChatGPT Apps
7 уровень , 1 лекция
Открыта

1. Зачем отдельная лекция про локальный debug

В прошлых модулях мы уже разобрали, как устроены стек Apps SDK и MCP. Теперь поговорим, зачем вообще нужна отдельная лекция про локальный debug.

У многих путь такой: «Ну, я просто открою ChatGPT, напишу "используй мой App", а дальше буду смотреть, что он скажет. Если не работает — перепишу код наугад». Это примерно как чинить backend, глядя только на HTML‑страницу в браузере и ни разу не открыв логи сервера.

С ChatGPT Apps особенно легко скатиться в магию: есть GPT, он сам решает, вызывать tool или нет, у него есть своя логика ошибок. Если вы не видите, что происходит под капотом, debug превращается в шаманство с бубном.

Наша цель: превратить это в нормальный инженерный процесс:

  • вы знаете, где смотреть логи Next/MCP;
  • вы умеете руками дергать MCP‑сервер через инспектор;
  • вы понимаете, что именно проверяет Dev Mode и как убедиться, что ChatGPT вообще может достучаться до вашего сервера.

И самое важное: вы перестаёте отлаживать через «угадайку GPT» и начинаете сначала проверять низкие уровни стека — сервер и протокол, а уже потом UI и поведение модели.

2. Ментальная модель: три уровня debug

Чтобы не тонуть в хаосе, договоримся думать о debug в терминах трёх уровней. Это наш маленький «слоёный пирог»:

Уровень Что там живёт Типичные симптомы Чем отлаживаем
UI (виджет) React‑компоненты, стейт, window.openai Пустой/серый виджет, кривой рендер, кнопки не работают DevTools браузера
Backend / MCP‑сервер инструменты, доступ к БД/API 500‑ки, «tool упал», странные данные логи сервера, MCP Inspector
Протокол MCP JSON‑RPC, tools/list, tools/call, схемы GPT пишет «не смог вызвать инструмент», invalid params инспектор + логи запросов

Во втором уровне нас интересует, что делает сам MCP‑сервер (инструменты, БД, API), а в третьем — уже «провода» и формат MCP‑сообщений (JSON‑RPC, схемы и т.п.).

Эта тройка — основа плана лекции и курса по debug.

Для наглядности можно посмотреть на поток запроса:

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 (через Tunnel)
    Next->>Next: Вызываем MCP tool, лезем в БД/API
    Next-->>ChatGPT: JSON-RPC result + ToolOutput
    ChatGPT-->>User: Ответ + рендер виджета

Сломаться может в любой точке: туннель, endpoint, MCP‑логика, JSON‑схема, React‑виджет. Ваша задача при debug — понять, в каком именно слое ошибка, а не сразу переписывать всё подряд.

3. Логи Next.js и MCP: основа всего

Начнём с самого скучного и самого полезного — с логов.

Где живут логи при локальной разработке

В стандартном шаблоне Apps SDK на Next.js MCP‑сервер, как правило, обёрнут в API‑роут (/api/mcp или похожий). Вы запускаете npm run dev, и в одном терминале у вас:

  • дев‑сервер Next.js;
  • обработчик для MCP endpoint, который принимает JSON‑RPC запросы tools/list, tools/call и т.п.;
  • печать всего веселья console.log/console.error.

Если вы выделяли MCP в отдельный процесс, там будет второй терминал, но идея та же: всё интересное видно в консоли.

Важно различать:

  • ошибки сборки/старта — не поднимается next dev, падает TypeScript, неправильный импорт и т.п.;
  • ошибки выполнения — всё запустилось, но конкретный запрос на /api/mcp приводит к падению инструмента.

Next.js в dev‑режиме показывает runtime‑ошибки ещё и красивым оверлеем, плюс пишет stack trace в консоль.

Что логировать в MCP‑сервере

Хотя MCP использует протокол JSON‑RPC, для debug вам не нужно печатать абсолютно весь JSON. Гораздо полезнее структурированные, но короткие логи.

Хорошая практика для MCP‑логов — логировать как минимум: timestamp, request_id/traceId, имя инструмента, параметры (обезличенные), статус (ok/error) и время выполнения.

Простейший logger.ts для GiftGenius может выглядеть так:

// 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, телефоны, номера карт, токены. Это не только некрасиво, но и конфликтует с базовыми практиками безопасности MCP.

Во‑вторых, traceId — ваш лучший друг. Когда вы смотрите логи Next.js и MCP вместе, по нему легко связать события: конкретный запрос tools/call, соответствующий React‑рендер и сетевой лог виджета.

Как понять по логам, где упало

У вас есть терминал, в нём летят JSON‑строчки от logToolEvent. Типовой сценарий:

  • пришёл phase: "start" с tool: "search_gifts";
  • нет phase: "end", зато есть phase: "error" и stack trace;
  • из этого видно, что до вашей логики инструмент дошёл, но что‑то сломал внутри — например, запрос к внешнему API, парсинг, работа с БД.

Если же вы вообще не видите логов для этого tool‑имени — значит, запрос даже не дошёл до инструмента. Тогда вы идёте выше по стеку: туннель, endpoint /mcp, JSON‑запрос tools/call.

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/list, tools/call и показывает сырые JSON‑сообщения, handshake, список инструментов, ресурсы и т.п.

Главная идея: убрать ChatGPT из уравнения. Если у вас инструмент не работает, вы сначала хотите понять, жив ли сервер, корректен ли протокол и схема, прежде чем обвинять GPT.

Мини‑поток работы с Inspector

Типовой сценарий локальной отладки выглядит так:

  1. Запускаете npm run dev, чтобы Next.js + MCP‑endpoint поднялись.
  2. Запускаете MCP Inspector, например:
npx @modelcontextprotocol/inspector

(конкретная команда зависит от используемого инструмента).

  1. В Inspector указываете URL вашего MCP‑endpoint, например http://localhost:3000/api/mcp (или HTTPS‑туннель, если хотите проверить его тоже).
  2. Смотрите, прошёл ли handshake: сервер должен ответить поддерживаемыми 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("Учебная ошибка для демонстрации debug");
  }
  // ... нормальная логика ...
  return [];
}

Дальше:

  1. В Inspector вызываете search_gifts с аргументом {"q": "сломайся"}.
  2. В логах видите phase: "error" и stack trace.
  3. Убеждаетесь, что MCP‑сервер честно возвращает ошибку.

Потом, когда вы подключите всё это к ChatGPT Dev Mode и попросите модель «подбери подарок со словом "сломайся"», она попытается вызвать инструмент и покажет уже пользователю сообщение вроде «I encountered an error running the tool». Видно: ошибка появляется не из‑за модели, а из‑за вашего явного исключения.

Такой приём хорошо тренирует мышление: вы чётко отделяете бизнес‑ошибку (мы сами бросили Error) от протокольной (сломали JSON, неправильное имя инструмента и т.п.).

5. Debug виджета: DevTools, стейт и «debug‑баннер»

Когда MCP‑сервер более‑менее понятен, переходим на фронтенд — виджет Apps SDK.

Где и как смотреть ошибки виджета

Ваш виджет рендерится внутри ChatGPT в iframe‑песочнице. Но хорошая новость: у этого iframe всё те же браузерные DevTools.

Мини‑процедура:

  1. Открываете ChatGPT в браузере (Chrome/Edge/Firefox).
  2. Открываете DevTools (обычно F12 или Ctrl+Shift+I).
  3. Вкладка Console — выбираете контекст фрейма, в котором живёт ваш виджет (часто это домен web-sandbox.oaiusercontent.com).
  4. Обновляете чат/отправляете сообщение, чтобы GPT показал ваш App.

Если виджет:

  • вообще не появился;
  • показался серым/пустым;
  • показывает красную ошибку в консоли

— это почти точно проблема React‑кода: недоступное свойство, неправильный импорт, кривой хук и т.п.

Вкладка Network тоже полезна. Там вы увидите:

  • загрузку JS‑бандла вашего приложения (если 404/500 — проблема на стороне dev‑сервера/туннеля);
  • запросы, которые ваш виджет делает наружу через window.fetch, и ответы 4xx/5xx.

Простейший debug‑баннер

Очень удобный приём — добавить в корневой компонент виджета небольшой «debug‑баннер», который в 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");
    // вызываем MCP tool через window.openai.callTool или хук Apps SDK
    setStatus("success");
  } catch (e) {
    console.error("Search failed", e);
    setStatus("error");
  }
}

В JSX:

{status === "error" && (
  <div style={{ color: "red" }}>Что-то пошло не так, попробуйте ещё раз.</div>
)}

Для debug критично, чтобы:

  • вы не проглатывали исключения (иначе в консоли пусто, а UI просто «завис»);
  • вы явно отражали ошибку в UI, иначе пользователю кажется, что App умер.

6. Dev Mode как часть debug: что он делает и как его не винить зря

Теперь включим в картину ChatGPT Dev Mode. До этого мы рассматривали чисто ваш код. Но иногда всё работает локально, в Inspector всё отлично, а ChatGPT всё равно отвечает «Error talking to [AppName]» или вообще не предлагает ваш App.

Что делает Dev Mode

Dev Mode — это режим ChatGPT, где вы можете:

  • создавать и редактировать свои Apps;
  • указывать endpoint MCP‑сервера (обычно https://ваш-домен/mcp или /api/mcp);
  • быстро обновлять манифест и метаданные без публикации в Store.

С точки зрения debug, 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, переподключать туннель, править схему инструментов.

Когда вы потом пойдёте в Store, endpoint станет более жёстко зафиксирован, и такие фокусы будут уже нехорошей идеей. Но до Store нам ещё несколько модулей, так что пока спокойно ломаем и чиним в Dev Mode.

7. Мини‑алгоритм отладки: что делать, когда «ничего не работает»

Теперь соберём всё в практический алгоритм. По сути, это те же три уровня debug из начала лекции, только записанные как последовательность шагов.

Допустим, вы открыли ChatGPT, выбрали GiftGenius, попросили «Подбери подарок до 30$ для друга‑гика», и:

  • GPT ничего не пишет про App;
  • или пишет «Error talking to GiftGenius»;
  • или открывается пустой/серый виджет.

Как не впасть в отчаяние?

Шаг 1 (уровень MCP/сервер). Проверить MCP через Inspector и логи

Сначала игнорируем GPT и UI. Нас интересует только сервер.

  1. Убедитесь, что npm run dev запущен, и endpoint (/api/mcp) отвечает.
  2. Подключите MCP Inspector к http://localhost:3000/api/mcp или к вашему туннелю.
  3. Проверьте handshake — список tools должен отображаться.
  4. Вызовите руками тот же инструмент, который, по идее, должен дергать GPT (например, search_gifts), с похожими аргументами.

Если уже тут всё падает — чините MCP: схемы, business‑логику, сетевые вызовы. Используйте логи и traceId, чтобы понять, что именно ломается.

Шаг 2 (уровень протокола/Dev Mode). Проверить Dev Mode и URL

Если в Inspector всё прекрасно, а ChatGPT по‑прежнему не видит ваше App или пишет про проблемы с соединением:

  1. Откройте настройки Dev Mode для вашего App.
  2. Посмотрите, какой там URL указан для MCP.
  3. Сверьте его с тем, что реально слушает ваш сервер/туннель (и не забудьте проверить, что в конце есть /mcp, если ваш сервер этого требует).

Часто проблема именно здесь.

Шаг 3 (уровень UI). Проверить виджет через DevTools

Если ChatGPT успешно дергает инструменты (видно по логам MCP), но сам виджет ведёт себя странно:

  1. Откройте DevTools в браузере на странице ChatGPT.
  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. Параллельно:
    • смотреть, какие JSON‑запросы приходят на MCP по логам;
    • в Inspector, при необходимости, повторить tools/call с теми же аргументами.

Так вы превращаете «иногда что‑то не работает» в воспроизводимый сценарий.

8. Небольшие кодовые штрихи для удобного debug

Чтобы закрепить материал, добавим ещё пару полезных фрагментов в наше приложение GiftGenius.

Конфигурация окружения и уровней логирования

Где‑то в конфигурации сервера удобно явно прописать endpoint 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",
};

И в 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. Типичные ошибки при локальном debug ChatGPT App

Ошибка №1: отлаживать «через GPT», а не через сервер и инспектор.
Очень соблазнительно просто смотреть, что отвечает модель, и пытаться угадать, где у вас баг. Но модель — самый верхний слой. Если MCP‑сервер не работает сам по себе (руками, через Inspector) — нечего ждать чудес от GPT. Сначала добейтесь стабильной работы MCP, а уже потом подключайте ChatGPT.

Ошибка №2: не смотреть логи вообще или логировать всё подряд.
Отсутствие логов приводит к полной слепоте: вы не знаете, какой инструмент вызвался, с какими аргументами и чем всё закончилось. Перелогирование, наоборот, превращает консоль в «матрицу» из несвязанных строк. Лучше иметь компактный, структурированный лог с tool, args (обезличенными), traceId, status и временем выполнения.

Ошибка №3: хранить в логах чувствительные данные.
Логировать токены, полные e‑mail и номера карт — плохая практика и с точки зрения безопасности, и с точки зрения политики OpenAI. В логах должна быть только та информация, которая реально помогает debug, а персональные данные — маскируем или не пишем вовсе.

Ошибка №4: винить Dev Mode во всех грехах.
Dev Mode часто становится козлом отпущения: «Ну это OpenAI что‑то сломал». На деле очень часто проблема в том, что вы забыли обновить URL после перезапуска туннеля или указали не тот путь (/ вместо /mcp). Перед тем, как писать в поддержку, загляните в настройки Dev Mode и сверьте endpoint с фактическим адресом сервера.

Ошибка №5: игнорировать DevTools и ошибку в виджете.
Пустой или серый виджет почти всегда означает JavaScript‑ошибку на клиенте. Если вы смотрите только на логи MCP, но не открываете DevTools в ChatGPT, вы видите только половину картины. Привычка на автомате нажимать F12 и смотреть Console/Network сэкономит часы жизни.

Ошибка №6: пытаться «починить» баг магическими задержками.
Иногда хочется сделать setTimeout или Thread.sleep‑стайл задержку «чтобы всё успело прогрузиться». В мире MCP/Next/React это почти всегда неправильное лечение: проблема обычно в схеме, неверном endpoint или ошибке кода, а не в том, что «сервер не успел». Лучше понять, где именно разрыв (Inspector → Dev Mode → виджет), чем закапывать его задержками.

Ошибка №7: деплоить на Vercel, не убедившись, что локально всё работает.
Желание «быстрее в прод» понятно, но переносить сломанный MCP на Vercel — идеальный способ получить два уровня проблем: локальный и продакшен. В этом модуле мы сознательно требуем: сначала MCP Jam/Inspector → всё ок, Dev Mode → базовые сценарии работают, и только потом деплой.

1
Задача
ChatGPT Apps, 7 уровень, 1 лекция
Недоступна
Структурные логи tool-calls на MCP-сервере (traceId + sanitize)
Структурные логи tool-calls на MCP-сервере (traceId + sanitize)
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ