use crate::mcp::{QualifiedMcpToolName, protocol::Tool};
use serde_json::{Value, json};
pub(crate) fn provider_tool_definition(qualified: &QualifiedMcpToolName, tool: &Tool) -> Value {
let name = qualified.as_str();
let description = tool
.description
.as_deref()
.or(tool.title.as_deref())
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToString::to_string)
.unwrap_or_else(|| format!("MCP tool from server {}", qualified.server()));
json!({
"type": "function",
"name": name,
"description": description,
"parameters": object_schema_or_default(&tool.input_schema),
})
}
fn object_schema_or_default(schema: &Value) -> Value {
if schema.as_object().and_then(|object| object.get("type"))
== Some(&Value::String("object".to_string()))
{
schema.clone()
} else {
default_schema()
}
}
fn default_schema() -> Value {
json!({"type":"object","properties":{},"additionalProperties":true})
}
#[cfg(test)]
mod tests {
use super::*;
fn tool(name: &str, input_schema: Value) -> Tool {
Tool {
name: name.to_string(),
title: Some("Title".to_string()),
description: None,
input_schema,
output_schema: None,
annotations: None,
}
}
fn qualified(server: &str, name: &str) -> QualifiedMcpToolName {
QualifiedMcpToolName::new(server, name).unwrap()
}
#[test]
fn converts_tool_to_provider_definition() {
let definition = provider_tool_definition(
&qualified("mock", "echo"),
&Tool {
description: Some("Echo input".to_string()),
..tool(
"echo",
json!({"type":"object","properties":{"text":{"type":"string"}}}),
)
},
);
assert_eq!(definition["name"], "mcp__mock__echo");
assert_eq!(definition["description"], "Echo input");
}
#[test]
fn falls_back_for_missing_description_and_non_object_schema() {
let definition = provider_tool_definition(
&qualified("mock", "echo"),
&tool("echo", json!({"type":"string"})),
);
assert_eq!(definition["description"], "Title");
assert_eq!(
definition["parameters"],
json!({"type":"object","properties":{},"additionalProperties":true})
);
}
#[test]
fn rejects_unsafe_tool_names() {
for name in ["bad/name", "bad__name", ""] {
assert!(
QualifiedMcpToolName::new("mock", name).is_err(),
"accepted {name}"
);
}
}
}