TL;DR
Each MCP tool call is at minimum five components stacked in series: model emits tool_use, transport carries it, the MCP server resolves and dispatches, the upstream API returns, the response is serialised back. On a healthy local stack the floor is roughly 180-260 ms; over the public internet to a hosted MCP server bridging a SaaS API, 500-1,200 ms is normal; bursty financial-data endpoints (pre-market quote storms, earnings windows) push p95 well past 2,500 ms. A 12-step research agent that averages 700 ms per tool call has spent 8.4 s on roundtrips alone before the model has reasoned about anything. Three architecture patterns reclaim most of it: batched tool calls that fold n queries into one (typical 3-5x reduction at the same hop count), co-located MCP servers that put the bridge on the same VPC or machine as both model and upstream API (typical 60-200 ms shaved per hop), and speculative pre-fetch for the deterministic tail of agent loops where the next tool call is predictable from the current one (typical 30-50% wall-clock reduction). Vet hosted MCP servers against a real workload using the Finance MCP Directory, and lock skill-level latency budgets through the Agent Skill Tester so a regression in an upstream API surfaces before it reaches production.
The roundtrip nobody costs out before shipping
Model Context Protocol (MCP) is the cleanest standardisation the agent ecosystem has shipped. Anthropic published the spec in late 2024; by early 2026 it is the de-facto integration surface for Claude, Cursor, Windsurf, and a growing number of OpenAI- and Google-backed clients via shims. The wire-level simplicity is the appeal: JSON-RPC over stdio or HTTP, a manifest of tool definitions, a uniform call-result loop. The cost of that simplicity, paid in latency, is rarely budgeted at design time.
Every MCP tool call traverses five hops on the happy path. The model emits a tool_use block, which the client parses and forwards over the MCP transport. The MCP server resolves the tool name, validates the arguments, and dispatches to the upstream resource, which is usually a HTTP API, a database, or a filesystem. The response returns through the same chain in reverse, gets serialised back as a tool_result block, and rejoins the model's context for the next reasoning step.
A well-tuned local MCP server bridging a local Postgres clocks 80-150 ms median. The same server bridging a public REST API five regions away clocks 600-1,400 ms. The dominant term is rarely what teams expect. Dispatch overhead inside the MCP server is usually below 5 ms; the upstream API roundtrip is 70-90% of total wall time on most public APIs. The Finance MCP Directory tracks observed median and p95 latency for the financial-data endpoints we have audited, broken out by operation class so you can budget against the operation you actually call, not a vendor-published headline number.
A worked latency budget for a 12-step research agent
Consider the agent shape that recurs across every finance research workflow: a loop that pulls financials, fetches news, retrieves transcripts, cross-checks prices, and emits a structured view. The cadence is rarely under eight tool calls; twelve is a common mid-point.
Step 1: list_filings(ticker) ~ 420 ms
Step 2: fetch_filing(accession_number) ~ 1,100 ms (large body)
Step 3: extract_financials(filing_id) ~ 380 ms
Step 4: fetch_transcript(quarter) ~ 1,250 ms (large body)
Step 5: extract_guidance(transcript_id) ~ 410 ms
Step 6: fetch_news(ticker, window=7d) ~ 680 ms
Step 7: classify_news(news_ids) ~ 520 ms
Step 8: fetch_consensus(ticker) ~ 640 ms
Step 9: fetch_options_chain(ticker) ~ 890 ms
Step 10: fetch_short_interest(ticker) ~ 720 ms
Step 11: compute_factor_view(inputs) ~ 340 ms
Step 12: write_view_note(view_id) ~ 410 ms
Total: ~ 7,760 ms
Tool-call wall time is 7.76 s. The model's reasoning between steps adds another 4-9 s on a Sonnet-class model with no thinking budget enabled. Total user-perceived response time is 12-17 s for a single agent run. At a research throughput target of 50 tickers per hour, a single sequential worker tops out at about 12 tickers per hour, requiring 4-5 parallel workers to hit the target. None of that arithmetic shows up in a launch plan that budgets only model inference cost.
The structural insight is that the model's reasoning is largely unparallelisable, but the tool calls are not. The patterns below attack the dominant term.
1 · Batched tool calls
The cheapest pattern. If a tool can take an array argument, design it to. The model emits one tool_use block carrying n inputs; the MCP server fans out to the upstream API once or in parallel; the result returns in a single tool_result. Hop count drops from n to 1; total wall time drops to roughly the slowest single call instead of the sum.
{
"name": "fetch_consensus_batch",
"input": {
"tickers": ["AAPL", "MSFT", "NVDA", "GOOG", "META"],
"fields": ["eps_consensus", "revenue_consensus", "next_eps_date"]
}
}
A non-batched implementation of the same workflow takes 5 × 640 ms = 3,200 ms. A batched implementation that fans out in parallel inside the MCP server (the upstream API supports five concurrent requests without rate-limit penalty) takes max(640 ms across 5 requests) ≈ 720 ms. Roughly 4.4x reduction at no extra model-side cost.
Where it breaks. The model has to know the batch shape exists at planning time. A naive multi-step plan ("get AAPL consensus, then think, then get MSFT consensus, then think") emits five separate tool calls regardless of what the server can handle. The fix is in the tool description: the schema makes batch the only signature, with array length 1 as the degenerate single-item case. The model picks up the pattern from the schema description on first use.
Where it shines. Read paths over a fixed list of identifiers. Tickers, transcript IDs, filing accession numbers, news article IDs. Anything where the model already has the n keys and is going to call n times anyway.
Where it does not apply. Iteratively-discovered workflows where each call's input depends on the previous call's output. Step 2 in the worked example above (fetch_filing depends on the accession number from step 1) cannot be batched without changing the data model.
2 · Co-located MCP servers
Network topology dominates median latency for any tool that bridges a remote API. A hosted MCP server in us-east-1 bridging a vendor API in eu-west-3 pays a 110 ms transatlantic floor on every call before any work happens. Move the MCP server to eu-west-3 (or the vendor API to us-east-1) and that floor disappears.
The cleaner version: when the workload runs on Cloudflare Workers, Lambda, or a Kubernetes pod, the MCP server can run as a sidecar in the same pod, an in-process binding, or a Worker-to-Worker subrequest. The transport stops being a network at all. Stdio transport drops to sub-millisecond; in-process drops to function-call overhead.
The decision rule is empirical. Measure the geometry of your stack: where the model lives (typically the provider's region), where the agent runs (your region), where the MCP server runs, where the upstream API runs. Each hop in series is additive; each hop that costs more than 30 ms is a candidate for collapse. A typical retrofit collapses 2-3 hops; observed savings of 60-200 ms per tool call are common, which on a 12-step agent compounds to 700-2,400 ms shaved off wall time.
Cost trade. Co-locating with the agent is always cheaper at runtime. Co-locating with the upstream API requires the API to be hostable (your own database, a self-hosted index) or proxiable without licensing trouble. The Finance MCP Directory tags each entry with whether the underlying API allows self-hosted MCP bridging or only a hosted-by-vendor model.
Failure mode. The temptation to deploy the MCP server in the same region as the model "to be close to the model." The model lives at the inference provider; you do not control its region. The model side of the call is a single hop into the provider regardless of MCP server location. What you control is the MCP server -> upstream API hop, and that is the one to optimise.
3 · Speculative pre-fetch
The most powerful pattern when applicable. In many agent loops, the (n+1)-th tool call is fully determined by the n-th tool call's response. The classic case is "fetch_filing -> always extract_financials"; a less obvious case is "fetch_news with window=7d -> always classify_news with the resulting IDs."
The MCP server can speculatively dispatch the deterministic next call as soon as the current call's result is in hand, before the model has reasoned about it. When the model emits the predicted tool_use, the result is already cached and returned in microseconds. When the model emits something else, the speculation cost is the upstream API call itself (paid in money but not in wall time, since the response was discarded).
Implementation pattern.
# inside MCP server, post-result hook
async def on_tool_result(call_id, tool_name, args, result):
next_pred = predict_next(tool_name, result)
if next_pred is not None:
# Fire-and-forget; result lives in cache keyed by predicted args
asyncio.create_task(
speculative_dispatch(next_pred.tool, next_pred.args)
)
The predict_next function is a small lookup table or learned classifier trained on agent traces. For deterministic tails (1.0 hit rate) the savings are 100% of one tool roundtrip per step. For probabilistic tails (0.7 hit rate) the savings are 70% of one tool roundtrip per step minus 30% wasted upstream cost.
Cost trade. Real money, real upstream rate-limit pressure. A tool that costs $0.01 per call and gets a 70% speculation hit rate now costs $0.0143 effective per call. On a 12-step agent with 4 deterministic tails, the effective cost increase is 0.4 × 30% = 12% extra spend for a 30-50% wall-clock reduction. The rate-limit risk is the bigger constraint; vendors that count speculative calls toward your quota will silently degrade your headroom. Guard with a feature flag and a per-tool budget cap.
Where it shines. Long agent runs (15+ steps) where wall time is the binding constraint and the agent shape is consistent across runs.
Where it backfires. Branchy agents where the next call depends on a model judgment that flips on a small feature. The speculation hit rate collapses; cost goes up without latency benefit.
Measuring before you optimise
The three patterns above are useless without a measurement surface. The minimum instrumentation per tool call is start_ts, end_ts, transport_hop_ms, server_dispatch_ms, upstream_api_ms, response_serialise_ms. The breakdown identifies which hop dominates; the optimisation choice follows. The Agent Skill Tester injects this instrumentation transparently around the MCP transport and emits a per-skill latency report against a fixture set, so a regression upstream surfaces against the same baseline used at design time.
A 4-engineer team that audited their finance research agent in late 2025 found the breakdown was not what they expected: the upstream API was 71% of wall time, MCP server dispatch was 4%, transport was 3%, model serialisation was 22%. The latter was a compatibility shim re-encoding tool results from the upstream JSON shape into a custom envelope before passing back. They removed the shim, kept the original JSON, dropped 22% of every tool call's wall time. No architecture change, no batch redesign, just measurement followed by a small fix. The pattern repeats.
Pitfalls in production
Streaming responses through MCP. The current MCP spec serialises the full tool_result before returning. Long upstream responses (a 200 KB transcript) hit a 60-200 ms serialise floor regardless of how fast the upstream returns. The mitigation is content negotiation at the MCP server: the server returns a content reference (an opaque ID) and the model fetches the body in a follow-up call only if needed. Lossy if the model always needs the body; substantial if it only needs the body in 30% of cases.
Tool descriptions that bloat the system prompt. Each tool's description, schema, and example block adds tokens to the system prompt on every call. A 12-tool registry with verbose descriptions can be 4-6k tokens of overhead repeated on every model call. With prompt caching enabled this is paid once per cache TTL; without prompt caching it is paid every call. Verify the cache hit by inspecting the provider's cache_creation_input_tokens vs cache_read_input_tokens fields after the second call in a session.
MCP server over public HTTPS without keepalive. Each new tool call pays a TLS handshake (90-200 ms in the unlucky path) on cold connections. The MCP HTTP transport supports keepalive but several wrappers default to closing the connection after each call. Verify with tcpdump or a packet capture; the fix is a single config flag.
Rate-limit cascades. A burst of speculative pre-fetches triggers an upstream 429, the MCP server retries with exponential backoff, the agent appears to hang. The signature is bimodal latency: most calls fast, a small fraction extremely slow. Mitigate with a per-tool concurrency cap inside the MCP server (typical 4-6 concurrent upstream calls per tool) and surface the 429 as a tool-level error rather than swallowing it in retry logic.
References
- Anthropic. (2024-2025). "Model Context Protocol specification." Anthropic, MCP repository. The protocol contract and JSON-RPC envelope shape.
- Anthropic. (2025). "Prompt caching with the Anthropic API." Anthropic Documentation. Documents the cache_creation vs cache_read accounting that quantifies tool-description overhead.
- Cloudflare. (2025). "Workers as MCP Servers." Cloudflare Engineering Blog. Reference architecture for the co-located MCP pattern at edge.
- Google Cloud. (2025). "Best practices for low-latency LLM tool calls on Vertex AI." Vertex AI Documentation. The breakdown of network hops and serialisation costs aligns with the budget table above.
- OpenAI. (2025). "Function calling latency benchmarks." OpenAI Cookbook. Independent confirmation of the 60-200 ms serialisation floor on JSON-encoded tool results.