Documentation Index
Fetch the complete documentation index at: https://modelcontextprotocol.io/llms.txt
Use this file to discover all available pages before exploring further.
DraftStandards Track
Abstract
This SEP proposes loosening the restrictions oninputSchema, outputSchema, and structuredContent to better support JSON Schema 2020-12. Specifically:
inputSchema: Keepstype: "object"required (since tool arguments are objects), but allows any additional JSON Schema properties to support powerful validation compositions (anyOf,oneOf,allOf, etc.)outputSchema: Fully supports JSON Schema 2020-12 since MCP servers may return any valid JSONstructuredContent: Accepts any JSON value validated byoutputSchema
Motivation
The current MCP specification restricts tool schemas in ways that conflict with full JSON Schema support:-
inputSchema restriction: Currently only allows
type,properties, andrequiredfields. This prevents use of composition keywords likeanyOf,oneOf, andallOffor sophisticated object validation patterns. -
outputSchema restriction: Also restricted to
type: "object"with onlypropertiesandrequired, despite the specification claiming to support “JSON Schema.” -
structuredContent restriction: Defined as
{ [key: string]: unknown }(an object with string keys), which prevents returning arrays—a common API response pattern.
Real-World Impact
Consider a weather API tool that returns hourly forecasts:structuredContent must be an object. Developers are forced to wrap arrays in unnecessary container objects:
- Adds unnecessary nesting to responses
- Conflicts with common REST API patterns
- Prevents direct schema validation of array responses
Schema Composition Use Cases
The currentinputSchema restriction prevents legitimate schema patterns. With this SEP, tools can use composition keywords alongside type: "object":
type, properties, and required fields.
Specification
1. Loosen inputSchema
Current definition:inputSchema field retains the type: "object" requirement (since tool arguments are always objects), but now accepts any additional JSON Schema properties. This enables:
- Composition keywords:
anyOf,oneOf,allOf,not - Conditional schemas:
if/then/else - Reference schemas:
$ref,$defs - Any other valid JSON Schema 2020-12 keywords
2. Loosen outputSchema
Current definition:outputSchema field accepts any valid JSON Schema 2020-12 object, enabling schemas that validate arrays, primitives, or complex compositions. Unlike inputSchema, there is no type: "object" requirement since tool outputs can be any valid JSON.
3. Loosen structuredContent
Current definition:structuredContent field accepts any valid JSON value that conforms to the tool’s outputSchema. This includes:
- Objects:
{ "key": "value" } - Arrays:
[1, 2, 3]or[{ "id": "abc" }, { "id": "xyz" }] - Primitives:
"string",42,true,null
4. Documentation Updates
Updatedocs/specification/draft/server/tools.mdx:
- Remove statement that
structuredContentis “returned as a JSON object” - Clarify that
structuredContentcan be any JSON value conforming tooutputSchema - Add examples demonstrating array responses
5. Examples
Tool returning an array of objects:
Tool with composition schema:
Rationale
Why not just allow arrays?
While we could simply extendstructuredContent to allow arrays, this would be an incomplete solution. The root cause is that the schema types are artificially restricted to type: "object". By allowing any valid JSON Schema, we:
- Enable the full power of JSON Schema 2020-12
- Align with the specification’s claim of JSON Schema support
- Provide a consistent, principled approach rather than piecemeal fixes
Why not require a wrapper object?
Requiring arrays to be wrapped in objects (e.g.,{ "items": [...] }) was considered but rejected because:
- It adds unnecessary complexity to responses
- It conflicts with common API design patterns
- It prevents direct schema validation of the actual response structure
- JSON Schema already handles array validation elegantly
Real-World API Patterns
Many production APIs return arrays directly:- GitHub Events API: Returns arrays of event objects
- AccuWeather Search API: Returns arrays of location matches
- REST collection endpoints: Standard
GET /usersreturns[{...}, {...}]
Alignment with JSON Schema 2020-12
JSON Schema 2020-12 provides powerful features for schema composition and validation. By removing artificial restrictions, MCP aligns with industry standards (OpenAPI 3.1 uses JSON Schema 2020-12) and enables developers to leverage existing JSON Schema knowledge and tooling.SDK Ecosystem Evidence
The friction caused by current restrictions is not theoretical. FastMCP, one of the most popular Python SDKs for MCP, has implemented extensive workarounds:-
Explicit error messages acknowledge the limitation:
-
Auto-wrapping infrastructure adds complexity:
- A
_WrappedResultdataclass wraps non-object returns - A custom
x-fastmcp-wrap-resultextension enables client-side unwrapping - Both SDK and client need matching wrap/unwrap logic
- A
-
Real bugs have resulted from these workarounds:
- Issue #2455:
$refschemas withouttype: objectbroke ALL tools on the server - Issue #2421: Unexpected
{"result": ...}wrapping confused users
- Issue #2455:
OpenAPI Precedent
The OpenAPI specification went through a similar evolution. OpenAPI 3.0 used an “extended subset” of JSON Schema with custom restrictions (like requiringnullable: true instead of allowing "null" as a type).
OpenAPI 3.1 made the strategic decision to fully align with JSON Schema 2020-12, accepting breaking changes to eliminate the friction. The result: better tooling compatibility and less ecosystem confusion.
| OpenAPI’s Problem | MCP’s Parallel |
|---|---|
type must be string, not array | inputSchema only allows specific fields |
| Couldn’t use standard null handling | Can’t use oneOf/anyOf in schemas |
Custom nullable keyword | Object-only structuredContent |
| Caused tooling confusion | Causes SDK workarounds |
Backward Compatibility
This change is wire-format backward compatible but has nuances depending on the direction of the version mismatch.Compatibility Matrix
| New client (post-SEP) | Old client (pre-SEP) | |
|---|---|---|
| New server (post-SEP) | Fully compatible. | Compatible only when the server returns object-typed structuredContent. Arrays/primitives in structuredContent may break. |
| Old server (pre-SEP) | Fully compatible. Existing object-only schemas remain valid. | Unchanged. |
structuredContent (or composition keywords in inputSchema) cannot assume an old client will accept the response. Old clients written against the previous wire format may reject structuredContent that is not a JSON object, or fail to validate inputSchema containing keywords beyond type/properties/required.
To remain interoperable with older clients, servers using array or primitive structuredContent MUST also emit a TextContent block containing the serialized JSON (as already recommended in the tools specification). Clients that do not understand non-object structuredContent can fall back to the text content.
TypeScript / SDK Migration
Widening thestructuredContent field type from { [key: string]: unknown } to unknown is a source-breaking change for typed consumers, even though the wire format is unchanged. Code such as:
unknown without a narrowing guard:
- Document the migration in SDK release notes.
- Where ergonomic, provide typed helpers (e.g. generics over a tool’s
outputSchema) so consumers do not need to write narrowing guards by hand.
Migration Path
- Servers: No migration is required to keep working as before. To use array or primitive
structuredContent, also emit a serializedTextContentfallback. - Clients: Old clients continue to work against object-only servers. To consume the new flexibility, accept any JSON value in
structuredContentand validate againstoutputSchemaif present. - SDKs: Update generated types to mirror the new schema (
unknownforstructuredContent, open-endedinputSchema/outputSchema) and call out the source-breaking type change in release notes.
Security Implications
JSON Schema validation already handles type checking, value constraints, and required field validation, and implementations MUST continue to validate all inputs and outputs against declared schemas. Allowing the full JSON Schema 2020-12 vocabulary surfaces two areas that warrant explicit guidance.$ref Dereferencing (SSRF and Fetch-DoS)
JSON Schema 2020-12 permits $ref to point at an absolute URI, not just a JSON Pointer into the same document. A naive implementation that resolves every $ref it encounters by issuing an HTTP request gives an attacker a server-side request forgery / fetch amplification primitive: a malicious tool definition can cause the host to fetch arbitrary URLs, including internal metadata endpoints or large payloads designed to exhaust resources.
To mitigate this:
- Implementations MUST NOT automatically dereference
$refvalues that resolve to a network URI (i.e. anything that is not a same-document JSON Pointer such as#/$defs/Fooor an internal$anchor). - “Automatically” here means “as part of normal validation or schema processing, without explicit operator action.” Implementations MAY offer an opt-in mode that fetches non-local
$refs, but it MUST be disabled by default and SHOULD enforce an allowlist of hosts (or at minimum reject loopback, link-local, and private network addresses), apply timeouts and size limits, and log dereferenced URIs. - Schemas that fail to validate due to an unresolved external
$refSHOULD be rejected rather than silently treated as permissive.
Composition-Keyword Resource Use
Composition keywords (anyOf, oneOf, allOf, if/then/else) and $defs enable expressive schemas, but pathological combinations can be expensive to validate. Implementations SHOULD apply reasonable bounds — for example, a maximum schema depth, a cap on the total number of subschemas, or a per-validation time budget — to prevent a malicious tool definition from acting as a CPU DoS vector against the validator.
Reference Implementation
TypeScript SDK
A reference implementation demonstrating the loosened type restrictions:- Branch: olaservo/typescript-sdk@sep-834-v1x
- npm:
@olaservo/mcp-sdk@1.25.2-sep834.4 - Key changes:
inputSchema: Retainstype: "object"but allows any additional JSON Schema properties (compositions likeoneOf/anyOf)outputSchema: Any valid JSON Schema object (arrays, primitives, objects, compositions)structuredContent: Any JSON value (objects, arrays, or primitives)- McpServer high-level API updated to support array and primitive outputSchema
Everything Server Demo Tools
Three demo tools added to theeverything server demonstrating SEP-2106 capabilities:
- Branch: olaservo/servers@sep-834-json-schema-2020-12
- npm:
@olaservo/mcp-server-everything-sep834@1.1.0-sep834.1 - Tools:
get-weather-forecast: Returns raw array of hourly forecasts directly instructuredContent- Matches the exact example from SEP-2106’s Motivation section
outputSchema:z.array(HourlyForecastSchema)- array type at rootstructuredContent:[{hour, temp, conditions}, ...]- direct array
find-by-id-or-name: Demonstrates flexible input patterns (acceptsidORname)get-count: Returns raw number directly instructuredContent(not wrapped in object)outputSchema:z.number()- primitive type at rootstructuredContent:42- direct primitive
Related Links
- Original PR: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/881
- Related issue: https://github.com/modelcontextprotocol/modelcontextprotocol/issues/834
outputSchematype restriction inconsistency: https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1906- TypeScript SDK schema types: https://github.com/modelcontextprotocol/typescript-sdk/issues/1149
- SEP-2200 (Clarify Tool Result Content and Model Visibility): https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2200
Implementation Guidance
SDK implementations will need to:- Update
inputSchematypes to retaintype: "object"but allow any additional JSON Schema properties - Update
outputSchematypes to allow any valid JSON Schema (removetype: "object"constraint) - Update
structuredContenttypes to accept any valid JSON value - Update JSON Schema definitions accordingly