1pub use oxios_mcp::{
7 McpBridge, McpCapabilities, McpClient, McpContentBlock, McpError, McpRequest, McpResponse,
8 McpServer, McpServerConfig, McpTool, McpToolCallResult, McpToolsResult, MappedResource,
9 ClientInfo, InitializeParams, InitializeResult, ServerInfo,
10};
11
12use crate::program::{ArgumentDef, ToolDef};
13
14pub fn mcp_tool_to_tool_def(tool: &McpTool) -> ToolDef {
19 let arguments = if let Some(properties) = tool
20 .input_schema()
21 .get("properties")
22 .and_then(|p| p.as_object())
23 {
24 let required_list: Vec<&str> = tool
25 .input_schema()
26 .get("required")
27 .and_then(|r| r.as_array())
28 .map(|arr| arr.iter().filter_map(|v| v.as_str()).collect())
29 .unwrap_or_default();
30
31 properties
32 .iter()
33 .map(|(name, schema)| {
34 let description = schema
35 .get("description")
36 .and_then(|d| d.as_str())
37 .unwrap_or("No description")
38 .to_string();
39 let required = required_list.iter().any(|r| *r == name)
40 && schema.get("default").is_none();
41
42 ArgumentDef {
43 name: name.clone(),
44 description,
45 required,
46 default: schema
47 .get("default")
48 .and_then(|d| d.as_str().map(String::from)),
49 }
50 })
51 .collect()
52 } else {
53 Vec::new()
54 };
55
56 ToolDef {
57 name: tool.name().to_string(),
58 description: tool.description().to_string(),
59 arguments,
60 command: String::new(),
61 }
62}
63
64pub async fn list_tool_defs(bridge: &McpBridge) -> anyhow::Result<Vec<ToolDef>> {
66 let tools = bridge.list_tools().await?;
67 Ok(tools.iter().map(mcp_tool_to_tool_def).collect())
68}
69
70pub async fn cached_tool_defs(bridge: &McpBridge, server_name: &str) -> Option<Vec<ToolDef>> {
72 bridge
73 .cached_tools(server_name)
74 .await
75 .map(|tools| tools.iter().map(mcp_tool_to_tool_def).collect())
76}
77
78pub async fn refresh_tool_defs(bridge: &McpBridge, server_name: &str) -> anyhow::Result<Vec<ToolDef>> {
80 let tools = bridge.refresh_tools(server_name).await?;
81 Ok(tools.iter().map(mcp_tool_to_tool_def).collect())
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn test_mcp_tool_to_tool_def() {
90 let mcp_tool = McpTool {
91 name: "test_tool".to_string(),
92 description: "A test tool".to_string(),
93 input_schema: serde_json::json!({
94 "type": "object",
95 "properties": {
96 "arg1": {
97 "type": "string",
98 "description": "First argument"
99 },
100 "arg2": {
101 "type": "number",
102 "description": "Second argument",
103 "default": "42"
104 }
105 },
106 "required": ["arg1"]
107 }),
108 };
109
110 let tool_def = mcp_tool_to_tool_def(&mcp_tool);
111
112 assert_eq!(tool_def.name, "test_tool");
113 assert_eq!(tool_def.description, "A test tool");
114 assert_eq!(tool_def.arguments.len(), 2);
115
116 let arg1 = tool_def
117 .arguments
118 .iter()
119 .find(|a| a.name == "arg1")
120 .unwrap();
121 assert!(arg1.required);
122 assert_eq!(arg1.description, "First argument");
123
124 let arg2 = tool_def
125 .arguments
126 .iter()
127 .find(|a| a.name == "arg2")
128 .unwrap();
129 assert!(!arg2.required);
130 assert_eq!(arg2.default, Some("42".to_string()));
131 }
132
133 #[test]
134 fn test_mcp_tool_to_tool_def_no_properties() {
135 let mcp_tool = McpTool {
136 name: "simple".to_string(),
137 description: "No args".to_string(),
138 input_schema: serde_json::json!({"type": "object"}),
139 };
140
141 let tool_def = mcp_tool_to_tool_def(&mcp_tool);
142 assert!(tool_def.arguments.is_empty());
143 assert_eq!(tool_def.command, "");
144 }
145}