fix tool-schema

This commit is contained in:
jaberjaber23
2026-03-10 01:41:33 +03:00
parent b4e6a693f5
commit 56aeb499c9
3 changed files with 76 additions and 2 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ members = [
]
[workspace.package]
version = "0.3.38"
version = "0.3.39"
edition = "2021"
license = "Apache-2.0 OR MIT"
repository = "https://github.com/RightNow-AI/openfang"
+13
View File
@@ -200,6 +200,19 @@ impl McpConnection {
let input_schema = tool
.get("inputSchema")
.cloned()
.and_then(|v| {
// Ensure input_schema is a JSON object. MCP servers may
// return it as a string, null, or omit it entirely.
match &v {
serde_json::Value::Object(_) => Some(v),
serde_json::Value::String(s) => {
serde_json::from_str::<serde_json::Value>(s)
.ok()
.filter(|p| p.is_object())
}
_ => None,
}
})
.unwrap_or(serde_json::json!({"type": "object"}));
// Namespace: mcp_{server}_{tool}
+62 -1
View File
@@ -56,7 +56,20 @@ pub fn normalize_schema_for_provider(
fn normalize_schema_recursive(schema: &serde_json::Value) -> serde_json::Value {
let obj = match schema.as_object() {
Some(o) => o,
None => return schema.clone(),
None => {
// If the schema is a JSON string, try to parse it as a JSON object.
// Some MCP servers / skill definitions serialize schemas as strings.
if let Some(s) = schema.as_str() {
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(s) {
if parsed.is_object() {
return normalize_schema_recursive(&parsed);
}
}
}
// Non-object schema (null, number, bool, unparseable string, array) —
// return a valid empty object schema so providers don't reject it.
return serde_json::json!({"type": "object", "properties": {}});
}
};
let mut result = serde_json::Map::new();
@@ -258,4 +271,52 @@ mod tests {
.get("$schema")
.is_none());
}
#[test]
fn test_normalize_schema_string_parsed_to_object() {
// MCP servers may return inputSchema as a JSON string
let schema = serde_json::Value::String(
r#"{"type":"object","properties":{"query":{"type":"string"}}}"#.to_string(),
);
let result = normalize_schema_for_provider(&schema, "openai");
assert!(result.is_object());
assert_eq!(result["type"], "object");
assert!(result["properties"]["query"].is_object());
}
#[test]
fn test_normalize_schema_null_becomes_empty_object() {
let schema = serde_json::Value::Null;
let result = normalize_schema_for_provider(&schema, "openai");
assert!(result.is_object());
assert_eq!(result["type"], "object");
}
#[test]
fn test_normalize_schema_unparseable_string_becomes_empty_object() {
let schema = serde_json::Value::String("not valid json".to_string());
let result = normalize_schema_for_provider(&schema, "openai");
assert!(result.is_object());
assert_eq!(result["type"], "object");
}
#[test]
fn test_normalize_schema_number_becomes_empty_object() {
let schema = serde_json::json!(42);
let result = normalize_schema_for_provider(&schema, "openai");
assert!(result.is_object());
assert_eq!(result["type"], "object");
}
#[test]
fn test_normalize_schema_string_with_dollar_schema_stripped() {
// String schema that contains $schema — should be parsed AND normalized
let schema = serde_json::Value::String(
r#"{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{}}"#.to_string(),
);
let result = normalize_schema_for_provider(&schema, "openai");
assert!(result.is_object());
assert_eq!(result["type"], "object");
assert!(result.get("$schema").is_none());
}
}