OpenAI MCP App: OAuth and 24-Hour Learning-Session Binding
Generated artifact. Do not edit manually.
Generated by:
scripts/build_compendia.pyRegenerate with:python3 scripts/build_compendia.py --compendium runtime-workflows --web-and-texSource of truth:docs/concept/runtime-workflows/source/
Language / Sprache: English (EN) · Deutsch (DE) | PDF: English PDF · Deutsches PDF
Compendium: §1 Personalization · §2 Learning Workflow · §3 SRS Sync · §4 Import/Export · §5 Portable Host · §6 Coach Contract · §7 Coach Topology · §8 OAuth & Session · §9 Coach Behavior · §10 Plugin Lifecycle
8. OpenAI MCP App: OAuth and 24-Hour Learning-Session Binding
Scope and current baseline
Authoritative identity and session-binding specification for the multilingual OpenAI MCP coach (SkillPilot Coach / skillpilot-coach on https://mcp-openai.skillpilot.com/mcp per the 19 October 2026 decision in 10.1; historically skillpilot-coach-v1 on https://mcp-coach-v1.skillpilot.com/mcp in the preserved 1.1.2 Git beta). For plugin identity, contract major, and lifecycle rules, see 10.
8.1. Architectural Decision: Two Independent Credentials
SkillPilot separates integration authentication from learner addressing through two independent credentials:
- OAuth authenticates and authorizes the MCP integration: The hosted directory app uses a single pre-registered confidential OAuth client (
client_id+ high-entropyclient_secretviaclient_secret_basicat the token endpoint, Authorization Code + PKCES256). For the authorized native desktop route, a separate, strictly pinned public CIMD profile (https://chatgpt.com/oauth/codex/7yTcOAOqrumX/client.json, loopback callbackhttp://127.0.0.1/callback/7yTcOAOqrumXon a dynamic port with PKCES256) exists alongside it and never serves as a fallback from failed confidential authentication. - A temporary 24h learning session selects the learner and interaction locale: Only an explicitly confirmed Start learning (Lernen starten) action in the first-party WebGUI mints a fresh opaque
learningSessionId(PT24H) and opens a new chat. The mapping to the permanent SkillPilot ID andcommunicationLocaleremains exclusively inside the backend.
Every domain MCP tool call requires both credentials simultaneously:
valid OAuth access token (exact resource + read/write scope)
AND
valid learningSessionId (active + remaining lifetime >= PT1H)
OAuth alone never creates a learning session or selects a learner; a learningSessionId alone never authorizes an MCP call.
| Binding | Transport | Server-side meaning |
|---|---|---|
| ChatGPT/App -> SkillPilot | OAuth code flow (client_secret_basic or pinned desktop CIMD + PKCE S256), then Authorization: Bearer |
Only the approved client profile can obtain and present an access token audience-restricted to the active MCP resource. |
| Chat -> Session -> Learner | learningSessionId as a required input on every domain MCP tool |
This active first-party session addresses exactly one authoritative learner state and communicationLocale. |
The permanent SkillPilot ID never leaves the backend in the MCP flow and never appears in chat messages, tool arguments, or tool responses.
8.2. Mandatory Launch Sequence, Renewal, and Diagnostic TTL
Every click on Start learning in the first-party WebGUI is an atomic launch operation:
- The WebGUI holds the selected SkillPilot ID, chosen
communicationLocale, and configured Level 2/Level 3 context. - It sends a single launch request to the SkillPilot backend.
- The backend atomically applies the typed launch context to the authoritative learner state.
- At that exact moment, it generates a fresh cryptographically random
learningSessionId. - It stores only the HMAC hash together with internal
learner_id,communication_locale,started_at, and absoluteexpires_at. - It returns a prepared bootstrap prompt and launch URL; the WebGUI opens the new chat pre-populated with that message:
Use SkillPilot Coach and continue.
learningSessionId: sps_<random opaque value>
- Every launch is independent: Every launch request mints a distinct
learningSessionId— even when the same learner restarts immediately, when an older session is still valid, when another learner shares the same ChatGPT account, or when OAuth is already connected. Concurrent sessions remain independently valid; usage never extendsexpires_at. - Renewal exclusively via first-party WebGUI: Without a current bootstrap message, the model calls zero SkillPilot tools and instead points succinctly in the user's language to
https://skillpilot.com/and the WebGUI launch flow. When a tool returnsSESSION_REQUIRED,SESSION_RENEWAL_REQUIRED, orSESSION_VERSION_UNAVAILABLE, the coach outputsinstructionverbatim (or the exact localized entry frominstructions), appendingstartUrlonly if not already included — with no domain teaching, no reuse of the old session, and no OAuth reconnection. - Gated first-party diagnostic TTL: Exclusively
POST /api/ui/learners/{skillpilotId}/openai/v1/launchmay accept optionaldiagnosticSessionTtlSecondswhenSKILLPILOT_OPENAI_COACH_V1_DIAGNOSTIC_SESSION_TTL_ENABLED=trueand the integer lies within3601..86400seconds (never exceedingPT24H). Shortened lifetimes (e.g.,3660s to observe the< PT1Hcutoff after 1 minute, or5400s for a 90-minute soak) apply solely to that single session; omitting the field immediately restoresPT24H.
8.3. MCP Validation Order, Action Horizon, and Forbidden Fallbacks
The model copies learningSessionId unchanged into every domain tool call as a plain required string field (keeping regex and length rules server-side so the LLM never fabricates tokens), while the connector sends Authorization: Bearer <token> outside model context. On every call, the backend validates in this exact 8-step order:
- valid OAuth access token;
- exact Resource/Audience (
https://mcp-openai.skillpilot.com/mcp) and required read or write scope; - present, syntactically valid
learningSessionId; - HMAC lookup in
openai_de_learning_session; - non-expired, non-revoked session status;
- authoritative learner resolution and availability of the pinned contract, workflow, and curriculum revisions;
- remaining lifetime
expiresAt >= now + PT1Hfor every read, write, or replay response (exactPT1His valid; belowPT1Hfails withSESSION_RENEWAL_REQUIRED); - on writes, check for an exact committed idempotent replay (identical tool name, canonically normalized arguments, identical
clientRequestId, available pinned versions, andcompletedStateVersion == current learner revision; if canonical learner state has since advanced, fail withSTATE_VERSION_CONFLICT).
Explicitly forbidden fallbacks: A missing or invalid learningSessionId must never fall back to an OAuth subject, the "most recent" or any other session of the learner, a pending launch, a SkillPilot ID typed in chat, or a session implicitly created during an OAuth callback. OAuth callbacks, token refreshes, and MCP reconnects never create, extend, replace, or reactivate a learning session. Shared household or classroom ChatGPT accounts therefore remain strictly isolated per launch.
8.4. Persistence Schema, OAuth/mTLS Hardening, and Error Behavior
Table openai_de_learning_session is the sole persistence boundary for short-lived session bindings (retiring earlier OAuth-subject primary keys):
| Column | Meaning |
|---|---|
token_hash |
HMAC hash of the issued learningSessionId (plaintext is never stored) |
learner_id |
Server-internal foreign key referencing the learner record |
communication_locale |
Launch-pinned locale governing backend payloads and visible coach replies |
started_at |
Timestamp of the confirmed Start learning action |
expires_at |
Absolute expiration (started_at + PT24H, or + diagnosticSessionTtlSeconds in gated tests) |
OAuth client secrets reside strictly in protected provider and server configuration (never in repositories, browser bundles, prompts, tool arguments, responses, or logs). Open Dynamic Client Registration (DCR) and unpinned CIMD documents are disabled; exact callback allowlists, PKCE S256, Resource/Audience, and scopes are enforced. In addition, the dedicated MCP vHost validates the OpenAI-managed client certificate (observe mode during initial integration passes missing certificates to OAuth while rejecting invalid ones; enforce mode requires the verified OpenAI chain, clientAuth EKU, and exact connector SAN; see OpenAI MCP Client Binding). mTLS never replaces OAuth client authentication or learningSessionId.
| Failure condition | Mandatory behavior |
|---|---|
| OAuth missing / invalid | Standard host OAuth re-authorization; creates zero learning sessions |
learningSessionId missing / unknown / tampered / expired / revoked |
SESSION_REQUIRED; coach outputs verbatim instruction (plus startUrl if absent) with zero domain teaching; fresh WebGUI launch + new chat |
Remaining lifetime < PT1H |
SESSION_RENEWAL_REQUIRED (exact PT1H remains valid); fresh WebGUI launch + new chat |
| Pinned workflow / curriculum revision unavailable | SESSION_VERSION_UNAVAILABLE; fail-closed, fresh WebGUI launch + new chat |
| Write scope missing | Reject before any partial domain mutation |
| Launch context cannot apply atomically / invalid diagnostic TTL | Reject launch request; issue zero learningSessionId |
8.5. Security Invariants and Automated Acceptance Criteria
The architecture enforces 17 security invariants: (1) server-only permanent SkillPilot ID, (2) random opaque session IDs stored solely as HMAC hashes, (3) absolute PT24H lifetime except gated 3601..86400 diagnostic launches, (4) zero sliding expiration on use, (5) PT1H action horizon for new operations, (6) identical PT1H and learner-revision guards for idempotent write replays, (7) fresh independent session per launch, (8) no learner selection via OAuth alone, (9) no MCP authorization via session alone, (10) mandatory learningSessionId on all domain tools, (11) zero OAuth-subject lookup or fallback, (12) unchanged skill-graph, frontier, and mastery semantics, (13) strict client-profile separation (client_secret_basic vs. pinned desktop CIMD with zero fallback), (14) secret-free code/UI/prompts/logs, (15) launch-fixed communicationLocale, (16) mandatory fresh get_skillpilot_context at the start of every learner turn with direct in-turn use of mutation successor states, and (17) first-party WebGUI ownership of permanent IDs, provider notices, and Level 2 configuration.
Automated tests must verify all 20 acceptance criteria:
- One UI launch click triggers exactly one backend launch request and opens ChatGPT.
- Every launch mints a distinct
learningSessionId, including repeated launches for the same learner. - Concurrent launches for different learners operate without interference over one OAuth connection.
- The bootstrap message carries exactly one
learningSessionIdand zero SkillPilot IDs. - Every domain MCP tool schema requires
learningSessionId. - A valid OAuth token without
learningSessionIdis rejected. - A valid
learningSessionIdwithout a valid OAuth token is rejected. - Unknown, tampered, revoked, or expired sessions fail closed with zero fallback.
- Successful tool calls resolve the learner solely from the explicit
learningSessionId. - Repeated tool usage leaves
expires_atunchanged. - Domain read and write scopes are enforced per tool.
- Existing goal, frontier, and mastery test suites remain green.
- German and English share the same V1 tool contract governed by
communicationLocale. - Token requests with missing/wrong secrets, foreign
client_id, unapproved callbacks, or wrong resources are rejected. - Authorization-server metadata advertises the active production profile without open DCR or unpinned public fallbacks.
- The diagnostic TTL field accepts
3601and86400when gated on, and rejects3600,86401, values abovePT24H, and any value when gated off. - Diagnostic launches (
3660/5400) affect only their own session; the next normal launch defaults back toPT24H. - Every learner turn starts with a successful context read (session errors block all domain prose), and mutations return authoritative successor state used directly in the same turn without redundant reads.
- The public V1 tool catalog exposes zero sessionless bootstrap, capability, curriculum, or personalization tools.
- Write replays below
PT1H, with unavailable pinned workflow/curriculum versions, or after canonical learner-revision advancement are rejected even when tool name, canonical arguments, andclientRequestIdmatch.