Aller au contenu principal

MCP Apps : UIs interactives dans un appel d'outil

Avancé

Pendant toute sa première année, MCP a été un protocole texte : un appel d'outil retournait du JSON ou du Markdown et le client rendait ce qu'il voulait. MCP Apps — la première extension officielle, finalisée le 26 janvier 2026 et pliée dans la spec core du 2026-07-28 — ajoute un canal UI. Un serveur peut désormais livrer un morceau de HTML, le client le rend dans une iframe sandboxée, et l'iframe recommunique via JSON-RPC 2.0 sur postMessage. Chaque écriture passe toujours par le même chemin d'audit que n'importe quel autre appel d'outil. Pensez rapport interactif + confirmer-avant-exécuter, pas « faire tourner une webapp complète dans Claude ».

What you'll learn
  • Ce qu'est vraiment MCP Apps — et les quatre choses qu'il n'est délibérément PAS
  • La forme wire : négociation de capacité, ressources ui://, le lien tool _meta.ui, et le pont postMessage
  • Le modèle de sécurité — sandbox, CSP, Permission Policy, approbation host — et où il vous laisse toujours exposé
  • Quand se saisir d'un App plutôt qu'un résultat d'outil brut (patterns rares-mais-valables)
  • Comment ça se compose avec le reste de la spec sans état 2026-07-28 que vous parlez déjà

La version en un paragraphe

Le serveur déclare une ou plusieurs ressources UI à des URIs ui://<server>/<name> avec le type MIME text/html;profile=mcp-app. Un outil annonce « j'ai une UI » en mettant _meta.ui.resourceUri sur son schéma. Sur un tools/call le serveur peut retourner le texte/données habituels plus une référence à la ressource UI ; le client va chercher le HTML avec resources/read, le dépose dans une <iframe> sandboxée, et maintenant l'iframe et l'host discutent sur postMessage en utilisant JSON-RPC 2.0. Les appels d'outils initiés par l'UI nécessitent toujours la même approbation utilisateur que n'importe quel autre appel d'outil — l'App peut rendre, il peut proposer, il ne peut pas exécuter silencieusement.

Ce que MCP Apps n'EST PAS

Watch out
  • Pas un runtime webapp complet. L'iframe par défaut n'a pas de réseau (`connect-src 'none'`), pas de navigation top-level, pas de scripts tiers. Si votre idée nécessite de charger React depuis un CDN et d'appeler votre propre API, ce n'est pas ça.
  • Pas un moyen de contourner l'approbation d'appel d'outil. Les actions UI qui mutent quoi que ce soit voyagent toujours comme des appels d'outils JSON-RPC normaux que l'host peut logger, throttle, et pour lesquels il peut exiger un consentement utilisateur explicite.
  • Pas de la sanitization. Le sandbox limite ce que l'UI peut FAIRE, pas ce qu'il peut tromper un utilisateur à taper. Traitez chaque App comme du code tiers — mettez en liste blanche quels serveurs peuvent rendre de l'UI du tout.
  • Pas persistant. Il n'y a pas de session ; quand l'appel d'outil se termine, l'iframe s'en va. Tout état dont vous avez besoin à travers les appels vit dans un handle côté serveur que vous retournez, exactement comme le reste de la spec sans état.

Les quatre pièces mobiles

Guided walkthrough1 of 4
  1. Le client annonce l'extension dans son _meta.capabilities par-requête sous le namespace reverse-DNS io.modelcontextprotocol/ui, listant les types MIME qu'il peut rendre. Un serveur qui ne voit pas une telle capacité saute simplement le canal UI et retourne des résultats bruts — toute l'extension est opt-in des deux côtés.

Formes wire que vous taperez vraiment

Annonce de capacité sur une requête 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"]
}
}
}
}
}

Le schéma d'outil, côté serveur :

{
"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 ressource UI déclarée à côté :

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

Et le premier message que l'iframe renvoie :

UI → host : JSON-RPC 2.0 sur 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 */
  }
},
"*"
);

Tout après ça — demander à l'host d'appeler un autre outil, s'abonner à une ressource, envoyer un événement côté UI — c'est plus de JSON-RPC 2.0 sur le même canal, structuré identiquement au protocole wire que vous parlez déjà côté serveur. C'est tout l'intérêt : les développeurs UI peuvent utiliser le SDK standard @modelcontextprotocol/sdk au lieu d'apprendre une couche bespoke.

Modèle de sécurité en un écran

What you'll learn
  • Sandbox : le contenu se rend dans une <iframe> sandboxée. Par défaut : pas de navigation top-level, pas de formulaires vers des origines arbitraires, pas de popups, pas de plugins.
  • CSP par défaut : connect-src 'none'. L'UI ne peut fetch() nulle part. Pour autoriser des origines spécifiques, le serveur les déclare dans le _meta.ui.connectDomains de la ressource et l'host construit l'en-tête CSP à partir de cette liste.
  • Permission Policy : les _meta.ui.permissions de la ressource se cartographient dans l'attribut allow de l'iframe — caméra, microphone, géolocalisation, clipboard-write. Rien n'est accordé implicitement.
  • Chaque écriture initiée par l'UI est un appel d'outil normal. L'host valide, peut demander une approbation utilisateur, et logge dans le même stream d'audit qu'un appel initié par le modèle.
  • Les templates sont préfetchables et hashables. Un host qui pin sur un hash spécifique attrape une attaque de swap-under-you ; un host qui ne le fait pas obtient ce que le serveur sert ce jour-là.

Quand se saisir vraiment d'un App

La barre est haute exprès — chaque App est une surface client pour vous et une surface d'attaque pour l'utilisateur. Ne vous en saisissez que quand une UI bat un tour de chat par une marge claire :

  • Confirmations de grille de données. « Voici 47 lignes que je m'apprête à mettre à jour — décochez celles que vous ne voulez pas. » Un rendu chat de ça est soit énorme soit malhonnête ; une grille avec des cases à cocher est honnête et rapide.
  • Approbation pilotée par graphique. « Voici le plan de requête / la projection de coût / le waterfall de trace. Approuver ou rejeter. » Les graphiques sont pas chers en HTML et terribles en ASCII.
  • Sélecteurs structurés où la forme de l'entrée n'est pas du texte. Plage de dates avec mini calendrier, tree-select sur un filesystem ou un organigramme, sélecteur de carte avec bounding box.
  • Éditeurs sur place pour la petite étape où une vue diff + accepter/rejeter bat la régénération de la réponse entière.

Si votre idée est « laissez-moi embarquer un dashboard entier », ça n'appartient pas ici — faites un lien vers l'extérieur. Si votre idée est « laissez-moi faire tourner du code utilisateur arbitraire dans le sandbox », ça n'appartient définitivement pas ici.

Le contrat de compatibilité 2025-11-25

MCP Apps roule sur le framework d'extension introduit dans la spec core 2026-07-28, mais l'extension elle-même est Finale depuis le 26 janvier 2026. Implication pratique : un client qui ne parle que la révision plus ancienne 2025-11-25 plus cette extension peut déjà rendre des Apps ; un client sans état 2026-07-28 la ramasse comme une entrée dans sa carte de capacités par-requête. Comme l'extension est opt-in des deux côtés, un serveur qui l'ajoute ne casse jamais un ancien client — il saute juste la branche UI. C'est la forme que prendra chaque future extension MCP, donc comprendre ce handshake paie la dette pour celles à venir.

Où MCP Apps atterrit sur la carte AILmanac

  • MCP 2026-07-28 : la spec sans état — le protocole core sur lequel Apps roule ; surtout la section framework Extensions (SEP-2133).
  • MCP & se connecter aux outils — le connecteur côté API. C'est le client qui rendra l'iframe ; le connecteur d'aujourd'hui abstrait le transport, donc Apps y coule sans changement.
  • MCP dans Claude Code — où va le support MCP de Claude Code ; le rendu UI est une capacité client, pas serveur, donc c'est la surface qui décide quels Apps rendent jamais pour vous.
  • Sécuriser les serveurs MCP — le pattern de durcissement pour les serveurs à l'autre bout de cette iframe.

Vérification rapide

Check yourself

0/4
  1. Comment une iframe MCP Apps recommunique-t-elle à l'host ?
  2. Par défaut, quel accès réseau a une iframe MCP Apps ?
  3. Un outil veut rendre une UI. Quelle est LA SEULE pièce de métadonnées qui transforme un outil brut en un outil porteur d'UI ?
  4. Une UI dans l'iframe veut muter quelque chose sur le serveur. Que se passe-t-il ?

Vocabulaire

Terminologie MCP Apps
Appuyez sur Entrée ou Espace pour retourner la carte. Utilisez les flèches gauche et droite pour naviguer entre les cartes.Terme affiché.
1 / 9

Sources et lectures complémentaires

Ensuite