PlugWiki

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. 1

    Create a project and add the crate

    You need Rust 1.88 or later from rustup.rs. Create a project with cargo new calculator and cd calculator, then add the dependencies with the commands in the Code section.

    Pick the server, macros and transport-io features of rmcp. The last one gives you the stdio transport, which is how Claude Code and Claude Desktop launch a local server.

  2. 2

    Write src/main.rs

    Copy main.rs from the Code section. #[tool_router(server_handler)] is the short form for a server that only has tools. It skips the separate ServerHandler impl.

    #[tool(description = ...)] marks add as a tool. The fields of AddParams become its input schema, and Calculator.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. 3

    Build it

    Run cargo build --release. The binary is target/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. 4

    Test it in MCP Inspector

    Run npx @modelcontextprotocol/inspector ./target/release/calculator and open the URL it prints. Go to Tools, pick add, enter a=1 and b=2 and run it. The result is 3.

    The Inspector needs Node 22.19 or later. See MCP Inspector for the CLI and terminal modes.

  5. 5

    Add it to Claude Code

    Run claude mcp add calculator -- /absolute/path/to/target/release/calculator. Everything after -- is the launch command.

    Start claude and run /mcp to see calculator connected with add listed. Add --scope project to write the entry to .mcp.json so your team gets it.

    Prompt
    Use the calculator server to add 19 and 23.
  6. 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 in command.

    Fully quit Claude Desktop and reopen it. Closing the window is not enough.

  7. 7

    Serve it over HTTP when others need it

    Enable the transport-streamable-http-server feature. StreamableHttpService is a Tower service you mount on an axum or hyper router, and the SDK's counter_streamhttp.rs example 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

terminal
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 anyhow
Rust 1.88 or later. The feature list follows the SDK README transport table and its examples crate.
src/main.rs
use 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(())
}
The tools-only server from the rmcp README. Do not add println!: on stdio it corrupts the protocol messages.
terminal
# 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
The Inspector pattern matches the SDK example, which runs it on a cargo run command.
claude_desktop_config.json
{
  "mcpServers": {
    "calculator": {
      "command": "/absolute/path/to/target/release/calculator"
    }
  }
}
On Windows use the full path to calculator.exe with escaped backslashes. Fully quit and reopen Claude Desktop after saving.

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.

    Source

  • 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.

    Source

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 10
  1. rmcp README: migration note · github.com
  2. rmcp on crates.io · crates.io
  3. rust-sdk workspace Cargo.toml · github.com
  4. SDKs - Model Context Protocol · modelcontextprotocol.io
  5. Build an MCP server · modelcontextprotocol.io
  6. MCP Inspector · modelcontextprotocol.io
  7. calculator_stdio.rs example · github.com
  8. Connect to a real host (Python SDK docs) · py.sdk.modelcontextprotocol.io
  9. Connect Claude Code to tools via MCP · code.claude.com
  10. rust-sdk examples Cargo.toml · github.com

Updated Nov 3, 2026.