Ein MCP-Server für Dein SaaS: Eine praxisnahe Symfony-Implementierung
Die Anfrage kommt meist über den Vertrieb, nicht über das Engineering: Ein Interessent fragt, ob Dein Produkt "mit Claude funktioniert" oder "einen MCP-Server hat", und plötzlich steht eine MCP-Server-Implementierung mit Quartalsende-Deadline auf der Roadmap. Die gute Nachricht: Für ein Symfony-basiertes SaaS ist eine solide Implementierung ein paar Wochen fokussierte Arbeit, kein Replatforming. Die schlechte Nachricht: Die meisten ersten Versuche machen dieselben drei Dinge falsch. Sie spiegeln die REST-API eins zu eins, sie schrauben API-Keys statt echter Autorisierung an, und sie lassen Evaluation komplett weg.
Dieser Beitrag ist der Implementierungsleitfaden, den diese Teams vorher hätten lesen sollen. Wenn Du noch den Business Case und die Abwägung zwischen Bauen und Warten brauchst, starte mit unserem früheren Erklärstück dazu, was das Model Context Protocol für SaaS-Gründer bedeutet. Dieser hier setzt die Entscheidung voraus und geht in die Symfony-Details.
Was eine MCP-Server-Implementierung tatsächlich umfasst
Das Model Context Protocol ist ein auf JSON-RPC 2.0 basierendes Protokoll, mit dem AI-Clients (Claude, ChatGPT, Agent-Frameworks, IDE-Assistenten) Fähigkeiten entdecken und aufrufen können, die Dein Produkt bereitstellt. Ein produktionsreifer MCP-Server für ein SaaS hat fünf Teile:
- Ein Transport-Endpoint. Für ein Multi-Tenant-SaaS ist das Streamable HTTP, der Remote-Transport in der aktuellen Spezifikation. Der Stdio-Transport ist für lokale Tools gedacht und hat in Deiner Produktionsarchitektur nichts verloren.
- Tools. Benannte Operationen mit JSON-Schema-Eingabedefinitionen, die das Modell aufrufen kann:
create_invoice,search_tickets,summarize_account_activity. - Resources und Prompts (optional, aber wertvoll). Read-only-Daten, die der Client in den Kontext ziehen kann, und wiederverwendbare Prompt-Templates für häufige Workflows in Deiner Domäne.
- Autorisierung. Die MCP-Spezifikation baut für Remote-Server auf OAuth 2.1 auf. Hier steht und fällt die Tenant-Isolation.
- Operatives Drumherum. Logging, Rate Limits, Evals und Audit-Trails, denn Du lässt einen nicht-deterministischen Aufrufer Aktionen in Kundenkonten ausführen.
Alles Folgende mappt diese Teile auf eine Symfony-Anwendung.
Schritt 1: SDK wählen und den Transport verdrahten
Das PHP-Ökosystem ist hier schnell gereift. Es gibt ein offizielles PHP-SDK für MCP, entwickelt in Zusammenarbeit mit der PHP Foundation und dem Symfony-Team, und Symfony liefert MCP-Integration als Teil seiner AI-Initiative. Prüfe den aktuellen Stand von beidem, bevor Du startest, denn die Spezifikation entwickelt sich weiter und die Transport-Details haben sich schon einmal geändert (der ältere HTTP+SSE-Transport wurde durch Streamable HTTP abgelöst). Pinne die Protokollversion, die Du unterstützt, und dokumentiere sie in Deinen Compliance-Unterlagen.
In einer Symfony-App ist der Endpoint eine Controller-Route, kein separater Service:
#[Route('/mcp', name: 'mcp_endpoint', methods: ['POST', 'GET', 'DELETE'])]
public function handle(Request $request, McpServer $server): Response
{
// The SDK handles JSON-RPC parsing, session negotiation,
// and capability discovery. You handle auth and wiring.
return $server->process($request);
}
Halte ihn innerhalb der Hauptanwendung statt in einem Sidecar-Service. Deine Tools brauchen Deine Doctrine-Entities, Deine Voter und Deine Business-Regeln. Ein separater Microservice, der Berechtigungsprüfungen neu implementiert, ist der Weg, auf dem Tenant-Daten leaken.
Schritt 2: Tools um Jobs herum designen, nicht um Endpoints
Der stärkste Prädiktor dafür, ob sich eine MCP-Integration in der Praxis gut anfühlt, ist das Tool-Design. Die verlockende Abkürzung ist, ein Tool pro REST-Endpoint zu generieren. Widersteh ihr. Ein Modell vor 80 feingranularen Tools mit überlappenden Namen wählt ständig falsch, verbrennt Tokens mit Retries und produziert Support-Tickets.
Designe Tools so, wie Du eine CLI für einen kompetenten, aber wortwörtlich denkenden Operator designen würdest:
- Wenige und grobe. Fünf bis fünfzehn Tools, die auf Nutzer-Jobs mappen ("überfällige Rechnungen eines Kunden finden"), schlagen fünfzig CRUD-Wrapper. Jedes zusätzliche Tool verwässert die Trefferquote des Modells bei allen anderen.
- Beschreibungen sind Prompts. Die Tool-Beschreibung ist die einzige Dokumentation, die das Modell liest. Beschreibe, was das Tool tut, wann man es nutzt, wann NICHT, und was die Parameter in Domänenbegriffen bedeuten.
- Strikte Input-Schemas. Enums statt freier Strings, explizite Pflichtfelder, enge Formate. Jeder lose Parameter ist eine Einladung an das Modell, Werte zu erfinden. Dieselbe Disziplin, die strukturierte LLM-Outputs zuverlässig macht, gilt auch auf der Empfängerseite.
- Fehler in Klartext. Gib "customer 4711 has no draft invoices; use search_invoices to list payable ones" zurück statt eines Stack Trace oder eines nackten 422. Das Modell liest den Fehler und korrigiert sich selbst; gib ihm etwas, womit es arbeiten kann.
In Symfony ist ein Tool ein kleiner invokable Service. Validiere den Input mit der Validator-Komponente genau wie ein API-Payload und delegiere dann an denselben Application-Service, den auch Deine Controller nutzen. Keine Business-Logik in der Tool-Klasse selbst.
Schritt 3: Autorisierung und Multi-Tenant-Scoping
Das ist der Schritt, der eine Demo von einem Produkt trennt, und der erste, den wir uns ansehen, wenn wir eine MCP-Implementierung auditieren. Das Autorisierungsmodell der Spezifikation ist OAuth 2.1: Dein MCP-Server ist ein Resource Server, Tokens kommen als Bearer Tokens an, und der Client entdeckt Deinen Authorization Server über Protected-Resource-Metadaten.
Die Regeln, die für ein Multi-Tenant-SaaS zählen:
- Tokens tragen den Tenant, den Nutzer und einen reduzierten Scope. Eine MCP-Session sollte nie breitere Rechte haben als der Nutzer, der sie verbunden hat. Idealerweise weniger: standardmäßig leselastige Scopes, Schreib-Scopes nur, wo der Use Case sie verlangt.
- Löse den Tenant-Kontext bei jedem Aufruf aus dem Token auf. Nie aus einem Tool-Parameter. Wenn
tenant_idetwas ist, das das Modell übergeben kann, hast Du eine Confused-Deputy-Schwachstelle mit Extraschritten gebaut. - Leite Tool-Ausführungen durch Deine bestehende Autorisierungsschicht. In Symfony heißt das: dieselben Security-Voter, die Deine Controller nutzen. Wenn ein Nutzer eine Rechnung im UI nicht löschen darf, darf es das Modell in seinem Namen auch nicht, und die Ablehnung sollte einen lesbaren Fehler erzeugen, keinen stillen Fehlschlag.
- Mach destruktive Aktionen interaktiv. Gib für alles Irreversible einen Bestätigungsschritt zurück oder verlange einen expliziten
confirm: true-Parameter, den der Client dem Menschen anzeigt. Agents sind enthusiastisch; Dein Job ist, Enthusiasmus sicher zu machen.
Logge jeden Tool-Aufruf mit Token-Subjekt, Tenant, Argumenten und Ergebnis. Enterprise-Käufer werden diesen Audit-Trail in demselben Security-Review anfordern, in dem sie nach MCP gefragt haben.
Schritt 4: Betreibe ihn wie eine öffentliche API, kalkuliere ihn wie ein LLM-Feature
Ein MCP-Server ist eine öffentliche API mit einem ungewöhnlichen Client, also gilt das Standard-Hardening: Rate Limits pro Tenant, Request-Größenlimits, Timeouts auf jedem Downstream-Call. Zwei Ergänzungen sind MCP-spezifisch.
Erstens: Rechne mit stoßweisem, redundantem Traffic. Agents wiederholen, explorieren und rufen dieselbe Suche auf drei verschiedene Arten auf. Idempotenz bei Schreib-Tools ist nicht optional, und Caching bei Lese-Tools zahlt sich schnell aus.
Zweitens: Behalte den Kostenpfad im Auge. Wenn eines Deiner Tools intern ein LLM aufruft (Zusammenfassung, Klassifikation), kann eine Agent-Schleife auf Client-Seite Deine Inferenzkosten unsichtbar vervielfachen. Lege um Tool-getriggerte Inferenz dieselben Budgets pro Tenant wie um Deine eigenen AI-Features; unser Beitrag zu LLM-Kostenkontrolle für SaaS beschreibt das Budget-Muster im Detail.
Testen: Unit-Tests reichen nicht
Teste einen MCP-Server auf drei Ebenen:
- Protokollebene. Die Test-Utilities des SDK oder der MCP Inspector verifizieren Discovery, Schema-Validität und Transport-Verhalten. Günstig, schnell, läuft in der CI.
- Vertragsebene. PHPUnit-Tests, die jedes Tool mit validen, invaliden und bösartigen Inputs aufrufen (Cross-Tenant-IDs, überdimensionierte Payloads, Injection-Versuche in String-Feldern) und auf Responses und Seiteneffekte prüfen.
- Verhaltensebene. Eine kleine Eval-Suite: 20 bis 50 realistische Prompts gegen ein echtes Modell mit Verbindung zu einem Staging-Server, bewertet danach, ob die richtigen Tools mit den richtigen Argumenten aufgerufen wurden. Diese Ebene fängt schlechte Tool-Beschreibungen, die kein deterministischer Test sehen kann. Führe sie bei jeder Änderung einer Beschreibung erneut aus, denn Beschreibungen sind Prompts, und Prompt-Änderungen regressieren still.
Häufige Fehler, die wir in MCP-Audits sehen
- Die REST-API eins zu eins spiegeln, was Dutzende Tools erzeugt, zwischen denen das Modell nicht wählen kann.
- Einen statischen API-Key akzeptieren, weil OAuth "zu lange gedauert hat", was Berechtigungen pro Nutzer und Widerruf unmöglich macht.
- Modell-gelieferten Identifiern für Tenant- oder Nutzerkontext vertrauen.
- Rohe Exception-Messages zurückgeben, die Schema-Details leaken und das Modell in Sackgassen-Retries treiben.
- Ohne Evals shippen und über Support-Tickets herausfinden, dass das Modell ein Tool mit mehrdeutiger Beschreibung falsch nutzt.
- Kein Pinning der Protokollversion, sodass ein Client-Update die Integration in Produktion bricht.
Nichts davon ist exotisch. Es sind dieselben API-Design- und Security-Grundlagen, die überall gelten, unter Stress gesetzt durch einen Client, der Dokumentation wortwörtlich liest und nie müde wird. Wenn Dein Team abwägt, wo MCP in die breitere Produktarchitektur passt, ist das ein Tech-Stack-Strategie-Gespräch, das sich vor dem ersten Sprint lohnt, nicht danach.
Häufig gestellte Fragen
Brauche ich einen separaten Service für den MCP-Server? Meist nicht. Innerhalb Deines Symfony-Monolithen nutzt Du Entities, Voter und Services direkt wieder. Lagere ihn nur aus, wenn Du harte Isolations- oder Skalierungsanforderungen hast, und akzeptiere die duplizierte Autorisierungslogik als echten Preis.
Streamable HTTP oder stdio? Streamable HTTP für alles, was Multi-Tenant oder remote ist. Stdio ist nur für lokale Desktop-Integrationen und internes Developer-Tooling.
Wie viele Tools sollten wir bereitstellen? Starte mit drei bis fünf für Deine wertvollsten Workflows. Erweitere anhand beobachteter Nutzung. Ein Tool hinzuzufügen ist weit einfacher, als eines stillzulegen, das Kunden in Agent-Workflows verdrahtet haben.
Wie lange dauert eine MCP-Server-Implementierung? Für eine gut strukturierte Symfony-Anwendung mit bestehender OAuth-Infrastruktur ist eine erste Produktionsversion mit drei bis fünf Tools, Scoping und Eval-Suite typischerweise zwei bis vier Wochen entfernt. Codebasen ohne saubere Service-Schicht dauern länger, weil die Tools nichts Sicheres zum Aufrufen haben.
Ist MCP 2026 stabil genug, um darauf zu bauen? Das Protokoll hat sich geändert (insbesondere der Transport), aber die Verbreitung über die großen AI-Clients macht es zum De-facto-Standard für dieses Integrationsmuster. Pinne die Protokollversion, isoliere spezifikationsnahen Code hinter dem SDK und behandle Upgrades als eingeplante Arbeit.
Shippe ihn mit Vertrauen
Wolf-Tech baut und auditiert AI-facing Integrationen für B2B-SaaS-Teams, von Custom Software Development bis zu fokussierten Code-Quality- und Security-Reviews bestehender MCP- und LLM-Features. Wenn Du ein zweites Paar erfahrener Augen auf Deinem Tool-Design oder Deiner Tenant-Isolation willst, bevor Enterprise-Kunden ihre Agents verbinden, schreib an hello@wolf-tech.io oder finde uns auf wolf-tech.io.

