feat: add local feature for !Send tool handler support (#740)

* feat: add local feature for !Send tool handler support

* fix: gate streamable HTTP transport on not(local) feature
This commit is contained in:
Dale Seo 2026-03-11 17:22:56 -04:00 • committed by GitHub
commit 1a4a52a173
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -95,6 +95,9 @@ pub struct ToolAttribute {
pub icons: Option<Expr>,
/// Optional metadata for the tool
pub meta: Option<Expr>,
/// When true, the generated future will not require `Send`. Useful for `!Send` handlers
/// (e.g. single-threaded database connections). Also enabled globally by the `local` crate feature.
pub local: bool,
}
#[derive(FromMeta, Debug, Default)]
@ -333,7 +336,9 @@ pub fn tool(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
if fn_item.sig.asyncness.is_some() {
// 1. remove asyncness from sig
// 2. make return type: `std::pin::Pin<Box<dyn std::future::Future<Output = #ReturnType> + Send + '_>>`
// (omit `+ Send` when the `local` crate feature is active or `#[tool(local)]` is used)
// 3. make body: { Box::pin(async move { #body }) }
let omit_send = cfg!(feature = "local") || attribute.local;
let new_output = syn::parse2::<ReturnType>({
let mut lt = quote! { 'static };
if let Some(receiver) = fn_item.sig.receiver() {
@ -347,10 +352,18 @@ pub fn tool(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
}
match &fn_item.sig.output {
syn::ReturnType::Default => {
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = ()> + Send + #lt>> }
if omit_send {
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = ()> + #lt>> }
} else {
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = ()> + Send + #lt>> }
}
}
syn::ReturnType::Type(_, ty) => {
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = #ty> + Send + #lt>> }
if omit_send {
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = #ty> + #lt>> }
} else {
quote! { -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = #ty> + Send + #lt>> }
}
}
}
})?;