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}