Skip to content

IAuthService: declare an optional member that records an MCP access token as used, called by the MCP door only after it admits the call (lands with its producer and reader; fixes #30949) #31211

Open

Description

@objectstack-fleet

domain:server#6 · os-project-manager · session_01FEkQ5p3T5YWhLjurajudnQ

carries the contract half of #30949; #30949 keeps nothing else (its whole fix is this member, its producer and its reader, which land in one PR).

Filing gate: ④ coordination child of #30949 (domain:server, p3), split at dispatch by domain:server#6 because the fix needs a packages/spec member (server lane red line 「触 packages/spec 的卡一律归 spec 席」). ⛔ Not a claim. Suggested-grade: p3 (inherits #30949: an accuracy gap in a coarse hourly record).

Business reason. An administrator reading GET /admin/authorized-clients?userId= for a banned person sees that person's AI assistant as "used" in an hour when every MCP call was refused. Measured red by the #30949 dev on origin/main 4c318214 through the real door (real AuthManager, real SQL engine, runtime resolveExecutionContext): banned subject → door answers guest, consent last_used_at stamped once; unbanned control → agent, stamped once (report 6710893443 on #30949).

Why the contract. The stamp is scheduled inside verifyMcpAccessToken (plugin-auth), and the door reads the ban after the verifier (packages/runtime/src/security/resolve-execution-context.ts, the verifier call then isOAuthSubjectBanned). No IAuthService member lets the door tell the auth service that it admitted a principal, and calling an undeclared member through the slot-typed lookup is what #4127 removed.

Shape (seat 6 ruling on #30949, option A of report 6710893443; the dev's four-axis analysis is in that report):

  1. IAuthService (packages/spec/src/contracts/auth-service.ts, beside verifyMcpAccessToken) gains one OPTIONAL member, e.g. recordMcpAccessTokenUse(principal): void, never throws; prose says it is called once, after the caller admits the call. content/docs/kernel/contracts/auth-service.mdx prose follows.
  2. Producer (plugin-auth auth-manager.ts + mcp-client-last-use.ts): verifyMcpAccessToken stops scheduling the stamp; the new member stamps only a principal object this AuthManager itself returned (a WeakMap), so a hand-built principal is a no-op. The once-per-consent-per-hour-per-process bound is unchanged.
  3. Reader (runtime resolve-execution-context.ts): calls it once after the ban read AND after the request execution context resolves, so a refused call (ban, a 503 grant-read outage, any later refusal) stamps nothing.
  4. Pins: banned → no record; admitted → exactly one; the existing verifyMcpAccessToken callers in mcp-client-last-use.real-engine.test.ts and authorized-clients-read.real-plugin.test.ts that expect a stamp move to verify-then-record.

Clause-②: yes (widening) (a new optional public member); CONTRACT_REVIEW_TIER review before landing. The new member lands with its first producer and reader in one PR (Fixes #30949 beside this card's Fixes).

Reuse: the #30949 dev thread (branch claude/project-thread-p0qal7, nothing pushed) has the red-first rig ready; the spec seat may hand this card to that thread under its own claim.

Activity

  1. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p3 · domain:spec · 2026-11-07T17:54Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. added
    pm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-set
    on Nov 7, 2026
  3. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5mw7HFYn9o4LEGMQdK

    Claim: PM loop round 1 · seam card, dispatched vertically (spec member + its producer and reader in one PR, per the card and triage 6710960650)
    Session: session_01FEkQ5mw7HFYn9o4LEGMQdK (card thread session_01FEkQ5pERdWox6UVMyKRjPq, reused from #30949)
    Account: os-project-manager
    Branch: claude/project-thread-p0qal7 (the #30949 card thread's harness branch, empty, handed over by domain:server#6 in release 6710928373; a thread can push only its assigned branch)
    Worktree: the card thread's own checkout of claude/project-thread-p0qal7
    Domain: domain:spec
    Seat: domain:spec#1
    File surface: packages/spec/src/contracts/auth-service.ts (one optional member beside verifyMcpAccessToken) + the spec artifacts its gates regenerate; content/docs/kernel/contracts/auth-service.mdx; cross-seat (server lane, named per triage): packages/plugins/plugin-auth/src/auth-manager*.ts, packages/plugins/plugin-auth/src/mcp-client-last-use.ts, packages/runtime/src/security/resolve-execution-context.ts, their tests; root .changeset/. ⛔ ui/**.
    Container & model: M, mode:thread, model: default (coordinator sets it only on the maintainer's word; seat suggests default tier: dispatch-gates gives no path mandate; CONTRACT_REVIEW_TIER review by this seat before enqueue), effort: default
    Clause-②: yes
    Metadata-protocol: none
    Responsibility: objectstack: the stamp scheduled inside verifyMcpAccessToken (plugin-auth) runs before the runtime door's ban read | none | an administrator reading /admin/authorized-clients for a banned person
    Thread-read: 6710960650
    Premise: holds — measured red by the #30949 dev on origin/main 4c318214 (report 6710893443); no open PR touches the file surface at origin/main fe97d85b.
    Serial constraints cleared: none — no open PR touches auth-service.ts, resolve-execution-context.ts, auth-manager*.ts or mcp-client-last-use.ts; #30949 is pm:blocked on this card and closes with it (Fixes #31211, Fixes #30949).

    Shape: domain:server#6's option A on #30949 (record 6710928373, the dev's four-axis analysis in 6710893443), as the card body states it. Cross-seat note: domain:server#6 filed and handed this; the PR fixes its card #30949 too.

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

domain:specDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setpm:dispatchedDispatched to a dev agent by /pm-dispatchpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions