nobrainer / silverstripe-mcp
MCP (Model Context Protocol) server for SilverStripe CMS - lets AI assistants interact with the CMS over Streamable HTTP
Package info
bitbucket.org/nobrainerweb/silverstripe-mcp
Type:silverstripe-vendormodule
pkg:composer/nobrainer/silverstripe-mcp
Requires
- php: ^8.1
- silverstripe/cms: ^6.0
- silverstripe/framework: ^6.0
- silverstripe/versioned: ^3.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
An MCP (Model Context Protocol) server for SilverStripe CMS 6. Lets AI assistants like Claude read and manage pages, query the ORM, inspect the database schema and run dev/build — all gated by SilverStripe's normal permission system.
The server implements the MCP Streamable HTTP transport: clients POST JSON-RPC
messages to /mcp and receive JSON responses. The server is stateless, which fits
PHP's request/response model. (The older, deprecated HTTP+SSE transport requires a
persistent server-push channel and is not supported under PHP-FPM.)
Installation
composer require nobrainer/silverstripe-mcp
vendor/bin/sake db:build --flush
dev/build creates an MCP Access group holding the MCP_ACCESS permission, and
the three OAuth database tables (MCPOAuthClient, MCPOAuthCode, MCPOAuthToken).
Configuration
Create (or pick) a member that the AI should act as, add it to the MCP Access
group, and give it whatever CMS permissions it should have (e.g. content author).
Every operation respects that member's canView() / canEdit() / canPublish() /
canDelete().
Connecting a client
Claude Desktop (OAuth — recommended)
Claude Desktop supports OAuth autodiscovery. Add the server URL in the Claude Desktop settings and it will handle authentication automatically:
- Open Claude Desktop → Settings → MCP Servers → Add Server
- Enter the URL:
https://your-site.com/mcp - Claude Desktop discovers OAuth endpoints via
/.well-known/oauth-authorization-server - Your browser opens and you log in with your SilverStripe credentials
- Claude Desktop receives a Bearer token (valid 90 days) and connects
The member you log in as must be in the MCP Access group.
OAuth token lifetime is configurable (default 90 days):
Nobrainer\MCP\OAuth\OAuthController:
token_lifetime: 7776000 # seconds
Claude Code (API key)
claude mcp add --transport http silverstripe https://your-site.com/mcp \
--header "Authorization: Bearer your-api-key"
API key via environment
Set in .env on the server:
MCP_API_KEY="some-long-random-string"
MCP_MEMBER_EMAIL="mcp@example.com"
API key via YAML (multiple keys / members)
Nobrainer\MCP\Auth\MCPAuthenticator:
api_keys:
'some-long-random-string': 'mcp@example.com'
'another-key': 'editor@example.com'
HTTP Basic auth
Pass a member's email and password as HTTP Basic credentials. Disable if not needed:
Nobrainer\MCP\Auth\MCPAuthenticator:
allow_basic_auth: false
OAuth endpoints
| URL | Purpose |
|---|---|
/.well-known/oauth-authorization-server | Autodiscovery metadata (RFC 8414) |
POST /mcp/oauth/register | Dynamic client registration (RFC 7591) |
GET /mcp/oauth/authorize | Authorization endpoint — redirects to SS login |
POST /mcp/oauth/token | Token endpoint — authorization code → Bearer token |
These are consumed by MCP clients automatically; you do not call them directly.
Tools
| Tool | Description |
|---|---|
page_list | List pages (draft or live) with publication status |
page_get | All fields of a page, including Content |
page_create | Create a page as draft |
page_update | Update draft fields |
page_publish / page_unpublish | Publication workflow |
page_delete | Archive a page (draft + live) |
dataobject_classes | List available DataObject classes |
dataobject_query | Filter/sort/paginate any DataObject list |
dataobject_get / dataobject_create / dataobject_update / dataobject_delete | Generic ORM CRUD |
dataobject_publish | Publish/unpublish versioned DataObjects |
dev_build | Run dev/build (requires ADMIN or CAN_DEV_BUILD) |
flush | Flush manifests and caches (requires ADMIN) |
Resources
| URI | Content |
|---|---|
silverstripe://schema | Overview of all DataObject classes |
silverstripe://schema/{class} | Fields and relations for one class (use . instead of \ in the FQCN) |
Security notes
- Every request must authenticate and the member must hold the
MCP_ACCESSpermission. - All reads and writes go through the ORM and respect record-level
can*()checks; drafts and live stages are handled explicitly (writes always target draft). - Sensitive member fields (password hashes, login tokens, …) are stripped from output and rejected on write — see
RecordSerializer.blocked_fields. - Password/session storage classes are hidden entirely — see
DataObjectTool.excluded_classes. - OAuth authorization codes are single-use and expire after 5 minutes.
- OAuth Bearer tokens are stored as SHA-256 hashes in the database.
- Browser-origin requests are rejected unless the origin is whitelisted in
MCPController.allowed_origins(DNS-rebinding protection). - Always serve the endpoint over HTTPS.
License
BSD-3-Clause