Building an MCP in Rust
Supercharging AI models with a Rust-powered MCPs.
The Model Context Protocol (MCP) is a standard for connecting AI models to external tools and data.
Large Language Models (LLMs) such as Claude, ChatGPT, and Gemini are advanced chatbot systems powered by artificial intelligence. These models are trained on vast datasets of text and code, enabling them to generate human-like responses across a wide range of topics. However, they operate within a closed environment, meaning they lack direct access to the internet or real-time external data, and cannot interact with the outside world beyond their training data and predefined capabilities.
MCPs transform LLMs from static conversational agents into dynamic assistants capable of practical, real-world applications, significantly expanding their utility and impact.
The Setup
Building MCP servers in Rust offers excellent performance, type safety, and reliability. This guide will walk you through creating a simple MCP server using the rmcp crate.
Prerequisites:
Rust and Cargo installed
Basic knowledge of Rust and async programming (Tokio)
Let’s start by creating a new Rust project:
Dependencies
Add the necessary dependencies to your Cargo.toml. You’ll need rmcp for the protocol implementation, tokio for the async runtime, and serde for JSON serialization.
Project Structure
A typical Rust MCP project structure looks like this:
For larger projects (like prod-mcp-server), you might split it further:
Core Concepts
MCP servers operate through well-defined lifecycles that ensure reliable communication with AI clients. The key stages are:
Initialization: The server is set up with its capabilities, metadata, and configurations.
Tool Registration: Available tools (functions callable by the AI) are registered and exposed to clients through schemas and metadata.
Request Handling: The server processes incoming tool calls from AI clients, validating inputs and executing the corresponding logic.
Response Formatting: The server returns structured responses (typically in JSON format) that include metadata and schema information for consistency and interpretability.
Key RMCP Components
The rmcp library provides several essential components and macros to simplify MCP server development in Rust:
ServerHandler: A core trait that defines the server’s identity, metadata, and capabilities.
#[tool]: A macro to annotate methods as MCP tools, making them callable by AI models.
#[tool_router]: A macro to generate routing logic for tool calls, mapping requests to the appropriate methods.
#[tool_handler]: A macro for implementing the
ServerHandlertrait on a server struct.Parameters<T>: A typed wrapper for tool input parameters, ensuring safe deserialization and validation.
Json<T>: A wrapper for responses, embedding schema metadata for structured output.
Defining the Server
The core of an rmcp server is a Rust struct that implements the ServerHandler trait. This struct holds any necessary state (e.g., shared data or external client connections) and defines the server’s behavior and capabilities.
In this example, MyMcpServer is a simple MCP server with metadata such as its name, instructions, and capabilities (e.g., support for tools and logging). The get_info method returns a ServerInfo struct that clients use to understand the server’s purpose and protocol compatibility.
Implementing Tools
Tools are the functions exposed by an MCP server to AI models. In the rmcp framework, tools are implemented as async methods on the server struct, annotated with the #[tool] macro to define their name and description.
Implementing Tool Methods
Once schemas are defined, the corresponding tool methods are implemented as asynchronous functions on the server struct. The #[tool] macro specifies the tool’s name and a brief description for AI clients.
Error Handling Best Practices
Error handling matters. A robust MCP server should never panic or crash due to malformed input and it should always provide meaningful, actionable messages when things go wrong.
Let’s look at a practical example: implementing a divide tool that checks for invalid input and emits friendly error messages to clients.
Key takeaways:
Validate input upfront: Don’t assume. Always check constraints like “divisor cannot be zero.”
Fail fast, fail clear: Construct errors
McpErrorwith clear, helpful messages that clients and users can immediately understand.Log intentionally: Use tracing to log what’s happening internally. Good logs make debugging production issues much easier.
Transport Layers
The MCP docs require that servers support flexible ways to talk to clients from local development to production deployments. Here’s how you can set up your server to work over different transports.
Stdio Transport (Recommended for Local Development)
What is
stdio?It’s Rust’s (and Unix’s) universal interface for input and output:
stdinfor receiving data (from your keyboard, or piped from a file or script) andstdoutfor returning results (to your screen, or anywhere you redirect it).
When you use stdio, your MCP server becomes a simple, powerful building block talking directly to other programs, editors, or automation tools with zero setup.
stdio is ideal for fast, frictionless local integrations with Claude Desktop, Cursor, OpenAI Codex, prototyping, or deep debugging with nothing but your terminal and a test script.
Here, our code sets up an asynchronous MCP (Model Context Protocol) server with configurable transport options. It uses the clap library to parse command-line arguments, allowing users to specify whether the server should communicate via standard input/output (stdio) or HTTP through a --transport flag that defaults to stdio.
We also initialize logging to stderr to avoid interfering with protocol messages and then start the server with the selected transport type. When stdio mode is chosen, the server reads from standard input and writes to standard output, creating a simple communication channel for the MCP protocol.
HTTP Transport with Server-Sent Events
For web applications or remote integrations, HTTP is king. Pair it with Server-Sent Events (SSE) for efficient streaming responses. SSE enables long-lived, real-time connections perfect for streaming tool outputs or server logs to web UIs
First, add these dependencies to your Cargo.toml:
MCP Inspector
The easiest way I test my servers is with MCP Inspector, a browser-based tool for interactive debugging:
npx @modelcontextprotocol/inspector cargo run --release -- --transport stdioFeatures:
Browse and explore available tool schemas
Execute tool methods with JSON payloads and inspect responses
Monitor real-time console logs and protocol messages
Here’s our Simple MCP running on Cherry Studio
🎉 You’re ready to ship
We now have a high-performance, type-safe MCP server built with Rust. Whether you’re powering AI agents, building developer tools, or creating custom integrations, you have a solid foundation to build on. Till next time, happy coding Padawans🦀















Brillaint breakdown of the MCP architecture! The way you frame stdio as the "universal interface" for local dev really clarifies why it's so practical for quick iterations. One thing that caught my attention is how rmcp handles validation at the schema level but the divide tool still checks for zero explicitly. It makes me wonder if there's an oportunity to push more of these domain constraints into the type system itself, maybe with newtypes or refined types, so the compiler catches impossible states before runtime. That could reduce surface area for those McpError paths.