Der MCP-Dienst für KI-Assistenten
Ein Assistent soll mit Ihrem Bestand arbeiten: Adresse, Anmeldung, Werkzeugliste, Bestätigungspflicht, Grenzen und Fehler.
15 Minuten LesezeitAPIStand: 25.10.2026
Auf dieser Seite
Bevor Sie loslegen
- Für wen
- Wer einen Assistenten anbindet, der über das Model Context Protocol spricht, oder wissen will, was ein solcher Assistent überhaupt tun kann.
- Voraussetzung
- die Adresse des Dienstes Ihrer Umgebung und ein Ausweis: ein Zugriffstoken
oat_…aus dem delegierten Zugang oder ein API-Schlüsselak_…
Der MCP-Dienst gibt einem KI-Assistenten eine kuratierte Auswahl an Werkzeugen auf Ihren Bestand: Szenen und Medien ansehen, Displays und Standorte prüfen, Entwürfe vorschlagen und, nach ausdrücklicher Bestätigung, ein Programm setzen. Diese Seite nennt die Adresse, die Anmeldung, die Werkzeuge, die Grenzen und die Fehler.
Was der Dienst ist
Er ist ein dünner Adapter vor derselben API, die auch das Portal und die Kommandozeile benutzen. Er hält keine Daten, kennt kein Geheimnis und prüft keinen Ausweis: Ihr Bearer reist unverändert an die Plattform weiter, und die Rechteauflösung bleibt die der API. Es gibt dort keine Route, kein Feld und keine Regel, die es nur dort gäbe.
Was er beiträgt, sind vier Dinge: eine Auswahl statt einer Abbildung der ganzen API, eine Bestätigung vor jeder Wirkung, Grenzen für Ergebnisgröße und Laufzeit — und die Zusage, dass ein Werkzeug nur eines löscht: ein Display, das im Browser läuft.
Der Dienst spricht Englisch. Werkzeugnamen, Eingabefelder, Antwortfelder, Statuswerte, Fehlerarten, Beschreibungen und Hinweise sind englisch, damit ein Assistent in jeder Sprache dieselben Namen liest. Diese Seite bleibt deutsch und zitiert die Antworten des Dienstes wörtlich.
Verbinden
| Was | Wert |
|---|---|
| Transport | Streamable HTTP nach der MCP-Spezifikation |
| Adresse | https://mcp.<ihre-domain>/mcp |
| Anmeldung | Authorization: Bearer oat_… oder Authorization: Bearer ak_… |
| Beschreibung der Ressource | GET https://mcp.<ihre-domain>/.well-known/oauth-protected-resource (RFC 9728) |
| Lebenszeichen | GET /health/live, GET /health/ready |
Ein Assistent, der OAuth beherrscht, braucht nur die Adresse: Ohne Ausweis antwortet /mcp mit 401 und der Kopfzeile WWW-Authenticate, die auf die Beschreibung der Ressource zeigt. Von dort findet der Client den Autorisierungs-Server und beginnt den Rundlauf aus Delegierter Zugang mit OAuth 2.1. Ein Werkzeug ohne diesen Weg trägt stattdessen einen API-Schlüssel ein.
Schon beim Handschlag prüft der Dienst den Ausweis einmal gegen GET /v1/me. Wird er abgelehnt, antwortet er 401 mit derselben Aufforderung — der Client beginnt dann die Anmeldung, statt in ein Gespräch zu geraten, in dem jedes Werkzeug scheitert.
Der Ausweis gehört an jede Anfrage, auch innerhalb einer Sitzung. Das gilt ebenso für GET /mcp, das den Ereignisstrom einer Sitzung öffnet, und für DELETE /mcp, das sie beendet. Beide prüft der Dienst bei jeder Anfrage gegen GET /v1/me, bevor er die Sitzung überhaupt ansieht. Ohne Ausweis antworten sie mit 401 und error="invalid_request", mit einem abgelehnten Ausweis mit 401 und error="invalid_token". Die Sitzungskennung allein öffnet also nichts.
Die Sitzung, die der Handschlag eröffnet, lebt 24 Stunden gleitend: Jeder Aufruf setzt die Frist neu. Wer sie überschreitet, bekommt auf die nächste Nachricht ein 404 mit {"error": "no_session"} — das Zeichen der MCP-Spezifikation, einen neuen initialize zu schicken. Fehlt der Kopf Mcp-Session-Id ganz oder trägt er Zeichen, die eine Kennung nie trägt, antwortet der Dienst mit 400 und {"error": "no_session_id"}: Das ist kein Verfall, sondern eine falsch gebaute Anfrage.
Die Rechte, um die der Client bittet
Die Beschreibung der Ressource nennt unter scopes_supported die Rechte, die die Werkzeuge dieses Dienstes brauchen — genau sie, keine weiteren. Dieselbe Liste steht als scope in der Kopfzeile WWW-Authenticate, damit auch ein Client sie hat, der die Beschreibung nicht abruft. Ein Client übernimmt sie unverändert in seine Anmeldung. Ohne scope weist der Autorisierungs-Server die Anfrage ab.
Auf der Einwilligung stehen diese Rechte danach gebündelt zu Bereichen — „Szenen“, „Playlisten und Zeitpläne“, „Medien“, „Displays und Standorte“, „Berichte“ —, jeder mit einem Satz und einem Schalter. Alle angefragten Bereiche sind zu Beginn eingeschaltet, und ein Mensch darf einzelne abschalten: Die Einwilligung darf eine Teilmenge sein. Ihr Zugriffstoken trägt dann nur die erteilten Rechte, und die Token-Antwort nennt sie unter scope.
Fehlt einem Werkzeug ein Recht, antwortet es permission_denied und nennt den Bereich, den die Einwilligung tragen müsste — mit den Worten des Einwilligungs-Bildschirms, in der englischen Fassung („Displays & locations“; auf einem deutschen Bildschirm steht dafür „Displays und Standorte“):
{
"error": "permission_denied",
"hint": "The credential lacks the permission for this action. This tool works in the area \"Displays & locations\". The consent of this connection must include it.",
"correlationId": "0198f2c1-…"
}
Damit kann ein Assistent seinem Menschen sagen, welchen Schalter er beim nächsten Rundlauf einschalten muss. Nachträglich erweitern lässt sich eine Einwilligung nicht — der Weg führt über Widerruf und erneutes Erlauben.
Neue Rechte, neue Einwilligung
Kommt ein Werkzeug dazu, das ein Recht braucht, das bisher keines brauchte, wächst diese Liste — und ein bestehendes Zugriffstoken wächst nicht mit. Es trägt die Rechte, die zum Zeitpunkt seiner Einwilligung erteilt wurden. Die neuen sind darin nicht enthalten, und die Werkzeuge dahinter antworten mit permission_denied.
whoami sagt das, bevor es jemand ausprobiert: Es vergleicht die Rechte Ihres Ausweises mit denen, die die Werkzeuge dieses Dienstes brauchen, und nennt die fehlenden unter missingPermissions.
{
"account": { "…": "…" },
"permissionCount": 14,
"missingPermissions": ["location.create"],
"hint": "These tools need a new consent: disconnect the connection in the client and connect again, new permissions only appear then. If a permission is still missing afterwards, the account itself does not carry it."
}
Der zweite Satz ist kein Beiwerk: Ein delegierter Zugang bekommt nie mehr, als das Konto dahinter hat. Steht ein Recht nach dem neuen Erlauben immer noch in der Liste, fehlt es der Rolle des Kontos — dann hilft nur, wer Rollen vergibt. Mit einem API-Schlüssel stellen Sie stattdessen einen neuen mit dem passenden Recht aus.
Alle Rechte, die dieser Dienst nennt, sieht der Rechte-Katalog für delegierten Zugang vor — auch die an Displays und an der Auswertung. Jedes Werkzeug ist damit über beide Wege erreichbar: über einen delegierten Zugang und über einen API-Schlüssel, der dieselben Rechte trägt. Was ein Werkzeug verändert, bleibt trotzdem an Ihre ausdrückliche Bestätigung gebunden (siehe unten).
Die Werkzeuge
Die Werkzeuge stehen in drei Risikoklassen, und ein Client zeigt sie in dieser Reihenfolge.
| Klasse | Werkzeuge | Was sie tun |
|---|---|---|
| Lesen | whoami, list_scenes, get_scene, get_scene_document, list_scene_templates, list_widgets, get_widget_schema, list_graphics, list_assets, list_players, get_player, get_player_screenshot, list_display_groups, list_locations, get_location, list_playlists, get_playlist, list_schedules, get_hygiene, validate_scene |
Bestand ansehen: wer der Ausweis ist, welche Szenen, Bausteine, Motive, Medien, Displays, Leinwände, Standorte, Playlisten und Programme es gibt, was in einer Szene steht, was auf einem Display zu sehen ist (samt seiner jüngsten Aufnahme als Bild), was liegen geblieben ist — und ob ein Szenen-Dokument die Regeln einhält |
| Vorschlagen | create_scene_from_template, create_scene, update_scene, render_scene, create_playlist, add_to_playlist |
Entwürfe anlegen und ändern, die auf keinem Display erscheinen, solange sie niemand veröffentlicht — und eine veröffentlichte Fassung als Bild ansehen |
| Schreiben | publish_scene, set_player_program, set_display_group_program, set_player_override, clear_player_override, send_player_command, assign_player_location, create_location, set_location_opening_hours, create_web_display, issue_web_display_link, delete_web_display |
veröffentlichen, ein Programm setzen, übersteuern und wieder freigeben, einen Fernwartungs-Befehl absetzen, ein Display einem Standort zuordnen, einen Standort anlegen, seine Öffnungszeiten setzen — und ein Display anlegen, das im Browser läuft, ihm eine neue Anschluss-Adresse geben oder es wieder wegräumen |
Löschen gibt es genau einmal — und nur für Displays, die im Browser laufen. delete_web_display nimmt ein Web-Display samt seinem Browser-Zugang aus dem Bestand und gibt den gebuchten Platz wieder frei. Das Gerät im Browser verlässt dabei die Organisation. Ein Display mit Gerät wird abgewiesen, mit eigenem Fehlercode und ohne Umweg. Sonst entfernt kein Werkzeug eine Szene, ein Medium, ein Display oder ein Konto.
Die Ausnahme hat einen engen Grund. Alles andere, was hier erreichbar ist, hat ein Gegenüber in der Welt: ein Gerät in einem Raum, eine Szene, an der jemand gearbeitet hat, eine Playliste, die eine Filiale seit Monaten fährt. Ein Web-Display hat keines — es ist eine Adresse in einem Browser, in einer Sekunde angelegt und in einer Sekunde weg. Ein Assistent, der eines anlegen kann, aber keines wegräumen, hinterlässt bei jedem Probelauf eine Karteileiche mit belegtem Platz.
Ebenso fehlen Konten- und Rollenverwaltung, die Ausgabe von Schlüsseln, die Abrechnung und alles, was der Betreiber der Plattform tut. Diese Bereiche sind im Dienst nicht verdrahtet, nicht nur ausgeblendet.
Ein Display, auf dem etwas zu sehen ist
create_web_display ist der Weg, auf dem ein Assistent überhaupt etwas zeigen kann — ohne ihn setzt jedes andere Werkzeug einen Bildschirm voraus, den irgendwann jemand angeschlossen hat. Er antwortet mit der Kennung des Displays und einer Anschluss-Adresse. Die Adresse gehört in die Antwort an den Menschen: Sie auf dem Zielgerät im Browser zu öffnen ist der eine Schritt dieses Vorgangs, den kein Assistent selbst tun kann.
Bis das geschieht, steht das Display in der Flotte als „nicht verbunden“ ("status": "not_connected" in der Antwort) — mit Namen, Standort und, wenn gewünscht, schon mit seinem Programm. Es belegt von der Anlage an einen gebuchten Platz. Sind alle belegt, antwortet der Dienst mit no_seat_available statt mit einem technischen Fehler. Sobald ein Browser die Adresse geöffnet hat, zeigt das Display jede weitere Änderung von selbst — es muss dafür nicht neu geladen werden.
issue_web_display_link gibt demselben Display eine neue Adresse, wenn der Browser dahinter seine Ablage verloren hat. Wer sie öffnet, übernimmt das Display, und der Browser, der es bis dahin zeigte, verliert es. Name, Standort, Programm und der gebuchte Platz bleiben unberührt.
Die Aufräum-Fragen sind keine eigenen Werkzeuge, sondern Filter auf den Listen, die es ohnehin gibt: unbenutzte Medien, veröffentlichte Szenen ohne Zuweisung, Displays ohne Programm oder lange ohne Lebenszeichen. get_hygiene zählt sie und nennt zu jeder Zahl das Werkzeug, mit dem sich die Zeilen dahinter ansehen lassen.
Zwei Werkzeuge der Klasse Vorschlagen führen trotzdem confirm: create_scene und update_scene, weil dort Inhalt entsteht oder ersetzt wird, den vorher niemand gesehen hat. render_scene führt keine Bestätigung — es erzeugt ein Vorschaubild und sonst nichts.
Eine Szene bauen
Der Weg von der Idee zum Bild ist eine Kette aus sechs Schritten. Jede Werkzeug-Beschreibung nennt den nächsten, damit ein Assistent die Reihenfolge nicht raten muss.
1. Bausteine ansehen. list_widgets nennt die Widgets, aus denen eine Szene besteht: die Kern-Widgets text, image, video, shape, graphic und document, die immer da sind, und die Widgets der Apps, die Ihre Organisation installiert hat. Je Eintrag stehen Kennung, App, Name, ein Satz, die Pflichtfelder und ob die App installiert ist. Ein Widget einer nicht installierten App lässt sich in ein Dokument schreiben, läuft aber auf keinem Display. Hat Ihre Organisation keine App gebucht, stehen alle App-Widgets auf installed: false — die Antwort sagt das dann auch im Klartext, statt es Sie raten zu lassen.
2. Regeln holen. get_widget_schema liefert zu einem Widget das vollständige JSON-Schema-Dokument seiner Konfiguration (Draft 2020-12): jedes Feld mit title und description, die Pflichtfelder unter required und unter examples mindestens eine vollständige Beispiel-Konfiguration. Nehmen Sie ein Beispiel und tauschen Sie die Werte aus — das ist die kürzeste Anleitung, die es gibt. Dazu kommen die Start-Konfiguration und die Vorschau-Varianten des Widgets.
Ein Feld nennt das Schema nicht mit Werten: die graphicId des Kern-Widgets graphic. Sie ist eine feste Kennung aus der Motiv-Bibliothek, und die listet list_graphics — mit Kategorie und Schlagworten, suchbar nach einer Absicht („pfeil“, „kaffee“). Geraten wird sie nicht. Das Schema prüft sie, kennt aber keine Liste, die es Ihnen zeigen könnte.
3. Dokument bauen. Ein Szenen-Dokument hat vier Felder:
{
"canvas": { "width": 1920, "height": 1080, "backgroundColor": "#ffffff" },
"layoutTemplateId": null,
"widgets": [
{
"id": "wgi_01K7Z9M4T0W14MCPTEXT000001",
"appId": "core",
"widgetType": "text",
"appVersionPin": "0.0.0",
"frame": { "x": 0, "y": 0, "w": 1920, "h": 1080 },
"zIndex": 0,
"config": { "content": "Welcome" }
}
],
"transitions": {
"in": { "kind": "fade", "durationMs": 600 },
"out": { "kind": "fade", "durationMs": 600 }
}
}
layoutTemplateId: null ist die freie Szene: Jedes Widget sitzt allein über seinen frame in Pixeln der Leinwand. Mit einer Vorlage kommen benannte Bereiche dazu, an die sich ein Widget über slotName binden lässt — je Bereich höchstens eines. config ist der Teil, der aus Schritt 2 kommt.
4. Trockenlauf. validate_scene prüft das Dokument, ohne etwas zu speichern: die Form, die Layout-Bindungen, die referenzierten Medien und jede Konfiguration gegen das Schema ihres Widgets. Die Antwort nennt je Befund die Widget-Instanz und den Feldpfad:
{
"valid": false,
"findings": [
{
"severity": "BLOCKING",
"type": "ConfigSchemaIncompatible",
"message": "widget 'wgi_…' (text): config/content must NOT have fewer than 1 characters",
"widgetId": "wgi_…",
"field": "/content"
}
]
}
BLOCKING verhindert später das Veröffentlichen, WARNING nicht — ein Widget, das über den Rand der Leinwand steht, wird gemeldet und hält nichts auf.
Was der Trockenlauf meldet, kann auch später als offene Beanstandung an der Szene stehen — etwa, wenn ein Medium gelöscht oder eine App deinstalliert wurde. get_scene gibt sie unter openIssues mit payload und einem hint im Klartext heraus, der die Abhilfe beim Namen nennt: „Widget wgi_… is bound to the slot "main", which the scene does not have. Either it has no layout template or the template does not know this slot. Remove slotName from the widget or set layoutTemplateId to a template with this slot.“
Die Löschung deckt genau ein Werkzeug ab. Gelöscht wird nur ein Display, das im Browser läuft (delete_web_display, mit confirm). Displays mit Gerät, Szenen und Medien löscht kein Werkzeug.
5. Anlegen oder ändern. create_scene legt aus dem Dokument einen Entwurf an, update_scene ersetzt das Dokument einer bestehenden Szene. Beide laufen den Trockenlauf noch einmal selbst und schreiben bei einem blockierenden Befund gar nichts — Sie bekommen die Befunde zurück, aber keinen halben Entwurf im Bestand. Beide brauchen confirm: true. Ohne beschreiben sie nur, was sie täten.
Eine BESTEHENDE Szene zu ändern beginnt deshalb einen Schritt früher: get_scene_document liefert das Dokument des Entwurfs und die version dazu. Ändern Sie dieses Dokument, prüfen Sie es mit validate_scene und schicken Sie es ganz an update_scene — zusammen mit expectedVersion aus derselben Antwort, damit eine fremde Änderung dazwischen nicht überschrieben wird. Ein einzelnes Widget zu ändern gibt es nicht. Wer das Dokument neu erfindet statt es zu lesen, verliert jede Konfiguration, die er nicht kannte.
6. Ansehen und nachbessern. render_scene rendert eine veröffentlichte Fassung und liefert das Bild als PNG im Ergebnis — als Inhalt, nicht als Adresse. Ein reiner Entwurf hat keine Fassung. Veröffentlichen Sie ihn zuerst mit publish_scene — auf einem Display erscheint er dadurch nicht, solange ihm keines zugewiesen ist. Das Bild ist der einzige Weg, Fragen zu beantworten, die in keinem Schema stehen: ob der Text in seiner Box bleibt, ob etwas verdeckt ist, ob die Farben tragen. Passt etwas nicht, geht es mit update_scene und einem erneuten publish_scene weiter.
Braucht Ihre Umgebung kein Vorschaubild oder ist kein Schnappschuss-Dienst eingerichtet, sagt render_scene das im Klartext, statt einen Fehler zu werfen.
Was auf einem Display wirklich steht, beantwortet dagegen kein Render, sondern nur das Gerät selbst: get_player_screenshot liefert dessen jüngste Aufnahme — ebenfalls als Bild im Ergebnis, nicht als Adresse — zusammen mit capturedAt. Die Aufnahme kann einige Minuten alt sein, denn die Geräte nehmen im Takt auf und nach jedem Inhaltswechsel. Liegt noch keine vor, ist das kein Fehler: Die Antwort trägt status: "no_screenshot" und den Weg zu einer — send_player_command mit command: "screenshot" und confirm: true fordert eine an, die das Gerät bei seiner nächsten Meldung ablegt. Damit prüfen Sie, was eine Zuweisung am Bildschirm tatsächlich bewirkt hat, statt es aus dem Programm abzuleiten.
Die Bestätigung
Jedes schreibende Werkzeug führt das Feld confirm. Ohne confirm: true geschieht nichts: Das Werkzeug liest den Ist-Zustand und antwortet mit einer Vorschau — welche Ziele es träfe, wie sie heißen, ob sie online sind, was ersetzt würde.
{
"status": "preview",
"executed": false,
"hint": "Nothing was changed. Show the effect to the human, then call the same tool again with the same values and confirm: true to execute it.",
"effect": { "…": "…" }
}
Erst der zweite Aufruf mit denselben Werten und confirm: true führt aus und antwortet mit "status": "executed", "executed": true und dem Ergebnis unter result. Die Vorschau ist ausdrücklich keine Reservierung: Sie hält nichts fest. Ändert sich zwischen beiden Aufrufen etwas, greift die Nebenläufigkeits-Prüfung der API, nicht der Dienst.
Zusätzlich tragen alle Werkzeuge die Angaben readOnlyHint und destructiveHint, damit ein Client seine eigene Rückfrage stellen kann. Jeder schreibende Aufruf reist mit einem Idempotency-Key, der aus Sitzung, Werkzeugname und Argumenten abgeleitet ist: Dieselbe Absicht wirkt einmal, auch wenn sie zweimal ankommt.
Grenzen
| Grenze | Vorgabe |
|---|---|
| Laufzeit eines Werkzeug-Aufrufs | 30 Sekunden |
| Laufzeit eines einzelnen Aufrufs an die API | 15 Sekunden |
| Länge eines Ergebnisses | 20.000 Zeichen, darüber wird sichtbar gekürzt |
| Einträge einer Liste | 50 je Antwort. Die Zahl deckelt zugleich die angeforderte Seitengröße |
| Ruhende Sitzung | 24 Stunden, gleitend — jeder Aufruf setzt die Frist neu |
Der Betreiber Ihrer Umgebung kann diese Werte anders setzen. Ergebnisse enthalten keine signierten Download-Adressen, und Namen und Texte aus einer Antwort sind für den Assistenten Daten, keine Anweisungen — jede Werkzeug-Beschreibung sagt das ausdrücklich.
Wenn etwas nicht klappt
Ein gescheitertes Werkzeug antwortet mit drei Feldern: error (eine der Arten unten), hint (ein Satz auf Englisch) und correlationId (die Kennung, mit der der Betrieb den Aufruf wiederfindet). Die Feldpfade und Kennungen aus dem Fehlerrumpf der API bleiben absichtlich drinnen.
error |
Bedeutung | Lösung |
|---|---|---|
unauthenticated |
Die Plattform hat den Ausweis abgewiesen (401). |
Neu anmelden. Ein Zugriffstoken oat_… gilt eine Stunde. |
permission_denied |
Dem Ausweis fehlt das Recht (403). |
Der Einwilligung das Recht mitgeben oder den Schlüssel mit dem passenden Recht ausstellen. |
not_found |
Zu dieser Kennung gibt es in dieser Organisation nichts (404). |
Kennung aus einer Liste desselben Zugangs nehmen. |
conflict |
Der Gegenstand hat sich seit dem Lesen geändert (409). |
Neu lesen und erneut entscheiden. |
invalid_input |
Die Plattform hat die Angaben abgelehnt (422). |
Werte prüfen. |
no_seat_available |
Alle gebuchten Plätze der Organisation sind belegt (402). |
Einen zusätzlichen Platz bucht ein Mensch im Portal. |
rate_limited |
Das Kontingent ist erschöpft (429). |
Warten, siehe Drosselung und Rate-Limit-Kopfzeilen. |
platform_unreachable |
Die API antwortet nicht. | Später erneut versuchen. Der Dienst kann ohne sie nichts. |
timeout |
Die zugestandene Zeit ist abgelaufen. | Die Frage enger stellen, etwa mit einem Filter oder einer kleineren Liste. |
canceled |
Der Aufruf wurde abgebrochen, zum Beispiel weil die Verbindung abriss. | Erneut aufrufen. |
unexpected |
Die Plattform hat auf eine Weise geantwortet, die der Dienst nicht einordnen kann. | Später erneut versuchen und die correlationId dem Betrieb nennen. |
Ein fremder Mandant ist unsichtbar: Der Dienst hat keine eigene Identität, mit der ein Client mehr erreichen könnte, als sein Ausweis trägt.