-
Notifications
You must be signed in to change notification settings - Fork 129
Optional Fields in rust-sdk MCP Schemas (type: ["T", "null"]) Cause Client Incompatibility #135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Ideally, schemars itself would offer a configuration option to generate the type: "T", nullable: true style (or the equivalent OpenAPI 3.1 representation if targeting that). This would be the most ergonomic solution. You might consider opening an issue or checking the schemars repository for existing discussions on this topic. Failing an upstream fix, the we could potentially provide a helper attribute or modify its #[tool] macro processing to automatically apply the PR to document here meanwhile #137 |
I'm considering that if we wrap a schemars macro will be greater? |
@jokemanfire , I feel like we want to be compatible, but if we create a macro for this workaround we are adding kruft. |
I think that's not an official spec. Arrays for typedef are not supported. We can either:
|
Googling I see settings can be set for schemars and Don't know if that breaks things for other clients? We expose configuration? or use schemars::{schema_for, JsonSchema};
use schemars::gen::{SchemaGenerator, SchemaSettings};
#[derive(JsonSchema)]
pub struct UpdateTaskRequest {
pub description: Option<String>,
}
let settings = SchemaSettings::default()
.with(|s| {
s.option_nullable = true; // emit `nullable: true`
s.option_add_null_type = false; // don’t emit `type: ["T","null"]`
});
let mut generator: SchemaGenerator = settings.into();
let schema = generator.into_root_schema_for::<UpdateTaskRequest>();
println!("{}", serde_json::to_string_pretty(&schema).unwrap()); |
I've tested the Schemasettings::openapi3() and that seems to work in this PR #137. Does some one want to confirm this solves the problem? Do we need for rmcp model as well? |
…ld schema workaround (modelcontextprotocol#135)
Thanks @Hendler for the recent work and discussion on this! I’ve tested the latest branch with
|
@aitoroses can you show some code to reproduce? Is it a new error or existed before? The exact error you mention is a the serde error, but not sure where exactly it is triggered. edit: This test passes, but maybe I'm not replicating the issue? |
…ld schema workaround (modelcontextprotocol#135)
…ld schema workaround (modelcontextprotocol#135)
In the rust-sdk MCP implementation, when using schemars to generate OpenAPI/JSON schemas for structs with optional fields (e.g.,
Option<String>
,Option<i64>
, etc.), the default output istype: ["T", "null"]
(whereT
is the underlying type). While this is technically correct per the OpenAPI spec, it leads to incompatibility with some clients and tools, such as Cursor and Windsurf, which expect a different representation for optional fields.To work around this, I had to combine
#[serde(default)]
with a custom schema using#[schemars(schema_with = "nullable_string_schema", description = "A new short description of the task")]
to ensure compatibility. This workaround adds boilerplate and is not ideal for maintainability.What doesn't work (default approach):
Generated schema:
Problem:
Some clients (e.g., Cursor, Windsurf) do not accept this representation for optional fields.
What works (workaround):
Generated schema:
This is accepted by more clients.
Steps to Reproduce
Option<T>
field and deriveJsonSchema
.type: ["T", "null"]
.Expected Behavior
Schemas for optional fields should be compatible with a wide range of clients, or there should be clear documentation or configuration options for this scenario.
Actual Behavior
The default schema output is not accepted by some clients, requiring custom workarounds.
Request
type: ["T", "null"]
in popular clients for the rust-sdk MCP implementation.Thank you!
The text was updated successfully, but these errors were encountered: