WP Manifestindependent plugin directory
manifest / ai / wordpress-content-mcp

Spoko Content MCP

WordPress plugin: AI agents read and edit posts block by block over MCP, with a hash check, dry run and a revision on every save

★ 1stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/spokospace/wordpress-content-mcp/archive/refs/heads/main.zip
Updates itself. It ships its own updater, so new versions appear under Dashboard → Updates.
Spoko Content MCP: AI agents read and edit WordPress posts block by block, with a hash check and a revision on every save

Spoko Content MCP

A WordPress plugin that lets AI agents (Claude, Cursor, any MCP client) read and edit posts and their Gutenberg blocks, through the official MCP Adapter and the Abilities API.

An agent that rewrites a whole post to change one paragraph can break blocks it never meant to touch, or overwrite an edit someone made a minute ago. This plugin gives it smaller, safer steps:

  • Edit one block, not the whole post. Blocks are addressed by a path (12.0.3), so a change replaces, inserts, moves or deletes exactly that block and leaves the rest byte for byte.
  • No lost edits. Every read returns a content_hash; every write needs it. If the post changed in the meantime, the write is refused.
  • Look before you save. Every write takes dry_run.
  • Always a way back. Every save is a normal post update with a revision (revision_id).
  • Valid blocks only. Before saving, the content is checked for broken block structure; if the check fails, nothing is saved.

Requirements

  • WordPress 6.9+ (Abilities API in core), PHP 7.4+
  • The MCP Adapter plugin
  • A WordPress user for the agent (an application password). The tools run with that user's own capabilities: edit_post for the post, upload_files for find-media.

Install

  1. Download spoko-content-mcp.zip from the latest release.
  2. Plugins → Add New → Upload Plugin, activate it.
  3. Turn on Enable auto-updates for it: new releases install themselves (see Updates).
  4. Point your MCP client at https://your-site/wp-json/spoko/content, e.g. with @automattic/mcp-wordpress-remote:
{
  "mcpServers": {
    "spoko-content": {
      "command": "npx",
      "args": ["-y", "@automattic/mcp-wordpress-remote@latest"],
      "env": {
        "WP_API_URL": "https://your-site/wp-json/spoko/content",
        "WP_API_USERNAME": "agent-user",
        "WP_API_PASSWORD": "application password"
      }
    }
  }
}

MCP clients list the tools as spoko-content-<name>, e.g. spoko-content-get-outline. Most clients read the tool list once when they start, so restart the client after an update that adds tools.

Tools

Tool Writes What it does
find-posts no Search posts and pages by title text, slug or ID.
get-outline no Block tree with paths, names and text previews, plus content_hash.
get-blocks no Serialized markup of blocks by path, plus content_hash.
validate-post no Report broken block structure.
find-media no Search the media library: IDs, URLs, image sizes.
replace-block yes Replace the block at a path with new markup.
insert-blocks yes Insert blocks before or after a path.
delete-block yes Delete the block at a path.
move-blocks yes Move consecutive sibling blocks next to another block, markup unchanged.
unwrap-block yes Replace a container (e.g. a group) with its inner blocks.
link-images yes Link every unlinked image of a post to its full-size file.

A block's path is its index in the parsed block tree, joined with dots: 12 is the 13th top-level block, 12.0.3 the 4th inner block of its 1st inner block. Blank separators between blocks take an index too, so top-level blocks are usually 0, 2, 4… Get paths from get-outline.

Typical edit:

  1. get-outline → paths and content_hash.
  2. The write tool with expected_hash and dry_run: true → check the result.
  3. The same call without dry_run → saved; revision_id is the backup.

link-images

Lightboxes usually open only images that have a link (<a href> around the <img>). Images added with "Link to: None" have none, so the lightbox skips them. For every core/image block in the post, galleries and columns included, link-images:

  • links it the way the editor does for "Link to: Media file": linkDestination: "media" and <a href="…"> around the <img>. The file is the attachment's full URL (the -scaled file for big uploads); without an attachment, the src without its -WxH size suffix;
  • leaves it alone when it already has a link outside the caption (never a double <a>);
  • skips it, with a reason in skipped, when the figure has the class no-lightbox (the opt-out), the full file cannot be told, or the markup is unexpected.

Nothing else in the post changes, and a second run saves nothing. Images outside core/image blocks (tables, classic content, core/html) are out of scope.

Headless sites: if the frontend serves the uploads under its own address, set it, and links point there instead of at WordPress:

// wp-config.php
define( 'SPOKO_CONTENT_MCP_MEDIA_BASE', 'https://example.com/images' );

// or in code, plus old media hosts to rewrite as well:
add_filter( 'spoko_content_mcp_media_base', fn () => 'https://example.com/images' );
add_filter( 'spoko_content_mcp_media_aliases', fn ( array $bases ) => [ ...$bases, 'https://img.example.com' ] );

URLs under the uploads base URL (and under the aliases) are rewritten; other hosts are left as they are. Without a media base, links use the URLs WordPress gives.

Roadmap

Tools for the rest of a content workflow, with the same hash, dry-run and revision rules:

  • creating and updating posts and pages, moving them to the trash;
  • categories and tags;
  • Polylang translations: pairing posts as translations of each other (only when Polylang is active).

Updates and releases

There are two builds of the same code:

  • GitHub build (dist/spoko-content-mcp.zip, the GitHub release asset) updates itself from this repo's GitHub releases (updater.php): the Plugins screen shows the new version, and with Enable auto-updates on, WordPress installs it by itself. A release counts when its tag is a higher version (v2.0.1), it is not a draft or a prerelease, and it has the asset spoko-content-mcp.zip.
  • WordPress.org build (dist/wporg/spoko-content-mcp.zip) has no updater.php, since the plugin directory does not allow plugins to update themselves; WordPress.org delivers its updates. The plugin loads the updater only when the file is there.

Releasing (no CI needed):

  1. Bump Version: in the plugin header, VERSION in the class and Stable tag in readme.txt (the script refuses if they differ), add changelog entries to README.md and readme.txt, merge to main.
  2. On main, run bin/release.sh --publish. It builds both zips with git archive (top folder spoko-content-mcp/) and creates the GitHub release v<version> with the changelog entry as notes. Without --publish it only builds the zips. On Windows, run it from Git Bash: bash started from PowerShell may be WSL, whose git sees the checkout as modified and the script refuses.
  3. WordPress.org (once the plugin is approved): copy the WordPress.org build into the SVN checkout's trunk/, copy trunk/ to tags/<version>/, and svn commit. The directory assets (banner, icon) are art/wporg/*.jpg|png and go to SVN assets/; repaint them with python art/wporg/render.py.

Before a WordPress.org release, run Plugin Check on the WordPress.org build (wp plugin check spoko-content-mcp); 2.0.1 passes with no errors or warnings, experimental checks included.

Tests

No WordPress install or database needed:

php tests/link-images.php /path/to/wordpress spoko-content-mcp.php
php tests/updater.php

link-images.php runs link-images against WordPress's real block parser and serializer (from the given WordPress checkout) with stubs for the rest. updater.php checks the updater against stubbed GitHub responses. Both exit 0 when everything passes.

Changelog

  • 2.0.1: one endpoint only, /wp-json/spoko/content.
  • 2.0.0: first public release. Block tools (find-posts, get-outline, get-blocks, validate-post, find-media, replace-block, insert-blocks, delete-block, move-blocks, unwrap-block, link-images) at /wp-json/spoko/content, a configurable public media address for link-images (SPOKO_CONTENT_MCP_MEDIA_BASE, filters), updates from GitHub releases. MIT license.

Made by spoko.space

spoko.space

Spoko Content MCP is built and maintained by spoko.space: websites, online shops and web apps, headless WordPress included. Questions and ideas are welcome as issues.

License

MIT © spoko.space