Python-App-Vertrag

ci-python-app/v1 beschreibt Python-Apps, ihre Fähigkeiten und die Kommunikation mit dem Fensterhost. Eine fehlende optionale Klasse erhält ein definiertes, funktionsloses Standardverhalten. Eine fehlende oder ungültige Pflichtbasis ist ein Vertragsfehler und verhindert den Start. Der Vertrag ergänzt den XP-Inhaltsvertrag, den App-Zugriffsvertrag und den Fenstervertrag.

Das SDK ci_app_contract verwendet die Python-Standardbibliothek. App-Typ, Darstellung und Transport sind getrennte Eigenschaften: Eine App kann Werkzeuge ohne Fenster anbieten, eine reine Oberfläche besitzen oder als MCP App erreichbar sein. Die Implementierung einer Klasse erteilt keine Nutzerrechte.

Pflichtbasis und optionale Klassen

Klasse Pflicht Verhalten bei fehlender Klasse
AppInfo und AppBase Ja Scanfehler; App wird nicht als vertragskonform gestartet
ToolProvider Nein Keine Fachwerkzeuge; leere Toolliste
ResourceProvider Nein Keine App-Ressourcen; leere Ressourcenliste
WindowUI Nein Keine eigene Fensteroberfläche oder Fensteraktionen
SettingsProvider Nein Keine App-Einstellungssektion; settings: null
StreamProvider Nein Keine App-Streams; keine Stream-Abonnements
EventProvider Nein Keine zusätzlichen App-Ereignisse; Standardereignisse bleiben vorhanden
AgentProvider Nein Keine App-Zusätze zum Agenten; Host-Basisagent bleibt möglich

Apps implementieren nur die benötigten Klassen. Ein leeres ToolProvider-Gerüst wird nicht angelegt, um eine reine Informations-App vollständig aussehen zu lassen. Ist eine optionale Klasse vorhanden, aber ihre Implementierung ungültig, wird dieser Fehler angezeigt; er wird nicht als fehlende Klasse verschluckt.

AppInfo liefert mindestens die stabile App-ID, den Namen und die App-Version. Der konservative Rollenstandard ist customer. Weitere technisch unterstützte Rollen müssen ausdrücklich angegeben werden. Ohne ausdrückliche Rollenunterstützung wird kein höheres Wechselangebot erfunden. Lebenszyklus-Hooks besitzen einen sicheren No-op-Standard; eine App mit eigenen Prozessen, Dateien oder Streams muss deren Freigabe selbst implementieren. Der Host sperrt und beendet den Kontext unabhängig von einer erfolgreichen App-Antwort.

from ci_app_contract import AppBase, AppInfo

app = AppBase(AppInfo(
    app_id="example.info",
    name="Information",
    version="1.0.0",
))
description = await app.describe(principal)

principal ist ein bereits serverseitig geprüfter Kontext. Der Browser darf ihn nicht erzeugen oder die Rollenfreigabe daraus selbst bestimmen. Dieses Minimalbeispiel bietet keine Tools, UI, Einstellungen, Streams oder zusätzlichen Events an.

Erkennung und Fähigkeitsabfrage

Der Host findet Apps ausschließlich über seine registrierten Quellen und zugelassenen Einstiegspunkte. Ein Scan importiert keine beliebigen vom Browser genannten Python-Module und ruft keine frei angegebenen URLs auf. Der Host prüft ID, Vertragsversion, Typ und Antwortform, bevor eine App sichtbar wird.

await app.describe(principal) liefert die technischen Rollen, implementierten Klassen und tatsächlichen Angebote. supported_events enthält die Standardereignisse sowie ausdrücklich registrierte Zusatzereignisse. supported_actions enthält ausschließlich tatsächliche Fensteraktionen als Objekte mit stabiler id. Toolnamen stehen separat in capabilities.tools. Die weiteren Angebote stehen in capabilities.resources, ui, settings, streams, agent_extension und agent_tools. Dateifunktionen werden nicht aus einem App-Namen oder einem Menütext erraten. Minimaler Beispieldeskriptor.

Der Host führt die Abfrage beim Start des XP-Desktops, nach bestätigter Anmeldung beziehungsweise Identitätsänderung und bei ausdrücklichem Aktualisieren aus. Diese Auslöser verwenden dieselbe begrenzte Discovery-Route. Der Scan prüft bereits registrierte, für diesen Nutzer erreichbare Apps und Verbindungen; er erfindet keine App aus fremden URL-Parametern. Ein Login-Scan muss die App nicht vollständig starten.

Technische, nutzerunabhängige Beschreibungen dürfen anhand App-ID, App-/Vertragsversion und Revision zwischengespeichert werden. Antworten mit rollenabhängigen Tools, Aktionen, Ressourcen oder persönlichen Freigaben sind an Identität beziehungsweise Sitzung, Fensterrolle, Autorisierungsrevision, App und Verbindung gebunden. Fenster- oder instanzspezifische Menüs benötigen zusätzlich ihre konkrete Instanzbindung. Ein globaler Admin-Deskriptor darf nicht als Customer-Angebot wiederverwendet werden. Anmeldung, Abmeldung, bestätigter Rollenwechsel, Widerruf, relevante App-/Verbindungsänderung und Aktualisieren verwerfen betroffene Angebote. Ein Ablaufdatum begrenzt die Wiederverwendung zusätzlich; es ersetzt keine aktuelle Rechteprüfung.

Fehler, nicht unterstützte Vertragsversionen und nicht erreichbare Verbindungen werden als solche angezeigt. Ein fehlgeschlagener Scan darf keine früheren höheren Fähigkeiten wieder freigeben. Jeder eigentliche Backend-Aufruf prüft den aktuellen Kontext erneut, auch wenn die Beschreibung aus einem Cache stammt. Automatische Discovery, veröffentlichter Vertrag und aktive Durchsetzung in einzelnen Apps sind getrennte Nachweise.

Der Metadatenscanner im WP-Host verwendet POST /wp-json/wp-agent-lab/v1/program-capabilities mit trigger: startup|login|refresh und optionalem program. Ohne program liefert er erlaubte registrierte MCP-Ziele; für ein Ziel prüft er einen begrenzten Inline-Deskriptor ci-python-app/v1 mit passender App-ID und gleicht angebotene Tools mit den tatsächlich autorisierten MCP-Tools ab. Bestandsangebote ohne Deskriptor heißen ausdrücklich legacy; nicht erreichbare Angebote heißen unavailable. Damit wird ihnen keine Vertragstreue zugeschrieben. Cookie, Nonce, Origin und aktiver Kontext werden geprüft; nach der Anbieterabfrage werden Sitzung, Konto, Desktoprolle und erlaubte Ziele erneut verglichen. Eine Kontextänderung verwirft das Ergebnis. Dieser Scan liefert Metadaten, kein UI-HTML. Er besitzt keinen persistenten Cache; der Browser verwirft abgebrochene oder veraltete Generationen. Das Desktopinventar verwendet die aktuelle Desktoprolle; die Beschreibung einer geöffneten Fensterinstanz verwendet deren eigene bestätigte Rolle. Dies beschreibt den geprüften Implementierungsstand; Aktivierung und persönliche Bedienprüfung werden separat nachgewiesen.

Rollen sind technische Fähigkeit und geprüfte Erlaubnis

Die App beschreibt technische Unterstützung. WP-Lab bleibt die Autorität für Identität, Kontostatus, Kontorolle und persönliche App-Freigaben. Der Host berechnet die tatsächlich auswählbaren Rollen aus Konto, App, persönlicher App-Grenze und authentifizierter Verbindung. Die Desktopansicht ist die Startrolle eines neuen Fensters; sie ist keine zusätzliche Obergrenze für eine ausdrücklich gewählte Fensterrolle.

Ein Admin-Konto in Customer-Desktopansicht darf Entwickler für ein einzelnes Fenster wählen, wenn App und Verbindung dies erlauben. Andere Fenster behalten ihren Kontext. Ein Customer-Konto erhält dadurch keinen Entwicklerzugang. Gastzugriff erfordert die ausdrückliche App-Fähigkeit. Werkzeugaufrufe, Ressourcenlesen, Menüaktionen und direkte Backend-Aktionen prüfen dieselbe serverseitige Entscheidung.

Ein Rollenwechsel wird erst nach Serverbestätigung und erneuerter Autorisierungsrevision als Ereignis an die App gesendet. Die alte Bindung wird ungültig. Eine Nachricht window-role-changed aus dem Browser ist keine Rechtevergabe. Bei Entzug sperrt der Host den Inhalt sofort, verhindert weitere Aufrufe und beendet alte Streams; App-Hooks dürfen die Sperre nicht verzögern.

Standard-Fensterereignisse

AppBase besitzt immer den Dispatch-Einstieg. Die App muss für unbenötigte Ereignisse keine eigenen Handler schreiben. Standardereignisse werden validiert und haben ohne eigenen Handler keine Fachwirkung:

Name Bedeutung
window-ready Die gebundene Fensterinstanz ist bereit
window-size-changed Größe des Inhaltsbereichs hat sich geändert
window-position-changed Position des Fensters hat sich geändert
window-state-changed Zustand normal, minimiert oder maximiert hat sich geändert
window-gained-focus Fenster hat den Fokus erhalten
window-lost-focus Fenster hat den Fokus verloren
window-role-changed Eine neue Fensterrolle wurde vom Server bestätigt
view.changed Das bestätigte Darstellungsformat hat sich geändert
agent-capabilities-changed Die effektiven Agentenfähigkeiten haben sich geändert
window-action Eine ausdrücklich angebotene Fensteraktion wurde gewählt
window-closed Die gebundene Fensterinstanz wurde geschlossen
context-revoked Der bisherige Autorisierungskontext ist ungültig

Der Nachrichtenumschlag verwendet ci-python-app-event/v1:

{
  "schema": "ci-python-app-event/v1",
  "eventId": "evt-123",
  "windowId": "window-123",
  "instanceId": "instance-123",
  "contextRevision": 4,
  "name": "window-size-changed",
  "data": {"width": 960, "height": 620}
}

correlationId kann eine zugehörige Anfrage verknüpfen. Kennungen sind auf 128 Zeichen begrenzt; die Kontextrevision ist eine nicht negative ganze Zahl. Vor Dispatch muss der Host authentifizierten Absender, Besitz, Fenster-/Instanzbindung und aktuelle Revision prüfen. await app.dispatch_event(envelope, authorize=...) führt diese Hostprüfung vor Default oder Handler aus. Ereignisse mit ungültigem oder veraltetem Kontext führen keine Aktion aus.

EventProvider ergänzt ausdrücklich registrierte Namen. Unbekannte Namen erhalten handled: false und diagnostic: "unsupported-event". Sie werden ausschließlich diagnostisch und ohne Payload protokolliert. Es entstehen weder neue Fähigkeiten noch Fehlerbehebungsaktionen aus einem unbekannten Namen.

Menüaktionen und responsive Darstellung

Der vorhandene Explorer-Menüvertrag bleibt die Quelle für menu und menu-activate. Eine Auswahl bindet die stabile Aktions-ID an das aktuell bestätigte Menü und dessen Revision, die Fenster-/Instanzbindung und die Autorisierungsrevision. Unbekannte, entfernte, deaktivierte oder veraltete Aktionen werden abgewiesen; eine Aktions-ID allein genügt nicht. Ein veraltetes Menü wird erneut abgefragt. Die App meldet beispielsweise file.open nur in supported_actions, wenn diese Funktion tatsächlich angeboten wird.

Die Runtime-Fähigkeitsabfrage liefert supported_events, supported_actions sowie die obersten Felder revision und action_source. Jedes Aktionsobjekt enthält id und label, gegebenenfalls disabled oder busy. Die Quelle lautet xp-content-menu oder foundation-window. Vor einer tatsächlichen Menüanmeldung sind revision und action_source null; statische App-Metadaten erfinden keine ausführbaren Menüs. Die Menürevision ist von app.revision des technischen Deskriptors und von contextRevision der Autorisierung zu unterscheiden.

Legacy-Auswahl behält menu-activate mit {revision, id}; Foundation behält actionRequest mit {revision, actionId, arguments}. Nur bei weiterhin aktuellem Menü-/Autorisierungskontext kann der Host einmal window-action mit data.actionId, data.arguments und einer correlationId weitergeben. Dieses zusätzliche Ereignis ist informierend und darf die Fachaktion nicht erneut ausführen. Bei Foundation muss dafür die bestehende Ausführung fachlich erfolgreich sein: Ein error im Antwortumschlag, isError: true im Ergebnis oder ein fehlendes Foundation-Manifest verhindern das Informationsereignis. Eine angenommene Legacy-Notification belegt dagegen Transport, nicht den abgeschlossenen Erfolg der Fachaktion. Keine zweite parallele Route für „Datei → Öffnen“ einführen. Der Server prüft Rolle und Kontext auch nach einem sichtbaren Menüklick; ausgeblendete Menüs sind kein Zugriffsschutz.

Ein Fenster verwendet eines der Formate phone-format, tablet-hochformat, tablet-querformat, desktop-format. Die Standardableitung verwendet die tatsächliche Größe des Inhaltsbereichs in CSS-Pixeln, ohne Fensterrahmen und Taskleiste: Breite unter 600 Pixeln → Phone; unter 1200 Pixeln → Tablet, mit Höhe größer Breite im Hochformat, sonst Querformat; ab 1200 Pixeln → Desktop. Bildschirmgröße oder die äußere Fenstergröße ersetzen diese Maße nicht. view.changed führt width, height, mediaType und format; Größenwechsel verhandeln das Angebot erneut.

Eine App mit WindowUI reagiert über responsive Styles und, wenn notwendig, angepasste Inhalte. Eine App ohne UI braucht keine künstlichen Styles oder View-Handler. Fenstergröße und Darstellungsformat sind getrennte Angaben. Die vier angebotenen Formate müssen ohne abgeschnittene Bedienung funktionieren; eine deklarierte Fähigkeit allein ist keine visuelle Abnahme.

Bestehende PHP-Fachlogik über Python anbinden

Für vorhandene XP-Apps mit PHP-Backend stellt ein Python-Adapter die verbindliche App-Schnittstelle bereit. Die Fachlogik bleibt in PHP; WP-Lab bleibt Autorität für Identität, SSO, Kontostatus, Rollen und App-Freigaben. Der Adapter verwendet dieselben AppBase/AppInfo und nur die tatsächlich benötigten optionalen Provider. Eine zusätzliche Pflichtklasse ist nicht erforderlich. Eine reine Oberfläche benötigt keinen ToolProvider; ein vorhandener PHP-Endpunkt wird nur dann als Werkzeug angeboten, wenn seine Fachaktion ausdrücklich registriert und autorisiert ist.

Für reine Discovery darf ein bereits autorisierter PHP-Host einen fest registrierten Python-CLI-Probe aufrufen, der nur die technische Beschreibung liefert. PHP prüft Konto, Sitzung, wirksame Rolle und App-Freigabe vor und nach dem begrenzten Probeaufruf. Ohne persönliche Daten oder Fachtools muss der Probe keinen Nutzerkontext erhalten; seine Antwort erteilt keine Rechte. Ein Startscan ohne geöffnetes Fenster besitzt keine Fenster-/Instanzbindung und darf keine solche Bindung vortäuschen. Discovery beweist weder Python→PHP-Fachtransport noch Fensterrollenwechsel oder Backend-Ereigniszustellung.

Für tatsächlich adaptierte Fachaktionen lautet der Aufrufweg: Host mit bestätigtem Kontext → Python-App-Adapter → fest registrierter PHP-Dienst. Das Ziel, erlaubte Operationen und Eingabeschemas stammen aus vertrauenswürdiger Serverkonfiguration. Der Browser bestimmt weder PHP-Code, Modulimporte noch beliebige Ziel-URLs. Backend-Aufrufe sind authentifiziert und an App, bestätigte Identität beziehungsweise Sitzung, wirksame Fensterrolle, Autorisierungsrevision und beabsichtigtes Ziel gebunden. Die Bindung ist zeitlich begrenzt; Credentials bleiben serverseitig. Ein Dienstschlüssel bestätigt den Adapter, ersetzt jedoch keine Nutzerentscheidung.

PHP validiert diese Bindung und prüft die aktuelle WP-Lab-Entscheidung sowie seine bestehenden fachlichen Rechte bei jedem Aufruf erneut. Ein globales Admin-Servicekonto darf keine niedrigere Fensterrolle übergehen. Direkte PHP-Aktionen und Adapteraufrufe müssen dieselben wirksamen Rechte durchsetzen. Falsche App/Zielbindung, abgelaufene oder widerrufene Kontexte und alte Revisionen werden abgewiesen. Python übernimmt eine ablehnende PHP-Antwort unverändert als Fehler; nach Kontextänderung verwirft der Host verspätete Ergebnisse. Zeit-, Größen- und Eingabegrenzen verhindern unbegrenzte Backend-Aufrufe; Schreibaktionen werden nicht blind wiederholt.

Der Deskriptor zeigt ausschließlich tatsächlich angebundene, in diesem Kontext erlaubte Fähigkeiten. Fehler bei einer benötigten PHP-Abfrage sind kein leeres Erfolgsangebot und reaktivieren keine früheren höheren Fähigkeiten. Ein funktionierender Deskriptor beweist noch keine funktionierende Fachaktion.

Bestehende UI- und Menüwege bleiben erhalten. Fensterereignisse verwenden den vereinbarten Umschlag und sind ohne registrierte Fachwirkung informativ oder No-op. Ein window-action führt eine bereits über den vorhandenen Menüweg ausgeführte PHP-Aktion nicht erneut aus. Eine WindowUI reagiert auf die vier Inhaltsformate. Ein Backend-Ereignistransport oder eine MCP-App-UI wird erst behauptet, wenn er tatsächlich implementiert und geprüft ist. Der Adapter allein macht eine normale App nicht automatisch zur MCP App; dafür bleiben echte UI-Ressource, Metadatenbindung, Transport und Clientprüfung erforderlich.

Prüfung: zulässige und unzulässige Rollen auf beiden Seiten, Admin-Konto mit Customer-Fensterrolle, fremde App-/Fensterbindung, alte Revision, Ablauf, Entzug während eines Aufrufs, direkte PHP-Umgehungsversuche und PHP-Ausfall. Quellstand, authentifizierter Adapter-/PHP-Aufruf, Veröffentlichung und angemeldete UI-Abnahme sind getrennte Nachweise. Die Architekturentscheidung migriert keine weitere App und erteilt keine zusätzlichen PHP-Rechte.

MCP Apps

Eine MCP App erhält dieselbe Fachbeschreibung über eine geschützte JSON-Ressource. Customer und höhere Rollen dürfen die für sie freigegebene technische Beschreibung lesen; persönliche Zuordnungen anderer Nutzer werden nicht exportiert. Eine öffentliche Vertragsseite beschreibt die Schnittstelle und enthält keine Live-Nutzerentscheidungen.

MCP-Darstellung erfordert eine tatsächliche ui://-Resource mit MIME-Typ text/html;profile=mcp-app und einen Toolbezug über _meta.ui.resourceUri. Der Host unterstützt bestehende flache ui/resourceUri-Metadaten für Kompatibilität. visibility: ["app"] ist eine MCP-Sichtbarkeitsangabe und keine Customer-/Developer-/Adminfreigabe. OAuth-Resource, Audience, Sandbox und CSP bleiben erhalten.

McpAppAdapter beschreibt vorhandene MCP-Tools und Ressourcen über servergeprüfte Callbacks. Er erzeugt keine synthetischen Fachtools und verpackt keine beliebige Oberfläche automatisch. Eine fehlende deklarierte MCP-UI-Bindung ist ein Fehler. Ein zusätzliches UI-Startwerkzeug wäre Transportinfrastruktur und verlangte keine künstlichen Fachtools in einer reinen UI-App. Eine normale WindowUI wird erst durch einen ausdrücklich implementierten und geprüften MCP-Adapter zur MCP App. Ein Python-Adapter vor bestehender PHP-Fachlogik ist die gewählte Architektur. Ein automatischer allgemeiner Normal-App-zu-MCP-Wrapper bleibt eine gesondert zu klärende Aufgabe.

Optionaler App-Agent und Livevoice

Der Host kann einen Basisagenten über den zugelassenen Codex-Agent-MCP beziehungsweise die bestehende Voice-Anbindung anbieten. Der optionale AgentProvider ergänzt mit agent_context(principal) appbezogenen Kontext und mit agent_tools(principal) zusätzliche, serverautorisierte Angebote. Fehlt die Klasse, gibt es keine App-Zusätze; das verhindert keinen Host-Basisagenten.

Die Agentensitzung ist an App, Nutzer, Fenster und Autorisierungsrevision gebunden. Standardmäßig erhält sie alle in der aktuellen Fensterrolle erlaubten App-Tools und MCP-Funktionen. Rollen verwenden guest < customer < developer < admin: Eine Mindestrolle schließt höhere Rollen ein, solange keine ausdrückliche App-/Nutzer-/Verbindungsbeschränkung entgegensteht. Maßgeblich ist die aktive Fensterrolle, nicht die höchste Kontorolle. Die App darf das Agentenangebot weiter einschränken. Der Nutzer kann einzelne Tools und MCP-Anbindungen für diesen Fensteragenten jederzeit deaktivieren.

Der Server speichert und prüft die Einschränkung in der gebundenen Agentensitzung sofort. Jeder weitere Tool-/MCP-Aufruf verwendet das neue effektive Angebot; eine UI-Umschaltung, erfolgreiche Ereigniszustellung oder App-Bestätigung ist keine Voraussetzung. Noch abbrechbare laufende Aufrufe und betroffene Voice-Sitzungen werden beendet. Eine bereits abgeschlossene Fachaktion wird dadurch nicht rückgängig gemacht.

Erst nach wirksamer Änderung informiert agent-capabilities-changed die App best effort über den neuen Zustand. Ein versäumtes Ereignis darf alte Rechte nicht erhalten. Capability-Caches und laufende Antworten dürfen keine gesperrten Angebote zurückbringen. Agenten erweitern weder Kontorolle noch Dateiwurzeln. Die Vertragsforderung nach sofortiger Serversperre ist von ihrer tatsächlichen Implementierung und Abnahme zu unterscheiden.

Einwilligung, Mikrofonberechtigung, Verbindung und aktive Aufnahme sind getrennte Zustände. Rollenentzug, Schließen oder Ende der Sitzung sperren Agentenaktionen und beenden Livevoice. Eine harte Aufnahmedeadline und ein ausdrücklich vereinbartes Kostenlimit müssen serverseitig durchgesetzt werden; eine Browseranzeige ersetzt sie nicht. Nicht implementierte Voice-Fähigkeiten werden nicht behauptet. Für echte API-Entwicklung gilt zusätzlich der Skill paid-api-budget; die Vertragsprüfung benötigt keinen Live-API-Aufruf.

Prüfanforderungen und Einführungsstand

Vertragstests prüfen die minimale App, jede vorhandene optionale Klasse, fehlende Klassen, ungültige Beschreibungen, bekannte und unbekannte Events, nicht erklärte Aktionen und Ablehnung falscher Bindungen oder Revisionen. Rollenprüfungen decken mindestens Admin-Konto mit Customer-Desktop → Entwicklerfenster und Ablehnung eines echten Customer-Kontos ab. Browserprüfung kontrolliert zwei unabhängige Fenster und die tatsächlichen responsive Formate.

SDK-Tests, veröffentlichte Vertragsdateien, aktive Backend-Durchsetzung, persönliche Browserprüfung und MCP-Client-Abnahme sind getrennte Nachweise. Veröffentlichung dieses Vertrags migriert keine Apps, aktiviert keine neue OAuth-Verbindung und belegt keine Agenten-/Voice-Abnahme. Der konkrete Rolloutstand gehört in den bestehenden Werkstatt-Auftrag und die Betriebsdokumentation.

Offizielle MCP-Referenz: MCP Apps.