MCP Apps: Interaktive UIs innerhalb eines Tool-Calls
Sein gesamtes erstes Jahr lang war MCP ein Textprotokoll: ein Tool-Call gab JSON oder Markdown zurück, und der Client renderte, was ihm gefiel. MCP Apps — die erste offizielle Extension, finalisiert am 26. Januar 2026 und in die 2026-07-28 Core-Spec eingefaltet — fügt einen UI-Kanal hinzu. Ein Server kann jetzt ein Stück HTML ausliefern, der Client rendert es in einem sandboxed iframe, und der iframe spricht über JSON-RPC 2.0 auf postMessage zurück. Jeder Schreibvorgang läuft weiterhin über denselben Audit-Pfad wie jeder andere Tool-Call. Denke an interaktiver Bericht + Bestätigen-vor-Ausführen, nicht "eine ganze Webapp innerhalb von Claude laufen lassen".
- Was MCP Apps tatsächlich ist — und die vier Dinge, die es bewusst NICHT ist
- Die Wire-Shape: Capability-Verhandlung, ui://-Resources, der Tool-_meta.ui-Link und die postMessage-Bridge
- Das Sicherheitsmodell — Sandbox, CSP, Permission Policy, Host-Approval — und wo es dich trotzdem noch exponiert lässt
- Wann man nach einer App statt einem einfachen Tool-Ergebnis greift (seltene, aber lohnenswerte Muster)
- Wie sich das mit dem Rest der 2026-07-28-Stateless-Spec komponiert, die du bereits sprichst
Die Ein-Absatz-Version
Der Server deklariert eine oder mehrere UI-Resources an ui://<server>/<name>-URIs mit MIME-Typ text/html;profile=mcp-app. Ein Tool bewirbt "ich habe eine UI", indem es _meta.ui.resourceUri auf sein Schema legt. Bei einem tools/call kann der Server die üblichen Text-/Daten-Ergebnisse plus eine Referenz auf die UI-Resource zurückgeben; der Client holt das HTML mit resources/read, wirft es in einen sandboxed <iframe>, und jetzt chatten der iframe und der Host über postMessage mit JSON-RPC 2.0. UI-initiierte Tool-Calls erfordern weiterhin dieselbe Nutzerzustimmung wie jeder andere Tool-Call — die App kann rendern, sie kann vorschlagen, sie kann nicht still ausführen.
Was MCP Apps NICHT ist
- Keine vollwertige Webapp-Runtime. Der iframe hat standardmäßig kein Netzwerk (connect-src 'none'), keine Top-Level-Navigation und keine Drittanbieter-Skripte. Wenn deine Idee React von einem CDN laden und deine eigene API aufrufen muss, ist das nicht das Richtige.
- Kein Weg, Tool-Call-Approval zu umgehen. UI-Aktionen, die etwas mutieren, reisen weiterhin als normale JSON-RPC-Tool-Calls, die der Host loggen, drosseln und für die er explizite Nutzerzustimmung verlangen kann.
- Keine Sanitization. Die Sandbox begrenzt, was die UI TUN kann, nicht, wozu sie einen Nutzer verleiten kann. Behandle jede App wie Drittanbieter-Code — führe eine Allow-Liste, welche Server überhaupt UIs rendern dürfen.
- Nicht persistent. Es gibt keine Session; wenn der Tool-Call endet, verschwindet der iframe. Jeder Zustand, den du über Calls hinweg brauchst, lebt in einem serverseitigen Handle, das du zurückgibst — genau wie im Rest der Stateless-Spec.
Die vier beweglichen Teile
- Der Client bewirbt die Extension in seinem Per-Request _meta.capabilities unter dem Reverse-DNS-Namespace io.modelcontextprotocol/ui und listet die MIME-Typen auf, die er rendern kann. Ein Server, der keine solche Capability sieht, überspringt einfach den UI-Kanal und gibt einfache Ergebnisse zurück — die gesamte Extension ist auf beiden Seiten opt-in.
- Der Server exponiert eine oder mehrere Resources an ui://<server>/<name>-URIs mit MIME text/html;profile=mcp-app. Resources sind vordeklariert, sodass der Host sie prefetchen, hashen und reviewen kann, bevor jemals ein Tool läuft. Inhalt wird über den gewöhnlichen resources/read-Call ausgeliefert — keine neuen Endpoints.
- Jedes Tool, das eine UI rendern will, hängt _meta.ui = { resourceUri: 'ui://…', visibility: ['model' | 'app'] } an sein Schema. visibility steuert, ob das Modell die UI-Referenz 'sehen' kann (nützlich für Modelle, die über UI-Affordances räsonnieren) oder ob sie nur app-seitig sichtbar ist.
- Innerhalb des sandboxed iframes spricht UI-Code JSON-RPC 2.0 zu window.parent über postMessage. Die erste Message ist typischerweise ui/initialize mit den Tool-Argumenten und beliebigen Handles, die der Server heruntergegeben hat. Von dort aus kann die UI zusätzliche Resources anfordern, Tools aufrufen (mit Nutzerzustimmung) und Notifications zurücksenden.
Wire-Shapes, die du tatsächlich tippen wirst
Capability-Werbung auf einer Client-Request:
{
"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"]
}
}
}
}
}
Das Tool-Schema auf der Server-Seite:
{
"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"]
}
}
}
Die UI-Resource, direkt daneben deklariert:
{
"uri": "ui://weather-server/dashboard-template",
"mimeType": "text/html;profile=mcp-app",
"_meta": {
"ui": {
"connectDomains": [],
"permissions": []
}
}
}
Und die erste Message, die der iframe zurücksendet:
UI → host: JSON-RPC 2.0 over 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 */
}
},
"*"
);Alles danach — den Host bitten, ein anderes Tool aufzurufen, eine Resource abonnieren, ein UI-seitiges Event senden — ist mehr JSON-RPC 2.0 auf demselben Kanal, strukturiert identisch zum Wire-Protocol, das du auf der Server-Seite bereits sprichst. Das ist der ganze Punkt: UI-Entwickler können das Standard-@modelcontextprotocol/sdk verwenden, statt einen maßgeschneiderten Shim zu lernen.
Sicherheitsmodell auf einem Screen
- Sandbox: Inhalt rendert in einem sandboxed <iframe>. Standard: keine Top-Level-Navigation, keine Formulare an beliebige Origins, keine Popups, keine Plugins.
- CSP standardmäßig: connect-src 'none'. Die UI kann nirgendwo fetch()en. Um bestimmte Origins zu erlauben, deklariert der Server sie im _meta.ui.connectDomains der Resource, und der Host baut den CSP-Header aus dieser Liste.
- Permission Policy: die _meta.ui.permissions der Resource werden in das allow-Attribut des iframes gemappt — Kamera, Mikrofon, Geolocation, clipboard-write. Nichts wird implizit gewährt.
- Jeder UI-initiierte Schreibvorgang ist ein normaler Tool-Call. Der Host validiert ihn, kann Nutzerzustimmung verlangen und loggt ihn im selben Audit-Stream wie einen modell-initiierten Call.
- Templates sind prefetchbar und hashbar. Ein Host, der auf einen bestimmten Hash pinnt, fängt einen Swap-under-you-Angriff ab; ein Host, der es nicht tut, bekommt, was der Server an diesem Tag ausliefert.
Wann man tatsächlich zu einer App greift
Die Latte liegt bewusst hoch — jede App ist Client-Surface für dich und Angriffsfläche für den Nutzer. Greife nur dann zu einer, wenn eine UI eine Chat-Runde mit klarem Abstand schlägt:
- Data-Grid-Bestätigungen. "Hier sind 47 Zeilen, die ich gerade aktualisieren will — entcheck die, die du nicht willst." Ein Chat-Rendering davon ist entweder riesig oder unehrlich; ein Grid mit Checkboxen ist ehrlich und schnell.
- Chart-getriebene Genehmigung. "Hier ist der Query-Plan / die Kostenprojektion / das Trace-Waterfall. Genehmigen oder ablehnen." Charts sind in HTML billig und als ASCII schrecklich.
- Strukturierte Picker, bei denen die Form der Eingabe nicht Text ist. Datumsbereich mit einem Mini-Kalender, Tree-Select über einem Dateisystem oder einem Org-Chart, Map-Picker mit einer Bounding Box.
- In-place-Editoren für den kleinen Schritt, in dem eine Diff-Ansicht + Accept/Reject das Regenerieren der ganzen Antwort schlägt.
Wenn deine Idee "lass mich ein ganzes Dashboard einbetten" ist, gehört sie nicht hierher — verlinke nach außen. Wenn deine Idee "lass mich beliebigen Nutzercode in der Sandbox ausführen" ist, gehört sie definitiv nicht hierher.
Der 2025-11-25-Kompatibilitätsvertrag
MCP Apps reitet auf dem Extension-Framework, das in der 2026-07-28 Core-Spec eingeführt wurde, aber die Extension selbst ist seit 2026-01-26 Final. Praktische Implikation: ein Client, der nur die ältere 2025-11-25-Revision plus diese Extension spricht, kann bereits Apps rendern; ein stateless 2026-07-28-Client nimmt sie als einen Eintrag in seiner Per-Request-Capability-Map auf. Weil die Extension auf beiden Seiten opt-in ist, bricht ein Server, der sie hinzufügt, nie einen alten Client — er überspringt einfach den UI-Zweig. Das ist die Form, die jede zukünftige MCP-Extension annehmen wird, sodass das Verständnis dieses Handshakes Schulden für die kommenden abbaut.
Wo MCP Apps auf der AILmanac-Karte landet
- MCP 2026-07-28: Die Stateless-Spec — das Core-Protokoll, auf dem Apps reitet; insbesondere der Extensions-Framework-Abschnitt (SEP-2133).
- MCP & Verbindung zu Tools — der API-seitige Connector. Er ist der Client, der den iframe rendert; der heutige Connector abstrahiert den Transport, sodass Apps unverändert durchfließt.
- MCP in Claude Code — wohin sich Claude Codes MCP-Support entwickelt; UI-Rendering ist eine Client-Capability, keine Server-Capability, also ist dies die Oberfläche, die entscheidet, welche Apps überhaupt für dich rendern.
- MCP-Server absichern — das Härtungsmuster für die Server auf der anderen Seite dieses iframes.
Schneller Check
Check yourself
0/4Vokabular
Quellen & Weiterführendes
- SEP-1865: MCP Apps — Interactive User Interfaces for MCP — die Spezifikation selbst.
ext-apps-Repository — der eingefrorene 2026-01-26-Spec-Text: Capability-Shape, CSP-Defaults, postMessage-Semantik.- MCP Apps: Extending servers with interactive user interfaces — der Announcement-Post, der durch das Design-Rationale führt.
- The 2026-07-28 MCP Specification Release Candidate — das Core-Release, mit dem Apps ausgeliefert wird; der Extensions-Framework-Abschnitt erklärt das Verhandlungsmuster, dem alle Extensions jetzt folgen.
- Anthropic and OpenAI Join Forces to Standardize Interactive AI Interfaces with MCP Apps — Kontext zur Multi-Vendor-Zusammenarbeit hinter SEP-1865.
Weiter
- MCP 2026-07-28: Die Stateless-Spec — das Core-Protokoll, das jeder MCP-Apps-Host und -Server ebenfalls spricht.
- MCP-Server absichern — die serverseitige Härtung, die du brauchst, bevor du UI-initiierte Tool-Calls akzeptierst.
- MCP & Verbindung zu Tools — wie der API-seitige Connector den Server tatsächlich erreicht, dessen App du renderst.