alengo/sulu-mcp-server-bundle
Read-only Sulu bundle exposing local template XML via authenticated admin API endpoints for MCP servers. Lists templates by type and returns raw XML. Secured by Sulu admin session plus required Bearer token; disabled if token is empty.
Install the Bundle
composer require alengo/sulu-mcp-server-bundle
Add to config/bundles.php:
Alengo\SuluMcpServerBundle\McpServerBundle::class => ['all' => true],
Configure Routing
Create config/routes/alengo_mcp_server.yaml:
alengo_mcp_server:
resource: "@McpServerBundle/Resources/config/routing_admin_api.yaml"
prefix: /admin/api
Set Bearer Token
Generate a token (e.g., openssl rand -hex 32) and add to .env.local:
MCP_SERVER_TOKEN=your_generated_token_here
Test the API
/admin/login).curl to fetch templates:
curl -X GET http://your-sulu-app/admin/api/mcp/templates/page \
-H "Authorization: Bearer your_generated_token_here" \
-b "SULU_SESSION=your_admin_session_cookie"
Expose Templates Fetch all page templates:
const response = await fetch('http://sulu-admin/admin/api/mcp/templates/page', {
headers: {
'Authorization': 'Bearer your_token',
'Cookie': 'SULU_SESSION=your_cookie'
}
});
const templates = await response.json(); // ["homepage.xml", "blog.xml"]
Fetch a Specific Template
const templateResponse = await fetch('http://sulu-admin/admin/api/mcp/templates/page/homepage.xml', {
headers: {
'Authorization': 'Bearer your_token',
'Cookie': 'SULU_SESSION=your_cookie'
}
});
const xml = await templateResponse.text();
const parser = new DOMParser();
const doc = parser.parseFromString(xml, "text/xml");
// Process XML (e.g., extract content blocks for React components).
Cache Templates Locally Store fetched templates in a React context or Redux store for offline use.
async function pollTemplates() {
const lastUpdated = localStorage.getItem('templateLastUpdated');
const response = await fetch('http://sulu-admin/admin/api/mcp/templates/page', {
headers: { /* auth headers */ }
});
const templates = await response.json();
if (JSON.stringify(templates) !== lastUpdated) {
localStorage.setItem('templateLastUpdated', JSON.stringify(templates));
// Trigger UI update or refetch all templates.
}
setTimeout(pollTemplates, 5000); // Poll every 5s.
}
Last-Modified header to responses (requires custom controller extension):
# config/packages/alengo_mcp_server.yaml
alengo_mcp_server:
template_dirs:
page: config/templates/pages
cache_headers: true # Hypothetical config (not natively supported).
getStaticProps:
export async function getStaticProps() {
const templates = await Promise.all([
fetchTemplate('page', 'homepage'),
fetchTemplate('block', 'hero')
]);
return { props: { templates } };
}
async function fetchTemplate(type, name) {
const res = await fetch(`http://sulu-admin/admin/api/mcp/templates/${type}/${name}`, {
headers: { /* auth headers */ }
});
return res.text();
}
# Python (FastAPI) example
@app.get("/mobile/templates/{type}/{name}")
async def get_template(type: str, name: str):
sulu_res = requests.get(
f"http://sulu-admin/admin/api/mcp/templates/{type}/{name}",
headers={"Authorization": f"Bearer {os.getenv('MCP_TOKEN')}"},
cookies={"SULU_SESSION": os.getenv("SULU_SESSION_COOKIE")}
)
return {"template": sulu_res.text()}
// Flutter example
Future<String> fetchTemplate(String type, String name) async {
final response = await http.get(
Uri.parse('http://your-backend/mobile/templates/$type/$name'),
headers: {"Authorization": "Bearer mobile_app_token"},
);
return response.body;
}
MCP_SERVER_TOKEN in .env.local and clear Symfony cache:
php bin/console cache:clear
#!/bin/bash
NEW_TOKEN=$(openssl rand -hex 32)
sed -i "s/MCP_SERVER_TOKEN=.*/MCP_SERVER_TOKEN=$NEW_TOKEN/" .env.local
php bin/console cache:clear
httpOnly cookies or secure storage).# config/packages/alengo_mcp_server.yaml
alengo_mcp_server:
template_dirs:
page: custom/path/to/pages
article: vendor/sulu-bundle/config/templates/articles
alengo_mcp_server:
template_dirs:
custom_block: app/Resources/templates/blocks
Now accessible at /admin/api/mcp/templates/custom_block.// src/Controller/McpTemplateController.php
namespace App\Controller;
use Alengo\SuluMcpServerBundle\Controller\McpTemplateController as BaseController;
use Symfony\Component\HttpFoundation\Response;
class McpTemplateController extends BaseController
{
public function getTemplate(string $type, string $name): Response
{
$xml = parent::getTemplate($type, $name)->getContent();
$json = json_encode($this->xmlToArray($xml));
return new Response($json, 200, ['Content-Type' => 'application/json']);
}
private function xmlToArray(string $xml): array
{
// Implement XML-to-array conversion.
}
}
Update routing to point to your controller.Missing Authentication Headers
401 Unauthorized or 403 Forbidden.Authorization: Bearer <token> header is included.curl -v http://sulu-admin/admin/api/mcp/templates/page \
-H "Authorization: Bearer $TOKEN" \
-b "SULU_SESSION=..."
Check the -v output for missing headers.Incorrect Template Paths
404 Not Found for valid template names.template_dirs in config/packages/alengo_mcp_server.yaml matches your Sulu setup.find config/templates -type f | grep "homepage.xml"
Token Not Configured
403 Forbidden even with valid session.MCP_SERVER_TOKEN is set in .env.local and not empty.XML Parsing Issues
?format=json query parameter (requires custom controller extension):
// In your custom controller:
public function getTemplate(string $type, string $
How can I help you explore Laravel packages today?