| description | TODO. |
|---|---|
| month_change | true |
TODO: built-in MCP servers VS custom MCP servers
MCP servers use JWT for authentication.
TODO: Enable authorization header in config/packages/lexik_jwt_authentication.yaml.
In config/packages/security.yaml, uncomment the ibexa_jwt_mcp firewall.
TODO: Config to get a JWT token in the first place. Through REST, GraphQL or something else?
MCP servers are configured per repository then enabled per SiteAccess scope.
ibexa:
repositories:
<repository_identifier>:
mcp:
<server_identifier>:
path: <server_route_path>
enabled: true
# Server options…
discovery_cache: <cache_pool_service>
session:
type: <psr16|file|memory>
# Session options…
system:
<siteaccess_scope>:
mcp:
servers:
- <server_identifier>TODO: ddev php bin/console debug:router --siteaccess=<within_scope_siteaccess> should list some ibexa.mcp.<server_identifier> GET|POST|DELETE|OPTIONS <server_route_path>
TODO: Maybe explain that routes are built automatically from MCP server path configs thank to config/routes/ibexa_mcp.yaml and \Ibexa\Bundle\Mcp\Routing\McpRouteLoader
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | Yes | MCP server endpoint path | |
enabled |
boolean | No | false |
Whether the server is enabled |
version |
string | No | 1.0.0 |
MCP server version |
description |
string | No | null |
Human-readable server description |
instructions |
string | No | null |
Instructions dedicated for LLM interaction |
discovery_cache |
string | Yes | PSR-6 ou PSR-16 cache pool service identifier | |
session |
object | Yes | Session storage configuration |
Notice that a server is disabled by default, it needs to be explicitly enabled.
TODO
| Option | Type | Default | Description |
|---|---|---|---|
type |
enum | memory |
Session store type: psr16, file, or memory |
service |
string | null |
PSR-16 cache service ID for psr16 session store |
prefix |
string | mcp_ |
Key prefix for psr16 session store |
directory |
string | null |
Directory path for file session store |
ttl |
integer | 3600 |
Session TTL in seconds |
Sessions are stored using a PSR-16 compatible cache implementation. Requires service option pointing to a valid cache service ID.
session:
type: psr16
service: cache.redis.mcp
prefix: 'mcp_<server_identifier>_'
services:
cache.redis.mcp:
public: true
class: Symfony\Component\Cache\Adapter\RedisTagAwareAdapter
parent: cache.adapter.redis
tags:
- name: cache.pool
clearer: cache.app_clearer
provider: 'redis://mcp.redis:6379'
namespace: 'mcp'Sessions are persisted to the filesystem. Requires directory option to be set.
session:
type: file
directory: '%kernel.cache_dir%/mcp/sessions'Sessions are stored in memory. Suitable for development and STDIO transport.
TODO: Might not work with DDEV or Docker
session:
type: memoryTODO: Ibexa\Contracts\Mcp\McpCapabilityInterface
TODO: Ibexa\Contracts\Mcp\Attribute namespace
This example introduce an example MCP server with a single greet tool.
It's enabled on all SiteAccesses.
It's accessible with the path /mcp/example (for example, on http://localhost/mcp/example and http://localhost/admin/mcp/example).
It uses files for both discovery cache and session storage.
In a new config/packages/mcp.yaml file, the configuration of the MCP server:
[[= include_file('code_samples/mcp/config/packages/mcp.yaml') =]]Then, a McpCapabilityInterfacecontaining a greet function with a McpTool attribute associating with the example server:
[[= include_file('code_samples/mcp/src/Mcp/ExampleTools.php') =]]To check the server configuration, a short command using the MCP server configuration registry (injected through McpServerConfigurationRegistryInterface and autowiring):
[[= include_file('code_samples/mcp/src/Command/McpServerListCommand.php') =]]To test the example MCP server, a sequence of curl commands is used to simulate an AI to MCP server communication.
- Ask for a JWT token through REST
- Initialize a connection to the MCP server
- Validate the MCP Session ID
- List the available tools
- Call a tool
jq, grep, and sed are also used to parse or display outputs.
The initialization:
[[= include_file('code_samples/mcp/mcp.sh', 0, 36) =]]HTTP/1.1 202 Accepted
Access-Control-Allow-Headers: Content-Type, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID, Authorization, Accept
Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS
Access-Control-Expose-Headers: Mcp-Session-Id
The list of tools:
[[= include_file('code_samples/mcp/mcp.sh', 37, 45) =]]{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "greet",
"inputSchema": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
]
},
"description": "Greet a user by name"
}
]
}
}The greet tool usage:
[[= include_file('code_samples/mcp/mcp.sh', 46) =]]{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "Hello, World!"
}
],
"isError": false
}
}TODO: Connect an AI client to the MCP server. Copilot CLI MCP server addition is strangely asking for some OAuth ID even with a proper JWT/Bearer header.