mcp-host-macros 0.1.26

Procedural macros for mcp-host crate
Documentation
//! Procedural macros for mcp-host
//!
//! Provides attribute macros for ergonomic MCP server definition.
//!
//! ## Usage
//!
//! ```rust,ignore
//! use mcp_host::prelude::*;
//! use schemars::JsonSchema;
//!
//! #[derive(Deserialize, JsonSchema)]
//! struct CalcParams { x: f64, y: f64 }
//!
//! #[mcp_router]
//! impl MyServer {
//!     #[mcp_tool(name = "calculate")]
//!     async fn calculate(&self, ctx: Ctx, params: Parameters<CalcParams>) -> ToolResult {
//!         Ok(ToolOutput::text(format!("Result: {}", params.0.x + params.0.y)))
//!     }
//!
//!     #[mcp_prompt(name = "greeting", argument(name = "name", required = true))]
//!     async fn greeting(&self, ctx: Ctx, args: Value) -> PromptResult {
//!         let name = args.get("name").and_then(|v| v.as_str()).unwrap_or("World");
//!         prompt_messages(vec![user_message(format!("Hello, {name}!"))])
//!     }
//!
//!     #[mcp_resource(uri = "config:///app", name = "config")]
//!     async fn config(&self, ctx: Ctx) -> ResourceResult {
//!         Ok(vec![text_resource("config:///app", "{\"version\": \"1.0\"}")])
//!     }
//! }
//!
//! // Registration:
//! let server = Arc::new(MyServer::new());
//! MyServer::tool_router().register_all(&tool_registry, server.clone());
//! MyServer::prompt_router().register_all(&prompt_manager, server.clone());
//! MyServer::resource_router().register_all(&resource_manager, server);
//! ```

mod prompt;
mod resource;
mod resource_template;
mod router;
mod tool;

use proc_macro::TokenStream;

/// Mark an async function as an MCP tool handler
///
/// Generates:
/// - `{fn_name}_tool_info()` - Returns `ToolInfo` with schema from `Parameters<T>`
/// - `{fn_name}_handler()` - Handler wrapper for the router
/// - `{fn_name}_visibility()` - Visibility predicate (if `visible` attribute specified)
///
/// # Attributes
///
/// - `name`: Tool name (default: function name)
/// - `description`: Tool description (default: doc comments)
/// - `visible`: Visibility predicate expression (default: always visible)
///
/// # Example
///
/// ```rust,ignore
/// #[mcp_tool(name = "calculate", description = "Performs math")]
/// async fn calculate(&self, ctx: Ctx, params: Parameters<CalcParams>) -> ToolResult {
///     Ok(vec![text(&format!("Result: {}", params.0.value))])
/// }
/// ```
#[proc_macro_attribute]
pub fn mcp_tool(attr: TokenStream, item: TokenStream) -> TokenStream {
    tool::expand_mcp_tool(attr, item)
}

/// Mark an async function as an MCP prompt handler
///
/// Generates:
/// - `{fn_name}_prompt_info()` - Returns `PromptInfo` with arguments metadata
/// - `{fn_name}_handler()` - Handler wrapper for the router
/// - `{fn_name}_visibility()` - Visibility predicate (if `visible` attribute specified)
///
/// # Attributes
///
/// - `name`: Prompt name (default: function name)
/// - `description`: Prompt description (default: doc comments)
/// - `visible`: Visibility predicate expression (default: always visible)
/// - `argument`: Prompt argument definition (can be repeated):
///   - `name`: Argument name (required)
///   - `description`: Argument description (optional)
///   - `required`: Whether the argument is required (optional)
///
/// # Example
///
/// ```rust,ignore
/// #[mcp_prompt(
///     name = "greeting",
///     argument(name = "name", description = "Name to greet", required = true)
/// )]
/// async fn greeting(&self, ctx: Ctx, args: Value) -> PromptResult {
///     let name = args.get("name").and_then(|v| v.as_str()).unwrap_or("World");
///     prompt_with_description("A greeting", vec![user_message(format!("Hello, {name}!"))])
/// }
/// ```
#[proc_macro_attribute]
pub fn mcp_prompt(attr: TokenStream, item: TokenStream) -> TokenStream {
    prompt::expand_mcp_prompt(attr, item)
}

/// Mark an async function as an MCP resource handler
///
/// Generates:
/// - `{fn_name}_resource_info()` - Returns `ResourceInfo` with metadata
/// - `{fn_name}_handler()` - Handler wrapper for the router
/// - `{fn_name}_visibility()` - Visibility predicate (if `visible` attribute specified)
///
/// # Attributes
///
/// - `uri`: Resource URI (required)
/// - `name`: Resource name (required)
/// - `description`: Resource description (default: doc comments)
/// - `mime_type`: MIME type for the resource content
/// - `visible`: Visibility predicate expression (default: always visible)
///
/// # Example
///
/// ```rust,ignore
/// #[mcp_resource(uri = "config:///app", name = "app_config", mime_type = "application/json")]
/// async fn app_config(&self, ctx: Ctx) -> ResourceResult {
///     Ok(vec![text_resource("config:///app", "{\"version\": \"1.0\"}")])
/// }
/// ```
#[proc_macro_attribute]
pub fn mcp_resource(attr: TokenStream, item: TokenStream) -> TokenStream {
    resource::expand_mcp_resource(attr, item)
}

/// Mark an async function as an MCP resource template handler
///
/// Generates:
/// - `{fn_name}_template_info()` - Returns `ResourceTemplateInfo` with metadata
/// - `{fn_name}_handler()` - Handler wrapper for the router
/// - `{fn_name}_visibility()` - Visibility predicate (if `visible` attribute specified)
///
/// # Attributes
///
/// - `uri_template`: URI template pattern (required, e.g., "file:///{path}")
/// - `name`: Template name (required)
/// - `title`: Display title
/// - `description`: Template description (default: doc comments)
/// - `mime_type`: MIME type for resources matching this template
/// - `visible`: Visibility predicate expression (default: always visible)
///
/// # Example
///
/// ```rust,ignore
/// #[mcp_resource_template(uri_template = "file:///{path}", name = "files")]
/// async fn read_file(&self, ctx: Ctx) -> ResourceResult {
///     let path = ctx.get_uri_param("path").ok_or(ResourceError::InvalidUri("missing path".into()))?;
///     let content = std::fs::read_to_string(path)?;
///     Ok(vec![text_resource(format!("file:///{path}"), content)])
/// }
/// ```
#[proc_macro_attribute]
pub fn mcp_resource_template(attr: TokenStream, item: TokenStream) -> TokenStream {
    resource_template::expand_mcp_resource_template(attr, item)
}

/// Router macro for MCP servers
///
/// Scans an impl block for all MCP handler attributes (`#[mcp_tool]`, `#[mcp_prompt]`,
/// `#[mcp_resource]`, `#[mcp_resource_template]`) and generates the appropriate router
/// methods for each type found.
///
/// # Attributes
///
/// - `tools`: Name of generated tool router function (default: "tool_router")
/// - `prompts`: Name of generated prompt router function (default: "prompt_router")
/// - `resources`: Name of generated resource router function (default: "resource_router")
/// - `templates`: Name of generated template router function (default: "resource_template_router")
///
/// # Example
///
/// ```rust,ignore
/// #[mcp_router]
/// impl MyServer {
///     #[mcp_tool(name = "echo")]
///     async fn echo(&self, ctx: Ctx, params: Parameters<EchoParams>) -> ToolResult { ... }
///
///     #[mcp_prompt(name = "greeting")]
///     async fn greeting(&self, ctx: Ctx, args: Value) -> PromptResult { ... }
///
///     #[mcp_resource(uri = "config:///app", name = "config")]
///     async fn config(&self, ctx: Ctx) -> ResourceResult { ... }
///
///     #[mcp_resource_template(uri_template = "file:///{path}", name = "files")]
///     async fn files(&self, ctx: Ctx) -> ResourceResult { ... }
/// }
///
/// // Generated (only for types that have at least one handler):
/// // MyServer::tool_router() -> McpToolRouter<MyServer>
/// // MyServer::prompt_router() -> McpPromptRouter<MyServer>
/// // MyServer::resource_router() -> McpResourceRouter<MyServer>
/// // MyServer::resource_template_router() -> McpResourceTemplateRouter<MyServer>
/// ```
#[proc_macro_attribute]
pub fn mcp_router(attr: TokenStream, item: TokenStream) -> TokenStream {
    router::expand_mcp_router(attr, item)
}