Tu implémentes ton premier serveur MCP en Rust avec rmcp.
L'objectif est de comprendre le protocole dans ses fondamentaux : comment un LLM découvre les tools, comment il les appelle, comment le serveur répond.
Pas de base de données, pas d'API externe — juste le protocole pur avec 3 tools simples mais complets.
Transport : stdio (Claude Desktop lit stdout, écrit stdin)
Connexion : Claude Desktop + VS Code Copilot Chat
Focus : JSON-RPC 2.0, cycle initialize → list_tools → call_tool
- Comprendre le cycle de vie d'une session MCP :
initialize,tools/list,tools/call - Implémenter un serveur MCP with
rmcpen Rust - Exposer 3 tools avec des signatures JSON Schema typées
- Gérer les erreurs MCP (codes standardisés)
- Connecter le serveur à Claude Desktop et observer les appels en temps réel
mcp-hello-tools/
├── Cargo.toml
├── claude_desktop_config.json ← config à copier dans Claude Desktop
└── src/
├── main.rs ← setup transport stdio + run server
├── server.rs ← impl ServerHandler (rmcp trait)
└── tools/
├── mod.rs
├── echo.rs ← tool : echo un message
├── timestamp.rs ← tool : heure actuelle, timezone param
└── calculate.rs ← tool : expression arithmétique simple
{
"name": "echo",
"description": "Returns the input message unchanged. Use to verify the MCP connection is working.",
"inputSchema": {
"type": "object",
"properties": {
"message": { "type": "string", "description": "The message to echo back" }
},
"required": ["message"]
}
}{
"name": "get_timestamp",
"description": "Returns the current UTC timestamp. Optionally formats it for a given timezone.",
"inputSchema": {
"type": "object",
"properties": {
"format": {
"type": "string",
"enum": ["iso8601", "unix", "human"],
"description": "Output format (default: iso8601)"
},
"timezone": {
"type": "string",
"description": "IANA timezone name (e.g. 'Europe/Paris'). Default: UTC"
}
}
}
}{
"name": "calculate",
"description": "Evaluates a simple arithmetic expression: +, -, *, /. Returns the numeric result.",
"inputSchema": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "Arithmetic expression, e.g. '(12 + 4) * 3'"
}
},
"required": ["expression"]
}
}use rmcp::{ServerHandler, model::*, service::RequestContext, tool};
pub struct HelloServer;
#[rmcp::tool(tool_box)]
impl HelloServer {
#[tool(description = "Returns the input message unchanged.")]
async fn echo(&self, #[tool(param)] message: String) -> String {
message
}
}
#[rmcp::async_trait]
impl ServerHandler for HelloServer {
fn get_info(&self) -> ServerInfo {
ServerInfo {
name: "mcp-hello-tools".into(),
version: "0.1.0".into(),
..Default::default()
}
}
}use rmcp::transport::stdio;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// MCP over stdio : Claude Desktop pipe stdin/stdout
let server = HelloServer;
stdio::run_server(server).await?;
Ok(())
}MCP définit des codes d'erreur standardisés dans la spec JSON-RPC :
| Code | Constante | Usage |
|---|---|---|
| -32700 | PARSE_ERROR |
JSON invalide |
| -32600 | INVALID_REQUEST |
Structure JSON-RPC invalide |
| -32601 | METHOD_NOT_FOUND |
Tool inconnu |
| -32602 | INVALID_PARAMS |
Paramètres manquants/invalides |
| -32603 | INTERNAL_ERROR |
Erreur interne du serveur |
use rmcp::model::ErrorCode;
fn calculate(expr: &str) -> Result<f64, rmcp::Error> {
parse_and_eval(expr).map_err(|e| rmcp::Error {
code: ErrorCode::INVALID_PARAMS,
message: format!("Invalid expression: {}", e),
data: None,
})
}- Serveur MCP qui démarre via
cargo runen mode stdio - 3 tools fonctionnels avec JSON Schema précis
-
get_timestampsupporte les 3 formats et les timezones IANA -
calculateparse et évalue les 4 opérations de base avec parenthèses - Erreurs MCP correctement retournées (pas de panic)
- Connexion à Claude Desktop vérifiée : Claude appelle les tools depuis le chat
-
claude_desktop_config.jsondocumenté dans le README
- Zéro
unwrap()hors tests - Descriptions des tools rédigées pour le LLM : précises, sans ambiguïté
cargo clippy -- -D warnings: zéro warning- Les paramètres optionnels ont des valeurs par défaut documentées dans la description
- Log vers stderr uniquement (stdout est réservé au protocole MCP)
- rmcp crate : https://docs.rs/rmcp
- Spec MCP officielle : https://spec.modelcontextprotocol.io
- JSON-RPC 2.0 : https://www.jsonrpc.org/specification
- Claude Desktop config : https://modelcontextprotocol.io/quickstart/user