rust-sdk/docs/readme
Humberto Yusta Gómez 64995cc40c
fix: cleanup zombie processes for child process client (#156)
* feat(client): cleanup of zombie processes in child process client

Using process wrap library, Process Group for Unix and Job Object for Windows

* fix: install extra dep based on feature, update examples take owned command

Install process-wrap if transport-child-process feature is enabled. Update examples to take ownership
of the command instead of mutable reference

* fix: update other examples, comments and readme to take ownership of command

Updated more examples, comments and readme to take command instead of mutable ref.
Also small fix in cargo toml of example to use local path.

* refactor: add configure command ext

Added configure command ext to tokio process command so that
you can use .configure() to use inline commands for mcp stdio client.
Added warning if start kill process fails
2025-05-18 18:38:47 +08:00
..
README.zh-cn.md fix: cleanup zombie processes for child process client (#156) 2025-05-18 18:38:47 +08:00

RMCP

Crates.io Version Release status docs.rs

一个基于tokio异步运行时的官方Model Context Protocol SDK实现。

使用

导入

rmcp = { version = "0.1", features = ["server"] }
## 或者开发者频道
rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", branch = "main" }

快速上手

一行代码启动客户端:

use rmcp::{ServiceExt, transport::{TokioChildProcess, ConfigureCommandExt}};
use tokio::process::Command;

let client = ().serve(TokioChildProcess::new(Command::new("npx").configure(|cmd| {
    cmd.arg("-y").arg("@modelcontextprotocol/server-everything");
}))?).await?;

1. 构建传输层

use tokio::io::{stdin, stdout};
let transport = (stdin(), stdout());

传输层类型必须实现 IntoTransport trait, 这个特性允许分割成一个sink和一个stream。

对于客户端, Sink 的 Item 是 ClientJsonRpcMessage Stream 的 Item 是 ServerJsonRpcMessage

对于服务端, Sink 的 Item 是 ServerJsonRpcMessage Stream 的 Item 是 ClientJsonRpcMessage

这些类型自动实现了 IntoTransport trait
  1. 已经同时实现了 SinkStream trait的类型。
  2. 由sink Tx 和 stream Rx组成的元组: (Tx, Rx)
  3. 同时实现了 [tokio::io::AsyncRead] 和 [tokio::io::AsyncWrite] trait的类型。
  4. 由 [tokio::io::AsyncRead] R 和 [tokio::io::AsyncWrite] W 组成的元组: (R, W)

例如你可以看到我们如何轻松地通过TCP流或http升级构建传输层。 examples

2. 构建服务

你可以通过 ServerHandlerClientHandler 轻松构建服务

let service = common::counter::Counter::new();

3. 把他们组装到一起

// 这里会自动完成初始化流程
let server = service.serve(transport).await?;

4. 与服务交互

一旦服务初始化完成,你可以发送请求或通知:

// 请求
let roots = server.list_roots().await?;

// 或发送通知
server.notify_cancelled(...).await?;

5. 等待服务关闭

let quit_reason = server.waiting().await?;
// 或取消它
let quit_reason = server.cancel().await?;

使用宏来声明工具

使用 toolboxtool 宏来快速创建工具。

请看这个文件

use rmcp::{ServerHandler, model::ServerInfo, schemars, tool};

use super::counter::Counter;

#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct SumRequest {
    #[schemars(description = "the left hand side number")]
    pub a: i32,
    #[schemars(description = "the right hand side number")]
    pub b: i32,
}
#[derive(Debug, Clone)]
pub struct Calculator;

// create a static toolbox to store the tool attributes
#[tool(tool_box)]
impl Calculator {
    // async function
    #[tool(description = "Calculate the sum of two numbers")]
    async fn sum(&self, #[tool(aggr)] SumRequest { a, b }: SumRequest) -> String {
        (a + b).to_string()
    }

    // sync function
    #[tool(description = "Calculate the sum of two numbers")]
    fn sub(
        &self,
        #[tool(param)]
        // this macro will transfer the schemars and serde's attributes
        #[schemars(description = "the left hand side number")]
        a: i32,
        #[tool(param)]
        #[schemars(description = "the right hand side number")]
        b: i32,
    ) -> String {
        (a - b).to_string()
    }
}

// impl call_tool and list_tool by querying static toolbox
#[tool(tool_box)]
impl ServerHandler for Calculator {
    fn get_info(&self) -> ServerInfo {
        ServerInfo {
            instructions: Some("A simple calculator".into()),
            ..Default::default()
        }
    }
}

你要做的唯一事情就是确保函数的返回类型实现了 IntoCallToolResult

你可以为返回类型实现 IntoContents,那么返回值将自动标记为成功。

如果返回类型是 Result<T, E>,其中 TE 都实现了 IntoContents,那也是可以的。

管理多个服务

在很多情况下你需要在一个集合中管理多个服务,你可以调用 into_dyn 来将服务转换为相同类型。

let service = service.into_dyn();

示例

查看 examples

功能特性

  • client: 使用客户端sdk
  • server: 使用服务端sdk
  • macros: 宏默认

传输层

  • transport-io: 服务端标准输入输出传输
  • transport-sse-server: 服务端SSE传输
  • transport-child-process: 客户端标准输入输出传输
  • transport-sse: 客户端SSE传输

相关资源