1. MCP e JSON‑RPC: il «noioso» fondamento da capire una volta per tutte
Nella lezione precedente abbiamo parlato del perché serve MCP e di come si inserisce nello stack di Apps SDK. In questa lezione restringiamo il focus allo strato più «noioso» — il formato dei messaggi MCP, così da potervi far leggere con sicurezza i log JSON grezzi e capire cosa esattamente ChatGPT invia al vostro server e cosa risponde quest’ultimo.
MCP utilizza JSON‑RPC 2.0 come trasporto dati: tutte le richieste, le risposte e le notifiche sono normali oggetti JSON con uno schema prevedibile.
Cioè, invece di «ogni servizio si inventa il proprio formato», esiste un contratto di base:
- una richiesta ha il campo obbligatorio jsonrpc (di solito "2.0"), un id univoco, un nome di metodo testuale method e un oggetto params con i parametri;
- la risposta è collegata alla richiesta tramite id e contiene o result, o error;
- le notifiche (notifications) assomigliano alle richieste, ma senza id, e non prevedono risposta.
Si presenta più o meno così:
{
"jsonrpc": "2.0",
"id": 42,
"method": "tools/list",
"params": {
"cursor": null
}
}
Questo è un request. E la risposta in caso di successo:
{
"jsonrpc": "2.0",
"id": 42,
"result": {
"tools": [],
"nextCursor": null
}
}
Se vi è venuto in mente «ma è il solito RPC», è proprio così. MCP si limita a fissare quali metodi esistono (tools/list, tools/call, resources/list, prompts/list, …) e in quale formato si aspettano i parametri e restituiscono i dati.
È importante cogliere l’idea: JSON‑RPC è l’ossatura «richiesta–risposta–notifica». MCP è «quali richieste esistono e cosa contengono».
2. Request: come MCP chiede di fare qualcosa
Partiamo dalle richieste. Vanno sempre nella direzione «qualcuno vuole fare qualcosa». Di solito è client → server (ChatGPT → il vostro server MCP), ma MCP ammette anche richieste inverse, quando il server chiede al client di fare sampling o elicitation. In questa lezione ci interessa soprattutto il caso classico: il client chiede al server.
Qualsiasi MCP‑request ha tre campi chiave:
- jsonrpc — la versione del protocollo JSON‑RPC, normalmente "2.0".
- id — l’identificatore della richiesta; qualunque tipo JSON, ma nella pratica più spesso numero o stringa. L’importante è che gli id siano univoci per le richieste attive.
- method — una stringa del tipo "tools/list" o "tools/call". MCP specifica l’insieme dei metodi ammessi.
C’è poi l’oggetto params, che contiene i parametri del metodo specifico.
Esempio: richiesta dell’elenco degli strumenti
Immaginiamo che ChatGPT si sia appena connesso al vostro server MCP e voglia sapere quali tools può invocare. Invierà una richiesta di questo tipo:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {
"cursor": null
}
}
Il campo cursor serve per la paginazione — se gli strumenti sono molti, il server può restituirli a tranche.
Per la nostra app didattica (selezione di regali) qui per ora è tutto un po’ noioso: uno‑due strumenti, ma il protocollo resta lo stesso. Prendetelo ancora come esempio intuitivo; le strutture formali le analizzeremo più avanti nella sezione sui tools.
Esempio: invocazione di uno strumento (tools/call)
Ora qualcosa di più interessante. Supponiamo che abbiamo già un MCP‑tool suggest_gifts, che prevedete di implementare nella lezione sul server MCP. Si aspetta i parametri:
- occasion — l’occasione (Birthday, Wedding, …),
- budget — un numero in dollari,
- recipient — una stringa che descrive a chi è destinato il regalo.
Decidendo di usare questo strumento, ChatGPT formerà una richiesta MCP:
{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "suggest_gifts",
"arguments": {
"occasion": "birthday",
"budget": 100,
"recipient": "friend who loves board games"
}
}
}
Notate alcuni dettagli.
Primo, il nome dello strumento è quello che avete dichiarato lato server (server.registerTool("suggest_gifts", …)). Secondo, l’oggetto arguments deve rispettare la JSON Schema che allegate nella descrizione dello strumento.
Se il GPT prova a inviare argomenti non conformi allo schema (ad esempio, budget: "cento dollari"), il server ha il diritto di restituire un errore a livello di protocollo o di logica di business, a seconda dell’implementazione. Per ora è fondamentale afferrare la forma generale della richiesta; nella sezione sui tools qui sotto vedremo gli stessi messaggi in modo più sistematico.
Requests per risorse e prompt
In modo analogo appaiono le richieste a risorse e prompt. La specifica MCP definisce i metodi:
- resources/list — elencare le risorse disponibili;
- resources/read (o resources/get) — leggere una risorsa specifica tramite URI;
- prompts/list — ottenere l’elenco dei prompt disponibili;
- prompts/get — ottenere il testo di un prompt specifico.
Esempio di richiesta di lettura di una risorsa con il catalogo dei regali:
{
"jsonrpc": "2.0",
"id": 15,
"method": "resources/read",
"params": {
"uri": "mcp://gift-server/resources/gift_catalog"
}
}
Per ora basta ricordare due cose. Primo, per ogni primitiva esistono i metodi */list e */get/*/read. Secondo, il nome del metodo vive sempre nel campo testuale method, e tutto il contenuto è nell’oggetto params.
3. Reply: come risponde MCP — result e error
La risposta (reply) è sempre legata alla richiesta tramite il campo id. È come il correlationId in molti sistemi distribuiti: guardate i log e vedete che la richiesta con id=7 ha ricevuto la risposta con id=7, quindi è la stessa coppia.
JSON‑RPC stabilisce una regola semplice: nella risposta o c’è result, oppure error, ma non entrambi insieme. MCP sopra a ciò precisa la struttura di result per i diversi metodi (tools/list, tools/call, ecc.) e raccomanda codici di errore.
Risposta di successo (result)
Guardiamo un esempio di risposta di successo a tools/call del nostro suggest_gifts. Il server ha eseguito tutto, ha trovato regali adatti e restituisce l’elenco nel campo result:
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"content": [
{
"type": "text",
"text": "Here are some gift ideas for your friend..."
}
],
"structuredContent": {
"gifts": [
{ "name": "Board game: Catan", "price": 45 },
{ "name": "Dice set", "price": 20 }
]
},
"isError": false
}
}
Qui sono importanti diversi aspetti.
- Primo, content e structuredContent sono proprio quelle parti della risposta dei MCP‑tools che avete già visto in Apps SDK. Il modello utilizza il testo da content, e il vostro widget renderizza ordinatamente i dati da structuredContent.
- Secondo, il flag isError si riferisce al risultato di business. Dal punto di vista del protocollo è andato tutto bene: JSON valido, metodo esistente, argomenti letti. Ma la logica di business può dire: «non ho trovato nessuna idea regalo, dal punto di vista della UX lo consideriamo un errore». Allora impostate isError: true e descrivete il problema in content.
- Terzo, la specifica MCP per i diversi metodi (tools/list, tools/call, */list, */get) descrive in dettaglio quali campi devono essere in result. Ad esempio, per tools/list il server restituisce un array di descrizioni degli strumenti con nomi, titoli, descrizioni e la JSON Schema degli argomenti in input.
Risposta di errore (error)
Se qualcosa è andato storto a livello di protocollo o di server, invece di result viene restituito un oggetto error. In genere ha:
- code — codice numerico dell’errore;
- message — descrizione leggibile;
- data — dati aggiuntivi opzionali (stack trace, dettagli, …).
Esempio: il modello ha invocato un metodo inesistente:
{
"jsonrpc": "2.0",
"id": 99,
"error": {
"code": -32601,
"message": "Method not found: tools/col"
}
}
Il codice -32601 è quello classico di JSON‑RPC «method not found».
C’è un confine sottile ma importante tra due tipi di errori.
Errore di protocollo — quando sono violate le regole MCP/JSON‑RPC: metodo sconosciuto, tipo errato nel campo params, JSON non valido. Allora è opportuno restituire error al livello superiore.
Errore di business — quando il protocollo è rispettato, ma l’operazione non riesce per una ragione di dominio: catalogo vuoto, mancanza di permessi su una risorsa, identificatore di business non valido. In tal caso MCP di solito consiglia di restituire un result valido, ma di marcarlo come isError: true e descrivere il problema nel contenuto.
Questa distinzione aiuta molto ChatGPT e gli strumenti di debug: guardando i log, vedete subito se è stato un guasto tecnico o un rifiuto consapevole della logica di business.
4. Notifications: messaggi monodirezionali
Una notifica (notification) è una «lettera senza aspettare risposta». In JSON‑RPC le notifiche assomigliano alle richieste senza il campo id. Il client non deve inviare una reply.
In MCP le notifiche sono usate per eventi: modifiche agli elenchi di tools/resources/prompts, avanzamento di operazioni lunghe, messaggi di log ecc.
L’esempio più semplice che incontrerete di sicuro è la notifica che la lista degli strumenti è cambiata. La specifica MCP per i tools descrive la capability listChanged e la notifica tools/list_changed, che il server invia quando il set di tools disponibili cambia.
Una notifica può apparire così:
{
"jsonrpc": "2.0",
"method": "tools/list_changed",
"params": {
"reason": "New tool 'suggest_gift_cards' was added"
}
}
Non serve rispondere. Il client, ricevuta la notifica, può decidere: «ok, chiamiamo di nuovo tools/list e aggiorniamo la cache degli strumenti».
Altre notifiche tipiche MCP (ne parleremo nel dettaglio nel modulo su stream ed eventi):
- eventi di avanzamento (notifications/progress) per operazioni lunghe;
- log del server (notifications/logging/message);
- modifiche alle risorse (resources/list_changed) e ai prompt (prompts/list_changed).
Per ora conta una cosa: notifica = richiesta senza id e senza risposta attesa. Se vedete nei log un JSON senza id, molto probabilmente è una notification.
Approfondimento
È stato verificato sperimentalmente che l’app ChatGPT ignora i messaggi a essa inviati (MCP‑notification). Tuttavia, considerando che le ChatGPT Apps sono solo all’inizio del loro sviluppo, la probabilità del pieno supporto di tutti gli aspetti del protocollo MCP nel prossimo futuro è molto alta. Quindi consiglio comunque di studiare anche questo lato del protocollo MCP.
5. Come appaiono tools/resources/prompts nei messaggi
Ora la parte più interessante: come esattamente all’interno dei messaggi MCP sono descritti i tools, le resources e i prompts di cui parliamo tanto.
Tools: descrizione e invocazione
A livello di protocollo i tools hanno due processi principali:
- discovery — il client scopre quali strumenti esistono;
- invocation — il client invoca uno strumento specifico.
Abbiamo già intravisto tools/list e tools/call sopra. Ora guardiamoli in modo più sistematico: quali processi coprono e cosa viene restituito in result.
5.1.1. Elenco degli strumenti — tools/list
Abbiamo già visto il request per tools/list. Vediamo la struttura della risposta. La specifica MCP dice: in result.tools deve essere restituito un array di oggetti, ciascuno dei quali descrive uno strumento. Ogni strumento deve avere:
- name — nome univoco, con cui poi verrà chiamato tools/call;
- title — titolo breve (lo vedono sia la persona sia il modello);
- description — descrizione più estesa di ciò che fa il tool, come se lo spiegaste a un collega;
- inputSchema — la JSON Schema degli argomenti dello strumento.
Per il nostro suggest_gifts la risposta di tools/list può essere così (molto semplificata):
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "suggest_gifts",
"title": "Gift ideas generator",
"description": "Suggests gift ideas for a given occasion and budget.",
"inputSchema": {
"type": "object",
"properties": {
"occasion": { "type": "string" },
"budget": { "type": "number" },
"recipient": { "type": "string" }
},
"required": ["occasion", "budget"]
}
}
],
"nextCursor": null
}
}
Se avete già scritto inputSchema nell’Apps SDK durante la registrazione dello strumento, avete praticamente già visto questo oggetto, solo «dall’alto» — sotto forma di oggetto TypeScript. MCP lo trasferisce semplicemente via protocollo al client.
5.1.2. Invocazione dello strumento — tools/call
Il formato dell’invocazione lo abbiamo già toccato. La specifica MCP descrive che params deve contenere:
- name — il nome dello strumento;
- arguments — un oggetto conforme a inputSchema.
Ad esempio:
{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "suggest_gifts",
"arguments": {
"occasion": "wedding",
"budget": 150,
"recipient": "coworker from marketing"
}
}
}
In risposta il server restituisce un result con content, structuredContent e, opzionalmente, _meta (ad esempio con l’indicazione openai/outputTemplate, se volete collegare questo tool a un widget specifico).
Questa coppia tools/list → tools/call è il ciclo di base del lavoro dei MCP‑tools: prima discovery, poi utilizzo.
Resources: dati con un indirizzo
Le resources in MCP sono qualsiasi porzione di dati a cui il client può accedere tramite URI: file, record di DB, config, cataloghi ecc.
Hanno un set standard di operazioni:
- resources/list — per scoprire quali risorse esistono;
- resources/read — per leggere una risorsa specifica (o una sua parte).
Immaginiamo la risorsa gift_catalog, che descrive un catalogo base di regali: categorie, brand, prezzi minimi e massimi. Il server può dichiararla con l’URI "mcp://gift-server/resources/gift_catalog".
La risposta a resources/list può essere (semplificando):
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"resources": [
{
"uri": "mcp://gift-server/resources/gift_catalog", // solo una stringa univoca. MCP non è un protocollo.
"name": "gift_catalog",
"description": "Base catalog of gifts with categories and prices",
"mimeType": "application/json"
}
],
"nextCursor": null
}
}
E la lettura della risorsa — resources/read:
{
"jsonrpc": "2.0",
"id": 4,
"method": "resources/read",
"params": {
"uri": "mcp://gift-server/resources/gift_catalog"
}
}
La risposta può contenere il contenuto e i metadati:
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"contents": [
{
"uri": "mcp://gift-server/resources/gift_catalog",
"mimeType": "application/json",
"text": "{\"categories\":[\"boardgames\",\"books\"]}"
}
]
}
}
L’idea principale: una resource è dati indirizzabili, mentre i tools sono operazioni. MCP rende esplicite entrambe le cose nel protocollo.
Prompts: template riutilizzabili
I prompts sono «suggerimenti preconfezionati» o template che il server può fornire al client. MCP li tratta come una primitiva che ha:
- un nome;
- un titolo/descrizione leggibili;
- un contenuto (spesso un template di system‑prompt o un set di esempi few‑shot).
E, prevedibilmente, ci sono due metodi:
- prompts/list — scoprire quali prompt ci sono;
- prompts/get — ottenere il contenuto di un prompt.
Per esempio, volete definire uno stile particolare per generare messaggi di auguri insieme al regalo. Allora nel server MCP potete dichiarare il prompt gift_congrats_style.
La risposta a prompts/list può essere così:
{
"jsonrpc": "2.0",
"id": 10,
"result": {
"prompts": [
{
"name": "gift_congrats_style",
"description": "Style guide for birthday congratulations in a friendly tone"
}
]
}
}
E prompts/get restituirà il testo (o il contenuto strutturato), che il client può poi passare alla LLM come parte del system‑prompt. Ecco un esempio di richiesta e risposta:
{
"jsonrpc": "2.0",
"id": 11,
"method": "prompts/get",
"params": {
"name": "gift_congrats_style"
}
}
{
"jsonrpc": "2.0",
"id": 11,
"result": {
"prompt": {
"name": "gift_congrats_style",
"messages": [
{
"role": "system",
"content": [
{
"type": "text",
"text": "You are a friendly assistant that writes short, warm birthday congratulations..."
}
]
}
]
}
}
}
6. Come questo si collega all’Apps SDK e al nostro widget
Ora l’MCP‑JSON potrebbe sembrarvi ancora un po’ macchinoso. Colleghiamolo a ciò che avete già fatto tramite Apps SDK.
Ricordiamo che, nel frontend del widget, potete avere codice come:
// dentro un componente React nella sandbox di ChatGPT
async function fetchGifts() {
const result = await window.openai.callTool("suggest_gifts", {
occasion: "birthday",
budget: 50,
recipient: "friend who loves sci-fi"
});
console.log(result);
}
A livello di Apps SDK è una funzione comoda che:
- conosce l’URL del server MCP (dalla configurazione dell’app);
- sa trovare per nome suggest_gifts la descrizione dello strumento;
- impacchetta la vostra chiamata in un MCP‑request tools/call;
- lo invia tramite il trasporto scelto (HTTP/SSE);
- attende la reply MCP, estrae il result e ve lo restituisce come result in JavaScript.
Se lo disegniamo a schema, viene più o meno così:
sequenceDiagram
participant Widget
participant AppsSDK as Apps SDK
participant MCP as server MCP
Widget->>AppsSDK: window.openai.callTool("suggest_gifts", {...})
AppsSDK->>MCP: JSON { id:7, method:"tools/call", params:{...} }
MCP-->>AppsSDK: JSON { id:7, result:{ content, structuredContent } }
AppsSDK-->>Widget: result (ToolOutput)
Widget->>Widget: setState(toolOutput)
Capire il formato MCP vi dà due ottime abilità.
Primo, potete guardare con cognizione i log MCP grezzi (per esempio, in MCP Inspector, di cui parleremo in una lezione a parte) e vedere: quale tools/call è partito, quali argomenti conteneva, cosa è tornato in result o error.
Secondo, progettando strumenti e risorse potete pensare non solo in termini di tipi TypeScript, ma anche in termini di schemi MCP: come apparirà in JSON, quanto sarà comodo per altri client (ad esempio agenti che possono anch’essi collegarsi al vostro server MCP).
7. Mini‑pratica: leggere e «aggiustare» MCP‑JSON
Per rendere familiare il formato MCP, è meglio smontare a mano un paio di messaggi. Prendiamo un esempio di dialogo completo tools/list → tools/call → risultato.
Il client vuole l’elenco degli strumenti
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}
Cosa vediamo:
- è un request (c’è id);
- il metodo è tools/list, quindi si parla di discovery degli strumenti;
- i parametri sono vuoti, senza paginazione.
Il server risponde:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "suggest_gifts",
"title": "Gift ideas generator",
"description": "Suggests gift ideas",
"inputSchema": { "type": "object", "properties": { "occasion": { "type": "string" } } }
}
]
}
}
Si vede subito che è la risposta a quella richiesta (stesso id: 1), il protocollo è andato a buon fine (c’è result, non c’è error), e ora il client sa che esiste il tool suggest_gifts.
Il client invoca lo strumento
Poi il client fa tools/call:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "suggest_gifts",
"arguments": {
"occasion": "anniversary"
}
}
}
Se il server si aspetta anche budget, ma il modello non l’ha indicato, il server può:
- o restituire un errore di protocollo (per esempio, un error con il codice «invalid params»);
- oppure applicare una decisione di default (ad esempio, usare un budget medio) e restituire un result normale.
Nei termini introdotti sopra, il primo caso è un errore di protocollo (error al livello superiore), il secondo è già ambito di logica di business: restituite comunque un result valido e decidete se considerare la situazione un errore di business (isError: true) o un comportamento normale.
La risposta in caso di errore degli argomenti potrebbe essere:
{
"jsonrpc": "2.0",
"id": 2,
"error": {
"code": -32602,
"message": "Missing required property 'budget' in arguments"
}
}
Di nuovo, distinguiamolo dall’errore di business: il protocollo è violato (gli argomenti non corrispondono allo schema), quindi qui è appropriato un error.
Esempio rotto: cerchiamo il bug
Ecco un JSON che a volte si vede tra i principianti:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"tool": "suggest_gifts",
"args": {
"occasion": "birthday",
"budget": 100
}
}
}
A prima vista sembra plausibile, ma confrontandolo con la specifica MCP noterete che i campi tool e args non coincidono con gli attesi name e arguments.
Un client/server MCP‑SDK probabilmente non genererà mai un JSON del genere, ma se, senza conoscere la specifica, vi integrate a mano, un bug simile è abbastanza realistico. È per questo che nel corso analizziamo il protocollo «allo scoperto», e non solo le astrazioni dell’SDK.
8. Errori tipici nell’uso dei messaggi MCP
Errore n. 1: confondere errori di protocollo e di business.
Spesso gli sviluppatori, per abitudine, incartano tutto ciò che «va storto» nel error di alto livello — sia l’assenza di una risorsa, sia argomenti errati, sia i crash del database. Nel contesto MCP è utile distinguere: se la struttura JSON e lo schema della chiamata sono violati (metodo sbagliato, campi errati, tipi non corretti), è il caso di restituire error. Se invece lo strumento non è riuscito a eseguire l’operazione di dominio (nessun regalo per quel budget, utente non trovato), è meglio restituire un result valido con isError: true e un messaggio chiaro in content. Così il modello di ChatGPT e i debugger possono distinguere correttamente tra «si è rotto il canale» e «il server ha rifiutato consapevolmente».
Errore n. 2: ignorare il campo id e la correlazione delle richieste.
A volte nei log del server MCP si vede output manuale senza id o con valori id ripetuti per diverse richieste attive. In un hello‑world single‑thread può ancora «funzionare», ma appena compaiono chiamate parallele o retry, diventa difficile capire quale risposta corrisponda a quale richiesta. JSON‑RPC richiede apposta un id univoco per la durata della richiesta, e MCP si basa su questa regola. Se usate gli SDK ufficiali, potete non pensare all’id, ma appena scrivete trasporto o logging da soli, non dimenticate di salvare e stampare l’id — è la prima cosa con cui farete debug di bug strani.
Errore n. 3: strutture instabili di result per lo stesso metodo.
È forte la tentazione di «cambiare un po’» il formato della risposta a seconda della situazione: a volte restituire un array di regali, a volte un oggetto con una sola stringa, a volte solo text senza structuredContent. Il modello forse sopporterà questi numeri, ma i vostri widget e qualsiasi altro client MCP — difficilmente. La specifica MCP per ogni metodo descrive una struttura prevedibile di result; cercate di attenervi a quella. Se serve un altro formato, meglio dichiarare un tool separato o una versione, piuttosto che cambiare lo schema al volo.
Errore n. 4: campi superflui o mancanti in params.
Un problema tipico delle implementazioni custom è aggiungere in params ciò che MCP non si aspetta, o dimenticare un campo obbligatorio. Per esempio, inviare toolName invece di name in tools/call, o resourceId invece di uri in resources/read. Gli MCP‑SDK in genere validano queste cose e lanciano un’eccezione chiara, ma se lavorate più vicino al protocollo, potreste passare molto tempo a cercare perché «il server non mi capisce». Una buona tecnica è tenere accanto all’handler un esempio di richiesta JSON corretta dalla specifica o dai log di un client funzionante e confrontarlo con ciò che inviate.
Errore n. 5: tentare di usare le notifications come «secondo canale di risposte».
A volte gli sviluppatori, vedendo le notifications, iniziano a inviare i risultati delle operazioni tramite notifiche invece delle normali replies: «siamo già in MCP e abbiamo SSE, spingiamo tutto con le notifiche». Il problema è che le notifiche JSON‑RPC per definizione non sono collegate a uno specifico id e non sono percepite dal client come risposta a una richiesta. Ne consegue che è più difficile fare debug ed è impossibile capire a quale invocazione di strumenti si riferisca un dato messaggio. Le notifiche vanno benissimo per eventi (lista di tools/resources/prompts modificata, nuovo progresso, log arrivato), ma non per risposte alle normali tools/call e simili.
Errore n. 6: non guardare log e inspector MCP.
L’errore più umano — provare a fare debug dell’integrazione solo via UI di ChatGPT: «ho premuto il pulsante, non è arrivato qualcosa, capirò prima o poi». Finché non vedete i messaggi MCP grezzi (requests, replies, notifications), è difficile capire a che livello è il problema: il modello non ha invocato il tool, l’Apps SDK non è arrivato al server MCP, il server ha restituito un JSON sbagliato, o tutto si è rotto già in fase di rendering del widget. MCP Inspector / Jam e un logging strutturato dei messaggi MCP — i vostri migliori amici. Dopo che avrete visto una volta un tools/call e un tools/list reali nei log, il formato dei messaggi MCP smetterà definitivamente di essere «magia» e diventerà una normale routine ingegneristica.
GO TO FULL VERSION