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