Structural TC39 spec lookup (ECMA-262 + ECMA-402) for AI agents β SHA-pinned, offline-first.
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
π‘ Paste the JSON block into your client's configuration file under mcpServers, then restart the application.
π 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.
Wire it into any MCP client β the stdio launch command is the same everywhere, only the config file differs:
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.getto readsec-tonumberand show me the steps
You should see structured JSON back:
Five-minute walkthrough: docs/getting-started.md.
Point your client at the hosted Cloudflare Worker instead of running a local subprocess β same MCP protocol, no install:
Traffic is rate-limited to 30 req/min per IP.
spec.search ranks
AOID-exact matches first; spec.symbol_resolve decodes
[[Prototype]] / %Object.prototype% / ~enumerate~.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.)spec.diff
between any two editions back to ES2016; spec.history walks
the upstream git log via pickaxe search.
(Cookbook recipe 2.)test262.search
with prefix-matched esid: catches sec-tonumber AND
sec-tonumber-applied-to-the-string-type in one call.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.~/.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.| Goal | Tool(s) |
|---|---|
| Verify what's being served | spec.about Β· spec.snapshots |
| Read a specific clause | clause.get |
| Find a clause from a name / symptom | spec.search Β· spec.global_search |
Resolve [[X]] / %X% / ~X~ notation | spec.symbol_resolve |
| Browse / outline | clause.list Β· clause.outline |
| Compare editions / commit history | spec.diff Β· spec.history |
| Walk references (in + out) | spec.crossrefs |
| Read structured tables | spec.tables |
| Inspect the grammar | spec.grammar Β· spec.sdo_index |
| Enumerate well-known intrinsics | spec.well_known_intrinsics |
| Find conformance tests | test262.search Β· test262.get |
| Look up a proposal | proposal.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.
Every spec-reading tool accepts spec ("262" or "402", default
"262") and edition (default "latest").
es2016 β es2026, main. (ES5 / ES5.1 / ES6
have no upstream tags and aren't supported.)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.)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.
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:
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).
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.
Point your MCP client at your local source instead of the published bin:
No reviews yet β be the first to share how this listing worked for you.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/tc39-mcp)<a href="https://allmcps.com/mcp/tc39-mcp"><img src="https://allmcps.com/api/badge/tc39-mcp?style=directory" alt="Tc39 MCP on AllMCPs" /></a>