Skip to content

Releases: basgr/cf-webmcp

v0.4.1 - Duplicate WebMCP tool-name renderer crash

10 Jun 09:44

Choose a tag to compare

Registering the same WebMCP tool name twice on a page kills the Chrome renderer
(bad_message 345, RFHI_WEBMCP_REGISTER_DUPLICATE_TOOL_NAME) - a Mojo IPC
validation kill that try/catch cannot trap. cf-webmcp emits two registration
surfaces per page (the bootstrap's registerTool calls and any <form toolname>
it stamps), so a name shared across [[tools]] and [[forms]] would crash.

  • Build guard: the build now fails on a duplicate name within [[tools]],
    within [[forms]], or shared across the two.
  • Runtime de-dupe: the injected bootstrap skips registering any tool whose
    name is already declared on the page as a <form toolname> (covers names
    hand-stamped in origin HTML, which the build cannot see).
  • Host detection: probes document.modelContext (current, Chrome 150+)
    before the deprecated navigator.modelContext (146-149).

v0.4.0 - extensionless manifest + runtime-compat bootstrap

03 Jun 07:27

Choose a tag to compare

Highlights

Extensionless manifest path is the default

The canonical WebMCP manifest now lives at /.well-known/webmcp (extensionless), matching the convention of IANA-registered well-known suffixes (api-catalog, openid-configuration). The legacy /.well-known/webmcp.json is kept as a 301 redirect alias so existing links and cached rel="webmcp" references keep working.

  • No action required: rebuilding moves the canonical manifest to /.well-known/webmcp and advertises it in the Link header, <link rel="webmcp">, llms.txt, and agents.md.
  • To keep .json canonical instead, set [manifest].path = "/.well-known/webmcp.json" and [manifest].aliases = ["/.well-known/webmcp"] (or [] to drop the redirect).
  • The 301 emits X-Robots-Tag: noindex and X-Content-Type-Options: nosniff (CI-enforced for every /.well-known/* route).

Bootstrap runtime compatibility

The injected in-page bootstrap was corrected against the current WebMCP runtime:

  • Host detection registers tools on whichever object exposes registerTool: navigator.modelContext (current Chrome Canary) or document.modelContext (Apr 2026 WebMCP draft). It no-ops when neither is present.
  • MCP tool-result shape: each tool's execute now returns { content: [{ type: "text", text }], isError } instead of the raw cf-webmcp { ok, data } envelope. The full executor envelope is carried as the text payload (the agent keeps structured success/error), and isError is set unless the envelope is an explicit ok: true. The POST /_webmcp/exec/<tool> endpoint is unchanged and still returns the envelope; only the in-page tool adapts it.

Other

Quality gate

  • Security review: no critical/high findings.
  • 258 tests pass (233 worker + 25 node); typecheck clean.

Full changelog: v0.3.9...v0.4.0

v0.3.9 - llms.txt token-budget hints + rel="alternate" markdown link

02 Jun 06:22

Choose a tag to compare

Two enrichments of existing discovery surfaces, both gated on the
llms_txt feature, prompted by a cross-review against Addy Osmani's
agentic-seo auditor.

llms.txt token-budget hints

The synthesized /llms.txt WebMCP block now annotates its pairing-page and
tool-catalogue links with (~N tokens) context-budget hints, e.g.:

Counts are computed at build time over the exact bodies the Worker
serves (manifest JSON, landing HTML) with a dependency-free ~4-chars/token
heuristic, emitted as a generated constant. agents.md and api-catalog
links stay unannotated (synthesized at request time, thin pointer docs).
This clears the one point agentic-seo's llms.txt check docked the demo
(9/10 → 10/10).

rel="alternate" markdown link

HTML pages now carry <link rel="alternate" type="text/markdown" href=".../llms.txt"> alongside the existing rel="describedby" tag,
matching the convention agentic-seo and specification.website use to
advertise a markdown representation. Reuses the same attribute escaping;
HTML-only (not added to the Link header).

Notes

  • A cross-review of agentic-seo found it is primarily a local-repo CI
    auditor
    : in URL-only mode only 3 of its 10 checks run, so its headline
    score is not a meaningful external grade. Lighthouse 13.3.0's
    agentic-browsing category remains the recommended external validator.
  • Security review: clean. All new values are build-time config-derived
    integers or an already-hardened link-tag clone; no new untrusted-input path.

Tests

  • 232 worker + 25 node = 257 green. Typecheck clean.

v0.3.8 - Lighthouse validation, toolchain refresh, specification.website cross-review

31 May 08:57

Choose a tag to compare

Validation

  • README now documents that cf-webmcp's discovery surfaces pass Google's
    Lighthouse 13.3.0 agentic-browsing audit category end-to-end on
    Chrome Canary 150.x. An independent third-party auditor confirms both
    injected and hand-stamped tools without TOML/source access.

Security

  • Bumped the dev test/build toolchain to clear all 11 Dependabot advisories
    (4 high, 7 moderate). Affected only the toolchain - the deployed Worker
    bundle was never vulnerable.
    • vitest 2 -> 4.1.7
    • @cloudflare/vitest-pool-workers 0.5 -> 0.16.10 (migrated
      vitest.config.ts to the new cloudflareTest() plugin pattern per
      Cloudflare's official v3->v4 codemod)
    • wrangler 4.90 -> 4.95 (clears the wrangler pages deploy OS-command-
      injection advisory; cf-webmcp uses wrangler deploy, not the affected
      path)
  • npm audit: 0 vulnerabilities.

Discovery polish

  • HTTP Link header entries now carry RFC 8288 title="..." human-readable
    metadata on every advertised rel (webmcp, api-catalog, agent-skills,
    describedby). The describedby title matches the wording used by
    specification.website for the same
    target, for free interop with peer publishers. No protocol impact.

Scope clarification

  • docs/scope.md adds A2A agent cards as an explicit out-of-scope entry,
    plus a new "Adjacent agent-readiness surfaces" section pointing at
    specification.website (Joost de Valk)
    as the worked-example reference for the surfaces cf-webmcp deliberately
    does not ship (markdown source endpoints, robots.txt Content-Signal,
    web-bot-auth, schemamap, DNS-AID, etc.).
  • README acknowledges specification.website as an independent reference
    converging on the same Agent Skills Discovery, RFC 9727 api-catalog,
    rel="describedby" llms.txt, and rel="agent-skills" conventions.

v0.3.7 - rel="describedby" pointing at /llms.txt (RFC 8288)

25 May 19:59

Choose a tag to compare

Small discoverability patch. cf-webmcp's existing /llms.txt is now advertised via the IANA-registered rel="describedby" relation (RFC 8288), in addition to the existing webmcp / api-catalog / agent-skills rels.

Why

The previous three rels we emit on every response are:

  • rel="webmcp" — private, not IANA-registered
  • rel="api-catalog" — IANA-registered via RFC 9727
  • rel="agent-skills" — private (Cloudflare RFC v0.2.0 does not register a rel)

Generic agent-aware scanners that anchor only on registered rel-types therefore find one entry. Adding rel="describedby" pointing at our existing /llms.txt gives those scanners a fourth, RFC-registered way to discover a description of the site — without adding any new well-known endpoints.

Surfaced by Suganthan Mohanadasan's May 2026 post which highlights describedby as the RFC-registered relation for site descriptions.

What's emitted

HTTP Link header (every response):

Link: <https://example.com/.well-known/webmcp.json>; rel="webmcp",
      <https://example.com/.well-known/api-catalog>; rel="api-catalog",
      <https://example.com/.well-known/agent-skills/site/SKILL.md>; rel="agent-skills",
      <https://example.com/llms.txt>; rel="describedby"; type="text/markdown"

HTML <head> (injected on every HTML response):

<link rel="describedby" type="text/markdown" href="https://example.com/llms.txt">

The type="text/markdown" hint is per RFC 8288 so scanners can pre-decide whether to fetch.

Gate

Both surfaces emit only when:

  • [features].llms_txt = true, AND
  • [llms_txt].mode != "passthrough"

Same pattern as our other discovery surfaces: don't advertise a route we don't serve.

Tests

  • 3 new tests in src/link-header.test.ts (gate logic)
  • 2 new tests in src/injection/html-rewriter.test.ts (link-tag injection)
  • 226 worker tests + 24 build-config tests = 250 total, all green
  • typecheck clean

Live verification

$ curl -sI https://webmcp.basgr.com/ | grep -oE 'rel="[^"]+"' | sort -u
rel="agent-skills"
rel="api-catalog"
rel="describedby"
rel="webmcp"

Compatibility

  • No config changes required. No schema_version bump.
  • Existing publishers upgrading get the new entry automatically on next deploy.
  • Disable via [features].llms_txt = false or [llms_txt].mode = "passthrough" (already-supported toggles).

Upgrade

cd cf-webmcp
git fetch
git checkout v0.3.7
npm install
npm run build
wrangler deploy

v0.3.6 - SRI on injected bootstrap; docs/security.md

20 May 20:12

Choose a tag to compare

Two related changes: Subresource Integrity (SRI) on the injected bootstrap <script>, and a new docs/security.md documenting what cf-webmcp does and does not defend against.

SRI on the bootstrap <script>

The HTMLRewriter injection that fires on every HTML response now emits:

<script src="https://example.com/_webmcp/bootstrap.<hash>.js" defer
        integrity="sha384-<base64>" crossorigin="anonymous"></script>

The sha384 digest is computed at build time over the exact UTF-8 bytes of the bootstrap source and embedded as BOOTSTRAP_SRI in the generated config module. Browsers refuse to execute the bootstrap if its body has been substituted between server and client.

Covers:

  • CDN cache poisoning serving a modified bootstrap at our URL
  • MITM on non-HTTPS legs (HTTPS already handles most cases; SRI is belt-and-suspenders)
  • Internal bug where the worker accidentally serves a stale or wrong bootstrap

Does not cover:

  • Compromised publisher who controls the TOML and re-deploys with a malicious bootstrap (they control the integrity hash too)
  • Cross-origin prompt-injection via tool descriptions — see docs/security.md
  • Service worker hijacks that strip the integrity attribute before the browser sees it

docs/security.md

New page making explicit what cf-webmcp does and does not enforce, anchored on the load-bearing claim:

Tool descriptions are not a security boundary.

Earlence Fernandes (UCSD) published a finding in April 2026 showing how a poisoned WebMCP tool description on attacker site A can prompt-inject an agent into invoking tools on victim site B via the agent's cross-tab model context. Browser SOP is doing its job; the agent runtime carries context across origins outside the browser.

cf-webmcp serves tool descriptions verbatim per the publisher's TOML and structurally cannot sanitise them — descriptions ARE the content agents consume. The doc spells out:

  • The attack class and why it falls outside cf-webmcp's layer
  • Publisher operational implications (never paste UGC into descriptions; treat hint/description bodies as model-visible text; if your TOML is CMS-generated, gate fields behind editorial review)
  • Agent-runtime trust model (treat tool endpoints as public read-only HTTP)
  • The new SRI behaviour as a separate, network-layer defence
  • Vulnerability reporting (email vs public issue)

Cross-links from README.md, docs/scope.md, docs/form-injection.md, docs/agent-skills.md so publishers see the call-out at the point they're authoring the risky content. docs/deployment.md gets a new "Subresource Integrity (SRI) on the injected bootstrap" section with CSP interaction notes (script-src 'self' works; 'strict-dynamic' needs a nonce or hash-pin; explicit sha384-X hash-source allowlists work alongside SRI).

Config

[features]
subresource_integrity = true   # default

Turn off only if a downstream tooling layer cannot accept the integrity attribute. Flipping it off is a deliberate security regression, not a casual cleanup.

Tests

  • 4 new HTMLRewriter cases (attribute presence/absence, escape safety against malformed integrity values, attribute order on the tag)
  • 3 new build-config cases (sha384-base64 shape, byte-exact match against sha384(bootstrap.js), null when feature off)
  • All existing fixtures extended with features.subresource_integrity
  • 245 tests green (221 worker + 24 build-config)

Live verification

$ curl -s https://webmcp.basgr.com/ | grep -oE '<script[^>]*bootstrap[^>]*>'
<script src="https://webmcp.basgr.com/_webmcp/bootstrap.b44e5237.js" defer
        integrity="sha384-vLLOpLJLQjO6YdeIDZB70nFto3GYFSr0aqLdEeDUDNVkCinMQDVew8TQEuTMAPxY"
        crossorigin="anonymous">
$ curl -s https://webmcp.basgr.com/_webmcp/bootstrap.b44e5237.js | \
    openssl dgst -sha384 -binary | base64
vLLOpLJLQjO6YdeIDZB70nFto3GYFSr0aqLdEeDUDNVkCinMQDVew8TQEuTMAPxY
# MATCH

Compatibility

  • No schema_version bump. New field is additive optional.
  • Existing publishers upgrading get SRI automatically on next deploy.
  • The injected script tag is byte-different (it now includes integrity and crossorigin). If you depend on a specific HTML diff or CSP hash for the injected HTML, you may need to refresh that.
  • For publishers with strict CSP script-src source lists: SRI integrates cleanly with sha384-X hash-source entries (the same string works for both CSP and SRI). See docs/deployment.md.

Upgrade

cd cf-webmcp
git fetch
git checkout v0.3.6
npm install
npm run build
wrangler deploy

v0.3.5 - /.well-known/agent-skills/index.json (Cloudflare RFC v0.2.0)

19 May 06:22

Choose a tag to compare

cf-webmcp now publishes the Cloudflare Agent Skills Discovery RFC v0.2.0 index file at /.well-known/agent-skills/index.json. Agent runtimes that scan well-known paths for a skill index now find this site's SKILL.md (already published since v0.3.0) listed with a SHA-256 digest for integrity verification.

What's emitted

{
  "$schema": "https://schemas.agentskills.io/discovery/0.2.0/schema.json",
  "skills": [
    {
      "name": "<slugified-site-name>",
      "type": "skill-md",
      "description": "<site description>",
      "url": "/.well-known/agent-skills/site/SKILL.md",
      "digest": "sha256:<64hex>"
    }
  ]
}

The digest is computed at build time over the synthesised SKILL.md body (frontmatter + body) and embedded as a constant in the generated config module. No runtime hashing, no cache invalidation surprises.

Config

[features]
agent_skills_index = true   # default

[agent_skills_index]
path = "/.well-known/agent-skills/index.json"   # default per RFC
mode = "synthesize"                              # synthesize | passthrough

merge and replace are intentionally not supported:

  • merge would require fetching origin on every request to compute a stable digest
  • replace is functionally identical to synthesize for our one-skill case

Behaviour when SKILL.md is unstable

If [agent_skills].mode = "merge", the SKILL.md content mixes in origin content and the build-time digest cannot represent the served body. The index route returns 404 with an explanatory body rather than serve a stale digest. Switch [agent_skills].mode to synthesize or replace to enable the index, or set [features].agent_skills_index = false to silence it.

$schema version-lock

cf-webmcp emits the $schema URI for v0.2.0 of the RFC. If the RFC bumps incompatibly (as v0.1.0 → v0.2.0 already did), we revise in a future release. Per the spec, agents MUST check $schema and skip unknown versions cleanly.

Discovery surfaces

Three pointers to the same skill now coexist:

  1. The SKILL.md itself at /.well-known/agent-skills/site/SKILL.md
  2. The manifest's links.agent_skills_index field
  3. The new /.well-known/agent-skills/index.json index document

No HTTP Link rel is defined by the RFC; discovery of the index is well-known-path only.

Build-time hardening

  • Path-collision check. The build refuses configs where two Worker-owned surfaces (manifest, landing, llms.txt, robots.txt, agents.md + aliases, api-catalog, agent-skills + aliases, agent-skills-index) claim the same path. Router uses first-match semantics, so a collision would silently break the second-listed surface.
  • End-to-end digest equivalence test. Locks the contract that the build-time digest matches sha256(agentSkillsResponse body) byte-for-byte. Defends against future refactors of either producer drifting.

Tests

  • 9 new tests in src/routes/agent-skills-index.test.ts covering modes, 404 fallback, digest semantics, $schema correctness, cache headers, byte-stable output, slug-name derivation, end-to-end digest match
  • 2 new tests in src/router.test.ts (routes when on, falls through when off / passthrough)
  • 7 new tests in scripts/build-config.test.ts (manifest links presence/absence, AGENT_SKILLS_DIGEST emission per mode, path-collision rejection)
  • src/x-robots.test.ts extends exhaustive Record<RouteMatch["kind"], ...> coverage to the new kind
  • 239 total tests, all green (218 worker + 21 build-config)

Live verification

$ curl -s https://webmcp.basgr.com/.well-known/agent-skills/index.json | jq
{
  "$schema": "https://schemas.agentskills.io/discovery/0.2.0/schema.json",
  "skills": [
    {
      "name": "cf-webmcp",
      "type": "skill-md",
      "description": "WebMCP at the edge...",
      "url": "/.well-known/agent-skills/site/SKILL.md",
      "digest": "sha256:318719d76991ecc284f3d4ad15479e3ef655da90c482d31581239c0eaccfb6de"
    }
  ]
}

$ curl -s https://webmcp.basgr.com/.well-known/agent-skills/site/SKILL.md | sha256sum
318719d76991ecc284f3d4ad15479e3ef655da90c482d31581239c0eaccfb6de  -
# MATCH

Compatibility

  • [features].agent_skills_index defaults to true. Existing publishers upgrading get the index automatically on next deploy.
  • schema_version stays at 1; no breaking schema changes.
  • Publishers using [agent_skills].mode = "merge" will see the index route return 404 with an explanatory body. Either flip the underlying mode to synthesize / replace or disable the feature.
  • Path-collision check is new: a publisher whose TOML accidentally claims the same path for two surfaces will see the build fail fast with a clear error.

Upgrade

cd cf-webmcp
git fetch
git checkout v0.3.5
npm install
npm run build
wrangler deploy

Templates updated with the new block. If you carry a custom TOML, copy [agent_skills_index] from templates/default.toml or rely on the Zod defaults.

v0.3.4 - WebMCP ToolAnnotations

18 May 19:47

Choose a tag to compare

A spec-completeness pass. The bootstrap script now emits the two ModelContextTool members cf-webmcp was previously not setting: annotations and title. Brings the registerTool() call into shape with the current W3C draft at webmachinelearning/webmcp.

What the spec says

dictionary ModelContextTool {
  required DOMString name;
  USVString title;             // <-- optional, was missing
  required DOMString description;
  object inputSchema;
  required ToolExecuteCallback execute;
  ToolAnnotations annotations; // <-- optional, was missing
};

dictionary ToolAnnotations {
  boolean readOnlyHint = false;
  boolean untrustedContentHint = false;
};

cf-webmcp's bootstrap now emits both. WebMCP-aware browsers (Chrome 148+ with the flag) read the annotations during registration; runtimes that ignore them are unaffected.

Annotations are derived from the executor type

[[tools]].executor.type already tells cf-webmcp what each tool does. The bootstrap sets sensible defaults:

Executor type readOnlyHint untrustedContentHint
sitemap_filter true false
rss_feed true true
dom_extract true true
http_json true true
http_get true true

All five built-in executors are read-only by construction (none mutate origin state), hence readOnlyHint: true across the board. Four of five surface origin-fetched content the agent should treat as untrusted; sitemap_filter returns structurally-constrained URL + lastmod strings only, so its untrustedContentHint defaults to false.

Per-tool overrides

When the executor-type defaults are wrong for your case, set them explicitly in TOML:

[[tools]]
name        = "site_status"
description = "Current health of this site."

  [tools.executor]
  type      = "http_get"
  url_template = "https://example.com/_status.json"

  [tools.annotations]
  read_only_hint         = true
  untrusted_content_hint = false   # we author this content ourselves

Snake-case in TOML (read_only_hint, untrusted_content_hint) maps to the spec's camelCase (readOnlyHint, untrustedContentHint) in the generated JS. Publishers write the natural TOML form; the bootstrap matches the WebIDL.

Optional title

A human-friendly tool label distinct from the machine-friendly name. Surfaces in tool pickers and similar UI. No default - emitted only when set.

[[tools]]
name        = "search_pages"
title       = "Page Search"
description = "Search the cf-webmcp site by keyword."

docs/scope.md update

The "MCP server cards" out-of-scope bullet now spells out the exact well-known path third-party write-ups reference (/.well-known/mcp/server-card.json), notes cf-webmcp does not run a streamable HTTP MCP endpoint, and documents the GET /mcp landing vs POST /mcp JSON-RPC path conflict for publishers running both side by side. Includes the concrete workaround: set [webmcp_landing].path = "/pair" to free /mcp for the MCP JSON-RPC endpoint.

Tests

  • 4 new build-config tests: default annotations per executor type, override behaviour, title surface
  • 206 worker tests + 15 build-config tests = 221 total, all green
  • CI green at 8f46fcb

Compatibility

  • No schema_version bump. New fields are additive optionals; existing TOML configs compile unchanged.
  • The generated bootstrap is byte-different from v0.3.3 because every tool now carries an annotations object. The config_hash is derived from the TOML, not the generated JS, so the bootstrap URL does not change for publishers whose TOML is unchanged. Browsers with the v0.3.3 bootstrap cached at the same URL will serve stale until cache expires; a no-op TOML edit (e.g. a comment change) rotates the hash and forces a fresh fetch.
  • No new dependencies, no breaking API changes, no schema changes that require publisher action.

Live verification

$ curl -s https://webmcp.basgr.com/_webmcp/bootstrap.<hash>.js \
    | grep -o 'annotations":{[^}]*}'
annotations":{"readOnlyHint":true,"untrustedContentHint":false}
annotations":{"readOnlyHint":true,"untrustedContentHint":true}
annotations":{"readOnlyHint":true,"untrustedContentHint":true}

Upgrade

cd cf-webmcp
git fetch
git checkout v0.3.4
npm install
npm run build
wrangler deploy

Templates are unchanged. If you want to add title or override annotations per tool, edit your webmcp.toml.

v0.3.3 - X-Robots-Tag noindex enforced

14 May 17:40

Choose a tag to compare

Policy

Every URL cf-webmcp serves under /_webmcp/* or /.well-known/* now emits X-Robots-Tag: noindex. The two explicit exemptions:

  • /llms.txt at apex — some publishers want it surfaced in AI-search indexes
  • /robots.txt at apex — special-cased by every major crawler regardless

/mcp landing is at apex; not in scope of the rule but already had noindex set by design (it's a per-visitor pairing surface).

What was missing

The audit found nine response paths under the protected prefixes that emitted no x-robots-tag:

Where Path
jsonResponse default headers All /_webmcp/exec/* error envelopes + success + cache-hit relay
exec.ts 405 / preflight 204 /_webmcp/exec/* method-not-allowed / CORS preflight
widget.ts 405 / 503 /_webmcp/widget.*.js method-not-allowed / asset-missing
health.ts 404 / 401 /_webmcp/health disabled / unauthorized
worker.ts SSRF rejection 502 (off-host redirect) reachable from any merge-mode route, including those under /.well-known/*
worker.ts SSRF rejection 502 (malformed final URL) same
agents-md.ts passthrough relay /.well-known/agents.md when origin returns unexpected content-type
api-catalog.ts passthrough relay /.well-known/api-catalog same
agent-skills.ts passthrough relay + 301 redirect /.well-known/agent-skills/<slug>/SKILL.md and case-variant aliases

All nine now stamp X-Robots-Tag: noindex.

How regressions are caught

src/x-robots.test.ts is the exhaustive coverage test. It uses Record<RouteMatch["kind"], "noindex_required" | "exempt"> so the file fails to compile if a new route kind is added to router.ts without classification. For every "noindex_required" kind, it builds the canonical response via the relevant handler and asserts the header is present. A cross-check verifies that no "exempt" kind's canonical path is actually under a protected prefix (catches misclassification).

This is the contract going forward: adding a new route handler under /_webmcp/* or /.well-known/* requires updating the classification map and emitting the header.

Live verification

$ curl -sI https://webmcp.basgr.com/.well-known/webmcp.json | grep -i x-robots
x-robots-tag: noindex

$ curl -sI https://webmcp.basgr.com/.well-known/api-catalog | grep -i x-robots
x-robots-tag: noindex

$ curl -sI https://webmcp.basgr.com/.well-known/agents.md | grep -i x-robots
x-robots-tag: noindex

$ curl -sI https://webmcp.basgr.com/.well-known/agent-skills/site/SKILL.md | grep -i x-robots
x-robots-tag: noindex

$ curl -sI https://webmcp.basgr.com/.well-known/agent-skills/site/SKILLS.md | grep -i x-robots
x-robots-tag: noindex

$ curl -sI https://webmcp.basgr.com/_webmcp/health | grep -i x-robots
x-robots-tag: noindex

# Exemptions correctly do NOT carry the header:
$ curl -sI https://webmcp.basgr.com/llms.txt | grep -i x-robots
$ curl -sI https://webmcp.basgr.com/robots.txt | grep -i x-robots

Tests

  • 11 new coverage tests in src/x-robots.test.ts
  • 206 worker tests + 11 build-config tests, all green
  • typecheck clean
  • CI green at 163b144

Compatibility

  • No breaking changes. schema_version stays at 1.
  • Existing publishers upgrading get the headers automatically on next deploy. If you depended on a cf-webmcp response under /_webmcp/* or /.well-known/* being indexable (you shouldn't have), this release stops it.

Upgrade

cd cf-webmcp
git fetch
git checkout v0.3.3
npm install
npm run build
wrangler deploy

v0.3.2 - Patch: woocommerce template fix, wrangler 4, extra path-char hardening

14 May 08:33

Choose a tag to compare

Fixes

templates/woocommerce.toml inheritance now builds clean

The shipped woocommerce template inherits from wordpress.toml (which uses example.com placeholders), but the woocommerce template itself was using shop.example.com. The build-time SSRF allow-list check correctly flagged this: inherited wordpress tool URLs (pointing at example.com) didn't match the child's allowed_origins (shop.example.com).

Fixed by aligning the woocommerce template to the same example.com placeholder convention. Publishers still copy the template and replace example.com with their actual domain - no change to deploy workflow. The template now compiles cleanly out of the box (10 tools total: 6 inherited from wordpress, 4 woocommerce-specific).

PathString now also blocks U+2028 / U+2029

JS LINE SEPARATOR and PARAGRAPH SEPARATOR are legal in modern URL parsers but pre-ES2019 inline <script> contexts treat them as line terminators. cf-webmcp doesn't currently inline paths into JS, but blocking is free defense-in-depth and keeps the path char-set future-compatible.

Two new test cases added to the existing rejection sweep in scripts/build-config.test.ts.

wrangler bumped 3.x → 4.x

CI was warning about the out-of-date wrangler. wrangler@4.90.1 is a clean drop-in:

  • Worker bundle build, worker deploy, pages deploy, r2 object put - all verified compatible
  • vitest-pool-workers keeps its own pinned wrangler 3.x internally (intentional, no behavior split at deploy time)
  • 195 worker tests + 11 build-config tests green under wrangler 4
  • Compatibility dates pinned at 2024-09-23 across all wrangler tomls, so no runtime semantics drift
  • New defaults (upload_source_maps, observability) remain opt-in; cf-webmcp doesn't set them

Cosmetic

  • wrangler.example.toml now sets compatibility_flags = ["nodejs_compat"] to match the dev/test wrangler configs.
  • package.json version bumped 0.1.00.3.2 to match the release tag.

Compatibility

  • No breaking changes. schema_version stays at 1.
  • The PathString tightening rejects characters no legitimate path uses.
  • The wrangler bump preserves all CLI args we depend on. If your own deploy uses commands or flags we don't (e.g. some legacy wrangler kv:key syntax), check the wrangler 4 migration notes.

Upgrade

cd cf-webmcp
git fetch
git checkout v0.3.2
npm install   # picks up wrangler 4
npm run build
wrangler deploy