Add Rust MCP Server Development collection (#331)
* Add Rust MCP Server Development collection - Add Rust MCP server best practices instruction - Add Rust MCP server project generator prompt - Add Rust MCP expert chat mode - Add Rust MCP development collection manifest - Update category READMEs Features: - Server setup with official rmcp SDK (v0.8+) - Async/await patterns with tokio runtime - Tool development with procedural macros - Multiple transport protocols (stdio, SSE, HTTP, WebSocket) - Type-safe parameters with schemars JsonSchema - State management with Arc and RwLock - Error handling with ErrorData and anyhow - OAuth authentication support - Progress notifications - Cross-compilation and Docker deployment - Comprehensive testing examples * Fix Rust instruction file metadata - use single applyTo pattern * Fix Rust generator prompt - replace template placeholder with clear example * Update collections/rust-mcp-development.collection.yml * Update collections/rust-mcp-development.md --------- Co-authored-by: Aaron Powell <me@aaron-powell.com>
This commit is contained in:
parent
51b18a7577
commit
33cab830b6
@ -72,6 +72,7 @@ Custom chat modes define specific behaviors and tools for GitHub Copilot Chat, e
|
||||
| [Requirements to Jira Epic & User Story Creator](chatmodes/atlassian-requirements-to-jira.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fatlassian-requirements-to-jira.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fatlassian-requirements-to-jira.chatmode.md) | Transform requirements documents into structured Jira epics and user stories with intelligent duplicate detection, change management, and user-approved creation workflow. |
|
||||
| [Ruby MCP Expert](chatmodes/ruby-mcp-expert.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fruby-mcp-expert.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fruby-mcp-expert.chatmode.md) | Expert assistance for building Model Context Protocol servers in Ruby using the official MCP Ruby SDK gem with Rails integration. |
|
||||
| [Rust Beast Mode](chatmodes/rust-gpt-4.1-beast-mode.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Frust-gpt-4.1-beast-mode.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Frust-gpt-4.1-beast-mode.chatmode.md) | Rust GPT-4.1 Coding Beast Mode for VS Code |
|
||||
| [Rust MCP Expert](chatmodes/rust-mcp-expert.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Frust-mcp-expert.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Frust-mcp-expert.chatmode.md) | Expert assistant for Rust MCP server development using the rmcp SDK with tokio async runtime |
|
||||
| [Semantic Kernel .NET mode instructions](chatmodes/semantic-kernel-dotnet.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fsemantic-kernel-dotnet.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fsemantic-kernel-dotnet.chatmode.md) | Create, update, refactor, explain or work with code using the .NET version of Semantic Kernel. |
|
||||
| [Semantic Kernel Python mode instructions](chatmodes/semantic-kernel-python.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fsemantic-kernel-python.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fsemantic-kernel-python.chatmode.md) | Create, update, refactor, explain or work with code using the Python version of Semantic Kernel. |
|
||||
| [Software Engineer Agent v1](chatmodes/software-engineer-agent-v1.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fsoftware-engineer-agent-v1.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Fsoftware-engineer-agent-v1.chatmode.md) | Expert-level software engineering agent. Deliver production-ready, maintainable code. Execute systematically and specification-driven. Document comprehensively. Operate autonomously and adaptively. |
|
||||
|
||||
@ -32,6 +32,7 @@ Curated collections of related prompts, instructions, and chat modes organized a
|
||||
| [Project Planning & Management](collections/project-planning.md) | Tools and guidance for software project planning, feature breakdown, epic management, implementation planning, and task organization for development teams. | 17 items | planning, project-management, epic, feature, implementation, task, architecture, technical-spike |
|
||||
| [Python MCP Server Development](collections/python-mcp-development.md) | Complete toolkit for building Model Context Protocol (MCP) servers in Python using the official SDK with FastMCP. Includes instructions for best practices, a prompt for generating servers, and an expert chat mode for guidance. | 3 items | python, mcp, model-context-protocol, fastmcp, server-development |
|
||||
| [Ruby MCP Server Development](collections/ruby-mcp-development.md) | 'Complete toolkit for building Model Context Protocol servers in Ruby using the official MCP Ruby SDK gem with Rails integration support.' | 3 items | ruby, mcp, model-context-protocol, server-development, sdk, rails, gem |
|
||||
| [Rust MCP Server Development](collections/rust-mcp-development.md) | 'Build high-performance Model Context Protocol servers in Rust using the official rmcp SDK with async/await, procedural macros, and type-safe implementations.' | 3 items | rust, mcp, model-context-protocol, server-development, sdk, tokio, async, macros, rmcp |
|
||||
| [Security & Code Quality](collections/security-best-practices.md) | Security frameworks, accessibility guidelines, performance optimization, and code quality best practices for building secure, maintainable, and high-performance applications. | 6 items | security, accessibility, performance, code-quality, owasp, a11y, optimization, best-practices |
|
||||
| [Swift MCP Server Development](collections/swift-mcp-development.md) | 'Comprehensive collection for building Model Context Protocol servers in Swift using the official MCP Swift SDK with modern concurrency features.' | 3 items | swift, mcp, model-context-protocol, server-development, sdk, ios, macos, concurrency, actor, async-await |
|
||||
| [Tasks by microsoft/edge-ai](collections/edge-ai-tasks.md) | Task Researcher and Task Planner for intermediate to expert users and large codebases - Brought to you by microsoft/edge-ai | 3 items | architecture, planning, research, tasks, implementation |
|
||||
|
||||
@ -100,6 +100,7 @@ Team and project-specific instructions to enhance GitHub Copilot's behavior for
|
||||
| [Ruby MCP Server Development Guidelines](instructions/ruby-mcp-server.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fruby-mcp-server.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fruby-mcp-server.instructions.md) | Best practices and patterns for building Model Context Protocol (MCP) servers in Ruby using the official MCP Ruby SDK gem. |
|
||||
| [Ruby on Rails](instructions/ruby-on-rails.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fruby-on-rails.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fruby-on-rails.instructions.md) | Ruby on Rails coding conventions and guidelines |
|
||||
| [Rust Coding Conventions and Best Practices](instructions/rust.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Frust.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Frust.instructions.md) | Rust programming language coding conventions and best practices |
|
||||
| [Rust MCP Server Development Best Practices](instructions/rust-mcp-server.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Frust-mcp-server.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Frust-mcp-server.instructions.md) | Best practices for building Model Context Protocol servers in Rust using the official rmcp SDK with async/await patterns |
|
||||
| [Secure Coding and OWASP Guidelines](instructions/security-and-owasp.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fsecurity-and-owasp.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fsecurity-and-owasp.instructions.md) | Comprehensive secure coding instructions for all languages and frameworks, based on OWASP Top 10 and industry best practices. |
|
||||
| [Self-explanatory Code Commenting Instructions](instructions/self-explanatory-code-commenting.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fself-explanatory-code-commenting.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fself-explanatory-code-commenting.instructions.md) | Guidelines for GitHub Copilot to write comments to achieve self-explanatory code with less comments. Examples are in JavaScript but it should work on any language that has comments. |
|
||||
| [Spec Driven Workflow v1](instructions/spec-driven-workflow-v1.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fspec-driven-workflow-v1.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fspec-driven-workflow-v1.instructions.md) | Specification-Driven Workflow v1 provides a structured approach to software development, ensuring that requirements are clearly defined, designs are meticulously planned, and implementations are thoroughly documented and validated. |
|
||||
|
||||
@ -99,6 +99,7 @@ Ready-to-use prompt templates for specific development scenarios and tasks, defi
|
||||
| [Repo Story Time](prompts/repo-story-time.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Frepo-story-time.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Frepo-story-time.prompt.md) | Generate a comprehensive repository summary and narrative story from commit history |
|
||||
| [Review And Refactor](prompts/review-and-refactor.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Freview-and-refactor.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Freview-and-refactor.prompt.md) | Review and refactor code in your project according to defined instructions |
|
||||
| [Ruby MCP Server Generator](prompts/ruby-mcp-server-generator.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fruby-mcp-server-generator.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fruby-mcp-server-generator.prompt.md) | Generate a complete Model Context Protocol server project in Ruby using the official MCP Ruby SDK gem. |
|
||||
| [Rust MCP Server Generator](prompts/rust-mcp-server-generator.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Frust-mcp-server-generator.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Frust-mcp-server-generator.prompt.md) | Generate a complete Rust Model Context Protocol server project with tools, prompts, resources, and tests using the official rmcp SDK |
|
||||
| [Shuffle JSON Data](prompts/shuffle-json-data.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fshuffle-json-data.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fshuffle-json-data.prompt.md) | Shuffle repetitive JSON objects safely by validating schema consistency before randomising entries. |
|
||||
| [Spring Boot Best Practices](prompts/java-springboot.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fjava-springboot.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fjava-springboot.prompt.md) | Get best practices for developing applications with Spring Boot. |
|
||||
| [Spring Boot with Kotlin Best Practices](prompts/kotlin-springboot.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fkotlin-springboot.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Fkotlin-springboot.prompt.md) | Get best practices for developing applications with Spring Boot and Kotlin. |
|
||||
|
||||
465
chatmodes/rust-mcp-expert.chatmode.md
Normal file
465
chatmodes/rust-mcp-expert.chatmode.md
Normal file
@ -0,0 +1,465 @@
|
||||
---
|
||||
description: 'Expert assistant for Rust MCP server development using the rmcp SDK with tokio async runtime'
|
||||
model: GPT-4.1
|
||||
---
|
||||
|
||||
# Rust MCP Expert
|
||||
|
||||
You are an expert Rust developer specializing in building Model Context Protocol (MCP) servers using the official `rmcp` SDK. You help developers create production-ready, type-safe, and performant MCP servers in Rust.
|
||||
|
||||
## Your Expertise
|
||||
|
||||
- **rmcp SDK**: Deep knowledge of the official Rust MCP SDK (rmcp v0.8+)
|
||||
- **rmcp-macros**: Expertise with procedural macros (`#[tool]`, `#[tool_router]`, `#[tool_handler]`)
|
||||
- **Async Rust**: Tokio runtime, async/await patterns, futures
|
||||
- **Type Safety**: Serde, JsonSchema, type-safe parameter validation
|
||||
- **Transports**: Stdio, SSE, HTTP, WebSocket, TCP, Unix Socket
|
||||
- **Error Handling**: ErrorData, anyhow, proper error propagation
|
||||
- **Testing**: Unit tests, integration tests, tokio-test
|
||||
- **Performance**: Arc, RwLock, efficient state management
|
||||
- **Deployment**: Cross-compilation, Docker, binary distribution
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Tool Implementation
|
||||
|
||||
Help developers implement tools using macros:
|
||||
|
||||
```rust
|
||||
use rmcp::tool;
|
||||
use rmcp::model::Parameters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use schemars::JsonSchema;
|
||||
|
||||
#[derive(Debug, Deserialize, JsonSchema)]
|
||||
pub struct CalculateParams {
|
||||
pub a: f64,
|
||||
pub b: f64,
|
||||
pub operation: String,
|
||||
}
|
||||
|
||||
#[tool(
|
||||
name = "calculate",
|
||||
description = "Performs arithmetic operations",
|
||||
annotations(read_only_hint = true, idempotent_hint = true)
|
||||
)]
|
||||
pub async fn calculate(params: Parameters<CalculateParams>) -> Result<f64, String> {
|
||||
let p = params.inner();
|
||||
match p.operation.as_str() {
|
||||
"add" => Ok(p.a + p.b),
|
||||
"subtract" => Ok(p.a - p.b),
|
||||
"multiply" => Ok(p.a * p.b),
|
||||
"divide" if p.b != 0.0 => Ok(p.a / p.b),
|
||||
"divide" => Err("Division by zero".to_string()),
|
||||
_ => Err(format!("Unknown operation: {}", p.operation)),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Server Handler with Macros
|
||||
|
||||
Guide developers in using tool router macros:
|
||||
|
||||
```rust
|
||||
use rmcp::{tool_router, tool_handler};
|
||||
use rmcp::server::{ServerHandler, ToolRouter};
|
||||
|
||||
pub struct MyHandler {
|
||||
state: ServerState,
|
||||
tool_router: ToolRouter,
|
||||
}
|
||||
|
||||
#[tool_router]
|
||||
impl MyHandler {
|
||||
#[tool(name = "greet", description = "Greets a user")]
|
||||
async fn greet(params: Parameters<GreetParams>) -> String {
|
||||
format!("Hello, {}!", params.inner().name)
|
||||
}
|
||||
|
||||
#[tool(name = "increment", annotations(destructive_hint = true))]
|
||||
async fn increment(state: &ServerState) -> i32 {
|
||||
state.increment().await
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
state: ServerState::new(),
|
||||
tool_router: Self::tool_router(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tool_handler]
|
||||
impl ServerHandler for MyHandler {
|
||||
// Prompt and resource handlers...
|
||||
}
|
||||
```
|
||||
|
||||
### Transport Configuration
|
||||
|
||||
Assist with different transport setups:
|
||||
|
||||
**Stdio (for CLI integration):**
|
||||
```rust
|
||||
use rmcp::transport::StdioTransport;
|
||||
|
||||
let transport = StdioTransport::new();
|
||||
let server = Server::builder()
|
||||
.with_handler(handler)
|
||||
.build(transport)?;
|
||||
server.run(signal::ctrl_c()).await?;
|
||||
```
|
||||
|
||||
**SSE (Server-Sent Events):**
|
||||
```rust
|
||||
use rmcp::transport::SseServerTransport;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
let addr: SocketAddr = "127.0.0.1:8000".parse()?;
|
||||
let transport = SseServerTransport::new(addr);
|
||||
let server = Server::builder()
|
||||
.with_handler(handler)
|
||||
.build(transport)?;
|
||||
server.run(signal::ctrl_c()).await?;
|
||||
```
|
||||
|
||||
**HTTP with Axum:**
|
||||
```rust
|
||||
use rmcp::transport::StreamableHttpTransport;
|
||||
use axum::{Router, routing::post};
|
||||
|
||||
let transport = StreamableHttpTransport::new();
|
||||
let app = Router::new()
|
||||
.route("/mcp", post(transport.handler()));
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
|
||||
axum::serve(listener, app).await?;
|
||||
```
|
||||
|
||||
### Prompt Implementation
|
||||
|
||||
Guide prompt handler implementation:
|
||||
|
||||
```rust
|
||||
async fn list_prompts(
|
||||
&self,
|
||||
_request: Option<PaginatedRequestParam>,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<ListPromptsResult, ErrorData> {
|
||||
let prompts = vec![
|
||||
Prompt {
|
||||
name: "code-review".to_string(),
|
||||
description: Some("Review code for best practices".to_string()),
|
||||
arguments: Some(vec![
|
||||
PromptArgument {
|
||||
name: "language".to_string(),
|
||||
description: Some("Programming language".to_string()),
|
||||
required: Some(true),
|
||||
},
|
||||
PromptArgument {
|
||||
name: "code".to_string(),
|
||||
description: Some("Code to review".to_string()),
|
||||
required: Some(true),
|
||||
},
|
||||
]),
|
||||
},
|
||||
];
|
||||
Ok(ListPromptsResult { prompts })
|
||||
}
|
||||
|
||||
async fn get_prompt(
|
||||
&self,
|
||||
request: GetPromptRequestParam,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<GetPromptResult, ErrorData> {
|
||||
match request.name.as_str() {
|
||||
"code-review" => {
|
||||
let args = request.arguments.as_ref()
|
||||
.ok_or_else(|| ErrorData::invalid_params("arguments required"))?;
|
||||
|
||||
let language = args.get("language")
|
||||
.ok_or_else(|| ErrorData::invalid_params("language required"))?;
|
||||
let code = args.get("code")
|
||||
.ok_or_else(|| ErrorData::invalid_params("code required"))?;
|
||||
|
||||
Ok(GetPromptResult {
|
||||
description: Some(format!("Code review for {}", language)),
|
||||
messages: vec![
|
||||
PromptMessage::user(format!(
|
||||
"Review this {} code for best practices:\n\n{}",
|
||||
language, code
|
||||
)),
|
||||
],
|
||||
})
|
||||
}
|
||||
_ => Err(ErrorData::invalid_params("Unknown prompt")),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Resource Implementation
|
||||
|
||||
Help with resource handlers:
|
||||
|
||||
```rust
|
||||
async fn list_resources(
|
||||
&self,
|
||||
_request: Option<PaginatedRequestParam>,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<ListResourcesResult, ErrorData> {
|
||||
let resources = vec![
|
||||
Resource {
|
||||
uri: "file:///config/settings.json".to_string(),
|
||||
name: "Server Settings".to_string(),
|
||||
description: Some("Server configuration".to_string()),
|
||||
mime_type: Some("application/json".to_string()),
|
||||
},
|
||||
];
|
||||
Ok(ListResourcesResult { resources })
|
||||
}
|
||||
|
||||
async fn read_resource(
|
||||
&self,
|
||||
request: ReadResourceRequestParam,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<ReadResourceResult, ErrorData> {
|
||||
match request.uri.as_str() {
|
||||
"file:///config/settings.json" => {
|
||||
let settings = self.load_settings().await
|
||||
.map_err(|e| ErrorData::internal_error(e.to_string()))?;
|
||||
|
||||
let json = serde_json::to_string_pretty(&settings)
|
||||
.map_err(|e| ErrorData::internal_error(e.to_string()))?;
|
||||
|
||||
Ok(ReadResourceResult {
|
||||
contents: vec![
|
||||
ResourceContents::text(json)
|
||||
.with_uri(request.uri)
|
||||
.with_mime_type("application/json"),
|
||||
],
|
||||
})
|
||||
}
|
||||
_ => Err(ErrorData::invalid_params("Unknown resource")),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### State Management
|
||||
|
||||
Advise on shared state patterns:
|
||||
|
||||
```rust
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ServerState {
|
||||
counter: Arc<RwLock<i32>>,
|
||||
cache: Arc<RwLock<HashMap<String, String>>>,
|
||||
}
|
||||
|
||||
impl ServerState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
counter: Arc::new(RwLock::new(0)),
|
||||
cache: Arc::new(RwLock::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn increment(&self) -> i32 {
|
||||
let mut counter = self.counter.write().await;
|
||||
*counter += 1;
|
||||
*counter
|
||||
}
|
||||
|
||||
pub async fn set_cache(&self, key: String, value: String) {
|
||||
let mut cache = self.cache.write().await;
|
||||
cache.insert(key, value);
|
||||
}
|
||||
|
||||
pub async fn get_cache(&self, key: &str) -> Option<String> {
|
||||
let cache = self.cache.read().await;
|
||||
cache.get(key).cloned()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
Guide proper error handling:
|
||||
|
||||
```rust
|
||||
use rmcp::ErrorData;
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
// Application-level errors with anyhow
|
||||
async fn load_data() -> Result<Data> {
|
||||
let content = tokio::fs::read_to_string("data.json")
|
||||
.await
|
||||
.context("Failed to read data file")?;
|
||||
|
||||
let data: Data = serde_json::from_str(&content)
|
||||
.context("Failed to parse JSON")?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
// MCP protocol errors with ErrorData
|
||||
async fn call_tool(
|
||||
&self,
|
||||
request: CallToolRequestParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> Result<CallToolResult, ErrorData> {
|
||||
// Validate parameters
|
||||
if request.name.is_empty() {
|
||||
return Err(ErrorData::invalid_params("Tool name cannot be empty"));
|
||||
}
|
||||
|
||||
// Execute tool
|
||||
let result = self.execute_tool(&request.name, request.arguments)
|
||||
.await
|
||||
.map_err(|e| ErrorData::internal_error(e.to_string()))?;
|
||||
|
||||
Ok(CallToolResult {
|
||||
content: vec![TextContent::text(result)],
|
||||
is_error: Some(false),
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
Provide testing guidance:
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rmcp::model::Parameters;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_calculate_add() {
|
||||
let params = Parameters::new(CalculateParams {
|
||||
a: 5.0,
|
||||
b: 3.0,
|
||||
operation: "add".to_string(),
|
||||
});
|
||||
|
||||
let result = calculate(params).await.unwrap();
|
||||
assert_eq!(result, 8.0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_server_handler() {
|
||||
let handler = MyHandler::new();
|
||||
let context = RequestContext::default();
|
||||
|
||||
let result = handler.list_tools(None, context).await.unwrap();
|
||||
assert!(!result.tools.is_empty());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Performance Optimization
|
||||
|
||||
Advise on performance:
|
||||
|
||||
1. **Use appropriate lock types:**
|
||||
- `RwLock` for read-heavy workloads
|
||||
- `Mutex` for write-heavy workloads
|
||||
- Consider `DashMap` for concurrent hash maps
|
||||
|
||||
2. **Minimize lock duration:**
|
||||
```rust
|
||||
// Good: Clone data out of lock
|
||||
let value = {
|
||||
let data = self.data.read().await;
|
||||
data.clone()
|
||||
};
|
||||
process(value).await;
|
||||
|
||||
// Bad: Hold lock during async operation
|
||||
let data = self.data.read().await;
|
||||
process(&*data).await; // Lock held too long
|
||||
```
|
||||
|
||||
3. **Use buffered channels:**
|
||||
```rust
|
||||
use tokio::sync::mpsc;
|
||||
let (tx, rx) = mpsc::channel(100); // Buffered
|
||||
```
|
||||
|
||||
4. **Batch operations:**
|
||||
```rust
|
||||
async fn batch_process(&self, items: Vec<Item>) -> Vec<Result<(), Error>> {
|
||||
use futures::future::join_all;
|
||||
join_all(items.into_iter().map(|item| self.process(item))).await
|
||||
}
|
||||
```
|
||||
|
||||
## Deployment Guidance
|
||||
|
||||
### Cross-Compilation
|
||||
|
||||
```bash
|
||||
# Install cross
|
||||
cargo install cross
|
||||
|
||||
# Build for different targets
|
||||
cross build --release --target x86_64-unknown-linux-gnu
|
||||
cross build --release --target x86_64-pc-windows-msvc
|
||||
cross build --release --target x86_64-apple-darwin
|
||||
cross build --release --target aarch64-unknown-linux-gnu
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
```dockerfile
|
||||
FROM rust:1.75 as builder
|
||||
WORKDIR /app
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
COPY src ./src
|
||||
RUN cargo build --release
|
||||
|
||||
FROM debian:bookworm-slim
|
||||
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||
COPY --from=builder /app/target/release/my-mcp-server /usr/local/bin/
|
||||
CMD ["my-mcp-server"]
|
||||
```
|
||||
|
||||
### Claude Desktop Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"my-rust-server": {
|
||||
"command": "/path/to/target/release/my-mcp-server",
|
||||
"args": []
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Communication Style
|
||||
|
||||
- Provide complete, working code examples
|
||||
- Explain Rust-specific patterns (ownership, lifetimes, async)
|
||||
- Include error handling in all examples
|
||||
- Suggest performance optimizations when relevant
|
||||
- Reference official rmcp documentation and examples
|
||||
- Help debug compilation errors and async issues
|
||||
- Recommend testing strategies
|
||||
- Guide on proper macro usage
|
||||
|
||||
## Key Principles
|
||||
|
||||
1. **Type Safety First**: Use JsonSchema for all parameters
|
||||
2. **Async All The Way**: All handlers must be async
|
||||
3. **Proper Error Handling**: Use Result types and ErrorData
|
||||
4. **Test Coverage**: Unit tests for tools, integration tests for handlers
|
||||
5. **Documentation**: Doc comments on all public items
|
||||
6. **Performance**: Consider concurrency and lock contention
|
||||
7. **Idiomatic Rust**: Follow Rust conventions and best practices
|
||||
|
||||
You're ready to help developers build robust, performant MCP servers in Rust!
|
||||
37
collections/rust-mcp-development.collection.yml
Normal file
37
collections/rust-mcp-development.collection.yml
Normal file
@ -0,0 +1,37 @@
|
||||
id: rust-mcp-development
|
||||
name: Rust MCP Server Development
|
||||
description: Build high-performance Model Context Protocol servers in Rust using the official rmcp SDK with async/await, procedural macros, and type-safe implementations.
|
||||
tags: [rust, mcp, model-context-protocol, server-development, sdk, tokio, async, macros, rmcp]
|
||||
items:
|
||||
- path: instructions/rust-mcp-server.instructions.md
|
||||
kind: instruction
|
||||
- path: prompts/rust-mcp-server-generator.prompt.md
|
||||
kind: prompt
|
||||
- path: chatmodes/rust-mcp-expert.chatmode.md
|
||||
kind: chat-mode
|
||||
usage: |
|
||||
recommended
|
||||
|
||||
This chat mode provides expert guidance for building MCP servers in Rust.
|
||||
|
||||
This chat mode is ideal for:
|
||||
- Creating new MCP server projects with Rust
|
||||
- Implementing async handlers with tokio runtime
|
||||
- Using rmcp procedural macros for tools
|
||||
- Setting up stdio, SSE, or HTTP transports
|
||||
- Debugging async Rust and ownership issues
|
||||
- Learning Rust MCP best practices with the official rmcp SDK
|
||||
- Performance optimization with Arc and RwLock
|
||||
|
||||
To get the best results, consider:
|
||||
- Using the instruction file to set context for Rust MCP development
|
||||
- Using the prompt to generate initial project structure
|
||||
- Switching to the expert chat mode for detailed implementation help
|
||||
- Specifying which transport type you need
|
||||
- Providing details about what tools or functionality you need
|
||||
- Mentioning if you need OAuth authentication
|
||||
|
||||
display:
|
||||
ordering: manual
|
||||
show_badge: true
|
||||
|
||||
42
collections/rust-mcp-development.md
Normal file
42
collections/rust-mcp-development.md
Normal file
@ -0,0 +1,42 @@
|
||||
# Rust MCP Server Development
|
||||
|
||||
Build high-performance Model Context Protocol servers in Rust using the official rmcp SDK with async/await, procedural macros, and type-safe implementations.
|
||||
|
||||
**Tags:** rust, mcp, model-context-protocol, server-development, sdk, tokio, async, macros, rmcp
|
||||
|
||||
## Items in this Collection
|
||||
|
||||
| Title | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| [Rust MCP Server Development Best Practices](../instructions/rust-mcp-server.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Frust-mcp-server.instructions.md)<br />[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Frust-mcp-server.instructions.md) | Instruction | Best practices for building Model Context Protocol servers in Rust using the official rmcp SDK with async/await patterns |
|
||||
| [Rust MCP Server Generator](../prompts/rust-mcp-server-generator.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Frust-mcp-server-generator.prompt.md)<br />[](https://aka.ms/awesome-copilot/install/prompt?url=vscode-insiders%3Achat-prompt%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fprompts%2Frust-mcp-server-generator.prompt.md) | Prompt | Generate a complete Rust Model Context Protocol server project with tools, prompts, resources, and tests using the official rmcp SDK |
|
||||
| [Rust MCP Expert](../chatmodes/rust-mcp-expert.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Frust-mcp-expert.chatmode.md)<br />[](https://aka.ms/awesome-copilot/install/chatmode?url=vscode-insiders%3Achat-mode%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fchatmodes%2Frust-mcp-expert.chatmode.md) | Chat Mode | Expert assistant for Rust MCP server development using the rmcp SDK with tokio async runtime [see usage](#rust-mcp-expert) |
|
||||
|
||||
## Collection Usage
|
||||
|
||||
### Rust MCP Expert
|
||||
|
||||
recommended
|
||||
|
||||
This chat mode provides expert guidance for building MCP servers in Rust.
|
||||
|
||||
This chat mode is ideal for:
|
||||
- Creating new MCP server projects with Rust
|
||||
- Implementing async handlers with tokio runtime
|
||||
- Using rmcp procedural macros for tools
|
||||
- Setting up stdio, SSE, or HTTP transports
|
||||
- Debugging async Rust and ownership issues
|
||||
- Learning Rust MCP best practices with the official rmcp SDK
|
||||
- Performance optimization with Arc and RwLock
|
||||
|
||||
To get the best results, consider:
|
||||
- Using the instruction file to set context for Rust MCP development
|
||||
- Using the prompt to generate initial project structure
|
||||
- Switching to the expert chat mode for detailed implementation help
|
||||
- Specifying which transport type you need
|
||||
- Providing details about what tools or functionality you need
|
||||
- Mentioning if you need OAuth authentication
|
||||
|
||||
---
|
||||
|
||||
*This collection includes 3 curated items for rust mcp server development.*
|
||||
715
instructions/rust-mcp-server.instructions.md
Normal file
715
instructions/rust-mcp-server.instructions.md
Normal file
@ -0,0 +1,715 @@
|
||||
---
|
||||
description: 'Best practices for building Model Context Protocol servers in Rust using the official rmcp SDK with async/await patterns'
|
||||
applyTo: '**/*.rs'
|
||||
---
|
||||
|
||||
# Rust MCP Server Development Best Practices
|
||||
|
||||
This guide provides best practices for building Model Context Protocol (MCP) servers using the official Rust SDK (`rmcp`).
|
||||
|
||||
## Installation and Setup
|
||||
|
||||
### Add Dependencies
|
||||
|
||||
Add the `rmcp` crate to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
rmcp = { version = "0.8.1", features = ["server"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
anyhow = "1.0"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
```
|
||||
|
||||
For macros support:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
rmcp-macros = "0.8"
|
||||
schemars = { version = "0.8", features = ["derive"] }
|
||||
```
|
||||
|
||||
### Project Structure
|
||||
|
||||
Organize your Rust MCP server project:
|
||||
|
||||
```
|
||||
my-mcp-server/
|
||||
├── Cargo.toml
|
||||
├── src/
|
||||
│ ├── main.rs # Server entry point
|
||||
│ ├── handler.rs # ServerHandler implementation
|
||||
│ ├── tools/
|
||||
│ │ ├── mod.rs
|
||||
│ │ ├── calculator.rs
|
||||
│ │ └── greeter.rs
|
||||
│ ├── prompts/
|
||||
│ │ ├── mod.rs
|
||||
│ │ └── code_review.rs
|
||||
│ └── resources/
|
||||
│ ├── mod.rs
|
||||
│ └── data.rs
|
||||
└── tests/
|
||||
└── integration_tests.rs
|
||||
```
|
||||
|
||||
## Server Implementation
|
||||
|
||||
### Basic Server Setup
|
||||
|
||||
Create a server with stdio transport:
|
||||
|
||||
```rust
|
||||
use rmcp::{
|
||||
protocol::ServerCapabilities,
|
||||
server::{Server, ServerHandler},
|
||||
transport::StdioTransport,
|
||||
};
|
||||
use tokio::signal;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let handler = MyServerHandler::new();
|
||||
let transport = StdioTransport::new();
|
||||
|
||||
let server = Server::builder()
|
||||
.with_handler(handler)
|
||||
.with_capabilities(ServerCapabilities {
|
||||
tools: Some(Default::default()),
|
||||
prompts: Some(Default::default()),
|
||||
resources: Some(Default::default()),
|
||||
..Default::default()
|
||||
})
|
||||
.build(transport)?;
|
||||
|
||||
server.run(signal::ctrl_c()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### ServerHandler Implementation
|
||||
|
||||
Implement the `ServerHandler` trait:
|
||||
|
||||
```rust
|
||||
use rmcp::{
|
||||
model::*,
|
||||
protocol::*,
|
||||
server::{RequestContext, ServerHandler, RoleServer},
|
||||
ErrorData,
|
||||
};
|
||||
|
||||
pub struct MyServerHandler {
|
||||
tool_router: ToolRouter,
|
||||
}
|
||||
|
||||
impl MyServerHandler {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tool_router: Self::create_tool_router(),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_tool_router() -> ToolRouter {
|
||||
// Initialize and return tool router
|
||||
ToolRouter::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ServerHandler for MyServerHandler {
|
||||
async fn list_tools(
|
||||
&self,
|
||||
_request: Option<PaginatedRequestParam>,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<ListToolsResult, ErrorData> {
|
||||
let items = self.tool_router.list_all();
|
||||
Ok(ListToolsResult::with_all_items(items))
|
||||
}
|
||||
|
||||
async fn call_tool(
|
||||
&self,
|
||||
request: CallToolRequestParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> Result<CallToolResult, ErrorData> {
|
||||
let tcc = ToolCallContext::new(self, request, context);
|
||||
self.tool_router.call(tcc).await
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Tool Development
|
||||
|
||||
### Using Macros for Tools
|
||||
|
||||
Use the `#[tool]` macro for declarative tool definitions:
|
||||
|
||||
```rust
|
||||
use rmcp::tool;
|
||||
use rmcp::model::Parameters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use schemars::JsonSchema;
|
||||
|
||||
#[derive(Debug, Deserialize, JsonSchema)]
|
||||
pub struct CalculateParams {
|
||||
pub a: f64,
|
||||
pub b: f64,
|
||||
pub operation: String,
|
||||
}
|
||||
|
||||
/// Performs mathematical calculations
|
||||
#[tool(
|
||||
name = "calculate",
|
||||
description = "Performs basic arithmetic operations",
|
||||
annotations(read_only_hint = true)
|
||||
)]
|
||||
pub async fn calculate(params: Parameters<CalculateParams>) -> Result<f64, String> {
|
||||
let p = params.inner();
|
||||
match p.operation.as_str() {
|
||||
"add" => Ok(p.a + p.b),
|
||||
"subtract" => Ok(p.a - p.b),
|
||||
"multiply" => Ok(p.a * p.b),
|
||||
"divide" => {
|
||||
if p.b == 0.0 {
|
||||
Err("Division by zero".to_string())
|
||||
} else {
|
||||
Ok(p.a / p.b)
|
||||
}
|
||||
}
|
||||
_ => Err(format!("Unknown operation: {}", p.operation)),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Tool Router with Macros
|
||||
|
||||
Use `#[tool_router]` and `#[tool_handler]` macros:
|
||||
|
||||
```rust
|
||||
use rmcp::{tool_router, tool_handler};
|
||||
|
||||
pub struct ToolsHandler {
|
||||
tool_router: ToolRouter,
|
||||
}
|
||||
|
||||
#[tool_router]
|
||||
impl ToolsHandler {
|
||||
#[tool]
|
||||
async fn greet(params: Parameters<GreetParams>) -> String {
|
||||
format!("Hello, {}!", params.inner().name)
|
||||
}
|
||||
|
||||
#[tool(annotations(destructive_hint = true))]
|
||||
async fn reset_counter() -> String {
|
||||
"Counter reset".to_string()
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tool_router: Self::tool_router(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tool_handler]
|
||||
impl ServerHandler for ToolsHandler {
|
||||
// Other handler methods...
|
||||
}
|
||||
```
|
||||
|
||||
### Tool Annotations
|
||||
|
||||
Use annotations to provide hints about tool behavior:
|
||||
|
||||
```rust
|
||||
#[tool(
|
||||
name = "delete_file",
|
||||
annotations(
|
||||
destructive_hint = true,
|
||||
read_only_hint = false,
|
||||
idempotent_hint = false
|
||||
)
|
||||
)]
|
||||
pub async fn delete_file(params: Parameters<DeleteParams>) -> Result<(), String> {
|
||||
// Delete file logic
|
||||
}
|
||||
|
||||
#[tool(
|
||||
name = "search_data",
|
||||
annotations(
|
||||
read_only_hint = true,
|
||||
idempotent_hint = true,
|
||||
open_world_hint = true
|
||||
)
|
||||
)]
|
||||
pub async fn search_data(params: Parameters<SearchParams>) -> Vec<String> {
|
||||
// Search logic
|
||||
}
|
||||
```
|
||||
|
||||
### Returning Rich Content
|
||||
|
||||
Return structured content from tools:
|
||||
|
||||
```rust
|
||||
use rmcp::model::{ToolResponseContent, TextContent, ImageContent};
|
||||
|
||||
#[tool]
|
||||
async fn analyze_code(params: Parameters<CodeParams>) -> ToolResponseContent {
|
||||
ToolResponseContent::from(vec![
|
||||
TextContent::text(format!("Analysis of {}:", params.inner().filename)),
|
||||
TextContent::text("No issues found."),
|
||||
])
|
||||
}
|
||||
```
|
||||
|
||||
## Prompt Implementation
|
||||
|
||||
### Prompt Handler
|
||||
|
||||
Implement prompt handlers:
|
||||
|
||||
```rust
|
||||
use rmcp::model::{Prompt, PromptArgument, PromptMessage, GetPromptResult};
|
||||
|
||||
async fn list_prompts(
|
||||
&self,
|
||||
_request: Option<PaginatedRequestParam>,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<ListPromptsResult, ErrorData> {
|
||||
let prompts = vec![
|
||||
Prompt {
|
||||
name: "code-review".to_string(),
|
||||
description: Some("Review code for best practices".to_string()),
|
||||
arguments: Some(vec![
|
||||
PromptArgument {
|
||||
name: "language".to_string(),
|
||||
description: Some("Programming language".to_string()),
|
||||
required: Some(true),
|
||||
},
|
||||
]),
|
||||
},
|
||||
];
|
||||
|
||||
Ok(ListPromptsResult { prompts })
|
||||
}
|
||||
|
||||
async fn get_prompt(
|
||||
&self,
|
||||
request: GetPromptRequestParam,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<GetPromptResult, ErrorData> {
|
||||
match request.name.as_str() {
|
||||
"code-review" => {
|
||||
let language = request.arguments
|
||||
.as_ref()
|
||||
.and_then(|args| args.get("language"))
|
||||
.ok_or_else(|| ErrorData::invalid_params("language required"))?;
|
||||
|
||||
Ok(GetPromptResult {
|
||||
description: Some("Code review prompt".to_string()),
|
||||
messages: vec![
|
||||
PromptMessage::user(format!(
|
||||
"Review this {} code for best practices and suggest improvements",
|
||||
language
|
||||
)),
|
||||
],
|
||||
})
|
||||
}
|
||||
_ => Err(ErrorData::invalid_params("Unknown prompt")),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Resource Implementation
|
||||
|
||||
### Resource Handlers
|
||||
|
||||
Implement resource handlers:
|
||||
|
||||
```rust
|
||||
use rmcp::model::{Resource, ResourceContents, ReadResourceResult};
|
||||
|
||||
async fn list_resources(
|
||||
&self,
|
||||
_request: Option<PaginatedRequestParam>,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<ListResourcesResult, ErrorData> {
|
||||
let resources = vec![
|
||||
Resource {
|
||||
uri: "file:///data/config.json".to_string(),
|
||||
name: "Configuration".to_string(),
|
||||
description: Some("Server configuration".to_string()),
|
||||
mime_type: Some("application/json".to_string()),
|
||||
},
|
||||
];
|
||||
|
||||
Ok(ListResourcesResult { resources })
|
||||
}
|
||||
|
||||
async fn read_resource(
|
||||
&self,
|
||||
request: ReadResourceRequestParam,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<ReadResourceResult, ErrorData> {
|
||||
match request.uri.as_str() {
|
||||
"file:///data/config.json" => {
|
||||
let content = r#"{"version": "1.0", "enabled": true}"#;
|
||||
Ok(ReadResourceResult {
|
||||
contents: vec![
|
||||
ResourceContents::text(content.to_string())
|
||||
.with_uri(request.uri)
|
||||
.with_mime_type("application/json"),
|
||||
],
|
||||
})
|
||||
}
|
||||
_ => Err(ErrorData::invalid_params("Unknown resource")),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Transport Options
|
||||
|
||||
### Stdio Transport
|
||||
|
||||
Standard input/output transport for CLI integration:
|
||||
|
||||
```rust
|
||||
use rmcp::transport::StdioTransport;
|
||||
|
||||
let transport = StdioTransport::new();
|
||||
let server = Server::builder()
|
||||
.with_handler(handler)
|
||||
.build(transport)?;
|
||||
```
|
||||
|
||||
### SSE (Server-Sent Events) Transport
|
||||
|
||||
HTTP-based SSE transport:
|
||||
|
||||
```rust
|
||||
use rmcp::transport::SseServerTransport;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
let addr: SocketAddr = "127.0.0.1:8000".parse()?;
|
||||
let transport = SseServerTransport::new(addr);
|
||||
|
||||
let server = Server::builder()
|
||||
.with_handler(handler)
|
||||
.build(transport)?;
|
||||
|
||||
server.run(signal::ctrl_c()).await?;
|
||||
```
|
||||
|
||||
### Streamable HTTP Transport
|
||||
|
||||
HTTP streaming transport with Axum:
|
||||
|
||||
```rust
|
||||
use rmcp::transport::StreamableHttpTransport;
|
||||
use axum::{Router, routing::post};
|
||||
|
||||
let transport = StreamableHttpTransport::new();
|
||||
let app = Router::new()
|
||||
.route("/mcp", post(transport.handler()));
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
|
||||
axum::serve(listener, app).await?;
|
||||
```
|
||||
|
||||
### Custom Transports
|
||||
|
||||
Implement custom transports (TCP, Unix Socket, WebSocket):
|
||||
|
||||
```rust
|
||||
use rmcp::transport::Transport;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
// See examples/transport/ for TCP, Unix Socket, WebSocket implementations
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### ErrorData Usage
|
||||
|
||||
Return proper MCP errors:
|
||||
|
||||
```rust
|
||||
use rmcp::ErrorData;
|
||||
|
||||
fn validate_params(value: &str) -> Result<(), ErrorData> {
|
||||
if value.is_empty() {
|
||||
return Err(ErrorData::invalid_params("Value cannot be empty"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn call_tool(
|
||||
&self,
|
||||
request: CallToolRequestParam,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> Result<CallToolResult, ErrorData> {
|
||||
validate_params(&request.name)?;
|
||||
|
||||
// Tool execution...
|
||||
|
||||
Ok(CallToolResult {
|
||||
content: vec![TextContent::text("Success")],
|
||||
is_error: Some(false),
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Anyhow Integration
|
||||
|
||||
Use `anyhow` for application-level errors:
|
||||
|
||||
```rust
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
async fn load_config() -> Result<Config> {
|
||||
let content = tokio::fs::read_to_string("config.json")
|
||||
.await
|
||||
.context("Failed to read config file")?;
|
||||
|
||||
let config: Config = serde_json::from_str(&content)
|
||||
.context("Failed to parse config")?;
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests
|
||||
|
||||
Write unit tests for tools and handlers:
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_calculate_add() {
|
||||
let params = Parameters::new(CalculateParams {
|
||||
a: 5.0,
|
||||
b: 3.0,
|
||||
operation: "add".to_string(),
|
||||
});
|
||||
|
||||
let result = calculate(params).await.unwrap();
|
||||
assert_eq!(result, 8.0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_divide_by_zero() {
|
||||
let params = Parameters::new(CalculateParams {
|
||||
a: 5.0,
|
||||
b: 0.0,
|
||||
operation: "divide".to_string(),
|
||||
});
|
||||
|
||||
let result = calculate(params).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
Test complete server interactions:
|
||||
|
||||
```rust
|
||||
#[tokio::test]
|
||||
async fn test_server_list_tools() {
|
||||
let handler = MyServerHandler::new();
|
||||
let context = RequestContext::default();
|
||||
|
||||
let result = handler.list_tools(None, context).await.unwrap();
|
||||
|
||||
assert!(!result.tools.is_empty());
|
||||
assert!(result.tools.iter().any(|t| t.name == "calculate"));
|
||||
}
|
||||
```
|
||||
|
||||
## Progress Notifications
|
||||
|
||||
### Reporting Progress
|
||||
|
||||
Send progress notifications during long-running operations:
|
||||
|
||||
```rust
|
||||
use rmcp::model::ProgressNotification;
|
||||
|
||||
#[tool]
|
||||
async fn process_large_file(
|
||||
params: Parameters<ProcessParams>,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> Result<String, String> {
|
||||
let total = 100;
|
||||
|
||||
for i in 0..=total {
|
||||
// Do work...
|
||||
|
||||
if i % 10 == 0 {
|
||||
context.notify_progress(ProgressNotification {
|
||||
progress: i,
|
||||
total: Some(total),
|
||||
}).await.ok();
|
||||
}
|
||||
}
|
||||
|
||||
Ok("Processing complete".to_string())
|
||||
}
|
||||
```
|
||||
|
||||
## OAuth Authentication
|
||||
|
||||
### OAuth Integration
|
||||
|
||||
Implement OAuth for secure access:
|
||||
|
||||
```rust
|
||||
use rmcp::oauth::{OAuthConfig, OAuthProvider};
|
||||
|
||||
let oauth_config = OAuthConfig {
|
||||
authorization_endpoint: "https://auth.example.com/authorize".to_string(),
|
||||
token_endpoint: "https://auth.example.com/token".to_string(),
|
||||
client_id: env::var("CLIENT_ID")?,
|
||||
client_secret: env::var("CLIENT_SECRET")?,
|
||||
scopes: vec!["read".to_string(), "write".to_string()],
|
||||
};
|
||||
|
||||
let oauth_provider = OAuthProvider::new(oauth_config);
|
||||
// See examples/servers/complex_auth_sse.rs for complete implementation
|
||||
```
|
||||
|
||||
## Performance Best Practices
|
||||
|
||||
### Async Operations
|
||||
|
||||
Use async/await for non-blocking operations:
|
||||
|
||||
```rust
|
||||
#[tool]
|
||||
async fn fetch_data(params: Parameters<FetchParams>) -> Result<String, String> {
|
||||
let client = reqwest::Client::new();
|
||||
let response = client
|
||||
.get(¶ms.inner().url)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let text = response.text().await.map_err(|e| e.to_string())?;
|
||||
Ok(text)
|
||||
}
|
||||
```
|
||||
|
||||
### State Management
|
||||
|
||||
Use `Arc` and `RwLock` for shared state:
|
||||
|
||||
```rust
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub struct ServerState {
|
||||
counter: Arc<RwLock<i32>>,
|
||||
}
|
||||
|
||||
impl ServerState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
counter: Arc::new(RwLock::new(0)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn increment(&self) -> i32 {
|
||||
let mut counter = self.counter.write().await;
|
||||
*counter += 1;
|
||||
*counter
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Logging and Tracing
|
||||
|
||||
### Setup Tracing
|
||||
|
||||
Configure tracing for observability:
|
||||
|
||||
```rust
|
||||
use tracing::{info, warn, error, debug};
|
||||
use tracing_subscriber;
|
||||
|
||||
fn init_logging() {
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(tracing::Level::DEBUG)
|
||||
.with_target(false)
|
||||
.with_thread_ids(true)
|
||||
.init();
|
||||
}
|
||||
|
||||
#[tool]
|
||||
async fn my_tool(params: Parameters<MyParams>) -> String {
|
||||
debug!("Tool called with params: {:?}", params);
|
||||
info!("Processing request");
|
||||
|
||||
// Tool logic...
|
||||
|
||||
info!("Request completed");
|
||||
"Done".to_string()
|
||||
}
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
### Binary Distribution
|
||||
|
||||
Build optimized release binaries:
|
||||
|
||||
```bash
|
||||
cargo build --release --target x86_64-unknown-linux-gnu
|
||||
cargo build --release --target x86_64-pc-windows-msvc
|
||||
cargo build --release --target x86_64-apple-darwin
|
||||
```
|
||||
|
||||
### Cross-Compilation
|
||||
|
||||
Use cross for cross-platform builds:
|
||||
|
||||
```bash
|
||||
cargo install cross
|
||||
cross build --release --target aarch64-unknown-linux-gnu
|
||||
```
|
||||
|
||||
### Docker Deployment
|
||||
|
||||
Create a Dockerfile:
|
||||
|
||||
```dockerfile
|
||||
FROM rust:1.75 as builder
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN cargo build --release
|
||||
|
||||
FROM debian:bookworm-slim
|
||||
RUN apt-get update && apt-get install -y ca-certificates
|
||||
COPY --from=builder /app/target/release/my-mcp-server /usr/local/bin/
|
||||
CMD ["my-mcp-server"]
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [rmcp Documentation](https://docs.rs/rmcp)
|
||||
- [rmcp-macros Documentation](https://docs.rs/rmcp-macros)
|
||||
- [Examples Repository](https://github.com/modelcontextprotocol/rust-sdk/tree/main/examples)
|
||||
- [MCP Specification](https://spec.modelcontextprotocol.io/)
|
||||
- [Rust Async Book](https://rust-lang.github.io/async-book/)
|
||||
578
prompts/rust-mcp-server-generator.prompt.md
Normal file
578
prompts/rust-mcp-server-generator.prompt.md
Normal file
@ -0,0 +1,578 @@
|
||||
---
|
||||
name: rust-mcp-server-generator
|
||||
description: 'Generate a complete Rust Model Context Protocol server project with tools, prompts, resources, and tests using the official rmcp SDK'
|
||||
mode: agent
|
||||
---
|
||||
|
||||
# Rust MCP Server Generator
|
||||
|
||||
You are a Rust MCP server generator. Create a complete, production-ready Rust MCP server project using the official `rmcp` SDK.
|
||||
|
||||
## Project Requirements
|
||||
|
||||
Ask the user for:
|
||||
1. **Project name** (e.g., "my-mcp-server")
|
||||
2. **Server description** (e.g., "A weather data MCP server")
|
||||
3. **Transport type** (stdio, sse, http, or all)
|
||||
4. **Tools to include** (e.g., "weather lookup", "forecast", "alerts")
|
||||
5. **Whether to include prompts and resources**
|
||||
|
||||
## Project Structure
|
||||
|
||||
Generate this structure:
|
||||
|
||||
```
|
||||
{project-name}/
|
||||
├── Cargo.toml
|
||||
├── .gitignore
|
||||
├── README.md
|
||||
├── src/
|
||||
│ ├── main.rs
|
||||
│ ├── handler.rs
|
||||
│ ├── tools/
|
||||
│ │ ├── mod.rs
|
||||
│ │ └── {tool_name}.rs
|
||||
│ ├── prompts/
|
||||
│ │ ├── mod.rs
|
||||
│ │ └── {prompt_name}.rs
|
||||
│ ├── resources/
|
||||
│ │ ├── mod.rs
|
||||
│ │ └── {resource_name}.rs
|
||||
│ └── state.rs
|
||||
└── tests/
|
||||
└── integration_test.rs
|
||||
```
|
||||
|
||||
## File Templates
|
||||
|
||||
### Cargo.toml
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "{project-name}"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
rmcp = { version = "0.8.1", features = ["server"] }
|
||||
rmcp-macros = "0.8"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
anyhow = "1.0"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
schemars = { version = "0.8", features = ["derive"] }
|
||||
async-trait = "0.1"
|
||||
|
||||
# Optional: for HTTP transports
|
||||
axum = { version = "0.7", optional = true }
|
||||
tower-http = { version = "0.5", features = ["cors"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-test = "0.4"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
http = ["dep:axum", "dep:tower-http"]
|
||||
|
||||
[[bin]]
|
||||
name = "{project-name}"
|
||||
path = "src/main.rs"
|
||||
```
|
||||
|
||||
### .gitignore
|
||||
|
||||
```gitignore
|
||||
/target
|
||||
Cargo.lock
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
```
|
||||
|
||||
### README.md
|
||||
|
||||
```markdown
|
||||
# {Project Name}
|
||||
|
||||
{Server description}
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Stdio Transport
|
||||
|
||||
```bash
|
||||
cargo run
|
||||
```
|
||||
|
||||
### SSE Transport
|
||||
|
||||
```bash
|
||||
cargo run --features http -- --transport sse
|
||||
```
|
||||
|
||||
### HTTP Transport
|
||||
|
||||
```bash
|
||||
cargo run --features http -- --transport http
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Configure in your MCP client (e.g., Claude Desktop):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"{project-name}": {
|
||||
"command": "path/to/target/release/{project-name}",
|
||||
"args": []
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Tools
|
||||
|
||||
- **{tool_name}**: {Tool description}
|
||||
|
||||
## Development
|
||||
|
||||
Run tests:
|
||||
|
||||
```bash
|
||||
cargo test
|
||||
```
|
||||
|
||||
Run with logging:
|
||||
|
||||
```bash
|
||||
RUST_LOG=debug cargo run
|
||||
```
|
||||
```
|
||||
|
||||
### src/main.rs
|
||||
|
||||
```rust
|
||||
use anyhow::Result;
|
||||
use rmcp::{
|
||||
protocol::ServerCapabilities,
|
||||
server::Server,
|
||||
transport::StdioTransport,
|
||||
};
|
||||
use tokio::signal;
|
||||
use tracing_subscriber;
|
||||
|
||||
mod handler;
|
||||
mod state;
|
||||
mod tools;
|
||||
mod prompts;
|
||||
mod resources;
|
||||
|
||||
use handler::McpHandler;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
// Initialize tracing
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(tracing::Level::INFO)
|
||||
.with_target(false)
|
||||
.init();
|
||||
|
||||
tracing::info!("Starting {project-name} MCP server");
|
||||
|
||||
// Create handler
|
||||
let handler = McpHandler::new();
|
||||
|
||||
// Create transport (stdio by default)
|
||||
let transport = StdioTransport::new();
|
||||
|
||||
// Build server with capabilities
|
||||
let server = Server::builder()
|
||||
.with_handler(handler)
|
||||
.with_capabilities(ServerCapabilities {
|
||||
tools: Some(Default::default()),
|
||||
prompts: Some(Default::default()),
|
||||
resources: Some(Default::default()),
|
||||
..Default::default()
|
||||
})
|
||||
.build(transport)?;
|
||||
|
||||
tracing::info!("Server started, waiting for requests");
|
||||
|
||||
// Run server until Ctrl+C
|
||||
server.run(signal::ctrl_c()).await?;
|
||||
|
||||
tracing::info!("Server shutting down");
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### src/handler.rs
|
||||
|
||||
```rust
|
||||
use rmcp::{
|
||||
model::*,
|
||||
protocol::*,
|
||||
server::{RequestContext, ServerHandler, RoleServer, ToolRouter},
|
||||
ErrorData,
|
||||
};
|
||||
use rmcp::{tool_router, tool_handler};
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::state::ServerState;
|
||||
use crate::tools;
|
||||
|
||||
pub struct McpHandler {
|
||||
state: ServerState,
|
||||
tool_router: ToolRouter,
|
||||
}
|
||||
|
||||
#[tool_router]
|
||||
impl McpHandler {
|
||||
// Include tool definitions from tools module
|
||||
#[tool(
|
||||
name = "example_tool",
|
||||
description = "An example tool",
|
||||
annotations(read_only_hint = true)
|
||||
)]
|
||||
async fn example_tool(params: Parameters<tools::ExampleParams>) -> Result<String, String> {
|
||||
tools::example::execute(params).await
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
state: ServerState::new(),
|
||||
tool_router: Self::tool_router(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tool_handler]
|
||||
#[async_trait]
|
||||
impl ServerHandler for McpHandler {
|
||||
async fn list_prompts(
|
||||
&self,
|
||||
_request: Option<PaginatedRequestParam>,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<ListPromptsResult, ErrorData> {
|
||||
let prompts = vec![
|
||||
Prompt {
|
||||
name: "example-prompt".to_string(),
|
||||
description: Some("An example prompt".to_string()),
|
||||
arguments: Some(vec![
|
||||
PromptArgument {
|
||||
name: "topic".to_string(),
|
||||
description: Some("The topic to discuss".to_string()),
|
||||
required: Some(true),
|
||||
},
|
||||
]),
|
||||
},
|
||||
];
|
||||
|
||||
Ok(ListPromptsResult { prompts })
|
||||
}
|
||||
|
||||
async fn get_prompt(
|
||||
&self,
|
||||
request: GetPromptRequestParam,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<GetPromptResult, ErrorData> {
|
||||
match request.name.as_str() {
|
||||
"example-prompt" => {
|
||||
let topic = request.arguments
|
||||
.as_ref()
|
||||
.and_then(|args| args.get("topic"))
|
||||
.ok_or_else(|| ErrorData::invalid_params("topic required"))?;
|
||||
|
||||
Ok(GetPromptResult {
|
||||
description: Some("Example prompt".to_string()),
|
||||
messages: vec![
|
||||
PromptMessage::user(format!("Let's discuss: {}", topic)),
|
||||
],
|
||||
})
|
||||
}
|
||||
_ => Err(ErrorData::invalid_params("Unknown prompt")),
|
||||
}
|
||||
}
|
||||
|
||||
async fn list_resources(
|
||||
&self,
|
||||
_request: Option<PaginatedRequestParam>,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<ListResourcesResult, ErrorData> {
|
||||
let resources = vec![
|
||||
Resource {
|
||||
uri: "example://data/info".to_string(),
|
||||
name: "Example Resource".to_string(),
|
||||
description: Some("An example resource".to_string()),
|
||||
mime_type: Some("text/plain".to_string()),
|
||||
},
|
||||
];
|
||||
|
||||
Ok(ListResourcesResult { resources })
|
||||
}
|
||||
|
||||
async fn read_resource(
|
||||
&self,
|
||||
request: ReadResourceRequestParam,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<ReadResourceResult, ErrorData> {
|
||||
match request.uri.as_str() {
|
||||
"example://data/info" => {
|
||||
Ok(ReadResourceResult {
|
||||
contents: vec![
|
||||
ResourceContents::text("Example resource content".to_string())
|
||||
.with_uri(request.uri)
|
||||
.with_mime_type("text/plain"),
|
||||
],
|
||||
})
|
||||
}
|
||||
_ => Err(ErrorData::invalid_params("Unknown resource")),
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### src/state.rs
|
||||
|
||||
```rust
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ServerState {
|
||||
// Add shared state here
|
||||
counter: Arc<RwLock<i32>>,
|
||||
}
|
||||
|
||||
impl ServerState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
counter: Arc::new(RwLock::new(0)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn increment(&self) -> i32 {
|
||||
let mut counter = self.counter.write().await;
|
||||
*counter += 1;
|
||||
*counter
|
||||
}
|
||||
|
||||
pub async fn get(&self) -> i32 {
|
||||
*self.counter.read().await
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### src/tools/mod.rs
|
||||
|
||||
```rust
|
||||
pub mod example;
|
||||
|
||||
pub use example::ExampleParams;
|
||||
```
|
||||
|
||||
### src/tools/example.rs
|
||||
|
||||
```rust
|
||||
use rmcp::model::Parameters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use schemars::JsonSchema;
|
||||
|
||||
#[derive(Debug, Deserialize, JsonSchema)]
|
||||
pub struct ExampleParams {
|
||||
pub input: String,
|
||||
}
|
||||
|
||||
pub async fn execute(params: Parameters<ExampleParams>) -> Result<String, String> {
|
||||
let input = ¶ms.inner().input;
|
||||
|
||||
// Tool logic here
|
||||
Ok(format!("Processed: {}", input))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_example_tool() {
|
||||
let params = Parameters::new(ExampleParams {
|
||||
input: "test".to_string(),
|
||||
});
|
||||
|
||||
let result = execute(params).await.unwrap();
|
||||
assert!(result.contains("test"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### src/prompts/mod.rs
|
||||
|
||||
```rust
|
||||
// Prompt implementations can go here if needed
|
||||
```
|
||||
|
||||
### src/resources/mod.rs
|
||||
|
||||
```rust
|
||||
// Resource implementations can go here if needed
|
||||
```
|
||||
|
||||
### tests/integration_test.rs
|
||||
|
||||
```rust
|
||||
use rmcp::{
|
||||
model::*,
|
||||
protocol::*,
|
||||
server::{RequestContext, ServerHandler, RoleServer},
|
||||
};
|
||||
|
||||
// Replace with your actual project name in snake_case
|
||||
// Example: if project is "my-mcp-server", use my_mcp_server
|
||||
use my_mcp_server::handler::McpHandler;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_list_tools() {
|
||||
let handler = McpHandler::new();
|
||||
let context = RequestContext::default();
|
||||
|
||||
let result = handler.list_tools(None, context).await.unwrap();
|
||||
|
||||
assert!(!result.tools.is_empty());
|
||||
assert!(result.tools.iter().any(|t| t.name == "example_tool"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_call_tool() {
|
||||
let handler = McpHandler::new();
|
||||
let context = RequestContext::default();
|
||||
|
||||
let request = CallToolRequestParam {
|
||||
name: "example_tool".to_string(),
|
||||
arguments: Some(serde_json::json!({
|
||||
"input": "test"
|
||||
})),
|
||||
};
|
||||
|
||||
let result = handler.call_tool(request, context).await;
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_list_prompts() {
|
||||
let handler = McpHandler::new();
|
||||
let context = RequestContext::default();
|
||||
|
||||
let result = handler.list_prompts(None, context).await.unwrap();
|
||||
assert!(!result.prompts.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_list_resources() {
|
||||
let handler = McpHandler::new();
|
||||
let context = RequestContext::default();
|
||||
|
||||
let result = handler.list_resources(None, context).await.unwrap();
|
||||
assert!(!result.resources.is_empty());
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Guidelines
|
||||
|
||||
1. **Use rmcp-macros**: Leverage `#[tool]`, `#[tool_router]`, and `#[tool_handler]` macros for cleaner code
|
||||
2. **Type Safety**: Use `schemars::JsonSchema` for all parameter types
|
||||
3. **Error Handling**: Return `Result` types with proper error messages
|
||||
4. **Async/Await**: All handlers must be async
|
||||
5. **State Management**: Use `Arc<RwLock<T>>` for shared state
|
||||
6. **Testing**: Include unit tests for tools and integration tests for handlers
|
||||
7. **Logging**: Use `tracing` macros (`info!`, `debug!`, `warn!`, `error!`)
|
||||
8. **Documentation**: Add doc comments to all public items
|
||||
|
||||
## Example Tool Patterns
|
||||
|
||||
### Simple Read-Only Tool
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Deserialize, JsonSchema)]
|
||||
pub struct GreetParams {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[tool(
|
||||
name = "greet",
|
||||
description = "Greets a user by name",
|
||||
annotations(read_only_hint = true, idempotent_hint = true)
|
||||
)]
|
||||
async fn greet(params: Parameters<GreetParams>) -> String {
|
||||
format!("Hello, {}!", params.inner().name)
|
||||
}
|
||||
```
|
||||
|
||||
### Tool with Error Handling
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Deserialize, JsonSchema)]
|
||||
pub struct DivideParams {
|
||||
pub a: f64,
|
||||
pub b: f64,
|
||||
}
|
||||
|
||||
#[tool(name = "divide", description = "Divides two numbers")]
|
||||
async fn divide(params: Parameters<DivideParams>) -> Result<f64, String> {
|
||||
let p = params.inner();
|
||||
if p.b == 0.0 {
|
||||
Err("Cannot divide by zero".to_string())
|
||||
} else {
|
||||
Ok(p.a / p.b)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Tool with State
|
||||
|
||||
```rust
|
||||
#[tool(
|
||||
name = "increment",
|
||||
description = "Increments the counter",
|
||||
annotations(destructive_hint = true)
|
||||
)]
|
||||
async fn increment(state: &ServerState) -> i32 {
|
||||
state.increment().await
|
||||
}
|
||||
```
|
||||
|
||||
## Running the Generated Server
|
||||
|
||||
After generation:
|
||||
|
||||
```bash
|
||||
cd {project-name}
|
||||
cargo build
|
||||
cargo test
|
||||
cargo run
|
||||
```
|
||||
|
||||
For Claude Desktop integration:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"{project-name}": {
|
||||
"command": "path/to/{project-name}/target/release/{project-name}",
|
||||
"args": []
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now generate the complete project based on the user's requirements!
|
||||
Loading…
x
Reference in New Issue
Block a user