1. Perché serve un ispettore MCP
Immaginate di fare debug del frontend, ma vi vietano di aprire DevTools. Più o meno così è la vita senza un ispettore MCP. Il protocollo MCP va «sotto il cofano» di ChatGPT e dell’Apps SDK; se guardate solo la risposta in chat e pensate: «Ma perché non vede il mio strumento?», in sostanza state sparando nel vuoto.
Gli inspector come MCP Inspector (ufficiale) o MCP Jam sono client MCP per sviluppatori. Sanno:
- connettersi al vostro server MCP esattamente come fa ChatGPT;
- eseguire handshake / capabilities;
- richiedere l’elenco di tools/resources/prompts;
- invocare manualmente qualsiasi tool con argomenti arbitrari;
- mostrare i messaggi JSON grezzi (requests / replies / errors).
In sostanza, è «Postman per MCP, ma con cervello». A differenza di un normale client REST, l’ispettore conosce la specificità MCP: capisce tools/list, tools/call, sa mostrare gli schemi degli argomenti e talvolta supporta anche il flusso OAuth per server protetti.
Se non avete un inspector, il debug è così: avviate ChatGPT, provate a chiamare l’App, vedete «Error talking to app» oppure che il tool non viene proprio invocato, e iniziate a chiedervi: è il modello che non ha voluto chiamare lo strumento, è il vostro MCP che non è partito o è un errore JSON? Con l’ispettore potete verificare ogni livello separatamente: prima il server MCP da solo con l’ispettore, poi il collegamento ChatGPT ↔ MCP.
2. Mini‑panoramica degli inspector: MCP Inspector, Jam e altri
Nella pratica userete più spesso due tipi di inspector per MCP.
Per prima cosa, c’è l’MCP Inspector ufficiale dal repository Model Context Protocol. È un’app web (di solito una SPA su React), che si avvia o in locale, o tramite npx/Docker e sa connettersi al vostro server MCP via HTTP/SSE.
In secondo luogo, ci sono inspector in stile MCP Jam, che spesso aggiungono comodità anche per OAuth. Possono leggere da soli .well-known/oauth-protected-resource, estrarne authorization_endpoint e token_endpoint, eseguire il flusso PKCE e poi, in stato autorizzato, inviare richieste al MCP.
MCP Jam è stato creato dagli sviluppatori sulla base di MCP Inspector. Se MCP Inspector implementa un set minimo di strumenti di debug, MCP Jam implementa tutto ciò che serve a uno sviluppatore nel lavoro quotidiano con MCP. Personalmente consiglio di usare da subito MCP Jam, così poi non dovrete riabituarvi.
Per il nostro corso, la differenza è che:
- l’Inspector di base vi serve sempre, anche per un server MCP semplicissimo e non protetto;
- MCP Jam (o analogo) diventa utile quando arrivate ai moduli su autenticazione e autorizzazione.
Ma l’idea generale è una: è un normale client MCP, solo che sa mostrare in modo chiaro ciò che ChatGPT fa «in silenzio».
3. Scenario tipico d’uso dell’ispettore MCP
Vediamo lo scenario tipico: avete scritto un nuovo tool nel vostro server MCP e volete assicurarvi che funzioni davvero.
Nella lezione precedente avete già avviato un server MCP minimale. Ora aggiungiamo un approccio sistematico di verifica: eseguiamo il ciclo completo «server → inspector → logica JSON» passo dopo passo.
Passo 1 — avviamo il server MCP
L’avete già fatto nella lezione precedente: supponiamo che abbiate uno script npm run mcp-dev:
# esempio di avvio del server MCP
npm run mcp-dev
# sotto il cofano qualcosa come: ts-node src/mcp-server.ts
È importante che il server ascolti il trasporto scelto: nel corso è di solito l’endpoint HTTP /mcp su qualche porta, per esempio http://localhost:4001/mcp.
Passo 2 — avviamo MCP Jam
Secondo terminale:
# una delle modalità di avvio di MCP Jam
npx @mcpjam/inspector@latest
# se necessario si può aggiungere --port 4002 ecc.
Dopodiché l’ispettore si apre nel browser, molto spesso su http://localhost:6274 o una porta simile.
Nella schermata iniziale di MCP Jam vi verrà chiesto di indicare l’URL del server MCP. Inserite:
http://localhost:4001/mcp
oppure il vostro URL tunnelizzato, se già mandate tutto tramite ngrok.
Passo 3 — handshake / capabilities
Non appena MCP Jam si connette, fa automaticamente ciò che fa ChatGPT:
- Invia una richiesta di inizializzazione (initialize) con i dati del client.
- Riceve la risposta con la versione del protocollo e le capabilities del vostro server.
- Sulla base delle capabilities capisce se il server supporta tools, resources, prompts e altre funzionalità.
Nell’UI questo di solito appare come:
Connected
Protocol: mcp/2025-06-18
Capabilities:
- tools: list, call
- resources: list, read
- prompts: list, get
Se già a questo passo l’ispettore non riesce a connettersi (connection refused, CORS, 500 ecc.), vedete subito l’errore e capite: il problema non è nel modello né in ChatGPT, ma nella vostra parte server o nella rete.
Passo 4 — discovery: guardiamo tools/resources/prompts
Dopo un handshake riuscito, l’ispettore invoca di solito metodi come tools/list, resources/list, prompts/list per popolare il pannello laterale. Vedrete:
- l’elenco degli strumenti con descrizioni e lo JSON Schema degli argomenti di input;
- l’elenco delle risorse, raggruppate per collezioni/percorsi;
- l’elenco dei prompt con brevi descrizioni.
Se avete appena aggiunto un nuovo tool ma non compare, significa che non è registrato correttamente sul server o che il server non è stato avviato con il codice aggiornato. È molto più semplice accorgersene qui che chiedersi perché ChatGPT «non vuole» chiamare il vostro strumento.
4. Invocazione manuale dei tools tramite MCP Jam
La funzione più utile di MCP Jam è la chiamata manuale degli strumenti. È la vostra UI personale per tools/call.
Selezioniamo lo strumento e compiliamo gli argomenti
Supponiamo che nel modulo precedente abbiate scritto il tool suggest_gifts:
// da qualche parte in src/mcp/tools/suggestGifts.ts
export const suggestGiftsTool = {
name: "suggest_gifts",
description: "Suggerisce idee regalo in base all’età, al budget e agli interessi",
inputSchema: {
type: "object",
properties: {
age: { type: "number" },
budget: { type: "number" },
interests: {
type: "array",
items: { type: "string" }
}
},
required: ["age", "budget"]
},
// l'handler è definito altrove
};
In MCP Jam fate clic su suggest_gifts. A destra si apre un form generato da inputSchema. Lì compilate:
{
"age": 30,
"budget": 100,
"interests": ["giochi", "libri"]
}
e premete «Call» o un pulsante analogo.
L’ispettore invia la richiesta MCP tools/call e vedete subito:
- i dati JSON grezzi della richiesta (cosa esattamente va al server);
- i dati JSON grezzi della risposta (result o error);
- eventualmente, un’anteprima formattata del risultato.
Leggiamo i log JSON nell’ispettore
Di solito l’ispettore mostra qualcosa come:
// Richiesta
{
"id": "1",
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "suggest_gifts",
"arguments": {
"age": 30,
"budget": 100,
"interests": ["giochi", "libri"]
}
}
}
// Risposta
{
"id": "1",
"jsonrpc": "2.0",
"result": {
"content": [
{
"type": "text",
"text": "1) Gioco da tavolo ... 2) Buono regalo per una libreria ..."
}
]
}
}
Se il vostro handler cade con un’eccezione, vedrete un error in stile JSON‑RPC:
{
"id": "1",
"jsonrpc": "2.0",
"error": {
"code": -32603,
"message": "Internal error",
"data": "TypeError: Cannot read properties of undefined ..."
}
}
Molto importante: qui vedete il livello di protocollo. Se nella risposta il formato non è quello che si aspetta Apps SDK/ChatGPT, potete accorgervene prima di iniziare a dare la colpa ai «bug di GPT».
5. Debug di risorse e prompt
Gli strumenti non sono tutto ciò che sa fare MCP. Sapete già che esistono anche resources e prompts.
Tramite l’ispettore potete:
- aprire l’elenco delle risorse (resources/list) e guardarne i metadati;
- leggere una risorsa specifica (resources/read) e assicurarvi che i dati restituiti siano corretti;
- eseguire ricerche sulle risorse (se avete implementato la funzionalità);
- guardare i prompt predefiniti e il loro testo.
Per esempio, se avete una risorsa gift_catalog:
// pseudocodice di registrazione della risorsa
registerResource({
uri: "resource://giftgenius/catalog",
name: "Catalogo regali",
mimeType: "application/json",
handler: async () => {
return JSON.stringify(giftCatalogData);
}
});
Nell’ispettore vedrete questa risorsa, ci fate clic e potete vedere subito il JSON. Se risulta che il JSON non è valido, o il MIME type è strano, lo intercetterete prima che ChatGPT inizi a inciampare tentando di leggerlo o integrarlo in un widget.
6. Log del server MCP: cosa, dove e come loggare
MCP Jam è ottimo, ma non basta: vi servono i log del server MCP. Senza di essi qualsiasi produzione diventa una lotteria.
Cosa loggare
Minimo utile:
- ogni messaggio MCP in ingresso (request/notification) con:
- timestamp;
- metodo (tools/call, tools/list ecc.);
- nome dello strumento (se presente);
- argomenti troncati (senza dati sensibili);
- ogni risposta in uscita:
- stato (successo / errore);
- tempo di esecuzione;
- una versione abbreviata del risultato o almeno il tipo;
- errori tecnici:
- parsing JSON;
- eccezioni inattese negli handler.
È molto importante non loggare PII e segreti integralmente: token, password, testi completi di richieste confidenziali. Le linee guida per il logging in produzione spesso raccomandano esplicitamente di loggare dati con PII troncata.
Dove scrivere i log: stdout / stderr
In MCP c’è un requisito importante: i messaggi JSON devono andare nel «canale giusto», mentre tutti i log di debug in un altro. Per esempio, se usate un trasporto su stdout/stderr, allora:
- i messaggi JSON‑RPC devono andare su stdout;
- tutti i console.log, console.error ecc. devono andare su stderr.
Se mescolate JSON e log testuali nello stesso flusso, il client (MCP Jam o ChatGPT) non riuscirà a fare il parse dei messaggi, perché in mezzo al JSON improvvisamente troverà una riga come Server started at http://localhost:4001. È uno degli errori più frequenti nei server MCP.
Nel caso HTTP il problema è più semplice, ma il principio è lo stesso: nella risposta HTTP deve esserci JSON pulito, e tutti i log vanno in console/file, non nel body della risposta.
Logger semplice per un server MCP in TypeScript
Aggiungiamo al nostro ipotetico server MCP un piccolo logger:
// src/logger.ts
export function logRequest(method: string, details: unknown) {
console.error(
JSON.stringify({
level: "info",
type: "request",
method,
details,
ts: new Date().toISOString(),
})
);
}
export function logError(method: string, error: unknown) {
console.error(
JSON.stringify({
level: "error",
type: "error",
method,
error: String(error),
ts: new Date().toISOString(),
})
);
}
E nell’handler dei tools:
// src/mcp-server.ts (frammento)
server.setRequestHandler("tools/call", async (req) => {
logRequest("tools/call", {
name: req.params?.name,
// qui è meglio non inserire l'intero payload, ma solo i campi safe
});
try {
const result = await handleToolCall(req);
return result;
} catch (e) {
logError("tools/call", e);
throw e;
}
});
Così vedrete in console log JSON strutturati, che poi è facile correlare tra loro tramite ts o un ulteriore requestId.
7. Combinazione: MCP Jam + log
La strategia corretta di debug MCP quasi sempre è questa:
- Riproducete il problema nell’ispettore: vedete che tools/list restituisce un elenco vuoto, tools/call cade, la risposta JSON è strana ecc.
- Nello stesso momento guardate i log del server MCP: cosa scrive all’avvio, quali errori emette per ogni messaggio, se c’è uno stack trace.
- Mettete in relazione id, method, ts nei log con ciò che vede l’ispettore.
Per esempio, nell’ispettore vedete:
{
"error": {
"code": -32603,
"message": "Internal error"
}
}
E in parallelo nei log:
{
"level": "error",
"type": "error",
"method": "tools/call",
"error": "TypeError: Cannot read properties of undefined (reading 'age')",
"ts": "2025-11-21T10:15:12.345Z"
}
Chiaro: da qualche parte nell’handler vi aspettate age, ma lo schema/gli argomenti sono diversi.
8. Mini‑checklist: il server MCP è pronto per l’integrazione con l’App?
Prima di collegare il server MCP a una vera ChatGPT App, è comodo passare una piccola checklist con l’ispettore.
Primo, handshake e capabilities devono passare senza errori. MCP Jam deve mostrare che il server supporta le entità che vi servono: almeno tools e, se usati, resources / prompts.
Secondo, l’elenco di tools/resources/prompts nell’ispettore deve coincidere con il set di strumenti, risorse e prompt che ritenete di aver implementato. Errori di battitura in name, registrazioni dimenticate ecc. qui si beccano al volo.
Terzo, le chiamate degli strumenti con argomenti validi devono restituire in modo stabile un result corretto. È consigliabile provare alcuni casi tipici (richieste su cui contate davvero in produzione).
Quarto, le chiamate con argomenti non validi devono restituire risposte di error chiare in stile JSON‑RPC, e non cadere con 500. Per esempio, se manca un parametro obbligatorio, è bene restituire un errore strutturato che ChatGPT possa poi trasformare in un messaggio comprensibile all’utente.
Quinto, i log del server non devono riempire la console di gigabyte di stack trace per ogni piccolezza. Gli errori devono essere strutturati, e i dati sensibili — filtrati con cura.
Se tutto questo funziona nell’ispettore, potete collegare molto più serenamente il server MCP all’Apps SDK e giocare con i widget in Dev Mode.
9. Bug tipici del server MCP e come intercettarli con l’ispettore
Ora passiamo al più interessante — cosa si rompe più spesso e come vederlo.
Configurazione e connessione
A volte sembra che «il server non funzioni», ma il problema è che non ascolta la porta o l’endpoint giusto. L’ispettore in tal caso dirà onestamente connection refused o non riuscirà proprio a connettersi. Cause frequenti: URL errato (per esempio, /mcp invece di /api/mcp), porta occupata da un altro processo, tunnel non attivo o CORS che blocca le richieste.
JSON non valido / mescolanza di log e protocollo
Una delle storie più dolorose — quando stampate console.log("Server started") su stdout, sopra al quale devono passare i messaggi JSON‑RPC. Il client si aspetta JSON pulito, ma riceve testo + JSON, prova a fare il parse e cade con un errore di formato.
La soluzione è semplice: separare rigorosamente ciò che va nel flusso di protocollo (stdout o body della risposta HTTP) da ciò che va nei log (stderr o file di log separato).
Disallineamento fra schema e implementazione dello strumento
Un altro errore comune: in inputSchema dichiarate una cosa, ma nel codice ve ne aspettate un’altra. Per esempio, lo schema dice: age — numero, interests — array opzionale di stringhe, ma il codice prova a fare arguments.interests.toLowerCase(). Il modello (e l’ispettore) inviano correttamente interests come null o non inviano affatto il campo — e qui tutto cade.
L’ispettore consente di vedere esplicitamente quale JSON va davvero in tools/call, e confrontarlo con il vostro codice.
Nomi errati di tools/resources
Se nelle capabilities / tools/list esportate il tool come suggest_gifts_v2, ma nel manifesto dell’App o nel widget vi aspettate suggest_gifts, allora «strumento non trovato» vi accompagnerà fino alla fine del progetto. Nell’ispettore dall’elenco dei tools e dai loro campi name questo si vede subito, senza dover indovinare cosa pensa GPT.
Tools lenti o bloccati
Se la chiamata di uno strumento nell’ispettore dura 30 secondi e poi cade in timeout, non sperate che ChatGPT reagisca meglio. L’ispettore MCP aiuterà a capire a quale stadio rallentate: chiamata di rete, DB, API esterna. Nei log è utile avere l’ora di inizio e fine dell’elaborazione di ogni richiesta, per vedere subito gli outlier.
10. Errori tipici nell’ispezione e nel debug MCP
Errore n. 1: cercare di fare debug MCP solo tramite ChatGPT.
Molti sviluppatori collegano prima MCP all’App, vedono che «qualcosa non funziona», e iniziano a cambiare prompt, descrizione dello strumento, a volte persino la versione del modello. Nel frattempo il server MCP non si avvia affatto o tools/list è vuoto. Iniziate sempre dall’ispettore: se lì va male, il modello non c’entra.
Errore n. 2: mescolare JSON‑RPC e log nello stesso flusso.
Quando il client MCP si aspetta JSON pulito e voi stampate su stdout stringhe di debug, il risultato è prevedibile — il parsing si rompe, Inspector mostra errori strani. I log devono andare separatamente (stderr, file, sistemi di logging esterni), mentre i messaggi di protocollo — rigorosamente nel loro canale.
Errore n. 3: non guardare capabilities e l’elenco dei tools.
Spesso uno strumento «scompare» semplicemente perché vi siete dimenticati di registrarlo o di abilitare la capability corrispondente. Se non guardate capabilities e tools/list nell’ispettore, potete pensare a lungo che la colpa sia del modello e non del vostro codice di registrazione.
Errore n. 4: ignorare gli errori di schema e le discrepanze nel JSON.
Quando inputSchema e il JSON effettivo divergono, il modello e l’ispettore iniziano prevedibilmente a comportarsi in modo strano. Se non guardate i messaggi JSON grezzi nell’ispettore e non convalidate lo schema, questi errori emergeranno nei momenti più inaspettati.
Errore n. 5: loggare di tutto, incluse PII e token.
Nel fervore del debug è facile iniziare a stampare nei log l’intero body della richiesta, incluse possibili informazioni personali o segreti. In produzione questo diventa una bomba a orologeria: leak, problemi di compliance ecc. Loggate solo ciò che serve davvero per la diagnosi, con dati troncati/anonimizzati.
Errore n. 6: non riprodurre il problema con casi minimi.
A volte il bug si manifesta in un dialogo complesso tramite ChatGPT e lo sviluppatore cerca di fare debug così com’è. È molto più efficace riprodurre lo stesso scenario nell’ispettore con una‑due richieste MCP, eliminando l’influenza dei prompt, della cronologia del dialogo e dell’«umore» del modello.
GO TO FULL VERSION