Expose tools, resources and prompts from a CakePHP 5 application to Claude and
any other Model Context Protocol client.
Built on the official mcp/sdk.
- Remote MCP over Streamable HTTP, mounted as a regular Cake route (
/mcp). bin/cake mcp serverfor local stdio clients, andbin/cake mcp inspectorto try the server by hand.- Tools are plain PHP classes with
#[McpTool]attributes, with constructor injection. - Authentication stays in your app (
cakephp/authentication); the plugin reads the identity. - OAuth 2.1 for remote clients: sign people in with your own login (
cakedc/cakephp-oauth-server) or accept tokens of Auth0, Keycloak, Microsoft Entra ID or WorkOS, with scopes per tool. bin/cake mcp checkfinds what keeps remote clients from connecting, without network calls.- Per-tool authorization, including
config/permissions.phprules throughcakedc/auth. McpTestTraitto test your tools, and who may call them, from a regular Cake test case.- An audit log of MCP calls (
Mcp.audit, off by default): who called which tool, with which arguments, and how it ended, to a log scope or your own table.
- PHP 8.2+
- CakePHP 5
composer require cakedc/cakephp-mcpIt installs mcp/sdk ^0.9, the official PHP SDK. Version 1.0.0 on Packagist
was this same code tagged by mistake: require ^2.0.
Load the plugin in src/Application.php:
$this->addPlugin('CakeDC/Mcp');Let MCP requests through CSRF protection (in Application::middleware()):
$csrf = new CsrfProtectionMiddleware();
$csrf->skipCheckCallback(fn (ServerRequest $request): bool => $request->getPath() === '/mcp');Create src/Mcp/ArticleTools.php:
namespace App\Mcp;
use Cake\ORM\Locator\LocatorAwareTrait;
use Mcp\Capability\Attribute\McpTool;
class ArticleTools
{
use LocatorAwareTrait;
/**
* Lists the titles of the latest articles.
*
* @return list<string>
*/
#[McpTool(name: 'latest_articles')]
public function latest(int $limit = 5): array
{
return $this->fetchTable('Articles')->find()
->select(['title'])
->orderByDesc('created')
->limit($limit)
->all()
->extract('title')
->toList();
}
}By default /mcp requires an identity. Add an authenticator that reads a Bearer
token, for example with cakephp/authentication:
$service->loadAuthenticator('Authentication.Token', [
'header' => 'Authorization',
'tokenPrefix' => 'Bearer',
'identifier' => ['Authentication.Token' => ['tokenField' => 'api_token']],
]);- Claude Code:
claude mcp add --transport http my-app https://example.com/mcp --header "Authorization: Bearer <token>" - claude.ai: Customize > Connectors > Add custom connector, with the URL and (where available) a request header. To have people sign in instead, see OAuth.
- Installation
- Configuration
- Writing tools
- Authentication
- OAuth
- Authorization
- Testing your tools
- stdio
- Connecting clients
- Troubleshooting
- Protocol conformance
MIT. Copyright (c) Cake Development Corporation.