Skip to main content

Agent surface – the page, as tools

An agent-surface node lets an AI agent operate the page: move between morph views, submit a form you have allowed, and read what is on screen. Nothing is authored twice – the tools are derived from the same (E, N) pair that renders the page.

let nodes = [
{ op: "morph", chain: [
{ from: "home", to: { Work: "work" } },
{ from: "work", to: { Aurora: "aurora" } } ] },

{ op: "agent-surface", name: "studio", forms: ["contact-form"] },
];

That derives four tools: studio_navigate, studio_go_back, studio_submit_contact-form and studio_read_view.

Why derive rather than annotate

Your page already says what it can do. The morph chain is the navigation graph, and a form's fields already declare their types and which are required. Writing tool handlers beside all that would restate it in a second place, where it would drift the first time either changed.

Opt-in, always

No node, no surface. Turning a page's interaction structure into callable tools is the page's decision, and upgrading the library never makes it for you. The same goes for forms: no form is exposed unless you name it in forms, because a derived submit tool is an agent acting – the one derived capability with a real side effect.

What each tool does

navigate takes a destination from an enum of the views your graph can reach. These are in-page views reached by a transition, not separate URLs – Nodality has no router, and between pages an agent already has links. Execution goes through the controller's own goToState, so an agent's traversal is the user's traversal and inherits the same rules.

go_back is present only where an edge declares back. It unwinds the path actually taken.

submit_<form> fills the real fields and submits through the form's own path, so your listeners and validation attributes apply. Its schema is read from your field descriptors: inputType: "email" becomes a string with an email format, a picker's items become an enum, a checkbox becomes a boolean, and required: true becomes a required property.

read_view returns the content currently on screen as structured text – headings, body copy, and the labels of anything actionable.

Errors are repair reports

A destination that exists but is not reachable from where the graph currently stands comes back as a report naming what is reachable:

{ "ok": false, "code": "UNREACHABLE_FROM_HERE",
"got": "aurora", "valid": ["work", "contact"] }

A submit missing a required field names the field rather than returning a bare failure. An agent repairs from either in one turn.

The static half

Prerendering writes the same declaration into the page as <script type="application/json" id="nodality-agent-manifest">, and gathers every page's declaration into agent-manifest.json beside your HTML. That file needs no browser support and no script execution: a crawler, an indexer, or an agent deciding whether to visit at all can read what your site can do.

The declaration and the live registration come from one derivation, so they cannot disagree.

Note that only the root view of a morph chain is in the prerendered HTML – the other states are held until they are transitioned to. Put a form you want annotated at build time outside the graph.

Fields

FieldMeaning
nameprefix for every derived tool name; defaults to the mount id
formsids of forms to expose; nothing is derived from a form you do not list
excludeview ids never offered as destinations

Browser support

Tool registration uses the WebMCP browser API, which is a W3C Community Group draft in origin trial – available in Chrome behind chrome://flags/#enable-webmcp-testing and in Edge behind a flag. Where it is absent nothing registers, nothing warns, and your page behaves identically. The manifest is written either way.