rust-sdk/docs/readme/README.zh-cn.md
4t145 0be6067326
Move whole rmcp crate to offcial rust sdk (#44)
* get rid of async-trait

* move rmcp to official-rust-sdk

* replace master with main

* update readme, change license

* fix typo

* Update README.md

Co-authored-by: Jefry Dewangga <jefrydco@gmail.com>

* fix typos

---------

Co-authored-by: = <=>
Co-authored-by: Jefry Dewangga <jefrydco@gmail.com>
2025-03-27 17:33:41 -04:00

164 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# RMCP
[![Crates.io Version](https://img.shields.io/crates/v/rmcp)](https://crates.io/crates/rmcp)
![Release status](https://github.commodelcontextprotocol/rust-sdk/actions/workflows/release.yml/badge.svg)
[![docs.rs](https://img.shields.io/docsrs/rmcp)](https://docs.rs/rmcp/latest/rmcp)
一个干净且完整的 MCP SDK
## 使用
### 导入
```toml
rmcp = { version = "0.1", features = ["server"] }
## 或者开发者频道
rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", branch = "dev" }
```
### 快速上手
你可以用一行代码,启动一个SSE客户端
```rust
use rmcp::{ServiceExt, transport::TokioChildProcess};
use tokio::process::Command;
let client = ().serve(
TokioChildProcess::new(Command::new("npx").arg("-y").arg("@modelcontextprotocol/server-everything"))?
).await?;
```
#### 1. 构建传输层
```rust, ignore
use tokio::io::{stdin, stdout};
let transport = (stdin(), stdout());
```
传输层类型只需要实现 [`IntoTransport`](crate::transport::IntoTransport) trait, 这个特性允许你创建一个Sink和一个Stream
对于客户端, Sink 的 Item 是 [`ClientJsonRpcMessage`](crate::model::ClientJsonRpcMessage), Stream 的 Item 是 [`ServerJsonRpcMessage`](crate::model::ServerJsonRpcMessage)
对于服务端, Sink 的 Item 是 [`ServerJsonRpcMessage`](crate::model::ServerJsonRpcMessage), Stream 的 Item 是 [`ClientJsonRpcMessage`](crate::model::ClientJsonRpcMessage)
##### 这些类型自动实现了 [`IntoTransport`](crate::transport::IntoTransport) trait
1. 兼具 [`Sink`](futures::Sink) 与 [`Stream`](futures::Stream) 的
2. 一对 Sink `Tx` Stream `Rx`, 类型 `(Tx, Rx)` 自动实现 [`IntoTransport`](crate::transport::IntoTransport)
3. 兼具 [`tokio::io::AsyncRead`] 与 [`tokio::io::AsyncWrite`] 的
4. 一对 Sink [`tokio::io::AsyncRead`] `R ` [`tokio::io::AsyncWrite`] `W`, 类型 `(R, W)`自动实现 [`IntoTransport`](crate::transport::IntoTransport)
示例,你可以轻松创建一个TCP流来作为传输层. [examples](examples/README.md)
#### 2. 构建服务
你可以通过 [`ServerHandler`](crates/rmcp/src/handler/server.rs) 或 [`ClientHandler`](crates/rmcp/src/handler/client.rs) 轻松构建服务
```rust, ignore
let service = common::counter::Counter::new();
```
如果你想用 `tower`, 你也可以使用 [`TowerHandler`] 来作为tower服务的适配器.
请参考 [服务用例](examples/servers/src/common/counter.rs).
#### 3. 把他们组装到一起
```rust, ignore
// 这里会自动完成初始化流程
let server = service.serve(transport).await?;
```
#### 4. 与服务端/客户端交互
一旦你完成初始化,你可以发送请求或者发送通知
```rust, ignore
// request
let roots = server.list_roots().await?;
// or send notification
server.notify_cancelled(...).await?;
```
#### 5. 等待服务结束
```rust, ignore
let quit_reason = server.waiting().await?;
// or cancel it
let quit_reason = server.cancel().await?;
```
### 使用宏来定义工具
使用 `tool` 宏来快速创建工具
请看这个[文件](examples/servers/src/common/calculator.rs).
```rust, ignore
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>` ,其中 `T` 与 `E` 都实现了 `IntoContents`, 那就会自动标记成功或者失败。
### 管理多个服务
在很多情况下你需要把不同类型的服务管理在一个集合当中,你可以调用 `into_dyn` 来把他们都转化成动态类型。
```rust, ignore
let service = service.into_dyn();
```
### 用例
查看 [用例文件夹](examples/README.md)
### Features
- `client`: 使用客户端sdk
- `server`: 使用服务端sdk
## 相关资源
- [MCP Specification](https://spec.modelcontextprotocol.io/specification/2024-11-05/)
- [Schema](https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.ts)