* refactor: refactor tool macros and router implementation - Updated the `#[tool(tool_box)]` macro to `#[tool_router]` across various modules for consistency. - Enhanced the `Calculator`, `Counter`, and `GenericService` structs to utilize `ToolRouter` for handling tool calls. - Introduced `Parameters` struct for better parameter handling in tool functions. - Added new methods for listing tools and calling tools in server handlers. - Improved test cases to reflect changes in tool routing and parameter handling. - Updated documentation and examples to align with the new router structure. * fix: fix fmt and build error * fix: fix test failure * docs: documents for macros, fix ci * fix: fix ci * fix: fix wrongly replaced documents * fix: remove useless file * fix: change the parameter format for tool_router * fix: update extract_doc_line to handle existing documentation and clean up unused code in server handler * doc: update document for macro and examples * doc: update readme and add contribute guide * fix: fix type
165 lines
No EOL
5.3 KiB
Markdown
165 lines
No EOL
5.3 KiB
Markdown
# RMCP
|
||
[](https://crates.io/crates/rmcp)
|
||

|
||
[](https://docs.rs/rmcp/latest/rmcp)
|
||
|
||
一个基于tokio异步运行时的官方Model Context Protocol SDK实现。
|
||
|
||
## 使用
|
||
|
||
### 导入
|
||
```toml
|
||
rmcp = { version = "0.1", features = ["server"] }
|
||
## 或者开发者频道
|
||
rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", branch = "main" }
|
||
```
|
||
|
||
### 快速上手
|
||
一行代码启动客户端:
|
||
```rust
|
||
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. 构建传输层
|
||
|
||
```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) 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](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();
|
||
```
|
||
|
||
#### 3. 把他们组装到一起
|
||
```rust, ignore
|
||
// 这里会自动完成初始化流程
|
||
let server = service.serve(transport).await?;
|
||
```
|
||
|
||
#### 4. 与服务交互
|
||
一旦服务初始化完成,你可以发送请求或通知:
|
||
|
||
```rust, ignore
|
||
// 请求
|
||
let roots = server.list_roots().await?;
|
||
|
||
// 或发送通知
|
||
server.notify_cancelled(...).await?;
|
||
```
|
||
|
||
#### 5. 等待服务关闭
|
||
```rust, ignore
|
||
let quit_reason = server.waiting().await?;
|
||
// 或取消它
|
||
let quit_reason = server.cancel().await?;
|
||
```
|
||
|
||
### 使用宏来声明工具
|
||
使用 `toolbox` 和 `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_router]
|
||
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_handler]
|
||
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](examples/README.md)
|
||
|
||
### 功能特性
|
||
- `client`: 使用客户端sdk
|
||
- `server`: 使用服务端sdk
|
||
- `macros`: 宏默认
|
||
#### 传输层
|
||
- `transport-io`: 服务端标准输入输出传输
|
||
- `transport-sse-server`: 服务端SSE传输
|
||
- `transport-child-process`: 客户端标准输入输出传输
|
||
- `transport-sse`: 客户端SSE传输
|
||
|
||
## 相关资源
|
||
- [MCP Specification](https://spec.modelcontextprotocol.io/specification/2024-11-05/)
|
||
|
||
- [Schema](https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.ts) |