Skip to content

Architecture

wasm-mcp is built around one idea: all the work happens at build time, so the running server is a pure, deterministic lookup over static JSON.

The contract

Every tool is:

  • Read-only — no state mutation, no writes outside an optional local cache.
  • Deterministic — same input → same output, over a pinned spec commit (vendor/PINNED.txt).
  • No execution — never compiles, validates-by-running, instantiates, or runs any WebAssembly or arbitrary code. Validation and reduction rules are returned as data.
  • No auth, no secrets, no PII — usable anonymously.
  • No network at request time — all spec data is fetched and indexed at build time and baked in.

This contract is the whole reason the server can run as an unauthenticated hosted endpoint.

Build-time pipeline

WebAssembly/spec @ pinned SHA          WebAssembly/proposals @ pinned SHA
        │                                       │
        ▼                                       ▼
 src/parser/upstream.ts                  *.md phase tables
  (parses index-instructions.py           │
   + macros.def — no code executed)       │
        │                                  │
        ▼                                  ▼
 src/parser/instructions.ts        src/parser/proposals.ts
 src/parser/sections.ts  (RST)
 src/parser/bikeshed.ts  (js/web-api)
 src/parser/types.ts
        │                                  │
        └──────────► src/index/build_spec.ts ◄── build_proposals.ts


        build/wasm-spec-core-main.json   build/wasm-proposals-main.json

              ┌─────────────┴─────────────┐
              ▼                           ▼
     stdio server (npx)         Cloudflare Worker (bundled)

Why not run SpecTec?

Since 2025 the spec is authored in SpecTec, an OCaml DSL that generates the rendered prose and the formal typing/reduction rules. Building the full rendered HTML needs an OCaml + SpecTec + Sphinx + LaTeX toolchain.

wasm-mcp deliberately avoids that. Instead:

  • Instructions come from parsing the upstream index source (document/core/appendix/index-instructions.py) plus the LaTeX macro table (document/core/util/macros.def) in TypeScript (src/parser/upstream.ts). No upstream code is executed — the parser reads the literal INSTRUCTIONS list. The whole build is pure TypeScript, no Python toolchain.
  • Sections come from parsing the reStructuredText sources directly. The prose is kept; the SpecTec splice macros ($${rule: …}) are recorded as formal_refs and linked to the rendered spec URL.

This keeps the build fast and deterministic over any pinned commit, with no heavy native toolchain — at the cost of not rendering the formal grammar/reduction notation inline (the url field links it).

Shared query logic

The ranking and filtering logic lives in dependency-free modules under src/spec/ (instructions_query, sections_query, proposals_query). Both the stdio server and the Worker import the same functions, so results can't drift between transports — a test asserts the Worker's tool set equals the declared hosted set.

Tool definitions as a single source

Every tool's name, description, Zod schema, and examples live once in src/mcp/tool_meta.ts. The stdio server registers from it, and this docs site's Tool reference is generated from it — so the docs always match the running server.

Released under the MIT License.