Publish an MCP tool as a static, hash-verified file. Run it sandboxed,
on demand, with zero infrastructure — the way static site hosting did
to web servers: don't run Apache, publish HTML.
Running someone else's tool code is a trust decision
MCP clients like Claude or Cursor can call arbitrary third-party
tools. Executing that code directly in your backend means it can
read your secrets, hit your database, phone home, or loop forever.
You either trust the author fully, or you don't run the tool.
Typical MCP server
Someone has to run a process, 24/7
Third-party code shares your runtime
Trust the author, or don't install
mcpwasm
The tool is a file. No process to run.
Code executes isolated in QuickJS-wasm
SHA-256 pinned; a flipped byte excludes it
How it works
Five steps, all sandboxed, no state kept between requests.
A publisher site ships /llms.txt plus per-skill tool.js and SKILL.md.
The gateway downloads llms.txt, fetches each tool.js, and verifies its SHA-256.
Verified tools load into a fresh QuickJS-wasm context, one per skill.
Tool code calls host.fetchOrigin — scoped to the publishing origin only; anything else throws inside the sandbox.
The gateway maps MCP tools/list / tools/call over JSON-RPC 2.0 back to the client.
Four ways to use it today
All four are real, deployed, and tested — not roadmap.
Gateway turnkey MCP server
Point any MCP client at the deployed gateway. It discovers, verifies, and sandboxes a publisher's skills on every request.
Run any origin's skills locally over stdio — including origin memory (verified BM25 search, 0.4.0), each skill's SKILL.md recipe served as an MCP resource (0.5.0), and multi-project origins via scopes: kdd__search_knowledge with per-scope memory (new in 0.6.0). No gateway, no server, on either end.
The whole runtime in a browser tab (new in 0.7.0): discovery, byte-for-byte SHA-256 verification, one QuickJS-wasm sandbox per tool, scopes and per-scope memory. The publisher only needs CORS — GitHub Pages already sends it. No server on their side, no Node on yours.
js
import { connectStaticSkills } from"@rckflr/mcpwasm/web";
const skills = await connectStaticSkills("https://mauricioperera.github.io", opts);
await skills.callTool("search_knowledge", { q: "how do I publish?" });
tab-agent is a personal AI agent that is nothing but static files + your browser: shared memory (the cq-git commons over the GitHub API), verified tools (this runtime, hash-pinned & sandboxed), and a model you own — the provider credential is never exposed (run it in-tab via WebGPU, your endpoint, or an operator proxy). It answers the personal-agent supply-chain problem by inverting it: tools are verified, not screened.
None of them alone is enough. Together, they bound what a third-party tool can do — and who vouches for it.
01
Content integrity
Every tool.js is pinned by SHA-256 in llms.txt. A single flipped byte and the skill is excluded — not degraded, not executed.
02
Sandbox isolation
Each skill runs in its own QuickJS-wasm context: no fetch, no process, no disk. The only bridge out is an explicit, origin-scoped host.fetchOrigin.
03
Signed attestations
Signed human review with an expiry window — pre-registered Ed25519 keys or keyless Sigstore identities (the spec's recommended default since v0.4). Honest today: one registered reviewer. The mechanism scales; the reviewer network hasn't yet.
What it costs
Real numbers from the deployed gateway, not synthetic estimates.
0ms
sandbox overhead, warm
55ms sandboxed vs 53ms raw ping
0ms
full gateway overhead
96ms via gateway vs 90ms direct API
0ms
p95, 10 concurrent requests
before → after the instance pool + preheat
0ms
cold discovery miss
~210–400ms range, compile + sha256 + fetch
Single-client benchmark from México to the Cloudflare Workers edge — this
is latency of one observer, not a load test. Full matrix,
methodology, and raw data are in
BENCHMARK.md.
The deployed gateway requires a bearer token (not published here). Full
curl walkthrough — including the open demo, bookstore, and docs
publishers — is in the
README.
From a static site to a live MCP server
llms-txt-skills is the format; mcpwasm is a runtime for it. A publisher serves hash-pinned, attested skills once, the standard way — the runtime discovers, verifies, and runs each as an MCP tool. The whole contract between them is one tool_sha256 and its attestation.
A publisher serves an llms.txt whose ## Skills section lists each executable skill with its tool.js and tool_sha256 — mirrored in /.well-known/agent-skills/index.json and signed in attestations.json. Optionally, a hash-pinned BM25 snapshot (one llms-skills memory command over an OKF bundle) adds serverless search over the site's own knowledge. This is exactly what the llms-txt-skills spec defines. Since llms-skills 0.4.0, the bundle can also carry signed freshness: a reviewer attests “still true”, voided on edit, expiring on a date.
mcpwasm points at that origin, fetches the llms.txt, and verifies every tool.js against its tool_sha256 and its attestation — rejecting any mismatch before loading it.
Each verified skill becomes an MCP tool, and its SKILL.md recipe is served alongside as an MCP resource (with a get_skill_guide fallback) — the agent gets the manual, not just the hammer. Claude, Cursor, any MCP host list and call it like any other tool.
On a call, mcpwasm executes that tool.jsverbatim inside a QuickJS-wasm sandbox — no network or filesystem except the host capabilities it grants (a scoped fetchOrigin back to the site, and search over the site's own content). The result returns to the client.
Neither side has to trust the other's prose: mcpwasm re-derives the hash and checks the signature itself. Static hosting + a verifying runtime = an MCP server with no server to run.
Want to be the publisher side? Start from the GitHub template — a working publisher out of the box (example knowledge bundle, generated skills, validation CI) that this runtime consumes as-is.
Part of a spec, not just a repo
mcpwasm is the reference implementation of two provisional extensions
to the
llms-txt-skills
standard: Executable Skills (v0.5, with origin memory and scopes)
and Skill Attestations (v0.4). Every MUST in those
specs is field-tested in this code — spec and implementation are kept
in sync.