Repository navigation
output schema has unnecessary restriction type=object #1149
Description
Describe the bug
A clear and concise description of what the bug is.
The typescript SDK requires that the outputSchema be in type: "object".
Lines 1013 to 1018 in 0c1cbf0
| outputSchema: z | |
| .object({ | |
| type: z.literal('object'), | |
| properties: z.record(z.string(), AssertObjectSchema).optional(), | |
| required: z.optional(z.array(z.string())), | |
| additionalProperties: z.optional(z.boolean()) |
The current constraint prevents describing the response for list and batch requests.
This is noteworthy as the specification does not strictly mandate that the outputSchema must be an object (see: https://modelcontextprotocol.io/specification/draft/server/tools#output-schema).
Consequently, the Python SDK does not enforce this schema constraint on the output.
To Reproduce
Steps to reproduce the behavior:
Let the MCP server return an outputSchema in this format:
{
type: "array",
properties: {
foo: { type: "string" }
}
}
Then an error is thrown:
ZodError: [
{
"received": "array",
"code": "invalid_literal",
"expected": "object",
"path": [
"tools",
0,
"outputSchema",
"type"
],
"message": "Invalid literal value, expected \"object\""
}
]
Expected behavior
A clear and concise description of what you expected to happen.
Should accept any valid JSONSchema, at least the array one.
Activity
fix: allow any JSON Schema type for outputSchema
fix: allow any JSON Schema type for outputSchema
RobJacobson commentedon Dec 12, 2025
Additional Details: Runtime Crash with Non-Object Output Schemas
I encountered this issue in a real-world scenario and traced it to a runtime crash. Here are the details:
Environment
z.date().optional()) and arraysStack Trace
Root Cause Analysis
In
validateToolOutput()(mcp.ts lines 200-201):normalizeObjectSchema()returnsundefinedfor non-object schemas (arrays, optionals, etc.). Thisundefinedis then passed tosafeParseAsync(), which callsisZ4Schema(undefined), causing the crash.Proposed Fix (4 lines)
Why This Fix
Workaround (for others hitting this)
Until this is fixed, you can work around it by ensuring your
outputSchemais always an object:Happy to submit a PR for this fix if it would be helpful!
felixweinberger commentedon Dec 16, 2025
Confirmed still an issue on v1.25.1. The spec doesn't require
outputSchemato betype: object.The quick fix (skip validation for non-object schemas) would work but leaves those schemas unvalidated. The proper fix is to use the existing
jsonSchemaValidator(AJV) infrastructure for output validation instead of Zod, which would support all JSON Schema types.Adding to v2 backlog.
domdomegg commentedon Dec 16, 2025
Related spec issue: modelcontextprotocol/modelcontextprotocol#1906
dastrobu commentedon Jan 8, 2026
is this related to modelcontextprotocol/modelcontextprotocol#834?
tobiasBora commentedon Jul 21, 2026
Any news? This bug is also present in v4: when the
structuredContentis a string is seems to wrap it inside a{ result: str }object, but it's NOT what the spec mentions.olaservo commentedon Aug 2, 2026
I think this can now be closed since v2.0 allows any 2020-12 JSON schema type for outputSchema, as of 2026-07-28.
Edit: expanding on this comment for more detail:
The original object restriction was lifted by SEP-2106, which landed in #2337 and shipped in
@modelcontextprotocol/core@2.0.0. Both enforcement points named in the triage comment above are gone:ToolSchemano longer pinstype: z.literal('object'), andstandardSchemaToJsonSchemano longer throws for a non-object root.Re-running the repro from this thread against the published
2.0.0, array, string, number, boolean,anyOf,oneOfand$refroots are all accepted, andregisterTooltakes a barez.array(...)as anoutputSchema.A couple other things to note:
This was only fixed in v2, and
@modelcontextprotocol/sdk@1.30.0still rejects the same non-object schemas.@tobiasBora re: the
{ result: … }wrapping you raised just above: that is a different mechanism and it is working as intended. On protocol revision 2026-07-28 a tool with a non-objectoutputSchemareturns the bare value. On the older revisions the wire shape requiresstructuredContentto be an object, so the SDK projects both halves together and the advertised schema becomes{"type":"object","properties":{"result":…},"required":["result"]}and the payload becomes{"result": …}.This is meant to keep the schema and the value consistent for a legacy-era client. The client's
versionNegotiationdefaults tolegacy, so an unconfigured client negotiates the older revision and sees the wrapped form. If it connects with a 2026-07-28 negotiation and the same tool returns your string unwrapped.#1169 was the fix proposed for this and is superseded by #2337, so it should be closed alongside.
mattzcarey commentedon Sep 10, 2026
Closed as resolved by the spec: SEP-2106 lifted the
type: objectrestriction in the 2026-07-28 revision (changelog, minor change 10). The SDK's 2026-07-28 wire schema accepts any JSON Schema 2020-12 foroutputSchemain buildSchemas.ts#L728-L732. The 2025-11-25 restriction remains correct for that revision.