Repository navigation
v0.5.0
MCP now saves content the way the Control Panel does. Each value goes through its fieldtype's processing and validation, URLs have to be unique, and permission checks follow the CP's author and site rules. Publishing is its own pair of tools.
Agents also get new tools. They can preview drafts, edit navigation menus, nest and move pages, read the blocks of your page builder fields, and follow guidelines you write in markdown.
This release has breaking changes, and two of them are security fixes. A ^0.4 constraint never resolves to 0.5, so read the upgrade notes before you bump.
Security
Two permission gaps let MCP do more than the Control Panel allows. Every version before 0.5.0 has both.
- On a blueprint with an
authorfield, a role with a collection's edit, publish, or delete permission could do that to anyone's entry through MCP. The CP limits it to entries the user authored. The entry tools now check what Statamic'sEntryPolicychecks, and a denial names the missing permission.statamic_overviewtells agents which of these permissions they have. - On multisite, MCP never checked
access {site} sitefor the default site. A role with onlyaccess fr sitecould still read and write default-site content. MCP now gates the default site like every other site.
One more fix concerns OAuth mode. mcp:setup printed the private signing key during its key step, also in unattended runs. If you ran mcp:setup --oauth in a pipeline whose logs others can read, rotate the key: delete the row in statamic_mcp_oauth_keys and any storage/oauth-*.key files. The next request provisions a fresh pair, and every connected client reconnects through the OAuth flow.
Upgrading
composer require danielgnh/statamic-mcp:^0.5Check these before you deploy.
- Statamic 6.31 or newer is required. Composer refuses earlier 6.x releases by default because of a security advisory, so most sites are there already. laravel/mcp 0.8 keeps working, and 0.9 and 1.x work too.
- Publishing has its own tools.
entries_createandentries_updateno longer acceptpublished. Agents publish withentries_publishand take entries offline withentries_unpublish, both behind the collection's publish permission. A client with a cached tool list gets an error that points toentries_publish. - Roles need the "other authors" permissions for entries they didn't write. Grant
edit other authors {collection} entries,publish other authors {collection} entries, ordelete other authors {collection} entriesto roles that should keep that access. Entries with no author count as someone else's. - The default site needs site access. On multisite, grant
access {site} sitefor the default site to every role that should keep reaching it. - New entries get an author. When the blueprint has an
authorfield anddatanames none,entries_createsets the acting user, as the CP does. Naming someone else, or changing the author withentries_update, needsedit other authors {collection} entries. - Published configs need the navigations key. If you published
config/statamic/mcp.php, add'navigations' => trueunderresources. Without it, the navigation tools see no menus. - Writes are validated like CP saves. Unknown set types, unknown keys inside sets, grid rows, and groups, asset paths missing from the field's container, and a URL another entry already has now fail with an error that names the problem. MCP used to save them.
- OAuth mode: run
php artisan migrate. The migration that widens Passport'suser_idcolumns ran too early on fresh installs and did nothing. It is renamed so it runs after Passport's own migrations. Sites it missed convert on the next migrate, and sites with converted columns see a no-op.
New
entries_publishandentries_unpublish. On revision-enabled collections they promote or apply the working copy and record a revision under the acting user, which only the CP could do before. Your MCP client asks for permission per tool, so allowingentries_updateno longer lets an agent publish.entries_previewreturns a Live Preview URL that renders a draft or working copy through your templates. Anyone with the URL can open it for an hour, so an agent can check its own work before anyone publishes. It changes no content, andread_onlykeeps it.navigations_getandnavigations_updateread and replace a navigation's tree.navigations_updatechecks the tree againstmax_depth, the root page rule, and the navigation's blueprint.entries_createandentries_updatetakeparentto nest or move an entry in a structured collection. Parallel tree edits take a lock per collection and site, so none of them drops another's placement.blueprints_getlists the sets of Replicator and Bard fields with their instructions. Passsetto get one set's fields and an example row. It also reports where asset, entry, term, and user fields point and how many items they take.- Guidelines for agents. Markdown files in
resources/mcp/guidelinesreach agents throughstatamic_overviewandblueprints_get.php please mcp:guidelinescreates the starter files without overwriting any and lists the blocks that have no instructions yet, blocks inside grids and groups included. Thestatamic-mcp-guidelinesBoost skill helps a coding agent write them. - Your own tools. Point the new
serverconfig key at a subclass ofDanielgnh\StatamicMcp\Server, then add, replace, or remove tools intools(). They run behind the same authentication andaccess mcpcheck as the built-in tools. - Scheduling.
statamic_overviewreports each dated collection'sdate_behaviorand the server timezone. When a date keeps a published entry hidden,entries_publishreports it as scheduled or expired.entries_gettakesworking_copy: true, andentries_listfilters bystatus: expired. - laravel/mcp 0.9 and 1.x work alongside 0.8. On 1.x, JSON-RPC errors come back as HTTP 400, 404, or 500 instead of 200, and clients on the 2026-07-28 protocol revision connect through
server/discover. If you run OAuth mode and have narrowedmcp.redirect_domains, move to laravel/mcp 0.9.6 or 1.0.1. Both fix the loopback redirect URI check in OAuth client registration, and 0.8 won't get that fix.
Fixed
- Writes store what the Control Panel stores. Each value goes through the fieldtype's
preProcess()andprocess(), so a single asset or relationship saves as a plain string, sets and grid rows get ids, dates use the field's save format, and HTML sent to a Bard field becomes ProseMirror. What the get tools return can be written back unchanged, also in collections with several blueprints:entries_getnow names the blueprint at the top level instead of insidedata. - Updating an entry, term, or global set no longer fails on a single-file asset, single-item relationship, or date that the CP saved, and a patch to a localization no longer fails on a required field it inherits from its origin.
- A new entry in a structured collection goes into the collection's tree. Its
urlused to come backnull. blueprints_getexamples match what the write tools accept.sluganddateare out of the example payload, option fields use the option's key, and relationship and date fields use the stored shape.entries_updateno longer reports "published" after a new date makes the entry scheduled or expired, and it reports the staged URL, not the live one, when it stages a working copy.entries_updaterefuses a date on a localization whose date field isn't localizable. Publishing used to drop that date without a word.mcp:keysprints only the keys on stdout, somcp:keys --json | jqandmcp:keys >> .envwork on the first run too. With only onePASSPORT_*key set, it reads each half the way Passport does.
Known caveats
entries_createalways uses the collection's default blueprint. It has noblueprintparameter, andblueprintinsidedatais refused as reserved.blueprints_getreports a set's group by name, without the group's instructions. Put instructions on the sets themselves.- Writes accept sets hidden from the CP's set picker.
blueprints_getmarks them"hidden": true.
The full list is in CHANGELOG.md.
Pull requests
- feat: add your own tools by naming a server subclass in config by @danielgnh in #18
- feat: publishing is its own tool pair (entries_publish / entries_unpublish) by @danielgnh in #19
- feat: tools() hook with a ToolRegistry to add, replace, and remove tools by @danielgnh in #21
- feat: make Statamic's own scheduling visible and honest to agents by @danielgnh in #22
- fix: writes store what the Control Panel stores by @danielgnh in #23
- fix: leave top-level parameters out of blueprints_get examples by @danielgnh in #24
- feat: page builder blocks and guidelines for agents by @danielgnh in #25
- feat: entries_preview returns a live-preview link for drafts by @danielgnh in #26
- feat: navigation tools and parent on entries_create by @danielgnh in #27
- fix!: match the Control Panel's author and site permissions by @danielgnh in #28
- feat: page builder sets as a short list, with a lookup per set by @danielgnh in #29
- feat: move entries in their tree with parent on entries_update by @danielgnh in #30
- feat: support laravel/mcp 0.9 and 1.x by @danielgnh in #31
- test: run the Passport-free tests in their own CI job by @danielgnh in #32
- fix: bugs found testing the unreleased changes end to end by @danielgnh in #33