#[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{..}))
Mantenedores costumam responder em até 2 dias
Ninguém assumiu esta issue ainda.
Avaliação
- Dificuldade
- 4/5
- Tempo estimado
- 3-5 dias
- Facilidade para iniciantes
- 48/100
Direção de pesquisa
Comece com a reprodução mínima em src/main.rs e execute cargo check para confirmar o erro de #[tool]. Em seguida, inspecione o comportamento do dispatch gerado descrito na issue e como a macro de ferramenta impõe seu bound de ordem superior. A tarefa estará concluída quando o padrão de stream assíncrono compilar por meio de #[tool] ou quando a limitação estiver documentada no local onde os usuários se depararão com ela.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
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.
- Linguagem predominante
- Rust
- Estrelas
- 4k
- Forks
- 688
- Merge médio
- 4d 10h
- PRs com merge (30d)
- 37
Preparar o ambiente
Inicia o contêiner de desenvolvimento do projeto no navegador, com a sua própria conta do GitHub.
- Sem Dockerfile nem arquivo Docker Compose
- Sem modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de modelcontextprotocol/rust-sdk
-
auth: refresh_token() requests offline_access that was never granted, so refreshes fail with invalid_scopeTalvez já em andamento @jstar0 assumiu há 16 dias. Abertabug P1 ready for work T-security T-transport
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 65/100
modelcontextprotocol/rust-sdk#1330 ·
Mantenedores costumam responder em até 2 dias
-
docs: ClientHandler list_changed callbacks don't mention Peer::listen (2026-07-28)Talvez já em andamento @DaleSeo assumiu há 1 dia. Abertaenhancement P2 ready for work T-documentation
modelcontextprotocol/rust-sdk#1394 · 1 comentário · 1 responsável ·
Mantenedores costumam responder em até 2 dias
-
Support the Skills extension (SEP-2640)Talvez já em andamento @DaleSeo assumiu há 1 dia. Abertaenhancement P2 ready for work T-enhancement T-handler T-model T-service
modelcontextprotocol/rust-sdk#1391 · 1 comentário · 1 responsável ·
Mantenedores costumam responder em até 2 dias
-
reqwest Streamable HTTP client matches response Content-Type by case-sensitive prefixTalvez já em andamento @DaleSeo assumiu há 2 dias. Abertabug P1 ready for work T-transport
modelcontextprotocol/rust-sdk#1389 · 1 comentário · 1 responsável ·
Mantenedores costumam responder em até 2 dias
-
Streamable HTTP server: Accept and Content-Type are matched case-sensitively and by substring/prefixTalvez já em andamento @DaleSeo assumiu há 4 dias. Abertabug P1 ready for work T-transport
modelcontextprotocol/rust-sdk#1381 · 1 comentário · 1 responsável ·
Mantenedores costumam responder em até 2 dias
Todas as issues de modelcontextprotocol/rust-sdk
Issues semelhantes
-
bug easy good first issue
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 85/100
lacs-project/sysknife#659 ·
Mantenedores costumam responder em até 2 dias
-
Document multi-value claims with rowBoundaries operator:in (and their per-record application)Abertadocumentation
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 70/100
registrystack/registry-stack#2431 ·
Mantenedores costumam responder em até 1 dia
-
contribution
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 85/100
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 85/100
farion1231/cc-switch#8863 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
dioxus-desktop on macOS: Finder file drops never fire `ondrop` — `dragenter` is not cancelledAberta
Dificuldade 1/5 Menos de uma hora Facilidade para iniciantes 85/100
DioxusLabs/dioxus#6006 ·
Mantenedores costumam responder em até 1 dia