Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
6.8 KiB
Gap Audit — the integration/exposure surface (MCP, A2A, x402) + foundations
A code-grounded robustness audit of the surfaces the strategy rests on, plus the
two foundations a real app (Mu) discovered it needed. Pair this with the
"requirements discovered from Mu" notes — together they are the roadmap's
evidence base. Each finding is file:line, severity, and what "robust in
practice" requires.
Headline
All four surfaces are well-built internally and fragile at the edge. The strategic spine — MCP gateway, A2A, x402 — is demo-robust, not production-robust: it works go-micro-to-go-micro and is unverified or broken against the real external clients the whole "integration and exposure" strategy depends on. Not one test drives a real external MCP host, a real third-party A2A SDK, or a real x402 facilitator/wallet.
There are two kinds of hardening, and the loop was doing the wrong one: guarding docs and chasing a weak provider's quirks is grooming; making MCP actually speak MCP to Claude Desktop, A2A interoperate with a real external agent, and x402 actually settle is strategic hardening. The axis is advances the strategy vs grooms a proxy, not capability vs hardening.
MCP gateway — gateway/mcp/
Works as go-micro plumbing; does not speak MCP to the outside world.
- BLOCKER
mcp.go:610— default HTTP transport is bespoke{tool,input}REST, not JSON-RPC/MCP. A conformant JSON-RPC handler exists (httpjsonrpc.goNewHandler) but is never mounted. → mount it / implement Streamable HTTP; unify transports behind one pre-call pipeline. - BLOCKER
stdio.go:327,websocket.go:306— tool results arefmt.Sprintf("%v", result)→ Go map-syntax, not JSON, on the path Claude Desktop uses. Zero stdio tests. → marshal JSON; add a stdio round-trip test. - BLOCKER
stdio.go:297,websocket.go:278— downstream errors returned as JSON-RPC protocol errors, not{isError:true}results. → wrap as tool-error results. - BLOCKER
websocket.go:20—CheckOriginalwaystrue(DNS-rebinding); and/mcp/wsbypasses payment + circuit breaker → paid tools free over WS. → origin allowlist; one shared pre-call pipeline. - MAJOR deregistered tools never pruned (
mcp.go:280); watcher never recovers + nolist_changed(mcp.go:564); unbounded goroutines, norecover()(stdio.go:112); no HTTP/WS timeouts or body limits; unauthenticatedmicro_store_write/micro_broker_publishby default (mcp.go:474).
A2A gateway — gateway/a2a/
Clean binding; cross-framework interop unproven.
- MAJOR
a2a.go:111— well-known path isagent.json; spec 0.3.0 servesagent-card.json→ external clients 404. → serve both. - MAJOR
a2a.go:587—message/streamemits fullTasksnapshots, notTaskStatusUpdateEvent/TaskArtifactUpdateEventwithfinal:true;:596sets JSON-RPCresult+errortogether (spec violation). → emit discriminated update events. - MAJOR
a2a.go:584— streaming ignores write errors / client disconnect (burns tokens on a dead socket);:531"streaming" is single-shot despitestreaming:true;:508tasks/cancelis a stub;:874push callbacks SSRF-open + auth ignored; no gateway auth / no security schemes (:342); in-memory state breaks multi-replica (:466). - Critical: no test against a real third-party A2A SDK — all interop claims self-certified.
x402 — wrapper/x402/
Clean, spec-aware scaffold; no real money can move.
- BLOCKER — no real wallet
Payer(no EIP-3009 signer); buyerClientwired into nothing — the agent's "spend budget" (agent/builtin.go:380spendWrap) is bookkeeping that never pays; CDP mainnet settlement unreachable from the CLI (creds never attached). This is the flagship (#4786). - MAJOR
client.go:84—ParseInterror swallowed → malformed amount parses to0, spend-cap check trivially passes while the Payer signs the string amount. Fix as part of #4786. - MAJOR
x402.go:225— verify-only facilitator serves the resource for free;:198no replay/idempotency;:262non-conformant settlement header;client.go:83no network/asset validation before signing.
Foundations (Mu-discovered)
In-process dispatch — client/, transport/
- MAJOR
client/rpc_client.go:148— no in-process fast-path: an in-processCallstill dials a transport and simulates a network hop;transport/memory.go:82double-serializes (gob over a pipe on top of the RPC codec) with ~4–5 goroutine handoffs. ~64µs/187 allocs confirmed. → a local transport / directrouter.ServeRequestdispatch (the server already keeps a process-local handler table; the codec already passes*Framebodies through unserialized). Low-risk; plausibly low-single-digit µs.
Durable agentic workflow — flow/
flow/ is genuinely close on the deterministic axis (checkpoints, resumes without replaying completed steps, ParentID, retry). The gap is the agentic axis:
- BLOCKER
flow/steps.go:107— no human-in-the-loop pause (nowaitingstate /Resume(runID, input)). Exactly what Mu hand-rolled. → add awaitingstatus + await-input signal + resume-with-input. - MAJOR
flow/steps.go:269— the agent's dynamic plan→tool→tool loop is one opaque flow step; a crash mid-turn replays every tool call. The durable unit is a fixed step list, not the agent's pausable per-tool-call loop — the convergence thesis is unmet. → checkpoint per tool call. - MAJOR
flow/loop.go:82Loopnot per-iteration checkpointed;flow/steps.go:457at-least-once, not exactly-once (duplicate side effects on resume);:158no run leasing for multi-replica.
Build order (prioritized)
- MCP stdio: real JSON +
isErrorresults + a round-trip test. Cheapest, highest-impact — the Claude Desktop path, currently emitting garbage. (loop-buildable) - MCP: mount JSON-RPC as the HTTP transport + unify all transports behind one pre-call pipeline. (architectural — human-reviewed)
- x402 flagship (#4786) done right: signing
Payer, buyer wired into the agent seam, budget-bypass fix, require a realSettler. (mixed — the wiring is human-reviewed; the budget-bypass fix is loop-buildable) - Durable agentic workflow: HITL pause + per-tool-call checkpointing in
flow. (architectural — human-reviewed) - In-process dispatch fast-path (local transport). (foundational, low-risk)
- A2A external conformance: well-known path + SSE event shapes, then a test against a real third-party A2A SDK. (loop-buildable + a test-setup task)
The architectural items (2, 4, 5) are the "real 1:1 development" work — too central and too ambiguous to hand to an autonomous agent. The well-scoped items (1, 3-budget-fix, 6) are what a forced, well-defined loop task looks like.