Skip to main content

dejadb_core/format/tool_schema/
mcp.rs

1//! MCP `tools/list` adapter — MCP 2025-06-18 spec.
2//!
3//! Shape: {"name":..., "description":..., "inputSchema":..., "outputSchema"?:...,
4//!         "annotations"?: {"readOnlyHint":bool, "destructiveHint":bool, "idempotentHint":bool}}
5
6use serde_json::{json, Value};
7
8use crate::error::Result;
9use crate::types::{Tool, ToolAnnotations};
10
11use super::definition_parts;
12
13pub fn render(action: &Tool) -> Result<Value> {
14    let (name, description, input_schema) = definition_parts(action)?;
15    let mut obj = serde_json::Map::new();
16    obj.insert("name".to_string(), Value::String(name));
17    obj.insert("description".to_string(), Value::String(description));
18    obj.insert("inputSchema".to_string(), input_schema);
19    if let Some(ref o) = action.output_schema {
20        obj.insert("outputSchema".to_string(), o.clone());
21    }
22    if let Some(ref a) = action.annotations {
23        obj.insert("annotations".to_string(), mcp_annotations(a));
24    }
25    Ok(Value::Object(obj))
26}
27
28fn mcp_annotations(a: &ToolAnnotations) -> Value {
29    json!({
30        "readOnlyHint": a.read_only,
31        "destructiveHint": a.destructive,
32        "idempotentHint": a.idempotent,
33    })
34}
35
36#[cfg(test)]
37mod tests {
38    use super::super::tests::sample_def;
39    use super::*;
40
41    #[test]
42    fn shape_uses_camelcase_mcp_names() {
43        let v = render(&sample_def()).unwrap();
44        assert_eq!(v["name"], "slack_post_message");
45        assert!(v.get("inputSchema").is_some());
46        assert!(v.get("input_schema").is_none(), "MCP uses camelCase");
47    }
48
49    #[test]
50    fn annotations_carry_through() {
51        let mut a = sample_def();
52        a.annotations = Some(ToolAnnotations {
53            read_only: false,
54            destructive: false,
55            idempotent: true,
56        });
57        let v = render(&a).unwrap();
58        assert_eq!(v["annotations"]["idempotentHint"], true);
59    }
60}