Skip to content

Commit 55876cb

Browse files
committed
feat: add local feature for !Send tool handler support
1 parent 656a09a commit 55876cb

29 files changed

Lines changed: 306 additions & 180 deletions

crates/rmcp-macros/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@ serde_json = "1.0"
2323
darling = { version = "0.23" }
2424

2525
[features]
26+
local = []
27+
2628
[dev-dependencies]

crates/rmcp-macros/src/tool.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ pub struct ToolAttribute {
9595
pub icons: Option<Expr>,
9696
/// Optional metadata for the tool
9797
pub meta: Option<Expr>,
98+
/// When true, the generated future will not require `Send`. Useful for `!Send` handlers
99+
/// (e.g. single-threaded database connections). Also enabled globally by the `local` crate feature.
100+
pub local: bool,
98101
}
99102

100103
#[derive(FromMeta, Debug, Default)]
@@ -333,7 +336,9 @@ pub fn tool(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
333336
if fn_item.sig.asyncness.is_some() {
334337
// 1. remove asyncness from sig
335338
// 2. make return type: `std::pin::Pin<Box<dyn std::future::Future<Output = #ReturnType> + Send + '_>>`
339+
// (omit `+ Send` when the `local` crate feature is active or `#[tool(local)]` is used)
336340
// 3. make body: { Box::pin(async move { #body }) }
341+
let omit_send = cfg!(feature = "local") || attribute.local;
337342
let new_output = syn::parse2::<ReturnType>({
338343
let mut lt = quote! { 'static };
339344
if let Some(receiver) = fn_item.sig.receiver() {
@@ -347,10 +352,18 @@ pub fn tool(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
347352
}
348353
match &fn_item.sig.output {
349354
syn::ReturnType::Default => {
350-
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = ()> + Send + #lt>> }
355+
if omit_send {
356+
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = ()> + #lt>> }
357+
} else {
358+
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = ()> + Send + #lt>> }
359+
}
351360
}
352361
syn::ReturnType::Type(_, ty) => {
353-
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = #ty> + Send + #lt>> }
362+
if omit_send {
363+
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = #ty> + #lt>> }
364+
} else {
365+
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = #ty> + Send + #lt>> }
366+
}
354367
}
355368
}
356369
})?;

crates/rmcp/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ chrono = { version = "0.4.38", default-features = false, features = [
7878

7979
[features]
8080
default = ["base64", "macros", "server"]
81+
local = ["rmcp-macros?/local"]
8182
client = ["dep:tokio-stream"]
8283
server = ["transport-async-rw", "dep:schemars", "dep:pastey"]
8384
macros = ["dep:rmcp-macros", "dep:pastey"]

0 commit comments

Comments
 (0)