- Renamed Craft’s MCP tools to use snake_case, such as
elements_listinstead ofelements.list, since some MCP clients reject or rename tools with dots in their names. Public MCP server approvals are updated automatically. Plugins should name their MCP tools the same way, such asmy_plugin_orders_list. (#20351) - The
assets_createandassets_replaceMCP tools now accept base64-encoded data, local file paths (stdio only), and single-use upload URLs, in addition to download URLs. Download URLs no longer require afile_id, and now follow up to 3 redirects. - Added the
assets_upload_urlMCP tool, which returns single-use upload URLs forassets_createandassets_replace. Over stdio (mcp/serve), upload URLs are based on the site’s base URL, which must be absolute. - The
assets_createMCP tool can now upload files to an Assets field’s default upload location, via its newfieldIdandelementIdarguments. - Added the
user_permissions_user_updateanduser_permissions_group_updateMCP tools, which add and remove specific permissions without resending complete lists. (#20434) - The
configuration_get,configuration_list,configuration_create, andconfiguration_updateMCP tools now return each record’s revision, andconfiguration_updateandconfiguration_deleteaccept it asexpectedRevision, failing if the record has changed since it was read. (#20440) - MCP
fieldsarguments now accept dot paths, such asblocks.innerBlocks, which include nested fields of nested elements as summaries. - MCP tools now return users’ addresses when
addressesis included infields. - The
asset_folders_listMCP tool now acceptsnullforcriteria.parentId, which lists volumes’ root folders. - The My Account → Tokens screen is now only shown to admins until OAuth signing keys are set up, and explains how to set them up. Token creation requests now return a 400 error rather than a server error until then. (#20357)
- The web-based installer is now only available when Dev Mode is enabled. Otherwise, Craft must be installed with the
installCLI command. - The web-based installer’s actions are no longer available once Craft is installed, even when Dev Mode is enabled.
- The web-based installer now responds with a 503 error when Craft can’t connect to the configured database, rather than offering to change the database connection settings.
- The web-based installer now only saves database connection settings to the
.envfile once Craft has been installed with them, and no longer accepts them if the.envfile isn’t writable. - Added
craft\config\McpConfig::filters(). (#20558) - Added
craft\base\McpOutputFieldInterface, which fields can implement to control how their values are returned by MCP tools. - Added
craft\mcp\McpOutputContext. - Added
craft\mcp\elements\ElementAdapterRegistry::supports(). - Added
craft\mcp\serializers\ElementSerializer::serializeNestedValue()andsummarize(). - Added
craft\services\ProjectConfig::acquireLock(). (#20440) craft\elements\Asset::canSave()now returnstruefor users’ own assets in their temporary upload folder.- Updated
mcp/sdkto 0.9. MCP tools that declare anoutputSchemanow return an error if their structured content doesn’t match it. - Fixed a bug where authenticated GraphQL requests didn’t respect field layout conditions. Hidden fields are now excluded from the schema (or resolve to
nullfor element-specific conditions), and non-editable fields are excluded from mutation arguments (or error when set). (#20391) - Fixed a bug where field values submitted for nested entries, Content Block fields, and addresses could be saved even if the user wasn’t permitted to see or edit the fields, per their field layout conditions. (#20391)
- Fixed a bug where OAuth discovery metadata for front-end resources, such as the GraphQL API, revealed the control panel URL. OAuth endpoints for front-end resources are now advertised as action URLs, and discovery metadata is only served from
.well-knownURLs. - Fixed a bug where the PostgreSQL password was included in the error message when a database backup or restore command failed. (#20503)
- Fixed a bug where a failed project config YAML write left the
config/project/folder empty. Existing files are now only replaced as new ones are written, and the error now includes its underlying cause. (#20519) - Fixed a bug where saving user or group permissions dropped permissions that weren’t registered for the current request, such as when using the
user_permissions_group_setMCP tool over stdio. The MCP permission tools now also reject unknown permissions rather than ignoring them. (#20434) - Fixed a bug where garbage collection could leave foreign key constraints disabled if it didn’t finish, and on PostgreSQL, other requests could write data without foreign key enforcement while it ran. (#20386)
- Fixed a bug where entries and assets could be deleted for the only site they exist in, via the
elements/delete-for-siteaction or the “Delete for site” element action. - Fixed a bug where “Delete for this site” could be offered for an entry’s provisional draft when the canonical entry only existed in that site.
- Fixed a bug where
craft\base\Element::EVENT_AUTHORIZE_DELETE_FOR_SITEhandlers were never called, so they couldn’t authorize users to delete elements for a site. - Fixed a bug where “New entry” buttons could offer entry types or sections that
craft\elements\Entry::EVENT_DEFINE_ENTRY_TYPEShad excluded, resulting in a 400 error. (#20322) - Fixed a bug where field layout conditions within Content Block fields and nested entries didn’t update based on dropdown values, such as a Link field’s selected link type. (#20332)
- Fixed an error that occurred when editing an address within a draft, if the owner element wasn’t saved in the primary site. (#20413)
- Fixed a bug where Markdown field layout elements weren’t getting translated. (#20408)
- Fixed a bug where date pickers within slideouts could open far away from their inputs at some browser zoom and display scaling levels. (#20399)
- Fixed a bug where password reset and email verification URLs generated from the console, such as with
users/password-reset-url, always pointed to the front end, even for users with control panel access. (#16340) - Fixed a bug where MCP clients couldn’t sign in when the control panel had its own domain and no
cpTrigger. (#20353) - Fixed a bug where the authenticated MCP server returned a server error instead of an OAuth challenge when OAuth signing keys weren’t set up. Settings → MCP now explains how to set up missing or invalid keys.
- Fixed a bug where the “New token” button on the Tokens screen didn’t work after a personal access token was created. (#20356)
- Fixed a bug where revoking the last token or authorized app on the My Account → Tokens screen redirected to a 403 error when the screen was no longer available, such as when GraphQL was disabled. (#20531)
- Fixed a bug where MCP tools couldn’t set entries’
postDateorexpiryDatevalues. (#20405) - Fixed a bug where the MCP server required date-time values for Time fields within nested entries, which they couldn’t parse. (#20319)
- Fixed a bug where the
configuration_updateMCP tool couldn’t update Matrix field settings. (#20406) - Fixed a bug where the MCP server saved relation field sources and field layout elements that referenced nonexistent sections or fields. (#20412, #20439)
- Fixed a bug where MCP tools redacted all field and project config settings, rather than only sensitive ones. (#20412)
- Fixed a bug where MCP tools returned empty arrays for CKEditor field values.
- Fixed a bug where the
elements_list,drafts_list,revisions_list,search_query, andcraft_queryMCP tools ignoredfields: null, returning only element metadata instead of all non-nested custom fields. - Fixed a bug where strict MCP clients, such as VS Code, rejected the
search_queryMCP tool. (#20329) - Fixed a bug where project config changes made through the stdio MCP server (
mcp/serve) weren’t saved until the server stopped. (#20330) - Fixed a bug where unhandled MCP tool errors were logged without their exception details. (#20406)
- Fixed a bug where
craft\db\Migration::archiveTableIfExists()truncated archived table names, such aswebhoo_oldinstead ofwebhookgroups_old. (#20484) - Fixed a bug where system directory restrictions weren’t consistently enforced on Windows. (#20444)