App Protocol
Für Entwickler. Wähle im Entwicklerhandbuch zwischen API-Anbindung, App-Veröffentlichung und Projektarbeit.
Das OpenBot App Protocol ist ein offener Weg, eine App im Web zu veröffentlichen. Jeder Bot-Host kann die App dann finden, sich mit ihr verbinden und sie nutzen. Du brauchst kein Entwicklerkonto, keine Registry und keine Prüfung. Eine HTTPS-Adresse, die du kontrollierst, reicht.
Diese Seite beschreibt den Entwurf V1.1. Auf dieser Seite bedeutet „muss“ eine Regel, „sollte“ einen starken Rat und „darf“ eine Möglichkeit. Alle Domains sind Beispiele.
Standards, die es nutzt
Section titled “Standards, die es nutzt”Das Protokoll fügt sehr wenig Eigenes hinzu. Es wählt bestehende Standards aus und setzt sie zusammen.
| Teil | Frage, die es beantwortet |
|---|---|
| AI Catalog 1.0 | Welche Dienste gibt es hier? |
| MCP Server Card v1 | Wie verbinde ich mich? |
MCP 2026-07-28 over Streamable HTTP |
Was kann die App tun? |
MCP Apps 2026-01-26 |
Wie zeigt sie eine Ansicht? |
MCP (Model Context Protocol) ist ein Standardweg, auf dem KI-Hosts Werkzeuge auf einem Server aufrufen. Das Protokoll hat keine eigenen RPC-Methoden, keine festen Werkzeugnamen und keine eigene UI-Sprache.
Eine App veröffentlichen
Section titled “Eine App veröffentlichen”AI Catalog
Section titled “AI Catalog”Damit deine App über die Domain gefunden wird, lege einen AI Catalog unter
/.well-known/ai-catalog.json ab. Liefere ihn als application/ai-catalog+json
aus.
{ "specVersion": "1.0", "entries": [ { "identifier": "urn:air:apps.example.com:mcp:calendar", "type": "application/mcp-server-card+json", "url": "https://apps.example.com/mcp/calendar/server-card" } ]}Ein Catalog darf auf Cards auf anderen Domains zeigen. So kann jemand anderes den Dienst für dich betreiben.
Server Card
Section titled “Server Card”Jede App muss ihre eigene feste Card-URL haben. Liefere die Card als
application/mcp-server-card+json aus. Sie muss mindestens einen HTTPS-Endpunkt
nennen, der MCP 2026-07-28 unterstützt.
{ "$schema": "https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json", "name": "com.example/calendar", "title": "Example Calendar", "version": "1.0.0", "description": "Look up schedules and create calendar events.", "websiteUrl": "https://apps.example.com/apps/calendar", "remotes": [ { "type": "streamable-http", "url": "https://apps.example.com/mcp/calendar", "supportedProtocolVersions": ["2026-07-28"] } ]}Die Card-URL ist die Identität der App. Der name und der identifier im
Catalog beschreiben die App nur. Zwei Apps mit demselben Namen werden nie
zusammengelegt.
Ein Remote darf nach Einstellungen fragen, zum Beispiel nach headers oder
URL-variables. Markiere Geheimnisse mit isSecret: true. Hosts halten diese
Werte für jeden Benutzer privat.
Liefere den Catalog und die Card mit ETag oder Last-Modified aus. Hosts
senden bedingte Anfragen und erwarten 304 Not Modified, wenn sich nichts
geändert hat.
Icons und Nutzungsbeispiele
Section titled “Icons und Nutzungsbeispiele”Nutze die Standard-Liste icons der Card (src, mimeType, sizes, theme).
Hosts müssen deine App auch zeigen, wenn ein Icon fehlt. Hosts führen
SVG-Dateien nie als HTML aus.
Du darfst Nutzungsbeispiele unter _meta["openbot/presentation"] hinzufügen:
{ "_meta": { "openbot/presentation": { "version": "1", "examples": [ { "id": "meeting-plan", "title": "Plan the next team meeting", "prompt": "Find a free 30-minute slot next week and draft an agenda.", "description": "A sample week with one proposed meeting.", "preview": { "src": "https://apps.example.com/assets/meeting.webp", "alt": "A sample week with one proposed meeting", "width": 1200, "height": 800, "mimeType": "image/webp" } } ] } }}| Feld | Regel |
|---|---|
version |
Muss "1" sein. |
examples |
1 bis 12 Einträge. |
id |
Eindeutig. Bis zu 100 Buchstaben, Ziffern, ., _ oder -. Beginnt mit einem Buchstaben oder einer Ziffer. |
title |
Bis zu 100 Zeichen. |
prompt |
Bis zu 2000 Zeichen. |
description |
Optional. Bis zu 500 Zeichen. |
preview |
Optional. HTTPS-src, alt bis zu 500 Zeichen, width und height von 1 bis 8192, mimeType eines von PNG, JPEG, WebP, AVIF oder SVG. |
Beispiele dürfen nur öffentliche oder erfundene Daten enthalten. Hosts zeigen sie als reinen Text. Ein Beispiel ansehen oder auswählen fügt nie eine App hinzu, meldet nie an und führt nie ein Werkzeug aus. Hosts ignorieren einen fehlerhaften Präsentationsblock und nutzen die Card weiter.
Erkennung über eine Domain oder einen Link
Section titled “Erkennung über eine Domain oder einen Link”Ein Host muss vier Arten von Eingaben annehmen: eine Domain, die URL einer App-Seite, eine Catalog-URL oder eine Card-URL.
-
Domain. Der Host liest
https://<domain>/.well-known/ai-catalog.json. -
App-Seite. Die Seite zeigt mit einem Link-Tag oder einem HTTP-Header
Linkauf ihren Catalog:<linkrel="ai-catalog"type="application/ai-catalog+json"href="/apps/calendar/catalog.json"/>Der Host liest nur diese statischen Links. Er führt nie Skripte der Seite aus. Er behält den ganzen Pfad. So wird eine Seite für eine App nicht zur ganzen Domain.
-
Catalog mit vielen Cards. Der Host bittet den Benutzer, eine auszuwählen. Er fügt nie einen ganzen Catalog hinzu.
Ein App Link ist eine normale HTTPS-URL, am besten eine App-Seite. Ein QR-Code enthält dieselbe URL. Es gibt kein besonderes QR-Format und kein URI-Schema. Links dürfen keine Tokens und keine privaten Daten enthalten. Einen Link öffnen startet nur eine Prüfung. Es gibt keinen Zugriff und führt nichts aus.
Verbinden und aufrufen
Section titled “Verbinden und aufrufen”Hinzufügen, Anmelden und Ausführen sind drei getrennte Schritte. Hinzufügen speichert die Verbindung. Anmelden gibt klaren Zugriff. Ausführen braucht die Absicht des Benutzers, eine gültige Anmeldung und die eigenen Regeln des Hosts.
Nachdem der Benutzer die App hinzugefügt hat, macht der Host Folgendes:
- Er ruft
server/discoverauf, um die aktuelle Version und die Fähigkeiten zu lesen. - Er ruft
tools/list,resources/listundprompts/listauf. - Er erledigt die Arbeit mit
tools/call,resources/readundprompts/get.
Alle Nachrichten, Ergebnisse und Fehler folgen MCP. Benenne deine Werkzeuge nach
dem, was sie tun, zum Beispiel create_event. Beschreibe die Eingabe mit
inputSchema. Gib strukturierte Ausgabe mit outputSchema und
structuredContent zurück. Gib immer auch ein klares Text-Ergebnis zurück.
Anmeldung mit Konto
Section titled “Anmeldung mit Konto”Eine App, die ein Benutzerkonto braucht, muss die MCP-Autorisierung nutzen. Das ist OAuth 2.1:
- Protected Resource Metadata. Die App antwortet mit 401 oder 403 und einer Aufforderung, die auf ihre Metadaten zeigt. Die Metadaten nennen die Autorisierungsserver und Scopes.
- Client ID Metadata Document. Die Client-ID des Hosts ist eine HTTPS-URL zu einem öffentlichen JSON-Dokument. Autorisierungsserver lesen es. Niemand muss den Host von Hand registrieren. Beide Seiten müssen das unterstützen.
- PKCE mit S256. Es schützt den Anmelde-Code. Anfragen senden auch die
genaue
resource.
Deine App darf trotzdem nach einem Login, einem Tarif oder einer geschäftlichen Erlaubnis fragen. Das ist kein Registry-Schritt.
MCP-Apps-Ansichten
Section titled “MCP-Apps-Ansichten”Eine App darf eine interaktive Ansicht zeigen. Ein Werkzeug verweist in seinen Metadaten auf eine UI-Ressource:
{ "_meta": { "ui": { "resourceUri": "ui://calendar/planner.html", "visibility": ["model", "app"] } }}Die Ressource muss eine ui://-URI und den Typ text/html;profile=mcp-app
nutzen. Der Host liest sie mit resources/read. Der Host gibt
io.modelcontextprotocol/ui in seinen Client-Fähigkeiten an.
| Zweck | Standardmethode | Richtung |
|---|---|---|
| Starten und Bereitschaft melden | ui/initialize, ui/notifications/initialized |
Ansicht an Host |
| Werkzeug-Eingabe und -Ergebnis bekommen | ui/notifications/tool-input, ui/notifications/tool-result |
Host an Ansicht |
| Arbeit erledigen oder Daten lesen | tools/call, resources/read |
Ansicht, über den Host, an App |
| Den Model-Kontext aktualisieren | ui/update-model-context |
Ansicht an Host |
| Eine Chat-Nachricht senden | ui/message |
Ansicht an Host |
| Einen Link öffnen | ui/open-link |
Ansicht an Host |
| Theme und Größe teilen | ui/notifications/host-context-changed, ui/notifications/size-changed |
In beide Richtungen |
| Aufräumen | ui/resource-teardown |
Host an Ansicht |
Regeln für Ansichten:
- Der Host wartet, bis die Ansicht bereit ist, bevor er Werkzeug-Daten sendet.
- Ein Werkzeug mit
visibility: ["app"]ist nur für die Ansicht. Sichtbarkeit ist keine Zugriffskontrolle. Dein Server muss trotzdem Berechtigungen prüfen. - Wenn eine Ansicht nicht lädt, darf der Host das Werkzeug nicht noch einmal ausführen. Das Text-Ergebnis ist trotzdem da.
App-Einträge, Index-Einträge und Verbindungen
Section titled “App-Einträge, Index-Einträge und Verbindungen”Ein Host hält drei getrennte Dinge:
| Ding | Enthält | Geteilt? |
|---|---|---|
| App-Eintrag | Die Card und woher sie kam. | Pro Konto |
| Index-Eintrag | Die öffentliche Card und den Nachweis des Herausgebers. Für die Suche. | Öffentlich |
| Benutzer-Verbindung | Die Einstellungen, die Anmeldung und den Zugriff des Benutzers. | Privat für den Benutzer |
Viele Benutzer können dieselbe App verbinden. Wenn du eine Verbindung entfernst, betrifft das nie einen anderen Benutzer oder den Index. Ein öffentlicher Index, der nicht erreichbar ist, darf eine bekannte Verbindung nicht blockieren.
Öffentliche Indizierung
Section titled “Öffentliche Indizierung”Wenn du eine Card in deinem öffentlichen AI Catalog aufführst, erlaubst du ihre öffentliche Indizierung. Der Host muss das aus deinen eigenen Dokumenten bestätigen. Eine Kopie im Catalog von jemand anderem ist nur ein Hinweis.
Der Crawler arbeitet ohne Benutzerdaten. Er nutzt das User-Agent-Token OpenBot
und folgt robots.txt (RFC 9309). Er nimmt Nachweise des Herausgebers aus
diesen Quellen an:
- dem Catalog unter
/.well-known/ai-catalog.jsonam Origin der Card; - einem Catalog, den ein
ai-catalog-HeaderLinkin der Antwort der Card nennt; - einem Catalog, auf den die Seite in
websiteUrlder Card verlinkt.
Um dich abzumelden, sende X-Robots-Tag: noindex mit der Card. noindex ist
keine Zugriffskontrolle. Wenn du die Card aus deinem Catalog entfernst, entfernt
der Host den Index-Eintrag bei seiner nächsten Prüfung. Ein Catalog, der nicht
erreichbar ist, zählt nicht als Entfernung.
Du brauchst keine Registry, um eine App zu veröffentlichen, hinzuzufügen oder aufzurufen. Ein Host darf auch andere Registries lesen. Wie OpenBot das macht, siehst du unter App Index.
Sicherheitsregeln
Section titled “Sicherheitsregeln”- Alle Verbindungen nutzen HTTPS. Hosts zeigen die echte Quelle jeder App.
- Namen, Icons, Index-Einträge und Werkzeug-Hinweise sind keine Vertrauenszeichen. Inhalte von Apps sind Daten, nie Anweisungen für den Host.
- Öffentliche Crawler begrenzen Größe, Zeit und Weiterleitungen. Sie prüfen jedes Ziel und erreichen nie private, Loopback- oder Cloud-Metadaten-Adressen. Die Standardwerte der Bibliothek sind 1 MiB, 10 Sekunden und 5 Weiterleitungen.
- Hosts begrenzen auch die Größe jeder MCP-Nachricht. Der Standardwert der Bibliothek ist 16 MiB für eine JSON-Antwort oder für ein Ereignis eines Event-Streams.
- Die Suche nach Apps für eine Eingabe endet nach 64 Dokumenten oder 60 Sekunden. Sie folgt höchstens 64 Links aus einem Catalog oder einer Seite. Eine Prüfung für die öffentliche Indexierung liest höchstens 8 Catalogs des Herausgebers und endet nach 60 Sekunden. Die Bibliothek nutzt diese Grenzen.
- Schreibvorgänge brauchen eine Berechtigungsprüfung. Schreibvorgänge, die wiederholt werden können, brauchen einen geschäftlichen Idempotenz-Schlüssel.
- Wenn das Ergebnis eines Aufrufs unbekannt ist, darf der Host ihn nicht blind wiederholen.
- Ein Wechsel von Endpunkt, Anbieter oder Scopes braucht eine neue Prüfung. Alte Tokens gehen nie auf ein neues Ziel über.
- Eine Verbindung entfernen stoppt alle Aufrufe und löscht ihre Tokens.
Die Bibliothek nutzen
Section titled “Die Bibliothek nutzen”@openbot-build/protocol setzt dieses Protokoll mit Effect 4 um. Es hat die
Version 0.1.0 und ist noch nicht auf npm. Nutze es im pnpm-Workspace dieses
Repositorys.
| Einstiegspunkt | Nutzung |
|---|---|
McpPublisher |
Eine Card, einen Catalog und einen MCP-Endpunkt ausliefern. |
McpPublisher.layerProtected, Protection |
Einen Endpunkt mit OAuth schützen. |
Discovery, CatalogDiscovery |
Apps über eine Domain oder einen Link finden. |
AppConnections, McpClient |
Verbindungen hinzufügen, aufrufen, neu prüfen und entfernen. |
OAuthAuthorization |
Die Host-Seite der Anmeldung mit Konto ausführen. |
Indexing, PublicIndexing, RobotsPolicy |
Öffentliche Apps prüfen und indizieren. |
@openbot-build/protocol/NodeHttpServer |
Node-Netzwerk-Layer mit den Regeln für das öffentliche Netz. |
@openbot-build/protocol/McpApps |
Browser-Host für MCP-Apps-Ansichten. |
McpPublisher.layer und layerProtected nehmen einfache Daten für die
Veröffentlichung an. Sie prüfen sie einmal beim Start des Layers. registerUi
prüft einfache UI-Ressourcendaten in seinem Effect. Falsche Eingaben geben einen
Fehler mit Typ zurück.
McpClient.open öffnet eine Verbindung mit Schlüssel, Endpunkt, Headern und
Client-Daten. readToken liest das aktuelle Token; check prüft jede Aktion.
Ein Host kann mit registerScope die Verbindung vor jedem Netzwerkaufruf
anmelden. Der Client braucht keinen App- oder Zugangsdaten-Speicher. Sein Scope
hält und schließt die Verbindung. Für gespeicherte Verbindungen nutze
AppConnections.open. Dieser Adapter liest den aktuellen Zustand und die
Zugangsdaten, prüft den Zugriff und meldet den Scope an. Token-Abfragen und
Senden prüfen weiter den aktuellen Zustand.
Das Öffnen liest keine Listen der Fähigkeiten. Mit
client.resolve({ kind, name }, source) suchst du ein Tool, eine Ressource,
eine Vorlage oder einen Prompt. Es liest nur diese Art von Liste und stoppt beim
ersten Treffer. Der Treffer enthält die Deklaration und die Operationen invoke
und resume. Sie nutzen dieselbe Deklaration, ohne die Liste erneut zu lesen.
Gib invoke die Anfrageparameter ohne den gebundenen Namen oder die URI; eine
Vorlage nimmt { values }. Nutze die Aktion nur im Scope ihrer Verbindung. Eine
wiederhergestellte Phase sucht und prüft die Deklaration neu. Direktes
readResource unterstützt auch UI-Ressourcen außerhalb der Liste.
CatalogDiscovery.sameConnection vergleicht die Teile eines Remotes, die eine
Verbindung ändern. Neue Beschreibungstexte oder Erweiterungsfelder erhalten die
Verbindung und die Anmeldung. Ein neues Eingabeformat oder eine Änderung der
Kennzeichnung als geheim braucht eine neue Prüfung.
OAuth-Netzfehler bleiben NetworkError. Sie bedeuten nicht, dass der Anbieter
die Freigabe abgelehnt hat. Das Schließen des Verbindungs-Scopes stoppt offene
SDK-Anfragen. Das gilt auch beim Warten auf Antwort-Header oder gespeicherte
Tokens.
Ein Host mit dauerhafter Ausführung nutzt OAuthAuthorization.prepare,
redeem, renew und revokeCredential. Diese Funktionen geben einen Ablauf
oder Zugangsdaten zurück. Sie schreiben nicht in den Speicher des Hosts.
Speichere den Ablauf vor der Anzeige der Anmelde-URL. Speichere den Aufruf vor
dem Austausch eines Codes oder dem Erneuern eines Tokens. Speichere die neuen
Zugangsdaten verschlüsselt und übernimm sie mit dem Verbindungszustand. Bei
einem verlorenen Ergebnis ist eine Entscheidung nötig. Sende den Code oder das
Refresh-Token vorher nicht erneut. Der Widerruf nutzt feste Zugangsdaten und
darf wiederholt werden. Die eigenständigen Funktionen begin, complete,
refresh und revoke ändern auch den gegebenen Speicher. Sie stellen keine
Arbeit nach einem Prozessneustart wieder her.
Eine kleine App veröffentlichen:
import { Document, McpPublisher } from "@openbot-build/protocol"import { Effect, Schema } from "effect"import { McpServer, Tool, Toolkit } from "effect/ai"
const tools = Toolkit.make( Tool.make("hello", { parameters: Schema.Struct({ name: Schema.String }), success: Schema.Struct({ greeting: Schema.String }), }),)const handlers = tools.toLayer({ hello: ({ name }) => Effect.succeed({ greeting: `Hello, ${name}` }),})const origin = "https://apps.example.com"
export const publisher = McpPublisher.layer({ publication: { card: { $schema: Document.cardSchemaUrl, name: "com.example/hello", version: "1.0.0", description: "Say hello.", remotes: [ { type: "streamable-http", url: origin + "/mcp", supportedProtocolVersions: [Document.protocolVersion], }, ], }, cardUrl: origin + "/mcp/server-card", catalogUrl: origin + "/.well-known/ai-catalog.json", mcpUrl: origin + "/mcp", allowedOrigins: ["https://host.example.com"], }, register: McpServer.registerToolkit(tools).pipe(Effect.provide(handlers)),})Liefere publisher mit HttpRouter.serve von Effect aus. Den Layer zu bauen
macht kein I/O. Die publication nimmt auch etag (ein Entity-Tag in
Anführungszeichen), lastModified (ein HTTP-Datum wie
Fri, 09 Oct 2026 00:00:00 GMT), cacheControl und noindex. Nutze
McpPublisher.registerUi, um eine ui://-Ansichtsressource hinzuzufügen.
Eine App finden:
import { CatalogDiscovery, Discovery } from "@openbot-build/protocol"import * as NodeHttpServer from "@openbot-build/protocol/NodeHttpServer"import { Effect, Layer } from "effect"
const discovery = CatalogDiscovery.layer().pipe( Layer.provide(CatalogDiscovery.layerRules), Layer.provide(NodeHttpServer.layerPublicNetwork()),)const result = await Effect.runPromise( Discovery.resolve("apps.example.com").pipe(Effect.provide(discovery)),)Das Ergebnis ist Single oder Multiple. Um deine eigene App mit einem echten
Host zu testen, nutze den Protokolltest-Dienst und die
Entwicklerwerkzeuge.