| Crates.io | turbomcp-wasm-macros |
| lib.rs | turbomcp-wasm-macros |
| version | 3.0.0-beta.3 |
| created_at | 2026-01-13 19:36:04.784079+00 |
| updated_at | 2026-01-22 16:45:33.17585+00 |
| description | Procedural macros for TurboMCP WASM servers - zero-boilerplate MCP server development |
| homepage | https://turbomcp.org |
| repository | https://github.com/anthropics/turbomcp |
| max_upload_size | |
| id | 2041014 |
| size | 28,919 |
Zero-boilerplate procedural macros for building MCP servers in WASM environments like Cloudflare Workers, Deno Deploy, and other edge platforms.
#[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 handlersAdd 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 {
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
}
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#[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 { ... }
Your struct must implement Clone for the generated code to work, as handlers need to clone the server instance.
MIT