pmcp_macros/lib.rs
1//! Procedural macros for PMCP SDK
2//!
3//! This crate provides attribute macros to reduce boilerplate when implementing
4//! MCP servers with tools, prompts, and resources.
5//!
6//! # Features
7//!
8//! - `#[tool]` - Define a tool with automatic schema generation
9//! - `#[tool_router]` - Collect tools from an impl block
10//! - `#[prompt]` - Define a prompt template
11//! - `#[resource]` - Define a resource handler
12//!
13//! # Examples
14//!
15//! ## Tool Definition
16//!
17//! ```rust,ignore
18//! use pmcp_macros::{tool, tool_router};
19//! use serde::{Deserialize, Serialize};
20//! use schemars::JsonSchema;
21//!
22//! #[derive(Debug, Deserialize, JsonSchema)]
23//! struct CalculateParams {
24//! a: i32,
25//! b: i32,
26//! operation: String,
27//! }
28//!
29//! #[derive(Debug, Serialize, JsonSchema)]
30//! struct CalculateResult {
31//! result: i32,
32//! }
33//!
34//! #[tool_router]
35//! impl Calculator {
36//! #[tool(description = "Perform arithmetic operations")]
37//! async fn calculate(&self, params: CalculateParams) -> Result<CalculateResult, String> {
38//! let result = match params.operation.as_str() {
39//! "add" => params.a + params.b,
40//! "subtract" => params.a - params.b,
41//! "multiply" => params.a * params.b,
42//! "divide" => {
43//! if params.b == 0 {
44//! return Err("Division by zero".to_string());
45//! }
46//! params.a / params.b
47//! }
48//! _ => return Err("Unknown operation".to_string()),
49//! };
50//! Ok(CalculateResult { result })
51//! }
52//! }
53//! ```
54
55use proc_macro::TokenStream;
56use syn::{parse_macro_input, ItemFn, ItemImpl};
57
58mod tool;
59mod tool_router;
60mod utils;
61
62/// Defines a tool handler with automatic schema generation.
63///
64/// # Attributes
65///
66/// - `name` - Optional tool name (defaults to function name)
67/// - `description` - Tool description (required)
68/// - `annotations` - Additional metadata for the tool
69///
70/// # Examples
71///
72/// ```rust,ignore
73/// #[tool(description = "Add two numbers")]
74/// async fn add(a: i32, b: i32) -> Result<i32, String> {
75/// Ok(a + b)
76/// }
77/// ```
78///
79/// With custom name and annotations:
80///
81/// ```rust,ignore
82/// #[tool(
83/// name = "math_add",
84/// description = "Add two numbers",
85/// annotations(category = "math", complexity = "simple")
86/// )]
87/// async fn add(a: i32, b: i32) -> Result<i32, String> {
88/// Ok(a + b)
89/// }
90/// ```
91#[proc_macro_attribute]
92pub fn tool(args: TokenStream, input: TokenStream) -> TokenStream {
93 let input = parse_macro_input!(input as ItemFn);
94
95 tool::expand_tool(args.into(), input)
96 .unwrap_or_else(|err| err.to_compile_error())
97 .into()
98}
99
100/// Collects all tool methods from an impl block and generates a router.
101///
102/// This macro scans an impl block for methods marked with `#[tool]` and
103/// automatically generates registration code for them.
104///
105/// # Examples
106///
107/// ```rust,ignore
108/// #[tool_router]
109/// impl MyServer {
110/// #[tool(description = "Get current time")]
111/// async fn get_time(&self) -> Result<String, Error> {
112/// Ok(chrono::Utc::now().to_string())
113/// }
114///
115/// #[tool(description = "Echo message")]
116/// async fn echo(&self, message: String) -> Result<String, Error> {
117/// Ok(message)
118/// }
119/// }
120/// ```
121///
122/// The macro generates:
123/// - A `tools()` method returning all tool definitions
124/// - A `handle_tool()` method for routing tool calls
125/// - Automatic schema generation for parameters
126#[proc_macro_attribute]
127pub fn tool_router(args: TokenStream, input: TokenStream) -> TokenStream {
128 let input = parse_macro_input!(input as ItemImpl);
129
130 tool_router::expand_tool_router(args.into(), input)
131 .unwrap_or_else(|err| err.to_compile_error())
132 .into()
133}
134
135/// Defines a prompt template with typed arguments.
136///
137/// # Examples
138///
139/// ```rust,ignore
140/// #[prompt(
141/// name = "code_review",
142/// description = "Review code for quality issues"
143/// )]
144/// async fn review_code(&self, language: String, code: String) -> Result<String, Error> {
145/// Ok(format!("Review this {} code:\n{}", language, code))
146/// }
147/// ```
148#[proc_macro_attribute]
149pub fn prompt(_args: TokenStream, input: TokenStream) -> TokenStream {
150 // TODO: Implement prompt macro
151 input
152}
153
154/// Defines a resource handler with URI pattern matching.
155///
156/// # Examples
157///
158/// ```rust,ignore
159/// #[resource(
160/// uri_template = "file:///{path}",
161/// mime_type = "text/plain"
162/// )]
163/// async fn read_file(&self, path: String) -> Result<String, Error> {
164/// std::fs::read_to_string(path).map_err(|e| e.into())
165/// }
166/// ```
167#[proc_macro_attribute]
168pub fn resource(_args: TokenStream, input: TokenStream) -> TokenStream {
169 // TODO: Implement resource macro
170 input
171}