Build an MCP Server
The Model Context Protocol (MCP) lets AI assistants like Claude Desktop discover and call tools exposed by your application. In this guide you will install the MCP Bundle, create a tool, a prompt, and a resource, then test them with a client.
Prerequisites
- A Symfony application
- Symfony AI MCP Bundle
Step 1: Install the MCP Bundle
The MCP Bundle integrates the official mcp/sdk PHP package into your Symfony application:
1
composer require symfony/mcp-bundle
Step 2: Configure Routing
Add the MCP route to your routing configuration. This exposes the HTTP endpoint that MCP clients connect to:
1 2 3 4
# config/routes.yaml
mcp:
resource: .
type: mcp
Step 3: Create a Tool
Use the #[McpTool] attribute on a method to expose it as a callable tool. MCP clients see the
tool name and can invoke it with parameters:
1 2 3 4 5 6 7 8 9 10 11 12
namespace App\Mcp;
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);
}
}
Tip
You can place multiple #[McpTool] methods in the same class to group related tools
together.
Step 4: Configure Transport
Declare your server and the transports it offers. The STDIO transport is used by command-line clients;
HTTP is used by web-based clients and the MCP Inspector. A server exposes only what its capability lists
name, so ['*'] hands it everything you annotate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# config/packages/mcp.yaml
mcp:
servers:
default:
name: 'my-app'
version: '1.0.0'
description: 'My Symfony MCP server'
transports:
stdio: true
http: true
http:
path: /_mcp
registry: '*'
Step 5: Create a Prompt
Prompts provide system instructions that AI clients can request. Use the #[McpPrompt]
attribute and return an array of messages:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
namespace App\Mcp;
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.'],
];
}
}
Step 6: Create a Resource
Resources expose static data that AI clients can read. Use the #[McpResource] attribute with
a URI and a name:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
namespace App\Mcp;
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'),
];
}
}
Step 7: Test with an MCP Client
To connect Claude Desktop (or any MCP-compatible client) to your server, add an entry to the client's configuration file pointing at your Symfony console command for STDIO transport:
1 2 3 4 5 6 7
{
"mcpServers": {
"my-app": {
"command": "php /path/to/project/bin/console mcp:server"
}
}
}
For the HTTP transport, point the client at your application's MCP endpoint instead:
1 2 3 4 5 6 7
{
"mcpServers": {
"my-app": {
"url": "http://localhost:8000/_mcp"
}
}
}
Tip
Run symfony console mcp:server to start the STDIO server manually. This is useful for
debugging before connecting a client. With more than one server configured for STDIO, name the
one to run: symfony console mcp:server default.
Learn More
- MCP Bundle - Full configuration, session storage, and event system
- Model Context Protocol Specification - The official MCP standard
- MCP PHP SDK - Documentation for the official MCP SDK for PHP