- 将MCP服务和Mock API合并到单个HTTP服务器(8080端口) - 添加POST /mcp端点,使用无状态StreamableHttpService - 新增docs/mcp-implementation.md文档
428 lines
12 KiB
Markdown
428 lines
12 KiB
Markdown
# rmcp 0.11 MCP Server 实现指南
|
||
|
||
本文档详细介绍了如何使用 rmcp 0.11 实现 MCP Server,包括核心概念、关键代码模式和常见陷阱。
|
||
|
||
## 目录
|
||
|
||
- [核心概念](#核心概念)
|
||
- [Tool 注册三要素](#tool-注册三要素)
|
||
- [完整代码示例](#完整代码示例)
|
||
- [常见错误和解决方案](#常见错误和解决方案)
|
||
- [HTTP 传输配置](#http-传输配置)
|
||
- [客户端配置](#客户端配置)
|
||
|
||
---
|
||
|
||
## 核心概念
|
||
|
||
### rmcp 简介
|
||
|
||
rmcp 是 Rust 官方的 MCP SDK,提供了实现 MCP (Model Context Protocol) 服务器和客户端的完整工具链。
|
||
|
||
### MCP 协议
|
||
|
||
MCP (Model Context Protocol) 是一个开放协议,用于连接 AI 助手与外部系统。它定义了一套标准化的接口,允许 AI 模型:
|
||
|
||
- **Tools**: 调用外部工具/函数
|
||
- **Resources**: 访问外部资源
|
||
- **Prompts**: 使用预定义的提示模板
|
||
|
||
### 依赖配置
|
||
|
||
```toml
|
||
# Cargo.toml
|
||
[dependencies]
|
||
rmcp = { version = "0.11", features = ["server", "transport-streamable-http-server", "transport-streamable-http-server-session"] }
|
||
schemars = "1.0"
|
||
tokio-util = { version = "0.7", features = ["io"] }
|
||
```
|
||
|
||
---
|
||
|
||
## Tool 注册三要素
|
||
|
||
要使 MCP tools 在 rmcp 0.11 中正常工作,**必须**同时具备以下三个要素:
|
||
|
||
### 1. `#[tool_router]` 宏
|
||
|
||
放在包含 tool 方法的 impl 块上:
|
||
|
||
```rust
|
||
#[tool_router]
|
||
impl MockMcpServer {
|
||
// tool 方法...
|
||
}
|
||
```
|
||
|
||
### 2. `tool_router: ToolRouter<Self>` 字段
|
||
|
||
在结构体中必须包含此字段:
|
||
|
||
```rust
|
||
#[derive(Clone)]
|
||
pub struct MockMcpServer {
|
||
manager: Arc<MockManager>,
|
||
tool_router: ToolRouter<Self>, // 必需字段
|
||
}
|
||
```
|
||
|
||
### 3. `#[tool_handler]` 宏
|
||
|
||
放在 `ServerHandler` trait 实现块上:
|
||
|
||
```rust
|
||
#[tool_handler]
|
||
impl ServerHandler for MockMcpServer {
|
||
fn get_info(&self) -> ServerInfo {
|
||
// ...
|
||
}
|
||
}
|
||
```
|
||
|
||
> **重要**: 缺少 `#[tool_handler]` 会导致 `tools/list` 返回空数组 `{"tools": []}`!
|
||
|
||
---
|
||
|
||
## 完整代码示例
|
||
|
||
### 结构体定义
|
||
|
||
```rust
|
||
use std::sync::Arc;
|
||
|
||
use rmcp::{
|
||
handler::server::tool::ToolRouter,
|
||
handler::server::wrapper::Parameters,
|
||
model::*,
|
||
tool, tool_handler, tool_router,
|
||
ErrorData as McpError, ServerHandler,
|
||
};
|
||
use schemars::JsonSchema;
|
||
use serde::{Deserialize, Serialize};
|
||
|
||
/// MCP Server for Mock Server management
|
||
#[derive(Clone)]
|
||
pub struct MockMcpServer {
|
||
manager: Arc<MockManager>,
|
||
tool_router: ToolRouter<Self>, // 必需字段
|
||
}
|
||
```
|
||
|
||
### 请求参数结构体
|
||
|
||
```rust
|
||
/// 使用 schemars 和 serde 自动生成 JSON Schema
|
||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||
pub struct GetRuleRequest {
|
||
#[schemars(description = "Group name (directory name)")]
|
||
pub group: String,
|
||
|
||
#[schemars(description = "Rule name")]
|
||
pub name: String,
|
||
}
|
||
|
||
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
|
||
pub struct ListRulesRequest {
|
||
#[schemars(description = "Optional group name to filter by")]
|
||
pub group: Option<String>,
|
||
}
|
||
```
|
||
|
||
### Tool 方法实现
|
||
|
||
```rust
|
||
#[tool_router]
|
||
impl MockMcpServer {
|
||
pub fn new(manager: Arc<MockManager>) -> Self {
|
||
Self {
|
||
manager,
|
||
tool_router: Self::tool_router(), // 初始化 tool_router
|
||
}
|
||
}
|
||
|
||
/// 无参数 tool
|
||
#[tool(description = "List all groups (directories)")]
|
||
async fn list_groups(&self) -> Result<CallToolResult, McpError> {
|
||
let groups = self.manager.list_groups();
|
||
let result = serde_json::to_string_pretty(&groups)
|
||
.map_err(|e| McpError::internal_error(e.to_string(), None))?;
|
||
Ok(CallToolResult::success(vec![Content::text(result)]))
|
||
}
|
||
|
||
/// 带参数 tool - 使用 Parameters<T> 包装器
|
||
#[tool(description = "Get a specific mock rule by group and name")]
|
||
async fn get_mock_rule(
|
||
&self,
|
||
params: Parameters<GetRuleRequest>,
|
||
) -> Result<CallToolResult, McpError> {
|
||
match self.manager.get(¶ms.0.group, ¶ms.0.name) {
|
||
Some(rule) => {
|
||
let result = serde_json::to_string_pretty(&rule)
|
||
.map_err(|e| McpError::internal_error(e.to_string(), None))?;
|
||
Ok(CallToolResult::success(vec![Content::text(result)]))
|
||
}
|
||
None => Ok(CallToolResult::error(vec![Content::text(
|
||
format!("Rule not found: {}/{}", params.0.group, params.0.name),
|
||
)])),
|
||
}
|
||
}
|
||
|
||
/// 可选参数
|
||
#[tool(description = "List all mock rules, optionally filtered by group")]
|
||
async fn list_mock_rules(
|
||
&self,
|
||
params: Parameters<ListRulesRequest>,
|
||
) -> Result<CallToolResult, McpError> {
|
||
let rules = self.manager.list(params.0.group.as_deref());
|
||
let result = serde_json::to_string_pretty(&rules)
|
||
.map_err(|e| McpError::internal_error(e.to_string(), None))?;
|
||
Ok(CallToolResult::success(vec![Content::text(result)]))
|
||
}
|
||
}
|
||
```
|
||
|
||
### ServerHandler 实现
|
||
|
||
```rust
|
||
#[tool_handler] // 关键宏!
|
||
impl ServerHandler for MockMcpServer {
|
||
fn get_info(&self) -> ServerInfo {
|
||
ServerInfo {
|
||
capabilities: ServerCapabilities::builder()
|
||
.enable_tools() // 必须启用 tools 能力
|
||
.build(),
|
||
instructions: Some("Mock Server MCP - Manage mock API rules.".to_string()),
|
||
..Default::default()
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 常见错误和解决方案
|
||
|
||
### 1. Missing `#[tool_handler]`
|
||
|
||
**症状**: `tools/list` 返回空数组 `{"tools": []}`
|
||
|
||
**原因**: 没有在 `ServerHandler` impl 块上添加 `#[tool_handler]` 宏
|
||
|
||
**解决方案**:
|
||
|
||
```rust
|
||
// 错误 - 缺少宏
|
||
impl ServerHandler for MockMcpServer {
|
||
fn get_info(&self) -> ServerInfo { ... }
|
||
}
|
||
|
||
// 正确
|
||
#[tool_handler]
|
||
impl ServerHandler for MockMcpServer {
|
||
fn get_info(&self) -> ServerInfo { ... }
|
||
}
|
||
```
|
||
|
||
### 2. Missing `enable_tools()`
|
||
|
||
**症状**: Tools 不被广播,客户端无法发现 tools
|
||
|
||
**原因**: `ServerCapabilities` 没有启用 tools
|
||
|
||
**解决方案**:
|
||
|
||
```rust
|
||
// 错误
|
||
ServerInfo {
|
||
capabilities: ServerCapabilities::default (),
|
||
...
|
||
}
|
||
|
||
// 正确
|
||
ServerInfo {
|
||
capabilities: ServerCapabilities::builder()
|
||
.enable_tools()
|
||
.build(),
|
||
...
|
||
}
|
||
```
|
||
|
||
### 3. 使用 `#[tool(aggr)]`
|
||
|
||
**症状**: 编译错误
|
||
|
||
**原因**: rmcp 0.11 不支持 `aggr` 参数
|
||
|
||
**解决方案**:
|
||
|
||
```rust
|
||
// 错误 - rmcp 0.11 不支持
|
||
#[tool(aggr)]
|
||
async fn my_tool(&self, params: MyRequest) -> Result<...>
|
||
|
||
// 正确 - 使用 Parameters<T> 包装器
|
||
#[tool(description = "...")]
|
||
async fn my_tool(&self, params: Parameters<MyRequest>) -> Result<...>
|
||
```
|
||
|
||
### 4. 使用 `#[tool(tool_box)]`
|
||
|
||
**症状**: 编译错误
|
||
|
||
**原因**: rmcp 0.11 使用不同的宏名称
|
||
|
||
**解决方案**:
|
||
|
||
```rust
|
||
// 错误
|
||
#[tool(tool_box)]
|
||
impl MockMcpServer { ... }
|
||
|
||
// 正确
|
||
#[tool_router]
|
||
impl MockMcpServer { ... }
|
||
```
|
||
|
||
---
|
||
|
||
## HTTP 传输配置
|
||
|
||
### StreamableHttpService 无状态模式
|
||
|
||
适用于简单的 HTTP 集成,无需维护会话状态:
|
||
|
||
```rust
|
||
use rmcp::transport::streamable_http_server::{
|
||
StreamableHttpService, StreamableHttpServerConfig,
|
||
session::never::NeverSessionManager,
|
||
};
|
||
use tokio_util::sync::CancellationToken;
|
||
|
||
/// 创建无状态 MCP HTTP 服务
|
||
pub fn create_mcp_http_service(
|
||
manager: Arc<MockManager>,
|
||
) -> StreamableHttpService<MockMcpServer, NeverSessionManager> {
|
||
StreamableHttpService::new(
|
||
// 每次请求创建新的 server 实例
|
||
move || Ok(MockMcpServer::new(manager.clone())),
|
||
// 无状态会话管理器
|
||
Arc::new(NeverSessionManager::default()),
|
||
StreamableHttpServerConfig {
|
||
sse_keep_alive: None, // SSE 保活配置(可选)
|
||
stateful_mode: false, // 无状态模式
|
||
cancellation_token: CancellationToken::new(),
|
||
},
|
||
)
|
||
}
|
||
```
|
||
|
||
### 与 Axum 集成
|
||
|
||
```rust
|
||
use axum::{
|
||
routing::post,
|
||
Router,
|
||
body::Body,
|
||
http::Request,
|
||
};
|
||
|
||
let mcp_service = create_mcp_http_service(manager);
|
||
|
||
let app = Router::new()
|
||
.route("/mcp", post({
|
||
let service = mcp_service.clone();
|
||
move | req: Request < Body > | {
|
||
let service = service.clone();
|
||
async move { service.handle(req).await }
|
||
}
|
||
}));
|
||
```
|
||
|
||
### 配置选项说明
|
||
|
||
| 选项 | 类型 | 说明 |
|
||
|----------------------|---------------------|------------|
|
||
| `sse_keep_alive` | `Option<Duration>` | SSE 连接保活间隔 |
|
||
| `stateful_mode` | `bool` | 是否维护会话状态 |
|
||
| `cancellation_token` | `CancellationToken` | 用于优雅关闭 |
|
||
|
||
---
|
||
|
||
## 客户端配置
|
||
|
||
### Claude Code 配置
|
||
|
||
在 Claude Code 的 `settings.json` 中添加:
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"mock-server": {
|
||
"type": "http",
|
||
"url": "http://127.0.0.1:8080/mcp"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Claude Desktop 配置
|
||
|
||
在 Claude Desktop 的配置文件中添加:
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"mock-server": {
|
||
"url": "http://127.0.0.1:8080/mcp"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 架构说明
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ HTTP Server (Axum) │
|
||
│ Port 8080 │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ POST /mcp │ /* (fallback) │
|
||
│ ├─ tools/list │ Mock API endpoints │
|
||
│ ├─ tools/call │ │
|
||
│ └─ other MCP methods │ │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ StreamableHttpService<MockMcpServer> │
|
||
│ │
|
||
│ ┌─────────────────┐ ┌─────────────────────────────┐ │
|
||
│ │ MockMcpServer │ │ NeverSessionManager │ │
|
||
│ │ │ │ (无状态) │ │
|
||
│ │ - tool_router │ │ │ │
|
||
│ │ - manager │ │ │ │
|
||
│ └─────────────────┘ └─────────────────────────────┘ │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ MockManager (Arc) │
|
||
│ │
|
||
│ 共享于 HTTP MCP 端点和 Mock API handler │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 参考资源
|
||
|
||
- [rmcp GitHub Repository](https://github.com/anthropics/rmcp)
|
||
- [MCP Specification](https://spec.modelcontextprotocol.io/)
|
||
- [schemars Documentation](https://docs.rs/schemars/)
|