rust_mcp_sdk/mcp_macros/
tool_box.rs

1#[macro_export]
2/// Generates an enum representing a toolbox with mcp tool variants and associated functionality.
3///
4/// **Note:** The macro assumes that tool types provided are annotated with the mcp_tool() macro.
5///
6/// This macro creates:
7/// - An enum with the specified name containing variants for each mcp tool
8/// - A `tools()` function returning a vector of supported tools
9/// - A `TryFrom<CallToolRequestParams>` implementation for converting requests to tool instances
10///
11/// # Arguments
12/// * `$enum_name` - The name to give the generated enum
13/// * `[$($tool:ident),*]` - A comma-separated list of tool types to include in the enum
14///
15///
16/// # Example
17/// ```ignore
18/// tool_box!(FileSystemTools, [ReadFileTool, EditFileTool, SearchFilesTool]);
19/// // Creates:
20/// // pub enum FileSystemTools {
21/// //     ReadFileTool(ReadFileTool),
22/// //     EditFileTool(EditFileTool),
23/// //     SearchFilesTool(SearchFilesTool),
24/// // }
25/// // pub fn tools() -> Vec<Tool> {
26/// //     vec![ReadFileTool::tool(), EditFileTool::tool(), SearchFilesTool::tool()]
27/// // }
28///
29/// // impl TryFrom<CallToolRequestParams> for FileSystemTools {
30/// //  //.......
31/// // }
32macro_rules! tool_box {
33    ($enum_name:ident, [$($tool:ident),* $(,)?]) => {
34        #[derive(Debug)]
35        pub enum $enum_name {
36            $(
37                // Just create enum variants for each tool
38                $tool($tool),
39            )*
40        }
41
42        /// Returns the name of the tool as a String
43        impl $enum_name {
44            pub fn tool_name(&self) -> String {
45                match self {
46                    $(
47                        $enum_name::$tool(_) => $tool::tool_name(),
48                    )*
49                }
50            }
51
52            /// Returns a vector containing instances of all supported tools
53            pub fn tools() -> Vec<rust_mcp_sdk::schema::Tool> {
54                vec![
55                    $(
56                        $tool::tool(),
57                    )*
58                ]
59            }
60        }
61
62
63
64
65        impl TryFrom<rust_mcp_sdk::schema::CallToolRequestParams> for $enum_name {
66            type Error = rust_mcp_sdk::schema::schema_utils::CallToolError;
67
68            /// Attempts to convert a tool request into the appropriate tool variant
69            fn try_from(value: rust_mcp_sdk::schema::CallToolRequestParams) -> Result<Self, Self::Error> {
70                let arguments = value
71                    .arguments
72                    .ok_or(rust_mcp_sdk::schema::schema_utils::CallToolError::invalid_arguments(
73                        &value.name,
74                        Some("Missing 'arguments' field in the request".to_string())
75                    ))?;
76
77                    let v = serde_json::to_value(arguments).map_err(|err| {
78                        rust_mcp_sdk::schema::schema_utils::CallToolError::invalid_arguments(
79                            &value.name,
80                            Some(format!("{err}")),
81                        )
82                    })?;
83
84                    match value.name {
85                        $(
86                            name if name == $tool::tool_name().as_str() => {
87                                Ok(Self::$tool(serde_json::from_value(v).map_err(rust_mcp_sdk::schema::schema_utils::CallToolError::new)?))
88                            }
89                        )*
90                        _ => {
91                               Err(
92                                rust_mcp_sdk::schema::schema_utils::CallToolError::unknown_tool(value.name.to_string())
93                              )
94                        }
95                    }
96
97            }
98        }
99    }
100}