18 releases (stable)
Uses new Rust 2024
| new 3.0.11 | Apr 7, 2026 |
|---|---|
| 3.0.10 | Mar 27, 2026 |
| 3.0.0-exp.2 | Jan 13, 2026 |
| 3.0.0-beta.5 | Feb 24, 2026 |
#134 in #cloudflare
Used in turbomcp-wasm
42KB
659 lines
turbomcp-wasm-macros
Zero-boilerplate procedural macros for building MCP servers in WASM environments like Cloudflare Workers, Deno Deploy, and other edge platforms.
Features
#[server]- Transform impl blocks into MCP servers#[tool]- Mark methods as MCP tool handlers#[resource]- Mark methods as MCP resource handlers#[prompt]- Mark methods as MCP prompt handlers
Usage
Add turbomcp-wasm with the macros feature to your Cargo.toml:
[dependencies]
turbomcp-wasm = { version = "3.0", default-features = false, features = ["macros"] }
worker = "0.7"
serde = { version = "1.0", features = ["derive"] }
schemars = "1.0"
Then define your server:
use turbomcp_wasm::prelude::*;
use serde::Deserialize;
#[derive(Clone)]
struct MyServer {
greeting: String,
}
#[derive(Deserialize, schemars::JsonSchema)]
struct GreetArgs {
/// The name of the person to greet
name: String,
}
#[server(name = "my-server", version = "1.0.0", description = "My MCP server")]
impl MyServer {
#[tool("Greet someone by name")]
async fn greet(&self, args: GreetArgs) -> String {
format!("{}, {}!", self.greeting, args.name)
}
#[tool("Get server status")]
async fn status(&self) -> String {
"Server is running".to_string()
}
#[resource("config://app")]
async fn config(&self, uri: String) -> ResourceResult {
ResourceResult::text(&uri, r#"{"theme": "dark"}"#)
}
#[prompt("Default greeting")]
async fn greeting_prompt(&self) -> PromptResult {
PromptResult::user("Hello! How can I help?")
}
}
// In your Cloudflare Worker handler:
#[event(fetch)]
async fn fetch(req: Request, _env: Env, _ctx: Context) -> Result<Response> {
let server = MyServer { greeting: "Hello".into() };
server.into_mcp_server().handle(req).await
}
Generated Methods
The #[server] macro generates the following methods on your struct:
into_mcp_server(self) -> McpServer- Convert to a fully-configured MCP serverserver_info() -> (&'static str, &'static str)- Get (name, version) tupleget_tools_metadata() -> Vec<(&'static str, &'static str)>- Get tool metadataget_resources_metadata() -> Vec<(&'static str, &'static str)>- Get resource metadataget_prompts_metadata() -> Vec<(&'static str, &'static str)>- Get prompt metadata
Attribute Syntax
#[server]
#[server(name = "my-server", version = "1.0.0", description = "Optional description")]
impl MyServer { ... }
#[tool]
#[tool("Description of the tool")]
async fn my_tool(&self, args: MyArgs) -> String { ... }
// Or without arguments
#[tool("Description")]
async fn no_args_tool(&self) -> String { ... }
#[resource]
#[resource("config://app")]
async fn config(&self, uri: String) -> ResourceResult { ... }
// With URI template
#[resource("file://{path}")]
async fn file(&self, uri: String) -> ResourceResult { ... }
#[prompt]
#[prompt("Description of the prompt")]
async fn my_prompt(&self) -> PromptResult { ... }
// With optional arguments
#[prompt("Prompt with args")]
async fn prompt_with_args(&self, args: Option<MyArgs>) -> PromptResult { ... }
Parameter Descriptions
Add descriptions to tool parameters using doc comments on your args struct fields:
#[derive(Deserialize, schemars::JsonSchema)]
struct SearchArgs {
/// The search query to execute
query: String,
/// Maximum number of results (default: 10)
limit: Option<u32>,
}
Alternatively, use the #[schemars(description = "...")] attribute:
#[derive(Deserialize, schemars::JsonSchema)]
struct SearchArgs {
#[schemars(description = "The search query to execute")]
query: String,
}
These descriptions appear in the JSON schema and help LLMs understand how to use your tools.
Requirements
Your struct must implement Clone for the generated code to work, as handlers need to clone the server instance.
License
MIT
Dependencies
~105–465KB
~11K SLoC