konradmichalik / typo3-routing-mcp
Expose annotated typo3-routing frontend routes as MCP tools via a Streamable HTTP endpoint.
Package info
github.com/konradmichalik/typo3-routing-mcp
Type:typo3-cms-extension
pkg:composer/konradmichalik/typo3-routing-mcp
Requires
- php: ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0
- konradmichalik/typo3-routing: ^1.5.0
- mcp/sdk: ^0.9
- psr/http-message: ^1.0 || ^2.0
- psr/http-server-handler: ^1.0.2
- psr/http-server-middleware: ^1.0.2
- symfony/console: ^7.0 || ^8.0
- symfony/dependency-injection: ^7.1 || ^8.0
- symfony/polyfill-php83: ^1.43.0
- typo3/cms-core: ^13.4 || ^14.0
- typo3/cms-extbase: ^13.4 || ^14.0
Requires (Dev)
- eliashaeussler/version-bumper: ^4.1.2
- konradmichalik/ttt: ^0.6.0
- phpunit/phpunit: ^11.0 || ^12.0 || ^13.0
- typo3/cms-base-distribution: ^13.4 || ^14.0
- typo3/cms-lowlevel: ^13.4 || ^14.0
- typo3/testing-framework: ^8.2 || ^9.0 || ^10.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-10 22:55:43 UTC
README
TYPO3 extension typo3_routing_mcp
This extension exposes typo3-routing frontend routes as MCP tools over a Streamable HTTP endpoint. Add the #[McpTool] PHP attribute to a routed controller method, and an AI agent can invoke your project's own domain endpoints directly, not just generic TYPO3 content operations.
Note
Generic TYPO3 MCP servers can only talk about content: pages, records, files. Your project's own domain endpoints, course search, store locator, whatever your sitepackage needs, are invisible to them. This extension closes that gap: it turns routes you've already declared via typo3-routing into MCP tools an agent can call directly.
✨ Features
- One attribute, one tool: add
#[McpTool]next to an existing#[Route], flush caches, done - Streamable HTTP: a single endpoint (
/_mcpby default, configurable), bearer-token gated, usable on staging and production - Security by construction: session-scoped authenticators (
FrontendUserAuthenticator/BackendUserAuthenticator) and request-token-protected routes are never exposed, no matter the attribute
🔥 Installation
Requirements
- TYPO3 >= 13.4
- PHP 8.2+
konradmichalik/typo3-routinginstalled and configured
Composer
composer require konradmichalik/typo3-routing-mcp
TER
Download the zip file from TYPO3 extension repository (TER).
🚀 Quick start
Add #[McpTool] next to an existing #[Route]:
use KonradMichalik\Typo3Routing\Attribute\Route; use KonradMichalik\Typo3RoutingMcp\Attribute\McpTool; #[Route(path: '/api/courses/{id}', name: 'course_show', description: 'Fetch a single course by its numeric ID.')] #[McpTool] public function show(int $id): ResponseInterface { /* … */ }
#[McpTool] takes these optional arguments:
| Argument | Default | Description |
|---|---|---|
name |
route name | Tool name shown to the client. |
description |
route description |
Tool description shown to the client. |
readOnly |
false |
Sets readOnlyHint. A read-only tool carries no destructiveHint or idempotentHint. |
destructive |
derived | Overrides destructiveHint, which is true for DELETE routes and false otherwise. |
idempotent |
derived | Overrides idempotentHint, which is true for GET, PUT and DELETE routes and false otherwise. |
A route with a #[Returns] DTO gets an outputSchema built from it. Every successful call whose body is a JSON object returns it as structuredContent next to the text content. The schema is only set when every declared 2xx response is the same single DTO. Collections get none, because MCP revisions before 2026-07-28 only accept an object schema. Nullable DTO properties also accept null.
Important
The outputSchema is a contract: the MCP SDK validates every successful structuredContent against it and turns a mismatching response into a tool error. So the schema is only emitted when every public property of the DTO, including nested DTOs, has a precisely mappable type: int, float, bool, string, mixed, a backed enum, or another DTO that qualifies itself. The tool gets no outputSchema (but still returns structuredContent) when a property is untyped, a union or intersection type, array, iterable or object, an Extbase domain object, or a class without public properties such as DateTimeImmutable, or when the DTO or a nested one implements JsonSerializable.
Then audit what's exposed, via the TYPO3 console:
vendor/bin/typo3 routing:mcp:tools
Set a bearer token and point an MCP client at the endpoint. Any client that speaks Streamable HTTP works the same way, just configure the URL and the Authorization header:
export ROUTING_MCP_BEARER_TOKEN=<a-long-random-secret>
claude mcp add --transport http my-project \
https://your-project.example.org/_mcp \
--header "Authorization: Bearer <a-long-random-secret>"
Or, for clients configured via an mcp.json-style file (Cursor, VS Code Copilot, and others):
{
"servers": {
"my-project": {
"type": "http",
"url": "https://your-project.example.org/_mcp",
"headers": {
"Authorization": "Bearer <a-long-random-secret>"
}
}
}
}
Note
Without ROUTING_MCP_BEARER_TOKEN (or the env var name configured via the extension's bearerTokenEnvName setting) set, the endpoint is entirely inactive, not merely unauthenticated.
The initialize response also carries an instructions field describing how to use the exposed tools (read-only vs. mutating, RFC 9457 error content). Most MCP clients surface this to the connecting agent automatically.
Configuration
Set these via the extension's TYPO3 Extension Configuration (Admin Tools > Settings > Extension Configuration > typo3_routing_mcp):
| Setting | Default | Description |
|---|---|---|
bearerTokenEnvName |
ROUTING_MCP_BEARER_TOKEN |
Name of the process environment variable holding the expected bearer token. |
endpointPath |
/_mcp |
Request path the middleware listens on. Change this if /_mcp collides with something else in your project. |
🧑💻 Contributing
Please have a look at CONTRIBUTING.md.
⭐ License
This project is licensed under GNU General Public License 2.0 (or later).