Skip to content

outputSchema tuples (prefixItems + items:false) fail Cursor's structured-content validation ("boolean schema is false") — emit an interoperable projection #563

Description

@ScriptedAlchemy

Symptom

Calling a generated tool from Cursor's MCP client fails before the result reaches the model when the route's resultSchema contains a zod tuple:

MCP error -32602: Structured content does not match the tool's output schema:
data/metrics/cargo_run_ms/buckets/0/0 boolean schema is false,
data/metrics/cargo_run_ms/buckets/0/1 boolean schema is false, …
data/metrics/wait_ms_summary/quantiles/0/0 boolean schema is false, …

Seen on cargo-hauler's hauler_status (src/lib/protocol-schemas.ts:119,159: z.array(z.tuple([z.number().nullable(), z.number().int().nonnegative()]))). The server's structuredContent is correct; the client's validator rejects the schema, so the tool — and the MCP App attached to it (the dashboard) — is unusable from Cursor. Codex and Claude Code do not validate outputSchema this way and are unaffected.

Cause

src/mcp-server-runtime.ts:299,342 advertises outputSchema through Standard Schema with target: 'draft-2020-12'. For a tuple, zod v4 emits

{"type":"array","prefixItems":[{"type":"number"},{"type":"number"}],"items":false,"minItems":2,"maxItems":2}

which is correct 2020-12. Cursor validates with draft-07 semantics: prefixItems is an unknown keyword and items: false is applied to every element, hence "boolean schema is false" for each tuple member. (The MCP TS SDK's own validator and the Workbench accept it.)

Proposal

The framework controls the projection, so it can emit the one form both drafts read the same way:

  • When a projected schema has prefixItems, drop items: false (keep minItems/maxItems). A 2020-12 validator still checks the positions; a draft-07 validator sees a plain array of length 2 and passes. The only loss is "no extra items", already enforced by maxItems.
  • Alternatively emit draft-07 (items: [...], additionalItems: false) for tools' outputSchema — but that breaks strict 2020-12 validators, so the first option is the interoperable one.
  • Add a route-contract diagnostic (or at least a docs/diagnostics.md note) when a resultSchema produces a construct known not to validate in a shipping host, and a test in tests/mcp-server-runtime (or the packed contract matrix) that runs the emitted outputSchema through a draft-07 Ajv against a real result.

Until then, plugins must avoid tuples in resultSchema (e.g. z.array(z.number()).length(2)), which the docs should say.

Host reference: capability tables under packages/agent-bundle/src/adapters/capabilities/cursor-*.json should record that Cursor validates structuredContent against outputSchema with draft-07 semantics (observed on Cursor 3.18.25).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghost-integrationAgent host capabilities, projection, discovery, and lifecyclemeta-frameworkAgent Bundle compiler-coupled meta-framework

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions