From 54174b5d149ffb219660c74e942c99bae22916b8 Mon Sep 17 00:00:00 2001 From: Sworld <61224072+mcthesw@users.noreply.github.com> Date: Thu, 19 Mar 2026 22:38:47 +0800 Subject: [PATCH] fix(mcp): add empty properties to GetIndexInfoRequest schema for OpenAI compatibility --- src/mcp/mod.rs | 29 ++++++++++++++++++++++++- tests/integration/test_mcp_schema.rs | 32 +++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/mcp/mod.rs b/src/mcp/mod.rs index dc2e8553..db9f3764 100644 --- a/src/mcp/mod.rs +++ b/src/mcp/mod.rs @@ -161,9 +161,36 @@ pub struct SemanticSearchWithContextRequest { pub lang: Option, } -#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)] +#[derive(Debug, Deserialize, Serialize)] pub struct GetIndexInfoRequest {} +impl schemars::JsonSchema for GetIndexInfoRequest { + fn schema_name() -> std::borrow::Cow<'static, str> { + std::borrow::Cow::Borrowed("GetIndexInfoRequest") + } + + fn schema_id() -> std::borrow::Cow<'static, str> { + std::borrow::Cow::Borrowed(concat!(module_path!(), "::GetIndexInfoRequest")) + } + + fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema { + // MCP spec recommends `{"type":"object","additionalProperties":false}` for + // no-parameter tools. We also include an empty `properties` map because + // OpenAI's strict function-calling validation rejects object schemas that + // lack `properties` entirely. + schemars::Schema::from( + serde_json::json!({ + "type": "object", + "properties": {}, + "additionalProperties": false + }) + .as_object() + .unwrap() + .clone(), + ) + } +} + #[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)] pub struct SearchDocumentsRequest { /// Natural language search query diff --git a/tests/integration/test_mcp_schema.rs b/tests/integration/test_mcp_schema.rs index d745d0f8..58939549 100644 --- a/tests/integration/test_mcp_schema.rs +++ b/tests/integration/test_mcp_schema.rs @@ -1,6 +1,8 @@ //! Test to verify MCP schema generation for usize fields -use codanna::mcp::{AnalyzeImpactRequest, SearchSymbolsRequest, SemanticSearchRequest}; +use codanna::mcp::{ + AnalyzeImpactRequest, GetIndexInfoRequest, SearchSymbolsRequest, SemanticSearchRequest, +}; #[test] fn test_mcp_schema_uint_format() { @@ -60,3 +62,31 @@ fn test_mcp_schema_uint_format() { println!("✅ No 'uint' format found in schemas."); } } + +/// Regression test: `get_index_info` is a no-parameter tool whose inputSchema must satisfy +/// both MCP spec (recommends `additionalProperties: false`) and OpenAI's strict +/// function-calling validation (requires `properties` field). +#[test] +fn test_get_index_info_schema_has_properties() { + let schema = rmcp::schemars::schema_for!(GetIndexInfoRequest); + let json = serde_json::to_string_pretty(&schema).unwrap(); + println!("GetIndexInfoRequest schema:\n{json}"); + + let root: serde_json::Value = serde_json::from_str(&json).unwrap(); + + assert_eq!( + root.get("type").and_then(|v| v.as_str()), + Some("object"), + "schema must have type=object\nGot:\n{json}" + ); + assert!( + root.get("properties").is_some(), + "schema must contain 'properties' for OpenAI compatibility\nGot:\n{json}" + ); + assert_eq!( + root.get("additionalProperties").and_then(|v| v.as_bool()), + Some(false), + "schema should set additionalProperties=false per MCP spec\nGot:\n{json}" + ); + println!("✅ GetIndexInfoRequest schema is MCP-spec compliant and OpenAI-compatible."); +}