1. Perché una lezione separata sul debug locale
Nei moduli precedenti abbiamo già analizzato come sono strutturati lo stack dell’Apps SDK e MCP. Ora vediamo perché serve una lezione separata sul debug locale.
Molti seguono questo percorso: «Beh, apro ChatGPT, scrivo "usa la mia App", e poi guardo cosa dice. Se non funziona — riscrivo il codice a caso». È come riparare un backend guardando solo la pagina HTML nel browser senza mai aprire i log del server.
Con le ChatGPT Apps è particolarmente facile scivolare nella magia: c’è GPT, decide lui se chiamare o meno un tool, ha una sua logica degli errori. Se non vedete cosa succede sotto il cofano, il debug si trasforma in sciamanesimo.
Il nostro obiettivo: trasformarlo in un normale processo ingegneristico:
- sapete dove guardare i log di Next/MCP;
- sapete invocare a mano il server MCP tramite l’inspector;
- capite cosa verifica il Dev Mode e come assicurarvi che ChatGPT riesca effettivamente a raggiungere il vostro server.
E, soprattutto, smettete di fare debug con «l’indovinello di GPT» e iniziate prima a verificare i livelli bassi dello stack — server e protocollo, e solo dopo UI e comportamento del modello.
2. Modello mentale: tre livelli di debug
Per non affogare nel caos, conveniamo di pensare al debug in termini di tre livelli. È il nostro piccolo «millefoglie»:
| Livello | Cosa vive lì | Sintomi tipici | Con cosa facciamo debug |
|---|---|---|---|
| UI (widget) | componenti React, stato, window.openai | Widget vuoto/grigio, render difettoso, pulsanti non funzionano | DevTools del browser |
| Backend / MCP‑server | tools, accesso a DB/API | 500, «il tool è caduto», dati strani | log del server, MCP Inspector |
| Protocollo MCP | JSON‑RPC, tools/list, tools/call, schemi | GPT scrive «impossibile invocare lo strumento», invalid params | inspector + log delle richieste |
Al secondo livello ci interessa cosa fa il server MCP in sé (tools, DB, API), mentre al terzo — i «cavi» e il formato dei messaggi MCP (JSON‑RPC, schemi ecc.).
Questa triade è la base del piano della lezione e del corso sul debug.
Per chiarezza, guardiamo il flusso della richiesta:
sequenceDiagram
participant User as Utente
participant ChatGPT as ChatGPT (Dev Mode)
participant Tunnel as Tunnel (ngrok/CF)
participant Next as Next.js + MCP
User->>ChatGPT: "Scegli un regalo fino a $50"
ChatGPT->>Next: tools/call search_gifts (tramite Tunnel)
Next->>Next: Chiamiamo il tool MCP, andiamo su DB/API
Next-->>ChatGPT: JSON-RPC result + ToolOutput
ChatGPT-->>User: Risposta + render del widget
Può rompersi in qualunque punto: tunnel, endpoint, logica MCP, schema JSON, widget React. Il vostro compito nel debug è capire in quale livello sia l’errore, non riscrivere subito tutto a caso.
3. Log di Next.js e MCP: la base di tutto
Partiamo dalla cosa più noiosa e più utile — i log.
Dove vivono i log nello sviluppo locale
Nel template standard dell’Apps SDK su Next.js il server MCP è di solito incapsulato in una route API (/api/mcp o simile). Avviate npm run dev e in un terminale avete:
- il dev server di Next.js;
- un handler per l’endpoint MCP che riceve richieste JSON‑RPC tools/list, tools/call ecc.;
- la stampa di tutto il divertimento via console.log/console.error.
Se avete estratto MCP in un processo separato, ci sarà un secondo terminale, ma l’idea è la stessa: tutto ciò che è interessante si vede in console.
Importante distinguere:
- errori di build/avvio — non si alza next dev, cade TypeScript, import errato ecc.;
- errori di esecuzione — tutto si avvia, ma una specifica richiesta su /api/mcp porta al fallimento di un tool.
Next.js in modalità dev mostra gli errori runtime anche con un overlay carino e scrive lo stack trace in console.
Cosa loggare nel server MCP
Anche se MCP usa il protocollo JSON‑RPC, per il debug non serve stampare tutto il JSON. Sono molto più utili log strutturati ma brevi.
Una buona pratica per i log MCP è loggare almeno: timestamp, request_id/traceId, nome del tool, parametri (anonimizzati), stato (ok/error) e tempo di esecuzione.
Un semplicissimo logger.ts per GiftGenius può essere così:
// 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 }));
}
E nell’handler del tool:
// 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 {
// ... ricerca reale dei regali ...
const results = []; // segnaposto
logToolEvent("end", { tool: "search_gifts", traceId, count: results.length });
return results;
} catch (err) {
logToolEvent("error", { tool: "search_gifts", traceId, error: String(err) });
throw err;
}
}
Ci sono due sfumature importanti.
Primo, non memorizzate nei log email complete, telefoni, numeri di carta, token. Non è solo brutto, ma confligge con le pratiche basilari di sicurezza di MCP.
Secondo, traceId è il vostro migliore amico. Quando guardate insieme i log di Next.js e MCP, con esso legate facilmente gli eventi: una specifica richiesta tools/call, il corrispondente render di React e il log di rete del widget.
Come capire dai log dove è caduto
Avete il terminale con le righe JSON che arrivano da logToolEvent. Scenario tipico:
- arriva phase: "start" con tool: "search_gifts";
- non c’è phase: "end", ma c’è phase: "error" e lo stack trace;
- da ciò si vede che il tool è arrivato alla vostra logica, ma qualcosa si è rotto dentro — per esempio, una richiesta a un’API esterna, parsing, lavoro con il DB.
Se invece non vedete affatto log per quel nome di tool — la richiesta non è nemmeno arrivata al tool. Allora salite nello stack: tunnel, endpoint /mcp, richiesta JSON tools/call.
4. MCP Inspector: debug di MCP prima di ChatGPT
Se i log sono i vostri occhi, MCP Inspector (o MCPJam Inspector) è il microscopio.
Dettagli su MCP Inspector e a cosa serve
Nel modulo su MCP abbiamo già collegato l’Inspector per verificare il server «Hello, MCP». Qui lo usiamo come strumento principale di debug: prima ci assicuriamo che MCP stia in piedi da solo, e solo dopo passiamo al Dev Mode e all’UI.
L’Inspector è un’app separata (spesso web‑UI più CLI) che funge da client MCP. Si collega al vostro server via HTTP/SSE o stdin/stdout, esegue tools/list, tools/call e mostra i messaggi JSON grezzi, l’handshake, la lista dei tools, le risorse ecc.
L’idea principale: togliere ChatGPT dall’equazione. Se un tool non funziona, prima volete capire se il server è vivo, se il protocollo e lo schema sono corretti, prima di dare la colpa a GPT.
Mini‑flusso di lavoro con l’Inspector
Scenario tipico di debug locale:
- Avviate npm run dev, così Next.js + endpoint MCP si alzano.
- Avviate MCP Inspector, ad esempio:
npx @modelcontextprotocol/inspector
(il comando esatto dipende dallo strumento usato).
- Nell’Inspector indicate l’URL del vostro endpoint MCP, ad esempio http://localhost:3000/api/mcp (o il tunnel HTTPS, se volete verificare anche quello).
- Controllate se l’handshake è passato: il server deve rispondere con le capabilities supportate, la lista di tools, risorse, ecc.
- Chiamate a mano il tool che vi interessa: scegliete search_gifts, inserite gli argomenti {"q": "per una ragazza fino a 30"}, premete «Call tool» e verificate:
- se è arrivata una risposta;
- se non è tornato un errore JSON‑RPC o MCP;
- cosa scrive il server nei log per questa chiamata.
Se già nell’Inspector tutto cade, non serve nemmeno aprire ChatGPT: sistemate il server MCP.
Se nell’Inspector è tutto ok ma ChatGPT continua a lamentarsi — il problema è più in alto: URL del Dev Mode, autorizzazione, comportamento del modello.
Esempio «tool rotto di proposito»
Prendiamo il nostro search_gifts e rompiamolo apposta:
export async function searchGiftsTool(args: { q: string }) {
if (args.q === "rompiti") {
throw new Error("Errore didattico per la dimostrazione del debug");
}
// ... logica normale ...
return [];
}
Poi:
- Nell’Inspector chiamate search_gifts con l’argomento {"q": "rompiti"}.
- Nei log vedete phase: "error" e lo stack trace.
- Vi assicurate che il server MCP ritorni onestamente un errore.
Poi, quando collegherete tutto a ChatGPT Dev Mode e chiederete al modello «scegli un regalo con la parola "rompiti"», proverà a chiamare il tool e mostrerà all’utente un messaggio del tipo «I encountered an error running the tool». Si vede: l’errore non nasce dal modello, ma dalla vostra eccezione esplicita.
Questa tecnica allena bene la mente: separate chiaramente l’errore di business (noi lanciamo esplicitamente Error) da quello di protocollo (JSON rotto, nome del tool errato ecc.).
5. Debug del widget: DevTools, stato e «debug‑banner»
Quando il server MCP è più o meno chiaro, passiamo al frontend — il widget dell’Apps SDK.
Dove e come vedere gli errori del widget
Il vostro widget viene renderizzato dentro ChatGPT in una sandbox iframe. Ma la buona notizia: quell’iframe ha comunque i DevTools del browser.
Mini‑procedura:
- Aprite ChatGPT nel browser (Chrome/Edge/Firefox).
- Aprite i DevTools (di solito F12 o Ctrl+Shift+I).
- Nella scheda Console — scegliete il contesto del frame dove vive il vostro widget (spesso è il dominio web-sandbox.oaiusercontent.com).
- Aggiornate la chat/inviate il messaggio, così che GPT mostri la vostra App.
Se il widget:
- non è proprio apparso;
- è apparso grigio/vuoto;
- mostra un errore rosso in console
— quasi certamente è un problema del codice React: proprietà non disponibile, import errato, hook storto ecc.
Anche la scheda Network è utile. Lì vedrete:
- il caricamento del bundle JS della vostra app (se 404/500 — problema lato dev server/tunnel);
- le richieste che il vostro widget fa all’esterno via window.fetch, e le risposte 4xx/5xx.
Semplice debug‑banner
Molto comodo — aggiungere nel componente radice del widget un piccolo «debug‑banner» che in Dev Mode mostra ambiente e versione del build.
Per esempio:
// 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>
);
}
E nel componente radice del widget:
// src/app/widget/page.tsx
import { DebugBanner } from "@/components/DebugBanner";
export default function GiftGeniusWidget() {
return (
<div>
<DebugBanner />
{/* resto dell'UI di ricerca regali */}
</div>
);
}
Se avete aperto ChatGPT, lanciato la App, ma non vedete il banner — il vostro JS non è arrivato al browser: o errore di build, o problema con l’endpoint, o il widget semplicemente non è registrato nel server MCP.
Stato locale e gestione degli errori
Il vostro widget dovrebbe già saper mostrare stati diversi: caricamento, successo, errore. Se non lo fa — è il momento di aggiungerli.
Mini‑pattern:
const [status, setStatus] = useState<"idle"|"loading"|"error"|"success">("idle");
async function handleSearch(query: string) {
try {
setStatus("loading");
// chiamiamo il tool MCP tramite window.openai.callTool o un hook dell'Apps SDK
setStatus("success");
} catch (e) {
console.error("Search failed", e);
setStatus("error");
}
}
Nel JSX:
{status === "error" && (
<div style={{ color: "red" }}>Qualcosa è andato storto, riprova.</div>
)}
Per il debug è cruciale che:
- non inghiottiate le eccezioni (altrimenti in console nulla, e l’UI «si blocca»);
- riflettiate esplicitamente l’errore nell’UI, altrimenti all’utente sembra che la App sia morta.
6. Dev Mode come parte del debug: cosa fa e come non accusarlo a sproposito
Ora includiamo nella scena il ChatGPT Dev Mode. Finora abbiamo considerato solo il vostro codice. Ma a volte tutto funziona in locale, nell’Inspector tutto perfetto, e ChatGPT risponde comunque «Error talking to [AppName]» o non propone affatto la vostra App.
Cosa fa il Dev Mode
Il Dev Mode è una modalità di ChatGPT in cui potete:
- creare e modificare le vostre Apps;
- indicare l’endpoint del server MCP (di solito https://il-tuo-dominio/mcp o /api/mcp);
- aggiornare rapidamente manifest e metadati senza pubblicazione nello Store.
Dal punto di vista del debug, il Dev Mode è solo un ulteriore livello di configurazione:
- se lì è indicato un URL errato;
- se avete dimenticato /mcp alla fine;
- se il tunnel ha fornito un nuovo dominio e non avete aggiornato le impostazioni
— ChatGPT semplicemente non riesce a raggiungere il vostro server.
Scenario tipico di rottura del Dev Mode
Classico:
- Avete alzato un tunnel https://abcd.ngrok.io, l’avete indicato nel Dev Mode, tutto funzionava.
- Il giorno dopo riavviate ngrok, ottenete https://efgh.ngrok.io.
- Nel Dev Mode c’è ancora https://abcd.ngrok.io/mcp.
- ChatGPT scrive «Error talking to GiftGenius».
MCP Inspector nel frattempo, puntato a http://localhost:3000/api/mcp, mostra che è tutto ok. Questo significa che MCP è vivo, ma ChatGPT guarda nel posto sbagliato.
Soluzione: andare nelle impostazioni del Dev Mode, aggiornare l’URL, senza dimenticare /mcp alla fine.
Dev Mode vs Store
In questa lezione parliamo solo del Dev Mode — è la vostra sandbox. Qui è normale cambiare spesso l’URL, ricollegare il tunnel, modificare lo schema dei tools.
Quando poi andrete nello Store, l’endpoint sarà più rigidamente fissato, e questi giochetti non saranno una buona idea. Ma allo Store arriveremo più avanti, quindi per ora rompiamo e ripariamo in serenità nel Dev Mode.
7. Mini‑algoritmo di debug: cosa fare quando «non funziona niente»
Ora mettiamo tutto insieme in un algoritmo pratico. In sostanza, sono i tre livelli di debug dall’inizio della lezione, scritti come sequenza di passi.
Supponiamo che apriate ChatGPT, scegliate GiftGenius, chiediate «Scegli un regalo fino a 30$ per un amico geek», e:
- GPT non scrive nulla sulla App;
- oppure scrive «Error talking to GiftGenius»;
- oppure si apre un widget vuoto/grigio.
Come non cadere nella disperazione?
Passo 1 (livello MCP/server). Verificare MCP tramite Inspector e log
Prima ignoriamo GPT e l’UI. Ci interessa solo il server.
- Assicuratevi che npm run dev sia attivo e che l’endpoint (/api/mcp) risponda.
- Collegate MCP Inspector a http://localhost:3000/api/mcp o al vostro tunnel.
- Controllate l’handshake — la lista dei tools deve comparire.
- Chiamate a mano lo stesso tool che, in teoria, dovrebbe invocare GPT (per esempio, search_gifts), con argomenti simili.
Se già qui tutto cade — sistemate MCP: schemi, logica di business, chiamate di rete. Usate i log e il traceId per capire cosa esattamente si rompe.
Passo 2 (livello protocollo/Dev Mode). Verificare Dev Mode e URL
Se nell’Inspector è tutto perfetto, ma ChatGPT continua a non vedere la vostra App o a segnalare problemi di connessione:
- Aprite le impostazioni del Dev Mode per la vostra App.
- Guardate quale URL è indicato per MCP.
- Confrontatelo con ciò che realmente ascolta il vostro server/tunnel (e non dimenticate di verificare che alla fine ci sia /mcp, se il vostro server lo richiede).
Spesso il problema è proprio qui.
Passo 3 (livello UI). Verificare il widget con DevTools
Se ChatGPT riesce a chiamare i tools (si vede dai log di MCP), ma il widget si comporta in modo strano:
- Aprite i DevTools del browser sulla pagina di ChatGPT.
- Scheda Console — selezionate il contesto dell’iframe del vostro widget.
- Guardate gli errori JS.
- Scheda Network — assicuratevi che:
- il bundle JS del widget si carichi senza 404/500;
- le richieste aggiuntive (tramite fetch/window.openai.fetch) restituiscano risposte sensate.
In parallelo guardate il vostro DebugBanner: se non è apparso, non siete proprio arrivati all’albero React.
Passo 4. Usare il Dev Mode per riprodurre il bug report
Quando ricevete da un collega/utente un bug report, cercate di conservare il prompt esatto su cui si è rotto. Nel Dev Mode potete riprodurre molto rapidamente lo scenario:
- Avviare npm run dev, alzare il tunnel.
- Nel Dev Mode scegliere la App.
- Incollare il prompt problematico.
- In parallelo:
- vedere quali richieste JSON arrivano a MCP nei log;
- nell’Inspector, se necessario, ripetere tools/call con gli stessi argomenti.
Così trasformate «a volte qualcosa non funziona» in uno scenario riproducibile.
8. Piccoli ritocchi di codice per un debug più comodo
Per fissare il materiale, aggiungiamo ancora qualche frammento utile alla nostra applicazione GiftGenius.
Configurazione dell’ambiente e livelli di logging
Da qualche parte nella configurazione del server è comodo indicare esplicitamente l’endpoint MCP e il livello di logging:
// src/config.ts
export const config = {
mcpEndpoint:
process.env.NODE_ENV === "development"
? "http://localhost:3000/api/mcp" // il tunnel fa da proxy a questo
: "https://api.giftgenius.com/api/mcp",
logLevel: process.env.NODE_ENV === "development" ? "DEBUG" : "ERROR",
};
E in logToolEvent potete tener conto di logLevel, così da non spammare in produzione.
Logging di errori strutturati MCP
Durante l’elaborazione dei tools cercate di intercettare gli errori attesi e restituire messaggi comprensibili, invece di buttare tutto in throw:
export async function searchGiftsTool(args: { q: string }) {
const traceId = crypto.randomUUID();
logToolEvent("start", { tool: "search_gifts", traceId, args });
try {
// ... codice normale ...
return { content: [{ type: "text", text: "Trovati 3 regali" }] };
} catch (err) {
logToolEvent("error", { tool: "search_gifts", traceId, error: String(err) });
return {
content: [{ type: "text", text: "Errore nella ricerca dei regali. Riprova più tardi." }],
isError: true,
};
}
}
Così ChatGPT vedrà che il risultato è marcato come isError e potrà comunicare correttamente il problema all’utente, e voi — vedrete nei log cosa è successo.
9. Errori tipici nel debug locale di ChatGPT App
Errore №1: fare debug «tramite GPT», invece che tramite server e inspector.
È molto allettante guardare solo cosa risponde il modello e cercare di indovinare dove sia il bug. Ma il modello è il livello più alto. Se il server MCP non funziona da solo (a mano, tramite l’Inspector) — non c’è da aspettarsi miracoli da GPT. Prima ottenete il funzionamento stabile di MCP, poi collegate ChatGPT.
Errore №2: non guardare i log affatto o loggare tutto indiscriminatamente.
L’assenza di log porta a cecità totale: non sapete quale tool è stato chiamato, con quali argomenti e come è finito. Il super‑logging, al contrario, trasforma la console in una «matrice» di righe sconnesse. Meglio avere un log compatto e strutturato con tool, args (anonimizzati), traceId, status e tempo di esecuzione.
Errore №3: conservare nei log dati sensibili.
Loggare token, email complete e numeri di carta è una cattiva pratica sia dal punto di vista della sicurezza sia della policy di OpenAI. Nei log deve esserci solo l’informazione che aiuta davvero il debug, e i dati personali — si mascherano o non si scrivono affatto.
Errore №4: incolpare il Dev Mode di tutti i mali.
Il Dev Mode spesso diventa il capro espiatorio: «OpenAI ha rotto qualcosa». In realtà molto spesso il problema è che avete dimenticato di aggiornare l’URL dopo il riavvio del tunnel o avete indicato il percorso sbagliato (/ invece di /mcp). Prima di scrivere al supporto, guardate le impostazioni del Dev Mode e confrontate l’endpoint con l’indirizzo effettivo del server.
Errore №5: ignorare i DevTools e l’errore nel widget.
Un widget vuoto o grigio significa quasi sempre un errore JavaScript sul client. Se guardate solo i log di MCP ma non aprite i DevTools in ChatGPT, vedete solo metà del quadro. L’abitudine di premere automaticamente F12 e guardare Console/Network vi farà risparmiare ore di vita.
Errore №6: provare a «riparare» un bug con ritardi magici.
A volte viene voglia di fare un setTimeout o un ritardo in stile Thread.sleep «per far caricare tutto». Nel mondo MCP/Next/React è quasi sempre la cura sbagliata: il problema di solito è nello schema, nell’endpoint errato o nell’errore del codice, non nel fatto che «il server non ha fatto in tempo». Meglio capire dove sia esattamente la rottura (Inspector → Dev Mode → widget), invece di coprirla con ritardi.
Errore №7: fare deploy su Vercel senza essersi assicurati che in locale tutto funzioni.
Il desiderio di «andare in prod più in fretta» è comprensibile, ma spostare un MCP rotto su Vercel è il modo perfetto per ottenere due livelli di problemi: locale e produzione. In questo modulo chiediamo deliberatamente: prima MCP Jam/Inspector → tutto ok, Dev Mode → scenari base funzionano, e solo dopo il deploy.
GO TO FULL VERSION