dejadb_core/format/tool_schema/
mcp.rs1use 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}