-
Notifications
You must be signed in to change notification settings - Fork 222
refactor: refactor tool macros and router implementation #261
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR refactors the tool macros and router implementation to improve intuitiveness, composability, and generic handler support while removing the static router. Key changes include updating the macros (tool, tool_router, and tool_handler) and modifying test code to use the new API (e.g. using Calculator::new() and Parameters for function arguments).
Reviewed Changes
Copilot reviewed 32 out of 32 changed files in this pull request and generated 1 comment.
Show a summary per file
File | Description |
---|---|
crates/rmcp/tests/test_with_js.rs | Changed instantiation of Calculator using Calculator::new(). |
crates/rmcp/tests/test_tool_routers.rs | Added new router functions (test_router_1, test_router_2) and updated tool macros usage. |
crates/rmcp/tests/test_tool_macros.rs | Updated tool macros usage in tests, including changes in function parameter patterns and router setup. |
crates/rmcp/src/* | Refactored tool_router integration and related macro-generated code; added helper functions (e.g. with_all_items) in model. |
crates/rmcp-macros/* | Updated macro implementations for tool, tool_router, and tool_handler to support the new API style. |
Comments suppressed due to low confidence (1)
crates/rmcp/tests/test_tool_macros.rs:110
- [nitpick] The naming of generated tool attribute functions (e.g. get_weather_tool_attr) and their usage in tests should be reviewed for consistency. Ensure that the naming conventions across the tool macros and their invocations remain uniform to avoid confusion.
server.get_weather(Parameters(GetWeatherRequest { city: "Harbin".into(), date: "Yesterday".into() }))
@@ -382,174 +312,118 @@ macro_rules! impl_for { | |||
impl_for!([$($Tn)* $Tn_1] [$($Rest)*]); | |||
}; | |||
(@impl $($Tn: ident)*) => { | |||
impl<'s, $($Tn,)* S, F, Fut, R> CallToolHandler<'s, S, AsyncAdapter<($($Tn,)*), Fut, R>> for F | |||
impl<$($Tn,)* S, F, R> CallToolHandler<S, AsyncMethodAdapter<($($Tn,)*), R>> for F |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The adapter implementations for CallToolHandler (including AsyncMethodAdapter and SyncAdapter) are quite complex. Consider refactoring the inner logic or adding inline comments to clarify the lifetime conversions and function adapter patterns for future maintainers.
Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take it easy
@jokemanfire It's ready for review now |
…an up unused code in server handler
We can collect docs like this: // extract doc line from attribute
fn extract_doc_line(existing_docs: Option<String>, attr: &syn::Attribute) -> Option<String> {
if !attr.path().is_ident("doc") {
return None;
}
let syn::Meta::NameValue(name_value) = &attr.meta else {
return None;
};
let syn::Expr::Lit(expr_lit) = &name_value.value else {
return None;
};
let syn::Lit::Str(lit_str) = &expr_lit.lit else {
return None;
};
let content = lit_str.value().trim().to_string();
match (existing_docs, content) {
(Some(mut existing_docs), content) if !content.is_empty() => {
existing_docs.push('\n');
existing_docs.push_str(&content);
Some(existing_docs)
}
(Some(existing_docs), _) => Some(existing_docs),
(None, content) if !content.is_empty() => Some(content),
_ => None,
}
} and fn_item.attrs.iter().fold(None, extract_doc_line) And check again please. |
I will give a feedback tonight. |
@@ -26,7 +26,7 @@ fn calculator(&self, #[tool(param)] a: i32, #[tool(param)] b: i32) -> Result<Cal | |||
Use on an impl block to automatically register multiple tools: | |||
|
|||
```rust ignore | |||
#[tool(tool_box)] | |||
#[tool_router] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like tool_router 's expand is in func 'new' , The struct must have 'ToolRouter', I think it should be write in this readme
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not always, a static router is also ok. I will add more examples.
_request: Option<rmcp::model::PaginatedRequestParam>, | ||
_context: rmcp::service::RequestContext<rmcp::RoleServer>, | ||
) -> Result<ListToolsResult, rmcp::Error> { | ||
let items = self.tool_router.list_all(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The marco 'tool_handler' will expand the call_tool and list_tools func , why we need clarify it again?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will use #[tool_handler]
as much as possible, but remain one place to explain what it looks like after expanded
The tool_handler marco looks like not really using? |
Motivation and Context
Refactor tool macros:
How Has This Been Tested?
Breaking Changes
Now we need to use a combination of three macros:
tool
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
name
String
description
String
input_schema
Expr
Parameters<T>
annotations
ToolAnnotationsAttribute
None
.Example
tool_router
This macro is used to generate a tool router based on functions marked with
#[rmcp::tool]
in an implementation block.It creates a function that returns a
ToolRouter
instance.Usage
router
Ident
tool_router
.vis
Visibility
Example
Or specify the visibility and router name:
tool_handler
This macro will generate the handler for
tool_call
andlist_tools
methods in the implementation block, by using an existingToolRouter
instance.Usage
router
Expr
ToolRouter
instance. Defaults toself.tool_router
.Example
or using a custom router expression:
Types of changes
Checklist
Additional context
This also can be a template for possible prompt handler in the future. And also can be a base of implementation of
#[derive(ServerHandler)]