Anatomia dell'handshake MCP: cosa succede davvero durante initialize

ai-integration - 22/07/2026

7 min read

Introduzione

Ogni connessione MCP inizia con un handshake. È il momento in cui client e server si presentano, si mettono d'accordo sulla versione del protocollo e si dichiarano a vicenda cosa sanno fare. Se questo passaggio fallisce, tutto il resto — tool, risorse, prompt — è irraggiungibile.

Capire l'handshake è utile per due motivi: ti aiuta a debuggare connessioni che non partono, e ti spiega cosa controlla un monitoring protocol‑aware come MCPSonar a ogni check. Vediamolo passo per passo.

Se non hai chiaro cosa sia un client MCP, parti dalla guida al Client MCP. Per il "perché" del monitoring, c'è l'articolo su perché l'uptime non basta.


Le tre fasi dell'handshake

L'handshake MCP si compone di tre messaggi, in quest'ordine:

  1. Il client invia una richiesta initialize.
  2. Il server risponde con il risultato dell'initialize.
  3. Il client invia una notifica notifications/initialized per confermare che è pronto.

Solo dopo il terzo messaggio la connessione è "operativa" e il client può cominciare a chiamare tools/list, tools/call e gli altri metodi. Tutto viaggia su JSON‑RPC 2.0, indipendentemente dal transport (http, SSE o stdio).


Fase 1 — La richiesta initialize

Il client apre le danze dichiarando chi è, quale versione del protocollo parla e quali capabilities supporta. Una richiesta tipica ha questa forma:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-06-18",
    "capabilities": {
      "roots": { "listChanged": true },
      "sampling": {}
    },
    "clientInfo": {
      "name": "mcpsonar",
      "version": "1.0.0"
    }
  }
}

I campi che contano:

  • protocolVersion — la versione del protocollo che il client propone. Le versioni MCP sono stringhe datate (es. 2024-11-05, 2025-03-26, 2025-06-18): è una negoziazione, non un numero semver.
  • capabilities — cosa sa fare il client (es. roots, sampling). Il server userà questa informazione per sapere cosa può chiedergli.
  • clientInfo — nome e versione del client, utili per logging e debug lato server.

Fase 2 — La risposta del server

Il server risponde dichiarando la versione che effettivamente userà e le proprie capabilities:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-06-18",
    "capabilities": {
      "tools": { "listChanged": true },
      "resources": { "subscribe": true },
      "prompts": {}
    },
    "serverInfo": {
      "name": "example-mcp-server",
      "version": "2.3.1"
    }
  }
}

Qui succedono le cose interessanti dal punto di vista del monitoring:

  • La negoziazione della versione. Il server può accettare la protocolVersion proposta dal client o rispondere con una versione diversa che supporta. Se le due parti non trovano un terreno comune, la connessione non può procedere. Questo è esattamente il tipo di rottura che un HTTP 200 non vede.
  • Le capabilities dichiarate. Il blocco capabilities.tools dice se il server espone tool e se notifica i cambiamenti (listChanged). Se questo blocco sparisce dopo un deploy, il server non offre più tool — pur restando "up".
  • serverInfo — nome e versione del server, utili per correlare i problemi con un rilascio specifico.

MCPSonar registra la protocolVersion restituita a ogni check: se un giorno cambia inaspettatamente, è un segnale che qualcosa nel deploy è andato storto.


Fase 3 — La notifica initialized

Ricevuta la risposta, il client conferma di essere pronto con una notifica (nessun id, perché non attende risposta):

{
  "jsonrpc": "2.0",
  "method": "notifications/initialized"
}

Da questo momento la connessione è pienamente operativa. Prima di questa notifica, il server non dovrebbe accettare richieste diverse dall'initialize (e alcuni server sono severi su questo punto).


Cosa può andare storto — e come si vede

Ecco i fallimenti tipici dell'handshake, quelli che un monitoring protocol‑aware intercetta e uno generico no:

| Sintomo | Causa probabile | Un ping se ne accorge? | |---|---|---| | initialize restituisce un errore JSON‑RPC | Bug lato server, dipendenza mancante | No | | protocolVersion diversa dall'attesa | Deploy con versione MCP cambiata | No | | capabilities.tools assente | Il server non espone più tool | No | | Handshake lentissimo | Cold start, servizio a valle degradato | No | | Timeout prima della risposta | Transport instabile, server sovraccarico | A volte |

In tutti questi casi la porta è aperta e il processo è vivo. È solo completando l'handshake che scopri il problema — ed è precisamente ciò che MCPSonar fa a ogni check, misurando anche quanto tempo impiega ciascuna fase.


Perché questo conta per il monitoring

L'handshake è il primo, indispensabile cancello di ogni sessione MCP. Se si rompe, non importa quanto siano perfetti i tuoi tool: nessun client ci arriva. Ed è un tipo di rottura invisibile agli strumenti tradizionali, perché avviene dentro il protocollo, non a livello di rete.

Un monitoring che esegue l'handshake vero:

  • prova che la connessione è stabilibile end‑to‑end, non solo che la porta risponde;
  • cattura la spec version negoziata e ti avvisa se cambia;
  • misura la latenza dell'handshake, spesso il primo indicatore di un degrado;
  • verifica che il server dichiari ancora le capabilities che ti aspetti.

Conclusione

initialize → risposta → initialized: tre messaggi che sembrano una formalità, ma che sono la fondazione di ogni interazione MCP. Conoscerli ti rende più veloce nel debug e ti fa capire perché un check protocol‑aware è così più informativo di un ping.

Nel prossimo articolo vediamo l'altra metà della storia: i transport MCP a confronto — http, SSE e stdio, perché il canale su cui viaggia questo handshake cambia parecchio in termini di affidabilità. Se vuoi invece toccare con mano il monitoraggio, c'è il tutorial con MCPSonar e Docker.

I payload JSON qui sopra sono semplificati a scopo didattico: fai sempre riferimento alla specifica ufficiale MCP per i dettagli esatti della versione che stai usando.