Skip to content

docs: MCP OAuth tri-mode implementation plan (PR-cycle, no-regression, SOLID) - #457

Open
seanhanca wants to merge 27 commits into
mainfrom
feat/mcp-oauth-tri-mode-plan
Open

docs: MCP OAuth tri-mode implementation plan (PR-cycle, no-regression, SOLID)#457
seanhanca wants to merge 27 commits into
mainfrom
feat/mcp-oauth-tri-mode-plan

Conversation

@seanhanca

Copy link
Copy Markdown
Contributor

Purpose

Adds MCP-OAUTH-TRIMODE-IMPL-PLAN.html — the PR-cycle implementation plan for MCP OAuth tri-mode auth. Docs only; no production code changes.

Three ways to connect: paste a Daydream sk_*, paste a pymthouse composite, or sign in via OAuth. All auth-mode selection lives in one new Auth Resolution Layer; inference, SDK, gateway and signer stay auth-agnostic (Bearer in → ResolvedCredential { plane, forwardBearer, metadata? } out).

Review verdict

Criterion Result Note
Complete PASS 17 PRs, each with id · repo · real files · flag+default · acceptance · self-review · e2e · prod verify · rollback · revert-safety
Consistent FIXED 5 drifts vs the design doc corrected; every code symbol re-verified against source
Regression-safe PASS 12-row live matrix + INV-1…INV-6 golden tests landing before any refactor
Not over-engineered PASS 11 explicit "deliberately NOT building" items; exactly 2 new flags; no new registry, no device flow, no runtime picker
Decoupled PASS resolveAuth() is the sole mode-selection site; sdkCall keeps its apiKey: string; 11 anti-patterns reviewers must block

The 17 PRs by track

  • Track 1 — invariants & resolution layer (storyboard-a3, no flag): PR-01 golden invariants · PR-02 resolveAuth() · PR-03 pmth-composite scheme (log-only)
  • Track 2 — OAuth head, inert (MCP_OAUTH_ENABLED unset): PR-04 PRM/AS metadata + cached-404 fix · PR-05 401 challenge · PR-06 oauth-store · PR-07 broker routes · PR-08 store lookup
  • Track 3 — mint providers (MCP_OAUTH_PROVIDER): PR-09 NaaP mint route · PR-10 naap provider · PR-15 pymthouse provider
  • Track 4 — LR / signer infra (simple-infra): PR-11 build-time guard · PR-12 isolated canary · PR-13 promotion
  • Track 5 — enablement: PR-14 web_ client · PR-16 connector docs · PR-17 revocation (deferred)

No-regression guarantee

Daydream sk_* → Daydream signer → BYOC orchestrator stays byte-identical. All new behavior is scoped to LR orchestrators and the pymthouse signer. Pinned by six golden invariants that land in PR-01 before any refactor:

  • INV-1 flag-OFF invariance — an mcp_at_*-shaped bearer with the flag unset behaves as an unrecognized opaque bearer
  • INV-2 sk_* byte-identity in → out
  • INV-3 no path emits mcp_at_* as forwardBearer; store miss ⇒ 401, never a fallthrough
  • INV-4 the resolution layer imports no HTTP client
  • INV-5 sdk-call.ts byte-unchanged across the plan
  • INV-6 authForRestProxy unchanged — OAuth never reaches the 10+ REST routes

Rows 1–4 of the 12-row matrix re-run on every merge in tracks 1–3. Deviation halts the plan rather than triggering a fix-forward.

Top risks

  1. R1 regression on the Daydream plane — low likelihood, critical impact; invariants land first, no PR touches key_routing.py
  2. R3 agent/implementation complexity — 17 PRs across 3 repos; one PR = one card, and editing an invariant test is an escalation tripwire
  3. R10 durable store — Storyboard had no KV; now funded, with AP-9 (in-memory map) and AP-11 (public blob) blocking
  4. R9 the cached 404.well-known serves its 404 immutable for 24h, so a flag flip would not reach clients; PR-04 fixes headers before any flip
  5. R6 credential leakage — minted keys never returned to clients; secret scan is a merge precondition on every PR

Owner decisions folded in (2026-08-08)

  • KV: provision it. Former blocker OA-6 resolved; PR-06→PR-08 un-gated. Provisioning is now task T-KV with acceptance criteria (encryption at rest, server-side TTL, per-user isolation) gating PR-06's merge.
  • Metering: de-scoped. Usage-API debit reads deferred as FU-1. Proof bar for a billed call is now signer 200 + numTickets in range + real asset URL.

Remaining open asks

  1. PYMTHOUSE_M2M_CLIENT_SECRET — still required. The supplied m2m_… value is a client id. Verified in code: createPymthouseApiKey() and the user-upsert both call readM2mAuthHeader(), which throws 503 without both halves. This blocks the mint path (PR-09/PR-10/PR-15). The metering de-scope lowers the verification bar but does not unblock these.
  2. T-KV — provision the KV and sign off its acceptance criteria.
  3. pymthouse web_ confidential client for Scenario B (public app_ clients cannot do authorization_code).
  4. LR gateway rebuild — deployed image lacks live_runner.py, so call_runner is missing.
  5. Signer session-payment verification — whether paygen supports repeated calls under one MCP session.

No secrets appear in the document or this PR.

Made with Cursor

seanhanca and others added 2 commits August 7, 2026 17:06
…ass 3)

The previous pass ended mid-document: sections 5-10 were missing while the
review verdict already claimed them. This writes the PR breakdown (17 PRs
across 5 tracks), the PR-cycle loop, the 12-row regression matrix, risks,
rollout with an explicit stop line, and the owner asks.

Two findings from verifying claims against source rather than assuming:

- The supplied pymthouse M2M value is a client id, not a secret. Both the
  composite mint and every usage/metering read use HTTP Basic id:secret and
  fail closed without both halves, so the client secret remains a hard
  blocker for the mint providers (OA-1).

- Storyboard has no durable KV; its only durable store is public blob
  storage. The oauth-store binds a billing credential, so neither an
  in-memory map nor blob is acceptable. Raised as blocker OA-6 and
  anti-pattern AP-11, gating phase 1 on a provisioning decision.

Regression stance is unchanged and now testable: Daydream sk_* to Daydream
signer to BYOC orchestrator stays byte-identical, pinned by INV-1..INV-6
landing before any refactor. No credentials appear in this document.

Co-authored-by: Cursor <cursoragent@cursor.com>
Two owner decisions, applied consistently across the plan.

KV (was blocker OA-6): a real private KV will be provisioned rather than
shipping P0-passthrough only. This un-gates PR-06 through PR-08 and all of
phase 1. Provisioning becomes owner task T-KV with acceptance criteria:
resource reachable from prod and preview, env wired with clean boot when
absent, encryption at rest, server-side TTL under 24h for token bindings,
per-user namespacing with no scan surface, and the credential never
returned to the client. Recommendation is Vercel KV / Upstash Redis for
native key TTL. AP-9 (in-memory map) and AP-11 (public blob) remain
blocking anti-patterns: funding the right answer does not license a
cheaper one. The stop line is demoted to a contingency.

Metering: usage-API debit reads are deferred as follow-up FU-1. The proof
bar for a billed call becomes signer 200, numTickets in range, and a real
asset URL. Verified against source that this does NOT unblock the mint
path: createPymthouseApiKey and the user-upsert both call
readM2mAuthHeader, which throws 503 without both the M2M client id and
secret, so PR-09/PR-10/PR-15 stay blocked. Only the verification bar moves;
the billed tail is testable solely because RS-1 supplied an already-minted
composite.

Daydream sk_* to Daydream signer to BYOC orchestrator remains byte-identical
and pinned by INV-1..INV-6. No credentials appear in this document.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel

vercel Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
naap-platform Ready Ready Preview Aug 9, 2026 4:52am

Request Review

@github-actions github-actions Bot added the size/XL Extra large PR (500+ lines) label Aug 8, 2026
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

⚠️ This PR is very large (2739 lines changed). Please split it into smaller, focused PRs if possible.

…entation

Found while implementing PR-01 against the real repo:

- The planned test path lib/mcp-server/__tests__/ would never run; the
  vitest config only includes tests/unit, tests/sim and the creative-kit
  package. Corrected to tests/unit/mcp-oauth-auth-invariants.test.ts.
- Clarified that storyboard-a3 is the local working-copy name while the
  GitHub remote is livepeer/storyboard, verified via git remote -v.
- Marked PR-01 as shipped (livepeer/storyboard#868).

Co-authored-by: Cursor <cursoragent@cursor.com>
Track 1 on storyboard main reject unbound mcp_at_* unconditionally; remove the
flag-OFF opaque-forward contradiction, mark PRs MERGED with SHAs, and require a
single worker for Track 2+ auth work.

Co-authored-by: Cursor <cursoragent@cursor.com>
@seanhanca

Copy link
Copy Markdown
Contributor Author

Doc sync: aligned plan with merged Track 1 Auth Resolution contract.

  • Unbound mcp_at_* is fail-closed by resolveAuth always (not gated on MCP_OAUTH_ENABLED); removed §7 row 9 / §4.2 flag-OFF opaque-forward contradiction.
  • Track 1 MERGED: #868→7e39673f, #869→788202b6, #870→667afd74.
  • PR-08 note: bound → composite when oauth-store lands; unbound still fail-closes.
  • Process: Track 2+ single worker on auth code (no parallel agents) to avoid R3 collision.

No Track 2 implementation in this push.

Record PR-04/PR-05 merge SHAs, PR-06 open behind T-KV, and the
project-scoped token limitation blocking Upstash Marketplace create.
Owner-provisioned store smoke-tested; PR-06 merge gate cleared.

Co-authored-by: Cursor <cursoragent@cursor.com>
seanhanca and others added 2 commits August 8, 2026 11:24
Record PR-06/07/08 merge SHAs, T-KV already signed off, PR-09 naap#458
open, and OA-1 secret received with NaaP Preview env-wire still pending.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
seanhanca and others added 2 commits August 8, 2026 11:28
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Grounded summary of Tracks 1–3 / T-KV: merged PR table with SHAs,
honest FULLY/PARTIALLY/NOT VERIFIED prod curl evidence, Preview env
wiring steps (names only), and user-story enablement boundaries.

Co-authored-by: Cursor <cursoragent@cursor.com>
@seanhanca

Copy link
Copy Markdown
Contributor Author

Added MCP-OAUTH-TRIMODE-STATUS.html — verified status + ops wiring guide (no secrets).

File: https://github.com/livepeer/naap/blob/feat/mcp-oauth-tri-mode-plan/MCP-OAUTH-TRIMODE-STATUS.html

STATUS + plan: PR-11/12/15/16 + NaaP login bridge merged; remaining work
is Preview env, web_ client, LR canary deploy, and deferred PR-13/17/FU-1.
@seanhanca

Copy link
Copy Markdown
Contributor Author

Implementation complete (code) — ops remaining

All IMPLEMENT NOW PRs from Tracks 4–5 + NaaP login bridge are merged. Prod MCP_OAUTH_ENABLED remains OFF (rechecked 2026-08-09: well-known 404 max-age=60; broker authorize 404).

PR Class Result
PR-11 build guard impl simple-infra#125ae0f3f5a
PR-12 canary overlay impl (config) simple-infra#1264a7bdb48deploy/e2e still ops
PR-13 promote LR deferred blocked on PR-12 green + OA-4
PR-14 web_ client ops only §4.6 in STATUS
PR-15 pymthouse provider impl storyboard#878245879a0
PR-16 connector docs impl storyboard#87937db2dac
PR-17 revocation deferred N11
NaaP login bridge impl naap#4597dde3685
FU-1 metering deferred unchanged

INV: storyboard main @ 37db2dac — INV + naap + pymthouse provider unit tests 34 passed.

User ops next (guides in MCP-OAUTH-TRIMODE-STATUS.html): §4.1–4.5 Preview mint/flag; §4.6 Scenario B web_; §4.7 LR canary deploy (owner). Do not set prod MCP_OAUTH_ENABLED=1.

STATUS + plan HTML updated on this branch.

Flag ON confirmed (AS/PRM 200); PKCE blocked on Storyboard client redirect
allowlist + NaaP login mcp_oauth_invalid. Tripwire: delete MCP_OAUTH_ENABLED.

Co-authored-by: Cursor <cursoragent@cursor.com>
@seanhanca

Copy link
Copy Markdown
Contributor Author

Scenario A Production flip probe (2026-08-09T03:05–03:07Z) — BLOCKED

Flip OFF now: delete Storyboard Production MCP_OAUTH_ENABLED → Redeploy (public broker is live until then).

Evidence

Check Result
AS / PRM /.well-known/… 200 (flag ON)
/api/mcp/oauth/authorize not 404; 400 redirect_uri not allow-listed for all probed client callbacks
NaaP /login?mcp_oauth=1&redirect_uri=https://storyboard.daydream.monster/api/mcp/oauth/callback 307error=mcp_oauth_invalid
POST /api/internal/mcp/mint wrong Bearer 401 unauthorized (mint wired)
Signer /healthz 200 OK
Unbound mcp_at_* 401 fail-closed
PKCE → token → create_media not run

One-line fixes (if you re-open the window)

  1. NaaP Production: MCP_INTERNAL_MINT_ALLOWLIST=https://storyboard.daydream.monster + MCP_OAUTH_REDIRECT_ALLOWLIST=https://storyboard.daydream.monster/api/mcp/oauth/callback → Redeploy (bridge must not return mcp_oauth_invalid).
  2. Storyboard Production: set MCP_OAUTH_REDIRECT_ALLOWLIST for the MCP client callback(s) (e.g. http://127.0.0.1:*,http://localhost:* and/or Claude) → Redeploy (authorize must 302, not 400).

Report: MCP-OAUTH-TRIMODE-E2E-REPORT.html (updated on feat/mcp-oauth-tri-mode-plan).

Scenario A re-probe: flag ON + NaaP allowlist OK; e2e still BLOCKED on
Storyboard MCP_OAUTH_REDIRECT_ALLOWLIST. Remind flip OFF after test.

Co-authored-by: Cursor <cursoragent@cursor.com>
@seanhanca

Copy link
Copy Markdown
Contributor Author

Prod host correction + Scenario A re-probe (2026-08-09T03:11Z)

Correct Production MCP/OAuth host: https://agent.livepeer.org (not storyboard.daydream.monster).

Probe results

Check Result
GET /.well-known/oauth-authorization-server 200 — flag ON, issuer https://agent.livepeer.org
GET /.well-known/oauth-protected-resource 200 — broker metadata live
GET /api/mcp/oauth/authorize (localhost / agent callback) 400 redirect_uri not allow-listed
NaaP login bridge redirect_uri=https://agent.livepeer.org/api/mcp/oauth/callback 307 + naap_mcp_oauth_pending cookie — not mcp_oauth_invalid (NaaP allowlist OK)
Mint wrong Bearer + X-Mcp-Caller-Origin: https://agent.livepeer.org 401 unauthorized (wired)
Unbound mcp_at_* 401 + resource_metadata=https://agent.livepeer.org/...
Signer /healthz 200

E2E status: BLOCKED (not GREEN)

One-line fix: on Storyboard Production set
MCP_OAUTH_REDIRECT_ALLOWLIST=http://127.0.0.1:*,http://localhost:* (+ real MCP client callbacks) → Redeploy → authorize must 302 to NaaP. Then re-run maximal Scenario A.

Corrected Storyboard Production env (copy-paste)

NAAP_MCP_ORIGIN=https://operator.livepeer.org
NAAP_MCP_MINT_URL=https://operator.livepeer.org/api/internal/mcp/mint
MCP_INTERNAL_MINT_SECRET=<SAME_AS_NAAP>
MCP_INTERNAL_MINT_CALLER_ORIGIN=https://agent.livepeer.org
MCP_OAUTH_PROVIDER=naap
MCP_OAUTH_BILLING_APP_ID=app_98575870d7ae33589a3f0660
MCP_OAUTH_ENABLED=1
MCP_OAUTH_REDIRECT_ALLOWLIST=http://127.0.0.1:*,http://localhost:*

(Var name MCP_OAUTH_REDIRECT_ALLOWLIST confirmed in Storyboard oauth-broker.ts — fail-closed if empty.)

NaaP Production already correct:
MCP_INTERNAL_MINT_ALLOWLIST=https://agent.livepeer.org
MCP_OAUTH_REDIRECT_ALLOWLIST=https://agent.livepeer.org/api/mcp/oauth/callback

Remind

Flip OFF after test — delete Production MCP_OAUTH_ENABLED + Redeploy (public broker is live while ON).

Docs updated on feat/mcp-oauth-tri-mode-plan (STATUS §9 + E2E report). No secrets committed.

…man SSO

Storyboard Production MCP_OAUTH_REDIRECT_ALLOWLIST is live: authorize PKCE
returns 302 to NaaP login (not 400) for localhost and agent.livepeer.org
callback. Login bridge still sets naap_mcp_oauth_pending. Playwright reached
GitHub OAuth login; no E2E session in agent env, so bound mcp_at_* /
create_media not proven. Flip OFF Production MCP_OAUTH_ENABLED now.

Co-authored-by: Cursor <cursoragent@cursor.com>
@seanhanca

Copy link
Copy Markdown
Contributor Author

Scenario A prod re-probe (allowlist fixed) — BLOCKED (not GREEN)

Window: 2026-08-09T03:16–03:18Z against https://agent.livepeer.org
Commit: 7309acf1 on feat/mcp-oauth-tri-mode-plan
Report: MCP-OAUTH-TRIMODE-E2E-REPORT.html

Evidence

Check Result
AS / PRM well-known 200 (flag ON)
Authorize PKCE (127.0.0.1 / localhost / agent callback) 302operator.livepeer.org/login?mcp_oauth=1… (prior 400 redirect allowlist FIXED)
NaaP login bridge + agent callback 307 + naap_mcp_oauth_pendingnot mcp_oauth_invalid
Evil redirect still mcp_oauth_invalid
Playwright reached NaaP login → GitHub OAuth page; human SSO required (no E2E_USER_* in agent env)
Mint wrong-Bearer 401
Signer /healthz 200
Unbound mcp_at_* 401 fail-closed
Bound session / create_media asset not run

Why not GREEN

Redirect allowlist is fixed and the broker head works through NaaP login. Full Scenario A proof bar (bound mcp_at_*create_media → real asset) still needs a human to complete Google/GitHub/email login, or E2E_USER_EMAIL / E2E_USER_PASSWORD provided in-agent.

Flip OFF now

Delete Production MCP_OAUTH_ENABLED on Storyboard (agent.livepeer.org) → Redeploy to close the public broker window — whether or not SSO is finished later.

Record 3× Google SSO → opaque callback access_denied, live curls, and
the Storyboard PR that returns a safe reason enum for the next retry.

Co-authored-by: Cursor <cursoragent@cursor.com>
@seanhanca

Copy link
Copy Markdown
Contributor Author

Scenario A prod update — opaque access_denied + storyboard reason ship

Host: https://agent.livepeer.org (flag ON: AS/PRM 200, authorize localhost PKCE → 302 NaaP login).

Human evidence: Google SSO succeeded; Storyboard callback returned {"error":"access_denied"} (400) with state=st_…&external_user_id=… three times. Prior analysis: NaaP mint was never called — throw is in Storyboard completeUpstreamCallback before fetch(mint) (state miss or pre-mint config). Fake-state curl reproduces the same opaque JSON (cannot distinguish yet).

Throw inventory (Storyboard broker complete path):

Failure Env / condition
invalid_or_expired_state Redis pending miss/TTL/one-shot (KV_REST_API_* or UPSTASH_REDIS_REST_*)
kv store unset → OauthStoreUnavailableError
identity missing external_user_id/code; or NAAP_MCP_ORIGIN / identity exchange
mint_config MCP_OAUTH_BILLING_APP_ID or PYMTHOUSE_PUBLIC_CLIENT_IDapp_98575870d7ae33589a3f0660; or missing NAAP_MCP_MINT_URL + MCP_INTERNAL_MINT_SECRET + MCP_INTERNAL_MINT_CALLER_ORIGIN
mint_http_<n> mint HTTP non-OK
mint_failed non-composite response

Shipped + merged: livepeer/storyboard#880 — callback now returns { error: "access_denied", reason: "<safe enum>" } + server log. Production redeploy in progress (probe still opaque at comment time).

E2E report updated + pushed on this branch (MCP-OAUTH-TRIMODE-E2E-REPORT.html / STATUS).

Next user action (ONE retry after prod shows reason)

  1. Confirm deploy: curl -sS 'https://agent.livepeer.org/api/mcp/oauth/callback?state=st_fake&external_user_id=u1' → must include "reason":"invalid_state".
  2. Fresh authorize (durable local PKCE listener) → Google/GitHub login → if fail, paste the new JSON body (with reason).
  3. Flip OFF reminder: when the window ends, delete Production MCP_OAUTH_ENABLED on Storyboard → Redeploy.

Vercel runtime logs: 403 from this worker (no project token) — reason enum unblocks without needing logs.

@seanhanca

Copy link
Copy Markdown
Contributor Author

Production reason enum is LIVE

Confirmed on agent.livepeer.org after storyboard#880 Production deploy (32db8d04):

{"error":"access_denied","reason":"invalid_state"}

(fake-state probe)

ONE retry URL (fresh pending, ~10 min TTL)

Start a local PKCE listener on 127.0.0.1:54321 first, then open:

https://operator.livepeer.org/login?mcp_oauth=1&state=st_7cqoNYP1jzAlmSUSTE1UXsZKjPr_H5Hqm8j_cdjXStc&redirect_uri=https%3A%2F%2Fagent.livepeer.org%2Fapi%2Fmcp%2Foauth%2Fcallback

Complete Google (or GitHub).

  • Success → browser hits http://127.0.0.1:54321/callback?code=mcp_ac_… — paste that URL.
  • Failure → paste the JSON body from the Storyboard callback tab (must include reason).

Flip OFF after window: delete Production MCP_OAUTH_ENABLED → Redeploy.

Co-authored-by: Cursor <cursoragent@cursor.com>
@seanhanca

Copy link
Copy Markdown
Contributor Author

Fresh retry ready (reason codes LIVE + durable listener)

Open now (~10 min Redis TTL):
https://operator.livepeer.org/login?mcp_oauth=1&state=st_1EZLZJVtjpR8XytZK4GUqDtI1PG8x6U7jzDr_tu9D2o&redirect_uri=https%3A%2F%2Fagent.livepeer.org%2Fapi%2Fmcp%2Foauth%2Fcallback

  • Local PKCE listener: http://127.0.0.1:54321/callback (running)
  • Pending state: st_1EZLZJVtjpR8XytZK4GUqDtI1PG8x6U7jzDr_tu9D2o
  • Authorize: https://agent.livepeer.org/api/mcp/oauth/authorize?response_type=code&client_id=cursor-e2e&redirect_uri=http%3A%2F%2F127.0.0.1%3A54321%2Fcallback&code_challenge=AUz2sVoWdnB1c_8cg2EBM3DfLZcf1I-6EEoyzC5OI7g&code_challenge_method=S256&state=H3sH0KwQlOJul5zgcS9laG0a1NGXEEJ3

Complete Google/GitHub once. Success → agent auto token-exchange + create_media. Failure → paste JSON with reason.

Flip OFF after window: delete Production MCP_OAUTH_ENABLED → Redeploy.

…retry

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@seanhanca

Copy link
Copy Markdown
Contributor Author

Fresh retry URL (re-minted — prior pending expired unused)

Open now:
https://operator.livepeer.org/login?mcp_oauth=1&state=st_v6x5cUAfR6yMqWPMo5zRt4elv5uW1h7SxpXd9-pcbcM&redirect_uri=https%3A%2F%2Fagent.livepeer.org%2Fapi%2Fmcp%2Foauth%2Fcallback

  • Listener: http://127.0.0.1:54321/callback (running, 20m wait)
  • Pending: st_v6x5cUAfR6yMqWPMo5zRt4elv5uW1h7SxpXd9-pcbcM
  • Do not reuse older login links.

On fail paste JSON with reason. Flip OFF after: delete Production MCP_OAUTH_ENABLED.

Record agent.livepeer.org SSO → mcp_ac → mcp_at token 200 evidence,
invalid_grant on code reuse, and Flip-OFF reminder. create_media asset
pending optional re-login after harness action=generate patch.

Co-authored-by: Cursor <cursoragent@cursor.com>
@seanhanca

Copy link
Copy Markdown
Contributor Author

Scenario A prod e2e update (agent.livepeer.org)

OAuth = GREEN (2026-08-09T04:46Z)

  • Host: https://agent.livepeer.org ↔ NaaP https://operator.livepeer.org · test app app_98575870…
  • Human SSO callback returned mcp_ac_* + matching client state
  • Token endpoint 200token_type=Bearer, expires_in=86400, access_token prefix mcp_at_261… (secret not logged)
  • MCP initialize with that bearer 200
  • Root-cause fix live: livepeer/storyboard#881 (d29e161f) — Upstash auto-deserialize broke takePending → false invalid_state

create_media asset: first tools/call failed with -32602 (local harness omitted required action). Re-exchange of the same one-shot code → 400 {"error":"invalid_grant"}. Optional re-login for asset proof still available; harness patched.

Flip OFF now: delete Storyboard Production MCP_OAUTH_ENABLED → Redeploy.

E2E report: MCP-OAUTH-TRIMODE-E2E-REPORT.html on feat/mcp-oauth-tri-mode-plan (7190d21c).

SSO → token → create_media returned a real fal image URL; remind Flip-OFF.

Co-authored-by: Cursor <cursoragent@cursor.com>
@seanhanca

Copy link
Copy Markdown
Contributor Author

Scenario A = GREEN end-to-end ✅

Flip OFF now: delete Storyboard Production MCP_OAUTH_ENABLED → Redeploy.

Evidence (2026-08-09T04:49:29Z, no secrets):

Hosts: agent.livepeer.orgoperator.livepeer.org · test app app_98575870…
Fix: storyboard#881 (d29e161f)

E2E report updated on feat/mcp-oauth-tri-mode-plan.

@seanhanca

Copy link
Copy Markdown
Contributor Author

Scenario A = FULL GREEN ✅

Flip OFF now: delete Storyboard Production MCP_OAUTH_ENABLED → Redeploy.

Step Result
SSO → Storyboard callback mcp_ac_* + matching client state (04:49:29Z)
Token POST /api/mcp/oauth/token 200 · Bearer · expires_in=86400 · prefix mcp_at_1xk…
MCP initialize 200
create_media action=generate (flux-schnell) 200 · ~$0.003

Asset: https://v3b.fal.media/files/b/0aa59a49/OPfu9uKMx82zolqCboa2J.jpg

Hosts: agent.livepeer.orgoperator.livepeer.org · test app app_98575870…
Fix: storyboard#881 (d29e161f)
E2E report: MCP-OAUTH-TRIMODE-E2E-REPORT.html on feat/mcp-oauth-tri-mode-plan (288b46ba)

@github-actions

Copy link
Copy Markdown

This PR has been inactive for 7 days. It will be closed in 5 days if no activity occurs. Feel free to reopen anytime.

@github-actions github-actions Bot added the stale No recent activity label Aug 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XL Extra large PR (500+ lines) stale No recent activity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant