Symfony AI MCP SDK is the low level library that enables communication between a PHP application and an LLM model.
Install the SDK using Composer:
$ composer require symfony/mcp-sdk
The Model Context Protocol is built on top of JSON-RPC. There two types of messages. A Notification and Request. The Notification is just a status update that something has happened. There is never a response to a Notification. A Request is a message that expects a response. There are 3 concepts/capabilities that you may use. These are:
1. Resources: File-like data that can be read by clients (like API responses or file contents) 1. Tools: Functions that can be called by the LLM (with user approval) 1. Prompts: Pre-written templates that help users accomplish specific tasks
The SDK comes with NotificationHandlers and RequestHandlers which are expected to be wired up in your application.
The Symfony\AI\McpSdk\Server\JsonRpcHandler is the heart of the SDK. It is here
you inject the NotificationHandlers and RequestHandlers. It is recommended to use
the built-in handlers in Symfony\AI\McpSdk\Server\NotificationHandlers\* and
Symfony\AI\McpSdk\Server\RequestHandlers\*.
The Symfony\AI\McpSdk\Server\JsonRpcHandler is started and kept running by
the Symfony\AI\McpSdk\Server
The SDK supports multiple transports for sending and receiving messages. The
Symfony\AI\McpSdk\Server is using the transport to fetch a message, then
give it to the Symfony\AI\McpSdk\Server\JsonRpcHandler and finally send the
response/error back to the transport. The SDK comes with a few transports:
1. Symfony Console Transport: Good for testing and for CLI applications 1. Stream Transport: It uses Server Side Events (SSE) and HTTP streaming
Any client would like to discover the capabilities of the server. Exactly what
the server supports is defined in the Symfony\AI\McpSdk\Server\RequestHandler\InitializeHandler.
When the client connects, it sees the capabilities and will ask the server to list
the tools/resource/prompts etc. When you want to add a new capability, example a
Tool that can tell the current time, you need to provide some metadata to the
Symfony\AI\McpSdk\Server\RequestHandler\ToolListHandler:
namespace App;
use Symfony\AI\McpSdk\Capability\Tool\MetadataInterface;
class CurrentTimeToolMetadata implements MetadataInterface
{
public function getName(): string
{
return 'Current time';
}
public function getDescription(): string
{
return 'Returns the current time in UTC';
}
public function getInputSchema(): array
{
return [
'type' => 'object',
'properties' => [
'format' => [
'type' => 'string',
'description' => 'The format of the time, e.g. "Y-m-d H:i:s"',
'default' => 'Y-m-d H:i:s',
],
],
'required' => [],
];
}
}
We would also need a class to actually execute the tool:
namespace App;
use Symfony\AI\McpSdk\Capability\Tool\IdentifierInterface;
use Symfony\AI\McpSdk\Capability\Tool\ToolCall;
use Symfony\AI\McpSdk\Capability\Tool\ToolCallResult;
use Symfony\AI\McpSdk\Capability\Tool\ToolExecutorInterface;
class CurrentTimeToolExecutor implements ToolExecutorInterface, IdentifierInterface
{
public function getName(): string
{
return 'Current time';
}
public function call(ToolCall $input): ToolCallResult
{
$format = $input->arguments['format'] ?? 'Y-m-d H:i:s';
return new ToolCallResult(
(new \DateTime('now', new \DateTimeZone('UTC')))->format($format)
);
}
}
If you have multiple tools, you can put them in a ToolChain:
$tools = new ToolChain([
new CurrentTimeToolMetadata(),
new CurrentTimeToolExecutor(),
]);
$jsonRpcHandler = new Symfony\AI\McpSdk\Server\JsonRpcHandler(
new Symfony\AI\McpSdk\Message\Factory(),
[
new ToolCallHandler($tools),
new ToolListHandler($tools),
// Other RequestHandlers ...
],
[
// Other NotificationHandlers ...
],
new NullLogger()
);
With this metadata and executor, the client can now call the tool.
If you want to extend the SDK, you can create your own RequestHandlers and NotificationHandlers. The provided one are very good defaults for most applications but they are not a requirement.
If you do decide to use them, you get the benefit of having a well-defined interfaces and value objects to work with. They will assure that you follow the Model Context Protocol. specification.
You also have the Transport abstraction that allows you to create your own transport if non of the standard ones fit your needs.