#[tool] async method fails to compile (FnOnce not general enough) when its call tree uses |x: &T| async move {} closures (e.g. stream::iter(..).map(|x| async move{..}))
Les mainteneurs répondent en général sous 2 jours
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Accessibilité débutants
- 48/100
Piste de recherche
Commencez par la reproduction minimale dans src/main.rs et exécutez cargo check pour confirmer l’erreur #[tool]. Examinez ensuite le comportement du dispatch généré décrit dans l’issue et la manière dont la macro d’outil impose sa contrainte de rang supérieur. C’est terminé lorsque le modèle de flux asynchrone compile via #[tool] ou que la limitation est documentée à l’endroit où les utilisateurs la rencontreront.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
Summary
An async #[tool] method fails to compile with a higher-ranked-trait-bound error if the transitive call tree of the tool method contains a closure |x: &T| async move { .. } — most commonly futures::stream::iter(coll.iter()).map(|x| async move { .. }).buffer_unordered(..). The same code compiles fine outside the #[tool] macro.
Environment
- rmcp 3.0.1 (crates.io)
- rustc 1.94.0 stable
- macOS (reproduced locally)
Minimal reproduction
Cargo.toml
[package]
name = "rmcp-async-repro"
version = "0.0.0"
edition = "2021"
[dependencies]
rmcp = { version = "3", features = ["server", "macros"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
futures = "0.3"
src/main.rs
use rmcp::{
handler::server::{router::tool::ToolRouter, wrapper::Parameters},
model::{CallToolResult, ServerCapabilities, ServerInfo},
schemars, tool, tool_handler, tool_router, ServerHandler,
};
use serde_json::Value;
#[derive(serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
struct Req {
msg: String,
}
#[derive(Clone, Default)]
struct S {
tool_router: ToolRouter<S>,
}
impl S {
async fn execute(&self, _name: &str, _args: &Value) -> CallToolResult {
use futures::stream::{self, StreamExt};
let ips = vec!["1.1.1.1".to_string()];
let _out: Vec<String> = stream::iter(ips.iter())
.map(|ip| async move { ip.to_string() }) // <-- trigger
.buffer_unordered(4)
.collect()
.await;
CallToolResult::default()
}
}
#[tool_router]
impl S {
#[tool(name = "echo_tool", description = "echo")]
async fn echo(&self, Parameters(req): Parameters<Req>) -> CallToolResult {
let args = serde_json::to_value(&req).unwrap_or_default();
self.execute("echo", &args).await
}
}
#[tool_handler]
impl ServerHandler for S {
fn get_info(&self) -> ServerInfo {
let mut info = ServerInfo::default();
info.capabilities = ServerCapabilities::builder().enable_tools().build();
info
}
}
fn main() {}
Error
error: implementation of `FnOnce` is not general enough
--> src/main.rs (originates in the `#[tool]` attribute macro)
= note: closure with signature `fn(&'0 String) -> {async block@src/main.rs:26:23: 26:33}`
must implement `FnOnce<(&'1 String,)>`, for any two lifetimes `'0` and `'1`...
= note: ...but it actually implements `FnOnce<(&String,)>`
Analysis
The #[tool] macro generates dispatch code carrying a higher-ranked trait bound. A |x: &T| async move { .. } closure (e.g. produced by futures::stream::iter(..).map(..)) cannot satisfy a for<'a> Fn(&'a T) -> Future<..>-style bound — the long-standing rustc "async closure + HRTB" limitation. The pattern compiles in ordinary code because it is never placed under such a bound; the macro forces it into one.
Removing the stream::iter(..).map(|x| async move{..}) from the tool's call tree (or replacing it with an async fn helper + .then(..), or owning the items before the stream) makes the #[tool] macro compile. Implementing ServerHandler manually (without #[tool]) also avoids it.
Workaround
- Refactor
|x: &T| async move { .. }in the tool's call tree (use anasync fnhelper +.then(..), or own items before the stream), or - Implement
ServerHandlermanually instead of using#[tool].
Ask
If #[tool] could avoid forcing the user's call tree into an HRTB that async-move closures can't satisfy (e.g. boxing the future / restructuring the generated handler), that would unblock a fairly common futures::stream pattern. At minimum, documenting the limitation would save significant diagnosis time — the error is very hard to map back to the trigger.
- Langage dominant
- Rust
- Étoiles
- 4k
- Forks
- 688
- Merge moyen
- 4 j 10 h
- PR mergées (30 j)
- 37
Préparer son environnement
Lance le conteneur de développement du projet dans votre navigateur, avec votre propre compte GitHub.
- Aucun Dockerfile ni fichier Docker Compose
- Aucun modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de modelcontextprotocol/rust-sdk
-
auth: refresh_token() requests offline_access that was never granted, so refreshes fail with invalid_scopePeut-être pris @jstar0 l’a pris il y a 17 jours. Ouvertebug P1 ready for work T-security T-transport
Difficulté 2/5 1-3 heures Accessibilité débutants 65/100
modelcontextprotocol/rust-sdk#1330 ·
Les mainteneurs répondent en général sous 2 jours
-
docs: ClientHandler list_changed callbacks don't mention Peer::listen (2026-07-28)Peut-être pris @DaleSeo l’a pris il y a 1 jour. Ouverteenhancement P2 ready for work T-documentation
modelcontextprotocol/rust-sdk#1394 · 1 commentaire · 1 personne assignée ·
Les mainteneurs répondent en général sous 2 jours
-
Support the Skills extension (SEP-2640)Peut-être pris @DaleSeo l’a pris il y a 2 jours. Ouverteenhancement P2 ready for work T-enhancement T-handler T-model T-service
modelcontextprotocol/rust-sdk#1391 · 1 commentaire · 1 personne assignée ·
Les mainteneurs répondent en général sous 2 jours
-
reqwest Streamable HTTP client matches response Content-Type by case-sensitive prefixPeut-être pris @DaleSeo l’a pris il y a 2 jours. Ouvertebug P1 ready for work T-transport
modelcontextprotocol/rust-sdk#1389 · 1 commentaire · 1 personne assignée ·
Les mainteneurs répondent en général sous 2 jours
-
Streamable HTTP server: Accept and Content-Type are matched case-sensitively and by substring/prefixPeut-être pris @DaleSeo l’a pris il y a 5 jours. Ouvertebug P1 ready for work T-transport
modelcontextprotocol/rust-sdk#1381 · 1 commentaire · 1 personne assignée ·
Les mainteneurs répondent en général sous 2 jours
Toutes les issues de modelcontextprotocol/rust-sdk
Issues similaires
-
units: No `#` in rustdoc headingOuverte
Difficulté 1/5 Moins d'une heure Accessibilité débutants 67/100
rust-bitcoin/rust-bitcoin#7089 · 1 commentaire · 1 réaction ·
Les mainteneurs répondent en général sous 2 jours
-
area/tools bug session-analysis
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
Les mainteneurs répondent en général sous 1 jour
-
[Bug]: Python OpenTelemetrySectionConfig has no file_sinksPeut-être à nouveau libre Une pull request pour cette issue a été fermée sans être fusionnée. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 76/100
NVIDIA/NeMo-Relay#1514 ·
Les mainteneurs répondent en général sous 1 jour
-
prompting proposal tool triaged
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
can1357/oh-my-pi#17041 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
README crane example out of dateOuverte
Difficulté 1/5 Moins d'une heure Accessibilité débutants 84/100
nix-community/fenix#262 ·