OpenAI-MCP-App: OAuth- und Lernsession-Bindung
Stand: 11. August 2026 Status: verbindliche Zielarchitektur für den mehrsprachigen OpenAI-V1-MCP-Coach
Dieses Dokument ist die Quelle der Wahrheit für die Identitäts- und Sitzungsbindung der App SkillPilot Coach v1. Für Pluginidentität, Contract Major und Lebenszyklus ist ergänzend der Versionierungs- und Lebenszyklusplan verbindlich.
1. Architekturentscheidung
SkillPilot verwendet zwei bewusst voneinander getrennte Berechtigungen:
- OAuth authentisiert und autorisiert die MCP-App.
Die produktive App verwendet genau einen vorregistrierten vertraulichen
OAuth-Client. Dessen feste
client_idund langes zufälligesclient_secretwerden einmalig vom App-Autor in ChatGPT und im SkillPilot-Authorization-Server konfiguriert. Am Token-Endpunkt weist die App den Besitz des Secrets mitclient_secret_basicnach. - Eine temporäre Lernsession wählt den Lernenden und die Kommunikationssprache.
Erst ein ausdrücklich bestätigtes Lernen starten in der
First-Party-Oberfläche erzeugt eine neue
learningSessionIdund öffnet einen neuen Chat. Starts sind exakt 24 Stunden gültig. Die Session verweist ausschließlich im SkillPilot-Backend auf die gewählte SkillPilot-ID und die beim Start festgelegtecommunicationLocale.
Beide Nachweise sind für einen fachlichen MCP-Aufruf erforderlich:
gültiges OAuth Access Token
AND
gültige learningSessionId
OAuth allein darf weder eine Lernsession erzeugen noch einen Lernenden auswählen. Eine Lernsession allein darf keinen MCP-Aufruf autorisieren.
2. Die beiden Bindungen
| Bindung | Transport | Serverseitige Bedeutung |
|---|---|---|
| ChatGPT/App -> SkillPilot | client_id + client_secret_basic im OAuth-Code-Flow; danach Access Token im HTTP-Header |
Genau der vorregistrierte vertrauliche Client darf ein für die SkillPilot-MCP-Resource bestimmtes Token erhalten und verwenden. |
| Chat -> Lernsession -> Lernender | learningSessionId als Pflichtargument jedes fachlichen MCP-Tools |
Diese noch gültige, in SkillPilot gestartete Session gehört zu genau einer SkillPilot-ID. |
Die dauerhafte SkillPilot-ID bleibt ausschließlich im SkillPilot-Backend. Sie wird weder in den Chat noch in MCP-Toolargumente übernommen.
3. Verbindlicher Startablauf
Jeder Klick auf Lernen starten ist eine eigene atomare Startoperation:
- Die SkillPilot-Webanwendung kennt die aktuell ausgewählte SkillPilot-ID, die
gewählte
communicationLocaleund den vom Benutzer vorbereiteten Lernkontext. - Sie sendet genau einen Startrequest an das SkillPilot-Backend.
- Das Backend wendet den typisierten Startkontext auf den autoritativen Lernendenzustand an.
- Das Backend erzeugt genau in diesem Augenblick eine neue kryptografisch
zufällige
learningSessionId. - Das Backend speichert nur den HMAC-Hash der ID sowie die Zuordnung zur
SkillPilot-ID, die
communicationLocale, Startzeit und absolute Ablaufzeit. - Das Backend liefert eine fertige Startnachricht und die zugehörige ChatGPT-URL zurück.
- Die Webanwendung öffnet ChatGPT mit dieser bereits eingetragenen Startnachricht.
Beispiel:
Verwende SkillPilot Coach v1 und fahre fort.
learningSessionId: sps_<zufälliger opaker Wert>
Die Lernsession-ID ist damit für ChatGPT sichtbar, aber nicht die dauerhafte SkillPilot-ID. Der Benutzer muss nichts kopieren oder technisch konfigurieren.
3.1 Jeder Start ist neu
Jeder Startrequest erzeugt eine andere Lernsession-ID:
- auch wenn derselbe Lernende direkt erneut startet;
- auch wenn eine ältere Session noch gültig ist;
- unabhängig davon, ob ein anderer Lernender dasselbe ChatGPT-Konto nutzt;
- unabhängig davon, ob bereits eine OAuth-Verbindung besteht.
Mehrere Sessions dürfen parallel gültig sein. Ihre Gültigkeit wird nicht durch Benutzung verlängert. Sessions enden exakt 24 Stunden nach ihrer Startzeit; nur der nachfolgend definierte, requestlokale First-Party-Diagnosefall darf eine einzelne Session verkürzen.
3.2 Erneuerung über den First-Party-Webstart
Ohne aktuelle Startnachricht ruft das Modell kein SkillPilot-Werkzeug auf. Es
verweist kurz und lokalisiert auf https://skillpilot.com/, wo die lernende
Person ihre SkillPilot-ID lädt oder erzeugt, den Lernkontext konfiguriert und
Lernen starten wählt. Liefert ein sessiongebundenes Tool
SESSION_REQUIRED, SESSION_RENEWAL_REQUIRED oder
SESSION_VERSION_UNAVAILABLE, wird die alte Session nicht erneut verwendet.
Der Coach gibt instruction unverändert aus oder wählt den exakten
lokalisierten Eintrag aus instructions; die exakte startUrl ergänzt er nur,
wenn sie nicht schon enthalten ist. Es folgt keine Fachantwort. Der First-Party-Webstart erzeugt eine frische
Session und öffnet einen neuen Chat. OAuth wird dafür nicht neu verbunden.
3.3 Gegatete First-Party-Diagnoselaufzeit
Für einen kontrollierten Live-Test darf ausschließlich
POST /api/ui/learners/{skillpilotId}/openai/v1/launch das optionale JSON-Feld
diagnosticSessionTtlSeconds akzeptieren. Das ist kein alternativer
Sessionvertrag, sondern eine eng begrenzte Testeingabe mit folgenden
Invarianten:
- der Server akzeptiert sie nur bei
SKILLPILOT_OPENAI_COACH_V1_DIAGNOSTIC_SESSION_TTL_ENABLED=true; - der ganzzahlige Wert liegt zwischen
3601und86400Sekunden einschließlich und überschreitet niemals die normalePT24H-Laufzeit; - die verkürzte Laufzeit gilt nur für die unabhängige Session dieses einen Requests und verändert weder Konfiguration noch andere Sessiondatensätze;
- der nächste First-Party-Request ohne Feld ist automatisch wieder
PT24H;
3660 Sekunden erzeugen ungefähr eine Minute Beobachtungszeit bis zum Übergang
unter den PT1H-Aktionshorizont. 5400 Sekunden eignen sich für einen
90-Minuten-Soak. Die globale Learning-Session-TTL wird für beide Tests nicht
verändert. Schon das Weglassen des Feldes stellt das normale PT24H-Verhalten
ohne Deployment wieder her; das separate Diagnose-Gate wird nach dem
kontrollierten Testfenster über den regulären Konfigurationsweg deaktiviert.
4. MCP-Aufruf
ChatGPT übernimmt die Lernsession-ID aus der Startnachricht unverändert in jedes fachliche SkillPilot-Tool:
{
"learningSessionId": "sps_<zufälliger opaker Wert>",
"...weitere fachliche Argumente": "..."
}
Das modellseitige Tool-Schema beschreibt dafür nur ein erforderliches String-Feld mit der Anweisung zur unveränderten Übernahme. Regex, exakte Länge und weitere technische Tokenregeln bleiben absichtlich serverseitig, damit sie das LLM nicht zur Rekonstruktion eines opaken Werts verleiten.
Parallel sendet die Connector-Infrastruktur das OAuth Access Token außerhalb des Modellkontexts:
Authorization: Bearer <oauth-access-token>
Das Backend prüft bei jedem Toolaufruf in dieser Reihenfolge:
- gültiges OAuth Access Token;
- erwartete Resource/Audience und erforderlicher Read- oder Write-Scope;
- vorhandene, syntaktisch gültige
learningSessionId; - HMAC-basierte Auflösung der Lernsession;
- tatsächlichen Ablauf und Widerruf;
- Zuordnung zum autoritativen Lernendenzustand und Verfügbarkeit der gepinnten Contract-, Workflow- und Curriculumrevision;
- für jede Lese-, Schreib- oder Replay-Antwort mindestens
PT1HRestlaufzeit; - bei einem Write den gespeicherten Replay eines bereits committeten Requests
mit gleichem Toolnamen, kanonisch identischen Argumenten, derselben
clientRequestIdund einercompletedStateVersion, die weiterhin der aktuellen kanonischen Learner-Revision entspricht.
Erst danach wird die fachliche Operation ausgeführt. Schreibende Tools benötigen weiterhin zusätzlich den Write-Scope.
Für den Aktionshorizont gilt expiresAt >= now + PT1H: Genau eine Stunde
Restlaufzeit ist gültig, weniger als eine Stunde wird vor der fachlichen
Operation mit SESSION_RENEWAL_REQUIRED abgewiesen. Dasselbe gilt für einen
Replay eines bereits committeten Writes. Oberhalb der Grenze liefert er das
gespeicherte Resultat nur, wenn die gepinnten Workflow- und
Curriculumversionen weiter verfügbar sind und seine
completedStateVersion noch der aktuellen kanonischen Learner-Revision
entspricht. Er führt weder Operation noch Mutation erneut aus; bei einer
inzwischen fortgeschrittenen Revision endet er mit
STATE_VERSION_CONFLICT.
Die learningSessionId ist Pflichtargument aller fachlichen
SkillPilot-MCP-Tools. Es gibt keine Ausnahme für den ersten Leseaufruf.
5. Was ausdrücklich nicht zulässig ist
Das Backend darf eine fehlende oder ungültige Lernsession niemals ersetzen durch:
- den Lernenden, der früher mit dem OAuth-Subject verbunden war;
- die zuletzt erzeugte oder „aktuelle“ Session;
- irgendeine andere Session desselben Lernenden;
- einen Pending Launch;
- eine im Chat eingegebene dauerhafte SkillPilot-ID;
- eine beim OAuth-Callback implizit erzeugte Session.
OAuth-Callbacks, Token-Erneuerungen und erneute MCP-Verbindungen dürfen keine Lernsession erzeugen, ersetzen, verlängern oder reaktivieren.
Damit ist auch ein gemeinsames ChatGPT-Konto unproblematisch: Welcher
SkillPilot-Lernende fachlich adressiert wird, bestimmt ausschließlich die bei
diesem konkreten Start erzeugte learningSessionId.
6. Datenmodell
Die Tabelle openai_de_learning_session ist die kanonische
Persistenzgrenze für diese kurzlebige Zuordnung:
| Feld | Bedeutung |
|---|---|
token_hash |
HMAC-Hash der ausgegebenen learningSessionId; der Klartext wird nicht gespeichert |
learner_id |
serverinterne Fremdschlüsselzuordnung zum Lernenden |
communication_locale |
autoritative Sprache für Backendnutzdaten und jede sichtbare Coachkommunikation |
started_at |
Zeitpunkt des Klicks auf Lernen starten |
expires_at |
absolute Ablaufzeit; normal started_at + PT24H, ausschließlich beim gegateten First-Party-Diagnoserequest started_at + diagnosticSessionTtlSeconds |
Die frühere Belegung derselben Tabelle mit dem OAuth-Subject als Primärschlüssel wird bei der Migration verworfen. Ein OAuth-Subject darf bei MCP-Aufrufen weder gelesen noch als Lernenden- oder Session-Fallback verwendet werden.
7. OAuth-Bindung
OAuth Authorization Code mit PKCE bleibt von der Lernsession getrennt:
- Der App-Autor registriert genau eine feste produktive
client_id, genau die in ChatGPT angezeigte Callback-URL und ein langes zufälligesclient_secret. - ChatGPT verwendet die konfigurierte
client_idund authentisiert den vertraulichen Client am Token-Endpunkt mitclient_secret_basic. - Das Secret liegt ausschließlich in der geschützten ChatGPT-App-Konfiguration und in der SkillPilot-Serverkonfiguration. Es gehört weder ins Repository noch in Browsercode, Prompts, Toolargumente, Antworten oder Logs.
- PKCE
S256, die exakte Callback-Allowlist, die exakte Resource/Audiencehttps://mcp-coach-v1.skillpilot.com/mcp, Scopes, Ablauf und Widerruf werden weiterhin geprüft. - Offene Dynamic Client Registration und CIMD sind in diesem produktiven Profil weder erforderlich noch erlaubt.
- Der dedizierte V1-MCP-vHost prüft zusätzlich das von ChatGPT präsentierte,
OpenAI-verwaltete Clientzertifikat. Ein kontrollierter
observe-Modus lässt fehlende Zertifikate zunächst bis OAuth passieren, lehnt aber ungültige Zertifikate ab. Vor Veröffentlichung wird aufenforceumgestellt; dann benötigen externe/mcp-Aufrufe die verifizierte OpenAI-Kette, denclientAuth-EKU und den exakten Connector-SAN. mTLS ersetzt weder die app-spezifische OAuth-Clientauthentisierung noch die Lernsession.
Der OAuth-Principal oder ein OAuth-Subject ist kein Ersatz für die temporäre
Lernsession. OAuth dient ausschließlich der App-Autorisierung und dem
kontrollierten Verbindungsaufbau. Welcher Lernende bei einem Toolaufruf
adressiert wird, ergibt sich nur aus der expliziten learningSessionId.
8. Fehlerverhalten
| Situation | Verhalten |
|---|---|
| OAuth fehlt/ist ungültig | normale OAuth-Neuautorisierung; keine Lernsession wird erzeugt |
learningSessionId fehlt |
SESSION_REQUIRED; nur unveränderte Serverinstruktion und, falls darin noch nicht enthalten, exakte startUrl; keine Fachantwort |
| ID unbekannt/manipuliert | SESSION_REQUIRED; keine Identitätsableitung; neuer First-Party-Start und neuer Chat |
| Session abgelaufen/widerrufen | SESSION_REQUIRED; neuer First-Party-Start und neuer Chat |
| Session hat weniger als eine Stunde Restlaufzeit | SESSION_RENEWAL_REQUIRED; exakt eine Stunde ist noch zulässig; neuer First-Party-Start und neuer Chat |
| gepinnte Workflow-/Curriculumrevision fehlt | SESSION_VERSION_UNAVAILABLE; neuer First-Party-Start und neuer Chat |
| Write-Scope fehlt | Operation ablehnen; keine fachliche Teilmutation |
| Startkontext kann nicht atomar angewendet werden | keine Lernsession ausgeben |
Diagnosefeld deaktiviert, außerhalb 3601..86400 oder oberhalb der normalen Laufzeit |
Request ablehnen; keine Lernsession ausgeben |
Fehlerantworten und Logs dürfen weder Lernsession-ID, OAuth-Token noch dauerhafte SkillPilot-ID ausgeben. ChatGPT soll den Benutzer nicht auffordern, eine SkillPilot-ID oder einen Token manuell einzutippen. Der einzige aktive Wiederherstellungsweg ist Lernen starten in der First-Party-SkillPilot- Oberfläche und der dadurch geöffnete neue Chat. OAuth allein ist kein Startweg.
9. Sicherheitsinvarianten
- Die dauerhafte SkillPilot-ID bleibt serverseitig.
- Lernsession-IDs sind zufällig, opak und nur als HMAC-Hash gespeichert.
- Normale Lernsessions gelten absolut exakt 24 Stunden.
Ausschließlich der gegatete First-Party-Diagnoserequest darf eine einzelne
Session requestlokal auf
3601..86400Sekunden, höchstens jedoch die normalePT24H-Laufzeit, verkürzen. Ein nachfolgender Request ohne Feld ist wiederPT24H. - Benutzung verlängert die Ablaufzeit nicht.
- Jede neue fachliche Operation benötigt mindestens
PT1HRestlaufzeit; exaktPT1Hist gültig. - Auch ein gespeicherter Write-Replay benötigt mindestens
PT1HRestlaufzeit. Bei gleichem Toolnamen, kanonisch identischen Argumenten, derselbenclientRequestId, verfügbaren gepinnten Versionen und unveränderter kanonischer Learner-Revision liefert er das gespeicherte Resultat, ohne eine Operation erneut auszuführen. - Jeder Start erzeugt eine neue, unabhängige Session.
- OAuth allein wählt keinen Lernenden und erzeugt keine Lernsession.
- Eine Lernsession allein autorisiert keinen MCP-Aufruf.
- Jedes fachliche Tool verlangt dieselbe explizite
learningSessionId. - Es existiert kein Lookup oder Fallback über OAuth-Subject.
- Lernziel-, Frontier- und Mastery-Semantik bleiben unverändert.
- Nur der vorregistrierte vertrauliche OAuth-Client erhält Tokens; der
Token-Endpunkt akzeptiert für ihn ausschließlich
client_secret_basic. - Das OAuth-Client-Secret erscheint niemals in Repository, UI, Prompt, Toolargumenten, Antworten oder Logs.
- Die final bestätigte
communicationLocalewird beim First-Party-Start in der Session gespeichert, bei jedem Kontextabruf geliefert und danach weder aus Hostlocale noch aus neutral englischen Pluginmetadaten neu abgeleitet. - Zu Beginn jedes Learner-Turns muss im aktuellen Assistant-Turn
get_skillpilot_contexterfolgreich sein. Nach einer erfolgreichen Mutation ist ihr vollständiger Nachfolgerzustand für den Rest desselben Assistant-Turns autoritativ und wird nicht erneut geladen. - Permanente ID, Providerhinweis und Level-2-Konfiguration bleiben im First-Party-WebGUI; V1 besitzt dafür keine Modellwerkzeuge.
10. Abnahmekriterien
Die Implementierung ist erst vollständig, wenn automatisierte Tests mindestens Folgendes beweisen:
- Ein UI-Klick führt zu genau einem Startrequest und einer ChatGPT-Navigation.
- Jeder Start erzeugt eine neue Session-ID, auch für denselben Lernenden.
- Starts verschiedener Lernender können parallel über dieselbe OAuth-Verbindung verwendet werden.
- Die fertige Startnachricht enthält genau eine Lernsession-ID und keine SkillPilot-ID.
- Alle fachlichen MCP-Toolschemas verlangen
learningSessionId. - Ein gültiges OAuth-Token ohne Lernsession wird abgelehnt.
- Eine gültige Lernsession ohne OAuth wird abgelehnt.
- Eine unbekannte, manipulierte, widerrufene oder abgelaufene Session wird ohne Fallback abgelehnt.
- Ein erfolgreicher Toolaufruf löst den Lernenden ausschließlich aus der expliziten Lernsession auf.
- Wiederholte Nutzung verschiebt
expires_atnicht. - Fachliche Read-/Write-Scope-Prüfungen bleiben erhalten.
- Bestehende Lernziel-, Frontier- und Mastery-Tests bleiben unverändert grün.
- Deutsch und Englisch verwenden denselben V1-Toolvertrag; jede Antwort folgt
der in der jeweiligen Lernsession gespeicherten
communicationLocale. - Token Requests mit fehlendem oder falschem Client-Secret, fremder Client-ID, falscher Callback-URL oder anderer Resource werden abgelehnt.
- Die Authorization-Server-Metadaten veröffentlichen
client_secret_basic;none, DCR und CIMD sind nicht Teil des aktiven Produktionsprofils. - Der gegatete First-Party-Diagnoserequest akzeptiert
3601und86400, lehnt3600,86401, einen Wert oberhalb der normalen Laufzeit sowie ein gesetztes Feld bei deaktiviertem Gate ab. - Ein Diagnose-Start mit
3660oder5400wirkt nur auf seine eigene Session; bereits der nächste Start ohne Feld läuft wiederPT24H. - Zu Beginn jedes Learner-Turns läuft in demselben Assistant-Turn ein erfolgreicher Kontextabruf; ein Sessionfehler verhindert jeden fachlichen Text. Nach einer erfolgreichen Mutation wird ihr vollständiger Nachfolgerzustand im selben Turn ohne redundanten Kontextabruf verwendet.
- Der öffentliche V1-Toolkatalog enthält keine sessionlosen Start-, Capability-, Curriculum- oder Personalisierungswerkzeuge.
- Ein Write-Replay wird unterhalb
PT1H, bei nicht mehr verfügbarer gepinnter Workflow-/Curriculumversion oder nach Fortschritt der kanonischen Learner-Revision abgelehnt, auch wenn Toolname, kanonische Argumente undclientRequestIddem gespeicherten Write entsprechen.