-
Notifications
You must be signed in to change notification settings - Fork 471
Description
Problem
The #[tool] macro currently requires all tool handler futures to be Send. This Send bound propagates virally through the entire async call chain — not just the handler itself, but every function it awaits transitively.
This creates a fundamental incompatibility with popular async libraries that return !Send futures:
sqlx::raw_sql()— returns a future holdingPhantomData<fn(&mut PgConnection)>which is!Send. Cannot be used in any function that's transitively called from a#[tool]handler.sqlxtransactions — transaction futures borrow&mut PgConnectionacross await points, making them!Sendin many patterns.- Other database libraries with similar patterns.
Concrete Example
// This works (sqlx::query returns Send future):
#[tool]
async fn my_tool(&self) -> Result<String> {
sqlx::query("SELECT 1").execute(&self.pool).await?;
Ok("done".into())
}
// This fails to compile (sqlx::raw_sql returns !Send future):
#[tool]
async fn my_tool(&self) -> Result<String> {
sqlx::raw_sql("CREATE TABLE IF NOT EXISTS ...").execute(&self.pool).await?;
Ok("done".into())
}The error propagates even through indirect calls:
error[E0277]: `(dyn Any + Send + 'static)` cannot be sent between threads safely
Current Workaround
We split SQL strings by ; and execute each statement via sqlx::query() individually. This requires writing a custom SQL statement parser to handle semicolons inside string literals, comments, and dollar-quoted strings — significant complexity for what should be a simple migration runner.
Proposed Solution
Add a #[tool(local)] attribute (or similar) that allows handlers to return !Send futures. Implementation options:
#[tool(local)]— usetokio::task::LocalSetfor this specific handler#[tool(spawn_blocking)]— run the handler inspawn_blockingcontext- Relax the Send bound globally — if the MCP server doesn't actually need to send handler futures across threads (e.g., if it uses a single-threaded runtime or
LocalSetinternally)
Environment
- rmcp version: 1.1.0
- sqlx version: 0.8+
- Rust: stable (edition 2024)