Erfahren Sie, wie Sie einen Model-Context-Protocol-(MCP)-Server in Ihre Laravel-Anwendung integrieren. Sie können Tools, Ressourcen und Prompts definieren, mit denen KI-Coding-Agents mit Ihrer Anwendung interagieren.
Das Model Context Protocol (MCP) ist eine Spezifikation, mit der KI-Clients (Claude, Cursor, GitHub Copilot etc.) und Anwendungen über ein standardisiertes Protokoll kommunizieren. Durch die Implementierung eines MCP-Servers können KI-Agents auf Daten Ihrer Laravel-Anwendung zugreifen oder Aktionen ausführen.
Laravel MCP ist ein in Laravel 13 hinzugekommenes offizielles Paket. Es wird als laravel/mcp bereitgestellt und liefert die zum Aufbau eines MCP-Servers benötigten Funktionen.
Ein MCP-Server kann im Wesentlichen drei Arten von Funktionen bereitstellen:
Funktion
Beschreibung
Tools
Funktionen, die der KI-Client aufrufen kann. Für Suche, Aktualisierungen, externe API-Anbindungen usw.
Ressourcen (Resources)
Daten oder Kontextinformationen, die der KI-Client lesen kann
<?phpnamespace App\Mcp\Tools;use Illuminate\Contracts\JsonSchema\JsonSchema;use Laravel\Mcp\Request;use Laravel\Mcp\Response;use Laravel\Mcp\Server\Attributes\Description;use Laravel\Mcp\Server\Tool;#[Description('Fetches the current weather forecast for a specified location.')]class CurrentWeatherTool extends Tool{ public function handle(Request $request): Response { $location = $request->get('location'); // Wetterdaten abrufen... return Response::text('The weather is sunny, 22°C.'); } public function schema(JsonSchema $schema): array { return [ 'location' => $schema->string() ->description('The location to get the weather for.') ->required(), ]; }}
Aus dem Klassennamen werden automatisch ein Standardname und ein Standardtitel abgeleitet. Für CurrentWeatherTool lautet der Name current-weather und der Titel Current Weather Tool. Mit den Attributen Name und Title können Sie diese Werte anpassen.
Die Beschreibung eines Tools (Description) wird nicht automatisch erzeugt. Sie ist unerlässlich, damit das KI-Modell versteht, wie das Tool zu verwenden ist. Legen Sie daher unbedingt eine aussagekräftige Beschreibung fest.
In der Methode schema definieren Sie das Schema der Eingabeparameter. Mit dem JSON-Schema-Builder von Laravel können Sie Typen und Constraints angeben.
public function schema(JsonSchema $schema): array{ return [ 'location' => $schema->string() ->description('The location to get the weather for.') ->required(), 'units' => $schema->string() ->enum(['celsius', 'fahrenheit']) ->description('The temperature units to use.') ->default('celsius'), ];}
Innerhalb der Methode handle können Sie die Standard-Validierung von Laravel nutzen.
public function handle(Request $request): Response{ $validated = $request->validate([ 'location' => 'required|string|max:100', 'units' => 'in:celsius,fahrenheit', ], [ 'location.required' => 'You must specify a location. For example, "New York City" or "Tokyo".', 'units.in' => 'You must specify either "celsius" or "fahrenheit" for the units.', ]); // Mit den validierten Daten weiterarbeiten...}
Schlägt die Validierung fehl, versucht es der KI-Client anhand der Fehlermeldung erneut. Stellen Sie konkrete, umsetzbare Fehlermeldungen bereit.
Da Tools über den Service-Container von Laravel aufgelöst werden, können Sie Abhängigkeiten im Konstruktor oder in der Methode handle per Typ-Hint einbinden.
<?phpnamespace App\Mcp\Tools;use App\Repositories\WeatherRepository;use Laravel\Mcp\Request;use Laravel\Mcp\Response;use Laravel\Mcp\Server\Tool;class CurrentWeatherTool extends Tool{ public function __construct( protected WeatherRepository $weather, ) {} public function handle(Request $request, WeatherRepository $weather): Response { $location = $request->get('location'); $forecast = $weather->getForecastFor($location); return Response::text("Forecast: {$forecast}"); }}
return Response::error('Unable to fetch weather data. Please try again.');
Bild- oder Audioantwort
return Response::image(file_get_contents(storage_path('weather/radar.png')), 'image/png');return Response::audio(file_get_contents(storage_path('weather/alert.mp3')), 'audio/mp3');// Direkt aus dem Storage laden (MIME-Typ wird automatisch erkannt)return Response::fromStorage('weather/radar.png');
Antwort mit mehreren Inhalten
public function handle(Request $request): array{ return [ Response::text('Weather Summary: Sunny, 22°C'), Response::text("**Detailed Forecast**\n- Morning: 18°C\n- Afternoon: 25°C"), ];}
Strukturierte Antwort
Sie geben strukturierte Daten zurück, die vom KI-Client leicht geparst werden können.
In der Methode arguments definieren Sie die Parameter des Prompts.
<?phpnamespace App\Mcp\Prompts;use Laravel\Mcp\Server\Prompt;use Laravel\Mcp\Server\Prompts\Argument;class DescribeWeatherPrompt extends Prompt{ public function arguments(): array { return [ new Argument( name: 'tone', description: 'The tone to use in the weather description (e.g., formal, casual, humorous).', required: true, ), ]; }}
Die Prompt-Argumente werden gemäß Definition automatisch validiert; Sie können aber auch komplexere Validierungsregeln anwenden.Laravel MCP arbeitet nahtlos mit der Validierung von Laravel zusammen. Innerhalb der Methode handle können Sie die Argumente validieren.
<?phpnamespace App\Mcp\Prompts;use Laravel\Mcp\Request;use Laravel\Mcp\Response;use Laravel\Mcp\Server\Prompt;class DescribeWeatherPrompt extends Prompt{ public function handle(Request $request): Response { $validated = $request->validate([ 'tone' => 'required|string|max:50', ]); $tone = $validated['tone']; // Mit dem angegebenen tone einen Prompt erzeugen... }}
Schlägt die Validierung fehl, versucht der KI-Client den Aufruf anhand der Fehlermeldung erneut. Stellen Sie konkrete und umsetzbare Meldungen bereit.
$validated = $request->validate([ 'tone' => ['required', 'string', 'max:50'],], [ 'tone.*' => 'Bitte geben Sie einen tone an. Beispiel: "formal", "casual", "humorous".',]);
Da Prompts über den Service-Container von Laravel aufgelöst werden, können Sie Abhängigkeiten im Konstruktor oder in der Methode handle per Typ-Hint einbinden.
<?phpnamespace App\Mcp\Prompts;use App\Repositories\WeatherRepository;use Laravel\Mcp\Server\Prompt;class DescribeWeatherPrompt extends Prompt{ public function __construct( protected WeatherRepository $weather, ) {}}
Auch in der Methode handle können Sie Typ-Hints setzen; der Service-Container löst sie automatisch auf und injiziert die Instanzen.
public function handle(Request $request, WeatherRepository $weather): Response{ $isAvailable = $weather->isServiceAvailable(); // ...}
In der Methode handle eines Prompts können Sie Benutzer- und Assistant-Nachrichten zurückgeben. Mit asAssistant() behandeln Sie eine Nachricht als Assistant-Message.
<?phpnamespace App\Mcp\Prompts;use Laravel\Mcp\Request;use Laravel\Mcp\Response;use Laravel\Mcp\Server\Prompt;class DescribeWeatherPrompt extends Prompt{ public function handle(Request $request): array { $tone = $request->string('tone'); $systemMessage = "You are a helpful weather assistant. Please provide a weather description in a {$tone} tone."; $userMessage = 'What is the current weather like in Tokyo?'; return [ Response::text($systemMessage)->asAssistant(), Response::text($userMessage), ]; }}
Ressourcen sind Daten oder Informationen, die der KI-Client als Kontext einlesen kann. Sie stellen zum Beispiel Dokumentationen, Konfigurationsinformationen oder dynamische Daten bereit, um die Antwortqualität der KI zu verbessern.
<?phpnamespace App\Mcp\Resources;use Laravel\Mcp\Request;use Laravel\Mcp\Response;use Laravel\Mcp\Server\Attributes\Description;use Laravel\Mcp\Server\Resource;#[Description('Comprehensive guidelines for using the Weather API.')]class WeatherGuidelinesResource extends Resource{ public function handle(Request $request): Response { $guidelines = "# Weather API Guidelines\n\n- Always specify a location..."; return Response::text($guidelines); }}
Standardmäßig wird die URI aus dem Klassennamen erzeugt (z. B. weather://resources/weather-guidelines). Mit den Attributen Uri und MimeType können Sie beide anpassen.
use Laravel\Mcp\Server\Attributes\MimeType;use Laravel\Mcp\Server\Attributes\Uri;use Laravel\Mcp\Server\Resource;#[Uri('weather://resources/guidelines')]#[MimeType('application/pdf')]class WeatherGuidelinesResource extends Resource{ // ...}
Anders als Tools und Prompts können Ressourcen kein Eingabeschema und keine Argumente definieren. Sie können jedoch innerhalb der Methode handle über das Request-Objekt auf Informationen der Anfrage zugreifen.
<?phpnamespace App\Mcp\Resources;use Laravel\Mcp\Request;use Laravel\Mcp\Response;use Laravel\Mcp\Server\Resource;class WeatherGuidelinesResource extends Resource{ public function handle(Request $request): Response { // Auf Informationen der Anfrage zugreifen... }}
Da Ressourcen über den Service-Container von Laravel aufgelöst werden, können Sie Abhängigkeiten im Konstruktor oder in der Methode handle per Typ-Hint einbinden.
<?phpnamespace App\Mcp\Resources;use App\Repositories\WeatherRepository;use Laravel\Mcp\Server\Resource;class WeatherGuidelinesResource extends Resource{ public function __construct( protected WeatherRepository $weather, ) {}}
Auch in der Methode handle können Sie Typ-Hints verwenden; der Service-Container löst sie automatisch auf und injiziert sie.
public function handle(WeatherRepository $weather): Response{ return Response::text($weather->guidelines());}
Mit resourceLink können Sie einen Ressourcen-Link zurückgeben. Anders als eine eingebettete Ressource liefert dies einen URI-Verweis, den der KI-Client eigenständig abruft.
Sie können auch eine registrierte Ressourcen-Klasse oder -Instanz übergeben. URI, Name, Titel, Beschreibung und MIME-Typ werden automatisch übernommen.
Laravel MCP unterstützt MCP Apps. Dabei handelt es sich um eine Erweiterung des Model Context Protocol, mit der Tools interaktive HTML-Anwendungen in einem Sandbox-iframe des unterstützten Hosts rendern können. Damit lassen sich – über reine Textantworten hinaus – Dashboards, Formulare, Visualisierungen und andere Rich-Erlebnisse aufbauen.Eine MCP-App besteht aus zwei zusammenspielenden Teilen:
App-Ressource – Gibt das selbstständige HTML der Anwendung zurück.
Tool – Wird über das Attribut #[RendersApp] mit der App-Ressource verknüpft. Wird das Tool aufgerufen, ruft der Host die verknüpfte Ressource ab und rendert sie.
Der Befehl erstellt zwei Dateien: eine PHP-Klasse in app/Mcp/Resources und eine Blade-View in resources/views/mcp. Der View-Name wird aus dem Klassennamen abgeleitet – aus WeatherDashboardApp wird zum Beispiel mcp.weather-dashboard-app.
AppResource erweitert die Basisklasse Resource und setzt automatisch das von der MCP-Apps-Spezifikation geforderte URI-Schema ui:// und den MIME-Typ text/html;profile=mcp-app. Wie andere Ressourcen muss die Klasse im Array $resources des Servers registriert werden.Die generierte Blade-View verwendet die Komponente <x-mcp::app>. Diese rendert ein vollständiges HTML-Dokument, das das clientseitige MCP-SDK bündelt.
Die globale Funktion createMcpApp stellt das gebündelte SDK bereit. Sie kümmert sich um die Verbindung des iframes zum Server, wendet das Theme des Hosts an und stellt Helfer wie callServerTool, sendMessage und openLink sowie Event-Callbacks bereit. Die vollständige clientseitige API finden Sie in der MCP-Apps-Spezifikation.
Um eine App-Ressource anzuzeigen, verknüpfen Sie sie über das Attribut #[RendersApp] mit einem Tool. Wird das Tool aufgerufen, nimmt Laravel MCP die URI der Ressource in die Metadaten des Tools auf, sodass der Host die App in einem Sandbox-iframe rendern kann.
<?phpnamespace App\Mcp\Tools;use App\Mcp\Resources\WeatherDashboardApp;use Laravel\Mcp\Request;use Laravel\Mcp\Response;use Laravel\Mcp\Server\Attributes\RendersApp;use Laravel\Mcp\Server\Tool;#[RendersApp(resource: WeatherDashboardApp::class)]class ShowWeatherDashboard extends Tool{ /** * Handle the tool request. */ public function handle(Request $request): Response { return Response::text('Weather dashboard loaded.'); }}
Sobald eine AppResource registriert ist, wirbt Laravel MCP automatisch mit der Capability io.modelcontextprotocol/ui. Eine zusätzliche Server-Konfiguration ist nicht nötig.
Jedes #[RendersApp]-Tool lässt sich über das Argument visibility in den erlaubten Aufrufern einschränken. Das ist praktisch, wenn Sie private, nur für die App gedachte Tools, mit denen die UI Daten lädt oder aktualisiert, vor dem Modell verbergen möchten.
Das Enum Visibility kennt die Fälle Model und App. Voreingestellt sind beide. Für Backend-Aktionen, die die UI direkt aufruft, verwenden Sie [Visibility::App]; um ein Tool für die UI unerreichbar zu machen, [Visibility::Model].
Über das Attribut #[AppMeta] an der App-Ressource konfigurieren Sie die Content Security Policy des iframes, Browser-Berechtigungen und Bibliotheks-Skripte, die im <head> der View eingebunden werden.
Das Enum Library enthält vordefinierte CDN-Skripte gängiger Frontend-Bibliotheken wie Library::Tailwind oder Library::Alpine; deren CDN-Origins werden automatisch in die CSP eingebunden. Das Enum Permission deckt Browser-Berechtigungen wie Camera, Microphone, Geolocation, ClipboardWrite etc. ab.
Wenn Sie eine dynamische Konfiguration benötigen, überschreiben Sie die Methode appMeta der Ressource mithilfe der Fluent-Builder AppMeta, Csp und Permissions im Namespace Laravel\Mcp\Server\Ui.
Laravel MCP enthält eine spezielle Boost-Skill-Referenz zum Erstellen von MCP-Apps. Wenn Laravel Boost installiert ist, können KI-Coding-Agents die Skill mcp-development aufrufen und automatisch App-Ressourcen, Blade-Views und verknüpfte Tools generieren.Die vollständige Referenz des Protokolls (inklusive Details zur clientseitigen API und den Schemata) finden Sie in der offiziellen MCP-Apps-Dokumentation.
MCP-Clients können Icons für den Server und dessen Primitives anzeigen. Mit dem Attribut Icon deklarieren Sie Icons für Server, Tools, Ressourcen und Prompts.
Das Attribut Icon ist wiederholbar, sodass Sie mehrere Icons für verschiedene Größen oder Light-/Dark-Varianten deklarieren können.Alternativ können Sie die Methode icons überschreiben und Icons programmatisch definieren. Das ist nützlich, wenn Icons von Laufzeitbedingungen abhängen.
use Laravel\Mcp\Schema\Icon;class CurrentWeatherTool extends Tool{ /** * Liefert die Icons des Tools. * * @return array<int, Icon> */ public function icons(): array { return [ Icon::from('mcp/tool.png', mimeType: 'image/png'), ]; }}
Die per Attribut und über die Methode icons definierten Icons werden automatisch zusammengeführt. Icon-Pfade werden folgendermaßen aufgelöst:
Pfade mit einem URI-Schema wie https: oder data: werden unverändert übernommen.
Relative Pfade werden mit dem Laravel-Helper asset in URLs aufgelöst.
OAuth-Authentifizierung mit Laravel Passport. Geeignet, wenn Sie eine robustere Sicherheit benötigen.
use App\Mcp\Servers\WeatherServer;use Laravel\Mcp\Facades\Mcp;Mcp::oauthRoutes();Mcp::web('/mcp/weather', WeatherServer::class) ->middleware('auth:api');
Wenn Sie OAuth verwenden, veröffentlichen Sie die Autorisierungs-View von Passport und richten sie in einem Service-Provider ein.
Mit $request->user() erhalten Sie den authentifizierten Benutzer und können in Tools oder Ressourcen Autorisierungsprüfungen durchführen.
public function handle(Request $request): Response{ if (! $request->user()->can('read-weather')) { return Response::error('Permission denied.'); } // Weiter mit der Verarbeitung...}
Laravel MCP unterstützt nicht nur den Aufbau von Servern, sondern liefert auch einen Client, mit dem Sie eine Verbindung zu anderen MCP-Servern aufbauen. Damit können Sie Tools, die ein externer MCP-Server anbietet, entdecken und aufrufen. Das ist besonders nützlich, wenn Sie KI-Agents mit den Funktionen externer MCP-Server ausstatten möchten.
Für einen per HTTP erreichbaren MCP-Server verwenden Sie die Methode Client::web und übergeben die URL des Servers.
use Laravel\Mcp\Client;$client = Client::web('https://mcp.example.com');
Für einen lokalen MCP-Server, der als Kommando startet, verwenden Sie Client::local und übergeben Kommando und Argumente.
use Laravel\Mcp\Client;$client = Client::local('php', ['artisan', 'mcp:start']);
Der Client verbindet sich verzögert (lazy connect): Die Verbindung wird beim ersten Auflisten oder Aufrufen von Tools automatisch aufgebaut. Möchten Sie die Verbindung manuell verwalten, verwenden Sie die Methoden connect, connected, ping und disconnect.
Statt einen Client jedes Mal neu zu erzeugen, können Sie wiederverwendbare benannte Clients registrieren. In der Regel geschieht das in der Methode boot eines Service-Providers über die Fassade Mcp.
use Laravel\Mcp\Client;use Laravel\Mcp\Facades\Mcp;Mcp::registerClient('github', fn () => Client::web('https://mcp.example.com'));
Nach der Registrierung lösen Sie den Client per Name auf.
use Laravel\Mcp\Facades\Mcp;$client = Mcp::client('github');
Ein benannter Client wird pro Request nur einmal aufgelöst und am Ende des Request-Lebenszyklus automatisch getrennt.
Um sich zu einem per Bearer-Token geschützten Web-MCP-Server zu verbinden, verwenden Sie withToken. Sie können einen Token-String oder eine Closure für die verzögerte Auflösung übergeben.
Wenn der MCP-Server Dynamic Client Registration unterstützt, können Sie clientId und clientSecret weglassen. Der Client registriert sich dann automatisch.
Registrieren Sie anschließend in der Datei routes/ai.php mit der Methode oAuthRoutesFor OAuth-Routen für den benannten Client. Die übergebene Closure erhält nach dem Austausch des Authorization Codes gegen einen Access Token den Client-Namen und ein TokenSet.
use Illuminate\Support\Facades\Auth;use Laravel\Mcp\Client\OAuth\TokenSet;use Laravel\Mcp\Facades\Mcp;Mcp::oAuthRoutesFor('github', function (string $client, TokenSet $token) { Auth::user()->update([ 'github_mcp_token' => $token->accessToken, ]); return redirect('/dashboard');});
Dadurch werden zwei benannte Routen registriert: eine Connect-Route (mcp.oauth.{client}.connect), die Benutzer zum Autorisierungsserver weiterleitet, und eine Callback-Route (mcp.oauth.{client}.callback), die den Authorization Code austauscht und den Handler aufruft. Beide verwenden die Middleware-Gruppe web (überschreibbar über das Argument middleware).Um den Autorisierungsablauf zu starten, leiten Sie den Benutzer auf die Connect-Route weiter.
Mit der Methode tools erhalten Sie die von einem MCP-Server angebotenen Tools. Sie werden als Collection mit dem Namen als Schlüssel zurückgegeben.
use Laravel\Mcp\Facades\Mcp;$tools = Mcp::client('github')->tools();foreach ($tools as $tool) { $tool->name; $tool->title; $tool->description; $tool->inputSchema;}
Der Client übernimmt die Pagination automatisch und ruft alle Tools ab. Mit dem Argument limit können Sie die abgerufene Anzahl begrenzen.
$tools = Mcp::client('github')->tools(limit: 10);
Um ein Tool aufzurufen, verwenden Sie callTool und übergeben den Namen und ein Argument-Array. Die zurückgegebene ToolResult-Instanz enthält die Antwort.
use Laravel\Mcp\Facades\Mcp;$result = Mcp::client('github')->callTool('current-weather', [ 'location' => 'New York',]);$result->text(); // Textinhalt der Antwort(string) $result; // äquivalent zu text()$result->isError; // ob das Tool einen Fehler gemeldet hat$result->structuredContent; // strukturierter Inhalt (falls vorhanden)
Sie können ein Tool auch direkt über die aufgelistete Instanz aufrufen.
Wenn Sie mit dem Laravel AI SDK einen Agent bauen, können Sie die Tools des MCP-Clients direkt an den Agent übergeben. So kann das Modell sie beim Beantworten von Prompts aufrufen. Näheres finden Sie im Abschnitt MCP-Tools des AI SDK.
Um einen Prompt zu erhalten, verwenden Sie getPrompt und übergeben Name und Argument-Array. Die zurückgegebene PromptResult-Instanz enthält die generierten Nachrichten.
use Laravel\Mcp\Facades\Mcp;$result = Mcp::client('github')->getPrompt('describe-weather', [ 'location' => 'New York',]);$result->text(); // Textinhalt der Nachricht(string) $result; // äquivalent zu text()$result->messages; // vom Prompt zurückgegebene Nachrichten (Rohdaten)$result->description; // Beschreibung des Prompts (falls vorhanden)
Um eine Ressource zu laden, verwenden Sie readResource und übergeben die URI. Die zurückgegebene ResourceReadResult-Instanz enthält die Inhalte.
use Laravel\Mcp\Facades\Mcp;$result = Mcp::client('github')->readResource('weather://guidelines');$result->content(); // Inhalt der Ressource (Base64-Blobs werden automatisch dekodiert)(string) $result; // äquivalent zu content()$result->mimeType(); // MIME-Typ der Ressource (falls vorhanden)$result->contents; // von der Ressource zurückgegebene Inhalte (Rohdaten)
Zum Testen eines MCP-Servers eignet sich das interaktive Debugging-Tool „MCP Inspector“.
# Webserverphp artisan mcp:inspector mcp/weather# Lokaler Server (wenn der Name "weather" lautet)php artisan mcp:inspector weather
Der Befehl startet den MCP Inspector; die Client-Konfiguration können Sie kopieren. Wenn Sie Authentifizierungs-Middleware konfiguriert haben, verbinden Sie sich inklusive des Authorization-Headers.
Für Tools, Ressourcen und Prompts können Sie Unit-Tests schreiben.
test('tool', function () { $response = WeatherServer::tool(CurrentWeatherTool::class, [ 'location' => 'Tokyo', 'units' => 'celsius', ]); $response ->assertOk() ->assertSee('The current weather in Tokyo is 22°C and sunny.');});
public function test_tool(): void{ $response = WeatherServer::tool(CurrentWeatherTool::class, [ 'location' => 'Tokyo', 'units' => 'celsius', ]); $response ->assertOk() ->assertSee('The current weather in Tokyo is 22°C and sunny.');}
$response->assertOk(); // prüft, dass kein Fehler vorliegt$response->assertSee('...'); // prüft, dass ein bestimmter Text enthalten ist
Um das Vorhandensein von Fehlern zu prüfen, nutzen Sie assertHasErrors bzw. assertHasNoErrors.
$response->assertHasErrors();$response->assertHasErrors([ 'Something went wrong.',]);$response->assertHasNoErrors();
Sie können Name, Titel und Beschreibung eines Tools, einer Ressource oder eines Prompts verifizieren.
$response->assertName('current-weather');$response->assertTitle('Current Weather Tool');$response->assertDescription('Fetches the current weather forecast for a specified location.');
Zum Prüfen von Notifications einer Streaming-Antwort verwenden Sie assertSentNotification und assertNotificationCount.