Skip to content

Implement SEP-2106: Tool schemas conform to JSON Schema 2020-12 #874

@alexhancock

Description

@alexhancock

SEP-2106: Tools inputSchema & outputSchema Conform to JSON Schema 2020-12 — rust-sdk implementation

Spec PR: modelcontextprotocol/modelcontextprotocol#2106
Track: Specification · Stage: accepted · Priority: P1
Needs code changes: Yes (Medium)

Summary

Brings tool schemas to full JSON Schema 2020-12:

  • inputSchema: keep type: "object" required, but allow any 2020-12 keyword — composition (oneOf/anyOf/allOf/not), conditional (if/then/else), references ($ref/$defs/$anchor), etc.
  • outputSchema: fully support 2020-12 — arrays, primitives, objects, compositions (not just objects).
  • structuredContent: accept any JSON value validated by outputSchema (not only an object).

Motivated by real friction: tools that naturally return arrays must currently wrap them in a
container object.

Why this needs code changes in rust-sdk

Current state in the SDK (already partly there):

  • crates/rmcp/src/model/tool.rs: Tool.input_schema: Arc<JsonObject> and output_schema: Option<Arc<JsonObject>>. JsonObject here is the schema document (always a JSON object), so this typing is fine even for non-object schemas — no change needed to those field types.
  • CallToolResult.structured_content is already Option<Value> (crates/rmcp/src/model.rs ~line 2780), so the result type already accepts arrays/primitives. Good — no model change there.
  • Tool::with_input_schema::<T: JsonSchema>() / with_output_schema::<T: JsonSchema>() derive schemas via schemars (Cargo.toml pins schemars = "1.x", which emits JSON Schema 2020-12).

So the type system mostly already allows this. The real gaps are validation, helpers, and the tool macro:

  1. The #[tool]/tool_router proc-macro path and with_output_schema::<T>() should work when T is a non-object (e.g. Vec<Forecast>). Verify the macro doesn't force type: object.
  2. Any server-side validation that checks structured_content against output_schema must accept non-object roots and 2020-12 keywords (oneOf/anyOf/allOf/if/then/else/$ref/$defs).
  3. Confirm serde round-trips of Value structured content for array/primitive payloads (covered by model/serde_impl.rs).

Proposed work

  • Add tests proving schemars 1.x output (composition/conditional/$ref) is accepted end-to-end through Tool::new + with_output_schema.
  • Verify the #[tool] proc-macro (in the rmcp-macros crate) handles a non-object return type for output_schema without forcing type: object.
  • Audit any structured_contentoutput_schema validation for object-only assumptions; allow array/primitive roots.
  • Add a test with the SEP's array example (e.g. a tool returning Vec<Forecast> as top-level structuredContent).

Affected areas

crates/rmcp/src/model/tool.rs, crates/rmcp/src/model.rs (CallToolResult), crates/rmcp-macros/ (the #[tool] derive), handler/server/tool.rs.

Notes / risks

  • Low risk: schemars already emits 2020-12 and structured_content is already Option<Value>. Bulk of the work is the macro/validation audit plus tests, not new types.

Related existing issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High: significant functionality gap or spec violationT-enhancementNew features and enhancementsT-macrosMacro changesT-modelModel/data structure changes

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions