Skip to content

Commit 50f51d7

Browse files
committed
docs: add Tools section to README and simplify calculator examples with server_handler
1 parent 4546c53 commit 50f51d7

12 files changed

Lines changed: 153 additions & 39 deletions

File tree

README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ For the full MCP specification, see [modelcontextprotocol.io](https://modelconte
2222
## Table of Contents
2323

2424
- [Usage](#usage)
25+
- [Tools](#tools)
2526
- [Resources](#resources)
2627
- [Prompts](#prompts)
2728
- [Sampling](#sampling)
@@ -129,6 +130,76 @@ let quit_reason = server.cancel().await?;
129130

130131
---
131132

133+
## Tools
134+
135+
Tools let servers expose callable functions to clients. Each tool has a name, description, and a JSON Schema for its parameters. Clients discover tools via `list_tools` and invoke them via `call_tool`.
136+
137+
**MCP Spec:** [Tools](https://modelcontextprotocol.io/specification/2025-11-25/server/tools)
138+
139+
### Server-side
140+
141+
The `#[tool]`, `#[tool_router]`, and `#[tool_handler]` macros handle all the wiring. For a tools-only server you can use `#[tool_router(server_handler)]` to skip the separate `ServerHandler` impl:
142+
143+
```rust,ignore
144+
use rmcp::{tool, tool_router, ServiceExt, transport::stdio};
145+
146+
#[derive(Clone)]
147+
struct Calculator;
148+
149+
#[tool_router(server_handler)]
150+
impl Calculator {
151+
#[tool(description = "Add two numbers")]
152+
fn add(&self, #[tool(param)] a: i32, #[tool(param)] b: i32) -> String {
153+
(a + b).to_string()
154+
}
155+
}
156+
157+
#[tokio::main]
158+
async fn main() -> anyhow::Result<()> {
159+
let service = Calculator.serve(stdio()).await?;
160+
service.waiting().await?;
161+
Ok(())
162+
}
163+
```
164+
165+
When you need custom server metadata or multiple capabilities (tools + prompts), use explicit `#[tool_handler]`:
166+
167+
```rust,ignore
168+
use rmcp::{tool, tool_router, tool_handler, ServerHandler, ServiceExt};
169+
170+
#[derive(Clone)]
171+
struct Calculator;
172+
173+
#[tool_router]
174+
impl Calculator {
175+
#[tool(description = "Add two numbers")]
176+
fn add(&self, #[tool(param)] a: i32, #[tool(param)] b: i32) -> String {
177+
(a + b).to_string()
178+
}
179+
}
180+
181+
#[tool_handler(name = "calculator", version = "1.0.0", instructions = "A simple calculator")]
182+
impl ServerHandler for Calculator {}
183+
```
184+
185+
See [`crates/rmcp-macros`](crates/rmcp-macros/README.md) for full macro documentation.
186+
187+
### Client-side
188+
189+
```rust,ignore
190+
use rmcp::model::CallToolRequestParams;
191+
192+
// List all tools
193+
let tools = client.list_all_tools().await?;
194+
195+
// Call a tool by name
196+
let result = client.call_tool(CallToolRequestParams::new("add")).await?;
197+
```
198+
199+
**Example:** [`examples/servers/src/common/calculator.rs`](examples/servers/src/common/calculator.rs) (server), [`examples/servers/src/calculator_stdio.rs`](examples/servers/src/calculator_stdio.rs) (stdio runner)
200+
201+
---
202+
132203
## Resources
133204

134205
Resources let servers expose data (files, database records, API responses) that clients can read. Each resource is identified by a URI and returns content as text or binary (base64-encoded) data. Resource templates allow servers to declare URI patterns with dynamic parameters.

docs/readme/README.zh-cn.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
## 目录
2323

2424
- [使用](#使用)
25+
- [工具](#工具)
2526
- [资源](#资源)
2627
- [提示词](#提示词)
2728
- [采样](#采样)
@@ -129,6 +130,76 @@ let quit_reason = server.cancel().await?;
129130

130131
---
131132

133+
## 工具
134+
135+
工具允许服务端向客户端暴露可调用的函数。每个工具都有名称、描述和参数的 JSON Schema。客户端通过 `list_tools` 发现工具,通过 `call_tool` 调用工具。
136+
137+
**MCP 规范:** [Tools](https://modelcontextprotocol.io/specification/2025-11-25/server/tools)
138+
139+
### 服务端
140+
141+
`#[tool]``#[tool_router]``#[tool_handler]` 宏负责所有连接工作。对于纯工具服务端,可以使用 `#[tool_router(server_handler)]` 来省略单独的 `ServerHandler` 实现:
142+
143+
```rust,ignore
144+
use rmcp::{tool, tool_router, ServiceExt, transport::stdio};
145+
146+
#[derive(Clone)]
147+
struct Calculator;
148+
149+
#[tool_router(server_handler)]
150+
impl Calculator {
151+
#[tool(description = "Add two numbers")]
152+
fn add(&self, #[tool(param)] a: i32, #[tool(param)] b: i32) -> String {
153+
(a + b).to_string()
154+
}
155+
}
156+
157+
#[tokio::main]
158+
async fn main() -> anyhow::Result<()> {
159+
let service = Calculator.serve(stdio()).await?;
160+
service.waiting().await?;
161+
Ok(())
162+
}
163+
```
164+
165+
当需要自定义服务端元数据或多种能力(工具 + 提示词)时,使用显式的 `#[tool_handler]`
166+
167+
```rust,ignore
168+
use rmcp::{tool, tool_router, tool_handler, ServerHandler, ServiceExt};
169+
170+
#[derive(Clone)]
171+
struct Calculator;
172+
173+
#[tool_router]
174+
impl Calculator {
175+
#[tool(description = "Add two numbers")]
176+
fn add(&self, #[tool(param)] a: i32, #[tool(param)] b: i32) -> String {
177+
(a + b).to_string()
178+
}
179+
}
180+
181+
#[tool_handler(name = "calculator", version = "1.0.0", instructions = "A simple calculator")]
182+
impl ServerHandler for Calculator {}
183+
```
184+
185+
完整的宏文档请参阅 [`crates/rmcp-macros`](../../crates/rmcp-macros/README.md)
186+
187+
### 客户端
188+
189+
```rust,ignore
190+
use rmcp::model::CallToolRequestParams;
191+
192+
// 列出所有工具
193+
let tools = client.list_all_tools().await?;
194+
195+
// 按名称调用工具
196+
let result = client.call_tool(CallToolRequestParams::new("add")).await?;
197+
```
198+
199+
**示例:** [`examples/servers/src/common/calculator.rs`](../../examples/servers/src/common/calculator.rs)(服务端),[`examples/servers/src/calculator_stdio.rs`](../../examples/servers/src/calculator_stdio.rs)(stdio 运行器)
200+
201+
---
202+
132203
## 资源
133204

134205
资源允许服务端向客户端暴露数据(文件、数据库记录、API 响应)供其读取。每个资源通过 URI 标识,返回文本或二进制(base64 编码)内容。资源模板允许服务端声明带有动态参数的 URI 模式。

examples/servers/src/calculator_stdio.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ async fn main() -> Result<()> {
1717
tracing::info!("Starting Calculator MCP server");
1818

1919
// Create an instance of our calculator router
20-
let service = Calculator::new().serve(stdio()).await.inspect_err(|e| {
20+
let service = Calculator.serve(stdio()).await.inspect_err(|e| {
2121
tracing::error!("serving error: {:?}", e);
2222
})?;
2323

examples/servers/src/common/calculator.rs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
#![allow(dead_code)]
22

3-
use rmcp::{
4-
ServerHandler, handler::server::wrapper::Parameters, schemars, tool, tool_handler, tool_router,
5-
};
3+
use rmcp::{handler::server::wrapper::Parameters, schemars, tool, tool_router};
64

75
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
86
pub struct SumRequest {
@@ -22,12 +20,8 @@ pub struct SubRequest {
2220
#[derive(Debug, Clone)]
2321
pub struct Calculator;
2422

25-
#[tool_router]
23+
#[tool_router(server_handler)]
2624
impl Calculator {
27-
pub fn new() -> Self {
28-
Self
29-
}
30-
3125
#[tool(description = "Calculate the sum of two numbers")]
3226
fn sum(&self, Parameters(SumRequest { a, b }): Parameters<SumRequest>) -> String {
3327
(a + b).to_string()
@@ -38,6 +32,3 @@ impl Calculator {
3832
(a - b).to_string()
3933
}
4034
}
41-
42-
#[tool_handler(instructions = "A simple calculator")]
43-
impl ServerHandler for Calculator {}

examples/transport/src/common/calculator.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,6 @@ pub struct SubRequest {
2424
#[derive(Debug, Clone)]
2525
pub struct Calculator;
2626

27-
impl Calculator {
28-
pub fn new() -> Self {
29-
Self
30-
}
31-
}
32-
3327
#[tool_router]
3428
impl Calculator {
3529
#[tool(description = "Calculate the sum of two numbers")]

examples/transport/src/http_upgrade.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ async fn main() -> anyhow::Result<()> {
2424
async fn http_server(req: Request<Incoming>) -> Result<hyper::Response<String>, hyper::Error> {
2525
tokio::spawn(async move {
2626
let upgraded = hyper::upgrade::on(req).await?;
27-
let service = Calculator::new().serve(TokioIo::new(upgraded)).await?;
27+
let service = Calculator.serve(TokioIo::new(upgraded)).await?;
2828
service.waiting().await?;
2929
anyhow::Result::<()>::Ok(())
3030
});

examples/transport/src/named-pipe.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ async fn main() -> anyhow::Result<()> {
1616
let stream = server;
1717
server = ServerOptions::new().create(name)?;
1818
tokio::spawn(async move {
19-
match serve_server(Calculator::new(), stream).await {
19+
match serve_server(Calculator, stream).await {
2020
Ok(server) => {
2121
println!("Server initialized successfully");
2222
if let Err(e) = server.waiting().await {

examples/transport/src/tcp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ async fn server() -> anyhow::Result<()> {
1313
let tcp_listener = tokio::net::TcpListener::bind("127.0.0.1:8001").await?;
1414
while let Ok((stream, _)) = tcp_listener.accept().await {
1515
tokio::spawn(async move {
16-
let server = serve_server(Calculator::new(), stream).await?;
16+
let server = serve_server(Calculator, stream).await?;
1717
server.waiting().await?;
1818
anyhow::Ok(())
1919
});

examples/transport/src/unix_socket.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ async fn main() -> anyhow::Result<()> {
1414
while let Ok((stream, addr)) = unix_listener.accept().await {
1515
println!("Client connected: {:?}", addr);
1616
tokio::spawn(async move {
17-
match serve_server(Calculator::new(), stream).await {
17+
match serve_server(Calculator, stream).await {
1818
Ok(server) => {
1919
println!("Server initialized successfully");
2020
if let Err(e) = server.waiting().await {

examples/transport/src/websocket.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ async fn start_server() -> anyhow::Result<()> {
4040
tokio::spawn(async move {
4141
let ws_stream = tokio_tungstenite::accept_async(stream).await?;
4242
let transport = WebsocketTransport::new_server(ws_stream);
43-
let server = Calculator::new().serve(transport).await?;
43+
let server = Calculator.serve(transport).await?;
4444
server.waiting().await?;
4545
Ok::<(), anyhow::Error>(())
4646
});

0 commit comments

Comments
 (0)