Agent inherits users' MCP servers (strictMcpConfig unset) — one malformed tool schema 400s every run
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 197
- Forks
- 51
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 94
Description
Summary
The wizard's agent runs with the user's own MCP servers merged into the request — project .mcp.json,
user-level config, plugins. Neither query() call site sets strictMcpConfig, and the Anthropic API
rejects the entire request if any single tool schema is invalid. For a user who has one bad
third-party tool anywhere in their MCP config, the wizard is 100% broken, terminally, with no hint that
their local MCP setup is the cause:
API Error: 400 tools.25.custom.input_schema.properties: Property keys should match pattern '^[a-zA-Z0-9_.-]{1,64}$'
Surfaced by a customer support ticket (wizard v2.62.0, tools.679 — i.e. 680+ tool definitions on the
wire; the wizard's own surface is ~15–20 tools).
Telemetry (90 days, project 2)
wizard: agent api error where error_message contains input_schema.properties: 4 events, 3
distinct users, tool index varying per user (679 / 236 / 97) across self-driving-setup, nextjs,
fastapi. Rare, but terminal for whoever hits it, and the class is unbounded: any malformed schema in
any MCP server a user happens to have installed takes the wizard down.
Root cause — replicated, not inferred
settingSources: ['project'] does not gate this. Per the SDK's own typings
(@anthropic-ai/claude-agent-sdk@0.3.169, the exact pin), settingSources governs filesystem
settings.json only; the sole gate on foreign MCP servers is:
strictMcpConfig — Only use MCP servers passed via the
mcpServersoption (and servers declared
by explicitly-passed agent definitions inagents), ignoring all other MCP configurations: project
.mcp.json, user settings, plugins, and on-disk agent frontmatter.
Repro (node:22, SDK 0.3.169, fake endpoint on ANTHROPIC_BASE_URL applying Anthropic's documented
property-key rule; a planted .mcp.json server with property key "bad key"; one wizard-style server
passed in code via mcpServers; options mirroring the wizard: settingSources: ['project'],
permissionMode: 'acceptEdits'):
| Scenario | .mcp.json |
strictMcpConfig |
tools sent | foreign tool on wire | API |
|---|---|---|---|---|---|
| A — wizard as-is | yes | unset | 27 | yes | 400 |
| B — with fix | yes | true |
26 | no | 200 |
| C — control | no | unset | 26 | no | 200 |
B ≡ C: the flag removes exactly the inherited server; in-code mcpServers survive untouched.
Affected call sites
src/lib/agent/agent-interface.ts:882(options object;settingSourcesat :908)src/lib/agent/mcp-prompt-streaming.ts:254—allowedTools: ['mcp__posthog-wizard__*']gates
invocation, not which tool definitions are serialized, so this site is vulnerable too
Non-regression: the general-purpose subagent's mcpServers: inheritedMcpServerNames is
Object.keys(agentConfig.mcpServers) (:852) — our own names, exempt per the doc. Svelte
additionalMcpServers merge into the same in-code map. skills: 'all' / CLAUDE.md are a different
option. The repo's own .mcp.json (wizard-ci) is consumed by the developer session driving the
wizard, not by the wizard's subprocess.
Two adjacent defects (fire in 100% of these cases)
- The error prints twice, identically, same
request_id. One HTTP request; the SDK surfaces the
text in both an assistant block and the terminalresult.AgentOutputSignals.push()retains both
andapiErrorMessage()joins every match (output-signals.ts:86-91). Fix: dedupe there
([...new Set(m)]) — not inpush(), since one copy arrives bare and one wrapped in
Claude Code returned an error result: …, so line-level dedupe misses it. - Raw provider JSON + "report this to wizard@posthog.com" is the whole UX. Only 401/429 get
triage today. Atools.<n>.400 is deterministic and self-diagnosable — the user should be told
their MCP config is implicated and that moving.mcp.jsonaside unblocks them.
Proposed fix
-
strictMcpConfig: trueat both call sites - Contract test asserting
options.strictMcpConfig === trueon the wire —query()is loaded via
an untyped dynamic import at both sites (agent-interface.ts:63-67,
mcp-prompt-streaming.ts:28), so a typo silently no-ops through build and tests - Dedupe in
apiErrorMessage() -
TOOL_SCHEMA_REJECTEDtriage + honest copy (follow the existing 401 pattern; keep the
agent api errorevent name, varyerror_type)
Prior art / related
- PostHog/posthog#78073 — the identical fix for Desktop ("a repo's
.mcp.jsoncannot auto-spawn
servers"), closed unmerged after a stamphog ESCALATE (needs owning-team review — plan for that here) - PostHog/posthog#64182 — our own MCP generator emitting Anthropic-invalid schemas (top-level
anyOf),
open since June: with inheritance unfixed, the wizard installing the PostHog MCP server at
--scope user(clients/claude-code.ts:135) means we can break our own next run - #1038 — allowlisting
mcp__<name>__*for every mounted server is only safe once inheritance is off;
strictMcpConfigis effectively its prerequisite - Prior instance of the class: tool-name-length 400s, noted at
runner/harness/pi/mcp.ts:73
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the query() call sites in src/lib/agent/agent-interface.ts:882 and src/lib/agent/mcp-prompt-streaming.ts:254, then inspect their dynamic imports and the existing contract tests. Also read output-signals.ts:86-91 and the existing 401 triage pattern. Done means strict MCP configuration is asserted at both call sites, repeated API errors are deduplicated, and schema-rejection errors provide the proposed MCP configuration guidance.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, typescript
- Domain
- api, backend, testing
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100