Preserve nested nullable MCP tool schemas in code mode

This commit is contained in:
Vivian Fang
2026-04-05 19:34:19 -07:00
parent b5edeb98a0
commit c7c4fa6ab3
4 changed files with 737 additions and 44 deletions

View File

@@ -87,7 +87,13 @@ fn parse_tool_input_schema_sanitizes_additional_properties_schema() {
JsonSchema::Object {
properties: BTreeMap::from([(
"value".to_string(),
JsonSchema::String { description: None },
JsonSchema::AnyOf {
variants: vec![
JsonSchema::String { description: None },
JsonSchema::Number { description: None },
],
description: None,
},
)]),
required: Some(vec!["value".to_string()]),
additional_properties: None,
@@ -96,3 +102,157 @@ fn parse_tool_input_schema_sanitizes_additional_properties_schema() {
}
);
}
#[test]
fn parse_tool_input_schema_preserves_web_run_shape() {
let schema = parse_tool_input_schema(&serde_json::json!({
"type": "object",
"properties": {
"open": {
"anyOf": [
{
"type": "array",
"items": {
"type": "object",
"properties": {
"ref_id": {"type": "string"},
"lineno": {"anyOf": [{"type": "integer"}, {"type": "null"}]}
},
"required": ["ref_id"],
"additionalProperties": false
}
},
{"type": "null"}
]
},
"tagged_list": {
"anyOf": [
{
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {"type": "const", "const": "tagged"},
"variant": {"type": "enum", "enum": ["alpha", "beta"]},
"scope": {"type": "enum", "enum": ["one", "two"]}
},
"required": ["kind", "variant", "scope"]
}
},
{"type": "null"}
]
},
"response_length": {
"type": "enum",
"enum": ["short", "medium", "long"]
}
}
}))
.expect("parse schema");
assert_eq!(
schema,
JsonSchema::Object {
properties: BTreeMap::from([
(
"open".to_string(),
JsonSchema::AnyOf {
variants: vec![
JsonSchema::Array {
items: Box::new(JsonSchema::Object {
properties: BTreeMap::from([
(
"lineno".to_string(),
JsonSchema::AnyOf {
variants: vec![
JsonSchema::Number { description: None },
JsonSchema::Null { description: None },
],
description: None,
},
),
(
"ref_id".to_string(),
JsonSchema::String { description: None },
),
]),
required: Some(vec!["ref_id".to_string()]),
additional_properties: Some(false.into()),
}),
description: None,
},
JsonSchema::Null { description: None },
],
description: None,
},
),
(
"response_length".to_string(),
JsonSchema::Enum {
values: vec![
serde_json::json!("short"),
serde_json::json!("medium"),
serde_json::json!("long"),
],
schema_type: Some("enum".to_string()),
description: None,
},
),
(
"tagged_list".to_string(),
JsonSchema::AnyOf {
variants: vec![
JsonSchema::Array {
items: Box::new(JsonSchema::Object {
properties: BTreeMap::from([
(
"kind".to_string(),
JsonSchema::Const {
value: serde_json::json!("tagged"),
schema_type: Some("const".to_string()),
description: None,
},
),
(
"scope".to_string(),
JsonSchema::Enum {
values: vec![
serde_json::json!("one"),
serde_json::json!("two"),
],
schema_type: Some("enum".to_string()),
description: None,
},
),
(
"variant".to_string(),
JsonSchema::Enum {
values: vec![
serde_json::json!("alpha"),
serde_json::json!("beta"),
],
schema_type: Some("enum".to_string()),
description: None,
},
),
]),
required: Some(vec![
"kind".to_string(),
"variant".to_string(),
"scope".to_string(),
]),
additional_properties: None,
}),
description: None,
},
JsonSchema::Null { description: None },
],
description: None,
},
),
]),
required: None,
additional_properties: None,
}
);
}