barnacleis a high-performance MCP runtime in Zig. It exists because agent harnesses are latency-critical systems where p99 dominates user experience, and modern systems language features —comptime, no hidden control flow, explicit allocators, no GC — give us tools to make p99 predictable.barnaclereimagines CodeWhale'scrates/mcp/(Rust) as a Zig-native implementation, to test the thesis that an MCP runtime written in Zig can be both correct and substantially faster than equivalent Rust implementations on cold-start, message dispatch, and memory footprint. Where the numbers don't bear it out, the project will say so honestly.
Phase 2A — stdio transport with initialize round-trip. Early; the wire
surface is small and honest. See Roadmap.
What works today:
- JSON-RPC 2.0 message decoding/encoding (
src/jsonrpc.zig) - Newline-delimited stdio transport (
src/transport/stdio.zig) - A
Serverdispatch loop handlinginitialize,ping, notifications, and unknown methods with spec-correct error codes (src/server.zig) - Unit tests + an end-to-end smoke test that spawns the binary and drives a real
initializehandshake (tests/smoke_initialize.zig)
What does not work yet: tools/list, tools/call, SSE transport,
benchmarks. Those are Phases 2B–2E below.
Requires Zig 0.14.1 (pinned; see build.zig.zon → minimum_zig_version).
zig build # build ./zig-out/bin/barnacle
zig build test # unit + smoke tests
zig build run # serve MCP over stdio (reads JSON-RPC lines on stdin)Quick manual smoke check:
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
| ./zig-out/bin/barnacle
# -> {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05",
# "serverInfo":{"name":"barnacle","version":"0.1.0"},"capabilities":{}}}barnacle was built to test whether a Zig MCP runtime is faster and leaner
than the Rust reference. It has now been measured against codewhale-mcp's
run_stdio_server on an Apple M4. The result is "comparable, with different
tradeoffs" — not "Zig wins." Full methodology, numbers, and caveats:
bench/BENCHMARKS.md.
- Binary size — Zig wins. 323 KiB vs 789 KiB (~2.4× smaller).
- Memory (peak RSS) — Zig wins. ~1248 KiB vs ~1550 KiB (~20% less).
- Cold start — tie. ~1.5 ms both; Zig marginally ahead, both noisy.
- Warm dispatch — Rust wins. ~16 µs vs ~11 µs p50. A per-request arena
allocator roughly halved barnacle's latency (from ~32 µs), but Rust's
serdetyped parse still edges it out. - Memory safety — Rust wins, decisively. Rust enforces memory safety at
compile time (borrow checker). Zig only checks at runtime in Debug/
ReleaseSafe, and
ReleaseFastdisables those checks. For a runtime parsing untrusted input this is the dimension that matters most, and barnacle does not pretend otherwise.
So barnacle is best read as a systems-depth exploration plus an honest, reproducible comparison — not a claim that Zig beats Rust. The most useful output is the methodology and the numbers, including the ones that don't flatter Zig.
| Phase | Scope | Status |
|---|---|---|
| 2A | stdio transport + initialize round-trip |
✅ done |
| 2B | tools/list + tools/call dispatch |
⬜ next |
| 2C | SSE transport | ⬜ |
| 2D | benchmark vs Rust crates/mcp/ |
✅ measured (results) |
| 2E | cross-compile macOS / Linux / Windows | ⬜ |
src/
├── main.zig CLI entry — serve MCP over stdio
├── jsonrpc.zig JSON-RPC 2.0 types + (de)serialization
├── server.zig request dispatch (initialize / ping / …)
├── tools.zig tool registry (skeleton; wired in 2B)
└── transport/
└── stdio.zig newline-delimited stdio read/write loop
tests/
└── smoke_initialize.zig spawns the binary, drives a real handshake
MIT — see LICENSE. (Matches CodeWhale's license to keep cross-pollination friction-free.)