|
1 |
| -# rust-sdk |
2 |
| -The official Rust SDK for the Model Context Protocol |
| 1 | +<div align = "right"> |
| 2 | +<a href="docs/readme/README.zh-cn.md">简体中文</a> |
| 3 | +</div> |
| 4 | + |
| 5 | +# RMCP |
| 6 | +[](https://crates.io/crates/rmcp) |
| 7 | + |
| 8 | +[](https://docs.rs/rmcp/latest/rmcp) |
| 9 | + |
| 10 | +An official rust Model Context Protocol SDK implementation with tokio async runtime. |
| 11 | + |
| 12 | +## Usage |
| 13 | + |
| 14 | +### Import |
| 15 | +```toml |
| 16 | +rmcp = { version = "0.1", features = ["server"] } |
| 17 | +## or dev channel |
| 18 | +rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", branch = "dev" } |
| 19 | +``` |
| 20 | + |
| 21 | +### Quick start |
| 22 | +Start a client in one line: |
| 23 | +```rust |
| 24 | +use rmcp::{ServiceExt, transport::TokioChildProcess}; |
| 25 | +use tokio::process::Command; |
| 26 | + |
| 27 | +let client = ().serve( |
| 28 | + TokioChildProcess::new(Command::new("npx").arg("-y").arg("@modelcontextprotocol/server-everything"))? |
| 29 | +).await?; |
| 30 | +``` |
| 31 | + |
| 32 | +#### 1. Build a transport |
| 33 | + |
| 34 | +```rust, ignore |
| 35 | +use tokio::io::{stdin, stdout}; |
| 36 | +let transport = (stdin(), stdout()); |
| 37 | +``` |
| 38 | + |
| 39 | +The transport type must implemented [`IntoTransport`](crate::transport::IntoTransport) trait, which allow split into a sink and a stream. |
| 40 | + |
| 41 | +For client, the sink item is [`ClientJsonRpcMessage`](crate::model::ClientJsonRpcMessage) and stream item is [`ServerJsonRpcMessage`](crate::model::ServerJsonRpcMessage) |
| 42 | + |
| 43 | +For server, the sink item is [`ServerJsonRpcMessage`](crate::model::ServerJsonRpcMessage) and stream item is [`ClientJsonRpcMessage`](crate::model::ClientJsonRpcMessage) |
| 44 | + |
| 45 | +##### These types is automatically implemented [`IntoTransport`](crate::transport::IntoTransport) trait |
| 46 | +1. The types that already implement both [`Sink`](futures::Sink) and [`Stream`](futures::Stream) trait. |
| 47 | +2. A tuple of sink `Tx` and stream `Rx`: `(Tx, Rx)`. |
| 48 | +3. The type that implement both [`tokio::io::AsyncRead`] and [`tokio::io::AsyncWrite`] trait. |
| 49 | +4. A tuple of [`tokio::io::AsyncRead`] `R `and [`tokio::io::AsyncWrite`] `W`: `(R, W)`. |
| 50 | + |
| 51 | +For example, you can see how we build a transport through TCP stream or http upgrade so easily. [examples](examples/README.md) |
| 52 | + |
| 53 | +#### 2. Build a service |
| 54 | +You can easily build a service by using [`ServerHandler`](crates/rmcp/src/handler/server.rs) or [`ClientHandler`](crates/rmcp/src/handler/client.rs). |
| 55 | + |
| 56 | +```rust, ignore |
| 57 | +let service = common::counter::Counter::new(); |
| 58 | +``` |
| 59 | + |
| 60 | +#### 3. Serve them together |
| 61 | +```rust, ignore |
| 62 | +// this call will finish the initialization process |
| 63 | +let server = service.serve(transport).await?; |
| 64 | +``` |
| 65 | + |
| 66 | +#### 4. Interact with the server |
| 67 | +Once the server is initialized, you can send requests or notifications: |
| 68 | + |
| 69 | +```rust, ignore |
| 70 | +// request |
| 71 | +let roots = server.list_roots().await?; |
| 72 | +
|
| 73 | +// or send notification |
| 74 | +server.notify_cancelled(...).await?; |
| 75 | +``` |
| 76 | + |
| 77 | +#### 5. Waiting for service shutdown |
| 78 | +```rust, ignore |
| 79 | +let quit_reason = server.waiting().await?; |
| 80 | +// or cancel it |
| 81 | +let quit_reason = server.cancel().await?; |
| 82 | +``` |
| 83 | + |
| 84 | +### Use marcos to declaring tool |
| 85 | +Use `toolbox` and `tool` macros to create tool quickly. |
| 86 | + |
| 87 | +Check this [file](examples/servers/src/common/calculator.rs). |
| 88 | +```rust, ignore |
| 89 | +use rmcp::{ServerHandler, model::ServerInfo, schemars, tool}; |
| 90 | +
|
| 91 | +use super::counter::Counter; |
| 92 | +
|
| 93 | +#[derive(Debug, serde::Deserialize, schemars::JsonSchema)] |
| 94 | +pub struct SumRequest { |
| 95 | + #[schemars(description = "the left hand side number")] |
| 96 | + pub a: i32, |
| 97 | + #[schemars(description = "the right hand side number")] |
| 98 | + pub b: i32, |
| 99 | +} |
| 100 | +#[derive(Debug, Clone)] |
| 101 | +pub struct Calculator; |
| 102 | +
|
| 103 | +// create a static toolbox to store the tool attributes |
| 104 | +#[tool(tool_box)] |
| 105 | +impl Calculator { |
| 106 | + // async function |
| 107 | + #[tool(description = "Calculate the sum of two numbers")] |
| 108 | + async fn sum(&self, #[tool(aggr)] SumRequest { a, b }: SumRequest) -> String { |
| 109 | + (a + b).to_string() |
| 110 | + } |
| 111 | +
|
| 112 | + // sync function |
| 113 | + #[tool(description = "Calculate the sum of two numbers")] |
| 114 | + fn sub( |
| 115 | + &self, |
| 116 | + #[tool(param)] |
| 117 | + // this macro will transfer the schemars and serde's attributes |
| 118 | + #[schemars(description = "the left hand side number")] |
| 119 | + a: i32, |
| 120 | + #[tool(param)] |
| 121 | + #[schemars(description = "the right hand side number")] |
| 122 | + b: i32, |
| 123 | + ) -> String { |
| 124 | + (a - b).to_string() |
| 125 | + } |
| 126 | +} |
| 127 | +
|
| 128 | +// impl call_tool and list_tool by querying static toolbox |
| 129 | +#[tool(tool_box)] |
| 130 | +impl ServerHandler for Calculator { |
| 131 | + fn get_info(&self) -> ServerInfo { |
| 132 | + ServerInfo { |
| 133 | + instructions: Some("A simple calculator".into()), |
| 134 | + ..Default::default() |
| 135 | + } |
| 136 | + } |
| 137 | +} |
| 138 | +
|
| 139 | +``` |
| 140 | +The only thing you should do is to make the function's return type implement `IntoCallToolResult`. |
| 141 | + |
| 142 | +And you can just implement `IntoContents`, and the return value will be marked as success automatically. |
| 143 | + |
| 144 | +If you return a type of `Result<T, E>` where `T` and `E` both implemented `IntoContents`, it's also OK. |
| 145 | + |
| 146 | +### Manage Multi Services |
| 147 | +For many cases you need to manage several service in a collection, you can call `into_dyn` to convert services into the same type. |
| 148 | +```rust, ignore |
| 149 | +let service = service.into_dyn(); |
| 150 | +``` |
| 151 | + |
| 152 | + |
| 153 | +### Examples |
| 154 | +See [examples](examples/README.md) |
| 155 | + |
| 156 | +### Features |
| 157 | +- `client`: use client side sdk |
| 158 | +- `server`: use server side sdk |
| 159 | +- `macros`: macros default |
| 160 | +#### Transports |
| 161 | +- `transport-io`: Server stdio transport |
| 162 | +- `transport-sse-server`: Server SSE transport |
| 163 | +- `transport-child-process`: Client stdio transport |
| 164 | +- `transport-sse`: Client sse transport |
| 165 | + |
| 166 | +## Related Resources |
| 167 | +- [MCP Specification](https://spec.modelcontextprotocol.io/specification/2024-11-05/) |
| 168 | + |
| 169 | +- [Schema](https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.ts) |
0 commit comments