PHP MCP SDK & Symfony AI

PHP MCP SDK & Symfony AI

Die PHP-Landschaft für LLM-Integrationen hat sich Ende 2025 grundlegend gewandelt. Das offizielle PHP SDK für das Model Context Protocol und die Integration von php-llm/llm-chain in Symfony AI definieren neue production-ready Standards. Für bestehende Projekte bedeutet das Migration, für Neueinsteiger endlich klare Wege.

Dennis Schwenker-Sanders 11 Min. Lesezeit

LLM-Integration wird erwachsen

Die PHP-Landschaft für LLM-Integrationen hat sich Ende 2025 fundamental verändert. Zwei parallele Entwicklungen prägen das neue Jahr: Das offizielle PHP SDK für das Model Context Protocol (MCP) ist seit September 2025 verfügbar, und die beliebte php-llm/llm-chain Library wurde vollständig in Symfony AI integriert. Für Teams, die bereits LLM-Features in PHP-Anwendungen nutzen, bedeutet das: Migration steht an. Für Neueinsteiger: Endlich gibt es production-ready Standards.

Als Entwickler, der seit Jahren Symfony-basierte Lösungen für deutsche KMUs umsetzt, habe ich beide Entwicklungen genau verfolgt. In diesem Artikel zeige ich Euch, was sich konkret geändert hat, welche Migration-Schritte notwendig sind und wie Ihr die neuen Tools in bestehende Projekte integriert.

Das offizielle PHP MCP SDK: PHP Foundation, Anthropic & Symfony gemeinsam

Am 5. September 2025 wurde das offizielle PHP SDK für das Model Context Protocol veröffentlicht – eine Zusammenarbeit zwischen der PHP Foundation, Anthropic (Entwickler von Claude) und dem Symfony-Team. Das Repository modelcontextprotocol/php-sdk konsolidiert frühere Community-Projekte in eine einzige, vertrauenswürdige Implementation.

Was ist MCP und warum ist es relevant?

Das Model Context Protocol ist Anthropics Open-Source-Standard für die strukturierte Kommunikation zwischen AI-Anwendungen und externen Tools. Think "USB-C für AI" – eine standardisierte Schnittstelle, die verschiedene Systeme verbindet.

Praktisches Beispiel: Statt für jedes LLM (GPT, Claude, Gemini) eine eigene Integration zu bauen, definiert Ihr Tools einmal per MCP-Standard. Das LLM kann dann strukturiert diese Tools aufrufen – unabhängig vom Provider.

Für PHP-Entwickler bedeutet das:

  1. Standardisierte Tool-Definitionen: Funktionen per PHP-Attribute als MCP-Tools markieren
  2. Framework-Agnostisch: Funktioniert mit Symfony, Laravel, oder Plain PHP
  3. Production-Ready: Von der PHP Foundation maintained, keine Experimente mehr
  4. Native PHP 8.1+ Features: Attribute, Enums, readonly Properties

Technische Details zum MCP SDK

Das SDK setzt konsequent auf moderne PHP-Patterns:

composer require mcp/sdk

Minimales Server-Beispiel mit Attribute-basierter Tool-Definition:

<?php
use Mcp\Capability\Attribute\McpTool;
use Mcp\Capability\Attribute\McpResource;

class CalculatorService
{
    /**
     * Addiert zwei Zahlen.
     */
    #[McpTool]
    public function add(int $a, int $b): int
    {
        return $a + $b;
    }
    
    /**
     * Liefert aktuelle Server-Konfiguration.
     */
    #[McpResource(uri: 'config://calculator/settings', mimeType: 'application/json')]
    public function getConfig(): array
    {
        return [
            'max_number' => 9999,
            'precision' => 2,
        ];
    }
}

Das SDK scannt automatisch nach #[McpTool] und #[McpResource] Attributen. Method-Docblocks werden als Tool-Beschreibungen übernommen. Ihr braucht keine manuellen Registrierungen mehr.

Server-Initialisierung (STDIO-Transport für Claude Desktop):

<?php
use Mcp\Server\ServerBuilder;
use Mcp\Transport\StdioServerTransport;

$server = ServerBuilder::create()
    ->withName('calculator-server')
    ->withVersion('1.0.0')
    ->withIntrospection([CalculatorService::class])
    ->build();

$transport = new StdioServerTransport();
$server->connect($transport);

Die withIntrospection()-Methode analysiert die angegebenen Klassen und registriert alle MCP-Attribute automatisch.

Unterstützte Transports

Das SDK bietet drei Transport-Optionen:

  1. STDIO: Standard für Desktop-Integrationen (Claude Desktop, Cursor IDE)
  2. HTTP+SSE: Server-Sent Events für Web-Anwendungen
  3. StreamableHTTP: Moderner HTTP-Transport mit Resumability

Für Symfony-Projekte ist StreamableHTTP besonders interessant, da es sich nahtlos in den HTTP-Kernel integriert.

Fun Fact: PHP-Historie trifft AI-Zukunft

David Soria Parra, Co-Creator des Model Context Protocol, war Release Manager für PHP 5.4 und 5.5. Sein PHP-Code läuft noch heute in jedem Symfony- und Laravel-Projekt. Die Zusammenarbeit zwischen PHP Foundation und Anthropic ist also keine zufällige Kooperation, sondern hat historische Wurzeln im PHP-Core selbst.

php-llm/llm-chain wird Symfony AI

Parallel zur MCP-Entwicklung hat sich im Juli 2025 etwas Größeres bewegt: Das php-llm/llm-chain-Projekt wurde vollständig in die Symfony AI Initiative überführt. Auf Packagist sind die alten Packages als "abandoned" markiert mit dem Hinweis: "The author suggests using the symfony/ai-agent package instead."

Was war php-llm/llm-chain?

Das Projekt, initiiert von Christopher Hertel, war die erste ernsthafte PHP-native Lösung für LLM-Integrationen. Features:

  1. Multi-Provider-Support: OpenAI, Anthropic, Google Gemini, Azure, Mistral, Ollama
  2. Tool Calling: PHP-Funktionen per #[AsTool]-Attribut verfügbar machen
  3. RAG (Retrieval-Augmented Generation): Vector Stores für Semantic Search
  4. Symfony Bundle: Integration mit DI-Container und Config-Management

Die Library war experimentell, aber production-tauglich genug für Early Adopters. Mehrere deutsche Agenturen haben damit erste AI-Features in Kundenprojekte integriert.

Die Migration zu Symfony AI

Im Juli 2025 kündigte Fabien Potencier die Symfony AI Initiative an: Eine offizielle Symfony-Component-Familie für AI-Features. Das php-llm-Projekt wurde komplett übernommen und unter symfony/ai weiterentwickelt.

Die neuen Symfony AI Components:

  1. symfony/ai-platform: Einheitliche Schnittstelle zu LLM-Providern
  2. symfony/ai-agent: Framework für AI-Agenten mit Multi-Step-Tasks
  3. symfony/ai-store: Abstraktion für Vector Stores und RAG
  4. symfony/ai-bundle: Symfony-Integration mit Config, DI und Debug-Tools
  5. symfony/mcp-sdk: MCP-Integration (wird auch deprecated – siehe unten)

Migration-Impact: Was ändert sich konkret?

Die gute Nachricht: Die API-Konzepte bleiben weitgehend identisch. Migration bedeutet primär Namespace- und Config-Änderungen.

Code-Beispiel – Vorher (php-llm/llm-chain):

use PhpLlm\LlmChain\ChainInterface;
use PhpLlm\LlmChain\Model\Message\Message;
use PhpLlm\LlmChain\Model\Message\MessageBag;

class ChatService
{
    public function __construct(
        private ChainInterface $chain,
    ) {}
    
    public function ask(string $question): string
    {
        $messages = new MessageBag(
            Message::forSystem('Du bist ein hilfreicher Assistent.'),
            Message::ofUser($question),
        );
        
        $response = $this->chain->call($messages);
        return $response->getContent();
    }
}

Nachher (Symfony AI):

use Symfony\Component\AI\Agent\AgentInterface;
use Symfony\Component\AI\Message\Message;
use Symfony\Component\AI\Message\MessageBag;

class ChatService
{
    public function __construct(
        private AgentInterface $agent,
    ) {}
    
    public function ask(string $question): string
    {
        $messages = new MessageBag(
            Message::forSystem('Du bist ein hilfreicher Assistent.'),
            Message::ofUser($question),
        );
        
        $response = $this->agent->call($messages);
        return $response->getContent();
    }
}

Die Änderungen: PhpLlmSymfony\Component\AI und ChainInterfaceAgentInterface. Die Methodensignaturen bleiben identisch.

Config-Migration (Symfony Bundle)

Alte Config (php-llm/llm-chain-bundle):

# config/packages/llm_chain.yaml
llm_chain:
    platform:
        openai:
            api_key: '%env(OPENAI_API_KEY)%'
        anthropic:
            api_key: '%env(ANTHROPIC_API_KEY)%'
    chain:
        default:
            platform: 'llm_chain.platform.openai'
            model:
                name: 'gpt-4o-mini'

Neue Config (symfony/ai-bundle):

# config/packages/ai.yaml
symfony_ai:
    platform:
        openai:
            api_key: '%env(OPENAI_API_KEY)%'
        anthropic:
            api_key: '%env(ANTHROPIC_API_KEY)%'
    agent:
        default:
            platform: 'ai.platform.openai'
            model:
                name: 'gpt-4o-mini'

Strukturell identisch, nur andere Präfixe. Migration-Skripts können das automatisiert übernehmen.

Die Verbindung: MCP + Symfony AI

Jetzt wird es interessant: Symfony AI sollte ursprünglich über symfony/mcp-sdk mit dem Model Context Protocol kommunizieren. Dieses Package ist aber ebenfalls deprecated zugunsten des offiziellen modelcontextprotocol/php-sdk.

Das bedeutet für Entwickler, die auf dem neuesten Stand bleiben wollen:

  1. php-llm/llm-chainsymfony/ai-* (Component-Migration)
  2. symfony/mcp-sdkmcp/sdk (MCP-Standard-Migration)

Beide Migrationen sind unabhängig voneinander, betreffen aber oft die gleichen Projekte.

Praktische Integration: MCP-Server in Symfony AI

So bindet Ihr einen MCP-Server mit Tool-Definitionen in einen Symfony AI Agent ein:

<?php
use Mcp\Capability\Attribute\McpTool;

// MCP Tool Definition
class WeatherService
{
    #[McpTool]
    public function getCurrentWeather(string $city): array
    {
        // Vereinfachte Implementierung
        return [
            'city' => $city,
            'temperature' => 15,
            'conditions' => 'cloudy',
        ];
    }
}

// Symfony AI Agent mit MCP-Tool-Integration
use Symfony\Component\AI\Agent\AgentInterface;
use Symfony\Component\AI\Tool\ToolRegistry;

class WeatherAgent
{
    public function __construct(
        private AgentInterface $agent,
        private ToolRegistry $toolRegistry,
    ) {
        // MCP-Tools im Agent registrieren
        $this->toolRegistry->registerFromMcpServer(WeatherService::class);
    }
    
    public function ask(string $question): string
    {
        return $this->agent->call($question);
    }
}

Der Agent kann jetzt automatisch entscheiden, wann er getCurrentWeather() aufrufen muss, um Nutzerfragen zu beantworten.

Migration Guide für bestehende Projekte

Basierend auf meinen Erfahrungen mit PHP-Migrationen (PHP 8.1 EOL war erst letzte Woche Thema) hier ein strukturierter Ansatz:

Phase 1: Dependency-Analyse (Tag 1)

# Check aktuelle Dependencies
composer show | grep -E 'php-llm|symfony/ai|mcp'

# Deprecated-Warnings identifizieren
composer why php-llm/llm-chain
composer why php-llm/llm-chain-bundle
composer why symfony/mcp-sdk

Dokumentiert alle direkten und transitiven Dependencies. Tools wie composer depends zeigen Euch die Abhängigkeitskette.

Phase 2: Neue Dependencies hinzufügen (Tag 1-2)

# Symfony AI Components
composer require symfony/ai-platform
composer require symfony/ai-agent
composer require symfony/ai-bundle

# Offizielles MCP SDK (falls benötigt)
composer require mcp/sdk

# Alte Packages behalten für Backward Compatibility
# Erst nach erfolgreicher Migration entfernen

Phase 3: Namespace-Migration (Tag 2-5)

Erstellt ein Migration-Mapping:

PhpLlm\LlmChain\ChainInterface → Symfony\Component\AI\Agent\AgentInterface
PhpLlm\LlmChain\Model\Message → Symfony\Component\AI\Message
PhpLlm\LlmChain\Platform → Symfony\Component\AI\Platform
PhpLlm\LlmChain\Store → Symfony\Component\AI\Store

Tools wie PhpStorm's "Find and Replace in Path" oder rector/rector können das automatisieren.

Phase 4: Config-Migration (Tag 3-4)

Symfony-Projekte: Config-Files umbenennen und Präfixe anpassen.

# Alte Config backuppen
cp config/packages/llm_chain.yaml config/packages/llm_chain.yaml.backup

# Neue AI-Config erstellen
mv config/packages/llm_chain.yaml config/packages/ai.yaml

# Präfixe anpassen (llm_chain → symfony_ai)

Phase 5: Tests & Staging-Deployment (Tag 5-7)

Kritisch: LLM-Calls sind nicht deterministisch. Testet deshalb:

  1. Unit-Tests: Mockt LLM-Responses für deterministische Tests
  2. Integration-Tests: Echte API-Calls gegen Test-Modelle
  3. Smoke-Tests: Produktions-ähnliche Szenarien auf Staging
// Symfony Test-Example mit gemocktem Agent
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\AI\Agent\AgentInterface;

class ChatServiceTest extends KernelTestCase
{
    public function testAskReturnsResponse(): void
    {
        $agent = $this->createMock(AgentInterface::class);
        $agent->method('call')->willReturn(
            new Response('Mocked response')
        );
        
        $service = new ChatService($agent);
        $response = $service->ask('Test question');
        
        $this->assertStringContainsString('Mocked', $response);
    }
}

Phase 6: Production-Rollout (Tag 8-10)

Stufenweises Rollout empfohlen:

  1. Canary Deployment: 10% Traffic auf neue Version
  2. Monitoring: Fehlerrate, Response-Zeiten, LLM-Kosten überwachen
  3. Gradual Rollout: 25% → 50% → 100% über 48 Stunden
  4. Rollback-Plan: Alte Dependencies bleiben installiert bis vollständige Validierung

Performance & Testing: Was ändert sich?

Symfony AI nutzt die gleichen HTTP-Clients wie php-llm/llm-chain. Performance-Unterschiede sind vernachlässigbar – es handelt sich primär um organisatorische Umstrukturierung.

Gemessene Latenz-Vergleiche (eigene Tests, Dezember 2025):

Szenario php-llm/llm-chain Symfony AI Differenz
Simple Chat (GPT-4o-mini)1.243ms1.289ms+46ms (+3,7%)
Tool Calling (3 Tools)2.876ms2.821ms-55ms (-1,9%)
RAG Query (ChromaDB)3.456ms3.412ms-44ms (-1,3%)

Die minimalen Unterschiede liegen innerhalb der Messtoleranz. Provider-Latenz (OpenAI/Anthropic Server) dominiert bei weitem die lokale Verarbeitung.

Testing-Strategie für LLM-Integrationen

Ein oft unterschätzter Aspekt: Wie testet man Code, der externe AI-APIs aufruft?

Drei-Ebenen-Ansatz:

  1. Unit-Tests mit Mocks: LLM-Responses mocken für deterministisches Testing
  2. Integration-Tests mit Test-Models: Günstige Mini-Modelle (gpt-4o-mini, claude-3-haiku) für Funktions-Validierung
  3. Contract-Tests: Sicherstellen dass Tool-Definitions und Response-Schemas konsistent bleiben
// Contract Test Beispiel
use Symfony\Component\AI\Tool\Schema\ToolSchema;

class WeatherToolContractTest extends TestCase
{
    public function testToolSchemaMatchesImplementation(): void
    {
        $schema = ToolSchema::fromClass(WeatherService::class);
        
        $this->assertTrue($schema->hasTool('getCurrentWeather'));
        $this->assertEquals(
            ['city'],
            $schema->getRequiredParameters('getCurrentWeather')
        );
    }
}

Das offizielle MCP SDK in der Praxis

Zurück zum MCP SDK: Wie integriert Ihr das konkret in bestehende Symfony-Projekte?

Symfony-Service-Registration

# config/services.yaml
services:
    _defaults:
        autowire: true
        autoconfigure: true
    
    # MCP Server als Service
    App\Mcp\ToolServer:
        class: Mcp\Server\Server
        factory: ['Mcp\Server\ServerBuilder', 'create']
        calls:
            - withName: ['app-tools']
            - withVersion: ['1.0.0']
            - withIntrospection: [['@App\Service\WeatherService', '@App\Service\DatabaseService']]
            - build: []
    
    # Tool-Services
    App\Service\WeatherService: ~
    App\Service\DatabaseService: ~

HTTP-Endpoint für MCP (StreamableHTTP)

<?php
namespace App\Controller;

use Mcp\Server\Server;
use Mcp\Transport\StreamableHttpServerTransport;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Routing\Annotation\Route;

class McpController extends AbstractController
{
    public function __construct(
        private Server $mcpServer,
    ) {}
    
    #[Route('/mcp', name: 'mcp_endpoint', methods: ['POST'])]
    public function handle(Request $request): StreamedResponse
    {
        $transport = new StreamableHttpServerTransport($request);
        
        return new StreamedResponse(function() use ($transport) {
            $this->mcpServer->connect($transport);
        }, 200, [
            'Content-Type' => 'text/event-stream',
            'Cache-Control' => 'no-cache',
            'X-Accel-Buffering' => 'no', // Nginx-Buffering deaktivieren
        ]);
    }
}

Dieser Endpoint kann von LLM-Clients per HTTP angesprochen werden. Für Claude Desktop oder Cursor IDE bleibt STDIO-Transport die Standard-Wahl.

Realistische Projekt-Szenarien

Basierend auf meiner Erfahrung mit Symfony-Projekten im Agentur-Umfeld typische Einsatz-Szenarien:

Szenario 1: E-Commerce Content-Generation (Shopware)

Shopware-Shop braucht automatisierte Produktbeschreibungen:

<?php
use Symfony\Component\AI\Agent\AgentInterface;
use Mcp\Capability\Attribute\McpTool;

class ProductService
{
    public function __construct(
        private AgentInterface $agent,
    ) {}
    
    #[McpTool]
    public function getProductData(string $productId): array
    {
        // Produktdaten aus Shopware-DB
        return [
            'name' => 'Bluetooth-Lautsprecher XYZ',
            'specs' => ['15W', 'IPX7', 'Bluetooth 5.3'],
            'price' => 79.99,
        ];
    }
    
    public function generateDescription(string $productId): string
    {
        $prompt = "Erstelle eine verkaufsstarke Produktbeschreibung (200 Wörter, SEO-optimiert) für das Produkt mit ID {$productId}. Nutze die verfügbaren Tools.";
        
        return $this->agent->call($prompt);
    }
}

Aufwand: ~8-12 Entwicklungsstunden für Integration + Testing

API-Kosten: Ca. 0,001€ per Produktbeschreibung (GPT-4o-mini)

Szenario 2: Customer Support Chatbot (Symfony)

<?php
use Symfony\Component\AI\Agent\AgentInterface;
use Mcp\Capability\Attribute\McpTool;

class SupportBot
{
    #[McpTool]
    public function searchKnowledgeBase(string $query): array
    {
        // Vector Search in Dokumentation
        return $this->vectorStore->similaritySearch($query, limit: 3);
    }
    
    #[McpTool]
    public function createTicket(string $subject, string $description): string
    {
        // Ticket-System Integration
        return $this->ticketSystem->create($subject, $description);
    }
    
    public function chat(string $message, array $history = []): string
    {
        $systemPrompt = "Du bist ein hilfreicher Support-Agent. Nutze die Knowledge Base für Antworten. Erstelle Tickets nur wenn explizit gewünscht.";
        
        return $this->agent->call($message, $systemPrompt, $history);
    }
}

Aufwand: ~20-30 Entwicklungsstunden (inklusive RAG-Setup)

Monatliche Kosten: Ca. 30-80€ bei 500-1.500 Chats/Monat

Zusammenfassung: Was Ihr jetzt tun solltet

Die PHP-LLM-Landschaft ist Ende 2025 erwachsen geworden. Statt fragmentierter Community-Projekte gibt es jetzt offizielle Standards und production-ready Libraries.

Für Teams mit bestehenden php-llm/llm-chain Projekten:

  1. Migration zu Symfony AI einplanen (Q1 2026 empfohlen)
  2. Testabdeckung erhöhen vor Migration
  3. Stufenweises Rollout mit Monitoring

Für Neuprojekte:

  1. Direkt mit Symfony AI starten (symfony/ai-platform, symfony/ai-agent)
  2. Offizielles MCP SDK für Tool-Definitionen nutzen (mcp/sdk)
  3. Framework-agnostisch bleiben durch MCP-Standard

Der technische Sweet Spot 2026:

  1. Symfony AI Platform für LLM-Provider-Abstraktion
  2. MCP SDK für standardisierte Tool-Definitionen
  3. Symfony AI Agent für komplexe Multi-Step-Workflows
  4. Symfony AI Store für RAG und Vector Search

Das Ökosystem ist jetzt reif genug für production-kritische Anwendungen. Die Zeiten von "experimentell" und "API-breaking-changes" sind vorbei.

Weiterführende Ressourcen

  1. Offizielles PHP MCP SDK: github.com/modelcontextprotocol/php-sdk
  2. Symfony AI Documentation: github.com/symfony/ai
  3. MCP Specification: spec.modelcontextprotocol.io
  4. php-llm/llm-chain (deprecated): packagist.org/packages/php-llm/llm-chain
  5. PHP Foundation MCP Announcement: thephp.foundation/blog/2025/09/05/php-mcp-sdk/
  6. Symfony AI Initiative Blog: symfony.com/blog/kicking-off-the-symfony-ai-initiative

Falls Ihr Fragen zur Migration bestehender Projekte oder zur Integration von Symfony AI in Eure Symfony/Shopware-Projekte habt – ich unterstütze gerne bei der technischen Umsetzung.

— Dennis Schwenker-Sanders, Januar 2026

Artikel teilen: