WebMCP and MCP starter kit
Paste a form from your site and get the WebMCP attributes and script that let an in-browser agent fill it in, test the script in a sandbox, then read the security guidance and a recipe for a remote MCP server. The generator and the test run in your browser; nothing you paste is sent to us.
Checked against the draft of 2026-09-29. WebMCP is a draft, so names and behaviour may change. This page doesn’t claim that any AI product uses these tools; it helps you publish them correctly and safely.
1. Generate
Paste the HTML of a form. Nothing is sent to a server, and the pasted HTML is parsed but never run. Password, payment and file-upload forms are refused.
Declarative: attributes on your form
Paste a form to see the output.
Imperative: a script for document.modelContext
Paste a form, then answer both questions.
2. Test
Tests shape, not a real agent. The frame has no network access and nothing you paste leaves this page.
Generate the imperative script above to test it here.
What the test does and doesn’t show
The harness tests shape, not a real agent. It runs your snippet in a sandboxed frame against a stand-in document.modelContext, checks each tool’s name, schema, description and annotation members against the draft, and lets you call execute with sample input. It never contacts your site, and no AI agent or browser model is involved, so it can’t tell you how an agent will choose or use your tools. To try them in a real browser, use Chrome with the testing flag (chrome://flags/#enable-webmcp-testing) or an origin-trial token, and the Model Context Tool Inspector extension, on your own site. Sources: Chrome for Developers: WebMCP (testing flag, origin trial, Model Context Tool Inspector); WebMCP, Draft Community Group Report (W3C Web Machine Learning CG), 29 September 2026.
3. Guidance
Security
WebMCP lets a page hand an AI agent a list of actions. The agent reads your tool names, descriptions and results as text and decides what to do with them, and it may be following instructions someone else planted. These points come from the security section of the WebMCP draft (§6.3 risks, §6.4 mitigations) and from our review of our own tools.
- Mark tools that change something. Set
consequentialHint: trueon any tool that books, buys, sends, deletes, changes an account or spends a quota. The draft describes it as a signal that lets the agent or browser ask the user to confirm before the tool runs. Say the effect plainly in the description ("Places the order and charges the saved card"), not "Finalizes the cart": the draft’s own example of misrepresented intent is a vague name that makes a purchase. Sources: WebMCP draft §6.4.5 Consequential Annotation for Tool Executions; WebMCP draft §6.3.2 Misrepresentation of Intent. - Never autosubmit anything that changes data or costs money. Leave
toolautosubmitoff forms that change data or cost money. Without it the browser fills the form and the person presses submit. Use it only on read-only forms such as search. Sources: WebMCP declarative API explainer (toolautosubmit); Chrome for Developers: WebMCP declarative API. - Mark results that carry other people’s text. If a result includes text your site did not write (reviews, messages, forum posts, another site’s files), set
untrustedContentHint: true, keep that text in its own clearly named field, and start the result with a one-line notice that it is data, not instructions. Never copy such text into a tool’s name, title, description or parameter descriptions: agents read those as trusted context. Sources: WebMCP draft §6.4.4 Untrusted Annotation for Tool Responses; WebMCP draft §6.3.1.2 Output Injection Attacks; WebMCP draft §6.3.1.1 Metadata / Description Attacks (Tool Poisoning); Our prompt-injection review of our own agent tools, 2026-09-29; live example: the notes in our AGENTS.md. - Keep descriptions short and factual. Say what the tool does and what it returns, and nothing else. A description is text an agent will act on, so it must not tell the agent to do anything beyond the tool’s purpose. Build it from fixed strings in your code, never from stored or user-supplied data. Sources: WebMCP draft §6.3.1.1 Metadata / Description Attacks (Tool Poisoning); Our prompt-injection review of our own agent tools, 2026-09-29; live example: the notes in our AGENTS.md.
- Validate every input on your server. WebMCP input is written by an agent, not typed by a person, and the agent may be acting on someone else’s instructions. Check it on the server exactly as you check a form submission: same authentication, same authorisation, same limits. The draft warns that the tool path and the page path may end up with different checks. Annotations and schemas are hints, not controls. Sources: WebMCP draft §6.3.1.3 Tool Implementation as Attack Targets; MCP specification 2026-07-28, Tools: Security Considerations and annotations.
- Cap input lengths in the schema. Give every string a
maxLength(and apatternorenumwhere one fits) so the agent sends the right shape, and enforce the same caps on the server. The draft lists length limits as a mitigation that shrinks, but does not remove, prompt-injection attacks. Sources: WebMCP draft §6.4.2 Restricting maximum input lengths; Our prompt-injection review of our own agent tools, 2026-09-29; live example: the notes in our AGENTS.md. - Ask only for what the action needs. Don’t add parameters such as age, location or purchase history "for personalisation": an agent may fill them from what it knows about the user, which the draft calls privacy leakage through over-parameterization. Keep secrets, session tokens and personal data out of descriptions and results. Sources: WebMCP draft §6.3.3 Privacy Leakage Through Over-Parameterization.
- Don’t expose password, payment or file forms. Leave sign-in, password-reset, card-payment and file-upload forms for the person to complete in the page. The draft names password resets and transactions as the kind of high-value action that becomes a target once exposed as a tool. The generator on this page refuses these forms. Sources: WebMCP draft §6.3.1.3 Tool Implementation as Attack Targets.
- Rate-limit the endpoints behind your tools. An agent can repeat a call many times. Limit the endpoints your tools call as you limit any public endpoint, per address or per account. The MCP specification requires servers to rate-limit tool calls; the same applies to the endpoints a WebMCP tool calls. Sources: MCP specification 2026-07-28, Tools: Security Considerations and annotations; Our remote MCP server, described in AGENTS.md, "Remote MCP server".
- Keep WebMCP to the frames you trust. WebMCP is gated by the Permissions Policy feature
tools, whose default allowlist is'self': your own pages can register tools, and a cross-origin iframe can’t unless you delegate with<iframe allow="tools">. Delegate only to frames you trust. Tools are also invisible to other origins unless you list them inexposedTo. On pages that should never register tools, sendPermissions-Policy: tools=(); the draft notes this also stops injected scripts and compromised dependencies from using the API. Sources: WebMCP draft §4.5 Permissions policy integration; WebMCP draft §6.4.1 Disabling WebMCP with Permissions Policy; Chrome for Developers: WebMCP imperative API (iframes, exposedTo). - Treat prompt-injection defences as layers, not a fix. When a result quotes third-party text, remove invisible characters (bidi controls, zero-width characters, Unicode tag characters), cap each quoted string, return only JSON keys your code chose (never keys copied from stored data), and don’t echo long input back. None of this stops a model from reading the text. In our own review the notice first in each result, the warning in the description and confirmation on the one consequential tool were the remaining defences. Sources: Our prompt-injection review of our own agent tools, 2026-09-29; live example: the notes in our AGENTS.md; WebMCP draft §6.3.1.2 Output Injection Attacks.
Two risks in §6.3 are for browsers and agents rather than site owners: same-origin boundaries (§6.3.4, still marked TODO in the draft) and private browsing modes (§6.3.5). Sources: WebMCP draft §6.3 Key Security and Privacy Risks; WebMCP draft §6.4 Mitigations; Our prompt-injection review of our own agent tools, 2026-09-29; live example: the notes in our AGENTS.md.
Remote MCP server recipe
WebMCP tools live in an open browser tab. To offer the same actions to agents that call servers directly, wrap the same functions in a remote MCP server. This is a recipe for a Next.js route handler with the official TypeScript SDK v2, @modelcontextprotocol/server (2.2.0 on 2026-09-29), which implements MCP 2026-07-28.
- One handler, a fresh server per request.
createMcpHandler(factory, options)returns a handler whosefetch(request)gives a web-standardResponse, so an App Router route can return it directly. The factory builds a newMcpServerfor each request, so no state is kept between calls. Sources: MCP TypeScript SDK v2: serving over HTTP (createMcpHandler); @modelcontextprotocol/server on npm (2.2.0, latest on 2026-09-29). - Stateless, plain JSON. Set
responseMode: 'json'so answers are single JSON objects, never event streams, and declaretools: { listChanged: false }. GET and DELETE get 405. The defaultlegacy: 'stateless'also answers 2025-era clients that start withinitialize, from the same factory; setlegacy: 'reject'to serve only 2026-07-28 clients. Sources: MCP TypeScript SDK v2: serving over HTTP (createMcpHandler); MCP specification 2026-07-28, Streamable HTTP: Security & Endpoint (Origin). - Check Origin before anything else. The SDK handler checks neither Host nor Origin. Call
originValidationResponse(request, allowedHosts)first: a request from a browser page on another site gets 403, as the transport spec requires, and server-side clients, which send no Origin, pass. Send no CORS headers; browser agents have WebMCP. Sources: MCP specification 2026-07-28, Streamable HTTP: Security & Endpoint (Origin); MCP TypeScript SDK v2: serving over HTTP (createMcpHandler). - Limit each address before reading the body. Count requests per client address and answer 429 with
Retry-Afterbefore the SDK reads the request, then apply your usual limits inside tools that spend something. Hosted assistants may call from cloud addresses that many users share, so set the endpoint limit well above what one person would need. Ours is 600 requests per 10 minutes per address. Sources: MCP specification 2026-07-28, Tools: Security Considerations and annotations; Our remote MCP server, described in AGENTS.md, "Remote MCP server". - Reuse the WebMCP tool definitions. Register each tool with the same name, description and JSON Schema as its WebMCP twin (
fromJsonSchemaaccepts the schema as it is), so the two surfaces can’t drift and oversized input is refused before your handler runs. Map the annotations with the table below. ReturnstructuredContentplus the same object as JSON text, and turn your own errors intoisError: trueresults without stack traces. Sources: MCP specification 2026-07-28, Tools: Security Considerations and annotations; Our remote MCP server, described in AGENTS.md, "Remote MCP server".
// app/mcp/route.ts
import { createMcpHandler, fromJsonSchema, McpServer, originValidationResponse } from '@modelcontextprotocol/server';
export const runtime = 'nodejs';
const ALLOWED_ORIGIN_HOSTS = ['example.com', 'www.example.com'];
const mcp = createMcpHandler(
() => {
const server = new McpServer({ name: 'example', version: '1.0.0' }, { capabilities: { tools: { listChanged: false } } });
server.registerTool(
'search_products',
{
title: 'Search products',
description: 'Search the catalogue by keyword. Returns up to 10 products with name, price and URL.',
inputSchema: fromJsonSchema<{ q: string }>({
type: 'object',
properties: { q: { type: 'string', maxLength: 200 } },
required: ['q'],
additionalProperties: false,
}),
annotations: { readOnlyHint: true, openWorldHint: false },
},
async ({ q }) => {
const results = await searchProducts(q); // the function your search page already calls
return { content: [{ type: 'text', text: JSON.stringify({ results }) }], structuredContent: { results } };
},
);
return server;
},
{ responseMode: 'json', maxRequestBodySize: 1_000_000 },
);
async function handle(request: Request): Promise<Response> {
const rejected = originValidationResponse(request, ALLOWED_ORIGIN_HOSTS);
if (rejected) return rejected;
if (await overLimit(clientIp(request))) {
return new Response(JSON.stringify({ jsonrpc: '2.0', error: { code: -32000, message: 'rate limited' } }), {
status: 429,
headers: { 'content-type': 'application/json', 'retry-after': '600' },
});
}
return mcp.fetch(request);
}
export const POST = handle;
export const GET = handle;
export const DELETE = handle;
Annotations
| The tool | WebMCP members | MCP annotations | Description warning | Note |
|---|---|---|---|---|
| Reads only your own data; changes nothing | readOnlyHint | readOnlyHint: true openWorldHint: false | No | Closed world: it touches nothing outside your site. |
| Changes nothing; returns other people’s text | readOnlyHint untrustedContentHint | readOnlyHint: true openWorldHint: true | Yes | MCP has no untrusted-content annotation: add a warning to the description and a notice first in each result. |
| Changes something or spends a quota | consequentialHint | readOnlyHint: false destructiveHint: false idempotentHint: false openWorldHint: true | No | readOnlyHint false marks it as a write, and MCP asks clients to confirm sensitive operations. destructiveHint false says it only adds (MCP’s default is true); set it true for a tool that deletes or overwrites. idempotentHint false: each call has a new effect. |
| Changes something and returns other people’s text | consequentialHint untrustedContentHint | readOnlyHint: false destructiveHint: false idempotentHint: false openWorldHint: true | Yes | Both of the above. |
openWorldHint is an MCP member, not a WebMCP one; a browser ignores it. MCP says clients must treat annotations as untrusted unless they come from a trusted server, so they are hints, never a substitute for server-side checks. Sources: MCP specification 2026-07-28 schema: ToolAnnotations (defaults); MCP specification 2026-07-28, Tools: Security Considerations and annotations; WebMCP, Draft Community Group Report (W3C Web Machine Learning CG), 29 September 2026; Our remote MCP server, described in AGENTS.md, "Remote MCP server".
MCP Registry
The MCP Registry lists public servers so directories and clients can find them; it is in preview, and breaking changes or data resets may happen. A remote server’s entry is a server.json file. name is reverse-DNS (com.example/…) with one slash, and you prove you own the domain with a DNS record or a file at /.well-known/mcp-registry-auth. description is at most 100 characters, and version must be an exact version, not a range. Publish with the registry’s mcp-publisher tool.
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
"name": "com.example/store",
"title": "Example Store",
"description": "Search the Example Store catalogue and check order status",
"version": "1.0.0",
"websiteUrl": "https://example.com",
"remotes": [{ "type": "streamable-http", "url": "https://example.com/mcp" }]
}
An optional llms.txt line
- [WebMCP tools](https://example.com/search): This page registers WebMCP tools in the browser (document.modelContext): search_products (read-only), request_quote (changes data; the person confirms). - [MCP server](https://example.com/mcp): Remote MCP server (Streamable HTTP) with the same tools and the same limits as the site.
Optional. It tells people, and any agent that reads your llms.txt, that the tools exist and which ones change data. No AI vendor has documented that its assistant reads llms.txt, and a line here doesn’t register anything: the tools work, or don’t, whether or not it is present. The LLMSRank rubric counts a WebMCP or MCP mention under Machine surface (A5) and Action endpoints (A6); that is our rubric, not the behaviour of any AI product. Sources: Does ChatGPT, Claude, Perplexity or Google read llms.txt? (vendor statements, checked 2026-09-29).
Try it in Chrome
Chrome’s testing flag is chrome://flags/#enable-webmcp-testing; paste it into the address bar, because browsers don’t follow links to chrome:// pages. Chrome’s documentation covers the flag, the origin trial and the Model Context Tool Inspector extension for testing on your own site: Chrome for Developers: WebMCP (testing flag, origin trial, Model Context Tool Inspector). The imperative and declarative pages are here and here.