Skip to content

MCP server internals ​

The mcp/ module exposes the CMS over the Model Context Protocol through laravel/mcp. The user side is on MCP Server.

Wiring ​

mcp/src/Http/routes.php registers Mcp::local('evolve', EvolveServer::class) always (stdio, php artisan mcp:start evolve) and Mcp::web(config('mcp.http.path'), EvolveServer::class) when mcp.http.enabled. McpServiceProvider sets $defaultEnabled = false but the shipped config says 'mcp' => true, and the config wins; requiredPackages() names laravel/mcp, which the package only suggests since 2.21; without it the module does not boot. The provider publishes only mcp.config.

The server ​

mcp/src/Servers/EvolveServer.php carries #[Name], #[Version('2.1.0')] and #[Instructions] that describe every tool and its rules to the connecting agent. $tools lists thirteen tool classes from mcp/src/Tools/ (list pages, get page structure, get block content, get SEO meta, list modules, list media, create page, update block content, set page published, add block to page, add child block, delete block, delete page), $resources two from mcp/src/Resources/ (available block types, site config). $publicTools and $publicResources are the anonymous surface.

boot() runs per request: when restrictsToPublicContent() is true, the tool and resource lists are swapped for the public ones and Support\PublicScope::enable() marks the request. The decision: not the HTTP transport, trusted (stdio); HTTP without a configured mcp.http.auth_guard, public; otherwise auth()->guard($guard)->check(). Over HTTP, boot() also binds the authenticated user as the actor (Racerfish\Mcp\Support\McpActor, 2.18). Each write tool checks the actor against the permission catalogue before mutating (pages.create, pages.delete, pages.publish, pages.edit for the block tools) and honours Sanctum token abilities; a denial returns isError: true with the missing permission. Without an actor (stdio) the check passes.

Public scope ​

PublicScope is a request-scoped container flag; the read tools call its query helpers to restrict pages and blocks to what the frontend would show (the same public and published scopes). It is never set for stdio.

Tools ​

Each tool extends Laravel\Mcp\Server\Tool, declares its schema (page ID, locale, search, confirm for the delete tools, is_published for the publish tool) and returns JSON. CreatePageTool creates the page, its route, SEO title and a nested block tree in one call and returns page_id, path and edit_url. The delete tools hard-delete and descend into morph children themselves to avoid orphans. Shared behaviour sits in mcp/src/Tools/Concerns.

Tokens ​

mcp/src/Console/Commands/MakeMcpTokenCommand.php (mcp:token {user} {--name=mcp}) issues a Sanctum personal access token for a user by ID or email. Sanctum is a dev dependency of the package only; a project installs it and adds HasApiTokens to its user model. Without Sanctum the endpoint stays read-only for everyone.

Tests ​

mcp/tests/: public versus authenticated surface, the Sanctum path (2.3.1), each write tool, permission denials and narrow token abilities (2.18), and requests in the MCP 2026-07-28 shape (_meta plus headers), which skip below laravel/mcp 1.0. The constraint is ^0.6 || ^0.7 || ^0.8 || ^0.9.6 || ^1.0.1. The starter bundle core/stubs/claude/.mcp.json wires evolve and laravel-boost over stdio for Claude Code.