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-zhuang os-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

vercel Bot commented Jul 18, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 18, 2026 7:44am

Request Review

@github-actions github-actions Bot 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-actions github-actions Bot added the dependencies Pull requests that update a dependency file label Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

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

@os-zhuang os-zhuang closed this Jul 18, 2026
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 free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/m tests

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