Skip to content

Conversation

@DaleSeo
Copy link
Contributor

@DaleSeo DaleSeo commented Nov 28, 2025

Fixes #532

Implements validation to enforce MCP specification requirement that tool outputSchema must have a root type of "object". Tools using structured output (Json<T>) where T is a primitive type will fail at compile time with clear error messages.

Motivation and Context

As reported in issue #532, the MCP specification requires tool outputSchema to have a root type of "object" (see MCP Tool Schema). The Rust SDK did not validate this, allowing spec-violating schemas to be generated.

2025-11-28 at 17 28 42

The latest spec published on Nov 25 states this more clearly:

2025-11-28 at 17 29 45

How Has This Been Tested?

Noticed the sub tool in the calculator example in the repo doesn't comply with the MCP spec, which causes an issue with MCP Insepctor.

#[tool(description = "Calculate the difference of two numbers")]
fn sub(&self, Parameters(SubRequest { a, b }): Parameters<SubRequest>) -> Json<i32> {
Json(a - b)
}

2025-11-29 at 13 56 37

Now that the output schema validation is in place, the server panics during startup:

➜  servers git:(outputschema-validation) ✗ cargo run -p mcp-server-examples --example servers_calculator_stdio
   Compiling mcp-server-examples v0.1.5 (/Users/dale/work/rust-sdk/examples/servers)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.31s
     Running `/Users/dale/work/rust-sdk/target/debug/examples/servers_calculator_stdio`
2025-11-29T18:50:44.901451Z  INFO servers_calculator_stdio: Starting Calculator MCP server

thread 'main' panicked at examples/servers/src/common/calculator.rs:46:5:
Invalid output schema for Json<i32>: MCP specification requires tool outputSchema to have root type 'object', but found 'integer'.
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

After fixing the calculator example code so that the sub tool returns unstructured output just like the sum tool, the server starts up without any issues and works well with the MCP Inspector.

    #[tool(description = "Calculate the difference of two numbers")]
    fn sub(&self, Parameters(SubRequest { a, b }): Parameters<SubRequest>) -> String {
        (a - b).to_string()
    }
➜  servers git:(outputschema-validation) ✗ cargo run -p mcp-server-examples --example servers_calculator_stdio
   Compiling mcp-server-examples v0.1.5 (/Users/dale/work/rust-sdk/examples/servers)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.41s
     Running `/Users/dale/work/rust-sdk/target/debug/examples/servers_calculator_stdio`
2025-11-29T19:05:00.312995Z  INFO servers_calculator_stdio: Starting Calculator MCP server
2025-11-29 at 14 05 44

As suggested in issue #532, wrapping the primitive type also works as expected.

    #[tool(description = "Calculate the difference of two numbers")]
    fn sub(&self, Parameters(SubRequest { a, b }): Parameters<SubRequest>) -> Json<Wrapper<i32>> {
        Json(Wrapper::new(a - b))
    }
2025-11-29 at 14 14 16

Breaking Changes

Existing valid code works unchanged:

  • Code returning object types continues to work
  • Unstructured output (eg. i32, String) with no output stream continues to work
  • Only spec-violating code (e.g. <Json<i32>, <Json<String>) fails to compile

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

The TypeScript SDK also enforces this requirement in the Tool schema definition here:

2025-11-28 at 17 40 36

@github-actions github-actions bot added T-core Core library changes T-handler Handler implementation changes T-macros Macro changes T-model Model/data structure changes labels Nov 28, 2025
@DaleSeo DaleSeo changed the title Implements outputSchema validation to enforce MCP specification requirement Implements outputSchema validation Nov 28, 2025
@github-actions github-actions bot added T-dependencies Dependencies related changes T-config Configuration file changes T-examples Example code changes labels Nov 29, 2025
@DaleSeo DaleSeo force-pushed the outputschema-validation branch from 0088306 to 95984d1 Compare November 29, 2025 19:06
@DaleSeo DaleSeo force-pushed the outputschema-validation branch from 95984d1 to f30567b Compare November 29, 2025 19:18
@DaleSeo DaleSeo marked this pull request as ready for review November 29, 2025 19:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-config Configuration file changes T-core Core library changes T-dependencies Dependencies related changes T-examples Example code changes T-handler Handler implementation changes T-macros Macro changes T-model Model/data structure changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Structured tool output schema does not follow MCP 2025-06-18.

1 participant