diff --git a/backend/windmill-mcp/src/common/schema.rs b/backend/windmill-mcp/src/common/schema.rs index c7cec8849e..4d3679f607 100644 --- a/backend/windmill-mcp/src/common/schema.rs +++ b/backend/windmill-mcp/src/common/schema.rs @@ -39,3 +39,63 @@ pub fn extract_resource_types_from_schema(schema: &SchemaType) -> HashSet *s = "number".to_string(), + Value::Array(arr) => { + for item in arr.iter_mut() { + if let Value::String(s) = item { + if s == "integer" { + *s = "number".to_string(); + } + } + } + } + _ => {} + } + } + + // Recursively process nested schemas + if let Some(Value::Object(props)) = obj.get_mut("properties") { + for value in props.values_mut() { + make_schema_compatible(value); + } + } + + if let Some(items) = obj.get_mut("items") { + make_schema_compatible(items); + } + + if let Some(additional) = obj.get_mut("additionalProperties") { + if additional.is_object() { + make_schema_compatible(additional); + } + } + + if let Some(Value::Array(all_of)) = obj.get_mut("allOf") { + for s in all_of.iter_mut() { + make_schema_compatible(s); + } + } + + if let Some(Value::Array(one_of)) = obj.get_mut("oneOf") { + for s in one_of.iter_mut() { + make_schema_compatible(s); + } + } + + if let Some(Value::Array(any_of)) = obj.get_mut("anyOf") { + for s in any_of.iter_mut() { + make_schema_compatible(s); + } + } +} diff --git a/backend/windmill-mcp/src/server/endpoints.rs b/backend/windmill-mcp/src/server/endpoints.rs index ddf531bd1e..2a0d258e64 100644 --- a/backend/windmill-mcp/src/server/endpoints.rs +++ b/backend/windmill-mcp/src/server/endpoints.rs @@ -8,6 +8,8 @@ use serde::{Deserialize, Serialize}; use std::borrow::Cow; use std::sync::Arc; +use crate::common::schema::make_schema_compatible; + /// Represents an auto-generated endpoint tool from OpenAPI specification #[derive(Debug, Clone, Deserialize, Serialize)] pub struct EndpointTool { @@ -37,11 +39,12 @@ pub fn endpoint_tool_to_mcp_tool(tool: &EndpointTool) -> Tool { merge_schema_into(&mut combined_properties, &mut combined_required, schema); } - let combined_schema = serde_json::json!({ + let mut combined_schema = serde_json::json!({ "type": "object", "properties": combined_properties, "required": combined_required }); + make_schema_compatible(&mut combined_schema); let description = format!("{}. {}", tool.description, tool.instructions); diff --git a/backend/windmill-mcp/src/server/tools.rs b/backend/windmill-mcp/src/server/tools.rs index fea875e498..28cf811c69 100644 --- a/backend/windmill-mcp/src/server/tools.rs +++ b/backend/windmill-mcp/src/server/tools.rs @@ -8,7 +8,7 @@ use std::borrow::Cow; use std::collections::HashMap; use std::sync::Arc; -use crate::common::schema::convert_schema_to_schema_type; +use crate::common::schema::{convert_schema_to_schema_type, make_schema_compatible}; use crate::common::transform::transform_path; use crate::common::types::{ FlowInfo, HubScriptInfo, ResourceInfo, ResourceType, SchemaType, ScriptInfo, ToolableItem, @@ -147,13 +147,18 @@ pub fn create_tool_from_item( backend.transform_schema_for_resources(&schema, resources_cache, resources_types); let input_schema_map = match serde_json::to_value(schema_obj) { - Ok(serde_json::Value::Object(map)) => map, - Ok(_) => { - tracing::warn!( - "Schema object for tool '{}' did not serialize to a JSON object, using empty schema.", - path - ); - serde_json::Map::new() + Ok(mut value) => { + make_schema_compatible(&mut value); + match value { + serde_json::Value::Object(map) => map, + _ => { + tracing::warn!( + "Schema object for tool '{}' did not serialize to a JSON object, using empty schema.", + path + ); + serde_json::Map::new() + } + } } Err(e) => { tracing::error!(