A fully functional Sulu skeleton CMS with extended features for running Never Code Alone platform.
# Setup
ddev start && ddev exec composer install
ddev create-db && ddev php bin/console sulu:build dev --no-interaction
# Admin access
open https://sulu-never-code-alone.ddev.site/admin
# Create new admin
ddev php bin/console sulu:security:user:create --no-interaction
# Frontend
ddev launch
# Remove Setup
ddev delete --omit-snapshot# Export
ddev export-db >backup.sql.gz
# Import
ddev import-db --file=backup.sql.gzPHP >= 8.2 Mysql >= 8.0 Composer NPM
composer install --no-dev --optimize-autoloader --no-scripts
bin/console cache:clear
npm install
npm run buildbin/console server:run
## or php builtin server
php -S localhost:8000 -t publicHere is a great read on Symfony CMS Sulu: Roland Golla - YouTube Playlist
AI-powered Sulu CMS content management via Claude. MCP allows Claude to read, create, update, and publish content blocks directly in Sulu.
- Bundle: Uses
klapaudius/symfony-mcp-serverfor MCP protocol handling - Tool:
SuluPagesToolexposes page/block operations to Claude - Service:
PageServicehandles PHPCR database operations - OAuth: Self-hosted OAuth 2.1 with PKCE for remote access (Claude Chat)
src/
├── MCP/Tools/SuluPagesTool.php # MCP tool interface
├── Sulu/Service/PageService.php # PHPCR operations
├── Controller/McpOAuthController.php # OAuth endpoints
├── Service/McpOAuthService.php # Token management
└── Entity/
├── McpAuthCode.php # Auth code storage
└── McpAccessToken.php # Access token storage
| Action | Description |
|---|---|
list |
List pages under path prefix |
get |
Get page with blocks |
add_block |
Add content block |
update_block |
Update block content |
move_block |
Reorder blocks |
remove_block |
Delete block |
publish / unpublish |
Control page visibility |
update_resource_locator |
Change the URL slug; the old URL becomes a 301 history route |
move_page |
Move a page under another parent; old URLs become 301 history routes |
route_health / list_history_routes / check_url |
Inspect routes and how the website answers a URL |
list_redirects / create_redirect / update_redirect / delete_redirect |
Manage SuluRedirectBundle redirects (301, 302, 307, 308, 410) |
Page creation, publishing, URL changes and moves run Sulu's DocumentManager in a fresh console process (app:page:create, app:page:publish, app:page:set-url, app:page:move), so Sulu writes the routes in both workspaces. Pages created by the old MCP code need a one-off repair: bin/console nca:routes:repair --dry-run, then without --dry-run, then massive:search:reindex and cache:clear.
.mcp.json in project root:
{
"mcpServers": {
"sulu-ddev": {
"command": "ddev",
"args": ["exec", "php", "bin/console", "mcp:server"]
}
}
}Connect to https://your-domain.com/mcp - OAuth discovery is automatic.
Required env vars:
MCP_PROJECT_PASSWORD=your-secure-password
MCP_PROJECT_NAME="Project Name"Tests use mocked Doctrine\DBAL\Connection with sample PHPCR XML:
# Run MCP-related tests
ddev exec vendor/bin/phpunit tests/Unit/Sulu/Service/PageServiceTest.phpSee tests/Unit/Sulu/Service/PageServiceTest.php for examples of testing block operations.
Execute PHP unit tests:
ddev exec vendor/bin/phpunitClear Symfony cache:
ddev exec bin/console cache:clearWarm up cache:
ddev exec bin/console cache:warmup