MCP Bundle
Symfony integration bundle for Model Context Protocol using the official MCP SDK mcp/sdk.
Supports MCP capabilities (tools, prompts, resources) as server via HTTP transport and STDIO. Resource templates implementation ready but awaiting MCP SDK support.
Installation
1
$ composer require symfony/mcp-bundle
Usage
At first, you need to decide whether your application should act as a MCP server or client. Both can be configured in
the mcp section of your config/packages/mcp.yaml file.
Act as Server
To use your application as an MCP server, exposing tools, prompts, resources, and resource templates to clients like Claude Desktop, you need to configure in the
client_transports section the transports you want to expose to clients. You can use either STDIO or HTTP.
Creating MCP Capabilities
MCP capabilities are automatically discovered using PHP attributes.
Tools
Actions that can be executed:
1 2 3 4 5 6 7 8 9 10
use Mcp\Capability\Attribute\McpTool;
class CurrentTimeTool
{
#[McpTool(name: 'current-time')]
public function getCurrentTime(string $format = 'Y-m-d H:i:s'): string
{
return (new \DateTime('now', new \DateTimeZone('UTC')))->format($format);
}
}
Prompts
System instructions for AI context:
1 2 3 4 5 6 7 8 9 10 11 12
use Mcp\Capability\Attribute\McpPrompt;
class TimePrompts
{
#[McpPrompt(name: 'time-analysis')]
public function getTimeAnalysisPrompt(): array
{
return [
['role' => 'user', 'content' => 'You are a time management expert.']
];
}
}
Resources
Static data that can be read:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
use Mcp\Capability\Attribute\McpResource;
class TimeResource
{
#[McpResource(uri: 'time://current', name: 'current-time')]
public function getCurrentTimeResource(): array
{
return [
'uri' => 'time://current',
'mimeType' => 'text/plain',
'text' => (new \DateTime('now'))->format('Y-m-d H:i:s')
];
}
}
Resource Templates
Dynamic resources with parameters:
Note
Resource Templates are not yet functional as the underlying MCP SDK is missing the required handlers. See MCP SDK issue #9 for implementation status.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
use Mcp\Capability\Attribute\McpResourceTemplate;
class TimeResourceTemplate
{
#[McpResourceTemplate(uriTemplate: 'time://{timezone}', name: 'time-by-timezone')]
public function getTimeByTimezone(string $timezone): array
{
$time = (new \DateTime('now', new \DateTimeZone($timezone)))->format('Y-m-d H:i:s T');
return [
'uri' => "time://$timezone",
'mimeType' => 'text/plain',
'text' => $time
];
}
}
All capabilities are automatically discovered in the src/ directory when the server starts.
Attribute Placement Patterns
The MCP SDK, and therefore the MCP Bundle, supports two patterns for placing attributes on your capabilities:
Invokable Pattern - Attribute on a class with __invoke() method:
1 2 3 4 5 6 7 8
#[McpTool(name: 'my-tool')]
class MyTool
{
public function __invoke(string $param): string
{
// Implementation
}
}
Method-Based Pattern - Multiple attributes on individual methods:
1 2 3 4 5 6 7 8
class MyTools
{
#[McpTool(name: 'tool-one')]
public function toolOne(): string { }
#[McpTool(name: 'tool-two')]
public function toolTwo(): string { }
}
Transport Types
The MCP Bundle supports two transport types for server communication:
- STDIO Transport - For command-line clients (e.g.,
symfony console mcp:server) - HTTP Transport - For web-based clients and MCP Inspector using streamable HTTP connections
The HTTP transport uses the MCP SDK's StreamableHttpTransport which supports:
- JSON-RPC 2.0 over HTTP POST requests
- Session management with configurable storage (file/memory)
- CORS headers for cross-origin requests
- Proper MCP initialization handshake
Act as Client
Warning
Not implemented yet, but planned for the future.
To use your application as an MCP client, integrating other MCP servers, you need to configure the servers you want
to connect to. You can use either STDIO or HTTP as transport methods.
You can find a list of example Servers in the MCP Server List.
Tools of those servers are available in your AI Bundle configuration and usable in your agents.
Configuration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
# config/packages/mcp.yaml
mcp:
app: 'app' # Application name to be exposed to clients
version: '1.0.0' # Application version to be exposed to clients
pagination_limit: 50 # Maximum number of items returned per list request (default: 50)
instructions: | # Instructions describing server purpose and usage context (for LLMs)
This server provides time management capabilities for developers.
Use when working with timestamps, time zones, or time-based calculations.
All timestamps are in UTC unless specified otherwise.
Example contexts: logging, debugging, time-sensitive operations.
client_transports:
stdio: true # Enable STDIO via command
http: true # Enable HTTP transport via controller
# HTTP transport configuration (optional)
http:
path: /_mcp # HTTP endpoint path (default: /_mcp)
session:
store: file # Session store type: 'file' or 'memory' (default: file)
directory: '%kernel.cache_dir%/mcp-sessions' # Directory for file store (default: cache_dir/mcp-sessions)
ttl: 3600 # Session TTL in seconds (default: 3600)
servers:
name:
transport: 'stdio' # Transport method to use, either 'stdio' or 'http'
stdio:
command: 'php /path/bin/console mcp:server' # Command to execute to start the server
arguments: [] # Arguments to pass to the command
http:
url: 'http://localhost:8000/_mcp' # URL to HTTP endpoint of MCP server
Logging Configuration
By default, MCP uses a dedicated logger channel that inherits your application's default logging configuration.
To configure MCP-specific logging, add the following to your config/packages/monolog.yaml:
1 2 3 4 5 6 7 8 9 10
# config/packages/monolog.yaml
monolog:
channels: ['mcp']
handlers:
mcp:
type: rotating_file
path: '%kernel.logs_dir%/mcp.log'
level: info
channels: ['mcp']
max_files: 30
You can customize the logging level and destination according to your needs:
1 2 3 4 5 6 7 8 9 10 11 12 13
# Example: Different levels per environment
monolog:
handlers:
mcp_dev:
type: stream
path: '%kernel.logs_dir%/mcp.log'
level: debug
channels: ['mcp']
mcp_prod:
type: slack
level: error
channels: ['mcp']
webhook_url: '%env(SLACK_WEBHOOK)%'
Event System
The MCP Bundle automatically configures the Symfony EventDispatcher to work with the MCP SDK's event system. This allows you to listen for changes to your server's capabilities.
Available Events
The MCP SDK dispatches the following events when capabilities are registered:
Mcp\Event\ToolListChangedEvent- When a tool is registeredMcp\Event\ResourceListChangedEvent- When a resource is registeredMcp\Event\ResourceTemplateListChangedEvent- When a resource template is registeredMcp\Event\PromptListChangedEvent- When a prompt is registered
Listening to Events
You can create event listeners to respond to capability changes:
1 2 3 4 5 6 7 8 9 10 11 12
use Mcp\Event\ToolListChangedEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
#[AsEventListener]
class McpCapabilityListener
{
public function onToolListChanged(ToolListChangedEvent $event): void
{
// Handle tool registration
// For example: invalidate cache, log changes, notify clients
}
}
The events are simple marker events that notify when lists have changed, but don't contain specific details about what was added or modified.