Visitar URL original
[6.x] Add MCP servers by riasvdv · Pull Request #19846 · craftcms/cms · GitHub
Skip to content

[6.x] Add MCP servers - #19846

Merged
brandonkelly merged 74 commits into
6.xfrom
feat/craft-mcp-integration
Oct 6, 2026
Merged

brandonkelly merged 74 commits into
6.xfrom
feat/craft-mcp-integration

Conversation

@riasvdv

@riasvdv riasvdv commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds Model Context Protocol (MCP) servers to Craft, so AI agents and other MCP clients can inspect and manage a site's content, content model, users, plugins, and project config.

Craft runs three servers:

  • Authenticated, at {cpTrigger}/mcp. Clients sign in through OAuth 2.1 with Laravel Passport, using discovery metadata, dynamic client registration, and PKCE. Users need the new Use Craft MCP permission as well as control panel access.
  • Public, at a configurable route. It is disabled by default. Each public capability and each content source (sites, sections, volumes, user groups, element types) must be approved under Settings → MCP.
  • Stdio, through php craft mcp:serve --user=…, for clients that launch Craft as a local process. It acts as the named user, who needs the Use Craft MCP permission.

Capabilities cover sites, sections, entry types, fields and field layouts, entries, drafts, revisions, structures, assets and volumes, users, addresses, user groups and permissions, routes, image transforms, plugins, project config, editorial workflows, activity timelines and comments, cross-type search, and content-model audits. Activity recorded through MCP is labeled with an MCP origin, including activity from queued jobs started through MCP.

Plugins can extend the servers:

  • register capability classes with CapabilityRegistry;
  • gate capabilities with #[RequiresPermission], #[RequiresAdmin], #[RequiresAdminChanges], and #[RequiresHttp];
  • offer capabilities publicly with #[PublicMcp];
  • change serialized elements through the ElementSerializing event;
  • describe custom field input with ProvidesInputSchema;
  • add content-model checks through CheckRegistry.

docs/mcp.md describes how the servers work and how plugins extend them.

This adds the mcp/sdk, laravel/passport, and symfony/psr-http-message-bridge dependencies, and a migration for Passport's OAuth tables. Craft adds its Passport scope and guard alongside a host application's existing Passport configuration rather than replacing it.

php craft mcp:serve --user=<id|username|email> serves the authenticated MCP server over stdio as the named user. Capabilities marked #[RequiresHttp], such as assets.upload.prepare, are not offered over stdio. The stdio server replaces the debugUserId config setting for local debugging.
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

📚 Storybook previews

@craftcms/ui — open Storybook

No changed components detected in this Storybook.

resources/js — open Storybook

No changed components detected in this Storybook.

Registering OPTIONS and POST on the same URI against McpController::admin made Wayfinder generate an object literal with duplicate keys, which failed the TypeScript check.
tools/list returns 50 tools per page in filesystem scan order, so the asset tests now follow nextCursor instead of assuming their tool is on the first page. The nested pagination test soft-deletes its entry: on PostgreSQL, hard-deleting an element whose revisions were created in the same transaction re-checks the revision foreign key after the cascade has removed the revision.
elements.canonicalId nulls on delete while drafts and revisions cascade, so PostgreSQL could null a derivative element's canonicalId after removing its draft or revision row, failing the draftId or revisionId foreign key. Removing the drafts and revisions first leaves the same end state without the conflict. Restores the hard delete in the nested pagination MCP test.
@riasvdv
riasvdv marked this pull request as ready for review October 6, 2026 12:50
@riasvdv
riasvdv requested a review from brandonkelly October 6, 2026 12:51
brandonkelly and others added 3 commits October 6, 2026 06:01
Setting the acting user on the Craft guard fired `Authenticated`, which the
adapter answered by wrapping the user model in an IdentityWrapper (copying
no attributes, so a null ID) and logging it in through the console User
component. The resulting Login event recorded a login and called
findOrFail(null), throwing "No query results for model [User]".

- Wrap the user's element rather than its model in the adapter's
  Authenticated and Login listeners.
- Make the console User component's setIdentity() set the guard's user
  instead of logging in, as Craft 5's console identity did, and skip it when
  the guard already has that user, so it can't loop back through
  Authenticated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[6.x] Fix craft:mcp:serve failing with the Yii2 adapter installed
@riasvdv
riasvdv marked this pull request as draft October 6, 2026 13:19
@riasvdv
riasvdv marked this pull request as ready for review October 6, 2026 14:56
@brandonkelly
brandonkelly merged commit d5a8327 into 6.x Oct 6, 2026
20 checks passed
@brandonkelly
brandonkelly deleted the feat/craft-mcp-integration branch October 6, 2026 17:21
brandonkelly added a commit that referenced this pull request Oct 6, 2026
@boboldehampsink

Copy link
Copy Markdown
Contributor

@riasvdv may I ask why laravel/mcp was not used?

@linear-code

linear-code Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

CMS-2552

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.

3 participants