Build your first imperative WebMCP tool from an existing UI action
Expose one narrow action while keeping the visible interface, application services, and business rules intact.
The short version: A WebMCP handler should be a thin, validated entry point into behavior the application already trusts.
Pick one narrow action
Start with a read-only or reversible action that already works through the UI. Product search, availability lookup, or saving a non-final draft is safer to learn on than checkout, deletion, permission changes, or outbound messaging.
Name the action for what it actually does. A tool called search_products should search products, not silently personalize results, add items, or collect profile information.
Register only when the API is available
Feature detection keeps the application usable in browsers without WebMCP. Scope registration to the route or component where the action makes sense, and give that scope an AbortController so stale tools disappear when the page context changes.
if ('modelContext' in document) {
const registration = new AbortController();
await document.modelContext.registerTool({
name: 'search_products',
title: 'Search products',
description: 'Filter the visible catalog without changing cart state.',
inputSchema: {
type: 'object',
properties: { query: { type: 'string', maxLength: 120 } },
required: ['query'],
additionalProperties: false
},
annotations: { readOnlyHint: true },
execute: (input, { signal }) => searchProducts(input, currentUser, signal)
}, { signal: registration.signal });
// Call registration.abort() when this route or component is disposed.
}Validate structure and authority
The input schema helps an agent form a request, but it is not an authorization boundary. Reject unknown properties, bound strings and arrays, re-check identity, and enforce the same tenant and object permissions as the UI and backend API. Treat every call as untrusted input even when the browser performed schema checks.
Return structured errors that distinguish invalid input, missing state, denied access, conflict, and upstream failure without leaking sensitive details. The agent should know what can be corrected and what requires a person.
Return evidence and synchronize the UI
A useful result includes stable identifiers, the state that changed, and a compact verification summary. If the action affects the visible application, update the same state store the UI reads. The person should not have to guess whether the agent and page disagree.
- Stable record or product identifiers
- Previous and new state for mutations
- A visible_state_updated flag only when true
- Postcondition checks and bounded next actions
- No raw secrets, tokens, or unnecessary personal data
Primary references
Read the sources
Put it to work
Use the guide on a real product surface.