Performance benchmarks
Every number on this page is [MEASURED] on the machine below, with a reproducible harness committed under
benchmarks/. We publish only figures we actually measured, and we report where a competitor edges toolnexus rather than hide it. A partial-but-real table beats a complete-but-fake one.Measured July 2026 across the ports and their real competitors. Frameworks move fast — re-run before quoting.
Every framework points at the same local mock LLM and the same stdio MCP server, running one fixed tool-calling scenario (2 LLM round-trips + 2 tool calls). We measure only the framework’s own per-request overhead — a real model call (100 ms–seconds) would swamp it. Across 18 framework configurations in 6 languages:
- toolnexus has the lowest or joint-lowest per-request overhead in Python, JavaScript, Go, and Elixir, and sits within noise of the fastest in C# and Java — where a lighter native-tool competitor edges it by tenths of a millisecond at the sub-few-ms floor, while toolnexus is doing real MCP.
- toolnexus keeps the smallest or near-smallest install footprint in every language — often dramatically (vs CrewAI 828 MB, Google ADK 207 MB, LangChain.js 106 MB).
- Every competitor that ships an MCP client did real MCP here; toolnexus does real MCP in all six languages.
Results — pick your language
Section titled “Results — pick your language”All cells [MEASURED], N = 30 runs, 5 warmup, single machine (see Hardware).
ok = the loop produced the correct final answer. Lower is better. Tables are split by tool
source — an Over MCP row discovers tools over the real shared stdio MCP server (so it pays
the subprocess spawn + tools/list), a Native tools row uses in-process tools with the same
names/behavior. Comparing across the two is apples-to-oranges, so they’re separate. Click any
column header to sort (ascending / descending).
Every Python competitor ships a first-class MCP client, so all rows are over the real MCP server.
Over MCP
Section titled “Over MCP”| Framework | Init (ms) | p50 (ms) | p95 (ms) | mean (ms) | RSS (MB) | Tools | ok |
|---|---|---|---|---|---|---|---|
| toolnexus | 17 | 1.00 | 1.23 | 1.04 | 59.6 | 3 | ✅ |
| toolnexus (+ skill + native) | 19 | 1.07 | 1.37 | 1.12 | 59.4 | 5 | ✅ |
| OpenAI Agents SDK | 19 | 4.14 | 4.55 | 4.13 | 109 | 3 | ✅ |
| Pydantic AI | 37 | 5.45 | 6.34 | 6.43 | 123 | 3 | ✅ |
| LangGraph (persistent session) | 144 | 7.00 | 9.72 | 8.32 | 127 | 3 | ✅ |
| Google ADK | 23 | 8.80 | 10.53 | 8.87 | 292 | 3 | ✅ |
| CrewAI | 41 | 10.46 | 15.36 | 11.06 | 211 | 3 | ✅ |
| LangGraph (reconnect-per-call)¹ | 154 | 31.3 | 33.5 | 32.9 | 127 | 3 | ✅ |
Install footprint: toolnexus 33 MB (2 deps) · OpenAI Agents 64 MB · LangGraph 83 MB ·
Pydantic AI 96 MB · Google ADK 207 MB · CrewAI 828 MB (heaviest; also needed a manual
litellm add). ¹ LangGraph’s documented default re-spawns the MCP subprocess on every tool
call; the persistent-session row is the apples-to-apples one.
Over MCP
Section titled “Over MCP”| Framework | Init (ms) | p50 (ms) | p95 (ms) | mean (ms) | Tools | ok |
|---|---|---|---|---|---|---|
| toolnexus (+ skill + native) | 24 | 0.92 | 1.28 | 0.97 | 5 | ✅ |
| toolnexus | 24 | 1.16 | 1.96 | 1.21 | 3 | ✅ |
| Vercel AI SDK | 21 | 1.25 | 1.74 | 1.28 | 3 | ✅ |
| LangChain.js | 25 | 3.37 | 5.14 | 3.54 | 3 | ✅ |
| Mastra | 21 | 3.48 | 5.03 | 3.60 | 3 | ✅ |
Native tools
Section titled “Native tools”| Framework | Init (ms) | p50 (ms) | p95 (ms) | mean (ms) | Tools | ok |
|---|---|---|---|---|---|---|
| toolnexus | 0.2 | 0.80 | 1.04 | 0.82 | 2 | ✅ |
| Vercel AI SDK | 0.2 | 1.40 | 1.93 | 1.42 | 2 | ✅ |
| LangChain.js | 2.3 | 2.69 | 3.45 | 2.75 | 2 | ✅ |
| Mastra | 0.5 | 3.57 | 4.74 | 3.66 | 2 | ✅ |
Install footprint: Vercel AI SDK 21 MB · toolnexus 26 MB · LangChain.js 106 MB · Mastra 155 MB. All four do real MCP.
The Go MCP client is mark3labs/mcp-go (used by both toolnexus and Eino).
Over MCP (mark3labs/mcp-go)
Section titled “Over MCP (mark3labs/mcp-go)”| Framework | Init (ms) | p50 (ms) | p95 (ms) | mean (ms) | Tools | ok |
|---|---|---|---|---|---|---|
| Eino | 20 | 0.54 | 0.76 | 0.57 | 3 | ✅ |
| toolnexus | — | pending re-run¹ | — | — | — | — |
Native tools
Section titled “Native tools”| Framework | Init (ms) | p50 (ms) | p95 (ms) | mean (ms) | RSS (MB) | Tools | ok |
|---|---|---|---|---|---|---|---|
| toolnexus | 0.0 | 0.51 | 0.73 | 0.53 | 15.2 | 2 | ✅ |
| langchaingo | 0.0 | 0.53 | 0.77 | 0.57 | — | 2 | ✅ |
Install footprint: toolnexus 10 MB static binary · Eino 21 MB (91 modules) · langchaingo 16 MB (329 modules). Go framework overhead is uniformly sub-millisecond. ¹ The 0.9.0 stdio-MCP bug is fixed since 0.9.2 (the resilience benchmark confirms a live Go stdio session); this run measured native only, so the MCP row lands on a re-run.
Every Java competitor here ships an MCP client, so all rows are over the real MCP server.
Over MCP
Section titled “Over MCP”| Framework | Init (ms) | p50 (ms) | p95 (ms) | mean (ms) | RSS (MB) | Tools | ok |
|---|---|---|---|---|---|---|---|
| LangChain4j | 193 | 2.27 | 2.91 | 2.28 | 130 | 3 | ✅ |
| toolnexus (+ skill + native) | 391 | 3.02 | 4.03 | 3.20 | 176 | 5 | ✅ |
| toolnexus | 388 | 3.05 | 3.55 | 3.07 | 170 | 3 | ✅ |
| Spring AI | 1064¹ | 5.59 | 10.36 | 6.52 | 418 | 3 | ✅ |
Install footprint: toolnexus 156 KB jar + 8.6 MB deps · LangChain4j 11 MB · Spring AI
25 MB fat jar. Honest read: LangChain4j’s MCP path edges toolnexus here (2.27 vs 3.05 ms);
both are well under Spring AI, and toolnexus carries the smallest footprint. Post-warmup.
¹ Spring AI’s Init is Spring Boot context startup, not a toolkit build.
No C# competitor ships a first-class MCP client, so the comparison is toolnexus’s real MCP vs the competitors’ native tools — kept in separate tables so it stays honest.
Over MCP
Section titled “Over MCP”| Framework | Init (ms) | p50 (ms) | p95 (ms) | mean (ms) | RSS (MB) | Tools | ok |
|---|---|---|---|---|---|---|---|
| toolnexus | 76 | 0.590 | 0.781 | 0.621 | 78.6 | 3 | ✅ |
| toolnexus (+ skill + native) | 92 | 0.640 | 0.769 | 0.652 | 81.4 | 5 | ✅ |
Native tools
Section titled “Native tools”| Framework | Init (ms) | p50 (ms) | p95 (ms) | mean (ms) | RSS (MB) | Tools | ok |
|---|---|---|---|---|---|---|---|
| Semantic Kernel | 41 | 0.575 | 0.701 | 0.595 | 71.7 | 2 | ✅ |
| Microsoft.Extensions.AI | 25 | 0.575 | 0.724 | 0.601 | 68.7 | 2 | ✅ |
Install footprint: toolnexus 3.1 MB · Microsoft.Extensions.AI 5.4 MB · Semantic Kernel 7.8 MB. At the sub-ms floor SK/MEAI native paths shade toolnexus’s real-MCP path by ~15 µs (within noise); toolnexus is smallest and the only one here discovering tools over a live MCP session.
No Elixir framework ships an MCP client, so only toolnexus has an Over MCP row.
Over MCP
Section titled “Over MCP”| Framework | Init (ms) | p50 (ms) | p95 (ms) | mean (ms) | Tools | ok |
|---|---|---|---|---|---|---|
| toolnexus | 27 | 0.602 | 0.677 | 0.612 | 3 | ✅ |
Native tools
Section titled “Native tools”| Framework | Init (ms) | p50 (ms) | p95 (ms) | mean (ms) | Tools | ok |
|---|---|---|---|---|---|---|
| toolnexus | 5.3 | 0.534 | 0.671 | 0.554 | 2 | ✅ |
| Elixir LangChain | 0.0 | 0.654 | 0.734 | 0.652 | 2 | ✅ |
Install footprint: both ~5.3 MB deps. brainlid/langchain is the one mature Elixir competitor
that stood up — same sub-ms ballpark. The real differentiator: no Elixir framework ships an
MCP client, so toolnexus’s MCP variant has no Elixir peer.
Methodology
Section titled “Methodology”Why a mock LLM
Section titled “Why a mock LLM”Point every framework at the same local mock LLM and the same stdio MCP server, and measure
only the framework’s own cost. The mock answers one fixed question with exactly one tool-calling
turn (two parallel tool calls: get_weather, add), then a final message — so every run is
2 LLM round-trips + 2 tool executions through that framework’s own agent loop. Zero network,
zero cost, deterministic. It picks tool names from each framework’s advertised list, so prefixed
names work.
Metrics
Section titled “Metrics”| Metric | How |
|---|---|
| Cold init | Wall time to build the toolkit/agent including MCP connect + tool discovery. Honest per-language timers (perf_counter, time.Now, System.nanoTime, Stopwatch, monotonic_time). |
| p50/p95/mean | Wall time per scenario run, over N = 30 measured runs after 5 warmup. |
| Peak RSS | Max resident set of the runner process (/usr/bin/time -l). |
| Footprint | Isolated per-framework install (du -sh of each venv/module/dll set). |
Hardware / OS
Section titled “Hardware / OS”| Machine | Apple Mac17,8 (arm64), 18 cores, 48 GB RAM |
| OS | macOS 26.4 |
| Runtimes | CPython 3.11 · Node 22 · Go 1.23 · OpenJDK 21 · .NET 10 · Elixir 1.20/OTP 29 |
| Network | none — mock LLM + MCP server are both local, offline, deterministic |
MCP vs native
Section titled “MCP vs native”Where a framework ships a first-class MCP client (toolnexus everywhere; LangGraph, ADK, OpenAI
Agents, Pydantic AI, CrewAI, Vercel AI SDK, LangChain.js, Mastra, LangChain4j, Eino), rows use the real
shared stdio MCP server. Where MCP isn’t first-class (Semantic Kernel, Microsoft.Extensions.AI,
langchaingo, Elixir LangChain), the row uses in-process native tools with identical
names/behavior and is tagged (native). Native skips the stdio spawn + discovery, so a (native)
row has an inherent small advantage over an MCP row — kept explicit so the comparison stays honest.
Reproduce it
Section titled “Reproduce it”Full instructions + every runner:
benchmarks/README.md. Each
competitor installs in its own isolated venv/module (toolnexus from the local repo); start
mock_llm.py, then run each runner (or run_all.py) — N = 30, 5 warmup, one JSON line each.
Verdict
Section titled “Verdict”On pure framework overhead for a fixed MCP tool-calling scenario, toolnexus is the lowest or joint-lowest in Python, JavaScript, Go, and Elixir, and within noise of the fastest in C# and Java — where a lighter native-tool or MCP competitor edges it by fractions of a millisecond at a floor where a real model call is 100–1000× larger. What holds in every language: toolnexus does real MCP, carries the smallest or near-smallest install footprint, and never falls into the heavy tier (Spring AI, LangGraph, ADK, CrewAI). That matches its design goal — a small, right-sized library, not a runtime — and the honest asterisks (LangChain4j edges Java; SK/MEAI native edge C#) are exactly the kind of thing we’d rather publish than hide.