De Imperative API gebruikt document.modelContext.registerTool() om JavaScript-tools programmatisch te definiëren. Dit is ideaal voor apps met één pagina (SPA's) waar tools moeten worden toegevoegd of verwijderd op basis van de huidige route of gebruikersstatus.
WebMCP werkt op een producent/consument-architectuur:
- Webpagina's zijn toolproducenten: Je definieert en registreert tools met
document.modelContext.registerTool(). Dat is alles wat een webpagina moet implementeren. Je hoeftgetTools()ofexecuteTool()niet aan te roepen op de pagina om tools beschikbaar te maken voor agents. - Browser- en extensie-agents zijn de primaire consumenten: Agents die worden geleverd door of via de browser, waaronder de ingebouwde agent van de browser, browserextensies, DevTools en assistenten op systeemniveau, ontdekken en roepen geregistreerde tools out-of-band aan via interne platformmechanismen.
- In-page agents en test harnesses:
getTools()enexecuteTool()bestaan specifiek voor in-page JavaScript-assistenten (bijv. een ingesloten chatwidget of<iframe>) en geautomatiseerde testpakketten die tools rechtstreeks in de paginacontext moeten ontdekken en aanroepen.
Registratie en levenscyclus
Tools worden geregistreerd door een tooldefinitieobject en een optioneel optieobject (ModelContextRegisterToolOptions) door te geven. Dit ondersteunt 2 optionele leden:
signal: EenAbortSignaldie wordt gebruikt om de registratie van de tool ongedaan te maken als deze niet meer nodig is.exposedTo: Een matrix van oorsprongstekenreeksen die bepalen welke documenten in de documentstructuur de tool mogen ontdekken en uitvoeren over framegrenzen heen.
Levenscyclusbeheer met AbortController
WebMCP biedt geen unregisterTool()-methode. Als je de registratie van een tool ongedaan wilt maken, moet je tijdens de registratie een AbortSignal doorgeven en dat signaal afbreken als de tool niet meer nodig is.
const controller = new AbortController();
await document.modelContext.registerTool({
name: "get_user_preferences",
description: "Retrieves the user's saved preferences.",
inputSchema: { type: "object", properties: {} },
execute() {
const prefs = localStorage.getItem("user_prefs");
return prefs ? JSON.parse(prefs) : { theme: "light" };
},
annotations: { readOnlyHint: true }
}, { signal: controller.signal });
// To unregister the tool (e.g., on component unmount):
controller.abort();
Als je het registratiesignaal afbreekt, wordt de registratie van de tool ongedaan gemaakt. Uitvoeringen die al lopen, worden niet geannuleerd. Tools die langdurig werk starten, moeten ook het signaal per uitvoering respecteren dat hieronder wordt beschreven. Anders blijven er bij het ongedaan maken van de registratie verweesde verzoeken en verouderde UI-updates achter.
Blootstelling van cross-origin tools beheren met exposedTo
Een geregistreerde tool is standaard privé voor het registrerende document en frames van dezelfde oorsprong. Als je app is ingesloten in een <iframe> (of widgets van derden insluit) en je bovenliggende of onderliggende frames nodig hebt om de tool te vinden en aan te roepen, geef je de toegestane herkomsten op met exposedTo.
const controller = new AbortController();
await document.modelContext.registerTool({
name: "get_cart_items",
description: "Retrieves items currently in the user's shopping cart.",
inputSchema: { type: "object", properties: {} },
async execute(input, { signal }) {
return await cartService.getItems({ signal });
},
annotations: { readOnlyHint: true },
}, {
signal: controller.signal,
exposedTo: ["https://checkout.example.com"],
});
- Wederzijdse aanmelding: Cross-origin toolontdekking is een tweerichtingsaanmelding. Het frame van de toolprovider autoriseert de oorsprong van de consument via
exposedToen het consumentenframe moet die oorsprong van de provider expliciet aanvragen viagetTools({ fromOrigins: [...] }). ZonderfromOriginsvoertgetTools()alleen query's uit op documenten met dezelfde oorsprong. - Oorsprongovereenkomst: De eigen oorsprong van het registratiedocument heeft altijd toegang. Oorsprongen die zijn opgegeven in
exposedTokrijgen toestemming om de tool te ontdekken viadocument.modelContext.getTools({ fromOrigins: [...] })en uit te voeren viadocument.modelContext.executeTool(). - Beveiligingsvereiste: Elke oorsprong in
exposedToMOET potentieel betrouwbaar zijn (HTTPS of localhost). Als je een ongeldige of niet-beveiligde oorsprong doorgeeft, wordtregisterTool()afgewezen met eenSecurityError. - Bereik:
exposedTobeheert de detectie van meerdere documenten op de pagina in frames in de documentstructuur.
Parameters definiëren
Parameters (params) worden gedefinieerd met de eigenschap inputSchema. Dit moet een JSON-schema-object zijn dat de gestructureerde gegevens beschrijft die de tool verwacht.
await document.modelContext.registerTool({
name: "calculate_area",
description: "Calculates the area of a rectangle.",
inputSchema: {
type: "object",
properties: {
width: { type: "number", description: "The width of the rectangle." },
height: { type: "number", description: "The height of the rectangle." }
},
required: ["width", "height"]
},
execute(input) {
// input is { width: 10, height: 20 }
return input.width * input.height;
},
annotations: { readOnlyHint: true }
});
Beschrijvingen van schrijftools en parameters
De agent krijgt de volledige inputSchema al, dus de description hoeft alleen te zeggen wat de tool doet en wanneer deze moet worden gebruikt. Herhaal GEEN parameternamen, typen, enum-waarden, standaardwaarden of limieten. Declareer ze in plaats daarvan in het schema (enum, default, minimum/maximum). Voor parameterbeschrijvingen geldt dezelfde regel: zeg wat de waarde betekent, niet het type of de toegestane waarden.
De browser valideert geen argumenten tegen inputSchema en past geen default-waarden toe. Doe beide in execute.
const SORT_ORDERS = ["newest", "price_low_to_high"];
await document.modelContext.registerTool({
name: "search_products",
// Don't list the sort orders here, even via SORT_ORDERS: `enum` already does.
description: "Searches the product catalog by keyword. Use when the user wants to find or compare products.",
inputSchema: {
type: "object",
properties: {
query: { type: "string", description: "Words to search for." },
sort: { type: "string", enum: SORT_ORDERS, default: "newest", description: "Order of the results." },
},
required: ["query"],
},
async execute({ query, sort = "newest" }, { signal }) {
if (typeof query !== "string" || !SORT_ORDERS.includes(sort)) {
return { error: "Pass a search query and a supported sort order.", sortOrders: SORT_ORDERS };
}
return await productService.search({ query, sort }, { signal });
},
annotations: { readOnlyHint: true },
});
Uitvoeringspatronen
De callback execute krijgt altijd 2 argumenten: het invoerobject en een optieobject met een AbortSignal: execute(inputObject, { signal }).
Wanneer te gebruiken async execute
Gebruik async als de tool bewerkingen omvat die een Promise retourneren of tijd kosten om te voltooien:
- Netwerkaanroepen: Gegevens ophalen uit een API.
- Asynchrone opslag: Toegang tot IndexedDB.
- Externe gebeurtenissen: Wachten tot een specifieke statuswijziging of animatie is afgerond.
async execute(input, { signal }) {
const response = await fetch(`/api/data/${input.id}`, { signal });
return await response.json();
}
Wanneer u execute (synchroon) gebruikt
Gebruik een standaard synchrone functie voor onmiddellijke bewerkingen. Een synchrone tool wordt uitgevoerd tot deze is voltooid voordat de annulering kan worden waargenomen. De tool kan het signaal dus negeren:
- Pure logica: Wiskunde, filteren of sorteren van gegevens die al in het geheugen staan.
- Synchrone status: Lezen uit
localStorageof een synchrone statusbeheerder.
execute(input) {
return input.items.filter(item => item.active);
}
Fouten in execute afhandelen
Als execute een fout genereert (of een afgewezen promise retourneert), krijgt de agent alleen een algemene uitvoeringsfout. De foutmelding wordt niet doorgestuurd naar de medewerker. Als uw tool een actiegerichte of herstelbare fout tegenkomt (zoals ongeldige invoer, ontbrekende items of een mislukte HTTP-status), MOET U NIET throw new Error(...). Retourneer in plaats daarvan een gestructureerde foutpayload via de normale retourwaarde van de tool, zodat de AI-agent de diagnostische gegevens krijgt en zichzelf kan corrigeren of het opnieuw kan proberen:
execute({ itemName }) {
const item = menuItems.find((entry) => entry.name.toLowerCase() === itemName.toLowerCase());
if (!item) {
// Return the error as a value instead of throwing, so the agent receives the message:
return {
error: `Menu item "${itemName}" not found.`,
availableItems: menuItems.map((entry) => entry.name),
};
}
return item;
}
Alleen opnieuw genereren als de uitvoering is geannuleerd (signal.aborted is true).
Uitvoeringen die bezig zijn annuleren
De annulering wordt gestart door de gebruiker of de medewerker. VERPLICHT: Elke tool die netwerkverzoeken of ander langdurig asynchroon werk uitvoert, MOET signal respecteren. Anders blijven geannuleerde uitvoeringen resources verbruiken en kunnen ze verouderde gegevens in de UI schrijven nadat de agent is verdergegaan.
- API's die een signaal accepteren (
fetch(),addEventListener(),ReadableStreamlezers): geefsignalrechtstreeks door. - API's die dat niet doen (
setTimeout,setInterval, polling-loops,await-zware pipelines): roepsignal.throwIfAborted()aan tussen stappen en/of weiger opsignal.addEventListener('abort', ...). Timers accepteren geen signaal, dus je moet ze zelf wissen in de abort-listener.
await document.modelContext.registerTool({
name: "fetch_tool",
description: "Fetch the text content of a URL and show it on the page.",
inputSchema: {
type: "object",
properties: {
url: { type: "string", description: "The URL to fetch" },
priority: {
type: "string",
enum: ["high", "low", "auto"],
description: "Relative network priority for the request",
},
},
required: ["url"],
},
execute: async ({ url, priority }, { signal }) => {
const output = document.querySelector("pre");
output.textContent = "Loading…";
try {
// Passing `signal` aborts the network request the moment execution is cancelled.
const response = await fetch(url, { priority, signal });
if (!response.ok) {
output.textContent = "";
return { error: `Request failed with status ${response.status}` };
}
const text = await response.text();
output.textContent = text;
return text;
} catch (err) {
// Roll back the UI state this execution set, so a cancelled run leaves no trace.
output.textContent = "";
if (signal.aborted) {
// The execution was cancelled: re-throw instead of returning an error value.
throw err;
}
// Return other errors as a value instead of throwing, so the agent receives the message.
return { error: err instanceof Error ? err.message : String(err) };
}
},
annotations: { readOnlyHint: true }
});
Tools met gevolgen markeren
Sinds Chrome 154 ondersteunt annotations consequentialHint. Stel deze in op true voor tools die acties met een hoog risico, onomkeerbare acties of acties in de echte wereld uitvoeren, zoals reizen boeken, geld overmaken, berichten sturen of gegevens verwijderen. Hiermee wordt aangegeven dat agents expliciete gebruikersbevestiging moeten vragen voordat ze worden uitgevoerd.
await document.modelContext.registerTool({
name: 'book_flight',
description: 'Book a flight for the user with confirmed flight details.',
inputSchema: {
type: 'object',
properties: {
flightId: { type: 'string', description: 'ID of the flight to book' },
passengers: { type: 'number', description: 'Number of tickets to purchase' },
},
required: ['flightId', 'passengers'],
},
execute: async ({ flightId, passengers }, { signal }) => {
// Forward `signal` to the transaction API to honor cancellation:
return await bookFlightReservation({ flightId, passengers }, { signal });
},
annotations: {
readOnlyHint: false, // This tool mutates state (it makes a booking).
consequentialHint: true, // Spends money and is not trivially reversible.
untrustedContentHint: false, // Output is first-party data, not third-party content.
}
});
VERPLICHT: Behandel consequentialHint als een hint, niet als een handhavingsmechanisme. De hint blokkeert de uitvoering niet zelf en browsers die ouder zijn dan de hint, geven helemaal geen prompt. Behoud de in-app bevestiging die de actie al vereist.
Tools voor foutopsporing bij markeren
Sinds Chrome 156 ondersteunt annotations debugging. Stel annotations.debugging in op true voor tools die zijn gemaakt voor foutopsporing en tools voor ontwikkelaars in plaats van taken voor eindgebruikers. Voorbeelden: de interne app-status dumpen, functieflags aan- of uitzetten of testgegevens seeden. Zo kunnen agents tools die alleen voor ontwikkelaars zijn, onderscheiden van tools die bedoeld zijn om voor de gebruiker te handelen. De standaardwaarde is false. In-page agents en test harnesses kunnen dit lezen vanuit RegisteredTool.annotations.debugging via getTools().
await document.modelContext.registerTool({
name: "dump_app_state",
description: "Returns a snapshot of the internal application store for debugging.",
execute() {
return appStore.getState();
},
annotations: {
readOnlyHint: true, // Only reads state.
debugging: true, // Developer tooling, not an end-user action.
}
});
In-page agents en testen (getTools en executeTool)
Webpagina's hebben geen getTools() of executeTool() nodig om tools beschikbaar te maken voor AI-agents. Je hoeft alleen document.modelContext.registerTool() aan te roepen om browseragents, browserextensies, DevTools en assistenten op systeemniveau je tools out-of-band te laten ontdekken en uitvoeren.
De methoden getTools() en executeTool() zijn specifiek bedoeld voor consumenten die de JavaScript-omgeving gebruiken:
- In-page JavaScript-agents: Een AI-assistent, copilot-widget of chatinterface die rechtstreeks in de pagina is ingesloten (of wordt uitgevoerd in een
<iframe>) en die moet ontdekken welke tools de omringende pagina of toegankelijke frames in de documentstructuur bieden en deze namens de gebruiker moet aanroepen. - Geautomatiseerd testen en fouten opsporen: Testpakketten (zoals Playwright, Vitest of unittests in de browser) die geregistreerde toolmetadata inspecteren en aanroepen van agenttools simuleren zonder dat er een live externe LLM-harness nodig is.
Beschikbare tools ontdekken met getTools()
In-page agents roepen document.modelContext.getTools() aan om een array van RegisteredTool-objecten op te halen uit het huidige document en alle toegankelijke frames in de documentstructuur.
Sinds Chrome 155 wordt RegisteredTool.inputSchema rechtstreeks geretourneerd als een JavaScript-object (een diepe kopie van het schema dat bij de registratie is geleverd). Als je dit object muteert, verandert het werkelijke schema van de geregistreerde tool niet.
// Example: An in-page AI assistant discovering tools available on the page:
const tools = await document.modelContext.getTools();
// In-page agents can listen for tools being registered or unregistered dynamically:
document.modelContext.addEventListener("toolchange", async () => {
const updatedTools = await document.modelContext.getTools();
console.log(`Active tools updated: ${updatedTools.map((t) => t.name).join(", ")}`);
});
Als je tools wilt ontdekken die zijn geregistreerd in cross-origin onderliggende iframes (die expliciet toegang moeten verlenen via exposedTo tijdens de registratie), geef je fromOrigins door in de optiewoordenlijst:
// Query tools registered in the embedded cart frame that exposed them to this checkout origin:
const cartTools = await document.modelContext.getTools({
fromOrigins: ["https://cart.example.com"],
});
Tools aanroepen met executeTool()
In-page agents en testscripts roepen document.modelContext.executeTool(tool, inputObject, options) aan om een geregistreerde tool uit te voeren. De tool wordt uitgevoerd op het document waarin deze is geregistreerd en de promise wordt omgezet in het resultaat in tekenreeksvorm dat wordt geretourneerd door de callback execute van de tool.
Sinds Chrome 155 accepteert executeTool() een gewoon JavaScript-object rechtstreeks voor inputObject, wat overeenkomt met de invoerindeling die de callback execute van je tool krijgt.
// 1. Discover the tool (typically done during agent initialization or test setup):
const tools = await document.modelContext.getTools();
const areaTool = tools.find((t) => t.name === "calculate_area");
if (areaTool) {
const controller = new AbortController();
// Example input payload matching the tool's inputSchema:
const inputObject = { width: 10, height: 20 };
const options = { signal: controller.signal };
// 2. Pass input arguments as a JavaScript object, with a JSON string fallback for Chrome <155:
let resultString;
try {
resultString = await document.modelContext.executeTool(areaTool, inputObject, options);
} catch (err) {
if (err?.message?.startsWith("Failed to parse input")) {
resultString = await document.modelContext.executeTool(
areaTool,
JSON.stringify(inputObject),
options
);
} else {
// Re-throw real tool execution or cancellation errors:
throw err;
}
}
console.log("Execution output string:", resultString);
}
De geretourneerde promise wordt afgewezen als:
inputObjectis geen object of kan niet worden geserialiseerd naar een json-tekenreeks (bijv. circulaire verwijzingen ofBigInt-waarden). Waarden die worden weggelaten door json-serialisatie (zoals functies enundefined) worden weggelaten uit het object dat door de tool wordt ontvangen in plaats van dat ze een afwijzing veroorzaken.- De uitvoering is afgebroken via
options.signal. - De registratie van de tool is ongedaan gemaakt vóór de uitvoering of er is een onverwerkte runtimefout opgetreden.
Tool Factory Pattern
Gebruik factory-functies om context (zoals winkels of app-instanties) door te geven aan je tools.
export function createInventoryTool(inventoryManager) {
return {
name: "get_inventory",
description: "Lists items in the inventory.",
inputSchema: { type: "object", properties: {} },
execute() {
return inventoryManager.getItems();
},
annotations: { readOnlyHint: true }
};
}
TypeScript-typen
Schrijf in TypeScript-projecten GEEN handgeschreven declaraties voor document.modelContext. Installeer het officiële webmcp-types-pakket (npm install --save-dev webmcp-types) en voeg het toe aan tsconfig.json, omdat het niet automatisch wordt opgehaald:
{
"compilerOptions": {
"types": ["webmcp-types"]
}
}
document.modelContextis getypt als optioneel, dus beveilig het metif (document.modelContext)vóór gebruik.- Bij een inline
inputSchemawordt het invoertypeexecuteafgeleid van het schema. - Benoemde typen zoals
WebMCP.ModelContextToolzijn algemeen (geen import nodig) en zijn handig voor het typen van toolfabrieken.
Ondersteuning voor frameworks
VERPLICHT: Als de app React, Angular of Vue gebruikt, registreer je tools via de officiële WebMCP-integratie van dat framework, die hieronder wordt vermeld. Roep document.modelContext.registerTool() NIET aan vanuit componenten en schrijf geen eigen levenscycluswrapper. Elke integratie verwerkt al functiedetectie, AbortSignal-opschoning en scopingtools voor de levenscyclus van de component of injector. Deze API's zijn experimenteel. Zoek daarom de huidige handtekening op in de typen of documenten van het pakket voordat je code schrijft.
| Framework | Pakket | API | Documenten |
|---|---|---|---|
| React 18+ | use-webmcp-tool |
useWebMCP(options): één aanroep per tool |
npmjs.com/package/use-webmcp-tool |
| Angular | @angular/core, @angular/forms/signals |
provideExperimentalWebMcpTools([...]) (app- of routeproviders), declareExperimentalWebMcpTool(...) (interne services), provideExperimentalWebMcpForms() + de optie experimentalWebMcpTool op signaalformulieren |
angular.dev/ai/webmcp |
| Vue | @vueuse/core |
useWebMCP(options) composable |
vueuse.org/core/usewebmcp |
Alle regels in deze gids zijn nog steeds van toepassing als je deze integraties gebruikt:
- Houd de vorm van de tooldefinitie hetzelfde:
name,description,inputSchema,execute(input, { signal })enannotations. - Volg de regels voor beschrijvingen en annotaties.
- Valideer de invoer en pas de standaardwaarden toe in
execute. - Geef
signaldoor aan langlopend werk. - Als je een tool maar een deel van de tijd wilt aanbieden, geef je
enableddoor (React, Vue). Verpak de hook of composable aanroep NIET in een voorwaarde. - Integraties kunnen resultaten en fouten verpakken (bijvoorbeeld als
{ content, isError }). Controleer de documentatie van de integratie voordat je erop vertrouwt hoe fouten de agent bereiken.
API-notities
- annotations: (Optioneel) Een woordenboek voor toolmetadata.
- readOnlyHint: (Optioneel) Stel deze in op
trueals de tool geen status wijzigt en alleen gegevens leest. Zo kunnen agents bepalen wanneer het veilig is om de tool aan te roepen. - consequentialHint: (Optioneel, Chrome 154+) Stel in op
truevoor acties met grote gevolgen, onomkeerbare acties of acties in de echte wereld, zodat agents eerst om bevestiging van de gebruiker vragen. - untrustedContentHint: (Optioneel) Stel deze in op
trueals de tool content retourneert waarover je site geen controle heeft, zoals door gebruikers gegenereerde tekst of API-reacties van derden. - debugging: (Optioneel, Chrome 156+) Stel in op
trueals de tool is bedoeld voor foutopsporing en tools voor ontwikkelaars, niet voor taken van eindgebruikers. De standaardwaarde isfalse.
- readOnlyHint: (Optioneel) Stel deze in op
- Registratieopties:
- signal: (Optioneel) Een
AbortSignaldie wordt gebruikt om de registratie van de tool ongedaan te maken als deze niet meer nodig is. - exposedTo: (Optioneel) Een matrix van beveiligde oorsprongstekenreeksen die bepalen welke documenten in de documentstructuur de tool mogen ontdekken en uitvoeren over framegrenzen heen.
- signal: (Optioneel) Een
- Opties voor toolontdekking (
ModelContextGetToolOptions):- fromOrigins: (Optioneel) Een matrix van beveiligde oorsprongstekenreeksen om query's uit te voeren in toegankelijke frames. Als je
getTools()aanroept zonderfromOrigins, worden alleen documenten met dezelfde oorsprong opgevraagd.
- fromOrigins: (Optioneel) Een matrix van beveiligde oorsprongstekenreeksen om query's uit te voeren in toegankelijke frames. Als je
- Retourindeling: De functie
executekan elke JSON-serialiseerbare waarde retourneren (object, matrix, tekenreeks, getal, booleaans). Het bericht van een uitzondering die wordt gegenereerd doorexecute, wordt niet doorgestuurd naar de agent. Retourneer daarom actiegerichte fouten als gestructureerde foutpayloads (bijvoorbeeld{ error: "..." }) via de retourwaarde van de tool, zodat de agent deze kan lezen en erop kan reageren. - Beveiligde context: WebMCP vereist HTTPS. Alle oorsprongen in
exposedToenfromOriginsmoeten ook potentieel betrouwbaar zijn. - Beëindigd/verwijderd:
navigator.modelContext(beëindigd in Chromium 150),unregisterTool(),provideContext()enclearContext()worden niet meer ondersteund.
Reservestrategieën
De WebMCP Imperative API moet worden gebruikt met functiedetectie om compatibiliteit te garanderen met browsers die WebMCP nog niet ondersteunen.
if ('modelContext' in document) {
// Register tools
}