Decision issue — direction first, implementation split later. Counterpart of the consume-side work that closed #2977/#3056: those made ObjectStack call external MCP servers; this is about ObjectStack being one.
Current state (verified against main)
An odd half-wired shape:
- The runtime dispatcher already serves an MCP HTTP surface per-request at
/api/v1/mcp, default-ON (OS_MCP_SERVER_ENABLED=false opts out), plus the public GET /api/v1/mcp/skill SKILL.md download and a connect UI (packages/mcp/src/connect-ui.ts).
- But the service behind it —
MCPServerPlugin (registers the 'mcp' service; bridges ToolRegistry, MetadataService, DataEngine, and Agents; optionally starts a long-lived stdio transport via autoStart / OS_MCP_SERVER_ENABLED=true) — is wired nowhere by default: not in os dev/serve, not in the showcase, not in the scaffold. Its only in-repo consumer is an app-todo e2e.
Net effect: an AI-native platform whose official "let AI operate the running app" channel is a route that answers but a capability that is never registered. Discoverability is zero exactly where the platform's pitch is strongest — the dev loop, where a coding agent (Claude Code, Cursor, any MCP client) could read the schema, inspect seed data, and exercise actions of the app it is building.
Proposed direction (to be ratified or amended)
Dev default-on, prod explicit. os dev registers MCPServerPlugin by default (escape hatch: OS_MCP_SERVER_ENABLED=false); os serve/production keeps today's explicit opt-in. The value asymmetry is stark: in dev the MCP endpoint completes the AI-builds-apps loop; in prod it is a high-authority execution surface that must be a deliberate choice.
The four decisions to make (in order)
1. Identity admission — hard precondition (ADR-0096)
MCP tool execution is an execution surface, the same class as GraphQL and realtime, which ADR-0096 registered in the authz conformance matrix (packages/qa/dogfood/test/authz-conformance.matrix.ts) with CI tripwires. Today the matrix covers MCP only incidentally (the data-write ingress readonly strip names the MCP dispatcher as one funneled caller); there is no row for the MCP tool-execution principal: when an MCP client calls tools/call, whose ExecutionContext runs it? Dev-admin? A token-resolved user? An explicit guest? Fall-open on a missing principal = full authority — the exact failure mode ADR-0096 exists to prevent. Registering the row (and the answer) is the gate for any default-on wiring; nothing else in this issue proceeds before it.
2. The dev/prod boundary definition
What is "dev" for this purpose — the os dev command itself (recommended: wiring follows the command, not the env), NODE_ENV, or a config flag? And the off-switch semantics (OS_MCP_SERVER_ENABLED=false today disables the HTTP surface — should it also gate the plugin registration?).
3. Transport & mount shape
The per-request HTTP surface already exists; the open questions are (a) whether dev auto-start should also cover the stdio transport (local AI tools spawning the process directly — likely no: os dev owns the process), and (b) whether the connect UI / skill download need dev-specific affordances (e.g. printing the MCP URL + connect instructions on dev boot, which the dispatcher's skill/connect-ui pieces suggest was the intent).
4. Default exposure scope
Auto-generating tools for every object's CRUD explodes the tool list on real apps (hundreds of tools makes MCP clients unusable) and widens the misuse surface. Decide the default: e.g. only apiEnabled objects + explicitly marked actions/flows, with a metadata-declared exposure config as the future story (consistent with the ADR-0097 metadata-first philosophy — what is exposed should itself be authorable).
Optional extension (after the above)
With a stable in-repo serve-side endpoint, the showcase's declarative provider: 'mcp' connector gains a second demo target: the platform connecting to itself. Previously rejected for boot-ordering (automation start() runs before HTTP listens); the #3049 degrade+retry makes the self-connection heal seconds after boot, but the dogfood gate must not become timing-sensitive — treat as a separate, carefully-gated follow-up.
Refs
Decision issue — direction first, implementation split later. Counterpart of the consume-side work that closed #2977/#3056: those made ObjectStack call external MCP servers; this is about ObjectStack being one.
Current state (verified against main)
An odd half-wired shape:
/api/v1/mcp, default-ON (OS_MCP_SERVER_ENABLED=falseopts out), plus the publicGET /api/v1/mcp/skillSKILL.md download and a connect UI (packages/mcp/src/connect-ui.ts).MCPServerPlugin(registers the'mcp'service; bridges ToolRegistry, MetadataService, DataEngine, and Agents; optionally starts a long-lived stdio transport viaautoStart/OS_MCP_SERVER_ENABLED=true) — is wired nowhere by default: not inos dev/serve, not in the showcase, not in the scaffold. Its only in-repo consumer is anapp-todoe2e.Net effect: an AI-native platform whose official "let AI operate the running app" channel is a route that answers but a capability that is never registered. Discoverability is zero exactly where the platform's pitch is strongest — the dev loop, where a coding agent (Claude Code, Cursor, any MCP client) could read the schema, inspect seed data, and exercise actions of the app it is building.
Proposed direction (to be ratified or amended)
Dev default-on, prod explicit.
os devregistersMCPServerPluginby default (escape hatch:OS_MCP_SERVER_ENABLED=false);os serve/production keeps today's explicit opt-in. The value asymmetry is stark: in dev the MCP endpoint completes the AI-builds-apps loop; in prod it is a high-authority execution surface that must be a deliberate choice.The four decisions to make (in order)
1. Identity admission — hard precondition (ADR-0096)
MCP tool execution is an execution surface, the same class as GraphQL and realtime, which ADR-0096 registered in the authz conformance matrix (
packages/qa/dogfood/test/authz-conformance.matrix.ts) with CI tripwires. Today the matrix covers MCP only incidentally (the data-write ingress readonly strip names the MCP dispatcher as one funneled caller); there is no row for the MCP tool-execution principal: when an MCP client callstools/call, whoseExecutionContextruns it? Dev-admin? A token-resolved user? An explicit guest? Fall-open on a missing principal = full authority — the exact failure mode ADR-0096 exists to prevent. Registering the row (and the answer) is the gate for any default-on wiring; nothing else in this issue proceeds before it.2. The dev/prod boundary definition
What is "dev" for this purpose — the
os devcommand itself (recommended: wiring follows the command, not the env),NODE_ENV, or a config flag? And the off-switch semantics (OS_MCP_SERVER_ENABLED=falsetoday disables the HTTP surface — should it also gate the plugin registration?).3. Transport & mount shape
The per-request HTTP surface already exists; the open questions are (a) whether dev auto-start should also cover the stdio transport (local AI tools spawning the process directly — likely no:
os devowns the process), and (b) whether the connect UI / skill download need dev-specific affordances (e.g. printing the MCP URL + connect instructions on dev boot, which the dispatcher's skill/connect-ui pieces suggest was the intent).4. Default exposure scope
Auto-generating tools for every object's CRUD explodes the tool list on real apps (hundreds of tools makes MCP clients unusable) and widens the misuse surface. Decide the default: e.g. only
apiEnabledobjects + explicitly marked actions/flows, with a metadata-declared exposure config as the future story (consistent with the ADR-0097 metadata-first philosophy — what is exposed should itself be authorable).Optional extension (after the above)
With a stable in-repo serve-side endpoint, the showcase's declarative
provider: 'mcp'connector gains a second demo target: the platform connecting to itself. Previously rejected for boot-ordering (automationstart()runs before HTTP listens); the #3049 degrade+retry makes the self-connection heal seconds after boot, but the dogfood gate must not become timing-sensitive — treat as a separate, carefully-gated follow-up.Refs
provider-boundconnectors:materialized by generic executors) #2977 / feat: default preset includes the three generic connector executors (rest / openapi / mcp) — blocked by #3055 #3056 (consume side, closed), ADR-0024 §4 (trust model)packages/mcp(MCPServerPlugin,MCPServerRuntime, connect-ui, skill),packages/runtime/src/http-dispatcher.ts(/mcproutes)