Passa al contenuto principale

MCP Apps: UI interattive dentro una tool call

Avanzato

Per tutto il suo primo anno MCP è stato un protocollo testuale: una tool call restituiva JSON o Markdown e il client renderizzava come voleva. MCP Apps — la prima estensione ufficiale, finalizzata il 26 gennaio 2026 e integrata nella core spec del 2026-07-28 — aggiunge un canale UI. Un server può ora inviare un blocco di HTML, il client lo renderizza in un iframe sandboxed e l'iframe risponde tramite JSON-RPC 2.0 su postMessage. Ogni scrittura continua a passare per lo stesso audit path di qualsiasi altra tool call. Pensa a un report interattivo + conferma-prima-di-eseguire, non a "esegui una webapp completa dentro Claude."

What you'll learn
  • Cos'è davvero MCP Apps — e le quattro cose che deliberatamente NON è
  • La forma sul filo: negoziazione di capability, risorse ui://, il link _meta.ui del tool e il ponte postMessage
  • Il modello di sicurezza — sandbox, CSP, Permission Policy, approvazione dell'host — e dove ti lascia comunque esposto
  • Quando ricorrere a un App invece che a un semplice risultato di tool (pattern rari ma che valgono la pena)
  • Come si compone con il resto della spec stateless 2026-07-28 che già parli

La versione in un paragrafo​

Il server dichiara una o più risorse UI su URI ui://<server>/<name> con MIME type text/html;profile=mcp-app. Un tool segnala "ho una UI" mettendo _meta.ui.resourceUri nel suo schema. Su una tools/call il server può restituire il solito testo/dato più un riferimento alla risorsa UI; il client scarica l'HTML con resources/read, lo mette in un <iframe> sandboxed, e ora iframe e host chiacchierano via postMessage usando JSON-RPC 2.0. Le tool call originate dalla UI richiedono comunque la stessa approvazione utente di qualsiasi altra tool call — l'App può renderizzare, può proporre, non può eseguire in silenzio.

Cosa MCP Apps NON è​

Watch out
  • Non è un runtime per webapp complete. L'iframe di default non ha rete (`connect-src 'none'`), niente navigazione top-level, niente script di terze parti. Se la tua idea richiede di caricare React da una CDN e chiamare la tua API, non è questo lo strumento.
  • Non è un modo per bypassare l'approvazione delle tool call. Le azioni UI che modificano qualcosa viaggiano comunque come normali tool call JSON-RPC che l'host può loggare, throttlare e per cui può richiedere il consenso esplicito dell'utente.
  • Non è sanificazione. La sandbox limita ciò che la UI può FARE, non ciò a cui può indurre l'utente a digitare. Tratta ogni App come codice di terze parti — metti in allowlist quali server possono renderizzare UI, punto.
  • Non è persistente. Non c'è sessione; quando la tool call finisce, l'iframe scompare. Ogni stato che ti serve tra chiamate vive in un handle server-side che restituisci, esattamente come nel resto della spec stateless.

Le quattro parti in movimento​

Guided walkthrough1 of 4
  1. Il client dichiara l'estensione nel suo _meta.capabilities per-richiesta sotto il namespace reverse-DNS io.modelcontextprotocol/ui, elencando i MIME type che sa renderizzare. Un server che non vede questa capability salta il canale UI e restituisce risultati semplici — tutta l'estensione è opt-in su entrambi i lati.

Forme sul filo che scriverai davvero​

Dichiarazione di capability su una richiesta client:

{
"jsonrpc": "2.0",
"method": "tools/call",
"params": { "name": "get_weather", "arguments": { "location": "Milan" } },
"_meta": {
"capabilities": {
"extensions": {
"io.modelcontextprotocol/ui": {
"mimeTypes": ["text/html;profile=mcp-app"]
}
}
}
}
}

Lo schema del tool, lato server:

{
"name": "get_weather",
"description": "Get current weather and a 7-day forecast for a location.",
"inputSchema": {
"type": "object",
"properties": { "location": { "type": "string" } },
"required": ["location"]
},
"_meta": {
"ui": {
"resourceUri": "ui://weather-server/dashboard-template",
"visibility": ["model", "app"]
}
}
}

La risorsa UI dichiarata accanto:

{
"uri": "ui://weather-server/dashboard-template",
"mimeType": "text/html;profile=mcp-app",
"_meta": {
"ui": {
"connectDomains": [],
"permissions": []
}
}
}

E il primo messaggio che l'iframe rimanda indietro:

UI → host: JSON-RPC 2.0 su postMessage

window.parent.postMessage(
{
  jsonrpc: "2.0",
  id: 1,
  method: "ui/initialize",
  params: {
    toolName: "get_weather",
    toolArguments: { location: "Milan" },
    toolResult: /* whatever the server returned alongside the UI */
  }
},
"*"
);

Tutto ciò che segue — chiedere all'host di chiamare un altro tool, sottoscriversi a una risorsa, inviare un evento lato UI — è altro JSON-RPC 2.0 sullo stesso canale, strutturato in modo identico al protocollo sul filo che già parli lato server. È tutto il punto: gli sviluppatori UI possono usare l'@modelcontextprotocol/sdk standard invece di imparare uno shim custom.

Modello di sicurezza in una schermata​

What you'll learn
  • Sandbox: il contenuto è renderizzato in un <iframe> sandboxed. Default: niente navigazione top-level, niente form verso origin arbitrarie, niente popup, niente plugin.
  • CSP di default: connect-src 'none'. La UI non può fetch() da nessuna parte. Per consentire origin specifiche il server le dichiara nel _meta.ui.connectDomains della risorsa e l'host costruisce l'header CSP da quella lista.
  • Permission Policy: il _meta.ui.permissions della risorsa mappa nell'attributo allow dell'iframe — camera, microfono, geolocalizzazione, clipboard-write. Nulla è concesso implicitamente.
  • Ogni scrittura originata dalla UI è una normale tool call. L'host la valida, può richiedere approvazione utente e la logga nello stesso stream di audit di una chiamata originata dal modello.
  • I template sono prefetchabili e hashabili. Un host che si aggancia a un hash specifico intercetta un attacco di swap-sotto-di-te; un host che non lo fa si prende quello che il server serve quel giorno.

Quando ricorrere davvero a un App​

L'asticella è alta di proposito — ogni App è superficie client per te e superficie d'attacco per l'utente. Ricorri a uno solo quando una UI batte un turno di chat con un margine chiaro:

  • Conferme su data-grid. "Ecco 47 righe che sto per aggiornare — deseleziona quelle che non vuoi." Un rendering in chat è o enorme o disonesto; una griglia con checkbox è onesta e veloce.
  • Approvazione guidata da grafici. "Ecco il query plan / la proiezione di costo / il trace waterfall. Approva o rifiuta." I grafici costano poco in HTML e sono terribili come ASCII.
  • Picker strutturati dove la forma dell'input non è testo. Date-range con mini calendario, tree-select su un filesystem o un organigramma, map picker con bounding box.
  • Editor in-place per il piccolo passaggio in cui una vista diff + accetta/rifiuta batte il rigenerare tutta la risposta.

Se la tua idea è "fatemi embeddare un'intera dashboard", non è questo il posto — metti un link. Se la tua idea è "fatemi eseguire codice utente arbitrario nella sandbox", questo posto non fa proprio per te.

Il contratto di compatibilità con 2025-11-25​

MCP Apps cavalca il framework di estensioni introdotto nella core spec 2026-07-28, ma l'estensione in sé è Final dal 2026-01-26. Implicazione pratica: un client che parla solo la vecchia revisione 2025-11-25 più questa estensione può già renderizzare Apps; un client stateless 2026-07-28 la raccoglie come una entry nella sua mappa di capability per-richiesta. Poiché l'estensione è opt-in su entrambi i lati, un server che l'aggiunge non rompe mai un client vecchio — semplicemente salta il ramo UI. Questa è la forma che avrà ogni futura estensione MCP, quindi capire questo handshake ammortizza il debito per quelle che verranno.

Dove MCP Apps si colloca sulla mappa di AILmanac​

  • MCP 2026-07-28: la spec stateless — il protocollo core su cui viaggia Apps; in particolare la sezione sul framework di Extensions (SEP-2133).
  • MCP & connettersi ai tool — il connettore lato API. È il client che renderizzerà l'iframe; oggi il connettore astrae il trasporto, quindi Apps ci passa attraverso senza modifiche.
  • MCP in Claude Code — dove sta andando il supporto MCP di Claude Code; il rendering UI è una capability del client, non del server, quindi è la superficie che decide quali Apps verranno mai renderizzate per te.
  • Mettere in sicurezza i server MCP — il pattern di hardening per i server dall'altra parte di quell'iframe.

Controllo veloce​

Check yourself

0/4
  1. Come fa un iframe MCP Apps a comunicare con l'host?
  2. Di default, che accesso di rete ha un iframe MCP Apps?
  3. Un tool vuole renderizzare una UI. Qual è l'UNICO pezzo di metadata che trasforma un tool semplice in uno che porta una UI?
  4. Una UI dentro l'iframe vuole modificare qualcosa sul server. Cosa succede?

Vocabolario​

Terminologia MCP Apps
Premi Invio o Spazio per girare la carta. Usa le frecce sinistra e destra per spostarti tra le carte.Termine mostrato.
1 / 9

Fonti e approfondimenti​

Prossimi passi​