A Decentralized, Trusted MCP Runtime: Architecture and Implementation

Posted by LB on Sun, Nov 1, 2026
中文版是这篇文章的默认版本。 阅读中文版 →
This English page stays available at this URL; the Chinese version is the one we maintain first.

In one line: treat a tool as a content-addressed artifact, run it inside a sandbox, and keep an identity behind it — only then does “trusted” mean anything. That is the architecture; the second half of this article is the implementation.

1. The problem: an agent’s tools are black boxes

An agent’s capability is not bounded by its model. It is bounded by the tools it can actually call. And today’s tools are black boxes: third-party code, one container per tool, centrally hosted.

ConsequenceWhat it looks like in practiceThe price
UnverifiableYou don’t know which bytes are running, or whether they changedYou won’t hand it sensitive data
UnportableOne native binary per ISA (x86 / ARM / RISC-V)The tool never reaches the edge
UnscalableOne container per tool: 527–1119 ms cold start, 69–94 MB residentA small device can hold three of them, not thirty

This is not an operations problem. It is a trust problem: you cannot check the tool, you cannot move it, and you cannot run it on demand.

2. Where the research stands

Work on “MCP + WebAssembly” over the last two years has framed the problem cleanly. Three gaps, and a set of responses (figures and design claims below come from published work; measurements are from an Intel NUC):

Gap 1 — the protocol carries functional metadata only. A tool schema says what a tool is called, what it takes and what it returns. It says nothing about how long it runs, whether it is CPU-bound, where its data lives, or whether that data may leave the device. A scheduler is blind.

Gap 2 — cold start and instruction sets keep the edge out. Containerised MCP servers take 527–1119 ms to start and sit at 69–94 MB resident. Native binaries need a build per ISA. In one dataset of registered servers, 22.3% supported stdio only, 77.7% HTTP only, and 0% supported both — the transport is frozen at build time.

Gap 3 — the SDKs are bound to OS primitives. They assume threads, signals and sockets. WASI has none of them, so the WebAssembly side has no native transport layer and falls back to a sidecar proxy — which hands back every resource Wasm was meant to save.

The research response comes in three groups:

LayerClaimWhat it means for the architecture
Control planeproxy → registry + profiles → constraint filter → pluggable schedulerseparation of what to run from where it runs; candidates collapse from N×M to N
Runtimedual transport from one source, compile-time schema, stripped init path53 ms start; no sidecar; portable across architectures
Deploymentstateless HTTP, scale-to-zero, a wasmtime runtime class, volumes mapped to preopensWebAssembly modules can live in a cluster and read storage

The scheduler scores a candidate as:

1Score = α · Pexec + β · Pnet + (1 − α) · Pcomm

Pexec is normalised execution latency, Pcomm the communication cost to that node, β adds external network latency Pnet for network-bound tools, and α trades computation against transfer per tool type.

Figure 1 — one tool call, six hops: the agent sees the first and the last

3. The architecture

Research gives parts; parts are not an architecture. The design I would defend has four requirements and two principles.

RequirementWhat it solvesWhat breaks without it
Content-addressed distributionthe artifact is immutable and checkableyou trust whatever a service says its version is
Sandboxed executionuntrusted code cannot leave its boxthird-party tools inherit host privileges
Traceable identityyou can attribute publishing and callingnobody is answerable in a network without a centre
Standard MCP protocolany agent framework can connectone adapter per framework

The two principles are decentralized (distribution does not depend on a central registry) and trusted (immutable + sandboxed + attributable, all three at once).

In a running system that is three layers plus two lateral capabilities:

Figure 2 — agent, control plane, execution plane; distribution and identity feed both

Five invariants follow, and every implementation decision is measured against them:

  1. One logical tool. The caller sees a tool name, never a node.
  2. Pluggable control plane. Scheduling algorithms and constraints change without touching orchestration.
  3. Replaceable execution plane. The same tool semantics can run as WebAssembly or as a container.
  4. Immutable artifact. Once published, the address is the checksum.
  5. Attributable identity. Every publish and every call lands on an address.

4. Implementation: five layers

The implementation below is open source (Apache-2.0 / MIT). Its five layers map one-to-one onto the requirements, and each has a code anchor you can open.

Figure 3 — the five layers and where they live in the code

4.1 Manifest: a tool is a catalog entry, not a deployment unit

1modules:
2  - name: hello
3    wasm_path: IPFS://QmeDsaLTc8dAfPrQ5duC4j5KqPdGbcinEo5htDqSgU8u8Z
4    tools:
5      - name: say_hello
6        description: Greet someone by name
7        inputs: [{ name: name, type: string, required: true }]

Adding a tool edits this one file. Nothing is rebuilt; the runtime registers what the manifest declares.

4.2 Runtime: untrusted code goes into a sandbox

1// core/mcp/wasm.go (excerpt)
2if strings.HasPrefix(path, "IPFS://") {
3    data, _ := ipfs.ExtractWASMFromCID(client, strings.TrimPrefix(path, "IPFS://"))
4    for _, d := range data { manifest.Wasm = append(manifest.Wasm, extism.WasmData{Data: d}) }
5}
6cfg := extism.PluginConfig{ModuleConfig: wazero.NewModuleConfig().WithSysWalltime(), EnableWasi: true}

The sandbox buys two things: untrusted code cannot escape, and the same bytes run on x86 and ARM.

4.3 Distribution: the address is the hash

Change one byte and the CID changes. Publishing a tool therefore means publishing a content address: anyone can verify what they run, with no central registry involved. This layer is the part of “decentralized” that is actually hard.

4.4 Identity: an attributable slot in a network without a centre

At startup the service decrypts a keystore with a passphrase and gets an address. Today it is identity only — no metering, no billing. First you establish who published and who called; authorization and settlement come later.

4.5 Protocol: catch every agent, then dare to enter a cluster

MCP runs over Streamable HTTP with the registry exposed at GET /tools. Stateless requests are what allow scale-out and scale-to-zero — the real difference from a resident SSE server.

5. Four trade-offs and what they cost

An architecture is a list of trade-offs. Each one is written with its cost, or the next version pays for it again.

DecisionAlternativeWhyCost
Manifest-drivendynamic registration at runtimeconfig is the contract; small audit surface; tool code stays out of the runtimehot updates need a manifest reload
IPFS distributioncentral registryimmutable, verifiable, globally fetchablethe first fetch pays a network round trip (published p99: 50 ms)
Wallet identityAPI keys / OAuthself-verifiable in a network without a centretoday: identity only, no permission model
Stateless HTTPresident SSEscale-to-zero, horizontal scaleloses long-lived push semantics

One item on the list is not a trade-off but a debt: one instance per module with a single lock, so concurrent calls to the same tool queue. It keeps module state safe and caps throughput; an instance pool is the next step.

6. Where it stands against the research design

Figure 4 — capabilities: consensus, gaps, out of scope

The gap is easy to state: runtime, distribution and identity are shipping; the control plane is not. It runs — it is not smart. A human still decides which layer a tool lands on.

That is also why the control plane is drawn grey in Figure 2: part of the design, not yet implemented.

7. Numbers, with their sources

MetricValueSource
Module start53 msmeasured (Intel NUC)
Container start527–1119 msmeasured (same)
Cold-start gap10–21×measured
Memory31 MB vs 82 MB (−59~72%)measured
Distribution size0.3–1.4 MB vs 80–167 MB imagemeasured
Constraint violations (45 calls)0 vs 41 / 42 / 42measured
End to end2.3× faster than all-cloudmeasured
Throughput / avg response / IPFS p9925,000+/s, 5 ms, 50 msvendor-published, not independently re-measured

The last row matters: it is not our measurement. Vendor figures must be separated from measured ones wherever they appear, or one number drags the whole article down.

8. Trust is a product of three conditions

Figure 5 — immutable artifact × sandbox boundary × traceable identity

Here is a check a reader can perform: take the CID, fetch it yourself, hash it, compare with the address that was published. If they differ, what you got was modified. Trust means you can check it yourself — a property that does not depend on any company’s promise.

9. Roadmap and open questions

  • Control plane: runtime profiles → constraint filtering → placement. The goal is to move “which layer does this tool land on” from a human decision to a system decision.
  • Concurrency: one instance, one lock → an instance pool.
  • Cold path: caching and prewarming for the first fetch.
  • Open: should placement be exposed to tool authors? Who defines constraints — the platform, the data owner, or the caller? The answers decide whether this ends up a runtime or a market.

References

  • Published work: Orchestrating WASM-based MCP Tool Runtimes for AI Agents across Edge-Cloud Continuum (CCGrid 2026) — the three gaps, the control plane, the WASI-native runtime and the measurements above.
  • Implementation: DANP-LABS/DANP-Engine (Apache-2.0 / MIT).
  • Project site: danp.icefirelabs.xyz.