{"id":50424985,"url":"https://github.com/basgr/cf-webmcp","last_synced_at":"2026-05-31T10:01:02.729Z","repository":{"id":357609412,"uuid":"1237567076","full_name":"basgr/cf-webmcp","owner":"basgr","description":"WebMCP at the edge. A Cloudflare Worker that equips any website with WebMCP from one TOML config.","archived":false,"fork":false,"pushed_at":"2026-05-28T08:40:35.000Z","size":491,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-28T10:23:47.450Z","etag":null,"topics":["ai-agents","cloudflare","cloudflare-workers","edge-computing","mcp","model-context-protocol","typescript","webmcp"],"latest_commit_sha":null,"homepage":"https://webmcp.basgr.com/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/basgr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"docs/security.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-13T09:51:53.000Z","updated_at":"2026-05-28T08:40:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/basgr/cf-webmcp","commit_stats":null,"previous_names":["basgr/cf-webmcp"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/basgr/cf-webmcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/basgr%2Fcf-webmcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/basgr%2Fcf-webmcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/basgr%2Fcf-webmcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/basgr%2Fcf-webmcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/basgr","download_url":"https://codeload.github.com/basgr/cf-webmcp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/basgr%2Fcf-webmcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33726719,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agents","cloudflare","cloudflare-workers","edge-computing","mcp","model-context-protocol","typescript","webmcp"],"created_at":"2026-05-31T10:01:01.999Z","updated_at":"2026-05-31T10:01:02.720Z","avatar_url":"https://github.com/basgr.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cf-webmcp\n\n[![CI](https://github.com/basgr/cf-webmcp/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/basgr/cf-webmcp/actions/workflows/ci.yml) [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\nA Cloudflare Worker that sits in front of a website and equips it with [WebMCP](https://github.com/webmachinelearning/webmcp). One TOML file in, every WebMCP-aware browser sees the site's tools out.\n\n\u003e Not affiliated with Cloudflare. The `cf-` prefix only reflects that the project runs exclusively on Cloudflare primitives - this is not an official Cloudflare product.\n\n## What it does\n\nFor each request, the Worker does one of two things:\n\n**1. Handle a Worker-owned path directly.**\n\n| Path | What lives there |\n|------|------------------|\n| `/.well-known/webmcp.json` | Tool-catalogue manifest, machine-readable JSON |\n| `/.well-known/api-catalog` | RFC 9727 Linkset (RFC 9264) pointing at the manifest |\n| `/.well-known/agents.md` (+ `/AGENTS.md`, `/agents.md` 301 aliases) | AGENTS.md augmentation block for acting agents |\n| `/.well-known/agent-skills/\u003cslug\u003e/SKILL.md` (+ case-variant 301 aliases) | Anthropic-format Agent Skill, auto-generated from `[[tools]]` plus publisher hints |\n| `/.well-known/agent-skills/index.json` | Cloudflare Agent Skills Discovery RFC v0.2.0 index with build-time SHA-256 digest |\n| `/llms.txt` | Origin's llms.txt with a WebMCP block merged in |\n| `/robots.txt` | Origin's robots.txt with `Disallow: /_webmcp/` merged in |\n| `/mcp` | Landing page: native-API, desktop-pairing, or disabled state |\n| `/_webmcp/exec/\u003ctool\u003e` | Tool execution endpoint (POST) |\n| `/_webmcp/bootstrap.\u003chash\u003e.js` | In-page tool registration script |\n| `/_webmcp/widget.\u003chash\u003e.js` | Optional desktop-bridge widget |\n| `/_webmcp/health` | Operational health endpoint |\n\n**2. Otherwise, proxy to origin and modify the response on the way back.**\n\n- **HTTP `Link` header** added to every proxied response (HTML, PDF, image, JSON, anything). One entry per discovery surface: `rel=\"webmcp\"` to the manifest, `rel=\"api-catalog\"` to the catalog, `rel=\"agent-skills\"` to the SKILL.md. An agent doing a `HEAD` request finds all three without parsing a body.\n- **On HTML responses only** (status 200, `text/html`, UTF-8, path not in `[injection].exclude_paths`), HTMLRewriter injects:\n  - matching `\u003clink\u003e` tags into `\u003chead\u003e` (`rel=\"webmcp\"`, `rel=\"api-catalog\"`, `rel=\"agent-skills\"`),\n  - one `\u003cscript src=\"/_webmcp/bootstrap.\u003chash\u003e.js\" defer\u003e` before `\u003c/body\u003e` that auto-registers the tools via `navigator.modelContext`,\n  - W3C declarative form attributes (`toolname`, `tooldescription`, `toolparamdescription`, `toolautosubmit`) stamped onto matching `\u003cform\u003e` elements when a `[[forms]]` block matches the current path.\n\nNon-HTML responses (PDFs, images, JSON, CSS, JS, etc.) pass through with their body unchanged but with the `Link` header added.\n\nThe tool catalogue lives in one TOML file. Five server-side executor types (`sitemap_filter`, `rss_feed`, `dom_extract`, `http_json`, `http_get`) cover the imperative tool path; `[[forms]]` blocks cover the declarative-form path. Three deploy templates ship: `default`, `wordpress`, `woocommerce` (Store API).\n\n## Discovery surfaces\n\ncf-webmcp publishes the same tool catalogue through multiple complementary surfaces, all driven from the single TOML:\n\n- `/.well-known/webmcp.json` (manifest, machine-readable)\n- `\u003clink rel=\"webmcp\"\u003e` injected into every HTML page\n- `Link: rel=\"webmcp\"` HTTP header on every response\n- `/llms.txt` augmented with a WebMCP block (idempotent merge with origin's file). Also advertised in the `Link` header and as `\u003clink rel=\"describedby\" type=\"text/markdown\"\u003e` via the IANA-registered RFC 8288 relation, so generic agent-aware scanners that only recognise standard rels find a description of the site.\n- `/robots.txt` augmented with `Disallow: /_webmcp/` (idempotent merge)\n- `/.well-known/agents.md` for acting agents, with `/AGENTS.md` and `/agents.md` 301-redirecting to it\n- `/.well-known/api-catalog` ([RFC 9727](https://www.rfc-editor.org/rfc/rfc9727.html)) Linkset entry pointing at the WebMCP manifest. Also advertised in the `Link` header and as `\u003clink rel=\"api-catalog\"\u003e` on every response.\n- `/.well-known/agent-skills/\u003cslug\u003e/SKILL.md` Anthropic-format Agent Skill with auto-generated tool list + publisher-written hints. Also advertised via `rel=\"agent-skills\"` in the `Link` header and as a `\u003clink\u003e` tag.\n- `/.well-known/agent-skills/index.json` ([Cloudflare Agent Skills Discovery RFC](https://github.com/cloudflare/agent-skills-discovery-rfc) v0.2.0) wraps the SKILL.md in a spec-compliant index with a build-time SHA-256 digest for integrity verification. `links.agent_skills_index` field added to the manifest.\n- `/mcp` landing page that branches at runtime between native, pair, and disabled states\n\nPlus five executor types (`sitemap_filter`, `rss_feed`, `dom_extract`, `http_json`, `http_get`) for the imperative tool path, and a `[[forms]]` block for the declarative form path.\n\n\u003e [!NOTE]\n\u003e **Every surface above is opt-in.** Each one maps to a single boolean in your `[features]` block. If you disagree with a convention or do not want to publish it, flip the flag off and the route disappears, the link advertisement drops out, and no fingerprint is left. `llms.txt` is the most widely-debated example: set `llms_txt = false` and cf-webmcp stops claiming the path entirely. Same applies to `agents.md`, `api-catalog`, `agent-skills`, `fallback_widget`, form-attribute injection, and the in-page `\u003cscript\u003e` bootstrap. Defaults are \"on\" because cf-webmcp's value is publishing discovery surfaces; opting out is one TOML edit away. See [`docs/scope.md`](docs/scope.md) for what is in and out of scope at the project level.\n\n## Quick start\n\n```bash\ngit clone https://github.com/basgr/cf-webmcp\ncd cf-webmcp\ncp templates/default.toml webmcp.toml\ncp wrangler.example.toml wrangler.toml\n# edit webmcp.toml: set [site], [origin], and tool URLs\n# edit wrangler.toml: set the route for your domain\nnpm install\nnpm run build\nwrangler deploy\n```\n\n## Validation\n\nYou can verify a cf-webmcp deployment with a tool that does not know anything about your TOML or config - it only sees the rendered page. Lighthouse 13.3.0 ships an **agentic-browsing** audit category that does exactly this.\n\nRunning it against the demo's `/forms` page (Chrome Canary 150.x, verified 28 Aug 2026, WebMCP flag on) returns a perfect category score:\n\n- `agent-accessibility-tree` - pass\n- `webmcp-registered-tools` - finds all 3 imperative tools from the injected bootstrap plus both declarative form tools (the cf-webmcp-stamped form and a hand-stamped control)\n- `webmcp-form-coverage` - not applicable (every form on the page is already annotated)\n- `webmcp-schema-validity` - pass (generated `inputSchema` blocks validate)\n- `llms-txt` - pass\n\nA third-party auditor seeing only the HTTP response confirms both the auto-injected and the manually-stamped tools, which is the same vantage point external agent-readiness checkers use.\n\n## Documentation\n\nFull reference docs live in [`docs/`](docs/):\n\n**Getting started**\n\n- [Deployment](docs/deployment.md) - full-proxy vs route-only modes, wrangler config, Bot Management bypass.\n- [Local testing](docs/local-testing.md) - `wrangler dev` against the bundled `templates/example-site/` fixture.\n- [Browser support](docs/browser-support.md) - enabling the WebMCP flag in Chrome and verifying it.\n\n**Configuration**\n\n- [Customisation](docs/customisation.md) - overriding the `/mcp` landing template, placeholders, runtime state branching.\n- [Form injection](docs/form-injection.md) - the `[[forms]]` block, declarative `toolname` / `tooldescription` / `toolparamdescription` / `toolautosubmit` attribute stamping.\n- [AGENTS.md](docs/agents-md.md) - `/.well-known/agents.md` publication + 301 aliases.\n- [API catalog (RFC 9727)](docs/api-catalog.md) - the `/.well-known/api-catalog` Linkset.\n- [Agent Skills](docs/agent-skills.md) - the `/.well-known/agent-skills/\u003cslug\u003e/SKILL.md` publication.\n\n**Operations**\n\n- [Costs](docs/costs.md) - Workers, R2, and cache pricing under typical traffic.\n- [Privacy](docs/privacy.md) - what's logged, what's stripped from origin fetches, GDPR posture.\n- [Upgrade](docs/upgrade.md) - `schema_version` policy, tool-name immutability, breaking-change procedure.\n- [Limitations](docs/limitations.md) - SPA story, multi-language sites, service workers, other known edges.\n\n**Project**\n\n- [Scope](docs/scope.md) - what cf-webmcp is and is not. Read this before opening a feature request.\n- [Security model](docs/security.md) - tool descriptions are not a security boundary; what cf-webmcp does and does not defend against.\n\n## Acknowledgements\n\n- Suganthan Mohanadasan, [\"WebMCP: I Made My Website AI Agent Ready\"](https://suganthan.com/blog/webmcp-implementation-guide/) - the implementation guide that informed the publisher-side discovery patterns.\n- [jasonjmcghee/WebMCP](https://github.com/jasonjmcghee/WebMCP) - the fallback widget that bridges desktop MCP clients to WebMCP sites.\n- [webmachinelearning/webmcp](https://github.com/webmachinelearning/webmcp) - the W3C draft.\n- [specification.website](https://specification.website/) (Joost de Valk) - independent worked-example reference for agent-ready websites; converges on the same `/.well-known/agent-skills/index.json`, RFC 9727 `api-catalog`, `rel=\"describedby\"` for llms.txt, and `rel=\"agent-skills\"` conventions that cf-webmcp ships.\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbasgr%2Fcf-webmcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbasgr%2Fcf-webmcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbasgr%2Fcf-webmcp/lists"}