Forms as typed tools
What your agent sees on a page, the one call that fills a form, and how Deputy asks before it submits.
Deputy turns each form on a page into a tool with a JSON Schema: field names, types, allowed values, minimums and maximums, and which fields are required. Your agent reads the schema, then fills the whole form with one call.
What your agent sees
browser_navigate opens a page and answers with what it offers: each form as a typed tool, the start of the page's text, and the buttons and links your agent can act on, each with a ref. browser_capabilities gives the same for every open tab, plus what each form's fields currently hold, so your agent can see the state before it changes it.
A trimmed example, for a booking form:
{
"tools": [
{
"name": "book_a_table",
"inputSchema": {
"type": "object",
"properties": {
"party_size": { "type": "number", "minimum": 1, "maximum": 12 },
"date": { "type": "string", "format": "date" },
"seating": { "type": "string", "enum": ["indoor", "outdoor", "bar"] }
}
},
"needsApproval": true
}
],
"currentValues": {},
"actions": [{ "ref": "a8", "label": "Contact", "kind": "link" }]
}Deputy sends the full schema on purpose. When it once sent only tool names, to keep replies small, the agent had to probe for the parameters, and the same task cost more. See Measurements.
Long pages
A page can carry hundreds of links. Deputy returns the first 25 and always says how many there are, for example Showing 1-25 of 263. Your agent narrows the list with actionQuery (text in the label) or pages through it with actionOffset and actionLimit. Nothing is cut silently.
Filling a form
Page tools are not MCP tools. Your agent runs one with browser_invoke, passing the tool's name and arguments that match its schema:
{ "tool": "book_a_table", "args": { "party_size": 4, "seating": "outdoor" } }That fills the real form in your logged-in browser. Many forms navigate instead of returning a value, so when one does, Deputy answers with where the tab ended up and what tools appeared there.
Where the tools come from
- Tools the app already declared. Apps built with CopilotKit publish their frontend tools to the browser's WebMCP registry,
document.modelContext. Deputy hands them to your agent with their real handlers, fully executable. - Real
<form>elements. Deputy writes WebMCP attributes (toolname,tooldescription,toolparamdescription) onto the page's own form, and Chromium generates the schema and runs the submission. Deputy never overwrites a site's own tools, and the same form keeps the same tool name across reloads. - Pages with no
<form>. Google Forms, React apps, anything that builds a form out of divs. Deputy builds the tool from loose inputs and ARIA widgets, including inside open shadow roots:
| On screen | In the page | Deputy makes |
|---|---|---|
| Multiple choice | role="radiogroup" with role="radio" | enum |
| Checkboxes | role="checkbox" | boolean |
| Dropdown | role="listbox" with role="option" | enum |
| Paragraph answer | contenteditable | string |
Tools Deputy built from the page are tagged grafted in the popup. Chromium's WebMCP flag is optional: without it, Deputy builds the same schema from the form itself.
Before it submits
A form that changes something (anything other than a GET form, such as a search) is marked needsApproval, and the popup tags it asks first.
- If your client can show approval prompts, Deputy asks you: Submit "book_a_table" on https://…? It changes something. Yes submits it; no stops, and your agent is told you declined.
- If no one can answer, for example in a headless run, Deputy fills the form and names the submit button. Your agent presses it only if your instruction was to submit.
Deputy never sets WebMCP's auto-submit attribute on such a form, so Chromium itself fills the fields, focuses the submit button and waits for a person. That consent is enforced by the browser, not by a check Deputy could forget.
When there's no typed tool
Some pages have controls Deputy can't turn into a tool, or your agent needs something other than a form. Every action result says what changed on the page, so your agent rarely needs to look again.
browser_actdoes one thing to an element by ref or CSS selector: click, double-click, right-click, hover, focus, type, press keys, check, uncheck, select an option (native or custom dropdowns), clear, or scroll it into view.stepsruns several in order in one call, and stops at the first that fails.browser_fillsets one field by ref, for inputs outside any form.browser_findfinds elements by visible text, role or selector, including in shadow roots and off screen, and returns refs.browser_scroll,browser_drag,browser_uploadandbrowser_dialogcover infinite feeds, drag and drop, file inputs andalert/confirm/prompt.
When a typed tool exists, browser_invoke is better: a seven-field form is one validated call, not seven separate actions.