rustyclaw_core/gateway/
mcp_handler.rs1#[cfg(feature = "mcp")]
4use tracing::{debug, instrument, warn};
5
6#[cfg(feature = "mcp")]
7use crate::mcp::McpManager;
8#[cfg(feature = "mcp")]
9use std::sync::Arc;
10#[cfg(feature = "mcp")]
11use tokio::sync::Mutex;
12
13#[cfg(feature = "mcp")]
14pub type SharedMcpManager = Arc<Mutex<McpManager>>;
15
16pub fn is_mcp_tool(name: &str) -> bool {
18 name.starts_with("mcp_")
19}
20
21#[cfg(feature = "mcp")]
25#[instrument(skip(args, mcp_mgr), fields(%name))]
26pub async fn execute_mcp_tool(
27 name: &str,
28 args: &serde_json::Value,
29 mcp_mgr: &SharedMcpManager,
30) -> Result<String, String> {
31 debug!("Executing MCP tool");
32
33 let mgr = mcp_mgr.lock().await;
34
35 match mgr.call_tool_by_name(name, args.clone()).await {
36 Ok(result) => {
37 if result.success {
38 Ok(result.to_llm_string())
39 } else {
40 Err(result
41 .error
42 .unwrap_or_else(|| "Unknown MCP error".to_string()))
43 }
44 }
45 Err(e) => {
46 warn!(tool = name, error = %e, "MCP tool call failed");
47 Err(e.to_string())
48 }
49 }
50}
51
52#[cfg(not(feature = "mcp"))]
54pub async fn execute_mcp_tool(
55 name: &str,
56 _args: &serde_json::Value,
57 _mcp_mgr: &(),
58) -> Result<String, String> {
59 Err(format!(
60 "MCP tool '{}' called but MCP support is not enabled. Rebuild with --features mcp",
61 name
62 ))
63}
64
65#[cfg(feature = "mcp")]
67pub async fn get_mcp_tool_schemas(mcp_mgr: &SharedMcpManager) -> Vec<serde_json::Value> {
68 let mgr = mcp_mgr.lock().await;
69 mgr.get_tool_schemas().await
70}
71
72#[cfg(not(feature = "mcp"))]
74pub async fn get_mcp_tool_schemas(_mcp_mgr: &()) -> Vec<serde_json::Value> {
75 Vec::new()
76}
77
78#[cfg(feature = "mcp")]
80pub async fn generate_mcp_prompt_section(mcp_mgr: &SharedMcpManager) -> String {
81 let mgr = mcp_mgr.lock().await;
82 let tools = mgr.list_all_tools().await;
83
84 if tools.is_empty() {
85 return String::new();
86 }
87
88 let mut section = String::from("\n## MCP Tools\n\n");
89 section.push_str("The following tools are provided by connected MCP servers:\n\n");
90
91 let mut by_server: std::collections::HashMap<String, Vec<_>> = std::collections::HashMap::new();
93 for tool in tools {
94 by_server
95 .entry(tool.server_name.clone())
96 .or_default()
97 .push(tool);
98 }
99
100 for (server, tools) in by_server {
101 section.push_str(&format!("### {} (MCP server)\n\n", server));
102 for tool in tools {
103 section.push_str(&format!(
104 "- **{}**: {}\n",
105 tool.prefixed_name(),
106 tool.description.as_deref().unwrap_or("(no description)")
107 ));
108 }
109 section.push('\n');
110 }
111
112 section
113}
114
115#[cfg(not(feature = "mcp"))]
117pub async fn generate_mcp_prompt_section(_mcp_mgr: &()) -> String {
118 String::new()
119}