MCP Apps, annoncé en janvier 2026, étend le Model Context Protocol avec un mécanisme permettant aux serveurs de retourner des interfaces HTML interactives qui se rendent directement dans le client IA. Au lieu d’obtenir un résumé textuel d’un graphique, vous obtenez le graphique lui-même — interactif, défilable, explorable — intégré dans la même fenêtre de conversation.
L’extension est intentionnellement minimale. Elle ne redessine pas le protocole. Elle ajoute deux choses : une convention pour déclarer qu’un outil dispose d’une interface associée, et un type de ressource (ui://) pour servir cette interface. Tout le reste — communication JSON-RPC, invocation d’outils, streaming — reste identique.
Les deux primitives
MCP Apps étend le protocole via deux ajouts aux primitives existantes.
Outils avec métadonnées UI. Une déclaration d’outil MCP standard reçoit un champ supplémentaire _meta.ui.resourceUri dans son schéma. Ce champ pointe vers une ressource ui:// que le host peut précharger avant l’exécution de l’outil. Lorsque le client IA détecte ce champ, il sait qu’il doit récupérer et afficher la ressource UI en parallèle de la sortie textuelle de l’outil.
registerAppTool(server, "visualize-data", { title: "Visualize Data", description: "Returns an interactive chart visualization", inputSchema: { type: "object", properties: { data: { type: "array" } } }, _meta: { ui: { resourceUri: "ui://charts/interactive" } }}, async (args) => { return { content: [{ type: "text", text: JSON.stringify(args.data) }] };});Le handler de l’outil lui-même retourne les données sous forme de bloc de contenu texte standard. La couche UI gère le rendu — l’outil ne sait pas et ne se préoccupe pas de savoir s’il s’exécute dans un host supportant les Apps.
Ressources App. Une ressource ui:// contient le bundle HTML/JavaScript complet que le host affiche. Elle est servie par le serveur à la demande, enregistrée à l’aide de registerAppResource depuis le SDK @modelcontextprotocol/ext-apps. Le host la récupère une fois (ou la met en cache), puis la rend dans une iframe sandboxée.
const resourceUri = "ui://dashboard/main.html";
registerAppResource(server, resourceUri, resourceUri, { mimeType: RESOURCE_MIME_TYPE }, async () => { const html = await fs.readFile("./dist/dashboard.html", "utf-8"); return { contents: [{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html }] }; });À la date de la version de janvier 2026, seul le contenu text/html est supporté. Les iframes d’URL externe, l’accès DOM distant et les widgets natifs sont reportés à des itérations futures.
Comment le host affiche l’application
Lorsqu’un utilisateur invoque un outil qui déclare une ressource ui://, le host :
- Récupère le bundle HTML depuis le serveur via le protocole de ressource MCP
- L’affiche dans une iframe sandboxée à l’intérieur de la vue de conversation
- Transmet les données de résultat de l’outil à l’iframe via
postMessage - Établit un canal JSON-RPC via
postMessagepour la communication bidirectionnelle
L’iframe est sandboxée — pas d’accès au DOM de la fenêtre parente, pas d’accès aux cookies du host, pas de possibilité de naviguer la page parente, pas d’exécution de scripts dans le contexte parent. L’application s’exécute dans un environnement isolé avec uniquement ce que le canal MCP fournit.
Le SDK client
À l’intérieur de l’iframe, le SDK client @modelcontextprotocol/ext-apps gère la communication avec le host :
import { App } from "@modelcontextprotocol/ext-apps";
const app = new App({ name: "Dashboard App", version: "1.0.0" });
// Reçoit les données du résultat de l'outilapp.ontoolresult = (result) => { const data = JSON.parse(result.content?.find(c => c.type === "text")?.text); renderChart(data);};
// Appelle les outils serveur depuis les interactions UI (drill-down, changements de filtres)async function drillDown(segment) { const result = await app.callServerTool({ name: "get-segment-details", arguments: { segment } }); updateVisualization(result);}
// Met à jour le contexte de l'IA avec ce que l'utilisateur a fait dans l'interfaceapp.updateModelContext({ action: "filtered", dimension: "country", value: "FR" });
app.connect();Trois méthodes SDK font le travail essentiel :
app.ontoolresult— reçoit la charge utile de données initiale lors de l’exécution de l’outilapp.callServerTool()— initie des appels d’outils supplémentaires depuis l’interface (drill-down, filtre, export)app.updateModelContext()— indique à l’IA ce que l’utilisateur vient de faire dans l’interface, afin que la conversation suivante soit contextuelle
Cette dernière méthode est ce qui rend MCP Apps véritablement différent de l’intégration d’une bibliothèque de graphiques. L’IA sait que vous avez filtré sur la France et cliqué sur la décomposition « revenus par appareil ». Votre prochaine question — « pourquoi les revenus mobiles ont-ils chuté ? » — arrive avec ce contexte déjà en place.
Préchargement et latence
Le _meta.ui.resourceUri dans la déclaration d’outil sert un objectif précis : il permet aux hosts de précharger le bundle UI avant l’exécution de l’outil. Le host récupère la ressource HTML lors de l’initialisation plutôt d’attendre une invocation d’outil. Cela signifie que l’iframe s’affiche immédiatement lorsque l’outil se termine, plutôt d’ajouter un aller-retour séparé.
En pratique, chaque appel d’outil implique toujours une latence d’aller-retour. Si votre visualisation dépend d’un appel get-segment-details ultérieur, c’est un aller-retour supplémentaire. Pour l’analyse exploratoire, cela est acceptable ; pour les tableaux de bord de production attendant des réponses en moins d’une seconde, c’est une contrainte réelle.
Modèle de sécurité
MCP Apps hérite du modèle de sécurité MCP et ajoute des contraintes spécifiques aux applications.
L’iframe sandboxée constitue la frontière d’isolation principale. Les restrictions sandbox standard s’appliquent : pas de allow-same-origin, pas de allow-top-navigation, allow-scripts restreint. L’application ne peut pas sortir de son iframe pour accéder à la fenêtre parente ou à d’autres iframes dans le host.
Les templates pré-déclarés — les ressources ui:// enregistrées au démarrage du serveur — permettent une revue de sécurité avant l’exécution. Contrairement au HTML généré dynamiquement, une ressource ui:// est déclarée à l’avance, permettant aux opérateurs de host d’auditer quel code UI s’exécute. Les hosts peuvent exiger une approbation explicite de l’utilisateur pour les appels d’outils initiés par l’UI (appels effectués via app.callServerTool()), créant un point de contrôle de consentement entre les actions utilisateur et les opérations côté serveur.
L’écosystème MCP plus large présente des défis de sécurité réels que MCP Apps hérite. La recherche d’Equixly a trouvé des failles d’injection de commandes dans 43 % des serveurs MCP analysés. Les recommandations de Docker suggèrent de conteneuriser chaque serveur MCP avec des limites CPU/mémoire et des systèmes de fichiers en lecture seule. Maintenir une liste blanche de serveurs approuvés et auditer les journaux de messages devrait être une pratique standard pour tout déploiement MCP en production.
Exigences de bundling
La ressource ui:// doit être un fichier HTML autonome unique. Pas de références CDN externes, pas de modules chargés à la demande, pas de liens de feuilles de style séparés — tout doit être inlined. Vite avec vite-plugin-singlefile est l’outil de build standard pour cette contrainte :
npm create vite@latest my-app -- --template reactnpm add -D vite-plugin-singlefile# Ajouter dans vite.config.ts : plugins: [viteSingleFile()]npm run build# dist/index.html est votre bundle déployableLe SDK MCP Apps fournit des templates de démarrage pour React, Vue, Svelte, Preact, Solid et JavaScript vanilla. Tous fonctionnent — le host ne se préoccupe pas du framework qui a généré le HTML, seulement que le bundle soit autonome et se connecte via le SDK App.
Ce que cela permet
L’impact pratique pour les workflows de données : vous passez de six changements de contexte (écrire SQL → exécuter → exporter CSV → importer dans le BI → créer la viz → partager le lien) à zéro. La visualisation vit dans la conversation. Vous posez une question, voyez un graphique, posez une question de suivi, et l’IA sait ce que vous regardez.
La limitation est la contrepartie : MCP Apps est fait pour l’exploration, pas pour la production. Il n’existe pas d’URL de tableau de bord persistante, pas de vue partageable, pas de rafraîchissement planifié. Voir MCP Apps vs. BI traditionnel pour la place de ceci dans votre stack analytics globale.