# Tools, Resources, Prompts How to define, register, and call MCP primitives. See [Core Concepts](Core-Concepts.md) for Context/State basics. ## Tools (`#[tool]`) ```rust use fastrmcp::prelude::*; use fastrmcp_macros::tool; use fastrmcp_core::error::MCPError; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] struct Stats { mean: f64, count: usize } // Option parameters are optional; non-Option are required. #[tool] async fn summarize(numbers: Vec, precision: Option, ctx: Context) -> Result { ctx.report_progress(1, 3, Some("validating")).await.ok(); if numbers.is_empty() { return Err(MCPError::InvalidParams("numbers cannot be empty".into())); } let mean = numbers.iter().sum::() / numbers.len() as f64; let factor = 10f64.powi(precision.unwrap_or(2) as i32); let rounded = (mean * factor).round() / factor; ctx.log_info("ready").await.ok(); Ok(Stats { mean: rounded, count: numbers.len() }) } ``` - Macro auto-registers tool info + JSON schema from the signature and doc comments. - Return types: any serde-friendly type; strings/numbers/bools are coerced to `ToolResult::text`, complex types are JSON pretty-printed. - Pitfall: Parameter types must be serde-deserializable; add `#[derive(Serialize, Deserialize)]` to structs/enums. ### Call Shape ```json {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"summarize","arguments":{"numbers":[1,2,3]}}} ``` ## Resources (`#[resource("uri://{template}")]`) ```rust use fastrmcp_macros::resource; #[resource("file:///{path}")] async fn read_file(path: String, ctx: Context) -> Result { let content = tokio::fs::read_to_string(&path).await?; ctx.log_info(format!("read {path}")).await.ok(); Ok(content) } #[resource("config://{section}/{key}")] async fn config(section: String, key: String) -> Result { Ok(serde_json::json!({ "section": section, "key": key, "value": "demo" })) } ``` - URI template vars become parameters; unmatched URIs yield `MethodNotFound`. - `Context` is optional; use it to access client info or send progress/logs. - Registry requires server info before registration; do not manually call `ResourceRegistry::register_from_inventory` without first setting server info (handled by `FastMCP::new(...).run()`). - Return `ResourceContent` implicitly via helper; returning `String` becomes `text/plain` with URI preserved. ### Read Call Shape ```json {"jsonrpc":"2.0","id":3,"method":"resources/read","params":{"uri":"file:///tmp/demo.txt"}} ``` ## Prompts (`#[prompt]`) ```rust use fastrmcp_macros::prompt; #[prompt] async fn reviewer(file_name: String) -> Result { Ok(PromptResult { description: Some(format!("Review {file_name}")), messages: vec![ PromptMessage::system("You are a strict Rust reviewer."), PromptMessage::user(format!("Please review {file_name}")), ], }) } ``` - Arguments are serde-deserializable and optional unless typed otherwise. - Return `PromptResult` with `messages` (Text/Image/Resource content); descriptions appear in prompt metadata. ### Fetch Call Shape ```json {"jsonrpc":"2.0","id":4,"method":"prompts/get","params":{"name":"reviewer","arguments":{"file_name":"main.rs"}}} ``` ## Advanced Patterns - **Context injection**: Add `Context` to any handler signature; it is filled automatically. - **State**: Add `State` to access shared data (`state.get::()?`). - **Custom return types**: For binary resources, return `ResourceContent::binary(...)`. - **URI templates**: Avoid overlapping patterns; conflicting templates cause `ServerError -32002`. - **Validation**: Prefer `InvalidParams` for user errors; reserve `InternalError`/`ServerError` for server faults. - **Ordering**: Tools/resources/prompts are available after `initialize`; ensure `initialize` runs before list/read/call in clients. Related: [Core Concepts](Core-Concepts.md) • [Middleware Cookbook](Middleware-Cookbook.md) for metadata patterns • [Troubleshooting](Troubleshooting.md) for schema/param issues. [Back to Home](Home.md)