Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 56 additions & 9 deletions crates/rmcp-macros/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,10 @@

This library primarily provides the following macros:

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

## Usage

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

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

#### Usage
#### Tool attributes

| field | type | usage |
| :- | :- | :- |
Expand All@@ -25,7 +28,7 @@ This will generate a function that return the attribute of this tool, with type
| `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>` |
| `annotations` | `ToolAnnotationsAttribute` | Additional tool information. Defaults to `None`. |

#### Example
#### Tool example

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

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.

#### Usage
#### Router attributes

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

#### Example
#### Router example

```rust
#[tool_router]
Expand DownExpand Up@@ -104,13 +107,14 @@ impl MyToolHandler {

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

#### Usage
#### Handler attributes

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

#### Example
#### Handler example

```rust
#[tool_handler]
impl ServerHandler for MyToolHandler {
Expand All@@ -119,15 +123,18 @@ impl ServerHandler for MyToolHandler {
```

or using a custom router expression:

```rust
#[tool_handler(router = self.get_router().await)]
impl ServerHandler for MyToolHandler {
// ...implement other handler
}
```

#### Explained
#### Handler expansion

This macro will be expended to something like this:

```rust
impl ServerHandler for MyToolHandler {
async fn call_tool(
Expand All@@ -150,6 +157,46 @@ impl ServerHandler for MyToolHandler {
}
```

### task_handler

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.

#### Task handler attributes

| field | type | usage |
| :- | :- | :- |
| `processor` | `Expr` | Expression that yields an `Arc<dyn OperationProcessor>` (or compatible trait object). Defaults to `self.processor.clone()`. |

#### Task handler example

```rust
#[derive(Clone)]
pub struct TaskHandler {
processor: Arc<dyn OperationProcessor<RoleServer> + Send + Sync>,
}

#[task_handler(processor = self.processor.clone())]
impl ServerHandler for TaskHandler {}
```

#### Task handler expansion

At expansion time the macro implements the task-specific handler methods by forwarding to the processor expression, roughly equivalent to:

```rust
impl ServerHandler for TaskHandler {
async fn list_tasks(&self, request: TaskListRequest, ctx: RequestContext<RoleServer>) -> Result<TaskListResult, rmcp::ErrorData> {
self.processor.list_tasks(request, ctx).await
}

async fn enqueue_task(&self, request: TaskEnqueueRequest, ctx: RequestContext<RoleServer>) -> Result<TaskEnqueueResult, rmcp::ErrorData> {
self.processor.enqueue_task(request, ctx).await
}

// get_task and cancel_task are generated in the same manner.
}
```


## Advanced Features

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

## License

Please refer to the LICENSE file in the project root directory.
Please refer to the LICENSE file in the project root directory.
15 changes: 15 additions & 0 deletions crates/rmcp-macros/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ mod common;
mod prompt;
mod prompt_handler;
mod prompt_router;
mod task_handler;
mod tool;
mod tool_handler;
mod tool_router;
Expand DownExpand Up@@ -263,3 +264,17 @@ pub fn prompt_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
.unwrap_or_else(|err| err.to_compile_error())
.into()
}

/// # task_handler
///
/// Generates basic task-handling methods (`enqueue_task` and `list_tasks`) for a server handler
/// using a shared \[`OperationProcessor`\]. The default processor expression assumes a
/// `self.processor` field holding an `Arc<Mutex<OperationProcessor>>`, but it can be customized
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
Comment on lines +273 to +274

CopilotAIDec 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The macro requires the handler type to implement Clone (line 82) to spawn the task, but this requirement is not documented in the macro's documentation comment. Users will encounter confusing compiler errors if their handler doesn't implement Clone. The documentation at lines 268-274 should explicitly state that the handler must implement Clone.

Suggested change
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
/// via `#[task_handler(processor = ...)]`.
///
/// **Requirements:** The handler type must implement [`Clone`], as the macro captures `self` inside spawned futures.

Copilot uses AI. Check for mistakes.
#[proc_macro_attribute]
pub fn task_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
task_handler::task_handler(attr.into(), input.into())
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat(task): add task support (SEP-1686) by jokemanfire · Pull Request #536 · modelcontextprotocol/rust-sdk · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 56 additions & 9 deletions crates/rmcp-macros/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,10 @@

This library primarily provides the following macros:

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

## Usage

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

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

#### Usage
#### Tool attributes

| field | type | usage |
| :- | :- | :- |
Expand All@@ -25,7 +28,7 @@ This will generate a function that return the attribute of this tool, with type
| `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>` |
| `annotations` | `ToolAnnotationsAttribute` | Additional tool information. Defaults to `None`. |

#### Example
#### Tool example

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

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.

#### Usage
#### Router attributes

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

#### Example
#### Router example

```rust
#[tool_router]
Expand DownExpand Up@@ -104,13 +107,14 @@ impl MyToolHandler {

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

#### Usage
#### Handler attributes

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

#### Example
#### Handler example

```rust
#[tool_handler]
impl ServerHandler for MyToolHandler {
Expand All@@ -119,15 +123,18 @@ impl ServerHandler for MyToolHandler {
```

or using a custom router expression:

```rust
#[tool_handler(router = self.get_router().await)]
impl ServerHandler for MyToolHandler {
// ...implement other handler
}
```

#### Explained
#### Handler expansion

This macro will be expended to something like this:

```rust
impl ServerHandler for MyToolHandler {
async fn call_tool(
Expand All@@ -150,6 +157,46 @@ impl ServerHandler for MyToolHandler {
}
```

### task_handler

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.

#### Task handler attributes

| field | type | usage |
| :- | :- | :- |
| `processor` | `Expr` | Expression that yields an `Arc<dyn OperationProcessor>` (or compatible trait object). Defaults to `self.processor.clone()`. |

#### Task handler example

```rust
#[derive(Clone)]
pub struct TaskHandler {
processor: Arc<dyn OperationProcessor<RoleServer> + Send + Sync>,
}

#[task_handler(processor = self.processor.clone())]
impl ServerHandler for TaskHandler {}
```

#### Task handler expansion

At expansion time the macro implements the task-specific handler methods by forwarding to the processor expression, roughly equivalent to:

```rust
impl ServerHandler for TaskHandler {
async fn list_tasks(&self, request: TaskListRequest, ctx: RequestContext<RoleServer>) -> Result<TaskListResult, rmcp::ErrorData> {
self.processor.list_tasks(request, ctx).await
}

async fn enqueue_task(&self, request: TaskEnqueueRequest, ctx: RequestContext<RoleServer>) -> Result<TaskEnqueueResult, rmcp::ErrorData> {
self.processor.enqueue_task(request, ctx).await
}

// get_task and cancel_task are generated in the same manner.
}
```


## Advanced Features

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

## License

Please refer to the LICENSE file in the project root directory.
Please refer to the LICENSE file in the project root directory.
15 changes: 15 additions & 0 deletions crates/rmcp-macros/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ mod common;
mod prompt;
mod prompt_handler;
mod prompt_router;
mod task_handler;
mod tool;
mod tool_handler;
mod tool_router;
Expand DownExpand Up@@ -263,3 +264,17 @@ pub fn prompt_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
.unwrap_or_else(|err| err.to_compile_error())
.into()
}

/// # task_handler
///
/// Generates basic task-handling methods (`enqueue_task` and `list_tasks`) for a server handler
/// using a shared \[`OperationProcessor`\]. The default processor expression assumes a
/// `self.processor` field holding an `Arc<Mutex<OperationProcessor>>`, but it can be customized
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
Comment on lines +273 to +274

CopilotAIDec 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The macro requires the handler type to implement Clone (line 82) to spawn the task, but this requirement is not documented in the macro's documentation comment. Users will encounter confusing compiler errors if their handler doesn't implement Clone. The documentation at lines 268-274 should explicitly state that the handler must implement Clone.

Suggested change
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
/// via `#[task_handler(processor = ...)]`.
///
/// **Requirements:** The handler type must implement [`Clone`], as the macro captures `self` inside spawned futures.

Copilot uses AI. Check for mistakes.
#[proc_macro_attribute]
pub fn task_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
task_handler::task_handler(attr.into(), input.into())
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(task): add task support (SEP-1686) by jokemanfire · Pull Request #536 · modelcontextprotocol/rust-sdk · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 56 additions & 9 deletions crates/rmcp-macros/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,10 @@

This library primarily provides the following macros:

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

## Usage

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

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

#### Usage
#### Tool attributes

| field | type | usage |
| :- | :- | :- |
Expand All@@ -25,7 +28,7 @@ This will generate a function that return the attribute of this tool, with type
| `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>` |
| `annotations` | `ToolAnnotationsAttribute` | Additional tool information. Defaults to `None`. |

#### Example
#### Tool example

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

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.

#### Usage
#### Router attributes

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

#### Example
#### Router example

```rust
#[tool_router]
Expand DownExpand Up@@ -104,13 +107,14 @@ impl MyToolHandler {

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

#### Usage
#### Handler attributes

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

#### Example
#### Handler example

```rust
#[tool_handler]
impl ServerHandler for MyToolHandler {
Expand All@@ -119,15 +123,18 @@ impl ServerHandler for MyToolHandler {
```

or using a custom router expression:

```rust
#[tool_handler(router = self.get_router().await)]
impl ServerHandler for MyToolHandler {
// ...implement other handler
}
```

#### Explained
#### Handler expansion

This macro will be expended to something like this:

```rust
impl ServerHandler for MyToolHandler {
async fn call_tool(
Expand All@@ -150,6 +157,46 @@ impl ServerHandler for MyToolHandler {
}
```

### task_handler

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.

#### Task handler attributes

| field | type | usage |
| :- | :- | :- |
| `processor` | `Expr` | Expression that yields an `Arc<dyn OperationProcessor>` (or compatible trait object). Defaults to `self.processor.clone()`. |

#### Task handler example

```rust
#[derive(Clone)]
pub struct TaskHandler {
processor: Arc<dyn OperationProcessor<RoleServer> + Send + Sync>,
}

#[task_handler(processor = self.processor.clone())]
impl ServerHandler for TaskHandler {}
```

#### Task handler expansion

At expansion time the macro implements the task-specific handler methods by forwarding to the processor expression, roughly equivalent to:

```rust
impl ServerHandler for TaskHandler {
async fn list_tasks(&self, request: TaskListRequest, ctx: RequestContext<RoleServer>) -> Result<TaskListResult, rmcp::ErrorData> {
self.processor.list_tasks(request, ctx).await
}

async fn enqueue_task(&self, request: TaskEnqueueRequest, ctx: RequestContext<RoleServer>) -> Result<TaskEnqueueResult, rmcp::ErrorData> {
self.processor.enqueue_task(request, ctx).await
}

// get_task and cancel_task are generated in the same manner.
}
```


## Advanced Features

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

## License

Please refer to the LICENSE file in the project root directory.
Please refer to the LICENSE file in the project root directory.
15 changes: 15 additions & 0 deletions crates/rmcp-macros/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ mod common;
mod prompt;
mod prompt_handler;
mod prompt_router;
mod task_handler;
mod tool;
mod tool_handler;
mod tool_router;
Expand DownExpand Up@@ -263,3 +264,17 @@ pub fn prompt_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
.unwrap_or_else(|err| err.to_compile_error())
.into()
}

/// # task_handler
///
/// Generates basic task-handling methods (`enqueue_task` and `list_tasks`) for a server handler
/// using a shared \[`OperationProcessor`\]. The default processor expression assumes a
/// `self.processor` field holding an `Arc<Mutex<OperationProcessor>>`, but it can be customized
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
Comment on lines +273 to +274

CopilotAIDec 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The macro requires the handler type to implement Clone (line 82) to spawn the task, but this requirement is not documented in the macro's documentation comment. Users will encounter confusing compiler errors if their handler doesn't implement Clone. The documentation at lines 268-274 should explicitly state that the handler must implement Clone.

Suggested change
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
/// via `#[task_handler(processor = ...)]`.
///
/// **Requirements:** The handler type must implement [`Clone`], as the macro captures `self` inside spawned futures.

Copilot uses AI. Check for mistakes.
#[proc_macro_attribute]
pub fn task_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
task_handler::task_handler(attr.into(), input.into())
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(task): add task support (SEP-1686) by jokemanfire · Pull Request #536 · modelcontextprotocol/rust-sdk · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 56 additions & 9 deletions crates/rmcp-macros/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,10 @@

This library primarily provides the following macros:

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

## Usage

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

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

#### Usage
#### Tool attributes

| field | type | usage |
| :- | :- | :- |
Expand All@@ -25,7 +28,7 @@ This will generate a function that return the attribute of this tool, with type
| `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>` |
| `annotations` | `ToolAnnotationsAttribute` | Additional tool information. Defaults to `None`. |

#### Example
#### Tool example

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

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.

#### Usage
#### Router attributes

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

#### Example
#### Router example

```rust
#[tool_router]
Expand DownExpand Up@@ -104,13 +107,14 @@ impl MyToolHandler {

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

#### Usage
#### Handler attributes

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

#### Example
#### Handler example

```rust
#[tool_handler]
impl ServerHandler for MyToolHandler {
Expand All@@ -119,15 +123,18 @@ impl ServerHandler for MyToolHandler {
```

or using a custom router expression:

```rust
#[tool_handler(router = self.get_router().await)]
impl ServerHandler for MyToolHandler {
// ...implement other handler
}
```

#### Explained
#### Handler expansion

This macro will be expended to something like this:

```rust
impl ServerHandler for MyToolHandler {
async fn call_tool(
Expand All@@ -150,6 +157,46 @@ impl ServerHandler for MyToolHandler {
}
```

### task_handler

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.

#### Task handler attributes

| field | type | usage |
| :- | :- | :- |
| `processor` | `Expr` | Expression that yields an `Arc<dyn OperationProcessor>` (or compatible trait object). Defaults to `self.processor.clone()`. |

#### Task handler example

```rust
#[derive(Clone)]
pub struct TaskHandler {
processor: Arc<dyn OperationProcessor<RoleServer> + Send + Sync>,
}

#[task_handler(processor = self.processor.clone())]
impl ServerHandler for TaskHandler {}
```

#### Task handler expansion

At expansion time the macro implements the task-specific handler methods by forwarding to the processor expression, roughly equivalent to:

```rust
impl ServerHandler for TaskHandler {
async fn list_tasks(&self, request: TaskListRequest, ctx: RequestContext<RoleServer>) -> Result<TaskListResult, rmcp::ErrorData> {
self.processor.list_tasks(request, ctx).await
}

async fn enqueue_task(&self, request: TaskEnqueueRequest, ctx: RequestContext<RoleServer>) -> Result<TaskEnqueueResult, rmcp::ErrorData> {
self.processor.enqueue_task(request, ctx).await
}

// get_task and cancel_task are generated in the same manner.
}
```


## Advanced Features

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

## License

Please refer to the LICENSE file in the project root directory.
Please refer to the LICENSE file in the project root directory.
15 changes: 15 additions & 0 deletions crates/rmcp-macros/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ mod common;
mod prompt;
mod prompt_handler;
mod prompt_router;
mod task_handler;
mod tool;
mod tool_handler;
mod tool_router;
Expand DownExpand Up@@ -263,3 +264,17 @@ pub fn prompt_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
.unwrap_or_else(|err| err.to_compile_error())
.into()
}

/// # task_handler
///
/// Generates basic task-handling methods (`enqueue_task` and `list_tasks`) for a server handler
/// using a shared \[`OperationProcessor`\]. The default processor expression assumes a
/// `self.processor` field holding an `Arc<Mutex<OperationProcessor>>`, but it can be customized
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
Comment on lines +273 to +274

CopilotAIDec 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The macro requires the handler type to implement Clone (line 82) to spawn the task, but this requirement is not documented in the macro's documentation comment. Users will encounter confusing compiler errors if their handler doesn't implement Clone. The documentation at lines 268-274 should explicitly state that the handler must implement Clone.

Suggested change
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
/// via `#[task_handler(processor = ...)]`.
///
/// **Requirements:** The handler type must implement [`Clone`], as the macro captures `self` inside spawned futures.

Copilot uses AI. Check for mistakes.
#[proc_macro_attribute]
pub fn task_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
task_handler::task_handler(attr.into(), input.into())
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat(task): add task support (SEP-1686) by jokemanfire · Pull Request #536 · modelcontextprotocol/rust-sdk · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 56 additions & 9 deletions crates/rmcp-macros/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,10 @@

This library primarily provides the following macros:

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

## Usage

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

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

#### Usage
#### Tool attributes

| field | type | usage |
| :- | :- | :- |
Expand All@@ -25,7 +28,7 @@ This will generate a function that return the attribute of this tool, with type
| `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>` |
| `annotations` | `ToolAnnotationsAttribute` | Additional tool information. Defaults to `None`. |

#### Example
#### Tool example

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

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.

#### Usage
#### Router attributes

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

#### Example
#### Router example

```rust
#[tool_router]
Expand DownExpand Up@@ -104,13 +107,14 @@ impl MyToolHandler {

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

#### Usage
#### Handler attributes

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

#### Example
#### Handler example

```rust
#[tool_handler]
impl ServerHandler for MyToolHandler {
Expand All@@ -119,15 +123,18 @@ impl ServerHandler for MyToolHandler {
```

or using a custom router expression:

```rust
#[tool_handler(router = self.get_router().await)]
impl ServerHandler for MyToolHandler {
// ...implement other handler
}
```

#### Explained
#### Handler expansion

This macro will be expended to something like this:

```rust
impl ServerHandler for MyToolHandler {
async fn call_tool(
Expand All@@ -150,6 +157,46 @@ impl ServerHandler for MyToolHandler {
}
```

### task_handler

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.

#### Task handler attributes

| field | type | usage |
| :- | :- | :- |
| `processor` | `Expr` | Expression that yields an `Arc<dyn OperationProcessor>` (or compatible trait object). Defaults to `self.processor.clone()`. |

#### Task handler example

```rust
#[derive(Clone)]
pub struct TaskHandler {
processor: Arc<dyn OperationProcessor<RoleServer> + Send + Sync>,
}

#[task_handler(processor = self.processor.clone())]
impl ServerHandler for TaskHandler {}
```

#### Task handler expansion

At expansion time the macro implements the task-specific handler methods by forwarding to the processor expression, roughly equivalent to:

```rust
impl ServerHandler for TaskHandler {
async fn list_tasks(&self, request: TaskListRequest, ctx: RequestContext<RoleServer>) -> Result<TaskListResult, rmcp::ErrorData> {
self.processor.list_tasks(request, ctx).await
}

async fn enqueue_task(&self, request: TaskEnqueueRequest, ctx: RequestContext<RoleServer>) -> Result<TaskEnqueueResult, rmcp::ErrorData> {
self.processor.enqueue_task(request, ctx).await
}

// get_task and cancel_task are generated in the same manner.
}
```


## Advanced Features

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

## License

Please refer to the LICENSE file in the project root directory.
Please refer to the LICENSE file in the project root directory.
15 changes: 15 additions & 0 deletions crates/rmcp-macros/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ mod common;
mod prompt;
mod prompt_handler;
mod prompt_router;
mod task_handler;
mod tool;
mod tool_handler;
mod tool_router;
Expand DownExpand Up@@ -263,3 +264,17 @@ pub fn prompt_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
.unwrap_or_else(|err| err.to_compile_error())
.into()
}

/// # task_handler
///
/// Generates basic task-handling methods (`enqueue_task` and `list_tasks`) for a server handler
/// using a shared \[`OperationProcessor`\]. The default processor expression assumes a
/// `self.processor` field holding an `Arc<Mutex<OperationProcessor>>`, but it can be customized
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
Comment on lines +273 to +274

CopilotAIDec 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The macro requires the handler type to implement Clone (line 82) to spawn the task, but this requirement is not documented in the macro's documentation comment. Users will encounter confusing compiler errors if their handler doesn't implement Clone. The documentation at lines 268-274 should explicitly state that the handler must implement Clone.

Suggested change
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
/// via `#[task_handler(processor = ...)]`.
///
/// **Requirements:** The handler type must implement [`Clone`], as the macro captures `self` inside spawned futures.

Copilot uses AI. Check for mistakes.
#[proc_macro_attribute]
pub fn task_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
task_handler::task_handler(attr.into(), input.into())
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(task): add task support (SEP-1686) by jokemanfire · Pull Request #536 · modelcontextprotocol/rust-sdk · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 56 additions & 9 deletions crates/rmcp-macros/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,10 @@

This library primarily provides the following macros:

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

## Usage

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

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

#### Usage
#### Tool attributes

| field | type | usage |
| :- | :- | :- |
Expand All@@ -25,7 +28,7 @@ This will generate a function that return the attribute of this tool, with type
| `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>` |
| `annotations` | `ToolAnnotationsAttribute` | Additional tool information. Defaults to `None`. |

#### Example
#### Tool example

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

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.

#### Usage
#### Router attributes

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

#### Example
#### Router example

```rust
#[tool_router]
Expand DownExpand Up@@ -104,13 +107,14 @@ impl MyToolHandler {

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

#### Usage
#### Handler attributes

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

#### Example
#### Handler example

```rust
#[tool_handler]
impl ServerHandler for MyToolHandler {
Expand All@@ -119,15 +123,18 @@ impl ServerHandler for MyToolHandler {
```

or using a custom router expression:

```rust
#[tool_handler(router = self.get_router().await)]
impl ServerHandler for MyToolHandler {
// ...implement other handler
}
```

#### Explained
#### Handler expansion

This macro will be expended to something like this:

```rust
impl ServerHandler for MyToolHandler {
async fn call_tool(
Expand All@@ -150,6 +157,46 @@ impl ServerHandler for MyToolHandler {
}
```

### task_handler

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.

#### Task handler attributes

| field | type | usage |
| :- | :- | :- |
| `processor` | `Expr` | Expression that yields an `Arc<dyn OperationProcessor>` (or compatible trait object). Defaults to `self.processor.clone()`. |

#### Task handler example

```rust
#[derive(Clone)]
pub struct TaskHandler {
processor: Arc<dyn OperationProcessor<RoleServer> + Send + Sync>,
}

#[task_handler(processor = self.processor.clone())]
impl ServerHandler for TaskHandler {}
```

#### Task handler expansion

At expansion time the macro implements the task-specific handler methods by forwarding to the processor expression, roughly equivalent to:

```rust
impl ServerHandler for TaskHandler {
async fn list_tasks(&self, request: TaskListRequest, ctx: RequestContext<RoleServer>) -> Result<TaskListResult, rmcp::ErrorData> {
self.processor.list_tasks(request, ctx).await
}

async fn enqueue_task(&self, request: TaskEnqueueRequest, ctx: RequestContext<RoleServer>) -> Result<TaskEnqueueResult, rmcp::ErrorData> {
self.processor.enqueue_task(request, ctx).await
}

// get_task and cancel_task are generated in the same manner.
}
```


## Advanced Features

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

## License

Please refer to the LICENSE file in the project root directory.
Please refer to the LICENSE file in the project root directory.
15 changes: 15 additions & 0 deletions crates/rmcp-macros/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ mod common;
mod prompt;
mod prompt_handler;
mod prompt_router;
mod task_handler;
mod tool;
mod tool_handler;
mod tool_router;
Expand DownExpand Up@@ -263,3 +264,17 @@ pub fn prompt_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
.unwrap_or_else(|err| err.to_compile_error())
.into()
}

/// # task_handler
///
/// Generates basic task-handling methods (`enqueue_task` and `list_tasks`) for a server handler
/// using a shared \[`OperationProcessor`\]. The default processor expression assumes a
/// `self.processor` field holding an `Arc<Mutex<OperationProcessor>>`, but it can be customized
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
Comment on lines +273 to +274

CopilotAIDec 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The macro requires the handler type to implement Clone (line 82) to spawn the task, but this requirement is not documented in the macro's documentation comment. Users will encounter confusing compiler errors if their handler doesn't implement Clone. The documentation at lines 268-274 should explicitly state that the handler must implement Clone.

Suggested change
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
/// via `#[task_handler(processor = ...)]`.
///
/// **Requirements:** The handler type must implement [`Clone`], as the macro captures `self` inside spawned futures.

Copilot uses AI. Check for mistakes.
#[proc_macro_attribute]
pub fn task_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
task_handler::task_handler(attr.into(), input.into())
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(task): add task support (SEP-1686) by jokemanfire · Pull Request #536 · modelcontextprotocol/rust-sdk · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 56 additions & 9 deletions crates/rmcp-macros/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,10 @@

This library primarily provides the following macros:

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

## Usage

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

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

#### Usage
#### Tool attributes

| field | type | usage |
| :- | :- | :- |
Expand All@@ -25,7 +28,7 @@ This will generate a function that return the attribute of this tool, with type
| `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>` |
| `annotations` | `ToolAnnotationsAttribute` | Additional tool information. Defaults to `None`. |

#### Example
#### Tool example

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

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.

#### Usage
#### Router attributes

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

#### Example
#### Router example

```rust
#[tool_router]
Expand DownExpand Up@@ -104,13 +107,14 @@ impl MyToolHandler {

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

#### Usage
#### Handler attributes

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

#### Example
#### Handler example

```rust
#[tool_handler]
impl ServerHandler for MyToolHandler {
Expand All@@ -119,15 +123,18 @@ impl ServerHandler for MyToolHandler {
```

or using a custom router expression:

```rust
#[tool_handler(router = self.get_router().await)]
impl ServerHandler for MyToolHandler {
// ...implement other handler
}
```

#### Explained
#### Handler expansion

This macro will be expended to something like this:

```rust
impl ServerHandler for MyToolHandler {
async fn call_tool(
Expand All@@ -150,6 +157,46 @@ impl ServerHandler for MyToolHandler {
}
```

### task_handler

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.

#### Task handler attributes

| field | type | usage |
| :- | :- | :- |
| `processor` | `Expr` | Expression that yields an `Arc<dyn OperationProcessor>` (or compatible trait object). Defaults to `self.processor.clone()`. |

#### Task handler example

```rust
#[derive(Clone)]
pub struct TaskHandler {
processor: Arc<dyn OperationProcessor<RoleServer> + Send + Sync>,
}

#[task_handler(processor = self.processor.clone())]
impl ServerHandler for TaskHandler {}
```

#### Task handler expansion

At expansion time the macro implements the task-specific handler methods by forwarding to the processor expression, roughly equivalent to:

```rust
impl ServerHandler for TaskHandler {
async fn list_tasks(&self, request: TaskListRequest, ctx: RequestContext<RoleServer>) -> Result<TaskListResult, rmcp::ErrorData> {
self.processor.list_tasks(request, ctx).await
}

async fn enqueue_task(&self, request: TaskEnqueueRequest, ctx: RequestContext<RoleServer>) -> Result<TaskEnqueueResult, rmcp::ErrorData> {
self.processor.enqueue_task(request, ctx).await
}

// get_task and cancel_task are generated in the same manner.
}
```


## Advanced Features

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

## License

Please refer to the LICENSE file in the project root directory.
Please refer to the LICENSE file in the project root directory.
15 changes: 15 additions & 0 deletions crates/rmcp-macros/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ mod common;
mod prompt;
mod prompt_handler;
mod prompt_router;
mod task_handler;
mod tool;
mod tool_handler;
mod tool_router;
Expand DownExpand Up@@ -263,3 +264,17 @@ pub fn prompt_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
.unwrap_or_else(|err| err.to_compile_error())
.into()
}

/// # task_handler
///
/// Generates basic task-handling methods (`enqueue_task` and `list_tasks`) for a server handler
/// using a shared \[`OperationProcessor`\]. The default processor expression assumes a
/// `self.processor` field holding an `Arc<Mutex<OperationProcessor>>`, but it can be customized
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
Comment on lines +273 to +274

CopilotAIDec 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The macro requires the handler type to implement Clone (line 82) to spawn the task, but this requirement is not documented in the macro's documentation comment. Users will encounter confusing compiler errors if their handler doesn't implement Clone. The documentation at lines 268-274 should explicitly state that the handler must implement Clone.

Suggested change
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
/// via `#[task_handler(processor = ...)]`.
///
/// **Requirements:** The handler type must implement [`Clone`], as the macro captures `self` inside spawned futures.

Copilot uses AI. Check for mistakes.
#[proc_macro_attribute]
pub fn task_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
task_handler::task_handler(attr.into(), input.into())
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat(task): add task support (SEP-1686) by jokemanfire · Pull Request #536 · modelcontextprotocol/rust-sdk · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 56 additions & 9 deletions crates/rmcp-macros/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,10 @@

This library primarily provides the following macros:

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

## Usage

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

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

#### Usage
#### Tool attributes

| field | type | usage |
| :- | :- | :- |
Expand All@@ -25,7 +28,7 @@ This will generate a function that return the attribute of this tool, with type
| `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>` |
| `annotations` | `ToolAnnotationsAttribute` | Additional tool information. Defaults to `None`. |

#### Example
#### Tool example

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

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.

#### Usage
#### Router attributes

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

#### Example
#### Router example

```rust
#[tool_router]
Expand DownExpand Up@@ -104,13 +107,14 @@ impl MyToolHandler {

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

#### Usage
#### Handler attributes

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

#### Example
#### Handler example

```rust
#[tool_handler]
impl ServerHandler for MyToolHandler {
Expand All@@ -119,15 +123,18 @@ impl ServerHandler for MyToolHandler {
```

or using a custom router expression:

```rust
#[tool_handler(router = self.get_router().await)]
impl ServerHandler for MyToolHandler {
// ...implement other handler
}
```

#### Explained
#### Handler expansion

This macro will be expended to something like this:

```rust
impl ServerHandler for MyToolHandler {
async fn call_tool(
Expand All@@ -150,6 +157,46 @@ impl ServerHandler for MyToolHandler {
}
```

### task_handler

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.

#### Task handler attributes

| field | type | usage |
| :- | :- | :- |
| `processor` | `Expr` | Expression that yields an `Arc<dyn OperationProcessor>` (or compatible trait object). Defaults to `self.processor.clone()`. |

#### Task handler example

```rust
#[derive(Clone)]
pub struct TaskHandler {
processor: Arc<dyn OperationProcessor<RoleServer> + Send + Sync>,
}

#[task_handler(processor = self.processor.clone())]
impl ServerHandler for TaskHandler {}
```

#### Task handler expansion

At expansion time the macro implements the task-specific handler methods by forwarding to the processor expression, roughly equivalent to:

```rust
impl ServerHandler for TaskHandler {
async fn list_tasks(&self, request: TaskListRequest, ctx: RequestContext<RoleServer>) -> Result<TaskListResult, rmcp::ErrorData> {
self.processor.list_tasks(request, ctx).await
}

async fn enqueue_task(&self, request: TaskEnqueueRequest, ctx: RequestContext<RoleServer>) -> Result<TaskEnqueueResult, rmcp::ErrorData> {
self.processor.enqueue_task(request, ctx).await
}

// get_task and cancel_task are generated in the same manner.
}
```


## Advanced Features

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

## License

Please refer to the LICENSE file in the project root directory.
Please refer to the LICENSE file in the project root directory.
15 changes: 15 additions & 0 deletions crates/rmcp-macros/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ mod common;
mod prompt;
mod prompt_handler;
mod prompt_router;
mod task_handler;
mod tool;
mod tool_handler;
mod tool_router;
Expand DownExpand Up@@ -263,3 +264,17 @@ pub fn prompt_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
.unwrap_or_else(|err| err.to_compile_error())
.into()
}

/// # task_handler
///
/// Generates basic task-handling methods (`enqueue_task` and `list_tasks`) for a server handler
/// using a shared \[`OperationProcessor`\]. The default processor expression assumes a
/// `self.processor` field holding an `Arc<Mutex<OperationProcessor>>`, but it can be customized
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
Comment on lines +273 to +274

CopilotAIDec 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The macro requires the handler type to implement Clone (line 82) to spawn the task, but this requirement is not documented in the macro's documentation comment. Users will encounter confusing compiler errors if their handler doesn't implement Clone. The documentation at lines 268-274 should explicitly state that the handler must implement Clone.

Suggested change
/// via `#[task_handler(processor = ...)]`. Because the macro captures `self` inside spawned
/// futures, the handler type must implement [`Clone`].
/// via `#[task_handler(processor = ...)]`.
///
/// **Requirements:** The handler type must implement [`Clone`], as the macro captures `self` inside spawned futures.

Copilot uses AI. Check for mistakes.
#[proc_macro_attribute]
pub fn task_handler(attr: TokenStream, input: TokenStream) -> TokenStream {
task_handler::task_handler(attr.into(), input.into())
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
Loading