1. Навіщо окрема лекція про локальне налагодження
У попередніх модулях ми вже розібрали, як улаштований стек Apps SDK і MCP. Тепер поговорімо про те, навіщо взагалі потрібна окрема лекція про локальне налагодження.
У багатьох шлях такий: «Ну, я просто відкрию ChatGPT, напишу “використовуй мій App”, а далі дивитимуся, що він скаже. Якщо не працює — перепишу код навмання». Це приблизно як лагодити бекенд, дивлячись лише на HTML‑сторінку в браузері, і жодного разу не відкривши журнали сервера.
Із ChatGPT Apps особливо легко зісковзнути в «магію»: є GPT, він сам вирішує, викликати інструмент чи ні, і має власну логіку помилок. Якщо ви не бачите, що відбувається «під капотом», налагодження перетворюється на шаманство з бубном.
Наша мета: перетворити це на нормальний інженерний процес:
- ви знаєте, де дивитися журнали Next/MCP;
- ви вмієте вручну викликати MCP‑сервер через інспектор;
- ви розумієте, що саме перевіряє Dev Mode і як упевнитися, що ChatGPT узагалі може підʼєднатися до вашого сервера.
І найважливіше: ви перестаєте налагоджувати через «вгадайку з GPT» і починаєте спершу перевіряти нижчі рівні стеку — сервер і протокол, а вже потім UI та поведінку моделі.
2. Ментальна модель: три рівні налагодження
Щоб не тонути в хаосі, домовмося мислити про налагодження як про три рівні. Це наш маленький «листковий пиріг»:
| Рівень | Що там «живе» | Типові симптоми | Чим налагоджуємо |
|---|---|---|---|
| UI (віджет) | React‑компоненти, стан, window.openai | Порожній або сірий віджет, некоректне відображення, кнопки не працюють | DevTools браузера |
| Бекенд / MCP‑сервер | інструменти, доступ до БД/API | помилки 500, «інструмент упав», дивні дані | журнали сервера, MCP Inspector |
| Протокол MCP | JSON‑RPC, tools/list, tools/call, схеми | GPT пише «не зміг викликати інструмент», invalid params | інспектор і журнали запитів |
На другому рівні нас цікавить, що робить сам MCP‑сервер (інструменти, БД, API). На третьому — уже «дроти» й формат MCP‑повідомлень (JSON‑RPC, схеми тощо).
Ця трійка — основа плану лекції та всього підходу до налагодження.
Для наочності можна подивитися на потік запиту:
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 result + ToolOutput
ChatGPT-->>User: Відповідь + рендер віджета
Зламатися може в будь‑якій точці: тунель, ендпойнт, MCP‑логіка, JSON‑схема, React‑віджет. Ваше завдання під час налагодження — зрозуміти, на якому саме шарі сталася помилка, а не одразу переписувати все підряд.
3. Журнали Next.js і MCP: основа всього
Почнемо з найнуднішого й водночас найкориснішого — журналів.
Де «живуть» журнали під час локальної розробки
У стандартному шаблоні Apps SDK на Next.js MCP‑сервер зазвичай загорнутий в API‑маршрут (/api/mcp або подібний). Ви запускаєте npm run dev, і в одному терміналі бачите:
- dev‑сервер Next.js;
- обробник для MCP‑ендпойнта, який приймає JSON‑RPC‑запити tools/list, tools/call тощо;
- вивід усього «цікавого» через console.log/console.error.
Якщо ви винесли MCP в окремий процес, буде другий термінал. Але ідея та сама: усе важливе видно в консолі.
Важливо розрізняти:
- помилки збірки/запуску — не стартує next dev, падає TypeScript, неправильний імпорт тощо;
- помилки виконання — усе запустилося, але конкретний запит на /api/mcp призводить до падіння інструмента.
Next.js у режимі розробки показує помилки виконання ще й гарною накладкою, а також пише трасування стека в консоль.
Що логувати в MCP‑сервері
Хоча MCP використовує протокол JSON‑RPC, для налагодження вам не потрібно друкувати буквально весь 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;
}
}
Тут є дві важливі тонкощі.
По‑перше, не варто зберігати в журналах повні адреси електронної пошти, телефони, номери карток і токени. Це не лише неохайно, а й суперечить базовим практикам безпеки MCP.
По‑друге, traceId — ваш найкращий друг. Коли ви дивитеся журнали Next.js і MCP разом, за ним легко повʼязати події: конкретний запит tools/call, відповідний React‑рендер і мережевий журнал віджета.
Як зрозуміти з журналів, де «впало»
У вас є термінал, і в нього летять JSON‑рядки від logToolEvent. Типовий сценарій:
- прийшов phase: "start" з tool: "search_gifts";
- немає phase: "end", зате є phase: "error" і трасування стека;
- із цього видно, що інструмент дійшов до вашої логіки, але щось зламалося всередині — наприклад, запит до зовнішнього API, парсинг або робота з БД.
Якщо ж ви взагалі не бачите журналів для цього імені інструмента, отже, запит навіть не дійшов до інструмента. Тоді підіймайтеся вище по стеку: тунель, ендпойнт /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
Типовий сценарій локального налагодження виглядає так:
- Запускаєте npm run dev, щоб Next.js + MCP‑ендпойнт піднялися.
- Запускаєте MCP Inspector, наприклад:
npx @modelcontextprotocol/inspector
(конкретна команда залежить від інструмента, який ви використовуєте).
- В Inspector зазначаєте URL вашого MCP‑ендпойнта, наприклад http://localhost:3000/api/mcp (або HTTPS‑тунель, якщо хочете перевірити й його).
- Дивитеся, чи пройшов handshake: сервер має відповісти підтримуваними capabilities, списком tools, ресурсів тощо.
- Викликаєте потрібний інструмент вручну: обираєте 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 [];
}
Далі:
- В Inspector викликаєте search_gifts з аргументом {"q": "зламайся"}.
- У журналах бачите phase: "error" і трасування стека.
- Переконуєтеся, що MCP‑сервер чесно повертає помилку.
Потім, коли ви підʼєднаєте все це до ChatGPT Dev Mode і попросите модель «підібрати подарунок зі словом "зламайся"», вона спробує викликати інструмент і покаже користувачу повідомлення на кшталт «I encountered an error running the tool». Тобто помилка зʼявляється не через модель, а через ваш явний виняток.
Такий прийом добре тренує мислення: ви чітко відділяєте бізнес‑помилку (ми самі кинули Error) від протокольної (зламали JSON, неправильна назва інструмента тощо).
5. Налагодження віджета: DevTools, стан і «debug‑банер»
Коли MCP‑сервер більш‑менш зрозумілий, переходимо до фронтенду — віджета Apps SDK.
Де і як дивитися помилки віджета
Ваш віджет відображається всередині ChatGPT в iframe‑пісочниці. Але є хороша новина: для цього iframe доступні ті самі браузерні DevTools.
Міні‑процедура:
- Відкриваєте ChatGPT у браузері (Chrome/Edge/Firefox).
- Відкриваєте DevTools (зазвичай F12 або Ctrl+Shift+I).
- На вкладці Console обираєте контекст фрейму, у якому «живе» ваш віджет (часто це домен web-sandbox.oaiusercontent.com).
- Оновлюєте чат або надсилаєте повідомлення, щоб 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 узагалі не дійшов до браузера. Причина може бути різною: помилка збірки, проблема з ендпойнтом або те, що віджет просто не зареєстрований у 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>
)}
Для налагодження важливо, щоб:
- ви не «ковтали» винятки (інакше в консолі буде порожньо, а 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
Класика жанру:
- Ви підняли тунель https://abcd.ngrok.io, указали його в Dev Mode — усе працювало.
- Наступного дня перезапустили ngrok і отримали https://efgh.ngrok.io.
- У Dev Mode досі https://abcd.ngrok.io/mcp.
- 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, ендпойнт буде жорсткіше зафіксованим, і такі фокуси стануть поганою ідеєю. Але до Store нам іще кілька модулів, тож поки спокійно ламаємо й лагодимо в Dev Mode.
7. Міні‑алгоритм налагодження: що робити, коли «нічого не працює»
Тепер зберемо все в практичний алгоритм. По суті, це ті самі три рівні налагодження з початку лекції, тільки записані як послідовність кроків.
Припустімо, ви відкрили ChatGPT, вибрали GiftGenius, попросили «Підбери подарунок до 30 $ для друга‑гіка», і:
- GPT нічого не пише про App;
- або пише «Error talking to GiftGenius»;
- або відкривається порожній/сірий віджет.
Як не впасти у відчай?
Крок 1 (рівень MCP/сервер). Перевірити MCP через Inspector і журнали
Спочатку ігноруємо GPT і UI. Нас цікавить лише сервер.
- Переконайтеся, що npm run dev запущено, а ендпойнт (/api/mcp) відповідає.
- Підʼєднайте MCP Inspector до http://localhost:3000/api/mcp або до вашого тунелю.
- Перевірте handshake — список tools має відображатися.
- Викличте вручну той самий інструмент, який, за задумом, має викликати GPT (наприклад, search_gifts), зі схожими аргументами.
Якщо вже тут усе падає — лагодьте MCP: схеми, бізнес‑логіку, мережеві виклики. Використовуйте журнали і traceId, щоб зрозуміти, що саме ламається.
Крок 2 (рівень протоколу/Dev Mode). Перевірити Dev Mode і URL
Якщо в Inspector усе прекрасно, а ChatGPT досі не бачить ваш App або пише про проблеми зі зʼєднанням:
- Відкрийте налаштування Dev Mode для вашого App.
- Подивіться, який URL там вказано для MCP.
- Звірте його з тим, що реально слухає ваш сервер/тунель (і не забудьте перевірити, що в кінці є /mcp, якщо ваш сервер цього потребує).
Дуже часто проблема саме тут.
Крок 3 (рівень UI). Перевірити віджет через DevTools
Якщо ChatGPT успішно викликає інструменти (це видно з журналів MCP), але сам віджет поводиться дивно:
- Відкрийте DevTools у браузері на сторінці ChatGPT.
- На вкладці Console оберіть iframe‑контекст вашого віджета.
- Подивіться на JS‑помилки.
- На вкладці Network переконайтеся, що:
- JS‑бандл віджета завантажується без 404/500;
- додаткові запити (через fetch/window.openai.fetch) повертають змістовні відповіді.
Паралельно дивіться на ваш DebugBanner: якщо він не зʼявився, значить, до React‑дерева ви взагалі не дійшли.
Крок 4. Використовувати Dev Mode для відтворення баг‑репорту
Коли ви отримали від колеги/користувача баг‑репорт, намагайтеся зберегти точний промпт, на якому все зламалося. У Dev Mode можна дуже швидко відтворити сценарій:
- Запустити npm run dev, підняти тунель.
- У Dev Mode вибрати App.
- Вставити проблемний промпт.
- Паралельно:
- дивитися, які JSON‑запити приходять на MCP за журналами;
- в 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",
};
І в 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», а не через сервер та інспектор.
Дуже спокусливо просто дивитися, що відповідає модель, і намагатися вгадати, де у вас баг. Але модель — це найвищий шар. Якщо MCP‑сервер не працює сам по собі (вручну, через Inspector), нічого чекати чудес від GPT. Спочатку досягніть стабільної роботи MCP, а вже потім підʼєднуйте ChatGPT.
Помилка №2: узагалі не дивитися журнали або записувати в них усе підряд.
Відсутність журналів призводить до повної сліпоти: ви не знаєте, який інструмент викликався, з якими аргументами й чим усе закінчилося. Надмірне логування, навпаки, перетворює консоль на «матрицю» з неповʼязаних рядків. Краще мати компактний, структурований журнал із tool, args (знеособленими), traceId, status і часом виконання.
Помилка №3: зберігати в журналах чутливі дані.
Логувати токени, повні адреси електронної пошти й номери карток — погана практика і з погляду безпеки, і з погляду політики OpenAI. У журналах має бути лише інформація, яка справді допомагає налагодженню. Персональні дані маскуйте або не пишіть узагалі.
Помилка №4: звинувачувати Dev Mode у всіх гріхах.
Dev Mode часто стає козлом відпущення: «Ну, це OpenAI щось зламав». Насправді ж дуже часто проблема в тому, що ви забули оновити URL після перезапуску тунелю або вказали не той шлях (/ замість /mcp). Перш ніж писати в підтримку, зазирніть у налаштування Dev Mode і звірте ендпойнт із фактичною адресою сервера.
Помилка №5: ігнорувати DevTools і помилки у віджеті.
Порожній або сірий віджет майже завжди означає JavaScript‑помилку на клієнті. Якщо ви дивитеся лише на журнали MCP, але не відкриваєте DevTools у ChatGPT, ви бачите лише половину картини. Звичка автоматично натискати F12 і переглядати Console/Network заощадить години життя.
Помилка №6: намагатися «полагодити» баг магічними затримками.
Іноді хочеться зробити setTimeout або затримку в стилі Thread.sleep, «щоб усе встигло завантажитися». У світі MCP/Next/React це майже завжди неправильне лікування: проблема зазвичай у схемі, неправильному ендпойнті або помилці коду, а не в тому, що «сервер не встиг». Краще зрозуміти, де саме розрив (Inspector → Dev Mode → віджет), ніж маскувати його затримками.
Помилка №7: розгортати на Vercel, не переконавшись, що локально все працює.
Бажання «швидше в продакшн» зрозуміле, але переносити зламаний MCP на Vercel — ідеальний спосіб отримати два рівні проблем: локальний і продакшн. У цьому модулі ми свідомо вимагаємо: спочатку MCP Jam/Inspector → усе гаразд, Dev Mode → базові сценарії працюють, і лише потім деплой.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ