Skip to content

docs(adr): ADR-0100 — serve-side MCP default wiring; register the MCP tool-execution surface in the authz matrix (#3167) - #3182

Closed
os-zhuang wants to merge 2 commits into
mainfrom
claude/mcp-server-default-wiring-ia5erh
Closed

docs(adr): ADR-0100 — serve-side MCP default wiring; register the MCP tool-execution surface in the authz matrix (#3167)#3182
os-zhuang wants to merge 2 commits into
mainfrom
claude/mcp-server-default-wiring-ia5erh

Conversation

@os-zhuang

@os-zhuangos-zhuang commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Decision-issue response to #3167 (default wiring for the serve-side MCP server — identity admission first). #3167 makes decision #1 (identity admission) a hard gate: "nothing else in this issue proceeds before it." This PR lands the decision record and that gate, with an end-to-end proof.

Two findings that shaped the response

  1. The issue's premise is stale against main.decision: default wiring for the serve-side MCP server (@objectstack/mcp) in dev — identity admission first #3167 says MCPServerPlugin is "wired nowhere by default." Verified against main, MCP is already a default-on core capability: isMcpServerEnabled() defaults ON (packages/types/src/env.ts), packages/cli/src/commands/serve.ts pushes the mcp capability into os serve/os dev unless opted out, and the dispatcher route gate reads the same helper. The HTTP tool surface is also already fail-closed on identityhandleMcp returns 401 for a principal-less caller (no dev-admin fallback, no fall-open), and buildMcpBridge threads the caller ExecutionContext into callData(..., ec) on every verb (OAuth callers run at the ADR-0090 D10 ceiling∩user intersection).

  2. What was genuinely missing is exactly the gate. The MCP tool-execution surface had no row in the ADR-0096 D4 authz conformance matrix — so "whose ExecutionContext runs a tools/call" lived only in ADR-0096 prose, not as a checked, red-CI-on-regression property.

What this lands

  • docs/adr/0100-serve-side-mcp-default-wiring-identity-admission.md — the four decisions grounded in the verified state:
    • D1 identity admission — caller-principal (user) posture, fail-closed, registered as the gate.
    • D2 the dev/prod boundary is identity + the off-switch (OS_MCP_SERVER_ENABLED=false gates registration and route), not a code-level default flip (an opt-in-for-prod amendment is documented for ratification).
    • D3 HTTP-only default; stdio stays explicit opt-in (os dev owns the process; the stdio resource path carries the ADR-0096 E1 context-less read).
    • D4 bounded default exposure — fixed generic tool set over apiEnabled objects, sys_* fail-closed, actions only when ai.exposed; metadata-declared exposure is the future story.
  • authz-conformance.matrix.ts — two rows: enforced mcp-tool-exec-identity (fail-closed 401 gate + buildMcpBridge threading + OAuth scope gate; now carrying a real dogfood proof and marked high-risk) and experimental mcp-stdio-resource-read classifying the ADR-0096 E1 context-less stdio resource read (opt-in, never networked) with its admission requirement — registered honestly, not patched with a discouraged { isSystem: true } literal (ADR-0096 D2 retires those; the systemContext(reason) constructor doesn't exist yet).
  • authz-conformance.test.ts — three source probes pinning both halves of the HTTP admission and the stdio fall-open, so dropping the gate/threading or networking the stdio read trips CI (STALE / UNCLASSIFIED) instead of surfacing in an adversarial review later.
  • showcase-mcp-identity.dogfood.test.ts — the end-to-end proof: boots the showcase with MCPServerPlugin registered and asserts on the real HTTP stack that anonymous POST /mcp401 (not 404/501 — the surface is served and the service is registered; the denial is identity), an authenticated member clears the gate, and GET /mcp/skill200 (served default-on while tool-exec is gated).

Scope / safety

Docs + a private test package (@objectstack/dogfood) only — no runtime behavior change, no changeset. The identity path, OAuth admission (#2698), and ai.exposed + capability action gate are pre-existing and unchanged; this PR registers and proves them. (@objectstack/mcp added to the dogfood dev deps; lockfile updated.)

Validation

Validated locally: pnpm --filter @objectstack/dogfood test for authz-conformance + showcase-mcp-identity10/10 pass (7 conformance ratchet + 3 dogfood-boot). The three new probes each match their source anchor exactly once and are a clean bijection with the two rows' covers; the proof:-exists and high-risk assertions pass.

Open for ratification

  • D2: keep default-on-everywhere behind the off-switch (recommended), or adopt the NODE_ENV-aware opt-in-for-prod amendment.

Closes#3167 pending ratification of D2.

🤖 Generated with Claude Code

… tool-execution surface in the authz matrix (#3167)
#3167 is a decision issue whose #1 (identity admission) is the hard gate:
"nothing else proceeds before it." Two findings shaped the response:
1. The issue's premise — "MCPServerPlugin is wired nowhere by default" — is
stale against main. MCP is already a default-on core capability
(isMcpServerEnabled() default true + serve.ts capability push + the
matching route gate), and the HTTP tool surface is already fail-closed on
identity (handleMcp 401 for a principal-less caller; buildMcpBridge threads
the caller ExecutionContext into callData on every verb; OAuth callers run
at the ADR-0090 D10 ceiling∩user intersection).
2. What was genuinely missing is exactly the gate: the MCP tool-execution
surface had NO row in the ADR-0096 D4 authz conformance matrix, so "whose
ExecutionContext runs a tools/call" lived only in ADR-0096 prose, not as a
checked, red-CI-on-regression property.
This lands:
- ADR-0100 recording the four decisions grounded in the verified state:
D1 identity admission (caller-principal posture, registered as the gate),
D2 dev/prod boundary is identity + off-switch not a default flip,
D3 HTTP-only default with stdio kept explicit-opt-in,
D4 bounded default exposure (apiEnabled + ai.exposed).
- Two authz-conformance rows: an enforced `mcp-tool-exec-identity` (fail-closed
gate + bridge threading) and an experimental `mcp-stdio-resource-read`
classifying the ADR-0096 E1 context-less stdio resource read (opt-in,
never networked) with its admission requirement — registered honestly, not
patched with a discouraged { isSystem: true } literal.
- Three source probes pinning both halves of the HTTP admission and the stdio
fall-open, so dropping the gate/threading or networking the stdio read
trips CI (STALE/UNCLASSIFIED) instead of being an adversarial-review finding.
Test-only + docs (packages/qa/dogfood is private) — no runtime behavior change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155A3k1CMJQb44V4iwTLUcH
@vercel

vercelBot commented Jul 18, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJul 18, 2026 7:44am

Request Review

@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation tests labels Jul 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): packages/qa.

2 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/permissions/authorization.mdx(via packages/qa)
  • content/docs/permissions/delegated-administration.mdx(via packages/qa)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…3167)
Upgrades the `mcp-tool-exec-identity` conformance row from cited-tests-only to
a real end-to-end boundary proof (ADR-0096 D4 requires one for an agent/
external-reachable surface). `showcase-mcp-identity.dogfood.test.ts` boots the
showcase with MCPServerPlugin registered (via the harness `extraPlugins` — the
verify harness boots the plugin set directly, not through the CLI capability
push) and asserts on the real in-process HTTP stack:
- anonymous POST /mcp (tools/list) → 401 (fail-closed: not 404/501, so the
surface IS served and the service IS registered — the denial is identity);
- an authenticated member clears the gate (not 401);
- GET /mcp/skill → 200 (surface served default-on while tool-exec is gated).
Wires it in as the row's `proof:` and adds the row to the test's HIGH_RISK set
(so the surface must carry a proof). Adds @objectstack/mcp to the private
@objectstack/dogfood dev deps (lockfile updated).
Validated locally: `pnpm --filter @objectstack/dogfood test` authz-conformance
+ showcase-mcp-identity → 10/10 pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155A3k1CMJQb44V4iwTLUcH
@github-actionsgithub-actionsBot added the dependencies Pull requests that update a dependency file label Jul 18, 2026
@os-zhuangClaude

Copy link
Copy Markdown
ContributorAuthor

Superseded by #3202, which already registered the MCP execution-surface identity admission in the ADR-0096 matrix (mcp-http-identity + mcp-stdio-authority rows + probes) for #3167. Closing as redundant — one of the probe keys is identical to #3202's, so this branch would double-classify the ledger. No separate decision ADR needed.


Generated by Claude Code

akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 3, 2026
…-ai#4576)
* feat(agents): pm-dispatch escalation bar — obvious fixes ship, the maintainer gets a veto window, not a permission gate
Maintainer feedback after objectstack-ai#3182 sat waiting for confirmation: obvious
problems should just be fixed. Escalation now requires genuine product/
contract divergence with no repo-norm answer, or destructive/irreversible
action. Named non-escalation classes act immediately: restore-invariant
fixes (dual-version dependency graphs, inert tripwires, unwired gates),
sequencing, verification strategy, and dev needs_decision reports that
fall into those classes (the PM answers the dev directly). Also records
the GitHub body-sanitizer generics trap in the os-dev definition.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012C2cd7tL8QDoZ2QKN3djJ5
* feat(agents): resource discipline for parallel dev agents in one container
Maintainer-reported OOM under parallel dispatch. Memory peaks are the
build/test phases, so heavy phases serialize on a container-wide flock
(/tmp/os-heavy-verify.lock) while editing stays parallel; plus heap caps
(NODE_OPTIONS max-old-space-size), scoped --filter builds/tests, capped
vitest/turbo workers, and worktree cleanup after the PR is up. PM-side:
batch:2 or mode:cloud for build-heavy tasks; OOM-killed agents redispatch
alone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012C2cd7tL8QDoZ2QKN3djJ5
* feat(agents): pm-dispatch scaling — repo sharding for multi-PM, multi-account claims, batch:5 operating point
- Multiple PM sessions shard by whole repo, never share one queue (batch
independence is only checked within one PM's view; the merge queue is
one lane regardless). Scaling order: bigger batch + mode:cloud first,
repo shard second, same-queue multi-PM prohibited.
- Multiple GitHub accounts (colleagues' Claude Code) simplify claiming:
assignee alone disambiguates across accounts; the claim-comment ritual
is for sessions within one account. Partition by repo/label with a
recorded assignment table.
- batch:5 recorded as the maintainer's operating point, riding on the
container resource discipline.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012C2cd7tL8QDoZ2QKN3djJ5
---------
Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 3, 2026
* feat(agents): pm-dispatch backlog sweep duty + stale-premise triage checks + PID-only process discipline
- Step 0: classifying the unlabeled backlog is a standing PM duty —
auto-queue concrete/no-risk work, flag design cards and programs for
the maintainer, request repair for sanitizer-truncated bodies.
- Fetch step now requires reading issue comments and running a
stale-premise check against recent main history before dispatch
(three same-day cases: objectstack-ai#4525, objectstack-ai#4379, objectstack-ai#4075 step 1).
- os-dev resource discipline: never kill by process name; PID-scoped
operations only (from the objectstack-ai#3182 agent's pkill disclosure).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012C2cd7tL8QDoZ2QKN3djJ5
* feat(agents): pm-dispatch cross-shard transfer protocol — work crosses shards via the target queue, PMs never do
A sharded PM whose task needs a change in another shard's repo files it
into the target repo's queue (pm:queue + source line) instead of
dispatching across the line; dependencies via Blocked-by, follow-up
chores belong to the consuming shard, and anything touching
packages/spec transfers to the main-backlog PM. The queue label is the
inter-PM channel — no direct PM-to-PM coordination needed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012C2cd7tL8QDoZ2QKN3djJ5
* feat(agents): pm-dispatch shard ownership registry — caretaker mode is explicit, never assumed
A registry issue in the main backlog records which session owns which
shard; taking over or handing off a shard is a comment there. Unowned
shards may be caretaken by the main-backlog PM, but a registered shard
ends caretaking immediately — in-flight claimed tasks finish under their
claimant, everything else belongs to the new owner. Claim comments state
the mode so registry and claims never disagree silently.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012C2cd7tL8QDoZ2QKN3djJ5
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationsize/mtests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

decision: default wiring for the serve-side MCP server (@objectstack/mcp) in dev — identity admission first

2 participants

@os-zhuang@claude