How to Build an MCP Server in Rust
Build a Rust MCP server with the official rmcp SDK: one struct, one macro, tested in MCP Inspector and added to Claude Code or Claude Desktop.
You build a Rust MCP server with rmcp, the official Rust SDK (version 3.8.0 on crates.io in November 2026). You write a struct, mark a method with #[tool], and the macros turn it into a tool. The argument struct becomes the input schema and the attribute gives the description. You need Rust 1.88 or later and the tokio runtime.
The MCP site lists the Rust SDK as Tier 1. It implements the 2026-07-28 MCP spec and stays compatible with 2025-11-25 and earlier versions.
The MCP "Build an MCP server" tutorial pins an older rmcp (0.3) and says Rust 1.70 or higher. The SDK's own manifest says 1.88 for the current release, and its README links a migration guide for 3.x. This guide follows the current README.
Good for
- Rust developers who want Claude to call their own functions or services
- Teams that ship Rust services and want to expose them as MCP tools
- Anyone whose rmcp 0.x server stopped compiling on rmcp 3
Build, test and connect a Rust MCP server
- 1
Create a project and add the crate
You need Rust 1.88 or later from rustup.rs. Create a project with
cargo new calculatorandcd calculator, then add the dependencies with the commands in the Code section.Pick the
server,macrosandtransport-iofeatures ofrmcp. The last one gives you the stdio transport, which is how Claude Code and Claude Desktop launch a local server. - 2
Write src/main.rs
Copy
main.rsfrom the Code section.#[tool_router(server_handler)]is the short form for a server that only has tools. It skips the separateServerHandlerimpl.#[tool(description = ...)]marksaddas a tool. The fields ofAddParamsbecome its input schema, andCalculator.serve(stdio())starts the server on stdin and stdout.Tip: Derive Clone on the server struct and JsonSchema on the argument struct. The macros need both.
- 3
Build it
Run
cargo build --release. The binary istarget/release/calculator. Note its absolute path: Claude launches the binary itself, so it needs the full path.A stdio server prints nothing when you run it by hand and waits for a host. Stop it with Ctrl-C.
- 4
Test it in MCP Inspector
Run
npx @modelcontextprotocol/inspector ./target/release/calculatorand open the URL it prints. Go to Tools, pickadd, entera=1andb=2and run it. The result is3.The Inspector needs Node 22.19 or later. See MCP Inspector for the CLI and terminal modes.
- 5
Add it to Claude Code
Run
claude mcp add calculator -- /absolute/path/to/target/release/calculator. Everything after--is the launch command.Start
claudeand run/mcpto seecalculatorconnected withaddlisted. Add--scope projectto write the entry to.mcp.jsonso your team gets it.PromptUse the calculator server to add 19 and 23. - 6
Or add it to Claude Desktop
Add the entry from the Code section to
claude_desktop_config.json(macOS:~/Library/Application Support/Claude/claude_desktop_config.json, Windows:%APPDATA%\Claude\claude_desktop_config.json). Put the absolute path to the binary incommand.Fully quit Claude Desktop and reopen it. Closing the window is not enough.
- 7
Serve it over HTTP when others need it
Enable the
transport-streamable-http-serverfeature.StreamableHttpServiceis a Tower service you mount on an axum or hyper router, and the SDK'scounter_streamhttp.rsexample shows a full server.Stdio is the right choice for a server only you run. Use HTTP when several people connect to one running copy.
Code
cargo new calculator
cd calculator
cargo add rmcp --features server,macros,transport-io
cargo add tokio --features macros,rt-multi-thread,io-std
cargo add serde --features derive
cargo add anyhowuse rmcp::{handler::server::wrapper::Parameters, schemars, tool, tool_router, ServiceExt, transport::stdio};
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
struct AddParams {
a: i32,
b: i32,
}
#[derive(Clone)]
struct Calculator;
#[tool_router(server_handler)]
impl Calculator {
#[tool(description = "Add two numbers")]
fn add(&self, Parameters(AddParams { a, b }): Parameters<AddParams>) -> String {
(a + b).to_string()
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let service = Calculator.serve(stdio()).await?;
service.waiting().await?;
Ok(())
}# Build
cargo build --release
# Test in MCP Inspector
npx @modelcontextprotocol/inspector ./target/release/calculator
# Claude Code (everything after -- is the launch command)
claude mcp add calculator -- /absolute/path/to/target/release/calculator{
"mcpServers": {
"calculator": {
"command": "/absolute/path/to/target/release/calculator"
}
}
}Common errors and fixes
Server connects, then drops, or shows as failed in Claude
Cause: Something wrote to stdout, such as println!. On stdio, stdout carries the protocol.
Fix: Remove println! from the server. Log with eprintln! or a logging crate configured to write to stderr, such as tracing.
claude-desktop, claude-code Source
Server does not appear in Claude Desktop after editing claude_desktop_config.json
Cause: A relative path, a JSON syntax error, or Claude Desktop is still running with the old config.
Fix: Use the absolute path to the binary, check the JSON, then fully quit and reopen Claude Desktop.
claude-desktop Source
Tutorial code does not compile against the current rmcp
Cause: The MCP tutorial pins rmcp 0.3. The SDK is at 3.x, which has breaking changes.
Fix: Follow the SDK README for 3.x, or read its migration guide before upgrading an older server.
The compiler cannot find the stdio transport
Cause: The transport-io feature of rmcp is not enabled. It provides the stdio transport.
Fix: Add it: cargo add rmcp --features server,macros,transport-io.
Questions
Is there an official Rust SDK for MCP?
Yes. rmcp lives in the modelcontextprotocol GitHub organisation and the MCP site lists it as a Tier 1 SDK. Install it with the cargo add command in the Code section.
Which Rust version do I need?
Rust 1.88 or later for the current SDK, per its manifest. The MCP tutorial still says Rust 1.70 and pins rmcp 0.3, so the two pages disagree. Follow the SDK.
What is rmcp?
The official Rust implementation of the Model Context Protocol, built on the tokio async runtime. It has a core crate and a macro crate (rmcp-macros) that generate tools from annotated methods.
What is the difference between #[tool_router(server_handler)] and #[tool_handler]?
#[tool_router(server_handler)] is the short form for a tools-only server. Use #[tool_handler(...)] on an explicit ServerHandler impl when you need custom name, version or instructions, or more than tools.
How do I test a Rust MCP server with MCP Inspector?
Run npx @modelcontextprotocol/inspector followed by your binary or cargo run command, then open the URL it prints. You can call each tool with your own arguments and see the result. The Inspector needs Node 22.19 or later.
How do I add a Rust MCP server to Claude Code?
Run claude mcp add <name> -- /absolute/path/to/your/binary, then /mcp inside a session to confirm it is connected. Use an absolute path.
How do I add it to Claude Desktop?
Add an entry under mcpServers in claude_desktop_config.json with the absolute path to your binary as command, then fully quit and reopen Claude Desktop.
Why does println! break my server?
On stdio, stdout carries the protocol messages. A println! writes into that stream and corrupts it. The MCP tutorial says to use eprintln! or a logging crate such as tracing that writes to stderr.
Can a Rust MCP server run over HTTP?
Yes. Enable the transport-streamable-http-server feature and mount StreamableHttpService on an axum or hyper router. The SDK's examples include a full server.
Sources
Docs, issue trackers and discussions this page is built from.
Show all 10Hide
- rmcp README: migration note · github.com
- rmcp on crates.io · crates.io
- rust-sdk workspace Cargo.toml · github.com
- SDKs - Model Context Protocol · modelcontextprotocol.io
- Build an MCP server · modelcontextprotocol.io
- MCP Inspector · modelcontextprotocol.io
- calculator_stdio.rs example · github.com
- Connect to a real host (Python SDK docs) · py.sdk.modelcontextprotocol.io
- Connect Claude Code to tools via MCP · code.claude.com
- rust-sdk examples Cargo.toml · github.com