Skip to content

Commit 621c9f6

Browse files
authored
feat(task): add task support (SEP-1686) (#536)
Signed-off-by: jokemanfire <[email protected]>
1 parent eeacd13 commit 621c9f6

33 files changed

+1922
-57
lines changed

crates/rmcp-macros/README.md

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66

77
This library primarily provides the following macros:
88

9-
- `#[tool]`: Used to mark functions as RMCP tools, automatically generating necessary metadata and invocation mechanisms
9+
- `#[tool]`: Mark an async/sync function as an RMCP tool and generate metadata + schema glue
10+
- `#[tool_router]`: Collect all `#[tool]` functions in an impl block into a router value
11+
- `#[tool_handler]`: Implement the `call_tool` and `list_tools` entry points by delegating to a router expression
12+
- `#[task_handler]`: Wire up the task lifecycle (list/enqueue/get/cancel) on top of an `OperationProcessor`
1013

1114
## Usage
1215

@@ -16,7 +19,7 @@ This macro is used to mark a function as a tool handler.
1619

1720
This will generate a function that return the attribute of this tool, with type `rmcp::model::Tool`.
1821

19-
#### Usage
22+
#### Tool attributes
2023

2124
| field | type | usage |
2225
| :- | :- | :- |
@@ -25,7 +28,7 @@ This will generate a function that return the attribute of this tool, with type
2528
| `input_schema` | `Expr` | A JSON Schema object defining the expected parameters for the tool. If not provide, if will use the json schema of its argument with type `Parameters<T>` |
2629
| `annotations` | `ToolAnnotationsAttribute` | Additional tool information. Defaults to `None`. |
2730

28-
#### Example
31+
#### Tool example
2932

3033
```rust
3134
#[tool(name = "my_tool", description = "This is my tool", annotations(title = "我的工具", read_only_hint = true))]
@@ -42,14 +45,14 @@ It creates a function that returns a `ToolRouter` instance.
4245

4346
In most case, you need to add a field for handler to store the router information and initialize it when creating handler, or store it with a static variable.
4447

45-
#### Usage
48+
#### Router attributes
4649

4750
| field | type | usage |
4851
| :- | :- | :- |
4952
| `router` | `Ident` | The name of the router function to be generated. Defaults to `tool_router`. |
5053
| `vis` | `Visibility` | The visibility of the generated router function. Defaults to empty. |
5154

52-
#### Example
55+
#### Router example
5356

5457
```rust
5558
#[tool_router]
@@ -104,13 +107,14 @@ impl MyToolHandler {
104107

105108
This macro will generate the handler for `tool_call` and `list_tools` methods in the implementation block, by using an existing `ToolRouter` instance.
106109

107-
#### Usage
110+
#### Handler attributes
108111

109112
| field | type | usage |
110113
| :- | :- | :- |
111114
| `router` | `Expr` | The expression to access the `ToolRouter` instance. Defaults to `self.tool_router`. |
112115

113-
#### Example
116+
#### Handler example
117+
114118
```rust
115119
#[tool_handler]
116120
impl ServerHandler for MyToolHandler {
@@ -119,15 +123,18 @@ impl ServerHandler for MyToolHandler {
119123
```
120124

121125
or using a custom router expression:
126+
122127
```rust
123128
#[tool_handler(router = self.get_router().await)]
124129
impl ServerHandler for MyToolHandler {
125130
// ...implement other handler
126131
}
127132
```
128133

129-
#### Explained
134+
#### Handler expansion
135+
130136
This macro will be expended to something like this:
137+
131138
```rust
132139
impl ServerHandler for MyToolHandler {
133140
async fn call_tool(
@@ -150,6 +157,46 @@ impl ServerHandler for MyToolHandler {
150157
}
151158
```
152159

160+
### task_handler
161+
162+
This macro wires the task lifecycle endpoints (`list_tasks`, `enqueue_task`, `get_task`, `cancel_task`) to an implementation of `OperationProcessor`. It keeps the handler lean by delegating scheduling, status tracking, and cancellation semantics to the processor.
163+
164+
#### Task handler attributes
165+
166+
| field | type | usage |
167+
| :- | :- | :- |
168+
| `processor` | `Expr` | Expression that yields an `Arc<dyn OperationProcessor>` (or compatible trait object). Defaults to `self.processor.clone()`. |
169+
170+
#### Task handler example
171+
172+
```rust
173+
#[derive(Clone)]
174+
pub struct TaskHandler {
175+
processor: Arc<dyn OperationProcessor<RoleServer> + Send + Sync>,
176+
}
177+
178+
#[task_handler(processor = self.processor.clone())]
179+
impl ServerHandler for TaskHandler {}
180+
```
181+
182+
#### Task handler expansion
183+
184+
At expansion time the macro implements the task-specific handler methods by forwarding to the processor expression, roughly equivalent to:
185+
186+
```rust
187+
impl ServerHandler for TaskHandler {
188+
async fn list_tasks(&self, request: TaskListRequest, ctx: RequestContext<RoleServer>) -> Result<TaskListResult, rmcp::ErrorData> {
189+
self.processor.list_tasks(request, ctx).await
190+
}
191+
192+
async fn enqueue_task(&self, request: TaskEnqueueRequest, ctx: RequestContext<RoleServer>) -> Result<TaskEnqueueResult, rmcp::ErrorData> {
193+
self.processor.enqueue_task(request, ctx).await
194+
}
195+
196+
// get_task and cancel_task are generated in the same manner.
197+
}
198+
```
199+
153200

154201
## Advanced Features
155202

@@ -159,4 +206,4 @@ impl ServerHandler for MyToolHandler {
159206

160207
## License
161208

162-
Please refer to the LICENSE file in the project root directory.
209+
Please refer to the LICENSE file in the project root directory.

crates/rmcp-macros/src/lib.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod common;
55
mod prompt;
66
mod prompt_handler;
77
mod prompt_router;
8+
mod task_handler;
89
mod tool;
910
mod tool_handler;
1011
mod tool_router;
@@ -263,3 +264,17 @@ pub fn prompt_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
263264
.unwrap_or_else(|err| err.to_compile_error())
264265
.into()
265266
}
267+
268+
/// # task_handler
269+
///
270+
/// Generates basic task-handling methods (`enqueue_task` and `list_tasks`) for a server handler
271+
/// using a shared \[`OperationProcessor`\]. The default processor expression assumes a
272+
/// `self.processor` field holding an `Arc<Mutex<OperationProcessor>>`, but it can be customized
273+
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
274+
/// futures, the handler type must implement [`Clone`].
275+
#[proc_macro_attribute]
276+
pub fn task_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
277+
task_handler::task_handler(attr.into(), input.into())
278+
.unwrap_or_else(|err| err.to_compile_error())
279+
.into()
280+
}

0 commit comments

Comments
 (0)