Skip to content

Add WebMCP experiment - #1081

Open
webmyc wants to merge 9 commits into
WordPress:developfrom
webmyc:add/webmcp-experiment
Open

webmyc wants to merge 9 commits into
WordPress:developfrom
webmyc:add/webmcp-experiment

Conversation

@webmyc

@webmyc webmyc commented Sep 30, 2026 •

Copy link
Copy Markdown

What?

Adds an opt-in WebMCP experiment for the block editor, following #448. Fourteen tools let an agent browser read the current post, set its title, insert and edit blocks, move, duplicate and transform blocks, undo, save and publish. Changes happen through the editor's data stores and remain visible on the page.

Why?

WebMCP lets an agent work in the UI the person is looking at. The experiment does not mirror Abilities API tools; server-side abilities remain available through MCP.

How?

  • Uses develop's Abstract_Feature, feature registry and loader. The bridge loads on post editor screens, offers a JavaScript registry and filter for additional page tools, and registers each tool separately with a default cap of 30.
  • Registers built-in tools only when the current page has an initialized block editor, including when initialization finishes after DOM ready. Classic editor screens list and register no built-in tools.
  • Updates, removals and moves check the editor's lock selectors. A move after the same block is a no-op; moving a block into itself or a descendant returns an error.
  • The text shortcut supports blocks with a content attribute. Quote text is edited through its inner paragraphs; pullquote text uses its value attribute through the attribute tool.
  • Save and publish await savePost(), check that saving has settled, and report didPostSaveRequestFail() as an error.
  • Removes the unused eval files. CHANGELOG.md and CREDITS.md match develop; the changelog entry is below.

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code, Codex
Model(s): Claude Fable 5.1, GPT-6
Used for: drafting the implementation, addressing review feedback, and adding regression tests. I reviewed and take responsibility for the result.

Testing Instructions

  1. Run npm run build, then enable WebMCP under Settings, AI.
  2. Open a post. In an agent browser, ask it to set the title and add a paragraph; both should change on the page.
  3. Without an agent browser, call tools in the console:
    const tools = wpai.webmcp.getTools();
    await tools.find( t => t.name === 'editor-set-title' ).execute( { title: 'Hello' } );
    await tools.find( t => t.name === 'editor-insert-block' ).execute( { attributes: { content: 'A paragraph.' } } );
  4. Run npm run test:php -- --filter WebMCP and npm run test:e2e -- tests/e2e/specs/experiments/webmcp.spec.js. The nine browser tests cover visible editing, structural tools, locked operations, self/descendant moves, quote attributes, failed saves/publishes, classic editor availability and non-editor screens.

Validation

  • Build, TypeScript typecheck, JavaScript lint, PHPCS and PHPStan passed. JavaScript lint reports 33 warnings outside the changed files and zero errors; the changed JavaScript/TypeScript files lint without warnings.
  • Full browser suite: 207 passed. Focused WebMCP suite: 9 passed.
  • PHP suite and Composer strict test command: 1,777 tests, 5,003 assertions, 42 skipped.
  • Multisite PHP suite: 1,777 tests, 5,014 assertions, 38 skipped.
  • Local checks used Node 24.21.0, WordPress 7.1.2 and Docker wp-env. The local ~/wp-ai mount alias was removed using an ignored override, and pretty permalinks were restored before the full browser run.

Screenshots or screencast

No separate UI; the effect is the editor changing.

Changelog Entry

  • New Experiment: WebMCP; lets an agent browser work in the block editor through document.modelContext, with tools that set the title, insert and edit blocks, save and publish, every change visible on the page as it happens.
Open WordPress Playground Preview

@webmyc webmyc mentioned this pull request Sep 30, 2026
@codecov

codecov Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.99%. Comparing base (ecc9ecd) to head (58ec101).

Additional details and impacted files
@@              Coverage Diff              @@
##             develop    #1081      +/-   ##
=============================================
+ Coverage      81.95%   81.99%   +0.04%     
- Complexity      3350     3358       +8     
=============================================
  Files            135      136       +1     
  Lines          13026    13056      +30     
=============================================
+ Hits           10675    10705      +30     
  Misses          2351     2351              
Flag Coverage Δ
unit 81.99% <100.00%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jeffpaul jeffpaul added this to the Future Release milestone Sep 30, 2026
@jeffpaul jeffpaul linked an issue Sep 30, 2026 that may be closed by this pull request
@jeffpaul jeffpaul modified the milestones: Future Release, 1.5.0 Sep 30, 2026
@jeffpaul
jeffpaul requested a review from swissspidy September 30, 2026 15:30
@jeffpaul

Copy link
Copy Markdown
Member

@danieliser tagging you as you had some feedback on the prior iteration in #224.

@danieliser

Copy link
Copy Markdown

@zackkatz might have some feedback & insights here as well from his work on block-mcp and atomic editing for the block editor.

We use that for all agentic work outside a browser at this point. Any attempts to do it for WebMCP likely need similar exposures (though through the JS api of course).

@webmyc

webmyc commented Oct 1, 2026

Copy link
Copy Markdown
Author

@danieliser thank you, that pointer was the useful kind. I read Block MCP's surface against what this PR had and three gaps were plain: no way to place a block inside a container, no structural operations beyond remove, and no discovery of what a position allows or what attributes a block type takes.

Pushed in ee48cf5, through the JS API as you said:

  • editor-insert-block takes parentClientId and asks canInsertBlockType first, so a locked template, an allowed-blocks list or a parent restriction refuses the insert instead of being bypassed. That is the editor's own version of Block MCP's tier policy.
  • editor-move-block, editor-duplicate-block, editor-transform-block (through the editor's transforms, so a paragraph becomes a heading the way the Transform menu does it).
  • editor-get-block-types: what a position allows, or one block type's attribute schema.
  • editor-undo: each tool call is one undo step, which is the in-editor counterpart of revision-backed undo.

Two things Block MCP solves that the editor gives for free, which is worth knowing when comparing the two: clientId is the stable ref for the session, and attribute changes re-render through the block's own save function, so there is no attrs and innerHTML drift to guard against.

What I did not carry over: atomic batches (one undo step per call seemed the honest unit inside an editor a person is watching) and path-based addressing (refs cover it once the outline is read). @zackkatz if either of those turned out to matter more than it looks from the outside, I would rather hear it now.

@webmyc
webmyc marked this pull request as ready for review October 2, 2026 09:51
@webmyc
webmyc requested a review from a team October 2, 2026 09:51
@webmyc
webmyc requested a review from jeffpaul as a code owner October 2, 2026 09:51
@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @danieliser.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: danieliser.

Co-authored-by: webmyc <urbankidro@git.wordpress.org>
Co-authored-by: dkotter <dkotter@git.wordpress.org>
Co-authored-by: jeffpaul <jeffpaul@git.wordpress.org>
Co-authored-by: swissspidy <swissspidy@git.wordpress.org>
Co-authored-by: justlevine <justlevine@git.wordpress.org>
Co-authored-by: fellyph <fellyph@git.wordpress.org>
Co-authored-by: jorgefilipecosta <jorgefilipecosta@git.wordpress.org>
Co-authored-by: gziolo <gziolo@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Comment thread CHANGELOG.md Outdated
Comment thread CREDITS.md Outdated
Comment thread src/experiments/webmcp/evals.json Outdated
Comment thread tools/webmcp-evals.mjs Outdated
Comment thread src/experiments/webmcp/editor-tools.ts Outdated
Comment thread src/experiments/webmcp/editor-tools.ts
Comment thread src/experiments/webmcp/editor-tools.ts
Comment thread src/experiments/webmcp/editor-tools.ts
Comment thread src/experiments/webmcp/editor-tools.ts
Comment thread src/experiments/webmcp/editor-tools.ts
@webmyc

webmyc commented Oct 6, 2026

Copy link
Copy Markdown
Author

Thanks @dkotter, these are all fair. I'm working through them now: reverting the CHANGELOG and CREDITS edits, removing the evals files, fixing the quote blocks, the save check, locked blocks, the classic editor check and the two move cases, plus a rebase on develop. I'll reply on each thread when it's pushed.

@webmyc
webmyc force-pushed the add/webmcp-experiment branch from ee48cf5 to 40e4313 Compare October 6, 2026 09:37
@webmyc

webmyc commented Oct 6, 2026 •

Copy link
Copy Markdown
Author

@dkotter I addressed all 11 review comments in 40e4313 and rebased onto develop; could you take another look?

GitHub CI is now green for this commit, including the PHP matrix and all three E2E shards; the full local browser suite also passed all 207 tests.

Comment thread src/experiments/webmcp/editor-tools.ts Outdated
Comment thread src/experiments/webmcp/editor-tools.ts
Comment thread src/experiments/webmcp/editor-tools.ts
Comment thread src/experiments/webmcp/editor-tools.ts
Comment thread includes/Experiments/WebMCP/WebMCP.php
Comment thread includes/Experiments/WebMCP/WebMCP.php Outdated
@webmyc
webmyc force-pushed the add/webmcp-experiment branch from 40e4313 to f1a2157 Compare October 7, 2026 11:06
@webmyc

webmyc commented Oct 7, 2026

Copy link
Copy Markdown
Author

@dkotter I addressed the six comments in f1a2157 and rebased onto develop. CI is green. Could you take another look?

@dkotter dkotter left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the work here! I think just a few more things and this will be good to go

Comment thread src/experiments/webmcp/editor-tools.ts
Comment thread src/experiments/webmcp/editor-tools.ts Outdated
Comment thread src/experiments/webmcp/editor-tools.ts
Registers a curated, opt-in set of WordPress abilities as WebMCP tools on
the page through document.modelContext, one registerTool call per tool,
and executes them through a REST route that runs the ability's own
permission and input checks on the server. The Abilities API stays the
single registry.

Two page contexts (wp-admin, front end) with separate allowlists and a
per-page cap of 30 tools, because agent browsers cap what a page may
register. Exposure is explicit: an ability opts in through its meta, a
filter allows it, or the site owner lists it in the experiment settings.
Tool names carry the ability name with __ in place of /, since a URL-encoded
slash never reaches WordPress on stock Apache. Two tokens travel with each
execution: the wp_rest nonce core requires in X-WP-Nonce and the
experiment's own token in X-WPAI-WebMCP-Nonce.

See WordPress#448. Keeps WordPress#224 as prior art.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
webmyc and others added 6 commits October 8, 2026 14:09
phpstan-wordpress infers the filter's return type from its default and
WP_Ability::get_input_schema() is typed array, so the two is_array()
guards read as always true; one is dropped, the other kept with an
ignore, because a filter callback can return anything at runtime.

The enqueue tests now assert on the real handle (the loader prefixes
ai_, not ai-), register their own ability because core's are absent in
the PHPUnit context, and cover the positive paths on wp-admin and the
front end, which CI can run because it builds the scripts first.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The maintainers' reading of WebMCP is that an agent drives the UI on the
current page and the person watches it happen, and that a site already
connected over MCP gains nothing from the same server-side abilities
registered again in the browser (WordPress#448). This commit changes what the
experiment is accordingly.

The Abilities API path is gone: no REST routes, no allowlists, no tokens.
What stays is what is the same either way: registration on
document.modelContext one tool at a time, the per-page cap, and a screen
filter. The bridge now carries a JavaScript registry
(wpai.webmcp.registerTool, the wpai.webmcp.tools filter) and ships nine
editor tools that dispatch into core/editor and core/block-editor: read
the document outline, set the title, insert a block, replace a block's
text, change attributes, remove, select, save, publish. Every change is
visible immediately and lands in the post's undo history.

An e2e spec installs a document.modelContext shim before the editor
loads, calls the tools the way a browser would, and asserts that the
title field and the canvas change. An eval set with a small runner
judges the tool descriptions by whether a model picks the right tool.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Following the pointer to Block MCP in review: an agent editing blocks needs
stable references, structural operations and a way to discover what a
position allows. Inside the editor those come from its own stores.

Adds editor-move-block, editor-duplicate-block, editor-transform-block
(through the editor's transforms), editor-get-block-types (what a position
allows, or one type's attribute schema) and editor-undo (one step per tool
call). editor-insert-block can now place a block inside a container, and
asks canInsertBlockType first, so a locked template, an allowed-blocks
list or a parent restriction refuses the insert instead of being bypassed.

The e2e spec covers move, undo, duplicate, transform, discovery, nesting
and a refused insert. Seven eval cases added for the new tools.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- editor-publish checks the post can be saved before setting the status,
  and puts the previous status back if the save fails.
- editor-transform-block refuses blocks locked against removal and
  transforms the block does not offer.
- editor-duplicate-block throws when nothing was created.
- editor-update-block-attributes refuses changes to the lock attribute.
- The screens filter falls back to the defaults when it returns a non-array.
- Bridge data goes through localize_script; the bridge reads maxTools as a number.
…al saves

- Saving and publishing are refused while the post is locked by another
  user (isPostLocked) or saving is locked (isPostSavingLocked).
- editor-update-block-attributes rejects templateLock as well as lock.
- A save that reports no failure but leaves the post dirty is reported as
  a failure (isEditedPostDirty).
@webmyc
webmyc force-pushed the add/webmcp-experiment branch from f1a2157 to da009a7 Compare October 8, 2026 11:09
@webmyc

webmyc commented Oct 8, 2026

Copy link
Copy Markdown
Author

@dkotter the three are in da009a7, rebased onto develop, CI green. Thanks for the careful reviews.

…or duplicating blocks, ensure that actually worked before we send a success response. Don't rollback the post status if save worked but still threw an error
@jeffpaul
jeffpaul removed their request for review October 8, 2026 18:58
@jeffpaul

jeffpaul commented Oct 8, 2026

Copy link
Copy Markdown
Member

@swissspidy @gziolo @jorgefilipecosta would appreciate a parallel review from one of you on this one, will ideally ship in the 1.5.0 release either way though if you're swamped with work elsewhere (aka not the most urgent/important item)

@swissspidy
swissspidy removed their request for review October 8, 2026 19:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add WebMCP experiment

4 participants