Skip to main content

forge_sdk/mcp/
macros.rs

1//! Declarative `tool!` macro for convenient Tool-trait impls.
2//!
3//! See [`crate::tool!`] for usage.
4
5/// Declarative tool-trait impl.
6///
7/// # Example
8///
9/// ```ignore
10/// use forge_sdk::tool;
11/// use forge_sdk::mcp::{ToolInput, ToolOutput};
12/// use serde_json::json;
13///
14/// tool! {
15///     name: "greet",
16///     description: "Greet by name",
17///     schema: json!({"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}),
18///     call: |input: ToolInput| async move {
19///         let name = input.value["name"].as_str().unwrap_or("stranger");
20///         ToolOutput::text(format!("hello {name}"))
21///     },
22///     tool_type: GreetTool,
23/// }
24/// ```
25#[macro_export]
26macro_rules! tool {
27    (
28        name: $name:literal,
29        description: $desc:literal,
30        schema: $schema:expr,
31        call: |$input:ident : $input_ty:ty| async move $body:block,
32        tool_type: $ty:ident $(,)?
33    ) => {
34        #[derive(Clone, Copy, Debug, Default)]
35        // The caller names this struct, so it cannot carry a doc comment
36        // the macro wrote.
37        #[allow(missing_docs)]
38        pub struct $ty;
39
40        #[$crate::__private::async_trait]
41        impl $crate::mcp::Tool for $ty {
42            fn name(&self) -> &'static str {
43                $name
44            }
45            fn description(&self) -> &'static str {
46                $desc
47            }
48            fn input_schema(&self) -> ::serde_json::Value {
49                $schema
50            }
51            async fn call(&self, $input: $input_ty) -> $crate::mcp::ToolOutput {
52                $body
53            }
54        }
55    };
56}
57
58pub mod __private {
59    pub use async_trait::async_trait;
60}