WebMCP
An agent in the browser can fill in a survey Form, or work the Studio's inbox, without clicking through the DOM. The ingest contract stays the same POST. WebMCP only describes the open page.
Where it applies
Two places register tools:
| Page | Tools |
|---|---|
/f/:handle — a survey Form's public page, hosted or embedded | describe_form, submit_form |
| studio.noonmark.dev — signed in, as that person | reads: list_forms, get_form, list_submissions, get_submission, list_insights, list_workspaces; writes, each confirmed on screen: set_submission_status, create_form, create_workspace, update_workspace, switch_workspace |
| Everything else on noonmark.dev — the landing, these docs | none |
That is what "WebMCP ready" on the landing means, and all it means.
Use with your AI assistant
There are two ways to submit a response:
- From your app: use the Form's API key with the HTTP API or SDK.
- From an open browser tab: open the public survey with a WebMCP-aware browser agent. It can read the Fields and submit through the page's tools. You do not need to paste the Form's API key into your assistant.
For ChatGPT, Claude or Gemini, check the browser or client you are using. Opening a link in an assistant does not automatically enable WebMCP; the browser agent must support it. The normal form remains usable without it.
To work with Forms and responses outside an open tab, use the remote MCP setup guide. Remote MCP uses a Workspace token, not the public Form API key. Client support and authentication options vary; Gemini CLI is an example of an MCP client in the Gemini family.
What it is
WebMCP is a draft from the W3C Web
Machine Learning CG. The page registers tools on document.modelContext. The spec moved the
getter from navigator to document in May 2026; Noonmark reads both. The browser's agent
discovers and calls those functions in the tab, with the person watching.
It is not Anthropic's MCP on the wire. It replaces neither the SDK, the HTTP ingest nor a remote MCP server. Without a tab on the origin, there is nothing to discover.
Chrome: an origin trial in versions 149–156, or the local flag
chrome://flags/#enable-webmcp-testing. It is not on by default; shipping is
estimated for Chrome 157. Noonmark sends the trial token on /f/:handle and on
every page of studio.noonmark.dev, so Chrome 149–156 exposes the API there without the flag. In a browser
without the API, the page works as it always did.
What Noonmark registers
Public survey pages — /f/:handle — register two tools, with feature detection. A
feedback Form has no page on Noonmark: the screen lives in your own app, so the POST
via SDK or HTTP remains the path.
| Tool | What it does |
|---|---|
describe_form | Read-only. Returns name, kind and the list of Fields. |
submit_form | The same validation as the screen. POSTs to /api/v1/f/:publicKey/page, the same endpoint the public page uses. |
await modelContext.registerTool({
name: "describe_form",
description: "Returns this survey's name and Field list.",
inputSchema: { type: "object", properties: {} },
annotations: { readOnlyHint: true },
execute() {
return { name, kind: "survey", fields };
},
});await modelContext.registerTool({
name: "submit_form",
description: "Submit this survey. Keys match Field.key.",
inputSchema: fieldInputSchema(fields),
async execute(input) {
const response = await fetch(`/api/v1/f/${encodeURIComponent(apiKey)}/page`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(input),
});
return response.json();
},
});Success and error appear on the page, so the person can see what the agent did. Rate limits and Field 400s stay the same.
Field → JSON Schema
The Field's key is the property. The label becomes the description.
| Field | Schema |
|---|---|
text, textarea | string |
email | string with format: email |
url | string with format: uri |
number | number |
scale | number with the Field's min/max |
satisfaction | number 1 to 5 |
select | string enum of the options |
multiselect | array of string enum |
emoji | string enum of the options |
when | the Field is left optional in the schema; the rule is appended to its description |
required | goes into schema.required |
default | goes into the property's default |
hidden | stays in the schema — an agent may set it, as the link would |
locked | left out of the schema — the server writes the Form's value |
In the Studio
A signed-in Studio tab registers the MCP server's five reads, with the same names, inputs and answers, plus the person's Workspaces and five writes. They act as the person signed in, on the Workspace that is open.
| Tool | What it does |
|---|---|
list_forms | Read-only. Every Form in the Workspace. |
get_form | Read-only. One Form: Fields, Gate, status words. |
list_submissions | Read-only, untrusted content. Newest first, with search, status and a cursor. |
get_submission | Read-only, untrusted content. One Submission with its Timeline. |
list_insights | Read-only. The numbers the Insights page shows: arrivals, doors, status movement, what is open. |
list_workspaces | Read-only. The person's Workspaces, and which one is open. |
set_submission_status | Moves a Submission to another of its Form's words, with a note. |
create_form | A Form in the open Workspace: name, feedback or survey, Fields. |
create_workspace | A Workspace with the person as its first member; the tab opens it. |
update_workspace | The open Workspace's name and identifier. |
switch_workspace | Opens another of the person's Workspaces, like the Workspace menu. |
Every write is marked consequentialHint and waits on the person: the Studio shows
what the agent asked for — the Submission and the word, the Form and every Field, the
Workspace by name — and only Confirm makes the change. Decline, Escape or the agent
cancelling all leave things as they were. A status change lands on the Timeline under
the person's name, the same as the status menu. Fields go through the same checks as
the Form editor.
The two Submission reads carry untrustedContentHint: a payload is whatever a stranger
typed, and an agent should read it as data, not instructions.
What stays out of the tools
Nothing that deletes or reconfigures: rotating the API key, deleting a Form or a Submission, creating or deleting webhooks, Integrations, the slug, tokens. Those stay clicks.
Headless agents (a script, a GitHub Action, another server) use POST /api/v1/submit. WebMCP is not for that.
A widget on another origin
The tools Permissions Policy defaults to self. A cross-origin iframe only
registers tools if the embed carries allow="tools" — see
Embed a Form. HTTPS is required (Secure Context).
The page must also be origin-keyed: WebMCP is disabled when document.domain is
enabled, for example by Origin-Agent-Cluster: ?0. A site can opt out entirely with
Permissions-Policy: tools=(). Noonmark sends neither header.
What did you think of this content?