{"kind":"Skill","metadata":{"namespace":"community","name":"rust-mcp-server-generator","version":"0.1.0"},"spec":{"description":"Generate a complete Rust Model Context Protocol server project with tools, prompts, resources, and tests using the official rmcp SDK","files":{"SKILL.md":"---\nname: rust-mcp-server-generator\ndescription: 'Generate a complete Rust Model Context Protocol server project with tools, prompts, resources, and tests using the official rmcp SDK'\n---\n\n# Rust MCP Server Generator\n\nYou are a Rust MCP server generator. Create a complete, production-ready Rust MCP server project using the official `rmcp` SDK.\n\n## Project Requirements\n\nAsk the user for:\n1. **Project name** (e.g., \"my-mcp-server\")\n2. **Server description** (e.g., \"A weather data MCP server\")\n3. **Transport type** (stdio, sse, http, or all)\n4. **Tools to include** (e.g., \"weather lookup\", \"forecast\", \"alerts\")\n5. **Whether to include prompts and resources**\n\n## Project Structure\n\nGenerate this structure:\n\n```\n{project-name}/\n├── Cargo.toml\n├── .gitignore\n├── README.md\n├── src/\n│   ├── main.rs\n│   ├── handler.rs\n│   ├── tools/\n│   │   ├── mod.rs\n│   │   └── {tool_name}.rs\n│   ├── prompts/\n│   │   ├── mod.rs\n│   │   └── {prompt_name}.rs\n│   ├── resources/\n│   │   ├── mod.rs\n│   │   └── {resource_name}.rs\n│   └── state.rs\n└── tests/\n    └── integration_test.rs\n```\n\n## File Templates\n\n### Cargo.toml\n\n```toml\n[package]\nname = \"{project-name}\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nrmcp = { version = \"0.8.1\", features = [\"server\"] }\nrmcp-macros = \"0.8\"\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\"\nschemars = { version = \"0.8\", features = [\"derive\"] }\nasync-trait = \"0.1\"\n\n# Optional: for HTTP transports\naxum = { version = \"0.7\", optional = true }\ntower-http = { version = \"0.5\", features = [\"cors\"], optional = true }\n\n[dev-dependencies]\ntokio-test = \"0.4\"\n\n[features]\ndefault = []\nhttp = [\"dep:axum\", \"dep:tower-http\"]\n\n[[bin]]\nname = \"{project-name}\"\npath = \"src/main.rs\"\n```\n\n### .gitignore\n\n```gitignore\n/target\nCargo.lock\n*.swp\n*.swo\n*~\n.DS_Store\n```\n\n### README.md\n\n```markdown\n# {Project Name}\n\n{Server description}\n\n## Installation\n\n```bash\ncargo build --release\n```\n\n## Usage\n\n### Stdio Transport\n\n```bash\ncargo run\n```\n\n### SSE Transport\n\n```bash\ncargo run --features http -- --transport sse\n```\n\n### HTTP Transport\n\n```bash\ncargo run --features http -- --transport http\n```\n\n## Configuration\n\nConfigure in your MCP client (e.g., Claude Desktop):\n\n```json\n{\n  \"mcpServers\": {\n    \"{project-name}\": {\n      \"command\": \"path/to/target/release/{project-name}\",\n      \"args\": []\n    }\n  }\n}\n```\n\n## Tools\n\n- **{tool_name}**: {Tool description}\n\n## Development\n\nRun tests:\n\n```bash\ncargo test\n```\n\nRun with logging:\n\n```bash\nRUST_LOG=debug cargo run\n```\n```\n\n### src/main.rs\n\n```rust\nuse anyhow::Result;\nuse rmcp::{\n    protocol::ServerCapabilities,\n    server::Server,\n    transport::StdioTransport,\n};\nuse tokio::signal;\nuse tracing_subscriber;\n\nmod handler;\nmod state;\nmod tools;\nmod prompts;\nmod resources;\n\nuse handler::McpHandler;\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c()\u003e {\n    // Initialize tracing\n    tracing_subscriber::fmt()\n        .with_max_level(tracing::Level::INFO)\n        .with_target(false)\n        .init();\n    \n    tracing::info!(\"Starting {project-name} MCP server\");\n    \n    // Create handler\n    let handler = McpHandler::new();\n    \n    // Create transport (stdio by default)\n    let transport = StdioTransport::new();\n    \n    // Build server with capabilities\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    tracing::info!(\"Server started, waiting for requests\");\n    \n    // Run server until Ctrl+C\n    server.run(signal::ctrl_c()).await?;\n    \n    tracing::info!(\"Server shutting down\");\n    Ok(())\n}\n```\n\n### src/handler.rs\n\n```rust\nuse rmcp::{\n    model::*,\n    protocol::*,\n    server::{RequestContext, ServerHandler, RoleServer, ToolRouter},\n    ErrorData,\n};\nuse rmcp::{tool_router, tool_handler};\nuse async_trait::async_trait;\n\nuse crate::state::ServerState;\nuse crate::tools;\n\npub struct McpHandler {\n    state: ServerState,\n    tool_router: ToolRouter,\n}\n\n#[tool_router]\nimpl McpHandler {\n    // Include tool definitions from tools module\n    #[tool(\n        name = \"example_tool\",\n        description = \"An example tool\",\n        annotations(read_only_hint = true)\n    )]\n    async fn example_tool(params: Parameters\u003ctools::ExampleParams\u003e) -\u003e Result\u003cString, String\u003e {\n        tools::example::execute(params).await\n    }\n    \n    pub fn new() -\u003e Self {\n        Self {\n            state: ServerState::new(),\n            tool_router: Self::tool_router(),\n        }\n    }\n}\n\n#[tool_handler]\n#[async_trait]\nimpl ServerHandler for McpHandler {\n    async 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: \"example-prompt\".to_string(),\n                description: Some(\"An example prompt\".to_string()),\n                arguments: Some(vec![\n                    PromptArgument {\n                        name: \"topic\".to_string(),\n                        description: Some(\"The topic to discuss\".to_string()),\n                        required: Some(true),\n                    },\n                ]),\n            },\n        ];\n        \n        Ok(ListPromptsResult { prompts })\n    }\n    \n    async 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            \"example-prompt\" =\u003e {\n                let topic = request.arguments\n                    .as_ref()\n                    .and_then(|args| args.get(\"topic\"))\n                    .ok_or_else(|| ErrorData::invalid_params(\"topic required\"))?;\n                \n                Ok(GetPromptResult {\n                    description: Some(\"Example prompt\".to_string()),\n                    messages: vec![\n                        PromptMessage::user(format!(\"Let's discuss: {}\", topic)),\n                    ],\n                })\n            }\n            _ =\u003e Err(ErrorData::invalid_params(\"Unknown prompt\")),\n        }\n    }\n    \n    async 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: \"example://data/info\".to_string(),\n                name: \"Example Resource\".to_string(),\n                description: Some(\"An example resource\".to_string()),\n                mime_type: Some(\"text/plain\".to_string()),\n            },\n        ];\n        \n        Ok(ListResourcesResult { resources })\n    }\n    \n    async 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            \"example://data/info\" =\u003e {\n                Ok(ReadResourceResult {\n                    contents: vec![\n                        ResourceContents::text(\"Example resource content\".to_string())\n                            .with_uri(request.uri)\n                            .with_mime_type(\"text/plain\"),\n                    ],\n                })\n            }\n            _ =\u003e Err(ErrorData::invalid_params(\"Unknown resource\")),\n        }\n    }\n}\n```\n\n### src/state.rs\n\n```rust\nuse std::sync::Arc;\nuse tokio::sync::RwLock;\n\n#[derive(Clone)]\npub struct ServerState {\n    // Add shared state here\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    pub async fn get(\u0026self) -\u003e i32 {\n        *self.counter.read().await\n    }\n}\n```\n\n### src/tools/mod.rs\n\n```rust\npub mod example;\n\npub use example::ExampleParams;\n```\n\n### src/tools/example.rs\n\n```rust\nuse rmcp::model::Parameters;\nuse serde::{Deserialize, Serialize};\nuse schemars::JsonSchema;\n\n#[derive(Debug, Deserialize, JsonSchema)]\npub struct ExampleParams {\n    pub input: String,\n}\n\npub async fn execute(params: Parameters\u003cExampleParams\u003e) -\u003e Result\u003cString, String\u003e {\n    let input = \u0026params.inner().input;\n    \n    // Tool logic here\n    Ok(format!(\"Processed: {}\", input))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    \n    #[tokio::test]\n    async fn test_example_tool() {\n        let params = Parameters::new(ExampleParams {\n            input: \"test\".to_string(),\n        });\n        \n        let result = execute(params).await.unwrap();\n        assert!(result.contains(\"test\"));\n    }\n}\n```\n\n### src/prompts/mod.rs\n\n```rust\n// Prompt implementations can go here if needed\n```\n\n### src/resources/mod.rs\n\n```rust\n// Resource implementations can go here if needed\n```\n\n### tests/integration_test.rs\n\n```rust\nuse rmcp::{\n    model::*,\n    protocol::*,\n    server::{RequestContext, ServerHandler, RoleServer},\n};\n\n// Replace with your actual project name in snake_case\n// Example: if project is \"my-mcp-server\", use my_mcp_server\nuse my_mcp_server::handler::McpHandler;\n\n#[tokio::test]\nasync fn test_list_tools() {\n    let handler = McpHandler::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 == \"example_tool\"));\n}\n\n#[tokio::test]\nasync fn test_call_tool() {\n    let handler = McpHandler::new();\n    let context = RequestContext::default();\n    \n    let request = CallToolRequestParam {\n        name: \"example_tool\".to_string(),\n        arguments: Some(serde_json::json!({\n            \"input\": \"test\"\n        })),\n    };\n    \n    let result = handler.call_tool(request, context).await;\n    assert!(result.is_ok());\n}\n\n#[tokio::test]\nasync fn test_list_prompts() {\n    let handler = McpHandler::new();\n    let context = RequestContext::default();\n    \n    let result = handler.list_prompts(None, context).await.unwrap();\n    assert!(!result.prompts.is_empty());\n}\n\n#[tokio::test]\nasync fn test_list_resources() {\n    let handler = McpHandler::new();\n    let context = RequestContext::default();\n    \n    let result = handler.list_resources(None, context).await.unwrap();\n    assert!(!result.resources.is_empty());\n}\n```\n\n## Implementation Guidelines\n\n1. **Use rmcp-macros**: Leverage `#[tool]`, `#[tool_router]`, and `#[tool_handler]` macros for cleaner code\n2. **Type Safety**: Use `schemars::JsonSchema` for all parameter types\n3. **Error Handling**: Return `Result` types with proper error messages\n4. **Async/Await**: All handlers must be async\n5. **State Management**: Use `Arc\u003cRwLock\u003cT\u003e\u003e` for shared state\n6. **Testing**: Include unit tests for tools and integration tests for handlers\n7. **Logging**: Use `tracing` macros (`info!`, `debug!`, `warn!`, `error!`)\n8. **Documentation**: Add doc comments to all public items\n\n## Example Tool Patterns\n\n### Simple Read-Only Tool\n\n```rust\n#[derive(Debug, Deserialize, JsonSchema)]\npub struct GreetParams {\n    pub name: String,\n}\n\n#[tool(\n    name = \"greet\",\n    description = \"Greets a user by name\",\n    annotations(read_only_hint = true, idempotent_hint = true)\n)]\nasync fn greet(params: Parameters\u003cGreetParams\u003e) -\u003e String {\n    format!(\"Hello, {}!\", params.inner().name)\n}\n```\n\n### Tool with Error Handling\n\n```rust\n#[derive(Debug, Deserialize, JsonSchema)]\npub struct DivideParams {\n    pub a: f64,\n    pub b: f64,\n}\n\n#[tool(name = \"divide\", description = \"Divides two numbers\")]\nasync fn divide(params: Parameters\u003cDivideParams\u003e) -\u003e Result\u003cf64, String\u003e {\n    let p = params.inner();\n    if p.b == 0.0 {\n        Err(\"Cannot divide by zero\".to_string())\n    } else {\n        Ok(p.a / p.b)\n    }\n}\n```\n\n### Tool with State\n\n```rust\n#[tool(\n    name = \"increment\",\n    description = \"Increments the counter\",\n    annotations(destructive_hint = true)\n)]\nasync fn increment(state: \u0026ServerState) -\u003e i32 {\n    state.increment().await\n}\n```\n\n## Running the Generated Server\n\nAfter generation:\n\n```bash\ncd {project-name}\ncargo build\ncargo test\ncargo run\n```\n\nFor Claude Desktop integration:\n\n```json\n{\n  \"mcpServers\": {\n    \"{project-name}\": {\n      \"command\": \"path/to/{project-name}/target/release/{project-name}\",\n      \"args\": []\n    }\n  }\n}\n```\n\nNow generate the complete project based on the user's requirements!\n"},"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/tree/541b7819d8c3545c6df122491af4fa1eae415779/plugins/rust-mcp-development/skills/rust-mcp-server-generator"}},"content_hash":[143,191,40,25,62,217,209,121,54,34,147,94,122,229,59,15,27,82,94,233,148,222,157,54,233,49,147,48,109,152,57,154],"trust_level":"unsigned","yanked":false}
