Found by the independent Round 2 critic while re-verifying PR #276 (2026-09-03 Dream Cycle, dream/2026-09-03-security-adversarial).
Finding
packages/create-agent-harness/src/threat-model.ts had a narrow mcpInUse check that missed .claude/settings.json's mcpServers key (only recognized .harness/mcp-policy.json and .mcp.json) — fixed in #276 by routing detection through mcp-scan.ts's scanMcp().mcpEnabled, which now ORs all three surfaces.
Two other call sites have the identical bug and were not touched by #276, since they don't call scanMcp() at all:
packages/create-agent-harness/src/score.ts — scoreMcpSafety()'s hasMcp = policy != null || fileExists(dir, '.mcp.json')
packages/create-agent-harness/src/oia-manifest.ts — readHarnessProfile()'s hasMcp = mcpPolicy != null || existsSync('.mcp.json')
Reproduced live by the critic against a .claude/settings.json-mcpServers-only fixture (real built CLI, not mocks):
harness score --json → mcpRisk: "None" (scored as the safest possible posture — false negative)
harness oia-manifest --json → adjacentStandards.mcp.mode: "off", security.implementation: "ADR-022 (MCP off)" (also a false negative)
Both are security-relevant: score.ts feeds publish-readiness scoring, oia-manifest.ts feeds the ADR-034 cross-cutting manifest layer.
Suggested fix
Same mechanical pattern as #276: swap each hasMcp/mcpPolicy != null || existsSync('.mcp.json') expression for scanMcp(dir).mcpEnabled. Neither file imports mcp-scan.ts today, so there's no circular-import obstacle. Small, one-conceptual-change diff — good candidate for a future security-adversarial night, following #276's own precedent (2026-08-28's PR #243 flagged the original threat-model.ts gap; #276 closed it two nights later).
Also noted (lower priority, likely separate scope)
ADR-022 (docs/adrs/ADR-022-mcp-primitive.md) names additional per-host MCP registration surfaces (.codex/config.toml, .openclaw/openclaw.json, Hermes optional-mcps/<name>.json) that scanMcp()/threat-model.ts don't check at all — a pre-existing, architecturally larger gap (different permission-string formats per host), not introduced or regressed by #276.
References
Found by the independent Round 2 critic while re-verifying PR #276 (2026-09-03 Dream Cycle,
dream/2026-09-03-security-adversarial).Finding
packages/create-agent-harness/src/threat-model.tshad a narrowmcpInUsecheck that missed.claude/settings.json'smcpServerskey (only recognized.harness/mcp-policy.jsonand.mcp.json) — fixed in #276 by routing detection throughmcp-scan.ts'sscanMcp().mcpEnabled, which now ORs all three surfaces.Two other call sites have the identical bug and were not touched by #276, since they don't call
scanMcp()at all:packages/create-agent-harness/src/score.ts—scoreMcpSafety()'shasMcp = policy != null || fileExists(dir, '.mcp.json')packages/create-agent-harness/src/oia-manifest.ts—readHarnessProfile()'shasMcp = mcpPolicy != null || existsSync('.mcp.json')Reproduced live by the critic against a
.claude/settings.json-mcpServers-only fixture (real built CLI, not mocks):harness score --json→mcpRisk: "None"(scored as the safest possible posture — false negative)harness oia-manifest --json→adjacentStandards.mcp.mode: "off",security.implementation: "ADR-022 (MCP off)"(also a false negative)Both are security-relevant:
score.tsfeeds publish-readiness scoring,oia-manifest.tsfeeds the ADR-034 cross-cutting manifest layer.Suggested fix
Same mechanical pattern as #276: swap each
hasMcp/mcpPolicy != null || existsSync('.mcp.json')expression forscanMcp(dir).mcpEnabled. Neither file importsmcp-scan.tstoday, so there's no circular-import obstacle. Small, one-conceptual-change diff — good candidate for a futuresecurity-adversarialnight, following #276's own precedent (2026-08-28's PR #243 flagged the originalthreat-model.tsgap; #276 closed it two nights later).Also noted (lower priority, likely separate scope)
ADR-022 (
docs/adrs/ADR-022-mcp-primitive.md) names additional per-host MCP registration surfaces (.codex/config.toml,.openclaw/openclaw.json, Hermesoptional-mcps/<name>.json) thatscanMcp()/threat-model.tsdon't check at all — a pre-existing, architecturally larger gap (different permission-string formats per host), not introduced or regressed by #276.References
threat-model.ts/mcp-scan.ts)docs/adrs/ADR-022-mcp-primitive.md