Documentation Index
Fetch the complete documentation index at: https://mintlify.com/microsoft/mcp-for-beginners/llms.txt
Use this file to discover all available pages before exploring further.
The Rust sample uses the rmcp crate, which provides procedural macros (#[tool], #[tool_router], #[tool_handler]) to register MCP tools with minimal boilerplate. The server runs asynchronously using Tokio with a stdio transport.
Basic calculator server
Located at 03-GettingStarted/samples/rust.
Clone and navigate
git clone https://github.com/microsoft/mcp-for-beginners.git
cd mcp-for-beginners/03-GettingStarted/samples/rust
Cargo.toml
[package]
name = "calculator"
version = "1.0.0"
edition = "2024"
[dependencies]
rmcp = { version = "0.5.0", features = ["server", "transport-io"] }
serde = "1.0.219"
tokio = { version = "1.46.0", features = ["rt-multi-thread"] }
Server code
use rmcp::{
ServerHandler, ServiceExt,
handler::server::{router::tool::ToolRouter, tool::Parameters},
model::{ServerCapabilities, ServerInfo},
schemars, tool, tool_handler, tool_router,
transport::stdio,
};
use std::error::Error;
// Shared input schema for binary operations
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct CalculatorRequest {
pub a: f64,
pub b: f64,
}
// The Calculator struct holds a tool router generated by the macro
#[derive(Debug, Clone)]
pub struct Calculator {
tool_router: ToolRouter<Self>,
}
// #[tool_router] generates the routing glue for all #[tool] methods
#[tool_router]
impl Calculator {
pub fn new() -> Self {
Self {
tool_router: Self::tool_router(),
}
}
#[tool(description = "Adds a and b")]
async fn add(
&self,
Parameters(CalculatorRequest { a, b }): Parameters<CalculatorRequest>,
) -> String {
(a + b).to_string()
}
#[tool(description = "Subtracts b from a")]
async fn subtract(
&self,
Parameters(CalculatorRequest { a, b }): Parameters<CalculatorRequest>,
) -> String {
(a - b).to_string()
}
#[tool(description = "Multiply a with b")]
async fn multiply(
&self,
Parameters(CalculatorRequest { a, b }): Parameters<CalculatorRequest>,
) -> String {
(a * b).to_string()
}
#[tool(description = "Divides a by b")]
async fn divide(
&self,
Parameters(CalculatorRequest { a, b }): Parameters<CalculatorRequest>,
) -> String {
if b == 0.0 {
"Error: Division by zero".to_string()
} else {
(a / b).to_string()
}
}
}
// #[tool_handler] wires the tool router into the MCP ServerHandler trait
#[tool_handler]
impl ServerHandler for Calculator {
fn get_info(&self) -> ServerInfo {
ServerInfo {
instructions: Some("A simple calculator tool".into()),
capabilities: ServerCapabilities::builder().enable_tools().build(),
..Default::default()
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// Serve over stdio and wait until the client disconnects
let service = Calculator::new().serve(stdio()).await?;
service.waiting().await?;
Ok(())
}
How the macros work
| Macro | Purpose |
|---|
#[tool_router] | Applied to the impl Calculator block — generates a tool_router() method that builds a dispatch table from all #[tool] methods |
#[tool(description = "...")] | Marks an async method as an MCP tool and attaches a description used in the tool schema |
#[tool_handler] | Applied to the impl ServerHandler for Calculator block — wires ToolRouter into the MCP protocol handler |
Parameters<T> | Deserializes and validates the incoming JSON arguments into a typed struct |
All four tools share the same CalculatorRequest struct:
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct CalculatorRequest {
pub a: f64,
pub b: f64,
}
schemars::JsonSchema is derived automatically — rmcp uses this to generate the JSON Schema that clients see when they call tools/list.
| Tool | Input | Returns |
|---|
add | { a, b } | "a + b" as a string |
subtract | { a, b } | "a - b" as a string |
multiply | { a, b } | "a * b" as a string |
divide | { a, b } | "a / b" or "Error: Division by zero" |
Testing with MCP Inspector
Launch the Inspector in a new terminal
npx @modelcontextprotocol/inspector
Connect
Configure the Inspector to launch cargo run as the server process with stdio transport, then click List Tools and call any operation.