Repository navigation
StreamableHttpClientTransport has 10x higher latency on subsequent calls vs Python SDK #789
Copy link
Copy link
Closed
Labels
P1High: significant functionality gap or spec violationHigh: significant functionality gap or spec violationT-transportTransport layer changesTransport layer changesbugSomething is not workingSomething is not working
Description
Describe the bug
StreamableHttpClientTransport in rmcp v1.3.0 exhibits significantly higher latency on subsequent tool calls compared to the Python MCP SDK client. The first call is fast (~2ms), but every subsequent call takes ~42ms — roughly 10x slower than Python's subsequent calls (~3ms). The degradation is consistent and repro across all subsequent calls.
| Client | Call 1 | Call 2 | Call 3 | Call 4 | Call 5 |
|---|---|---|---|---|---|
| Rust (rmcp) | 1.7ms | 41.2ms | 42.0ms | 42.9ms | 41.9ms |
| Python | 12.0ms | 3.0ms | 2.9ms | 3.3ms | 3.2ms |
To Reproduce
- Start the
servers_counter_streamhttpserver fromrmcp v1.3.0. - Run the Rust client:
use rmcp::transport::StreamableHttpClientTransport; use rmcp::{ServiceExt, model::*}; use std::time::Instant; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let transport = StreamableHttpClientTransport::from_uri("http://localhost:8000/mcp"); let client = ClientInfo::default().serve(transport).await?; for i in 1..=5 { let start = Instant::now(); let _res = client.call_tool(CallToolRequestParams::new("say_hello")).await.unwrap(); println!("Call {}: {:.1}ms", i, start.elapsed().as_secs_f32() * 1000.0); } client.cancel().await?; Ok(()) }
- Run the equivalent Python client:
import asyncio, time from mcp import ClientSession from mcp.client.streamable_http import streamablehttp_client async def main(): async with streamablehttp_client(url="http://localhost:8000/mcp") as (read, write, _): async with ClientSession(read, write) as session: await session.initialize() for i in range(1, 6): start = time.perf_counter() await session.call_tool("say_hello", arguments={}) print(f"Call {i}: {(time.perf_counter() - start) * 1000:.1f}ms") asyncio.run(main())
Expected behavior
Subsequent tool calls should have similar or lower latency than the first call, as the session is already established. The Python client demonstrates this pattern (12ms → 3ms). The Rust client should not regress to ~42ms on every call after the first.
Logs
Rust client output:
Call 1: 1.7ms
Call 2: 41.2ms
Call 3: 42.0ms
Call 4: 42.9ms
Call 5: 41.9ms
Python client output:
Call 1: 12.0ms
Call 2: 3.0ms
Call 3: 2.9ms
Call 4: 3.3ms
Call 5: 3.2ms
Session termination failed: 202
Additional context
rmcpversion:1.3.0- Rust edition:
2024 - Server:
servers_counter_streamhttpfromrmcp v1.3.0samples - The issue is consistent across all post-initialization calls (not just a one-time spike)
- Fedora 43, x86_64
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
P1High: significant functionality gap or spec violationHigh: significant functionality gap or spec violationT-transportTransport layer changesTransport layer changesbugSomething is not workingSomething is not working
Activity
sco3 commentedon Apr 3, 2026
The possible workaround is to set tcp no delay flag:
https://docs.rs/axum/latest/axum/serve/trait.ListenerExt.html#method.tap_io
This is workaround on server side and the issue is found on client side.
sco3 commentedon Apr 3, 2026
The additional sleep between calls improves the latency:
sco3 commentedon Apr 5, 2026
Workaround: Using a custom reqwest client with connection pooling disabled (
pool_max_idle_per_host(0)) resolves the issue, bringing subsequent calls down to ~0.4ms.Custom Client
One can build a custom client with:
https://github.com/sco3/mcp-rust-sdk-ticket/blob/main/src/main.rs
fix(http): disable idle connection pooling for default client
glicht commentedon Apr 7, 2026
@sco3 this is an interesting report.
I ran your examples and I am seeing different results. It looks to me like this may be environment specific. I am running on a macbook air and getting the following results:
rmcp with connection pooling (default)
rmcp with custom client (no connection pooling)
python (3.13)
@DaleSeo FYI
dima-zakharov commentedon Apr 7, 2026
Yes it is interesting, I need to check on different environments as well, maybe github workflow can help.
sco3 commentedon Apr 7, 2026
Well the action shows the following results:
The full logs are attached here:
logs_63637729286.zip
sco3 commentedon Apr 7, 2026
The workflow file (gzipped):
benchmarks.yml.gz
sco3 commentedon Apr 7, 2026
I understand it may be environmental, but couple of my laptops and now the github environment show similar results.
I updated the sample repo with pyproject.toml, pinned python version to 3.13 and created workflow to run the tests on github vm.
sco3 commentedon Apr 7, 2026
I can confirm that on
macos-14github vm the results are similar to @glicht provided. So the issue is environmental.The
logs_63643190095.zip
sco3 commentedon Apr 7, 2026
The check in workflow:
dima-zakharov commentedon Apr 8, 2026
The problem still related to mcp sdk it gives inconsistent results on different platform.
DaleSeo commentedon Apr 10, 2026
Thanks for reporting this, @sco3. I've opened #790 to fix this. Could you benchmark it against your setup on Fedora to confirm the fix works? You can test by pointing your
Cargo.tomlat the PR branch: