Framework Guide · WebMCP · Static & Hugo
WebMCP for static sites and Hugo: agent tools without a build step
You do not need a framework to adopt WebMCP. The API is a browser standard, the imperative surface is fifteen lines of script, and the declarative surface is three attributes on forms you already wrote — which makes hand-rolled HTML and Hugo-generated sites the cheapest place on the web to become agent-callable. This article covers what static sites gain, the honest limit of what “static + agent” can ever mean, and the one architecture that turns a content site into a tool surface without touching its deployment model.
Fact-checked and updated on September 2, 2026 (W3C CG-DRAFT era; Chrome 149–156 origin trial). Primary sources are cited at the end of the article.
The shape of the opportunity
WebMCP, the W3C Web Machine Learning Community Group’s standard, lets a page declare callable actions — named tools with JSON-Schema inputs — that an in-browser agent invokes directly instead of scraping and simulating clicks ([specification](https://webmachinelearning.github.io/webmcp)). The browser support story in 2026 is a single family: Chrome added the API behind a testing flag (#enable-webmcp-testing) and, since June 2026, exposes it at scale through a public origin trial running Chrome 149–156 — a per-origin token delivered as a meta tag or response header enables it without any flag ([Chrome for Developers](https://developer.chrome.com/docs/ai/webmcp)).
For a static site this is unusually good news, because every moving part of WebMCP lives in exactly the two places static sites already excel at: markup and one small script. There is no server runtime to provision, no hydration model to fight, no state manager to keep in sync — the entire integration is an HTML decision and a JavaScript artifact you ship like a stylesheet.
The declarative layer: three attributes, every form on the site
The Declarative API is the form-native half of WebMCP: annotate a standard HTML form and the browser synthesizes a callable tool from it, deriving the JSON Schema from the controls’ native semantics — type="email" becomes a string format, required becomes schema-required, a <select> becomes an enum ([explainer](https://github.com/webmachinelearning/webmcp/blob/main/declarative-api-explainer.md)). The full mapping, the toolautosubmit decision and the pseudo-class styling are dissected in our Declarative API reference; applied to the two forms almost every static site has:
<!-- search: the tool agents use most -->
<form action="/search/" method="get"
toolname="search-site"
tooldescription="Searches this site. Returns a results page listing matching pages.">
<input type="search" name="q" required minlength="2">
<button type="submit">Search</button>
</form>
<!-- contact: note the absence of toolautosubmit — a human sends -->
<form action="https://formspree.io/f/yourid" method="post"
toolname="contact-form"
tooldescription="Sends a message to the site owner. Fields: name (string), email (string), message (string). Returns a thank-you page on success.">
<input name="name" required>
<input name="email" type="email" required>
<textarea name="message" required minlength="20"></textarea>
<button type="submit">Send</button>
</form>
That is the whole surface for a brochure site, docs page or portfolio — and it is zero-cost: no JavaScript, no token even (the trial gate applies to the API, and browsers without it render the forms identically). On Hugo, these are content decisions; on plain HTML, they are template edits. Neither is a release cycle.
The imperative layer: fifteen lines for sites with real state
Static sites are not static inside the browser. Client-side search over a JSON index — Hugo’s own outputs mechanism — filters, calculators and price tables are all state a tool can expose, with the same registration mechanics the specification defines: dual-root feature detection (document.modelContext || navigator.modelContext, because Chrome’s own guidance documents the migration between them), the MCP content envelope on return values, and an AbortController signal as the sole unregistration mechanism ([modern-web-guidance](https://github.com/GoogleChrome/modern-web-guidance/blob/main/skills/modern-web-guidance/guides/webmcp/agentic-javascript-tools.md)):
// layouts/partials/webmcp.html — one file, included site-wide
{{ with site.Params.webmcpToken }}<meta http-equiv="origin-trial" content="{{ . }}">{{ end }}
<script>
(async function () {
const ctx = document.modelContext || navigator.modelContext;
if (!ctx || !('registerTool' in ctx)) return;
const c = new AbortController();
await ctx.registerTool({
name: 'site-search',
description: 'Search this site\'s built index; returns up to 10 pages as {title, url, summary}.',
inputSchema: {
type: 'object',
properties: { query: { type: 'string', minLength: 2 } },
required: ['query'],
},
annotations: { readOnlyHint: true },
async execute({ query }) {
if (!window.__index) window.__index = await (await fetch('/index.json')).json();
const q = query.toLowerCase();
const hits = window.__index
.filter(p => (p.title + ' ' + (p.summary || '')).toLowerCase().includes(q))
.slice(0, 10);
return { content: [{ type: 'text', text: JSON.stringify(hits) }] };
},
}, { signal: c.signal });
})();
</script>
The static-site economics are almost comic: a tool that in a React app would need a provider tree and a hydration window is, here, a file that sits next to your analytics snippet. The search index the pattern rides on is itself a Hugo feature — outputs = ["html", "json"] with a template emits the /index.json — and on hand-rolled sites a build-time jq pass over your content files produces the same artifact.
Deployment realities: tokens, caches, and the token-in-params habit
The trial token is origin-bound, which in static hosting lands on the CDN/edge layer. The <meta> route is simplest — Hugo renders site.Params.webmcpToken into every page — but two cautions deserve the senior-engineer treatment. If your HTML is served from a CDN, the token is in the cached artifact: token rotation is a cache purge, and pages differing only by token can fragment or leak across cache keys. The header route (Origin-Trial set at the edge) decouples them; GitHub Pages users, take note, cannot set arbitrary headers and must prefer the meta tag. And the token’s own entropy matters — a trial token grants a powerful browser surface on your origin; it belongs in your site’s configuration, not a copy-pasted snippet from a tutorial.
The honest limit: a tab, a client, and no server of one’s own
Everything above requires a live tab. A WebMCP tool exists between page load and page close; a browser-side agent — Chrome 149–156, Gemini-era — is the only thing that can see it. The consequence, which static-site posts usually skip, is a decision, not an implementation detail:
| Visitor | What serves them | Tier |
|---|---|---|
| Human with JavaScript | The site as built | — |
| In-browser agent (trial) | Declarative + imperative tools | WebMCP |
| Headless agent, no browser | llms.txt, JSON feeds, content API — and, when you need transactions, an MCP endpoint you operate | MCP / static contracts |
| Crawler / search engine | Sitemap, RSS, structured data | The old contracts, still load-bearing |
Static sites can absolutely run the third tier — an MCP server is a small always-on process, and the schemas often lift directly from the same build artifacts that produce the site — but that crosses from “static” into “static with a service,” and the honest framing (with runnable code for both halves) is our WebMCP vs REST comparison. The series’ CMS-driven siblings make the same split from the other side: WordPress, Ghost, Strapi. And the discovery tier — who is allowed, what they’re for — now has its own emerging contracts: agents.json and WAIS.
Testing and measurement
Local development is one flag and a refresh: Chrome with #enable-webmcp-testing, DevTools, await document.modelContext.getTools() — your declarative forms and registered tools listed with schemas. The Model Context Tool Inspector extension adds manual invocation and a Gemini-keyed LLM simulation — the practical way to discover whether your tooldescription strings route correctly, which on a hand-rolled site is the one quality gate left. Measure what you shipped: tool invocations surface in your analytics as agent-driven traffic patterns (search-then-result without human pacing is distinctive once you look for it).
FAQ
Which static site generators work with this?
All of them. The declarative layer is markup-only — any generator, any age. The imperative layer is one script tag; Hugo, Jekyll, Eleventy, Astro (see the Astro guide) and bare HTML are equally qualified. There is no plugin to wait for, because there is no build integration required.
Do forms work with Formspree/Netlify Forms?
Yes — declarative tools wrap whatever the form action already does. Keep toolautosubmit off consequential actions; a contact form that opens support tickets is exactly the category the human-confirmation default protects.
Is this useful before Chrome’s GA?
For a content site: the declarative pass costs nothing, and semantic descriptions double as excellent SEO metadata for the agent-adjacent present. For a transactional site: run the MCP tier now — that traffic is already here; the trial tier is future-proofing you’re doing in your spare minutes.
Conclusion
Static sites spent twenty years optimizing for the reader with no plugins, no accounts and no runtime — and they are, almost ironically, the earliest WebMCP adopters available: semantic forms the browser can compile into tools, a JSON index that turns search into a tool call, a script that costs a cookie’s worth of bandwidth. The web’s oldest architecture is its most legible to machines, because it was always the most legible to humans who could only read markup. The agent, in the end, is that reader with a budget.
Primary sources
- webmachinelearning.github.io/webmcp — W3C CG specification.
- WebMCP — Declarative API explainer.
- developer.chrome.com/docs/ai/webmcp — origin trial documentation.
- gohugo.io — output formats (JSON indexes).
- GoogleChrome/modern-web-guidance — tool lifecycle guidance.
- mcptrail.com — server MCP vs in-browser WebMCP, with code.