1pub use oxios_mcp::{
7 ClientInfo, InitializeParams, InitializeResult, MappedResource, McpBridge, McpCapabilities,
8 McpClient, McpContentBlock, McpError, McpRequest, McpResponse, McpServer, McpServerConfig,
9 McpTool, McpToolCallResult, McpToolsResult, 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 =
40 required_list.iter().any(|r| *r == name) && 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(
80 bridge: &McpBridge,
81 server_name: &str,
82) -> anyhow::Result<Vec<ToolDef>> {
83 let tools = bridge.refresh_tools(server_name).await?;
84 Ok(tools.iter().map(mcp_tool_to_tool_def).collect())
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn test_mcp_tool_to_tool_def() {
93 let mcp_tool = McpTool {
94 name: "test_tool".to_string(),
95 description: "A test tool".to_string(),
96 input_schema: serde_json::json!({
97 "type": "object",
98 "properties": {
99 "arg1": {
100 "type": "string",
101 "description": "First argument"
102 },
103 "arg2": {
104 "type": "number",
105 "description": "Second argument",
106 "default": "42"
107 }
108 },
109 "required": ["arg1"]
110 }),
111 };
112
113 let tool_def = mcp_tool_to_tool_def(&mcp_tool);
114
115 assert_eq!(tool_def.name, "test_tool");
116 assert_eq!(tool_def.description, "A test tool");
117 assert_eq!(tool_def.arguments.len(), 2);
118
119 let arg1 = tool_def
120 .arguments
121 .iter()
122 .find(|a| a.name == "arg1")
123 .unwrap();
124 assert!(arg1.required);
125 assert_eq!(arg1.description, "First argument");
126
127 let arg2 = tool_def
128 .arguments
129 .iter()
130 .find(|a| a.name == "arg2")
131 .unwrap();
132 assert!(!arg2.required);
133 assert_eq!(arg2.default, Some("42".to_string()));
134 }
135
136 #[test]
137 fn test_mcp_tool_to_tool_def_no_properties() {
138 let mcp_tool = McpTool {
139 name: "simple".to_string(),
140 description: "No args".to_string(),
141 input_schema: serde_json::json!({"type": "object"}),
142 };
143
144 let tool_def = mcp_tool_to_tool_def(&mcp_tool);
145 assert!(tool_def.arguments.is_empty());
146 assert_eq!(tool_def.command, "");
147 }
148}