Rust MCP:构建智能上下文协议的未来桥梁
本文介绍了Rust MCP项目,这是一个基于Model Context Protocol(MCP)协议的Rust实现,为AI模型提供结构化上下文交互能力。文章详细解析了MCP三大核心组件:Tools(提供外部执行能力)、Prompt(控制模型行为)和Resources(数据资源访问),并阐述了它们协同工作的流程。通过计数器服务器案例,展示了如何使用Rust MCP框架构建工具服务器,重点介绍了#[
1. 引言
Model Context Protocol (MCP) 是一种新兴的协议标准,旨在为 AI 模型提供结构化的上下文交互能力。Rust MCP 项目则是这一协议在 Rust 语言中的实现,通过类型安全和并发优势,为开发者提供了一个强大而灵活的框架,用于构建能够与 AI 模型智能交互的应用程序。
本文将深入探讨 Rust MCP 项目的核心概念、实现细节以及实际应用案例,帮助读者全面了解这一技术栈的价值和潜力。
2. MCP 基础概念
2.1. Tools
定义:Tools 是 AI 可以调用的外部能力或函数,用于执行特定任务(如查询数据库、调用 API、执行代码、发送邮件等)。
MCP 中的作用:
- 提供"行动能力",让模型不仅能"说",还能"做"。
- 在 MCP 协议中,tools 通常以结构化方式注册(如 JSON Schema),供模型在推理时动态选择和调用。
- 支持工具链(tool chaining)和条件调用,实现复杂工作流。
2.2. Prompt
定义:Prompt 是引导模型行为的指令或上下文,包括系统提示(system prompt)、用户输入、对话历史等。
MCP 中的作用:
- 定义模型的角色、目标、约束和行为规范。
- 在 MCP 中,prompt 通常被结构化为"上下文模板",动态注入用户请求、工具描述、资源信息等。
- 控制模型何时调用工具、如何解释结果、如何回应用户。
2.2.1. SystemPrompt
作用:定义 AI 的角色、行为准则、能力边界、输出格式等全局指令。
特点:
- 通常由开发者或系统设定,对用户不可见(或部分可见)。
- 在整个对话会话中保持不变(除非主动更新)。
- 是模型"人格"和"规则"的来源。
核心价值:控制模型的"身份"和"行为规范",是安全、一致、可控对话的基础。
2.2.2. UserPrompt
作用:代表用户当前的输入或请求,是模型需要响应的直接任务。
特点:
- 由用户在每轮对话中提供。
- 内容多变,可以是问题、指令、陈述等。
- 是触发模型推理的"输入信号"。
核心价值:表达用户意图,驱动对话进展。
2.2.3. AssistantPrompt
作用:记录模型在上一轮(或多轮)中生成的回复内容,用于维持对话历史和上下文连贯性。
特点:
- 由模型生成,系统自动记录。
- 在多轮对话中作为历史上下文的一部分,帮助模型理解对话脉络。
- 通常不会由用户直接编辑。
核心价值:构建对话记忆,避免重复提问或逻辑断裂。
2.2.4. 示例
[
{"role": "system", "content": "你是一个电商客服助手..."},
{"role": "user", "content": "我的订单还没发货,能帮我查一下吗?"},
{"role": "assistant", "content": "请提供您的订单号,我将为您查询物流状态。"},
{"role": "user", "content": "订单号是 #123456。"}
]
2.3. Resources
定义:Resources 是 AI 在对话中可访问的静态或动态数据资产,如知识库、文档、用户配置、会话状态、上下文记忆等。
MCP 中的作用:
- 为模型提供"背景知识"或"个性化上下文"。
- 在 MCP 中,resources 通常通过检索增强(RAG)或上下文注入方式提供给模型。
- 可与 tools 结合(如工具查询数据库后更新资源缓存)。
2.4. 三者协同工作流程(MCP 典型交互)
- 用户输入 → 触发 MCP 服务。
- Prompt 引擎:加载系统提示 + 用户输入 + 历史对话。
- Resource 注入:检索相关知识/用户数据,注入上下文。
- Tool 注册:声明当前可用的工具列表(含功能描述)。
- 模型推理:
- 基于 prompt 理解任务;
- 利用 resources 获取背景信息;
- 决定是否调用 tool(如需要实时数据)。
- Tool 执行 → 返回结果 → 更新 resources(如缓存)。
- 生成最终回复 → 返回用户。
2.5. 总结
组件 | 在 MCP 中的作用 | 在对话中的角色 | 核心价值 |
---|---|---|---|
Tools | 提供可执行能力 | 执行器 | 让 AI 能"做事" |
Prompt | 控制行为逻辑 | 指令中枢 | 让 AI “知道怎么做” |
Resources | 提供上下文数据 | 记忆与知识库 | 让 AI “记得住、懂你” |
3. 案例分析
使用依赖:
rmcp = {version = "0.6.4", features = [
"server",
"macros",
"client",
"transport-sse-server",
"transport-sse-client-reqwest",
"transport-io",
"transport-streamable-http-server",
"transport-child-process",
"schemars"
]}
serde = {version="1.0", features = ["derive"]}
serde_json = "1.0.142"
schemars = "1.0"
想要了解schemars
请阅读:https://blog.csdn.net/qq_45515182/article/details/151934054
3.1. 计数器服务器案例
案例概述
计数器服务器是一个简单的 MCP 服务器实现,提供了基本的计数器功能,包括增加计数和获取当前计数值。这个案例展示了如何使用 Rust MCP 框架创建一个简单的工具服务器。
代码实现
use std::sync::Arc;
use std::sync::Mutex;
use rmcp::ErrorData as McpError;
use rmcp::ServerHandler;
use rmcp::ServiceExt;
use rmcp::handler::server::tool::ToolRouter;
use rmcp::model::CallToolResult;
use rmcp::model::Content;
use rmcp::model::Implementation;
use rmcp::model::ProtocolVersion;
use rmcp::model::ServerCapabilities;
use rmcp::model::ServerInfo;
use rmcp::tool;
use rmcp::tool_handler;
use rmcp::tool_router;
use rmcp::transport::stdio;
#[derive(Clone)]
struct Counter {
counter: Arc<Mutex<i32>>,
tool_router: ToolRouter<Counter>,
}
/**
* #[tool_router] 宏是 rmcp (Rust Model Context Protocol) 库中的一个过程宏,
* 用于在 MCP (Model Context Protocol) 服务器实现中自动生成工具路由逻辑。它的主要作用包括:
* 1、该宏会扫描 impl 块中所有使用 #[tool] 标记的方法,并为它们自动生成路由逻辑。
* 2、创建ToolRouter 实例。
* 3、将所有标记为 #[tool] 的方法注册到路由器中,使得当客户端请求特定的工具时,服务器能够正确地调用对应的处理函数。
*/
#[tool_router]
impl Counter {
pub fn new() -> Self {
Self {
counter: Arc::new(Mutex::new(0)),
tool_router: Self::tool_router(),
}
}
/**
* tool宏用于将方法标记为 MCP 工具。
*/
#[tool(description = "将计数器增加 1")]
async fn increment(&self) -> Result<CallToolResult, McpError> {
let mut count = self.counter.lock().unwrap();
*count += 1;
Ok(CallToolResult::success(vec![Content::text(format!(
"{}",
count
))]))
}
#[tool(description = "获取当前计数器值")]
async fn get_value(&self) -> Result<CallToolResult, McpError> {
let count = self.counter.lock().unwrap();
Ok(CallToolResult::success(vec![Content::text(format!(
"{}",
count
))]))
}
}
/**
* #[tool_handler] 宏是 rmcp (Rust Model Context Protocol) 库中的一个过程宏,
* 用于在 MCP (Model Context Protocol) 服务器实现中自动处理工具调用的逻辑,主要作用包括:
* 1、该宏为 Counter 结构体自动实现 ServerHandler trait,这是处理 MCP 服务器请求所需的核心 trait。
* 2、#[tool_handler] 宏与 #[tool_router] 宏协同工作。
* #[tool_router] 负责创建工具路由逻辑,而 #[tool_handler] 则负责将这些路由集成到服务器的请求处理流程中,
* 使得服务器能够正确地接收和分发工具调用请求。
* 3、当客户端发起工具调用请求时, #[tool_handler] 宏生成的代码会自动将请求路由到正确的处理函数(如 increment 或 get_value 方法),而不需要开发者手动编写请求分发逻辑。
* 4、使用这个宏可以大大简化 MCP 服务器的实现,开发者只需要专注于实现具体的工具逻辑和服务器基本信息(如 get_info 方法),而不需要手动处理底层协议细节。
*/
#[tool_handler]
impl ServerHandler for Counter {
fn get_info(&self) -> rmcp::model::ServerInfo {
ServerInfo {
protocol_version: ProtocolVersion::default(),
capabilities: ServerCapabilities::builder().enable_tools().build(),
server_info: Implementation::default(),
instructions: Some("此服务器提供一个计数器工具。计数器从 0 开始,可以增加。使用 'get_value' 检查当前计数。".to_string()),
}
}
}
#[tokio::main]
async fn main() {
let server = Counter::new().serve(stdio()).await.unwrap();
server.waiting().await.unwrap();
}
优点
- 简单直观:代码结构清晰,易于理解和实现。
- 状态管理:使用 Arc<Mutex> 安全地管理共享状态,适合多线程环境。
- 宏系统:利用
#[tool_router]
和#[tool_handler]
宏简化了路由和处理逻辑的编写。 - 类型安全:Rust 的类型系统确保了工具调用的安全性。
缺点
- 功能有限:仅提供基本的计数功能,缺乏复杂的业务逻辑。
- 持久化缺失:计数器值仅保存在内存中,服务器重启后数据会丢失。
- 扩展性不足:没有提供扩展机制,难以添加更多功能。
- 错误处理简单:错误处理较为基础,没有详细的错误分类和处理策略。
3.2. LLM 采样服务器案例
案例概述
LLM 采样服务器是一个更复杂的 MCP 服务器实现,它演示了如何通过 MCP 协议与大型语言模型(LLM)进行交互。这个服务器提供了一个 ask_LLM
工具,允许客户端向 LLM 提问并获取回答。
代码实现
use std::sync::Arc;
use rmcp::model::{
CallToolResult, Content, ContextInclusion, CreateMessageRequestParam, ErrorCode,
Implementation, ListToolsResult, ModelHint, ModelPreferences, ProtocolVersion, SamplingMessage,
ServerCapabilities, ServerInfo, Tool,
};
use rmcp::transport::stdio;
use rmcp::{ErrorData, ServerHandler, ServiceExt};
/// 一个简单的MCP(Model Context Protocol)服务器,演示了工具的使用
/// 和LLM采样功能。
#[derive(Clone, Debug, Default)]
struct SimpleServer;
impl ServerHandler for SimpleServer {
/// 返回服务器信息,包括协议版本、功能、实现详情和使用说明。
fn get_info(&self) -> ServerInfo {
ServerInfo{
protocol_version: ProtocolVersion::default(), // MCP协议版本
capabilities: ServerCapabilities::builder().enable_tools().build(), // 定义MCP服务器支持的功能和能力
server_info: Implementation::from_build_env(), // 服务器实现信息,包括名称、版本、作者等
// 提供服务器的使用说明或指导信息
instructions: Some(concat!(
"This is a demo server that requests sampling from clients. It provides tools that use LLM capabilities.\n\n",
"IMPORTANT: This server requires a client that supports the 'sampling/createMessage' method. ",
"Without sampling support, the tools will return errors."
).into()),
}
}
/// 处理来自客户端的工具调用。目前支持'ask_LLM'工具,
/// 该工具通过客户端采样向LLM发送提示。
async fn call_tool(
&self,
request: rmcp::model::CallToolRequestParam,
context: rmcp::service::RequestContext<rmcp::RoleServer>,
) -> Result<rmcp::model::CallToolResult, rmcp::ErrorData> {
match request.name.as_ref() {
"ask_LLM" => {
// 从请求参数中提取问题,如果没有提供则使用默认值
let prompt = request
.arguments
.as_ref()
.and_then(|args| args.get("question"))
.and_then(|q| q.as_str())
.unwrap_or("Hello, LLM!");
// 创建用于LLM采样的消息请求
let message_request = CreateMessageRequestParam {
messages: vec![SamplingMessage {
role: rmcp::model::Role::User, // 消息发送者的角色,这里表示用户消息
content: Content::text(prompt), // 消息内容,将提示文本转换为内容格式
}],
// 模型偏好设置,用于指导客户端选择合适的LLM模型
model_preferences: Some(ModelPreferences {
// 模型提示,指定偏好使用的模型
hints: Some(vec![ModelHint {
name: Some("claude".to_string()), // 指定使用Claude模型
}]),
cost_priority: Some(0.3), // 成本优先级,值越低表示越注重成本控制
speed_priority: Some(0.8), // 速度优先级,值越高表示越注重响应速度
intelligence_priority: Some(0.7), // 智能优先级,值越高表示越注重回答质量
}),
// 系统提示,用于设定LLM的角色和行为准则
system_prompt: Some("You are a helpful assistant.".to_string()),
// 是否包含上下文信息,None表示不包含之前的对话上下文
include_context: Some(ContextInclusion::None),
// 温度参数,控制LLM输出的随机性,0.7表示中等偏上的随机性
temperature: Some(0.7),
// 最大令牌数,限制LLM响应的长度,256个令牌约等于200-300个英文单词
max_tokens: 256,
// 停止序列,当LLM生成这些序列时停止生成,None表示不设置停止序列
stop_sequences: None,
// 元数据,可以包含任何额外的请求信息,None表示没有额外的元数据
metadata: None,
};
// 向客户端发送采样请求并处理响应
match context.peer.create_message(message_request).await {
Ok(response) => {
let text = format!(
"Question: {}\nAnswer:{}",
prompt,
response
.message
.content
.as_text()
.map(|t| &t.text)
.unwrap_or(&"No text response".to_string())
);
Ok(CallToolResult::success(vec![Content::text(text)]))
}
Err(e) => Err(ErrorData::new(
ErrorCode::INTERNAL_ERROR,
format!("Sampling request failed: {}", e),
None,
)),
}
}
// 对未知工具返回错误
_ => Err(ErrorData::new(
ErrorCode::INTERNAL_ERROR,
format!("Unknown tool: {}", request.name),
None,
)),
}
}
/// 返回此服务器提供的可用工具列表。
/// 目前只提供用于LLM交互的'ask_LLM'工具。
async fn list_tools(
&self,
_request: Option<rmcp::model::PaginatedRequestParam>,
_context: rmcp::service::RequestContext<rmcp::RoleServer>,
) -> Result<ListToolsResult, ErrorData> {
// 定义'ask_LLM'工具及其模式
let tool = Tool {
name: "ask_LLM".into(),
title: Some("Ask LLM".into()),
description: Some("Ask a question to the LLM through sampling".into()),
input_schema: Arc::new(
serde_json::from_value(serde_json::json!({
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "The question to ask the LLM"
}
},
"required": ["question"]
}))
.unwrap(),
),
output_schema: None,
annotations: None,
icons: None,
};
Ok(ListToolsResult {
tools: vec![tool],
next_cursor: None,
})
}
}
#[tokio::main]
async fn main() {
// 使用stdio传输创建并服务SimpleServer
let server = SimpleServer.serve(stdio()).await.unwrap();
// 等待服务器完成(这将阻塞直到服务器停止)
server.waiting().await.unwrap();
}
优点
- LLM 集成:展示了如何将 MCP 与大型语言模型集成,实现智能问答功能。
- 参数控制:提供了丰富的参数控制,如模型偏好、温度、最大令牌数等,可以精细调整 LLM 的行为。
- 错误处理:实现了较为完善的错误处理机制,能够优雅地处理各种异常情况。
- 工具描述:通过 JSON Schema 详细描述了工具的输入参数,提高了 API 的可发现性和可用性。
缺点
- 依赖客户端:服务器本身不直接与 LLM 交互,而是依赖客户端提供采样功能,增加了系统复杂性。
- 模型固定:代码中硬编码了使用 Claude 模型,缺乏灵活性。
- 上下文管理简单:没有实现复杂的上下文管理机制,难以维持多轮对话的连贯性。
- 安全性考虑不足:没有对输入进行验证和过滤,可能存在安全风险。
3.3. 提示模板服务器案例
案例概述
提示模板服务器是一个高级的 MCP 服务器实现,它展示了如何使用 MCP 的提示(prompt)功能创建结构化的对话模板。这个服务器提供了多种提示模板,包括代码审查、数据分析、写作助手、调试助手和学习路径等。
代码实现
use rmcp::{
ErrorData as McpError, RoleServer, ServerHandler, ServiceExt,
handler::server::{router::prompt::PromptRouter, wrapper::Parameters},
model::*,
prompt, prompt_handler, prompt_router,
schemars::JsonSchema,
service::RequestContext,
transport::stdio,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "Code review parameters")]
pub struct CodeReviewArgs {
#[schemars(description = "Programming language of the code")]
pub language: String,
#[schemars(description = "Path to the file or code snippet")]
pub file_path: String,
#[schemars(description = "Focus areas for the review")]
pub focus_areas: Option<Vec<String>>,
}
/// Arguments for the data analysis prompt
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "Data analysis parameters")]
pub struct DataAnalysisArgs {
#[schemars(description = "Type of data: 'csv', 'json', 'logs', etc.")]
pub data_type: String,
#[schemars(description = "What kind of analysis to perform")]
pub analysis_type: String,
#[schemars(description = "Additional context about the data")]
pub context: Option<String>,
}
/// Arguments for the writing assistant prompt
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "Writing assistant parameters")]
pub struct WritingAssistantArgs {
#[schemars(description = "Type of content: 'email', 'documentation', 'blog', etc.")]
pub content_type: String,
#[schemars(description = "Target audience")]
pub audience: String,
#[schemars(description = "Writing tone: 'formal', 'casual', 'technical', etc.")]
pub tone: Option<String>,
#[schemars(description = "Key points to cover")]
pub key_points: Vec<String>,
}
/// Arguments for the debug assistant prompt
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "Debug assistant parameters")]
pub struct DebugAssistantArgs {
#[schemars(description = "Error message or symptom")]
pub error_message: String,
#[schemars(description = "Technology stack involved")]
pub stack: Vec<String>,
#[schemars(description = "Steps already tried")]
pub tried_solutions: Option<Vec<String>>,
}
/// Simple prompt server demonstrating various prompt patterns
#[derive(Clone)]
pub struct PromptServer {
/// Stores user preferences that can be used in prompts
user_preferences: Arc<RwLock<UserPreferences>>,
prompt_router: PromptRouter<PromptServer>,
}
#[derive(Debug, Clone)]
struct UserPreferences {
preferred_language: String,
expertise_level: String,
}
impl Default for UserPreferences {
fn default() -> Self {
Self {
preferred_language: "English".to_string(),
expertise_level: "intermediate".to_string(),
}
}
}
impl PromptServer {
pub fn new() -> Self {
Self {
user_preferences: Arc::new(RwLock::new(UserPreferences::default())),
prompt_router: Self::prompt_router(),
}
}
}
impl Default for PromptServer {
fn default() -> Self {
Self::new()
}
}
/**
* 1、自动生成路由逻辑 :该宏会扫描 impl 块中所有使用 #[prompt] 标记的方法,并为它们自动生成路由逻辑。
* 2、创建 PromptRouter 实例 :从代码中可以看到, PromptServer 结构体中有一个 prompt_router: PromptRouter<PromptServer> 字段,这个宏负责创建和配置这个路由器。
* 3、注册提示处理函数 :将所有标记为 #[prompt] 的方法注册到路由器中,使得当客户端请求特定的提示时,服务器能够正确地调用对应的处理函数。
*/
#[prompt_router]
impl PromptServer {
/**
* 1、定义提示模板 :该宏将一个普通的方法转换为一个可调用的提示模板,每个提示都有一个名称和描述。
* 2、参数处理 :宏会处理方法的参数,将它们转换为提示模板可以接受的格式。例如, Parameters(args): Parameters<CodeReviewArgs> 这种参数形式就是由宏处理的,它会自动将传入的 JSON 数据反序列化为 CodeReviewArgs 结构体。
* 3、返回值处理 :宏会处理方法的返回值,将其转换为 MCP 服务器可以发送的格式。例如, Vec<PromptMessage> 类型的返回值会被转换为一个包含多个 PromptMessage 的 JSON 数组。
*/
#[prompt(
name = "greeting",
description = "A simple greeting prompt to start conversations"
)]
async fn greeting(&self) -> Vec<PromptMessage> {
vec![
PromptMessage::new_text(
PromptMessageRole::User,
"Hello! I'd like to start our conversation.",
),
PromptMessage::new_text(
PromptMessageRole::Assistant,
"Hello! I'm here to help. What would you like to discuss today?",
),
]
}
/// Code review prompt with typed parameters
#[prompt(
name = "code_review",
description = "Structured code review with language-specific best practices"
)]
async fn code_review(
&self,
Parameters(args): Parameters<CodeReviewArgs>,
) -> Result<GetPromptResult, McpError> {
let prefs = self.user_preferences.read().await;
let focus_areas = args
.focus_areas
.unwrap_or_else(|| vec!["correctness".to_string(), "performance".to_string()]);
let messages = vec![
PromptMessage::new_text(
PromptMessageRole::Assistant,
format!(
"You are an expert {} code reviewer. The user's expertise level is {}.",
args.language, prefs.expertise_level
),
),
PromptMessage::new_text(
PromptMessageRole::User,
format!(
"Please review the {} code at '{}'. Focus on: {}",
args.language,
args.file_path,
focus_areas.join(", ")
),
),
PromptMessage::new_text(
PromptMessageRole::Assistant,
format!(
"I'll review your {} code focusing on {}. Let me analyze the code at '{}'...",
args.language,
focus_areas.join(" and "),
args.file_path
),
),
];
Ok(GetPromptResult {
description: Some(format!(
"Code review for {} file focusing on {}",
args.language,
focus_areas.join(", ")
)),
messages,
})
}
/// Data analysis prompt demonstrating context usage
#[prompt(
name = "data_analysis",
description = "Analyze data with context-aware suggestions"
)]
async fn data_analysis(
&self,
Parameters(args): Parameters<DataAnalysisArgs>,
ctx: RequestContext<RoleServer>,
) -> Result<Vec<PromptMessage>, McpError> {
// Could use ctx to check for capabilities or metadata
let _request_id = &ctx.id;
let context = args
.context
.unwrap_or_else(|| "General analysis requested".to_string());
Ok(vec![
PromptMessage::new_text(
PromptMessageRole::User,
format!(
"I have {} data that needs {} analysis. Context: {}",
args.data_type, args.analysis_type, context
),
),
PromptMessage::new_text(
PromptMessageRole::Assistant,
format!(
"I'll help you analyze your {} data using {} techniques. Based on your context, \
I'll focus on providing actionable insights.",
args.data_type, args.analysis_type
),
),
])
}
/// Writing assistant with multiple conversation turns
#[prompt(
name = "writing_assistant",
description = "Multi-turn writing assistance with style guidance"
)]
async fn writing_assistant(
&self,
Parameters(args): Parameters<WritingAssistantArgs>,
) -> GetPromptResult {
let tone = args.tone.unwrap_or_else(|| "professional".to_string());
let mut messages = vec![
PromptMessage::new_text(
PromptMessageRole::Assistant,
format!(
"You are a writing assistant helping create {} content for {}. \
Use a {} tone.",
args.content_type, args.audience, tone
),
),
PromptMessage::new_text(
PromptMessageRole::User,
format!(
"I need help writing {} for {}. Key points to cover: {}",
args.content_type,
args.audience,
args.key_points.join(", ")
),
),
PromptMessage::new_text(
PromptMessageRole::Assistant,
"I'll help you create that content. Let me structure it based on your key points.",
),
];
// Add a message for each key point
for (i, point) in args.key_points.iter().enumerate() {
messages.push(PromptMessage::new_text(
PromptMessageRole::User,
format!("For point {}: {}, what would you suggest?", i + 1, point),
));
messages.push(PromptMessage::new_text(
PromptMessageRole::Assistant,
format!("For '{}', I recommend...", point),
));
}
GetPromptResult {
description: Some(format!(
"Writing {} for {} audience with {} tone",
args.content_type, args.audience, tone
)),
messages,
}
}
/// Debug assistant demonstrating error handling patterns
#[prompt(
name = "debug_assistant",
description = "Interactive debugging help with solution tracking"
)]
async fn debug_assistant(
&self,
Parameters(args): Parameters<DebugAssistantArgs>,
) -> Result<GetPromptResult, McpError> {
if args.stack.is_empty() {
return Err(McpError::invalid_params(
"Technology stack cannot be empty",
None,
));
}
let mut messages = vec![
PromptMessage::new_text(
PromptMessageRole::Assistant,
format!(
"You are a debugging expert for {}. Help diagnose and fix issues.",
args.stack.join(", ")
),
),
PromptMessage::new_text(
PromptMessageRole::User,
format!(
"I'm encountering this error: {}\nStack: {}",
args.error_message,
args.stack.join(", ")
),
),
];
// Add tried solutions if any
if let Some(tried) = args.tried_solutions {
if !tried.is_empty() {
messages.push(PromptMessage::new_text(
PromptMessageRole::User,
format!("I've already tried: {}", tried.join(", ")),
));
messages.push(PromptMessage::new_text(
PromptMessageRole::Assistant,
"I see you've already attempted some solutions. Let me suggest different approaches.",
));
}
}
messages.push(PromptMessage::new_text(
PromptMessageRole::Assistant,
"Let's debug this systematically. First, let me understand the error context better.",
));
Ok(GetPromptResult {
description: Some(format!(
"Debugging {} error in {}",
args.error_message.chars().take(50).collect::<String>(),
args.stack.first().map(|s| s.as_str()).unwrap_or("unknown")
)),
messages,
})
}
/// Learning path prompt that uses server state
#[prompt(
name = "learning_path",
description = "Generate a personalized learning path based on user preferences"
)]
async fn learning_path(&self) -> Result<Vec<PromptMessage>, McpError> {
let prefs = self.user_preferences.read().await;
Ok(vec![
PromptMessage::new_text(
PromptMessageRole::Assistant,
format!(
"Create a learning path for someone at {} level who prefers {} language explanations.",
prefs.expertise_level, prefs.preferred_language
),
),
PromptMessage::new_text(
PromptMessageRole::User,
"What should I learn next to improve my programming skills?",
),
PromptMessage::new_text(
PromptMessageRole::Assistant,
format!(
"Based on your {} expertise level, I recommend the following learning path...",
prefs.expertise_level
),
),
])
}
}
/**
* 1、实现服务器处理逻辑 :该宏为 PromptServer 实现 ServerHandler trait,这是处理 MCP (Model Context Protocol) 服务器请求所需的核心 trait。
*/
#[prompt_handler]
impl ServerHandler for PromptServer {
fn get_info(&self) -> ServerInfo {
ServerInfo {
capabilities: ServerCapabilities::builder().enable_prompts().build(),
server_info: Implementation::from_build_env(),
instructions: Some(
"This server provides various prompt templates for code review, data analysis, \
writing assistance, debugging help, and personalized learning paths. \
All prompts are designed to provide structured, context-aware assistance."
.to_string(),
),
..Default::default()
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("MCP Prompt Server Example");
println!("=======================");
println!();
println!("This server demonstrates various prompt patterns:");
println!("- Simple prompts without parameters");
println!("- Prompts with typed parameters");
println!("- Prompts using server state");
println!("- Multi-turn conversation prompts");
println!("- Error handling in prompts");
println!();
println!("To test with MCP Inspector:");
println!(
"npx @modelcontextprotocol/inspector cargo run -p mcp-server-examples --example servers_prompt_stdio"
);
println!();
let server = PromptServer::new();
let service = server.serve(stdio()).await?;
service.waiting().await?;
Ok(())
}
优点
- 丰富的提示模板:提供了多种实用的提示模板,覆盖了代码审查、数据分析、写作助手、调试助手和学习路径等多个场景。
- 类型安全参数:使用结构体和 JsonSchema 宏定义参数,确保了类型安全和良好的文档化。
- 状态管理:通过 UserPreferences 结构体实现了用户偏好状态的管理,可以提供个性化的服务。
- 错误处理:实现了参数验证和错误处理,提高了系统的健壮性。
- 多轮对话支持:某些提示模板(如写作助手)支持多轮对话,提供了更自然的交互体验。
缺点
- 复杂性高:代码结构较为复杂,对于初学者来说理解和维护难度较大。
- 资源管理:用户偏好存储在内存中,没有持久化机制,重启后数据会丢失。
- 扩展性限制:添加新的提示模板需要修改服务器代码,缺乏动态扩展能力。
- 性能考虑:对于大量并发请求,当前的 RwLock 机制可能成为性能瓶颈。
3.4. SSE 邮件服务器案例
案例概述
SSE 邮件服务器是一个使用 Server-Sent Events (SSE) 作为传输层的 MCP 服务器实现,提供了发送邮件和获取联系人列表的功能。这个案例展示了如何在 Web 环境中使用 MCP 协议,以及如何处理更复杂的业务逻辑。
代码实现
use rmcp::model::{Implementation, ProtocolVersion, ServerCapabilities, ServerInfo};
use rmcp::{model::{CallToolResult, Content}, tool_handler, tool_router, transport::{SseServer}, ErrorData, ServerHandler};
use rmcp::tool;
use rmcp::handler::server::tool::ToolRouter;
use serde::{Deserialize, Serialize};
use rmcp::schemars::JsonSchema;
use rmcp::handler::server::wrapper::Parameters;
/// 发送邮件的参数
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "发送邮件的参数")]
pub struct SendEmailArgs {
#[schemars(description = "收件人邮箱地址")]
pub to: String,
#[schemars(description = "邮件主题")]
pub subject: String,
#[schemars(description = "邮件内容")]
pub body: String,
}
/// 获取联系人列表的参数
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "获取联系人列表的参数")]
pub struct ContactListArgs {
#[schemars(description = "联系人分组名称,可选")]
pub group: Option<String>,
#[schemars(description = "返回的联系人数量限制,默认为10")]
pub limit: Option<i32>,
}
/// 获取工具列表的参数(空参数)
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[schemars(description = "获取工具列表的参数")]
pub struct ListToolArgs {
// 空结构体,不需要任何字段
}
#[tokio::main]
async fn main() {
// 创建一个 SSE 服务器,绑定到指定的地址和端口,然后为其注册一个服务处理程序。SSE 是一种允许服务器向客户端推送事件的技术,在这里用于实现 MCP 协议的通信。
let ct = SseServer::serve("127.0.0.1:8080".parse().unwrap()).await.unwrap().with_service(move || {
EmailServer::new()
});
tokio::signal::ctrl_c().await.unwrap();
ct.cancel();
}
struct EmailServer {
// tool_router 字段是必须的,不然 tool_handler 会报错
tool_router: ToolRouter<EmailServer>,
}
impl EmailServer {
pub fn new() -> Self {
Self {
tool_router: Self::tool_router()
}
}
}
#[tool_router]
impl EmailServer {
/**
* 在 MCP 框架中,所有使用 #[tool] 宏标记的方法都需要有参数结构体,即使这些参数是空的。这是因为:
* 1、协议规范要求 :MCP 协议要求每个工具都必须有一个输入模式(inputSchema),且类型必须是 "object"。
* 2、框架设计 : #[tool] 宏会为每个工具方法生成相应的 schema,如果方法没有参数,就无法生成符合协议要求的 schema。
*/
#[tool(description = "发送邮件")]
async fn send_email(&self, Parameters(args): Parameters<SendEmailArgs>) -> Result<CallToolResult, ErrorData> {
Ok(CallToolResult::success(vec![Content::text(format!(
"邮件已发送至: {}, 主题: {}, 内容: {}",
args.to, args.subject, args.body
))]))
}
#[tool(description = "获取联系人列表")]
async fn contact_list(&self, Parameters(args): Parameters<ContactListArgs>) -> Result<CallToolResult, ErrorData> {
let limit = args.limit.unwrap_or(10);
let group_info = args.group.map_or("所有联系人".to_string(), |g| format!("{}分组", g));
let mut list = Vec::new();
for i in 0..limit {
let contact = Content::text(format!("{}{}", group_info, i));
list.push(contact);
}
Ok(CallToolResult::success(list))
}
#[tool(description = "获取所有工具列表")]
async fn list_tool(&self, Parameters(_args): Parameters<ListToolArgs>) -> Result<CallToolResult, ErrorData> {
Ok(CallToolResult::success(vec![
Content::text("send_email".to_string()),
Content::text("contact_list".to_string()),
]))
}
}
#[tool_handler]
impl ServerHandler for EmailServer {
fn get_info(&self) -> ServerInfo {
ServerInfo {
protocol_version: ProtocolVersion::default(),
capabilities: ServerCapabilities::builder().enable_tools().build(),
server_info: Implementation::default(),
instructions: Some("此服务器提供发送邮件和获取联系人列表的工具。".to_string()),
}
}
}
优点
- Web 友好:使用 SSE 作为传输层,适合在 Web 环境中部署和使用,便于与前端应用集成。
- 实时通信:SSE 支持服务器向客户端推送事件,可以实现实时通信和状态更新。
- 业务逻辑完整:提供了完整的邮件发送和联系人管理功能,展示了如何处理实际业务场景。
- 参数验证:通过结构体和 JsonSchema 宏实现了参数验证,确保了输入数据的有效性。
缺点
- 模拟实现:邮件发送和联系人管理功能只是模拟实现,没有与实际的邮件服务集成。
- 错误处理简单:错误处理较为基础,没有考虑网络故障、认证失败等复杂情况。
- 安全性考虑不足:没有实现认证和授权机制,存在安全风险。
- 扩展性限制:当前实现难以扩展到大规模邮件系统,缺乏队列、重试等机制。
4. 技术实现细节
4.1. 客户端实现
Rust MCP 的客户端实现主要围绕 ClientHandler
trait 和各种传输层展开。客户端负责与 MCP 服务器建立连接,发送请求并处理响应。
核心组件
- ClientHandler trait:定义了客户端处理服务器响应的基本接口,包括
create_message
方法用于处理 LLM 采样请求。
impl ClientHandler for SimpleClient {
async fn create_message(
&self,
params: rmcp::model::CreateMessageRequestParam,
_context: rmcp::service::RequestContext<rmcp::RoleClient>,
) -> Result<CreateMessageResult, ErrorData> {
let response_txt =
self.mock_llm_response(¶ms.messages, params.system_prompt.as_deref());
let result = CreateMessageResult {
message: SamplingMessage {
role: Role::Assistant,
content: Content::text(response_txt),
},
model: "mock_llm".to_string(),
stop_reason: Some(CreateMessageResult::STOP_REASON_END_TURN.to_string()),
};
Ok(result)
}
}
- 传输层:Rust MCP 支持多种传输层,包括 stdio、SSE、子进程等。客户端可以根据需要选择合适的传输方式。
let client = client
.serve(TokioChildProcess::new(Command::new("cargo").configure(
|cmd| {
cmd.arg("run")
.arg("--example")
.arg("servers_sampling_stdio")
.current_dir(servers_dir);
},
))?)
.await?;
- 工具调用:客户端可以通过
call_tool
方法调用服务器提供的工具,并处理返回结果。
match client
.call_tool(CallToolRequestParam {
name: "ask_LLM".into(),
arguments: Some(object!({
"question": "Hello world"
})),
})
.await
{
Ok(result) => println!("Ask LLM result: {:?}", result),
Err(e) => println!("Ask LLM error: {e}"),
}
客户端示例分析
以 client_sampling_stdio.rs
为例,它展示了一个完整的客户端实现:
- 定义客户端结构体:
SimpleClient
结构体实现了ClientHandler
trait。 - 实现 LLM 响应模拟:
mock_llm_response
方法模拟了 LLM 的响应,实际应用中可以替换为真实的 LLM 调用。 - 建立连接:使用
TokioChildProcess
传输层连接到服务器。 - 调用工具:列出可用工具并调用
ask_LLM
工具。
4.2. 服务端实现
Rust MCP 的服务端实现主要围绕 ServerHandler
trait 和各种宏系统展开。服务端负责定义工具、处理请求并返回响应。
核心组件
- ServerHandler trait:定义了服务器处理客户端请求的基本接口,包括
get_info
、call_tool
、list_tools
等方法。
impl ServerHandler for Counter {
fn get_info(&self) -> rmcp::model::ServerInfo {
ServerInfo {
protocol_version: ProtocolVersion::default(),
capabilities: ServerCapabilities::builder().enable_tools().build(),
server_info: Implementation::default(),
instructions: Some("此服务器提供一个计数器工具。计数器从 0 开始,可以增加。使用 'get_value' 检查当前计数。".to_string()),
}
}
}
- 工具定义:使用
#[tool]
宏定义工具,包括工具名称、描述和参数。
#[tool(description = "将计数器增加 1")]
async fn increment(&self) -> Result<CallToolResult, McpError> {
let mut count = self.counter.lock().unwrap();
*count += 1;
Ok(CallToolResult::success(vec![Content::text(format!(
"{}",
count
))]))
}
- 工具路由:使用
#[tool_router]
宏自动生成工具路由逻辑,将客户端请求分发到对应的处理函数。
#[tool_router]
impl Counter {
// 工具方法定义
}
- 工具处理:使用
#[tool_handler]
宏自动实现ServerHandler
trait,处理工具调用请求。
#[tool_handler]
impl ServerHandler for Counter {
// ServerHandler trait 实现
}
服务端示例分析
以 count_server_stdio.rs
为例,它展示了一个完整的服务端实现:
- 定义服务器结构体:
Counter
结构体包含计数器状态和工具路由器。 - 实现工具方法:使用
#[tool]
宏定义increment
和get_value
工具。 - 生成路由逻辑:使用
#[tool_router]
宏自动生成工具路由逻辑。 - 实现服务器处理:使用
#[tool_handler]
宏自动实现ServerHandler
trait。 - 启动服务器:使用 stdio 传输层启动服务器并等待请求。
4.3. 宏系统
Rust MCP 的宏系统是其核心特性之一,它大大简化了 MCP 服务器的实现。主要的宏包括 #[tool]
、#[tool_router]
、#[tool_handler]
、#[prompt]
、#[prompt_router]
和 #[prompt_handler]
。
工具宏
#[tool]
:将方法标记为 MCP 工具,定义工具的名称、描述和参数。
#[tool(description = "将计数器增加 1")]
async fn increment(&self) -> Result<CallToolResult, McpError> {
// 工具实现
}
#[tool_router]
:自动生成工具路由逻辑,将客户端请求分发到对应的处理函数。
#[tool_router]
impl Counter {
// 工具方法定义
}
#[tool_handler]
:自动实现ServerHandler
trait,处理工具调用请求。
#[tool_handler]
impl ServerHandler for Counter {
// ServerHandler trait 实现
}
提示宏
#[prompt]
:将方法标记为 MCP 提示,定义提示的名称、描述和参数。
#[prompt(
name = "code_review",
description = "Structured code review with language-specific best practices"
)]
async fn code_review(
&self,
Parameters(args): Parameters<CodeReviewArgs>,
) -> Result<GetPromptResult, McpError> {
// 提示实现
}
#[prompt_router]
:自动生成提示路由逻辑,将客户端请求分发到对应的处理函数。
#[prompt_router]
impl PromptServer {
// 提示方法定义
}
#[prompt_handler]
:自动实现ServerHandler
trait,处理提示调用请求。
#[prompt_handler]
impl ServerHandler for PromptServer {
// ServerHandler trait 实现
}
宏系统的工作原理
Rust MCP 的宏系统在编译时展开,生成必要的代码来处理 MCP 协议的细节。以 #[tool_router]
为例,它会:
- 扫描 impl 块中所有使用
#[tool]
标记的方法。 - 为每个工具方法生成路由逻辑。
- 创建
ToolRouter
实例,并将所有工具注册到路由器中。 - 实现请求分发逻辑,将客户端请求路由到正确的处理函数。
这种设计大大简化了 MCP 服务器的实现,开发者只需要专注于工具或提示的业务逻辑,而不需要手动处理底层协议细节。
5. 优缺点分析
5.1. 优点
-
类型安全:Rust 的类型系统确保了 MCP 实现的类型安全,减少了运行时错误的可能性。通过结构体和 JsonSchema 宏定义参数,可以在编译时捕获许多错误。
-
高性能:Rust 的零成本抽象和高效并发模型使得 MCP 实现具有很高的性能,特别适合处理大量并发请求。
-
宏系统:Rust MCP 的宏系统大大简化了服务器的实现,开发者只需要专注于业务逻辑,而不需要手动处理底层协议细节。
-
多传输层支持:支持多种传输层,包括 stdio、SSE、子进程等,可以根据应用场景选择最合适的传输方式。
-
异步支持:基于 Tokio 异步运行时,支持高并发和异步 I/O,适合构建高性能的网络服务。
-
模块化设计:客户端和服务端的实现分离,工具和提示的处理逻辑模块化,便于维护和扩展。
-
丰富的示例:提供了多种示例代码,覆盖了不同的使用场景,便于开发者学习和参考。
-
文档完善:代码中的注释详细,解释了各个组件的作用和使用方法,降低了学习门槛。
5.2. 缺点
-
学习曲线陡峭:Rust 语言本身的学习曲线较陡,加上 MCP 协议的复杂性,对于初学者来说入门难度较大。
-
生态系统不成熟:Rust MCP 相对较新,生态系统还不够成熟,第三方库和工具支持有限。
-
错误处理复杂:Rust 的错误处理机制虽然强大,但在 MCP 实现中可能导致代码冗长和复杂。
-
编译时间长:Rust 的编译时间相对较长,特别是在使用大量宏的情况下,可能影响开发效率。
-
调试困难:宏系统生成的代码在调试时可能难以理解,增加了调试的难度。
-
资源消耗:Rust 程序的内存占用相对较高,对于资源受限的环境可能不是最佳选择。
-
文档不足:虽然有代码注释,但缺乏系统性的文档和教程,特别是高级特性的使用说明。
-
兼容性问题:与其他语言实现的 MCP 服务器可能存在兼容性问题,特别是在处理复杂的数据结构时。
6. 应用场景
Rust MCP 的应用场景广泛,特别适合以下情况:
-
AI 助手开发:构建能够与大型语言模型交互的 AI 助手,提供代码审查、文档生成、问题解答等功能。
-
企业级应用:开发需要高性能和高可靠性的企业级应用,如客户服务系统、数据分析平台等。
-
实时通信系统:构建需要实时通信能力的系统,如聊天机器人、实时监控系统等。
-
微服务架构:在微服务架构中作为服务间的通信协议,提供结构化的上下文交互能力。
-
DevOps 工具:开发自动化运维工具,如日志分析、错误诊断、性能监控等。
-
教育平台:构建智能教育平台,提供个性化的学习路径和实时的学习辅导。
-
内容创作:开发辅助内容创作的工具,如写作助手、翻译工具、内容摘要等。
-
游戏开发:在游戏开发中实现智能 NPC 和对话系统,提供更丰富的游戏体验。
7. 总结与展望
Rust MCP 是一个强大而灵活的框架,它将 Rust 的性能优势与 MCP 协议的上下文交互能力相结合,为开发者提供了一个构建智能应用的理想平台。通过本文的分析,我们可以看到 Rust MCP 在类型安全、高性能、宏系统等方面的优势,以及在学习曲线、生态系统成熟度等方面的挑战。
总结
-
技术优势:Rust MCP 充分利用了 Rust 语言的类型安全、高性能和并发特性,为 MCP 协议的实现提供了坚实的基础。
-
设计理念:通过宏系统简化了 MCP 服务器的实现,使开发者能够专注于业务逻辑,而不需要处理底层协议细节。
-
功能丰富:支持工具、提示和资源等 MCP 核心组件,提供了完整的上下文交互能力。
-
应用广泛:适用于 AI 助手、企业应用、实时通信等多种场景,具有广阔的应用前景。
展望
-
生态系统发展:随着 Rust MCP 的普及,预计会有更多的第三方库和工具出现,丰富生态系统。
-
性能优化:未来可能会有更多的性能优化,特别是在高并发和低延迟场景下的表现。
-
易用性提升:可能会提供更高级的抽象和更友好的 API,降低学习门槛,吸引更多开发者。
-
标准化进程:随着 MCP 协议的标准化进程,Rust MCP 可能会成为 Rust 生态中实现 MCP 协议的标准库。
-
跨语言互操作:可能会增强与其他语言实现的 MCP 服务器的互操作性,促进不同语言生态系统间的协作。
-
集成更多 AI 能力:随着 AI 技术的发展,Rust MCP 可能会集成更多先进的 AI 能力,如多模态交互、强化学习等。
总之,Rust MCP 是一个充满潜力的项目,它不仅展示了 Rust 在构建高性能网络服务方面的优势,也为 AI 与应用的集成提供了新的思路。随着技术的不断发展和生态系统的成熟,Rust MCP 有望成为构建智能应用的重要工具。
更多推荐
所有评论(0)