symfony/mcp-bundle
Experimental Symfony bundle integrating Model Context Protocol (MCP) via the official PHP SDK. Build MCP servers exposing tools, prompts, and resources over HTTP transport or STDIO; resource templates are prepared pending SDK support.
Since symfony/mcp-bundle is Symfony-specific, leverage its core functionality via the underlying mcp/sdk. Start with these steps:
Install Dependencies
composer require mcp/sdk symfony/http-client symfony/options-resolver
Create a Laravel MCP Client Service
In app/Providers/AppServiceProvider.php:
use Mcp\Sdk\Client;
use Mcp\Sdk\Transport\HttpTransport;
public function register()
{
$this->app->singleton(Client::class, function ($app) {
return new Client(new HttpTransport('https://mcp.example.com'));
});
}
First Use Case: Invoke a Tool
use Mcp\Sdk\Client;
use Mcp\Sdk\Tool\ToolCall;
public function callTool(Client $mcpClient)
{
$response = $mcpClient->callTool(
new ToolCall('generate-text', ['prompt' => 'Hello, world!'])
);
return $response->getResult();
}
Route the Tool Endpoint
Route::post('/mcp/tool/generate', [McpController::class, 'callTool']);
Mcp\Sdk\Tool\ToolCall (for tool invocation examples)Use MCP tools for modular operations (e.g., text generation, data validation):
// Define a tool
$tool = new ToolCall('summarize', [
'text' => $longDocument,
'max_length' => 200
]);
// Invoke via client
$summary = $mcpClient->callTool($tool)->getResult();
Pass structured prompts with context:
$prompt = new Prompt(
'Analyze user feedback',
['feedback' => $userFeedback, 'user_id' => $userId]
);
$analysis = $mcpClient->callPrompt($prompt);
Upload/download resources (e.g., files, embeddings):
// Upload a resource
$resource = $mcpClient->uploadResource(
'user_data.json',
file_get_contents('path/to/file')
);
// Use in a tool call
$tool->setResource('user_data', $resource->getId());
For Laravel routes, wrap MCP calls in controllers:
public function handleMcpRequest(Request $request, Client $mcpClient)
{
$payload = json_decode($request->getContent(), true);
$toolCall = ToolCall::fromArray($payload);
return response()->json($mcpClient->callTool($toolCall)->toArray());
}
Dependency Injection: Use Laravel’s bind() to register MCP services:
$this->app->bind(McpClient::class, function ($app) {
return new Client(new HttpTransport(config('mcp.endpoint')));
});
Middleware for Auth:
class McpAuthMiddleware
{
public function handle($request, Closure $next)
{
$request->headers->set('Authorization', 'Bearer '.config('mcp.api_key'));
return $next($request);
}
}
Extend MCP with Laravel events:
// Fire after tool invocation
event(new McpToolCalled($toolCall, $response));
Mock MCP responses in PHPUnit:
$mockClient = Mockery::mock(Client::class);
$mockClient->shouldReceive('callTool')
->andReturn(new ToolResponse('mocked_result'));
$this->app->instance(Client::class, $mockClient);
Case Sensitivity in Headers
Content-Type). Use:
$request->headers->set('Content-Type', 'application/json');
Streaming Responses (SSE)
StreamedResponse may conflict with MCP’s streaming. Use:
$response = $mcpClient->callTool($toolCall);
return response()->stream(function () use ($response) {
echo $response->getStreamedContent();
});
Memory Leaks
$mcpClient->reset();
Symfony-Specific Assumptions
Symfony\Component\HttpFoundation\Request in Laravel. Use:
use Symfony\Component\HttpFoundation\ParameterBag;
$bag = new ParameterBag($request->all());
Enable MCP Logging
$mcpClient = new Client(new HttpTransport('https://mcp.example.com'), [
'logger' => new MonologLogger(Logger::create('mcp')),
]);
Inspect Raw Responses
$response = $mcpClient->callTool($toolCall);
\Log::debug('MCP Raw Response:', $response->getRawContent());
Validate Tool Definitions
Use ToolDefinition::validate() to catch schema errors early:
$definition = new ToolDefinition('summarize', ['text' => 'string']);
$definition->validate();
Custom Transports
Extend Mcp\Sdk\Transport\TransportInterface for non-HTTP backends (e.g., gRPC):
class GrpcTransport implements TransportInterface
{
public function send(Request $request): Response
{
// Implement gRPC logic
}
}
Laravel Service Provider Hooks
Override MCP behavior in AppServiceProvider:
public function boot()
{
McpClient::extend(function ($app) {
return new Client(new HttpTransport('custom.endpoint'), [
'timeout' => 30,
]);
});
}
Resource Templates (Future-Proofing) Prepare for MCP SDK’s resource templates by implementing:
class UserDataTemplate implements ResourceTemplateInterface
{
public function getSchema(): array
{
return ['properties' => ['name' => ['type' => 'string']]];
}
}
API Endpoint Validation Ensure the MCP endpoint supports the expected transport (HTTP/STDIO). Test with:
curl -X POST https://mcp.example.com/tool/generate -H "Content-Type: application/json"
Rate Limiting MCP SDK does not enforce rate limits by default. Add middleware:
class McpRateLimiter
{
public function handle($request, Closure $next)
{
if ($this->isOverLimit()) {
throw new \RuntimeException('MCP rate limit exceeded');
}
return $next($request);
}
}
Error Handling
Catch McpException for SDK-specific errors:
try {
$response = $mcpClient->callTool($toolCall);
} catch (McpException $e) {
\Log::error('MCP Error:', ['code' => $e->getCode(), 'message' => $e->getMessage()]);
return response()->json(['error' => 'MCP service unavailable'], 503);
}
How can I help you explore Laravel packages today?