WebMCP page tools
Connect Agentak to structured WebMCP tools published by the current page or active browser tab.
WebMCP lets a page publish client-side tools on document.modelContext. Agentak turns
those tools into visible Pi tool calls inside its complete chat UI. The page keeps its own
UI, session, and state; the agent calls only the actions that the page deliberately
exposes.
This makes the chat part of the application instead of a separate assistant that only sees text. A shop can publish product search and cart actions. A dashboard can publish filtered queries. An editor can publish read and update actions while keeping its existing state and validation.
#Quick start
Enable discovery for the current document with one option:
import { createPiSession } from "agentak/pi";
const session = createPiSession({ page: true });This feature is off by default. A browser without WebMCP, or a page that publishes no
tools, simply adds nothing to the agent. Host tools passed through tools continue to work
alongside page tools.
Use Agentak in either place:
- Embed the chat in the application and use
page: trueto read that document directly. - Use the Agentak Chrome side panel to discover tools from the active tab as the user navigates.
#Browser support
WebMCP is experimental. The version Agentak targets is available in Chrome 149 and Edge 150 behind an origin trial, and in Brave's Leo integration. Firefox and Safari do not have a public build. Always rely on feature detection rather than a browser name:
import { webmcpSupported } from "agentak/pi";
if (webmcpSupported()) {
// document.modelContext is available now.
}documentTools() also checks for an API that appears shortly after the chat mounts. It
waits for up to five seconds, then stops polling.
#What Agentak maps
A registered WebMCP tool becomes a Pi AgentTool:
| WebMCP value | Pi and chat value |
|---|---|
name | provider-safe tool name |
title | human-readable label |
description | model description |
inputSchema | tool parameter JSON Schema |
executeTool() result | text or MCP-shaped text/image content |
readOnlyHint | approval policy |
untrustedContentHint | warning for the model and the user |
WebMCP arguments and results cross the API as JSON strings. Agentak accepts both the newer object schema and the JSON-string schema returned by earlier browser builds.
#Names and changing pages
WebMCP names can contain characters and lengths that model providers reject. Agentak:
- replaces unsafe characters with
_ - limits names to 64 characters
- adds
_2,_3, and so on when a host tool or another frame already uses the name
For example, cart.add reaches the model as cart_add.
A single-page application may register different tools on each screen. Agentak listens for
toolchange, reads the list again, and updates the live Pi tool set without creating a new
session.
#Approval and untrusted content
The page's annotations have security effects:
readOnlyHint: trueruns without a confirmation because the page says the tool changes nothing.- A missing or false read-only hint asks before every call.
untrustedContentHint: trueadds a warning before the result for the model and labels the output for the person reading it. The model is told to treat the content as data, never as instructions.
A global approvals: "never" disables all confirmation, including page-tool confirmation.
Do not use it when a page can expose tools that change user data.
#Provide a custom page source
Pass a PageTools implementation instead of true to control where tools come from. This
minimal example publishes one tool manually; an iframe or side panel can put its own bridge
behind the same interface:
import { createPiSession, type PageTools } from "agentak/pi";
const page: PageTools = {
async list() {
return [
{
name: "read_page",
title: "Read the page",
description: "Read the rendered text of the active page.",
inputSchema: { type: "object", properties: {} },
origin: location.origin,
readOnly: true,
untrusted: true,
},
];
},
async call(_tool, _args, _signal) {
return JSON.stringify({
title: document.title,
url: location.href,
text: document.body.innerText,
});
},
};
const session = createPiSession({ page });A source can also provide subscribe(listener) when its list changes and dispose() for
cleanup. list() returns only serializable metadata. call() receives the selected tool,
JSON arguments, and an optional abort signal, then returns JSON text.
A real WebMCP RegisteredTool contains a live Window and cannot cross a worker, port, or
extension boundary. A bridge must run getTools() and executeTool() in the source
document and send only tool metadata, arguments, and result text across.
#Extension behavior
The Agentak Chrome side panel combines two sources:
read_active_tab, supplied by the extension for every reachable tabdocument.modelContextThe extension tool steps aside where the page publishes a reader of its own, such as a tool
named read_page. It runs WebMCP calls in the tab's main world, follows tab changes and
navigation, and puts the count of page-published tools on the toolbar badge.
read_active_tab is not included in the badge because it exists on nearly every page.
Read Chrome extension for permissions, active-tab history, and link handling.