Follow-up from review on #401 (part of the hosted profile, #297).
Problem
The #306 import-linter contract (Tools read no process-global settings) catches settings imports from tool-reachable code, but not value reads off an object that already holds Settings. McpRuntime exposes the whole settings tree as a broad public property (McpRuntime.settings), and every tool can reach the runtime through its request context (tool_context binds it off the lifespan context). So a tool could read ctx.request_context.lifespan_context.settings.mcp.<anything> at call time — a process-global read — with no import edge, and lint-imports stays green.
This is documented as a known manual-review limit in packages/mcp/AGENTS.md ("Process-global configuration and the single-backend assumption"), but the surface itself remains.
Proposed change
Stop exposing McpRuntime.settings as a broad public property. Its only current consumer is server.py reading unified_envelope at build time — a startup read, not a per-request tool path. Narrowing the property (or replacing it with the specific value the composition root needs) removes the call-time read surface entirely, so the import contract becomes a complete guard rather than a partial one backed by manual review.
Acceptance criteria
McpRuntime no longer exposes the full Settings tree to anything reachable from a tool's request context.
- The
unified_envelope consumer in server.py still works (resolved at startup).
- The "known limit" caveat in the AGENTS.md process-global section can be removed once the read surface is gone.
Follow-up from review on #401 (part of the hosted profile, #297).
Problem
The #306 import-linter contract (
Tools read no process-global settings) catches settings imports from tool-reachable code, but not value reads off an object that already holdsSettings.McpRuntimeexposes the whole settings tree as a broad public property (McpRuntime.settings), and every tool can reach the runtime through its request context (tool_contextbinds it off the lifespan context). So a tool could readctx.request_context.lifespan_context.settings.mcp.<anything>at call time — a process-global read — with no import edge, andlint-importsstays green.This is documented as a known manual-review limit in
packages/mcp/AGENTS.md("Process-global configuration and the single-backend assumption"), but the surface itself remains.Proposed change
Stop exposing
McpRuntime.settingsas a broad public property. Its only current consumer isserver.pyreadingunified_envelopeat build time — a startup read, not a per-request tool path. Narrowing the property (or replacing it with the specific value the composition root needs) removes the call-time read surface entirely, so the import contract becomes a complete guard rather than a partial one backed by manual review.Acceptance criteria
McpRuntimeno longer exposes the fullSettingstree to anything reachable from a tool's request context.unified_envelopeconsumer inserver.pystill works (resolved at startup).