1. Dlaczego osobny wykład o lokalnym debugowaniu
W poprzednich modułach omówiliśmy już, jak zbudowane są stos Apps SDK i MCP. Teraz porozmawiajmy, po co w ogóle osobny wykład o lokalnym debugowaniu.
Wielu ma taką ścieżkę: „No, po prostu otworzę ChatGPT, napiszę „użyj mojej App”, a potem będę patrzeć, co on powie. Jeśli nie działa — przepiszę kod na chybił trafił”. To mniej więcej jak naprawiać backend, patrząc tylko na stronę HTML w przeglądarce i ani razu nie otwierając logów serwera.
Z ChatGPT Apps szczególnie łatwo popaść w magię: jest GPT, sam decyduje, czy wywołać narzędzie, ma własną logikę błędów. Jeśli nie widzisz, co dzieje się pod maską, debugowanie zamienia się w szamanizm z bębenkiem.
Nasz cel: zamienić to w normalny proces inżynieryjny:
- wiesz, gdzie patrzeć w logi Next/MCP;
- umiesz ręcznie wywoływać serwer MCP przez inspektor;
- rozumiesz, co dokładnie sprawdza Dev Mode i jak upewnić się, że ChatGPT w ogóle może dostać się do twojego serwera.
I najważniejsze: przestajesz debugować „zgaduj‑zgadulą GPT” i zaczynasz od weryfikacji niższych warstw stosu — serwera i protokołu, a dopiero później UI i zachowania modelu.
2. Model mentalny: trzy poziomy debugowania
Aby nie tonąć w chaosie, umówmy się myśleć o debugowaniu w kategoriach trzech poziomów. To nasz mały „tort warstwowy”:
| Poziom | Co tu jest | Typowe objawy | Czym debugujemy |
|---|---|---|---|
| UI (widżet) | Komponenty React, stan, window.openai | Pusty/szary widżet, błędny render, przyciski nie działają | DevTools przeglądarki |
| Backend / MCP-serwer | narzędzia, dostęp do bazy danych/API | 500-ki, „narzędzie padło”, dziwne dane | logi serwera, MCP Inspector |
| Protokół MCP | JSON‑RPC, tools/list, tools/call, schematy | GPT pisze „nie udało się wywołać narzędzia”, invalid params | inspektor + logi żądań |
Na drugim poziomie interesuje nas, co robi sam serwer MCP (narzędzia, baza danych, API), a na trzecim — już „kable” i format komunikatów MCP (JSON‑RPC, schematy itp.).
Ta trójka to podstawa planu wykładu i kursu debugowania.
Dla przejrzystości spójrzmy na przepływ żądania:
sequenceDiagram
participant User as Użytkownik
participant ChatGPT as ChatGPT (Dev Mode)
participant Tunnel as Tunel (ngrok/CF)
participant Next as Next.js + MCP
User->>ChatGPT: "Dobierz prezent do $50"
ChatGPT->>Next: tools/call search_gifts (przez tunel)
Next->>Next: Wywołujemy narzędzie MCP, sięgamy do bazy danych/API
Next-->>ChatGPT: JSON-RPC result + ToolOutput
ChatGPT-->>User: Odpowiedź + render widżetu
Może się zepsuć w dowolnym punkcie: tunel, endpoint, logika MCP, schemat JSON, widżet React. Twoje zadanie przy debugowaniu — zrozumieć, w której dokładnie warstwie jest błąd, a nie od razu przepisywać wszystko po kolei.
3. Logi Next.js i MCP: podstawa wszystkiego
Zacznijmy od najbardziej nudnego i najbardziej użytecznego — od logów.
Gdzie są logi przy lokalnym developmentzie
W standardowym szablonie Apps SDK na Next.js serwer MCP jest zwykle „owinięty” w trasę API (/api/mcp lub podobną). Uruchamiasz npm run dev i w jednym terminalu masz:
- dev‑serwer Next.js;
- obsługę endpointu MCP, który przyjmuje żądania JSON‑RPC tools/list, tools/call itd.;
- wypisywanie całej „zabawy” przez console.log/console.error.
Jeśli wydzieliłeś MCP do osobnego procesu, będzie drugi terminal, ale idea ta sama: wszystko, co interesujące, widać w konsoli.
Ważne, aby rozróżniać:
- błędy budowania/uruchomienia — nie startuje next dev, wywala się TypeScript, zły import itd.;
- błędy wykonania — wszystko się uruchomiło, ale konkretne żądanie na /api/mcp powoduje wywrotkę narzędzia.
Next.js w trybie dev pokazuje błędy runtime także ładnym overlayem oraz zapisuje stack trace w konsoli.
Co logować w serwerze MCP
Chociaż MCP używa protokołu JSON‑RPC, do debugowania nie musisz drukować całego JSON. Znacznie bardziej przydatne są logi strukturalne, ale krótkie.
Dobra praktyka dla logów MCP — logować co najmniej: timestamp, request_id/traceId, nazwę narzędzia, parametry (zanonimizowane), status (ok/error) i czas wykonania.
Najprostszy logger.ts dla GiftGenius może wyglądać tak:
// 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 }));
}
A w obsłudze narzędzia:
// 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 {
// ... rzeczywiste wyszukiwanie prezentów ...
const results = []; // placeholder
logToolEvent("end", { tool: "search_gifts", traceId, count: results.length });
return results;
} catch (err) {
logToolEvent("error", { tool: "search_gifts", traceId, error: String(err) });
throw err;
}
}
Są dwa ważne niuanse.
Po pierwsze, nie trzeba w logach przechowywać pełnych adresów e‑mail, telefonów, numerów kart, tokenów. To nie tylko nieeleganckie, ale też sprzeczne z podstawowymi praktykami bezpieczeństwa MCP.
Po drugie, traceId — twój najlepszy przyjaciel. Gdy oglądasz razem logi Next.js i MCP, łatwo po nim powiązać zdarzenia: konkretne żądanie tools/call, odpowiadający mu render Reacta i log sieciowy widżetu.
Jak z logów zrozumieć, gdzie się wywaliło
Masz terminal, lecą w nim linijki JSON od logToolEvent. Typowy scenariusz:
- przyszedł phase: "start" z tool: "search_gifts";
- brak phase: "end", za to jest phase: "error" i stack trace;
- z tego widać, że narzędzie dotarło do twojej logiki, ale coś się zepsuło w środku — np. żądanie do zewnętrznego API, parsowanie, praca z bazą danych.
Jeśli w ogóle nie widzisz logów dla tej nazwy narzędzia — znaczy, że żądanie nawet nie dotarło do narzędzia. Wtedy idziesz wyżej w stosie: tunel, endpoint /mcp, żądanie JSON tools/call.
4. MCP Inspector: debugowanie MCP jeszcze przed ChatGPT
Jeśli logi to twoje oczy, to MCP Inspector (lub MCPJam Inspector) to mikroskop.
Więcej o MCP Inspector i po co on jest
W module o MCP podłączaliśmy już Inspector, aby sprawdzić „Hello, MCP”-serwer. Tutaj używamy go jako głównego narzędzia debugowania: najpierw upewniamy się, że MCP żyje sam z siebie, a dopiero potem wchodzimy w Dev Mode i UI.
Inspector to osobna aplikacja (najczęściej web‑UI plus CLI), która pełni rolę klienta MCP. Łączy się z twoim serwerem przez HTTP/SSE lub stdin/stdout, wykonuje tools/list, tools/call i pokazuje surowe komunikaty JSON, handshake, listę narzędzi, zasoby itd.
Główny pomysł: usunąć ChatGPT z równania. Jeśli twoje narzędzie nie działa, najpierw chcesz zrozumieć, czy serwer żyje, czy protokół i schemat są poprawne, zanim obwinisz GPT.
Mini‑przebieg pracy z Inspectorem
Typowy scenariusz lokalnego debugowania wygląda tak:
- Uruchamiasz npm run dev, aby Next.js + endpoint MCP wstały.
- Uruchamiasz MCP Inspector, na przykład:
npx @modelcontextprotocol/inspector
(konkretna komenda zależy od użytego narzędzia).
- W Inspector podajesz URL swojego endpointu MCP, np. http://localhost:3000/api/mcp (albo HTTPS‑tunel, jeśli chcesz przetestować także jego).
- Sprawdzasz, czy przeszedł handshake: serwer powinien odpowiedzieć wspieranymi capabilities, listą tools, zasobów itd.
- Ręcznie wywołujesz interesujące narzędzie: wybierasz search_gifts, wpisujesz argumenty {"q": "dla dziewczyny do 30"}, klikasz „Call tool” i sprawdzasz:
- czy przyszła odpowiedź;
- czy nie zwrócił się błąd JSON‑RPC lub MCP;
- co serwer wypisał w logach dla tego wywołania.
Jeśli w Inspector już wszystko się wywala, nie musisz nawet otwierać ChatGPT: napraw serwer MCP.
Jeśli w Inspector wszystko jest świetnie, a ChatGPT wciąż narzeka — znaczy, że problem jest wyżej: URL w Dev Mode, autoryzacja, zachowanie modelu.
Przykład „celowo zepsuliśmy narzędzie”
Weźmy nasz search_gifts i zepsujmy go celowo:
export async function searchGiftsTool(args: { q: string }) {
if (args.q === "zepsuj się") {
throw new Error("Błąd szkoleniowy do demonstracji debugowania");
}
// ... normalna logika ...
return [];
}
Dalej:
- W Inspector wywołujesz search_gifts z argumentem {"q": "zepsuj się"}.
- W logach widzisz phase: "error" i stack trace.
- Upewniasz się, że serwer MCP uczciwie zwraca błąd.
Potem, gdy podłączysz to do ChatGPT Dev Mode i poprosisz model „dobierz prezent ze słowem „zepsuj się””, spróbuje on wywołać narzędzie i pokaże użytkownikowi komunikat w rodzaju „I encountered an error running the tool”. Widać: błąd pojawia się nie przez model, tylko przez twoje jawne wyjątki.
Taki zabieg dobrze trenuje myślenie: wyraźnie oddzielasz błąd biznesowy (sami rzuciliśmy Error) od protokołowego (zepsuty JSON, błędna nazwa narzędzia itd.).
5. Debugowanie widżetu: DevTools, stan i „debug‑baner”
Gdy serwer MCP jest już w miarę jasny, przechodzimy na frontend — widżet Apps SDK.
Gdzie i jak oglądać błędy widżetu
Twój widżet renderuje się wewnątrz ChatGPT w piaskownicy iframe. Dobra wiadomość: ten iframe ma zwykłe narzędzia DevTools przeglądarki.
Mini‑procedura:
- Otwórz ChatGPT w przeglądarce (Chrome/Edge/Firefox).
- Otwórz DevTools (zwykle F12 lub Ctrl+Shift+I).
- Karta Console — wybierz kontekst ramki, w której działa twój widżet (często to domena web-sandbox.oaiusercontent.com).
- Odśwież czat/wyślij wiadomość, aby GPT pokazał twoją App.
Jeśli widżet:
- w ogóle się nie pojawił;
- pokazał się szary/pusty;
- pokazuje czerwony błąd w konsoli
— to niemal na pewno problem kodu React: niedostępna właściwość, błędny import, źle użyty hook itp.
Karta Network też jest przydatna. Zobaczysz tam:
- ładowanie bundla JS twojej aplikacji (jeśli 404/500 — problem po stronie dev‑serwera/tunelu);
- żądania, które twój widżet wysyła na zewnątrz przez window.fetch, oraz odpowiedzi 4xx/5xx.
Najprostszy debug‑baner
Bardzo wygodny zabieg — dodać do korzeniowego komponentu widżetu niewielki „debug‑baner”, który w Dev Mode pokazuje, jakie to środowisko i jaka wersja builda.
Na przykład:
// 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>
);
}
A w korzeniowym komponencie widżetu:
// src/app/widget/page.tsx
import { DebugBanner } from "@/components/DebugBanner";
export default function GiftGeniusWidget() {
return (
<div>
<DebugBanner />
{/* pozostały UI wyszukiwania prezentów */}
</div>
);
}
Jeśli otworzyłeś ChatGPT, uruchomiłeś App, a banera nie widać — znaczy, że twój JS w ogóle nie dotarł do przeglądarki: albo błąd budowania, albo problem z endpointem, albo widżet po prostu nie jest zarejestrowany w serwerze MCP.
Stan lokalny i obsługa błędów
Twój widżet powinien umieć pokazywać różne stany: ładowanie, sukces, błąd. Jeśli nie — najwyższy czas dodać.
Mini‑wzorzec:
const [status, setStatus] = useState<"idle"|"loading"|"error"|"success">("idle");
async function handleSearch(query: string) {
try {
setStatus("loading");
// wywołujemy narzędzie MCP przez window.openai.callTool lub hook Apps SDK
setStatus("success");
} catch (e) {
console.error("Search failed", e);
setStatus("error");
}
}
W JSX:
{status === "error" && (
<div style={{ color: "red" }}>Coś poszło nie tak, spróbuj ponownie.</div>
)}
Dla debugowania kluczowe jest, aby:
- nie połykać wyjątków (inaczej w konsoli pusto, a UI po prostu „wisi”);
- wyraźnie pokazywać błąd w UI, inaczej użytkownikowi wydaje się, że App umarła.
6. Dev Mode jako część debugowania: co robi i jak nie obwiniać go na darmo
Teraz włączmy do obrazu ChatGPT Dev Mode. Do tej pory rozważaliśmy wyłącznie twój kod. Ale czasem wszystko działa lokalnie, w Inspector jest świetnie, a ChatGPT nadal odpowiada „Error talking to [AppName]” albo w ogóle nie proponuje twojej App.
Co robi Dev Mode
Dev Mode — to tryb ChatGPT, w którym możesz:
- tworzyć i edytować swoje Apps;
- wskazywać endpoint serwera MCP (zwykle https://twoja-domena/mcp lub /api/mcp);
- szybko aktualizować manifest i metadane bez publikacji w Store.
Z punktu widzenia debugowania Dev Mode to tylko kolejna warstwa konfiguracji:
- jeśli tam wskazany jest błędny URL;
- jeśli zapomniałeś o /mcp na końcu;
- jeśli tunel nadał nową domenę, a ty nie zaktualizowałeś ustawień
— ChatGPT zwyczajnie nie może dostać się do twojego serwera.
Typowy scenariusz awarii Dev Mode
Klasyka gatunku:
- Podniosłeś tunel https://abcd.ngrok.io, wskazałeś go w Dev Mode, wszystko działało.
- Następnego dnia zrestartowałeś ngrok, otrzymałeś https://efgh.ngrok.io.
- W Dev Mode wciąż jest https://abcd.ngrok.io/mcp.
- ChatGPT pisze „Error talking to GiftGenius”.
MCP Inspector wskazany na http://localhost:3000/api/mcp pokazuje, że wszystko OK. To znaczy, że MCP żyje, ale ChatGPT patrzy nie tam, gdzie trzeba.
Rozwiązanie: wejść w ustawienia Dev Mode, zaktualizować URL, nie zapominając o /mcp na końcu.
Dev Mode vs Store
W tym wykładzie mówimy wyłącznie o Dev Mode — to twoja piaskownica. Tutaj normalne jest częste zmienianie URL, przepinanie tunelu, poprawianie schematu narzędzi.
Gdy później pójdziesz do Store, endpoint będzie bardziej sztywno ustalony i takie żonglowanie będzie kiepskim pomysłem. Ale do Store mamy jeszcze kilka modułów, więc póki co spokojnie psujemy i naprawiamy w Dev Mode.
7. Mini‑algorytm debugowania: co robić, gdy „nic nie działa”
Teraz złóżmy wszystko w praktyczny algorytm. W istocie to te same trzy poziomy debugowania z początku wykładu, tylko zapisane jako sekwencja kroków.
Załóżmy, że otworzyłeś ChatGPT, wybrałeś GiftGenius, poprosiłeś „Dobierz prezent do 30$ dla kolegi‑geeka” i:
- GPT nic nie pisze o App;
- albo pisze „Error talking to GiftGenius”;
- albo otwiera się pusty/szary widżet.
Jak nie popaść w rozpacz?
Krok 1 (poziom MCP/serwer). Sprawdź MCP przez Inspector i logi
Na początek ignorujemy GPT i UI. Interesuje nas tylko serwer.
- Upewnij się, że npm run dev działa, a endpoint (/api/mcp) odpowiada.
- Podłącz MCP Inspector do http://localhost:3000/api/mcp lub do swojego tunelu.
- Sprawdź handshake — lista tools powinna się wyświetlać.
- Ręcznie wywołaj to samo narzędzie, które teoretycznie powinien wywołać GPT (np. search_gifts) z podobnymi argumentami.
Jeśli już tutaj wszystko się sypie — naprawiaj MCP: schematy, logikę biznesową, wywołania sieciowe. Użyj logów i traceId, aby zrozumieć, co dokładnie się psuje.
Krok 2 (poziom protokołu/Dev Mode). Sprawdź Dev Mode i URL
Jeśli w Inspector wszystko jest okej, a ChatGPT nadal nie widzi twojej App lub pisze o problemach z połączeniem:
- Otwórz ustawienia Dev Mode dla swojej App.
- Sprawdź, jaki URL jest tam wskazany dla MCP.
- Porównaj go z tym, czego faktycznie słucha twój serwer/tunel (i nie zapomnij upewnić się, że na końcu jest /mcp, jeśli twój serwer tego wymaga).
Często problem leży właśnie tutaj.
Krok 3 (poziom UI). Sprawdź widżet przez DevTools
Jeśli ChatGPT skutecznie wywołuje narzędzia (widać to w logach MCP), ale sam widżet zachowuje się dziwnie:
- Otwórz DevTools w przeglądarce na stronie ChatGPT.
- Karta Console — wybierz kontekst iframe twojego widżetu.
- Spójrz na błędy JS.
- Karta Network — upewnij się, że:
- bundle JS widżetu ładuje się bez 404/500;
- dodatkowe żądania (przez fetch/window.openai.fetch) zwracają sensowne odpowiedzi.
Równolegle spójrz na swój DebugBanner: jeśli się nie pojawił, znaczy, że w ogóle nie doszliśmy do drzewa React.
Krok 4. Użyj Dev Mode do odtworzenia zgłoszenia błędu
Gdy dostaniesz od kolegi/użytkownika zgłoszenie błędu, postaraj się zachować dokładny prompt, na którym się wywaliło. W Dev Mode możesz bardzo szybko odtworzyć scenariusz:
- Uruchom npm run dev, podnieś tunel.
- W Dev Mode wybierz swoją App.
- Wklej problematyczny prompt.
- Równolegle:
- patrz, jakie żądania JSON przychodzą na MCP w logach;
- w Inspector, jeśli trzeba, powtórz tools/call z tymi samymi argumentami.
Dzięki temu zamieniasz „czasem coś nie działa” w odtwarzalny scenariusz.
8. Drobne szlify kodu ułatwiające debugowanie
Aby utrwalić materiał, dodajmy jeszcze kilka przydatnych fragmentów do naszej aplikacji GiftGenius.
Konfiguracja środowiska i poziomów logowania
W konfiguracji serwera wygodnie jest jawnie określić endpoint MCP i poziom logowania:
// src/config.ts
export const config = {
mcpEndpoint:
process.env.NODE_ENV === "development"
? "http://localhost:3000/api/mcp" // tunel obsługuje to
: "https://api.giftgenius.com/api/mcp",
logLevel: process.env.NODE_ENV === "development" ? "DEBUG" : "ERROR",
};
I w logToolEvent można uwzględnić logLevel, aby na produkcji nie spamować zbędnymi wpisami.
Logowanie ustrukturyzowanych błędów MCP
Przy obsłudze narzędzi staraj się łapać spodziewane błędy i zwracać zrozumiałe komunikaty, zamiast wszystko walić w throw:
export async function searchGiftsTool(args: { q: string }) {
const traceId = crypto.randomUUID();
logToolEvent("start", { tool: "search_gifts", traceId, args });
try {
// ... normalny kod ...
return { content: [{ type: "text", text: "Znaleziono 3 prezenty" }] };
} catch (err) {
logToolEvent("error", { tool: "search_gifts", traceId, error: String(err) });
return {
content: [{ type: "text", text: "Błąd wyszukiwania prezentów. Spróbuj później." }],
isError: true,
};
}
}
W ten sposób ChatGPT zobaczy, że wynik jest oznaczony jako isError, i będzie mógł poprawnie zakomunikować problem użytkownikowi, a ty — zobaczysz w logach, co się wydarzyło.
9. Typowe błędy przy lokalnym debugowaniu ChatGPT App
Błąd nr 1: debugować „przez GPT”, zamiast przez serwer i inspektor.
Bardzo kusi po prostu patrzeć, co odpowiada model, i próbować zgadywać, gdzie masz buga. Ale model to najwyższa warstwa. Jeśli MCP-serwer nie działa sam z siebie (ręcznie, przez Inspector) — nie ma co liczyć na cuda od GPT. Najpierw doprowadź MCP do stabilnej pracy, a dopiero potem podłączaj ChatGPT.
Błąd nr 2: w ogóle nie patrzeć w logi lub logować wszystko jak leci.
Brak logów prowadzi do całkowitej ślepoty: nie wiesz, które narzędzie się wywołało, z jakimi argumentami i czym się skończyło. Nadmiar logów z kolei zamienia konsolę w „matrycę” niespójnych wierszy. Lepiej mieć kompaktowy, ustrukturyzowany log z tool, args (zanonimizowanymi), traceId, status i czasem wykonania.
Błąd nr 3: przechowywać w logach dane wrażliwe.
Logowanie tokenów, pełnych e‑maili i numerów kart to zła praktyka zarówno z punktu widzenia bezpieczeństwa, jak i polityki OpenAI. W logach powinna być tylko informacja, która realnie pomaga w debugowaniu, a dane osobowe — maskujemy albo nie zapisujemy wcale.
Błąd nr 4: obwiniać Dev Mode o wszystkie grzechy.
Dev Mode często staje się kozłem ofiarnym: „Pewnie OpenAI coś zepsuł”. W praktyce bardzo często problemem jest to, że zapomniałeś zaktualizować URL po restarcie tunelu lub wskazałeś złą ścieżkę (/ zamiast /mcp). Zanim napiszesz do wsparcia, zajrzyj do ustawień Dev Mode i porównaj endpoint z faktycznym adresem serwera.
Błąd nr 5: ignorować DevTools i błąd w widżecie.
Pusty lub szary widżet prawie zawsze oznacza błąd JavaScriptu po stronie klienta. Jeśli patrzysz tylko w logi MCP, ale nie otwierasz DevTools w ChatGPT, widzisz tylko połowę obrazu. Nawyk automatycznego wciskania F12 i patrzenia w Console/Network oszczędzi godziny życia.
Błąd nr 6: próbować „naprawić” błąd magicznymi opóźnieniami.
Czasem kusi zrobić setTimeout albo opóźnienie w stylu Thread.sleep „żeby wszystko zdążyło się załadować”. W świecie MCP/Next/React to prawie zawsze nietrafione leczenie: problem zwykle tkwi w schemacie, błędnym endpointcie lub błędzie w kodzie, a nie w tym, że „serwer nie zdążył”. Lepiej zrozumieć, gdzie jest przerwa (Inspector → Dev Mode → widżet), niż zasypywać ją opóźnieniami.
Błąd nr 7: wdrażać na Vercel, nie upewniwszy się, że lokalnie wszystko działa.
Chęć „szybciej na produkcję” jest zrozumiała, ale przenoszenie zepsutego MCP na Vercel to idealny sposób na uzyskanie dwóch warstw problemów: lokalnej i produkcyjnej. W tym module celowo wymagamy: najpierw MCP Jam/Inspector → wszystko OK, Dev Mode → bazowe scenariusze działają, i dopiero potem deploy.
GO TO FULL VERSION