Tool management
Surface the right tools to the right agents — and see every tool's schema, token cost, and page-wise context budget.
Tool management
You probably have far more tools than any agent should see at once. Tool management is the layer that decides — given the current agent's trust, class, and where they are in your flow — which subset to expose, and the dashboard that shows you what every tool costs an agent's context.
The point: agents stop thrashing through 60-tool menus. You ship one registry, the SDK decides what to surface, and the product UI shows you the bill.
Register tools
outputSchema is optional and, like inputSchema, is surfaced in the dashboard (see below). authz is forwarded into the policy engine automatically — every governed tool produces a matching tool:<name> policy rule.
Surfacing
client.surfaceTools({ identity }) returns the visible subset for a given agent. The decision pipeline:
- Trust floor. Tool's
authz.minTrustmust be ≤ identity trust. - Allowed classes. If
authz.allowedClassesis non-empty, identity class must match. - Stage gate. If the tool has a
stage, it must be the current stage or inenabledStages. - Authz decision. Tools whose authz
decision === 'deny'never surface.
client.explainSurfacing({ identity }) returns the same call with a reason string per tool, useful for debugging in the dashboard.
Groups
Every tool can declare a group. client.groupedTools({ identity }) returns the surfaced subset bucketed by group, sorted alphabetically. Groups are presentational — they help WebMCP clients render menus, but never affect surfacing decisions.
Progression stages
Stages model where a session is in your flow. A site that has browse and checkout stages surfaces different tools at each.
A stage transition emits tool.progressed with { from, to, trigger } metadata. The next surfacing call sees the new stage.
WebMCP adapter
client.publishToWebMcp({ identity }) calls navigator.modelContext.provideContext({ tools: [...] }) with the surfaced subset. Each tool's invoke runs through the registry's policy pipeline, so denies are still enforced even when called by a WebMCP-native agent.
The adapter no-ops in environments where navigator.modelContext is missing (server-side, locked-down browsers).
Token budget
Tool descriptions cost tokens — every surfaced tool occupies space in the agent's context. client.estimateTokens({ identity }) returns:
The estimate is Math.ceil(JSON.stringify(descriptor).length / 4) — a rough OpenAI-style rule of thumb. Use it to reject overlarge tool sets before publishing.
Syncing to the dashboard
client.syncTools() pushes the full registry — each tool's page (stage), group, input/output schema, and estimated token cost — to the gateway so the Tool management page can render it:
It's a cheap idempotent upsert — re-call it whenever your registry changes.
The Tool management view
The dashboard groups the synced tools page-wise (by page/stage), and for each page shows every tool's input/output schema and token cost. Each tool renders like this:
{
"type": "object",
"properties": {
"itemId": {
"type": "string"
}
},
"required": [
"itemId"
]
}{
"type": "object",
"properties": {
"ok": {
"type": "boolean"
}
}
}Context fullness, per page
The headline number on each page is context fullness — how much of an agent's context window the page's tools occupy if they all load at once. The dashboard budgets 4,000 tokens per page; the bar fills as you add tools:
It's the same tokens estimate estimateTokens() uses, so a fat description shows up as an outlier you can trim. As a page's tools approach the budget the bar shifts amber, then red past 100% — your cue to split the page, gate tools behind a later stage, or trim descriptions.
Worked example
A retail site has 14 tools. For a detected-trust visitor on /browse, surfacing returns the 4 read-only ones (catalog search, catalog read, reviews read, shipping estimate) — ~612 tokens, comfortably under budget. After the user signs in (linked) and advances to /checkout, the same registry surfaces 11 of 14 — ~3,120 tokens, still under 4,000 but now in the amber zone, exactly where you'd want to start trimming.
If a tool inflates description or inputSchema beyond reason, it lights up in perTool (and as a tall slice of the bar) as an outlier. Trim the description, or split a fat tool into two.
Traces
| Event | When |
|---|---|
tool.registered | A tool is added to the registry. |
tool.surfaced | Surfacing changes — state is enabled, disabled, or published. |
tool.executed | A tool runs — outcome is success or blocked. |
tool.progressed | A stage transition fires. |
Filter for tool.* in the dashboard to watch the surfacing layer in real time.