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. 示例
json 复制代码
[
  {"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 典型交互)

  1. 用户输入 → 触发 MCP 服务。
  2. Prompt 引擎:加载系统提示 + 用户输入 + 历史对话。
  3. Resource 注入:检索相关知识/用户数据,注入上下文。
  4. Tool 注册:声明当前可用的工具列表(含功能描述)。
  5. 模型推理:
    • 基于 prompt 理解任务;
    • 利用 resources 获取背景信息;
    • 决定是否调用 tool(如需要实时数据)。
    • Tool 执行 → 返回结果 → 更新 resources(如缓存)。
    • 生成最终回复 → 返回用户。

2.5. 总结

组件 在 MCP 中的作用 在对话中的角色 核心价值
Tools 提供可执行能力 执行器 让 AI 能"做事"
Prompt 控制行为逻辑 指令中枢 让 AI "知道怎么做"
Resources 提供上下文数据 记忆与知识库 让 AI "记得住、懂你"

3. 案例分析

使用依赖:

toml 复制代码
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 框架创建一个简单的工具服务器。

代码实现
rust 复制代码
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();
}
优点
  1. 简单直观:代码结构清晰,易于理解和实现。
  2. 状态管理:使用 Arc<Mutex> 安全地管理共享状态,适合多线程环境。
  3. 宏系统 :利用 #[tool_router]#[tool_handler] 宏简化了路由和处理逻辑的编写。
  4. 类型安全:Rust 的类型系统确保了工具调用的安全性。
缺点
  1. 功能有限:仅提供基本的计数功能,缺乏复杂的业务逻辑。
  2. 持久化缺失:计数器值仅保存在内存中,服务器重启后数据会丢失。
  3. 扩展性不足:没有提供扩展机制,难以添加更多功能。
  4. 错误处理简单:错误处理较为基础,没有详细的错误分类和处理策略。

3.2. LLM 采样服务器案例

案例概述

LLM 采样服务器是一个更复杂的 MCP 服务器实现,它演示了如何通过 MCP 协议与大型语言模型(LLM)进行交互。这个服务器提供了一个 ask_LLM 工具,允许客户端向 LLM 提问并获取回答。

代码实现
rust 复制代码
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();
}
优点
  1. LLM 集成:展示了如何将 MCP 与大型语言模型集成,实现智能问答功能。
  2. 参数控制:提供了丰富的参数控制,如模型偏好、温度、最大令牌数等,可以精细调整 LLM 的行为。
  3. 错误处理:实现了较为完善的错误处理机制,能够优雅地处理各种异常情况。
  4. 工具描述:通过 JSON Schema 详细描述了工具的输入参数,提高了 API 的可发现性和可用性。
缺点
  1. 依赖客户端:服务器本身不直接与 LLM 交互,而是依赖客户端提供采样功能,增加了系统复杂性。
  2. 模型固定:代码中硬编码了使用 Claude 模型,缺乏灵活性。
  3. 上下文管理简单:没有实现复杂的上下文管理机制,难以维持多轮对话的连贯性。
  4. 安全性考虑不足:没有对输入进行验证和过滤,可能存在安全风险。

3.3. 提示模板服务器案例

案例概述

提示模板服务器是一个高级的 MCP 服务器实现,它展示了如何使用 MCP 的提示(prompt)功能创建结构化的对话模板。这个服务器提供了多种提示模板,包括代码审查、数据分析、写作助手、调试助手和学习路径等。

代码实现
rust 复制代码
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(())
}
优点
  1. 丰富的提示模板:提供了多种实用的提示模板,覆盖了代码审查、数据分析、写作助手、调试助手和学习路径等多个场景。
  2. 类型安全参数:使用结构体和 JsonSchema 宏定义参数,确保了类型安全和良好的文档化。
  3. 状态管理:通过 UserPreferences 结构体实现了用户偏好状态的管理,可以提供个性化的服务。
  4. 错误处理:实现了参数验证和错误处理,提高了系统的健壮性。
  5. 多轮对话支持:某些提示模板(如写作助手)支持多轮对话,提供了更自然的交互体验。
缺点
  1. 复杂性高:代码结构较为复杂,对于初学者来说理解和维护难度较大。
  2. 资源管理:用户偏好存储在内存中,没有持久化机制,重启后数据会丢失。
  3. 扩展性限制:添加新的提示模板需要修改服务器代码,缺乏动态扩展能力。
  4. 性能考虑:对于大量并发请求,当前的 RwLock 机制可能成为性能瓶颈。

3.4. SSE 邮件服务器案例

案例概述

SSE 邮件服务器是一个使用 Server-Sent Events (SSE) 作为传输层的 MCP 服务器实现,提供了发送邮件和获取联系人列表的功能。这个案例展示了如何在 Web 环境中使用 MCP 协议,以及如何处理更复杂的业务逻辑。

代码实现
rust 复制代码
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()),
        }
    }
}
优点
  1. Web 友好:使用 SSE 作为传输层,适合在 Web 环境中部署和使用,便于与前端应用集成。
  2. 实时通信:SSE 支持服务器向客户端推送事件,可以实现实时通信和状态更新。
  3. 业务逻辑完整:提供了完整的邮件发送和联系人管理功能,展示了如何处理实际业务场景。
  4. 参数验证:通过结构体和 JsonSchema 宏实现了参数验证,确保了输入数据的有效性。
缺点
  1. 模拟实现:邮件发送和联系人管理功能只是模拟实现,没有与实际的邮件服务集成。
  2. 错误处理简单:错误处理较为基础,没有考虑网络故障、认证失败等复杂情况。
  3. 安全性考虑不足:没有实现认证和授权机制,存在安全风险。
  4. 扩展性限制:当前实现难以扩展到大规模邮件系统,缺乏队列、重试等机制。

4. 技术实现细节

4.1. 客户端实现

Rust MCP 的客户端实现主要围绕 ClientHandler trait 和各种传输层展开。客户端负责与 MCP 服务器建立连接,发送请求并处理响应。

核心组件
  1. ClientHandler trait :定义了客户端处理服务器响应的基本接口,包括 create_message 方法用于处理 LLM 采样请求。
rust 复制代码
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(&params.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)
    }
}
  1. 传输层:Rust MCP 支持多种传输层,包括 stdio、SSE、子进程等。客户端可以根据需要选择合适的传输方式。
rust 复制代码
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?;
  1. 工具调用 :客户端可以通过 call_tool 方法调用服务器提供的工具,并处理返回结果。
rust 复制代码
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 为例,它展示了一个完整的客户端实现:

  1. 定义客户端结构体SimpleClient 结构体实现了 ClientHandler trait。
  2. 实现 LLM 响应模拟mock_llm_response 方法模拟了 LLM 的响应,实际应用中可以替换为真实的 LLM 调用。
  3. 建立连接 :使用 TokioChildProcess 传输层连接到服务器。
  4. 调用工具 :列出可用工具并调用 ask_LLM 工具。

4.2. 服务端实现

Rust MCP 的服务端实现主要围绕 ServerHandler trait 和各种宏系统展开。服务端负责定义工具、处理请求并返回响应。

核心组件
  1. ServerHandler trait :定义了服务器处理客户端请求的基本接口,包括 get_infocall_toollist_tools 等方法。
rust 复制代码
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()),
        }
    }
}
  1. 工具定义 :使用 #[tool] 宏定义工具,包括工具名称、描述和参数。
rust 复制代码
#[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
    ))]))
}
  1. 工具路由 :使用 #[tool_router] 宏自动生成工具路由逻辑,将客户端请求分发到对应的处理函数。
rust 复制代码
#[tool_router]
impl Counter {
    // 工具方法定义
}
  1. 工具处理 :使用 #[tool_handler] 宏自动实现 ServerHandler trait,处理工具调用请求。
rust 复制代码
#[tool_handler]
impl ServerHandler for Counter {
    // ServerHandler trait 实现
}
服务端示例分析

count_server_stdio.rs 为例,它展示了一个完整的服务端实现:

  1. 定义服务器结构体Counter 结构体包含计数器状态和工具路由器。
  2. 实现工具方法 :使用 #[tool] 宏定义 incrementget_value 工具。
  3. 生成路由逻辑 :使用 #[tool_router] 宏自动生成工具路由逻辑。
  4. 实现服务器处理 :使用 #[tool_handler] 宏自动实现 ServerHandler trait。
  5. 启动服务器:使用 stdio 传输层启动服务器并等待请求。

4.3. 宏系统

Rust MCP 的宏系统是其核心特性之一,它大大简化了 MCP 服务器的实现。主要的宏包括 #[tool]#[tool_router]#[tool_handler]#[prompt]#[prompt_router]#[prompt_handler]

工具宏
  1. #[tool]:将方法标记为 MCP 工具,定义工具的名称、描述和参数。
rust 复制代码
#[tool(description = "将计数器增加 1")]
async fn increment(&self) -> Result<CallToolResult, McpError> {
    // 工具实现
}
  1. #[tool_router]:自动生成工具路由逻辑,将客户端请求分发到对应的处理函数。
rust 复制代码
#[tool_router]
impl Counter {
    // 工具方法定义
}
  1. #[tool_handler] :自动实现 ServerHandler trait,处理工具调用请求。
rust 复制代码
#[tool_handler]
impl ServerHandler for Counter {
    // ServerHandler trait 实现
}
提示宏
  1. #[prompt]:将方法标记为 MCP 提示,定义提示的名称、描述和参数。
rust 复制代码
#[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> {
    // 提示实现
}
  1. #[prompt_router]:自动生成提示路由逻辑,将客户端请求分发到对应的处理函数。
rust 复制代码
#[prompt_router]
impl PromptServer {
    // 提示方法定义
}
  1. #[prompt_handler] :自动实现 ServerHandler trait,处理提示调用请求。
rust 复制代码
#[prompt_handler]
impl ServerHandler for PromptServer {
    // ServerHandler trait 实现
}
宏系统的工作原理

Rust MCP 的宏系统在编译时展开,生成必要的代码来处理 MCP 协议的细节。以 #[tool_router] 为例,它会:

  1. 扫描 impl 块中所有使用 #[tool] 标记的方法。
  2. 为每个工具方法生成路由逻辑。
  3. 创建 ToolRouter 实例,并将所有工具注册到路由器中。
  4. 实现请求分发逻辑,将客户端请求路由到正确的处理函数。

这种设计大大简化了 MCP 服务器的实现,开发者只需要专注于工具或提示的业务逻辑,而不需要手动处理底层协议细节。

5. 优缺点分析

5.1. 优点

  1. 类型安全:Rust 的类型系统确保了 MCP 实现的类型安全,减少了运行时错误的可能性。通过结构体和 JsonSchema 宏定义参数,可以在编译时捕获许多错误。

  2. 高性能:Rust 的零成本抽象和高效并发模型使得 MCP 实现具有很高的性能,特别适合处理大量并发请求。

  3. 宏系统:Rust MCP 的宏系统大大简化了服务器的实现,开发者只需要专注于业务逻辑,而不需要手动处理底层协议细节。

  4. 多传输层支持:支持多种传输层,包括 stdio、SSE、子进程等,可以根据应用场景选择最合适的传输方式。

  5. 异步支持:基于 Tokio 异步运行时,支持高并发和异步 I/O,适合构建高性能的网络服务。

  6. 模块化设计:客户端和服务端的实现分离,工具和提示的处理逻辑模块化,便于维护和扩展。

  7. 丰富的示例:提供了多种示例代码,覆盖了不同的使用场景,便于开发者学习和参考。

  8. 文档完善:代码中的注释详细,解释了各个组件的作用和使用方法,降低了学习门槛。

5.2. 缺点

  1. 学习曲线陡峭:Rust 语言本身的学习曲线较陡,加上 MCP 协议的复杂性,对于初学者来说入门难度较大。

  2. 生态系统不成熟:Rust MCP 相对较新,生态系统还不够成熟,第三方库和工具支持有限。

  3. 错误处理复杂:Rust 的错误处理机制虽然强大,但在 MCP 实现中可能导致代码冗长和复杂。

  4. 编译时间长:Rust 的编译时间相对较长,特别是在使用大量宏的情况下,可能影响开发效率。

  5. 调试困难:宏系统生成的代码在调试时可能难以理解,增加了调试的难度。

  6. 资源消耗:Rust 程序的内存占用相对较高,对于资源受限的环境可能不是最佳选择。

  7. 文档不足:虽然有代码注释,但缺乏系统性的文档和教程,特别是高级特性的使用说明。

  8. 兼容性问题:与其他语言实现的 MCP 服务器可能存在兼容性问题,特别是在处理复杂的数据结构时。

6. 应用场景

Rust MCP 的应用场景广泛,特别适合以下情况:

  1. AI 助手开发:构建能够与大型语言模型交互的 AI 助手,提供代码审查、文档生成、问题解答等功能。

  2. 企业级应用:开发需要高性能和高可靠性的企业级应用,如客户服务系统、数据分析平台等。

  3. 实时通信系统:构建需要实时通信能力的系统,如聊天机器人、实时监控系统等。

  4. 微服务架构:在微服务架构中作为服务间的通信协议,提供结构化的上下文交互能力。

  5. DevOps 工具:开发自动化运维工具,如日志分析、错误诊断、性能监控等。

  6. 教育平台:构建智能教育平台,提供个性化的学习路径和实时的学习辅导。

  7. 内容创作:开发辅助内容创作的工具,如写作助手、翻译工具、内容摘要等。

  8. 游戏开发:在游戏开发中实现智能 NPC 和对话系统,提供更丰富的游戏体验。

7. 总结与展望

Rust MCP 是一个强大而灵活的框架,它将 Rust 的性能优势与 MCP 协议的上下文交互能力相结合,为开发者提供了一个构建智能应用的理想平台。通过本文的分析,我们可以看到 Rust MCP 在类型安全、高性能、宏系统等方面的优势,以及在学习曲线、生态系统成熟度等方面的挑战。

总结

  1. 技术优势:Rust MCP 充分利用了 Rust 语言的类型安全、高性能和并发特性,为 MCP 协议的实现提供了坚实的基础。

  2. 设计理念:通过宏系统简化了 MCP 服务器的实现,使开发者能够专注于业务逻辑,而不需要处理底层协议细节。

  3. 功能丰富:支持工具、提示和资源等 MCP 核心组件,提供了完整的上下文交互能力。

  4. 应用广泛:适用于 AI 助手、企业应用、实时通信等多种场景,具有广阔的应用前景。

展望

  1. 生态系统发展:随着 Rust MCP 的普及,预计会有更多的第三方库和工具出现,丰富生态系统。

  2. 性能优化:未来可能会有更多的性能优化,特别是在高并发和低延迟场景下的表现。

  3. 易用性提升:可能会提供更高级的抽象和更友好的 API,降低学习门槛,吸引更多开发者。

  4. 标准化进程:随着 MCP 协议的标准化进程,Rust MCP 可能会成为 Rust 生态中实现 MCP 协议的标准库。

  5. 跨语言互操作:可能会增强与其他语言实现的 MCP 服务器的互操作性,促进不同语言生态系统间的协作。

  6. 集成更多 AI 能力:随着 AI 技术的发展,Rust MCP 可能会集成更多先进的 AI 能力,如多模态交互、强化学习等。

总之,Rust MCP 是一个充满潜力的项目,它不仅展示了 Rust 在构建高性能网络服务方面的优势,也为 AI 与应用的集成提供了新的思路。随着技术的不断发展和生态系统的成熟,Rust MCP 有望成为构建智能应用的重要工具。

相关推荐
FIT2CLOUD飞致云5 小时前
1Panel AI一体机(GB10版)推出MiniMax H3本地视频生成方案
运维·ai·开源·1panel·ai视频·运维面板
tachibana25 小时前
AI Agent 的记忆机制
人工智能·ai·大模型·llm·agent
步行cgn5 小时前
Spring Boot 启动报错:MissingServletWebServerFactoryBean 的排查与解决
java·spring boot·后端
全栈弟弟6 小时前
智能商店+自主智能体APP 商业计划书
人工智能·机器人·ai-native
步行cgn6 小时前
Spring Boot 项目打包部署详解
java·spring boot·后端
ai小陈6 小时前
大模型推理显存不够怎么办?量化、KV Cache与CPU Offload优化实战
大数据·人工智能·ai·云计算·gpu算力
slacker-kian6 小时前
[笔记]-什么是相似性搜索?
python·ai·向量·相似性搜索·点积·l2 距离·余弦相似度和距离
sukioe6 小时前
城智连响:基于 LangGraph 与四库分层架构的城市公共设施智能报修与派单系统
人工智能·python·ai·架构·langchain
我的xiaodoujiao6 小时前
Django 基础知识详细图文教程 4-Django 视图定义与使用
开发语言·数据库·后端·测试工具·django·sqlite
Apifox7 小时前
Apifox 8 月更新|调试、权限与协作体验持续优化
前端·后端·测试