If you added WebMCP to a WordPress site in the first months of the origin trial, some of that code is now written against an API that no longer exists. The spec moved the entry point to document.modelContext, dropped provideContext() and friends, made registration a promise, and on October 8, 2026 took the declarative form attributes out of the spec altogether.
The short answer: search your theme and plugins for the old names, switch each tool to document.modelContext.registerTool() with an AbortSignal, keep your form attributes but stop relying on them, and test again in the current Chrome. On most sites that is an hour’s work. Here is what changed, how to find it, and what to replace it with.
Key takeaways
- Early WebMCP code on WordPress may now be written against an API that no longer exists. The entry point is now document.modelContext, provideContext() and its siblings are gone, and registration returns a promise. arvindkumar.ca
- Tools are now removed with an AbortSignal passed as the second argument to registerTool(). A duplicate tool name makes registerTool() reject with an InvalidStateError, so a script that registers twice fails loudly unless you catch it. arvindkumar.ca
- To find old code, search wp-content, your snippets plugin and the loaded sources in DevTools for modelContext. Report hits inside third-party plugins to their authors instead of editing them, or the next update will undo your fix. arvindkumar.ca
- Rewrite each tool to register on its own with registerTool(), keep a controller for removing it, and catch the promise. Inside each tool, read input as an object, listen for toolactivated and toolcancel on the model context, and set the annotations the spec names. arvindkumar.ca
- When code fails on WordPress, the usual causes are a script loaded twice, held back by a performance plugin, or registered on every page. Enqueue it once, exclude it from JavaScript delay, and wrap the enqueue in a conditional such as is_page( ‘contact’ ). arvindkumar.ca
- Keep toolname and tooldescription on your forms for now, but do not rely on them alone. The declarative API was removed from the spec on October 8, so those attributes are the part most likely to change again. arvindkumar.ca
- Test on a staging copy with getTools(), the Model Context Tool Inspector and a browser without WebMCP. Then check Chrome’s WebMCP pages and the spec once a month while the trial runs. arvindkumar.ca
Summarised from this post, formatted for answer engines.
What changed in the WebMCP API?
Five things changed that matter for code already on a site. I checked each against the current spec (the Community Group draft dated October 27, 2026) and Chrome’s WebMCP documentation.
- The entry point is
document.modelContext. The spec defines it on Document, in secure contexts only, and does not mentionnavigator.modelContextat all. Chrome’s documentation usesdocument.modelContexttoo. - Three methods remain:
registerTool(),getTools()andexecuteTool(). There is noprovideContext(),clearContext()orunregisterTool(). - You remove a tool with an AbortSignal. Pass
{ signal }as the second argument toregisterTool()and abort the controller when the tool should go. Chrome’s imperative API page says that since Chrome 153 this no longer breaks a call already in progress. registerTool()returns a promise. The spec says it rejects with an InvalidStateError when a tool with the same name is already registered, so a script that registers twice now fails loudly unless you catch it.- The declarative API left the spec. Pull request 338, merged on October 8, removed the Declarative WebMCP section. Its description says declarative WebMCP “is still being explored” in a separate explainer.
Two smaller notes from Chrome’s pages: the declarative page says window.ontoolactivated and window.ontoolcancel are not supported from Chrome 156, and the imperative page says JSON-stringified input arguments are deprecated from Chrome 155.
How do you find old WebMCP code on a WordPress site?
Search wp-content for the old names. WebMCP code tends to hide in three places: a child theme’s functions.php or script folder, a small custom plugin or mu-plugin, and snippets pasted into a code snippets plugin, which live in the database rather than in files.
- Run a search from the WordPress root on a copy of the site or over SSH. Any line it prints is a candidate.
grep -rnE "navigator\.modelContext|provideContext|clearContext|unregisterTool|ontoolactivated|ontoolcancel|toolname" \
wp-content/themes wp-content/plugins wp-content/mu-plugins \
--include=*.js --include=*.php
- Check your snippets plugin. Open its list and search for
modelContext. If it has no search, look through the active JavaScript snippets. - Check what the browser actually loads. Open a page that should have tools, open DevTools, press Ctrl+Shift+F (Cmd+Option+F on a Mac) and search all loaded sources for
modelContext. This catches code from a plugin you forgot about, and code a performance plugin has combined into another file. You are done when every hit is in a file you know.
Ignore hits inside third-party plugins you did not write. Report those to the plugin’s author instead of editing them, or the next update will undo your fix.
How do you rewrite provideContext() code for the current API?
Register each tool on its own with registerTool(), keep a controller for removing it, and catch the promise. Early guides often handed a whole list of tools to provideContext() in one call, like this:
// Old: no longer in the spec.
navigator.modelContext.provideContext( {
tools: [ searchTool, contactTool ],
} );
The same job against the current spec, where searchTool and contactTool are the tool objects you already have:
( () => {
const modelContext = document.modelContext;
if ( ! modelContext || typeof modelContext.registerTool !== 'function' ) {
return; // No WebMCP in this browser. The page works as before.
}
const controller = new AbortController();
for ( const tool of [ searchTool, contactTool ] ) {
modelContext
.registerTool( tool, { signal: controller.signal } )
.catch( ( error ) => console.warn( 'WebMCP tool not registered:', tool.name, error ) );
}
// Where old code called clearContext() or unregisterTool(): controller.abort();
} )();
Then check three details inside each tool object.
- Read the input as an object. If an
execute()function runsJSON.parse()on its input, it was written for stringified arguments, which Chrome now marks deprecated. Destructure the object instead:async execute( { query } ). It works when the tool returns results in the inspector without a parse error. - Move event listeners off window. The spec puts the
toolactivatedandtoolcancelevents on the model context, so listen withdocument.modelContext.addEventListener( 'toolactivated', handler ). It works when the handler fires as the inspector runs a form tool. - Use the annotations the spec names. They are
readOnlyHint,untrustedContentHintandconsequentialHint, all false unless you set them. SetreadOnlyHinton search and lookup tools, andconsequentialHinton anything that sends, books or buys.
Why does WebMCP code fail on WordPress after the update?
Usually because the script runs twice, too late, or on every page. Now that duplicate names reject, the first of those shows up as a console warning where it used to fail quietly.
- Loaded twice. The same tool file printed once by
wp_enqueue_script()and once as a pasted snippet, or enqueued under two handles, registers every name twice. Enqueue it once, under one handle, from one plugin. - Held back by a performance plugin. A “delay JavaScript until interaction” setting means an agent arrives at a page with no tools. Add the file to the plugin’s exclusions, and check again after every cache purge.
- Registered everywhere. A site-wide script gives the agent a contact tool on your privacy policy page. Wrap the enqueue in a conditional such as
is_page( 'contact' )oris_shop(), so each page offers only the jobs that make sense there.
Should you remove toolname and tooldescription from your forms?
No, leave them in for now, but do not treat them as your only route. Chrome’s Declarative API page, last updated in September, still documents toolname, tooldescription, toolparamdescription and toolautosubmit, and the declarative explainer in the WebMCP repository still uses the same names. What changed is that they are no longer in the spec, so they are the part most likely to change again.
The Lighthouse audit that flags them, “Forms missing declarative WebMCP”, lists forms without both attributes. Its own page says it is informational and does not result in any warnings, so an unmarked form is not a failure you have to fix this week.
What I do on the sites I run: keep the attributes on the one or two forms that matter, leave toolautosubmit off so a person still presses Send, and give any imperative tool a different name from any form’s toolname, so the two can never collide. If the declarative API comes back in a different shape, there is one filter to change, not a site to rework.
How do you check the updated code works?
Check it in the console, in Chrome’s Model Context Tool Inspector extension and in a browser without WebMCP, on a staging copy first.
- Turn WebMCP on locally. Enable
chrome://flags/#enable-webmcp-testingand relaunch, or make sure your origin trial token is still valid. Tokens expire; my origin trial post shows where to check. - List the tools. In the console,
await document.modelContext.getTools()should return each tool once. No “not registered” warnings should appear. - Run each tool in the inspector. Call it by hand with sample input. It works when it returns the result you expect.
- Open the page in another browser. Nothing should change for a person, and the console should stay clean.
Put a reminder in your calendar to look at Chrome’s WebMCP pages and the spec once a month while the trial runs. Each change so far has been small on its own; they only hurt when several pile up in code nobody has opened since it was written. If you are still deciding where WebMCP fits next to a server-side MCP setup, my MCP pillar post covers that.
