{"kind":"AgentDefinition","metadata":{"namespace":"community","name":"rust-mcp-server","version":"0.1.0"},"spec":{"agents_md":"---\ndescription: 'Best practices for building Model Context Protocol servers in Rust using the official rmcp SDK with async/await patterns'\napplyTo: '**/*.rs'\n---\n\n# Rust MCP Server Development Best Practices\n\nThis guide provides best practices for building Model Context Protocol (MCP) servers using the official Rust SDK (`rmcp`).\n\n## Installation and Setup\n\n### Add Dependencies\n\nAdd the `rmcp` crate to your `Cargo.toml`:\n\n```toml\n[dependencies]\nrmcp = { version = \"0.8.1\", features = [\"server\"] }\ntokio = { version = \"1\", features = [\"full\"] }\nserde = { version = \"1.0\", features = [\"derive\"] }\nserde_json = \"1.0\"\nanyhow = \"1.0\"\ntracing = \"0.1\"\ntracing-subscriber = \"0.3\"\n```\n\nFor macros support:\n\n```toml\n[dependencies]\nrmcp-macros = \"0.8\"\nschemars = { version = \"0.8\", features = [\"derive\"] }\n```\n\n### Project Structure\n\nOrganize your Rust MCP server project:\n\n```\nmy-mcp-server/\n├── Cargo.toml\n├── src/\n│   ├── main.rs           # Server entry point\n│   ├── handler.rs        # ServerHandler implementation\n│   ├── tools/\n│   │   ├── mod.rs\n│   │   ├── calculator.rs\n│   │   └── greeter.rs\n│   ├── prompts/\n│   │   ├── mod.rs\n│   │   └── code_review.rs\n│   └── resources/\n│       ├── mod.rs\n│       └── data.rs\n└── tests/\n    └── integration_tests.rs\n```\n\n## Server Implementation\n\n### Basic Server Setup\n\nCreate a server with stdio transport:\n\n```rust\nuse rmcp::{\n    protocol::ServerCapabilities,\n    server::{Server, ServerHandler},\n    transport::StdioTransport,\n};\nuse tokio::signal;\n\n#[tokio::main]\nasync fn main() -\u003e anyhow::Result\u003c()\u003e {\n    tracing_subscriber::fmt::init();\n    \n    let handler = MyServerHandler::new();\n    let transport = StdioTransport::new();\n    \n    let server = Server::builder()\n        .with_handler(handler)\n        .with_capabilities(ServerCapabilities {\n            tools: Some(Default::default()),\n            prompts: Some(Default::default()),\n            resources: Some(Default::default()),\n            ..Default::default()\n        })\n        .build(transport)?;\n    \n    server.run(signal::ctrl_c()).await?;\n    \n    Ok(())\n}\n```\n\n### ServerHandler Implementation\n\nImplement the `ServerHandler` trait:\n\n```rust\nuse rmcp::{\n    model::*,\n    protocol::*,\n    server::{RequestContext, ServerHandler, RoleServer},\n    ErrorData,\n};\n\npub struct MyServerHandler {\n    tool_router: ToolRouter,\n}\n\nimpl MyServerHandler {\n    pub fn new() -\u003e Self {\n        Self {\n            tool_router: Self::create_tool_router(),\n        }\n    }\n    \n    fn create_tool_router() -\u003e ToolRouter {\n        // Initialize and return tool router\n        ToolRouter::new()\n    }\n}\n\n#[async_trait::async_trait]\nimpl ServerHandler for MyServerHandler {\n    async fn list_tools(\n        \u0026self,\n        _request: Option\u003cPaginatedRequestParam\u003e,\n        _context: RequestContext\u003cRoleServer\u003e,\n    ) -\u003e Result\u003cListToolsResult, ErrorData\u003e {\n        let items = self.tool_router.list_all();\n        Ok(ListToolsResult::with_all_items(items))\n    }\n    \n    async fn call_tool(\n        \u0026self,\n        request: CallToolRequestParam,\n        context: RequestContext\u003cRoleServer\u003e,\n    ) -\u003e Result\u003cCallToolResult, ErrorData\u003e {\n        let tcc = ToolCallContext::new(self, request, context);\n        self.tool_router.call(tcc).await\n    }\n}\n```\n\n## Tool Development\n\n### Using Macros for Tools\n\nUse the `#[tool]` macro for declarative tool definitions:\n\n```rust\nuse rmcp::tool;\nuse rmcp::model::Parameters;\nuse serde::{Deserialize, Serialize};\nuse schemars::JsonSchema;\n\n#[derive(Debug, Deserialize, JsonSchema)]\npub struct CalculateParams {\n    pub a: f64,\n    pub b: f64,\n    pub operation: String,\n}\n\n/// Performs mathematical calculations\n#[tool(\n    name = \"calculate\",\n    description = \"Performs basic arithmetic operations\",\n    annotations(read_only_hint = true)\n)]\npub async fn calculate(params: Parameters\u003cCalculateParams\u003e) -\u003e Result\u003cf64, String\u003e {\n    let p = params.inner();\n    match p.operation.as_str() {\n        \"add\" =\u003e Ok(p.a + p.b),\n        \"subtract\" =\u003e Ok(p.a - p.b),\n        \"multiply\" =\u003e Ok(p.a * p.b),\n        \"divide\" =\u003e {\n            if p.b == 0.0 {\n                Err(\"Division by zero\".to_string())\n            } else {\n                Ok(p.a / p.b)\n            }\n        }\n        _ =\u003e Err(format!(\"Unknown operation: {}\", p.operation)),\n    }\n}\n```\n\n### Tool Router with Macros\n\nUse `#[tool_router]` and `#[tool_handler]` macros:\n\n```rust\nuse rmcp::{tool_router, tool_handler};\n\npub struct ToolsHandler {\n    tool_router: ToolRouter,\n}\n\n#[tool_router]\nimpl ToolsHandler {\n    #[tool]\n    async fn greet(params: Parameters\u003cGreetParams\u003e) -\u003e String {\n        format!(\"Hello, {}!\", params.inner().name)\n    }\n    \n    #[tool(annotations(destructive_hint = true))]\n    async fn reset_counter() -\u003e String {\n        \"Counter reset\".to_string()\n    }\n    \n    pub fn new() -\u003e Self {\n        Self {\n            tool_router: Self::tool_router(),\n        }\n    }\n}\n\n#[tool_handler]\nimpl ServerHandler for ToolsHandler {\n    // Other handler methods...\n}\n```\n\n### Tool Annotations\n\nUse annotations to provide hints about tool behavior:\n\n```rust\n#[tool(\n    name = \"delete_file\",\n    annotations(\n        destructive_hint = true,\n        read_only_hint = false,\n        idempotent_hint = false\n    )\n)]\npub async fn delete_file(params: Parameters\u003cDeleteParams\u003e) -\u003e Result\u003c(), String\u003e {\n    // Delete file logic\n}\n\n#[tool(\n    name = \"search_data\",\n    annotations(\n        read_only_hint = true,\n        idempotent_hint = true,\n        open_world_hint = true\n    )\n)]\npub async fn search_data(params: Parameters\u003cSearchParams\u003e) -\u003e Vec\u003cString\u003e {\n    // Search logic\n}\n```\n\n### Returning Rich Content\n\nReturn structured content from tools:\n\n```rust\nuse rmcp::model::{ToolResponseContent, TextContent, ImageContent};\n\n#[tool]\nasync fn analyze_code(params: Parameters\u003cCodeParams\u003e) -\u003e ToolResponseContent {\n    ToolResponseContent::from(vec![\n        TextContent::text(format!(\"Analysis of {}:\", params.inner().filename)),\n        TextContent::text(\"No issues found.\"),\n    ])\n}\n```\n\n## Prompt Implementation\n\n### Prompt Handler\n\nImplement prompt handlers:\n\n```rust\nuse rmcp::model::{Prompt, PromptArgument, PromptMessage, GetPromptResult};\n\nasync fn list_prompts(\n    \u0026self,\n    _request: Option\u003cPaginatedRequestParam\u003e,\n    _context: RequestContext\u003cRoleServer\u003e,\n) -\u003e Result\u003cListPromptsResult, ErrorData\u003e {\n    let prompts = vec![\n        Prompt {\n            name: \"code-review\".to_string(),\n            description: Some(\"Review code for best practices\".to_string()),\n            arguments: Some(vec![\n                PromptArgument {\n                    name: \"language\".to_string(),\n                    description: Some(\"Programming language\".to_string()),\n                    required: Some(true),\n                },\n            ]),\n        },\n    ];\n    \n    Ok(ListPromptsResult { prompts })\n}\n\nasync fn get_prompt(\n    \u0026self,\n    request: GetPromptRequestParam,\n    _context: RequestContext\u003cRoleServer\u003e,\n) -\u003e Result\u003cGetPromptResult, ErrorData\u003e {\n    match request.name.as_str() {\n        \"code-review\" =\u003e {\n            let language = request.arguments\n                .as_ref()\n                .and_then(|args| args.get(\"language\"))\n                .ok_or_else(|| ErrorData::invalid_params(\"language required\"))?;\n            \n            Ok(GetPromptResult {\n                description: Some(\"Code review prompt\".to_string()),\n                messages: vec![\n                    PromptMessage::user(format!(\n                        \"Review this {} code for best practices and suggest improvements\",\n                        language\n                    )),\n                ],\n            })\n        }\n        _ =\u003e Err(ErrorData::invalid_params(\"Unknown prompt\")),\n    }\n}\n```\n\n## Resource Implementation\n\n### Resource Handlers\n\nImplement resource handlers:\n\n```rust\nuse rmcp::model::{Resource, ResourceContents, ReadResourceResult};\n\nasync fn list_resources(\n    \u0026self,\n    _request: Option\u003cPaginatedRequestParam\u003e,\n    _context: RequestContext\u003cRoleServer\u003e,\n) -\u003e Result\u003cListResourcesResult, ErrorData\u003e {\n    let resources = vec![\n        Resource {\n            uri: \"file:///data/config.json\".to_string(),\n            name: \"Configuration\".to_string(),\n            description: Some(\"Server configuration\".to_string()),\n            mime_type: Some(\"application/json\".to_string()),\n        },\n    ];\n    \n    Ok(ListResourcesResult { resources })\n}\n\nasync fn read_resource(\n    \u0026self,\n    request: ReadResourceRequestParam,\n    _context: RequestContext\u003cRoleServer\u003e,\n) -\u003e Result\u003cReadResourceResult, ErrorData\u003e {\n    match request.uri.as_str() {\n        \"file:///data/config.json\" =\u003e {\n            let content = r#\"{\"version\": \"1.0\", \"enabled\": true}\"#;\n            Ok(ReadResourceResult {\n                contents: vec![\n                    ResourceContents::text(content.to_string())\n                        .with_uri(request.uri)\n                        .with_mime_type(\"application/json\"),\n                ],\n            })\n        }\n        _ =\u003e Err(ErrorData::invalid_params(\"Unknown resource\")),\n    }\n}\n```\n\n## Transport Options\n\n### Stdio Transport\n\nStandard input/output transport for CLI integration:\n\n```rust\nuse rmcp::transport::StdioTransport;\n\nlet transport = StdioTransport::new();\nlet server = Server::builder()\n    .with_handler(handler)\n    .build(transport)?;\n```\n\n### SSE (Server-Sent Events) Transport\n\nHTTP-based SSE transport:\n\n```rust\nuse rmcp::transport::SseServerTransport;\nuse std::net::SocketAddr;\n\nlet addr: SocketAddr = \"127.0.0.1:8000\".parse()?;\nlet transport = SseServerTransport::new(addr);\n\nlet server = Server::builder()\n    .with_handler(handler)\n    .build(transport)?;\n\nserver.run(signal::ctrl_c()).await?;\n```\n\n### Streamable HTTP Transport\n\nHTTP streaming transport with Axum:\n\n```rust\nuse rmcp::transport::StreamableHttpTransport;\nuse axum::{Router, routing::post};\n\nlet transport = StreamableHttpTransport::new();\nlet app = Router::new()\n    .route(\"/mcp\", post(transport.handler()));\n\nlet listener = tokio::net::TcpListener::bind(\"127.0.0.1:3000\").await?;\naxum::serve(listener, app).await?;\n```\n\n### Custom Transports\n\nImplement custom transports (TCP, Unix Socket, WebSocket):\n\n```rust\nuse rmcp::transport::Transport;\nuse tokio::net::TcpListener;\n\n// See examples/transport/ for TCP, Unix Socket, WebSocket implementations\n```\n\n## Error Handling\n\n### ErrorData Usage\n\nReturn proper MCP errors:\n\n```rust\nuse rmcp::ErrorData;\n\nfn validate_params(value: \u0026str) -\u003e Result\u003c(), ErrorData\u003e {\n    if value.is_empty() {\n        return Err(ErrorData::invalid_params(\"Value cannot be empty\"));\n    }\n    Ok(())\n}\n\nasync fn call_tool(\n    \u0026self,\n    request: CallToolRequestParam,\n    context: RequestContext\u003cRoleServer\u003e,\n) -\u003e Result\u003cCallToolResult, ErrorData\u003e {\n    validate_params(\u0026request.name)?;\n    \n    // Tool execution...\n    \n    Ok(CallToolResult {\n        content: vec![TextContent::text(\"Success\")],\n        is_error: Some(false),\n    })\n}\n```\n\n### Anyhow Integration\n\nUse `anyhow` for application-level errors:\n\n```rust\nuse anyhow::{Context, Result};\n\nasync fn load_config() -\u003e Result\u003cConfig\u003e {\n    let content = tokio::fs::read_to_string(\"config.json\")\n        .await\n        .context(\"Failed to read config file\")?;\n    \n    let config: Config = serde_json::from_str(\u0026content)\n        .context(\"Failed to parse config\")?;\n    \n    Ok(config)\n}\n```\n\n## Testing\n\n### Unit Tests\n\nWrite unit tests for tools and handlers:\n\n```rust\n#[cfg(test)]\nmod tests {\n    use super::*;\n    \n    #[tokio::test]\n    async fn test_calculate_add() {\n        let params = Parameters::new(CalculateParams {\n            a: 5.0,\n            b: 3.0,\n            operation: \"add\".to_string(),\n        });\n        \n        let result = calculate(params).await.unwrap();\n        assert_eq!(result, 8.0);\n    }\n    \n    #[tokio::test]\n    async fn test_divide_by_zero() {\n        let params = Parameters::new(CalculateParams {\n            a: 5.0,\n            b: 0.0,\n            operation: \"divide\".to_string(),\n        });\n        \n        let result = calculate(params).await;\n        assert!(result.is_err());\n    }\n}\n```\n\n### Integration Tests\n\nTest complete server interactions:\n\n```rust\n#[tokio::test]\nasync fn test_server_list_tools() {\n    let handler = MyServerHandler::new();\n    let context = RequestContext::default();\n    \n    let result = handler.list_tools(None, context).await.unwrap();\n    \n    assert!(!result.tools.is_empty());\n    assert!(result.tools.iter().any(|t| t.name == \"calculate\"));\n}\n```\n\n## Progress Notifications\n\n### Reporting Progress\n\nSend progress notifications during long-running operations:\n\n```rust\nuse rmcp::model::ProgressNotification;\n\n#[tool]\nasync fn process_large_file(\n    params: Parameters\u003cProcessParams\u003e,\n    context: RequestContext\u003cRoleServer\u003e,\n) -\u003e Result\u003cString, String\u003e {\n    let total = 100;\n    \n    for i in 0..=total {\n        // Do work...\n        \n        if i % 10 == 0 {\n            context.notify_progress(ProgressNotification {\n                progress: i,\n                total: Some(total),\n            }).await.ok();\n        }\n    }\n    \n    Ok(\"Processing complete\".to_string())\n}\n```\n\n## OAuth Authentication\n\n### OAuth Integration\n\nImplement OAuth for secure access:\n\n```rust\nuse rmcp::oauth::{OAuthConfig, OAuthProvider};\n\nlet oauth_config = OAuthConfig {\n    authorization_endpoint: \"https://auth.example.com/authorize\".to_string(),\n    token_endpoint: \"https://auth.example.com/token\".to_string(),\n    client_id: env::var(\"CLIENT_ID\")?,\n    client_secret: env::var(\"CLIENT_SECRET\")?,\n    scopes: vec![\"read\".to_string(), \"write\".to_string()],\n};\n\nlet oauth_provider = OAuthProvider::new(oauth_config);\n// See examples/servers/complex_auth_sse.rs for complete implementation\n```\n\n## Performance Best Practices\n\n### Async Operations\n\nUse async/await for non-blocking operations:\n\n```rust\n#[tool]\nasync fn fetch_data(params: Parameters\u003cFetchParams\u003e) -\u003e Result\u003cString, String\u003e {\n    let client = reqwest::Client::new();\n    let response = client\n        .get(\u0026params.inner().url)\n        .send()\n        .await\n        .map_err(|e| e.to_string())?;\n    \n    let text = response.text().await.map_err(|e| e.to_string())?;\n    Ok(text)\n}\n```\n\n### State Management\n\nUse `Arc` and `RwLock` for shared state:\n\n```rust\nuse std::sync::Arc;\nuse tokio::sync::RwLock;\n\npub struct ServerState {\n    counter: Arc\u003cRwLock\u003ci32\u003e\u003e,\n}\n\nimpl ServerState {\n    pub fn new() -\u003e Self {\n        Self {\n            counter: Arc::new(RwLock::new(0)),\n        }\n    }\n    \n    pub async fn increment(\u0026self) -\u003e i32 {\n        let mut counter = self.counter.write().await;\n        *counter += 1;\n        *counter\n    }\n}\n```\n\n## Logging and Tracing\n\n### Setup Tracing\n\nConfigure tracing for observability:\n\n```rust\nuse tracing::{info, warn, error, debug};\nuse tracing_subscriber;\n\nfn init_logging() {\n    tracing_subscriber::fmt()\n        .with_max_level(tracing::Level::DEBUG)\n        .with_target(false)\n        .with_thread_ids(true)\n        .init();\n}\n\n#[tool]\nasync fn my_tool(params: Parameters\u003cMyParams\u003e) -\u003e String {\n    debug!(\"Tool called with params: {:?}\", params);\n    info!(\"Processing request\");\n    \n    // Tool logic...\n    \n    info!(\"Request completed\");\n    \"Done\".to_string()\n}\n```\n\n## Deployment\n\n### Binary Distribution\n\nBuild optimized release binaries:\n\n```bash\ncargo build --release --target x86_64-unknown-linux-gnu\ncargo build --release --target x86_64-pc-windows-msvc\ncargo build --release --target x86_64-apple-darwin\n```\n\n### Cross-Compilation\n\nUse cross for cross-platform builds:\n\n```bash\ncargo install cross\ncross build --release --target aarch64-unknown-linux-gnu\n```\n\n### Docker Deployment\n\nCreate a Dockerfile:\n\n```dockerfile\nFROM rust:1.75 as builder\nWORKDIR /app\nCOPY . .\nRUN cargo build --release\n\nFROM debian:bookworm-slim\nRUN apt-get update \u0026\u0026 apt-get install -y ca-certificates\nCOPY --from=builder /app/target/release/my-mcp-server /usr/local/bin/\nCMD [\"my-mcp-server\"]\n```\n\n## Additional Resources\n\n- [rmcp Documentation](https://docs.rs/rmcp)\n- [rmcp-macros Documentation](https://docs.rs/rmcp-macros)\n- [Examples Repository](https://github.com/modelcontextprotocol/rust-sdk/tree/main/examples)\n- [MCP Specification](https://spec.modelcontextprotocol.io/)\n- [Rust Async Book](https://rust-lang.github.io/async-book/)\n","description":"Best practices for building Model Context Protocol servers in Rust using the official rmcp SDK with async/await patterns","import":{"commit_sha":"541b7819d8c3545c6df122491af4fa1eae415779","imported_at":"2026-05-18T20:05:35Z","license_text":"MIT License\n\nCopyright GitHub, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.","owner":"github","repo":"github/awesome-copilot","source_url":"https://github.com/github/awesome-copilot/blob/541b7819d8c3545c6df122491af4fa1eae415779/instructions/rust-mcp-server.instructions.md"},"manifest":{}},"content_hash":[12,209,64,124,252,102,19,156,87,241,93,223,104,190,228,225,44,227,162,125,189,204,206,80,84,49,137,15,55,169,30,55],"trust_level":"unsigned","yanked":false}
