Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
a03bf40
Rebuild the 2.x package skeleton: metadata, MIT license and test harness
ajibarra Oct 15, 2026
395a9a3
Add McpConfig defaults, McpLogger and default cache configs
ajibarra Oct 15, 2026
3fa1d36
Add an autowiring PSR-11 bridge over Cake's container
ajibarra Oct 15, 2026
49b5cda
Add CurrentIdentity and the identity-to-AccessToken middleware
ajibarra Oct 15, 2026
e5d85e2
Enforce per-tool authorization: call guard, list filter and PSR-14 di…
ajibarra Oct 15, 2026
41c7236
Add RbacToolAuthorizer on top of cakedc/auth Rbac
ajibarra Oct 15, 2026
0a6d827
Add ServerFactory: discovery, cache-backed sessions, container bridge…
ajibarra Oct 15, 2026
c7c731e
Serve MCP over Streamable HTTP at a Cake route
ajibarra Oct 15, 2026
978dea0
Assert the failing tool was reached in the leak test
ajibarra Oct 15, 2026
4f5d580
Add the mcp server stdio command
ajibarra Oct 15, 2026
52740fe
Add CI workflow, README, docs and changelog for 2.0.0-beta1
ajibarra Oct 15, 2026
70d46cc
Document the temporary mcp/sdk pin for host installs
ajibarra Oct 15, 2026
8c8d743
Keep info logs off STDOUT in the stdio server command
ajibarra Oct 15, 2026
bfbb11a
Add a front controller for manual testing of the test app
ajibarra Oct 15, 2026
557a7d8
Do not call a parent constructor Cake 5.0 Command lacks
ajibarra Oct 15, 2026
9ab9fa9
Log exception details and unused context from the SDK
ajibarra Oct 15, 2026
8836b17
Keep authentication on unless identity.required is explicitly false
ajibarra Oct 15, 2026
115913d
Fix documentation inaccuracies from the final review
ajibarra Oct 15, 2026
208ef33
Document tool annotations and check they reach tools/list
ajibarra Oct 15, 2026
1a13fdc
Add ToolError::invalid() to send Cake validation errors to the model
ajibarra Oct 15, 2026
0ce6b2c
Add McpTestTrait to test an application's tools in memory
ajibarra Oct 15, 2026
e9d9741
Add Mcp.server.configure to customize the SDK builder
ajibarra Oct 15, 2026
bce6eb6
Run the official MCP conformance suite against the plugin endpoint
ajibarra Oct 15, 2026
5f7214d
Add bin/cake mcp inspector to open the MCP Inspector on the server
ajibarra Oct 15, 2026
e9d4948
McpTestTrait: load routes, show the server log on failure, check tool…
ajibarra Oct 15, 2026
cc009a9
Keep field names of flat error arrays, add server title, description …
ajibarra Oct 15, 2026
b6b58cd
Inspector command: delete the config on Ctrl+C, support Windows, pass…
ajibarra Oct 15, 2026
d46e3eb
Conformance script: wait for php-fpm, clean files it created, match t…
ajibarra Oct 15, 2026
476d30a
Inspector command: poll the process so SIGTERM is passed on, warn wit…
ajibarra Oct 15, 2026
981124d
Run the test suite with the oauth plugin and a SQLite database
ajibarra Oct 16, 2026
aa060eb
Add the Mcp.auth settings
ajibarra Oct 16, 2026
57fb75d
Reject non-array and null Mcp.auth values
ajibarra Oct 16, 2026
86391d0
Validate MCP access tokens with the oauth plugin of the same app
ajibarra Oct 16, 2026
0231683
Protect the MCP endpoint with OAuth tokens and serve its resource met…
ajibarra Oct 16, 2026
f90810e
Accept JWT access tokens from an external authorization server
ajibarra Oct 16, 2026
ed4efd0
Keep token claims out of the top level of the external identity
ajibarra Oct 16, 2026
f0cb972
Let McpTestTrait act with scopes and check insufficient_scope
ajibarra Oct 16, 2026
852031d
Add bin/cake mcp check
ajibarra Oct 16, 2026
09117ab
mcp check: report external audiences and verify the metadata route
ajibarra Oct 16, 2026
e9108bf
mcp check: test the unconnected metadata route, reset test state
ajibarra Oct 16, 2026
0bcdfc3
Test the whole OAuth flow of a remote MCP client
ajibarra Oct 16, 2026
f595602
Document OAuth for the MCP endpoint
ajibarra Oct 16, 2026
7fdb9c5
Fix the external identity example in the OAuth docs
ajibarra Oct 16, 2026
eb233aa
Tidy the external identity example in the OAuth docs
ajibarra Oct 16, 2026
fa94299
Fix the OAuth review findings in code and tests
ajibarra Oct 16, 2026
306e015
Update the OAuth docs, changelog and CI for the review findings
ajibarra Oct 16, 2026
d880037
Require mcp/sdk ^0.9 now that 0.9.0 is released
ajibarra Oct 19, 2026
df31f53
Bound the request body with Mcp.http.maxBodyBytes and answer 413 befo…
ajibarra Oct 19, 2026
3adce38
Run the newest alpha of the conformance suite
ajibarra Oct 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Kept out of the archives Composer installs.
/.github export-ignore
/tests export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/phpcs.xml export-ignore
/phpstan.neon export-ignore
/phpunit.xml.dist export-ignore
120 changes: 120 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
name: CI

on:
push:
branches: ['2.next-cake5', 'master']
pull_request:

permissions:
contents: read

# The OAuth tests resolve cakedc/oauth-server and cakedc/cakephp-oauth-server from path repositories
# (../oauth-server and ../cakephp-oauth-server), so every job checks them out next to this one, at a
# released tag so CI does not drift with their branches. Both repositories are public, so the workflow
# token is enough; a CAKEDC_PRIVATE_REPOS_TOKEN secret is used instead when the organisation defines one.
env:
OAUTH_SERVER_REF: '1.0.0-beta1'
OAUTH_PLUGIN_REF: '1.0.0-beta1'

jobs:
testsuite:
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
php-version: ['8.2', '8.3', '8.4', '8.5']
dependencies: ['highest']
include:
- php-version: '8.2'
dependencies: 'lowest'
name: PHP ${{ matrix.php-version }} (${{ matrix.dependencies }})
steps:
- uses: actions/checkout@v4
with:
path: cakephp-mcp
- uses: actions/checkout@v4
with:
repository: CakeDC/oauth-server
ref: ${{ env.OAUTH_SERVER_REF }}
path: oauth-server
token: ${{ secrets.CAKEDC_PRIVATE_REPOS_TOKEN || github.token }}
- uses: actions/checkout@v4
with:
repository: CakeDC/cakephp-oauth-server
ref: ${{ env.OAUTH_PLUGIN_REF }}
path: cakephp-oauth-server
token: ${{ secrets.CAKEDC_PRIVATE_REPOS_TOKEN || github.token }}
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: mbstring, intl, pdo_sqlite, openssl
coverage: none
- uses: ramsey/composer-install@v3
with:
dependency-versions: ${{ matrix.dependencies }}
working-directory: cakephp-mcp
- run: vendor/bin/phpunit
working-directory: cakephp-mcp

cs-stan:
runs-on: ubuntu-24.04
name: Coding standard and static analysis
steps:
- uses: actions/checkout@v4
with:
path: cakephp-mcp
- uses: actions/checkout@v4
with:
repository: CakeDC/oauth-server
ref: ${{ env.OAUTH_SERVER_REF }}
path: oauth-server
token: ${{ secrets.CAKEDC_PRIVATE_REPOS_TOKEN || github.token }}
- uses: actions/checkout@v4
with:
repository: CakeDC/cakephp-oauth-server
ref: ${{ env.OAUTH_PLUGIN_REF }}
path: cakephp-oauth-server
token: ${{ secrets.CAKEDC_PRIVATE_REPOS_TOKEN || github.token }}
- uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
coverage: none
- uses: ramsey/composer-install@v3
with:
working-directory: cakephp-mcp
- run: vendor/bin/phpcs
working-directory: cakephp-mcp
- run: vendor/bin/phpstan analyse --no-progress
working-directory: cakephp-mcp

conformance:
runs-on: ubuntu-24.04
name: MCP conformance
steps:
- uses: actions/checkout@v4
with:
path: cakephp-mcp
- uses: actions/checkout@v4
with:
repository: CakeDC/oauth-server
ref: ${{ env.OAUTH_SERVER_REF }}
path: oauth-server
token: ${{ secrets.CAKEDC_PRIVATE_REPOS_TOKEN || github.token }}
- uses: actions/checkout@v4
with:
repository: CakeDC/cakephp-oauth-server
ref: ${{ env.OAUTH_PLUGIN_REF }}
path: cakephp-oauth-server
token: ${{ secrets.CAKEDC_PRIVATE_REPOS_TOKEN || github.token }}
- uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
coverage: none
- uses: ramsey/composer-install@v3
with:
working-directory: cakephp-mcp
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: tests/Conformance/run.sh
working-directory: cakephp-mcp
10 changes: 7 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.idea
vendor
composer.lock
/vendor/
/composer.lock
/.phpunit.cache/
/tests/test_app/tmp/
.phpunit.result.cache
/tests/Conformance/tmp/
/tests/Conformance/results/
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Changelog

## 2.1.0-beta1 (unreleased)

OAuth 2.1 for remote MCP clients. Existing applications keep working: `host` mode behaves as 2.0.

- `Mcp.auth` with three modes: `host` (default, as 2.0), `oauth` (access tokens of `cakedc/cakephp-oauth-server` in the same application) and `external` (JWT access tokens of Auth0, Keycloak, Microsoft Entra ID, WorkOS or any OpenID Connect issuer, through `firebase/php-jwt` and `psr/cache`). The `Mcp.auth` section is validated strictly and fails closed.
- Protected resource metadata (RFC 9728) at `/.well-known/oauth-protected-resource` plus the MCP route, and the `401` challenge that points MCP clients to it.
- Scope policy per method and per tool (`Mcp.auth.scopes`), with `implies` and step-up through `403 insufficient_scope`.
- `McpConfig::isEndpoint()` recognises the MCP route and its metadata path, for the CSRF skip callback and to keep other bearer-token authenticators off them.
- `bin/cake mcp check` inspects the endpoint configuration (authentication, resource, metadata route, allowed hosts, CSRF, authenticators, including the `Auth.Authenticators` of `cakedc/users`) without network calls.
- `McpTestTrait::mcpActingAs()` takes scopes and claims, and `assertMcpInsufficientScope()` asserts a refused call. In `oauth` mode the scopes are expanded with the `implies` of the declared resource, as the authorization server does.
- `McpConfig::rbacPermissions()` returns the `bypassAuth` rule `cakedc/users` hosts need for the endpoint and its metadata.
- oauth mode needs `cakedc/cakephp-oauth-server` and `cakedc/oauth-server` 1.0 (beta, public, not on Packagist yet: see the install notes), CakePHP 5.1.10+ and `cakephp/authentication` 3.3.6+.
- Contributors: the PHPUnit suite now runs on SQLite with the oauth packages. It needs the sibling clones `../oauth-server` and `../cakephp-oauth-server` (path repositories); both are public repositories, and CI checks them out at their `1.0.0-beta1` tags.
- `Mcp.http.maxBodyBytes` (default 4 MiB, the SDK's own limit) bounds the request body. The same value is passed to the SDK transport and to its `AuthorizationMiddleware`.
- A POST over that limit is answered with `413` and a JSON-RPC error (code `-32600`) whose message and `data.maxBodyBytes` name the limit and suggest a smaller payload, before authentication. Before, `oauth` and `external` modes answered it with a `400` Bearer `invalid_request` challenge.

- Contributors: `tests/Conformance/run.sh` (and so CI) runs the newest published alpha of the official conformance suite instead of a pinned version; `CONFORMANCE_PACKAGE` still pins one for a reproducible run.

## 2.0.0-beta1 (unreleased)

Complete rewrite for CakePHP 5 on top of the official `mcp/sdk`.

- Remote MCP over Streamable HTTP at a Cake route (`/mcp` by default), serving both protocol eras (handshake with sessions, and the stateless 2026-07-28 revision).
- `bin/cake mcp server` for stdio.
- `bin/cake mcp inspector` opens the MCP Inspector already configured for this server, over stdio or HTTP.
- Tool discovery through `#[McpTool]`, `#[McpResource]` and `#[McpPrompt]` attributes in `src/Mcp/`, with constructor injection from Cake's container (autowired).
- Sessions and the discovery cache stored in Cake cache configs.
- Authentication delegated to the host: the identity set by `cakephp/authentication` becomes the SDK access token and the injectable `CurrentIdentity`.
- Per-tool authorization with `ToolAuthorizerInterface`; `RbacToolAuthorizer` for `cakedc/auth` permission rules.
- `server.title`, `server.description` and `server.websiteUrl` config keys, reported in the server info.
- `Mcp.server.configure` hands the SDK builder to the host for anything the plugin does not configure itself; the plugin's own settings and the authorizer still apply.
- `McpTestTrait` calls the application's tools in memory from a Cake test case, acting as any identity, with assertions for results, tool errors, JSON-RPC errors, denials and `tools/list`.
- `ToolError::invalid()` turns Cake validation errors (an entity or a `Validator` result) into a tool error the model can read and act on.
- Checked against the official MCP conformance suite for both protocol revisions, behind nginx and php-fpm (`tests/Conformance/run.sh`, also in CI).
- DNS-rebinding protection defaults to the `App.fullBaseUrl` host. That only protects when `App.fullBaseUrl` is set explicitly (the Cake 5 skeleton derives it from the request `Host` otherwise); set `Mcp.http.allowedHosts`, which is required for servers with `identity.required` set to `false`.
- Removed `bin/mcp-server` and `CakeDC\Mcp\Server\CakeServer`.
- Requires `mcp/sdk` ^0.9.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2025-2026 Cake Development Corporation (https://www.cakedc.com)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
118 changes: 111 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,117 @@
# MCP Server for CakePHP
# CakePHP MCP Server

## Installation
* composer require cakedc/cakephp-mcp
Expose tools, resources and prompts from a CakePHP 5 application to Claude and
any other [Model Context Protocol](https://modelcontextprotocol.io) client.
Built on the official [`mcp/sdk`](https://github.com/modelcontextprotocol/php-sdk).

## Usage
* Select one or many methods in your CakePHP project. Add the attribute to expose the method to the MCP server.
- Remote MCP over Streamable HTTP, mounted as a regular Cake route (`/mcp`).
- `bin/cake mcp server` for local stdio clients, and `bin/cake mcp inspector` to 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 check` finds what keeps remote clients from connecting, without network calls.
- Per-tool authorization, including `config/permissions.php` rules through `cakedc/auth`.
- `McpTestTrait` to test your tools, and who may call them, from a regular Cake test case.

## Requirements

- PHP 8.2+
- CakePHP 5

## Install

```bash
composer require cakedc/cakephp-mcp
```
example here

> **Before the first 2.x tag.** 2.x is not tagged yet, so require the
> development branch:
>
> ```bash
> composer require cakedc/cakephp-mcp:dev-2.next-cake5
> ```
>
> It needs `mcp/sdk` ^0.9, a stable release, so no dev stability settings are
> needed for the SDK.

Load the plugin in `src/Application.php`:

```php
$this->addPlugin('CakeDC/Mcp');
```

## Test your MCP server works properly
Let MCP requests through CSRF protection (in `Application::middleware()`):

```php
$csrf = new CsrfProtectionMiddleware();
$csrf->skipCheckCallback(fn (ServerRequest $request): bool => $request->getPath() === '/mcp');
```

## Your first tool

Create `src/Mcp/ArticleTools.php`:

```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();
}
}
```

## Authenticate callers

By default `/mcp` requires an identity. Add an authenticator that reads a Bearer
token, for example with `cakephp/authentication`:

```php
$service->loadAuthenticator('Authentication.Token', [
'header' => 'Authorization',
'tokenPrefix' => 'Bearer',
'identifier' => ['Authentication.Token' => ['tokenField' => 'api_token']],
]);
```

## Connect Claude

- Claude Code: `claude mcp add --transport http my-app https://example.com/mcp --header "Authorization: Bearer <token>"`
- claude.ai: Settings, Connectors, Add custom connector, with the URL and (where available) a request header. To have people sign in instead, see [OAuth](docs/oauth.md).

## Documentation

- [Installation](docs/installation.md)
- [Configuration](docs/configuration.md)
- [Writing tools](docs/tools.md)
- [Authentication](docs/authentication.md)
- [OAuth](docs/oauth.md)
- [Authorization](docs/authorization.md)
- [Testing your tools](docs/testing.md)
- [stdio](docs/stdio.md)
- [Connecting clients](docs/clients.md)
- [Troubleshooting](docs/troubleshooting.md)
- [Protocol conformance](docs/conformance.md)

## License

MIT. Copyright (c) Cake Development Corporation.
24 changes: 0 additions & 24 deletions bin/mcp-server

This file was deleted.

Loading
Loading