tc39-mcp

io.github.xyzzylabsPublished by GitHub account xyzzylabsv0.6.4Updated Oct 24, 2026

Independent MCP server for the TC39 specs (ECMA-262 + ECMA-402): clauses, search, diffs, history.

VerifiedStreamable HTTPWeb executableKnowledge & MemoryDeveloper Tools

Overview

AI-generated overview

Read-only MCP server giving assistants structured, SHA-pinned access to the ECMAScript specs (ECMA-262 and ECMA-402): clauses, search, grammar, diffs and…

What you can do
  • Read clauses and browse the section outline or filtered clause lists
  • Search both specs by clause id, AOID or title, and resolve [[slot]], %intrinsic% and ~enum~ notation
  • Inspect grammar productions, syntax-directed operations, tables and well-known intrinsics
  • Compare a clause across editions and follow cross-references in both directions
  • Search the test262 conformance suite and look up TC39 proposals by stage, champion or name
  • Check which spec snapshots and SHAs are being served
When to use it
Useful when an assistant must answer JavaScript semantics questions from the actual specification rather than memory: finding the clause behind a behavior, checking algorithm steps, tracking prose changes between editions, or locating conformance tests and proposals.
Requirements
Either the hosted HTTP endpoint (no install) or a local process via npx tc39-mcp, which needs Node.js. No account, API key or header is required. The stdio version fetches snapshots from the hosted Worker on a cold cache and caches them under the user's cache directory; the optional TC39_MCP_BASE_URL variable points it at a private mirror. The hosted endpoint is rate-limited to 30 requests per minute per IP.
Before you install
Read-only lookup service; the stdio transport sends no telemetry and does not transmit queries, and the hosted Worker logs only standard request metadata. The stdio server contacts the hosted Worker to fetch or revalidate snapshots, so on strict-egress networks only bundled editions are reachable unless a mirror is configured. It is an independent project, not an official Ecma or TC39 publication.

Installation

In SourceWeft

  1. Open tc39-mcp in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.

Use with an agent

Paste this prompt into Claude Code, Codex, Cursor or another agent — or into SourceWeft chat. The agent reads this server's install guide, shows you its publisher, endpoint and sign-in, and adds it to your client once you agree. Keys never go into a command.

Read https://sourceweft.com/mcp/io.github.xyzzylabs%2Ftc39-mcp/install.md and connect the MCP server it describes to my client. Before changing any config, show me its publisher, endpoint and sign-in method and wait for my OK. Never put an API key in a command or in a file I would commit; ask me where it should come from.

Read the install guide the agent follows

Other MCP clients

Add this to your client's mcpServers config.

{
  "mcpServers": {
    "tc39": {
      "type": "http",
      "url": "https://mcp.xyzzylabs.ai/tc39/mcp"
    }
  }
}

README

tc39-mcp

[Test] [npm version] [License: MIT]

📖 Docs: mcp.xyzzylabs.ai/tc39 — Get started · Tools · Cookbook · Editions · Architecture · Hosting

Independent project — not an official Ecma International or TC39 publication. Reads the publicly published ECMAScript specs (ECMA-262 + ECMA-402).

Give MCP-speaking AI agents structural access to the JS spec. Any client that speaks the Model Context Protocol can call clause.get sec-tonumber and get back parsed JSON (algorithm steps as discrete arrays, cross-references as ids, signatures as typed values) instead of being handed a 4 MB spec.html to grep through. Tools cover ECMA-262 (the core language) and ECMA-402 (the Intl API): clauses, algorithm steps, cross-references both ways, edition diffs, upstream git history, test262 search, proposal lookup. Every response is SHA-pinned to a specific upstream commit so anything an agent cites stays reproducible.

Snapshots resolve through a local cache → hosted Worker → bundled fallback chain. The stdio transport (npx tc39-mcp) fetches each snapshot from the hosted Cloudflare Worker on a cold cache, writes it under ~/.cache/tc39-mcp/, and serves it from disk thereafter — revalidating only when the local copy is older than ~4 hours (a conditional If-None-Match request). The npm package also bundles the latest stable + main editions of both specs plus the test262 and proposals indexes; when the Worker is unreachable, those are served straight from the package (the offline fallback — not written to the cache). The hosted Worker is also the HTTP alternative when you want a shared network endpoint; its R2 data refreshes from upstream every ~4 hours.

Install + first call

Wire it into any MCP client — the stdio launch command is the same everywhere, only the config file differs:

json
{  "mcpServers": {    "tc39": { "command": "npx", "args": ["tc39-mcp"] }  }}

A global install works too — npm i -g tc39-mcp, then run tc39-mcp.

The first run downloads the npm package (latest stable + main editions plus the proposals and test262 indexes are bundled). The first call for a given snapshot fetches it from the hosted Worker and caches it locally; subsequent calls are served from disk, revalidated against the Worker only after the ~4-hour freshness window. If the Worker is unreachable, the bundled editions still answer offline. Then in your client:

use clause.get to read sec-tonumber and show me the steps

You should see structured JSON back:

json
{  "meta": {    "id": "sec-tonumber",    "aoid": "ToNumber",    "title": "ToNumber ( argument )",    "number": "7.1.4",    "kind": "op"  },  "signatureRaw": "ToNumber ( _argument_: an ECMAScript language value, ): either a normal completion containing a Number or a throw completion",  "algorithms": [    { "steps": [        { "text": "If _argument_ is a Number, return _argument_." },        { "text": "If _argument_ is either *undefined* or a Symbol, throw a *TypeError* exception." },        { "text": "If _argument_ is *null*, return *+0*<sub>𝔽</sub>." },        "..."    ]}  ],  "crossrefs": ["sec-tonumber-applied-to-the-string-type", "..."]}

Five-minute walkthrough: docs/getting-started.md.

Hosted HTTP

Point your client at the hosted Cloudflare Worker instead of running a local subprocess — same MCP protocol, no install:

json
{  "mcpServers": {    "tc39": {      "type": "http",      "url": "https://mcp.xyzzylabs.ai/tc39/mcp"    }  }}

Traffic is rate-limited to 30 req/min per IP.

What it's good at

  • Letting an agent reason about the spec without hallucinating. Structured JSON answers ground the model on real spec text: step numbering, cross-reference targets, signature shapes, edition deltas, conformance tests. Anything cited resolves to a specific clause id at a specific SHA — easy to verify, easy to reproduce.
  • Finding the clause you want from a hint. spec.search ranks AOID-exact matches first; spec.symbol_resolve decodes [[Prototype]] / %Object.prototype% / ~enumerate~.
  • Following references both ways. spec.crossrefs returns what a clause cites AND who cites it. AOID-densified so bare mentions in step text count, not just <emu-xref> hrefs. include_cross_spec resolves 262 ↔ 402 hops. (Cookbook recipe 1.)
  • Comparing editions and tracking prose drift. spec.diff between any two editions back to ES2016; spec.history walks the upstream git log via pickaxe search. (Cookbook recipe 2.)
  • Finding test262 coverage for a clause. test262.search with prefix-matched esid: catches sec-tonumber AND sec-tonumber-applied-to-the-string-type in one call.
  • Mapping proposals to the spec. proposal.list / proposal.get from a structured index of tc39/proposals, covering both ECMA-262 and ECMA-402 (Intl) proposals — filter by spec. Refreshed on the same 4-hour cadence as the specs.
  • Local cache, bundled fallback (stdio). Once a snapshot is cached under ~/.cache/tc39-mcp/, tool calls are served from disk and only revalidated against the hosted Worker after the ~4-hour freshness window (a conditional If-None-Match request that carries the R2 object key, never a clause-id). Bundled editions answer offline when the Worker is unreachable. The hosted Worker is the HTTP alternative for shared / multi-tenant use.

Tools (19 across 5 namespaces)

GoalTool(s)
Verify what's being servedspec.about · spec.snapshots
Read a specific clauseclause.get
Find a clause from a name / symptomspec.search · spec.global_search
Resolve [[X]] / %X% / ~X~ notationspec.symbol_resolve
Browse / outlineclause.list · clause.outline
Compare editions / commit historyspec.diff · spec.history
Walk references (in + out)spec.crossrefs
Read structured tablesspec.tables
Inspect the grammarspec.grammar · spec.sdo_index
Enumerate well-known intrinsicsspec.well_known_intrinsics
Find conformance teststest262.search · test262.get
Look up a proposalproposal.list · proposal.get

Full reference (input schemas, output types, example calls per tool): docs/tools.md — auto-generated from the schemas so it never drifts.

Specs + editions

Every spec-reading tool accepts spec ("262" or "402", default "262") and edition (default "latest").

  • ECMA-262: es2016 – es2026, main. (ES5 / ES5.1 / ES6 have no upstream tags and aren't supported.)
  • ECMA-402: es2016 – es2026, main. (402 publishes each annual edition as an esYYYY branch rather than a tag; the fetch step resolves a branch or a tag the same way.)
  • Aliases: latest is spec-aware (each spec → its current stable release, es2026 today). draft / next → main on both.

Full table + how to add new releases: docs/editions.md.

Self-hosting snapshots

The stdio server fetches snapshots from the public hosted Worker at https://mcp.xyzzylabs.ai/tc39/r2/<key> (cache → Worker → bundled fallback), so on a strict-egress network it falls back to the bundled editions and can't reach the others. Override the base URL via TC39_MCP_BASE_URL to point at a private mirror — useful for strict-egress networks, air-gapped environments, or running against a self-hosted Worker:

sh
TC39_MCP_BASE_URL=https://my-mirror.example.com npx tc39-mcp

The endpoint just needs to serve the same key structure (spec-<spec>-<edition>.json, test262-index.json, proposals-index.json) — a plain static file server works. If it returns ETags, the server revalidates with If-None-Match (cheap 304s); without them it just refetches the full object when a cached copy goes stale. To populate a mirror, run npm run parse against a local checkout (see below) and upload build/*.json to your bucket of choice.

The cache lives at $XDG_CACHE_HOME/tc39-mcp (or ~/.cache/tc39-mcp when XDG_CACHE_HOME is unset).

Build from source (contributors)

End users don't need this — the npm package and the hosted Worker are the supported surfaces above. This is for working on the server itself.

sh
git clone https://github.com/xyzzylabs/tc39-mcpcd tc39-mcpnpm installnpm run fetch-spec               # ~2 min, ~150 MB — both specs at every supported editionnpm run parse                    # spec.html → build/spec-<spec>-<edition>.jsonnpm run fetch-test262            # optional, enables test262.* (~300 MB)npm run build-test262-indexnpm run fetch-proposals          # optional, enables proposal.* (~50 MB)npm run build-proposals-indexnpm run mcp                      # start the stdio MCP server against your source

Point your MCP client at your local source instead of the published bin:

json
{  "mcpServers": {    "tc39": {      "type": "stdio",      "command": "npm",      "args": ["run", "--silent", "mcp"],      "cwd": "/abs/path/to/tc39-mcp"    }  }}

--silent keeps npm's lifecycle banner off stdout, so the MCP client receives a clean JSON-RPC stream.

Docs

Hosted at mcp.xyzzylabs.ai/tc39 — searchable, dark-mode-friendly, auto-rebuilt on every refresh so /snapshots always reflects the live SHAs.

In-repo (also browseable on GitHub):

  • docs/getting-started.md — install → wire → first call → verify. Five minutes.
  • docs/tools.md — every tool, every field, every example. Auto-generated from source.
  • docs/cookbook.md — multi-tool recipes: cross-spec lookups, prose-drift tracking, grammar/SDO cross-references, test262 coverage, proposal-to-clause mapping.
  • docs/editions.md — supported editions + alias resolution.
  • docs/architecture.md — data pipeline, parser, cache, memory model.
  • docs/deployment.md — local stdio, npm CLI, hosted Cloudflare Worker, refresh model, observability.
  • CONTRIBUTING.md — what kinds of changes land easily, what won't.
  • SECURITY.md — threat model + responsible disclosure.
  • CHANGELOG.md — version history + auto-refresh convention.

Privacy Policy

tc39-mcp is a read-only spec lookup service. The stdio transport (npx tc39-mcp) sends no telemetry and never transmits your queries — snapshots are fetched from the hosted Worker on a cold or stale cache and served from local disk otherwise; those fetches carry R2 object keys, never clause-ids or tool arguments. The hosted Cloudflare Worker collects only standard request metadata (IP for rate limiting, timestamps, request headers); it does not log request bodies, set cookies, or share data with third parties.

Full policy: mcp.xyzzylabs.ai/tc39/privacy

For privacy questions, open an issue with the privacy label on GitHub.

License

MIT

Source: README.md at commit 286e9bc

Tools

17

spec.about

Self-description of this MCP server: package name + version, per-snapshot pin metadata (sha, fetched_at, biblio_commit, clause_count) for every supported (spec, edition), plus test262 + proposals index headers when present.

Takes no parameters.

clause.get

Fetch a parsed TC39 clause as structured JSON. `spec` selects '262' (default) or '402'. `edition` defaults to 'latest'. `at: '<sha>'` pins to a historical main snapshot (only valid for edition='main'); omit to query the live snapshot.

Parametersatidrequiredspecedition

clause.list

List parsed spec clauses with optional filters (kind, section prefix, has_algorithm). `spec` selects '262' or '402'. `at: '<sha>'` queries a historical main snapshot.

Parametersatkindspeclimiteditionsectionhas_algorithm

proposal.list

List TC39 proposals from the static index (tc39/proposals). Filter by spec ('262'|'402'), stage ('0'|'1'|'2'|'2.7'|'3'|'finished'|'inactive'|'active'), champion (substring), or contains (name/slug substring).

Parametersspeclimitstagechampioncontains

proposal.get

Fetch one TC39 proposal by slug (exact) or name (case-insensitive).

Parametersnamerequired

spec.grammar

Query grammar productions captured from the spec's `<emu-grammar>` blocks. `{ nonterminal }` returns every production for that non-terminal (exact match); `{ contains }` filters by RHS / name substring; neither lists all non-terminals + their production counts. `include_sdo` folds in SDO-attached productions (off by default).

Parametersspeclimiteditioncontainsinclude_sdononterminal

spec.tables

List or fetch parsed `<emu-table>` content. `{ id }` returns exactly that table (full columns + rows); otherwise list table summaries, optionally narrowed by a `filter` substring over the caption or id.

Parametersidspeclimitfilteredition

spec.sdo_index

Index Syntax-Directed Operations by the grammar production they're defined on. `by: 'production'` (default) groups SDOs under each production; `by: 'sdo'` groups productions under each SDO title. `filter` narrows to keys containing a substring (case-insensitive).

Parametersbyspeclimitfilteredition

clause.outline

Return the section tree (table of contents) for a parsed (spec, edition). `depth` caps how deep the tree descends (1 = top-level only); `under` limits the tree to descendants of one clause id.

Parametersspecdepthunderedition

spec.snapshots

List the live (spec, edition, sha, fetched_at) snapshots the hosted Worker is serving from R2. Filter by `spec` ('262'|'402') or `edition` (e.g. 'main', 'es2026'). Historical SHA-pinned copies are reachable via `at:` on clause.get / spec.search but aren't enumerated here.

Parametersspecedition

spec.symbol_resolve

Resolve spec notation like `[[Prototype]]` (internal slot), `%Object.prototype%` (well-known intrinsic), or `~number~` (sigil enum): return clauses that mention or define it, ranked by occurrence with a bump for definition-y sections.

Parametersspeclimiteditionnotationrequired

spec.well_known_intrinsics

Enumerate the well-known intrinsics in a spec with their probable defining clauses. Driven from the canonical §6.1.7.4 WKI table when present (ECMA-262), else a `%X%` prose scan (e.g. ECMA-402). `filter` narrows by bare-name substring.

Parametersspeclimitfilteredition

spec.diff

Clause-level diff of one clause across two editions of a spec. Reports identical / modified / added / removed plus a field-level breakdown (title, signature, step count, reworded step indices, notes, crossrefs). `from` defaults to the latest stable release, `to` to main.

Parametersidrequiredtofromspec

spec.crossrefs

For a clause id, return its outgoing references (clauses it cites) and/or incoming references (clauses that cite it — the back-reference index the parse alone doesn't expose). The reverse index is AOID-densified from step text. `direction`: 'in' | 'out' | 'both' (default). Outgoing also carries an `external` category: the clause's citations to external specs (Unicode, IETF, WHATWG) as resolvable URLs. Set `include_cross_spec: true` to also resolve outgoing references from ECMA-262 → ECMA-402 (or vice versa).

Parametersidrequiredspeclimiteditiondirectioninclude_cross_spec

Version history

2
  1. v0.6.4LatestOct 7, 2026
  2. v0.6.3Sep 16, 2026

Score

100% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 daysRequired25 pts
  • Auth declaredRequired10 pts
  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts