Skip to content

使用 Rust 实现 MCP 协议:为大模型构建外部工具插件

kingzcheung
Published date:
Edit this post

MCP(Model Context Protocol) 协议很早就出来了,但是由于在早期支持 MCP协议 的客户端在国内就没几个,所以也没机会去搞 MCP server。早期 MCP 只有 Claude 自家的客户端支持得最好,但是你没点技术手段,还用不了Claude。

什么是 MCP?

大模型其实没有什么能力,本质上就是输入文本 => 输出文本。但是不妨碍用户在外部定义一些工具函数,让大模型在输出文本的时候,输出特写格式的数据(包括用哪个工具函数,参数是什么),然后再执行指定的工具函数。这就是 Function Call(Tool Call) 的功能。但是其实 Function Call 本质上还是内部函数,并没有什么外部的动态扩展能力。

MCP 本质上就是外部的“Function Call”,Agent 应用的插件。把函数调用做成了外部可拔插的标准化服务,客户端想用哪个服务直接注册即可。

这些能力能玩得转的前提是,大模型要有非常强的指令遵循能力。我主要使用 Qwen 模型,可以肯定的是,Qwen 模型在去年这个时间(2024 年 8 月)没有这个能力,甚至当时国内任何一家的大模型都没有这个能力,直到 DeepSeek V3/R1 的出现。

无论是函数调用还是 MCP,最简单的调用链路大致是这样的:

prompt -> 大模型理解  -> 返回输出
				    -> 判断需要调用工具(或者 mcp)->调用工具(或者 mcp)->合并结果再调用大模型理解 -> 返回输出

使用 Rust 实现一个 MCP 服务是比较简单的,因为 MCP 官方就直接 rust sdk。MCP 使用 json rpc 作为数据交换协议,而传输管道有两种:

前者适合本地,后者适合远程服务。

比如我觉得大模型应对 Rust 编程的时候效果太差了,很多时候根本是在胡说八道,无中生有。我希望大模型实现功能如果有使用到某些 crate 时,最好自己去查一下这个 crate 的最新文档,再好好给我干活。

依赖:

rmcp = {version = "0.5.0", features = ["transport-io","macros"]}
#[tool_router]
impl Docs {
    fn new() -> Self {
        Self {
            tool_router: Self::tool_router(),
        }
    }

    #[tool(name = "readme", description = "获取 rust crate 文档的信息")]
    async fn readme(&self, params: Parameters<LibraryRequest>) -> Result<CallToolResult, McpError> {
        let crate_name = params.0.name.as_str();
        let version = params.0.version.as_deref().unwrap_or("latest");
        let docs = reqwest::get(format!("https://docs.rs/{crate_name}/{version}")).await.unwrap().text().await.unwrap();
        Ok(CallToolResult::success(vec![Content::text(docs)]))
    }
}


#[tool_handler]
impl rmcp::ServerHandler for Docs {
    fn get_info(&self) -> ServerInfo {
        ServerInfo {
            instructions: Some("crate 文档".into()),
            capabilities: ServerCapabilities::builder().enable_tools().build(),
            ..Default::default()
        }
    }
}

这部分就是用户要实现的工具函数。

注册一个 stdin 作为传输层的 mcp server:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create and run the server with STDIO transport
    let service = Docs::new().serve(stdio()).await.inspect_err(|e| {
        println!("Error starting server: {e}");
    })?;
    service.waiting().await?;

    Ok(())
}

如何使用?

据我所知, vscode 上的 ai code 插件现在基本都支持 mcp server。比如国内阿里的 lingma,腾讯的云代码助手,字节的 trae。我们只需要 cargo build 得到二进制包,在上面这些助手里配置 mcp server 即可。阿里的 lingma 配置是最方便的,不过它的插件整体质量一般。

Previous
Implementing MCP Protocol with Rust: Building External Tool Plugins for Large Models
Next
Rust Tauri v2 Desktop App Development: Unconventional Windows and System Permissions Handling