Zum Hauptinhalt springen

Was ist MCP

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:
FunktionBeschreibung
ToolsFunktionen, 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
PromptsWiederverwendbare Prompt-Vorlagen

Installation

Installieren Sie das Paket mit Composer.
composer require laravel/mcp
Nach der Installation führen Sie den Artisan-Befehl vendor:publish aus, um die Datei routes/ai.php zu erzeugen.
php artisan vendor:publish --tag=ai-routes
Dadurch wird die Datei routes/ai.php angelegt. Dort registrieren Sie Ihre MCP-Server.

Einen Server erstellen

Erzeugen Sie eine Serverklasse mit dem Artisan-Befehl make:mcp-server.
php artisan make:mcp-server WeatherServer
Die Serverklasse wird im Verzeichnis app/Mcp/Servers angelegt.
<?php

namespace App\Mcp\Servers;

use Laravel\Mcp\Server\Attributes\Instructions;
use Laravel\Mcp\Server\Attributes\Name;
use Laravel\Mcp\Server\Attributes\Version;
use Laravel\Mcp\Server;

#[Name('Weather Server')]
#[Version('1.0.0')]
#[Instructions('This server provides weather information and forecasts.')]
class WeatherServer extends Server
{
    protected array $tools = [
        // GetCurrentWeatherTool::class,
    ];

    protected array $resources = [
        // WeatherGuidelinesResource::class,
    ];

    protected array $prompts = [
        // DescribeWeatherPrompt::class,
    ];
}

Server registrieren

Nachdem Sie den Server erstellt haben, registrieren Sie ihn in routes/ai.php. Es gibt zwei Registrierungsvarianten: Webserver und lokaler Server.

Webserver

Auf einen Webserver kann per HTTP-POST-Request zugegriffen werden. Er eignet sich ideal für entfernte KI-Clients oder Web-basierte Integrationen.
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::web('/mcp/weather', WeatherServer::class);
Wie bei normalen Routen können Sie Middleware anwenden.
Mcp::web('/mcp/weather', WeatherServer::class)
    ->middleware(['throttle:mcp']);

Lokaler Server

Ein lokaler Server läuft als Artisan-Befehl. Er wird für die Integration mit lokalen KI-Clients wie Claude Desktop verwendet.
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::local('weather', WeatherServer::class);
Ein lokaler Server wird üblicherweise vom MCP-Client automatisch gestartet. Sie müssen den Artisan-Befehl mcp:start nicht manuell ausführen.

Tools

Tools sind Funktionen, die der KI-Client aufrufen kann. Sie können damit Daten abrufen, externe APIs anbinden, Datenbanken bearbeiten und vieles mehr.

Ein Tool erstellen

Erzeugen Sie eine Tool-Klasse mit dem Artisan-Befehl make:mcp-tool.
php artisan make:mcp-tool CurrentWeatherTool
Registrieren Sie das erzeugte Tool anschließend in der Eigenschaft $tools des Servers.
use App\Mcp\Tools\CurrentWeatherTool;
use Laravel\Mcp\Server;

class WeatherServer extends Server
{
    protected array $tools = [
        CurrentWeatherTool::class,
    ];
}
Ein Beispiel für eine einfache Tool-Klasse:
<?php

namespace 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(),
        ];
    }
}

Name und Beschreibung eines Tools

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.
use Laravel\Mcp\Server\Attributes\Name;
use Laravel\Mcp\Server\Attributes\Title;

#[Name('get-optimistic-weather')]
#[Title('Get Optimistic Weather Forecast')]
class CurrentWeatherTool extends Tool
{
    // ...
}
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.

Eingabeschema

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'),
    ];
}

Ausgabeschema

In der Methode outputSchema können Sie die Struktur der Antwort definieren. Das erleichtert es dem KI-Client, die Antwort zu parsen.
public function outputSchema(JsonSchema $schema): array
{
    return [
        'temperature' => $schema->number()
            ->description('Temperature in Celsius')
            ->required(),

        'conditions' => $schema->string()
            ->description('Weather conditions')
            ->required(),

        'humidity' => $schema->integer()
            ->description('Humidity percentage')
            ->required(),
    ];
}

Validierung

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.

Dependency Injection

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.
<?php

namespace 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}");
    }
}

Annotationen

Mit zusätzlichen Annotationen können Sie dem KI-Client weitergehende Informationen zum Verhalten des Tools mitgeben.
use Laravel\Mcp\Server\Tools\Annotations\IsIdempotent;
use Laravel\Mcp\Server\Tools\Annotations\IsReadOnly;
use Laravel\Mcp\Server\Tool;

#[IsIdempotent]
#[IsReadOnly]
class CurrentWeatherTool extends Tool
{
    // ...
}
Verfügbar sind folgende Annotationen:
AnnotationBeschreibung
#[IsReadOnly]Zeigt an, dass das Tool die Umgebung nicht verändert
#[IsDestructive]Zeigt an, dass das Tool möglicherweise zerstörende Updates ausführt
#[IsIdempotent]Zeigt an, dass wiederholte Aufrufe mit denselben Argumenten keinen zusätzlichen Effekt haben
#[IsOpenWorld]Zeigt an, dass das Tool mit externen Entitäten interagieren kann

Bedingte Registrierung

Durch Implementieren der Methode shouldRegister können Sie ein Tool zur Laufzeit bedingt registrieren.
public function shouldRegister(Request $request): bool
{
    return $request?->user()?->subscribed() ?? false;
}
Gibt die Methode false zurück, ist das Tool für den KI-Client nicht sichtbar.

Antworten

Ein Tool muss eine Instanz von Laravel\Mcp\Response zurückgeben.
return Response::text('Weather Summary: Sunny, 22°C');
return Response::error('Unable to fetch weather data. Please try again.');
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');
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"),
    ];
}
Sie geben strukturierte Daten zurück, die vom KI-Client leicht geparst werden können.
return Response::structured([
    'temperature' => 22.5,
    'conditions' => 'Partly cloudy',
    'humidity' => 65,
]);
Bei länger laufenden Vorgängen können Sie den Fortschritt in Echtzeit übertragen.
public function handle(Request $request): Generator
{
    $locations = $request->array('locations');

    foreach ($locations as $index => $location) {
        yield Response::notification('processing/progress', [
            'current' => $index + 1,
            'total' => count($locations),
            'location' => $location,
        ]);

        yield Response::text($this->forecastFor($location));
    }
}

Prompts

Prompts sind wiederverwendbare Prompt-Vorlagen. Sie standardisieren typische Abfragen, mit denen KI-Clients mit Sprachmodellen interagieren.

Einen Prompt erstellen

php artisan make:mcp-prompt DescribeWeatherPrompt
Registrieren Sie den Prompt in der Eigenschaft $prompts des Servers.
use App\Mcp\Prompts\DescribeWeatherPrompt;

class WeatherServer extends Server
{
    protected array $prompts = [
        DescribeWeatherPrompt::class,
    ];
}

Prompt-Argumente

In der Methode arguments definieren Sie die Parameter des Prompts.
<?php

namespace 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,
            ),
        ];
    }
}

Validierung

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.
<?php

namespace 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".',
]);

Dependency Injection

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.
<?php

namespace 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();

    // ...
}

Bedingte Registrierung

Durch Implementieren der Methode shouldRegister können Sie einen Prompt zur Laufzeit bedingt registrieren.
<?php

namespace App\Mcp\Prompts;

use Laravel\Mcp\Request;
use Laravel\Mcp\Server\Prompt;

class CurrentWeatherPrompt extends Prompt
{
    public function shouldRegister(Request $request): bool
    {
        return $request?->user()?->subscribed() ?? false;
    }
}
Gibt die Methode false zurück, ist der Prompt für den KI-Client weder sichtbar noch aufrufbar.

Prompt-Antworten

In der Methode handle eines Prompts können Sie Benutzer- und Assistant-Nachrichten zurückgeben. Mit asAssistant() behandeln Sie eine Nachricht als Assistant-Message.
<?php

namespace 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

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.

Eine Ressource erstellen

php artisan make:mcp-resource WeatherGuidelinesResource
Registrieren Sie die Ressource in der Eigenschaft $resources des Servers.
use App\Mcp\Resources\WeatherGuidelinesResource;

class WeatherServer extends Server
{
    protected array $resources = [
        WeatherGuidelinesResource::class,
    ];
}
<?php

namespace 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);
    }
}

URI und MIME-Typ

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
{
    // ...
}

Ressourcen-Templates

Um eine dynamische Ressource mit URI-Variablen zu definieren, implementieren Sie das Interface HasUriTemplate.
<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Attributes\MimeType;
use Laravel\Mcp\Server\Contracts\HasUriTemplate;
use Laravel\Mcp\Server\Resource;
use Laravel\Mcp\Support\UriTemplate;

#[Description('Access user files by ID')]
#[MimeType('text/plain')]
class UserFileResource extends Resource implements HasUriTemplate
{
    public function uriTemplate(): UriTemplate
    {
        return new UriTemplate('file://users/{userId}/files/{fileId}');
    }

    public function handle(Request $request): Response
    {
        $userId = $request->get('userId');
        $fileId = $request->get('fileId');

        // Dateiinhalt laden und zurückgeben...

        return Response::text("File {$fileId} for user {$userId}");
    }
}
Die Variablen aus der URI werden automatisch in den Request übernommen und lassen sich mit get abrufen.

Anfragen an Ressourcen

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.
<?php

namespace 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...
    }
}

Dependency Injection für Ressourcen

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.
<?php

namespace 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());
}

Annotationen für Ressourcen

Ressourcen können mit Annotationen für Zielgruppe, Priorität und letztes Änderungsdatum versehen werden.
use Laravel\Mcp\Enums\Role;
use Laravel\Mcp\Server\Annotations\Audience;
use Laravel\Mcp\Server\Annotations\LastModified;
use Laravel\Mcp\Server\Annotations\Priority;
use Laravel\Mcp\Server\Resource;

#[Audience(Role::User)]
#[LastModified('2025-01-12T15:00:58Z')]
#[Priority(0.9)]
class UserDashboardResource extends Resource
{
    // ...
}
AnnotationTypBeschreibung
#[Audience]Role oder ArrayZielgruppe (Role::User, Role::Assistant oder beide)
#[Priority]floatWichtigkeitswert (0.0–1.0)
#[LastModified]stringLetztes Änderungsdatum im ISO-8601-Format

Bedingte Registrierung von Ressourcen

Durch Implementieren der Methode shouldRegister können Sie eine Ressource zur Laufzeit bedingt registrieren.
<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Request;
use Laravel\Mcp\Server\Resource;

class WeatherGuidelinesResource extends Resource
{
    public function shouldRegister(Request $request): bool
    {
        return $request?->user()?->subscribed() ?? false;
    }
}
Gibt die Methode false zurück, ist die Ressource für den KI-Client weder sichtbar noch abrufbar.

Ressourcen-Antworten

Eine Ressource muss eine Instanz von Laravel\Mcp\Response zurückgeben. Für Textinhalte verwenden Sie die Methode text.
return Response::text($weatherData);
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.
return Response::resourceLink(
    uri: 'file:///data/report.json',
    name: 'monthly-report',
    mimeType: 'application/json',
);
Sie können auch eine registrierte Ressourcen-Klasse oder -Instanz übergeben. URI, Name, Titel, Beschreibung und MIME-Typ werden automatisch übernommen.
return Response::resourceLink(new WeatherForecastResource);

Blob-Antwort

Um Binärinhalte zurückzugeben, verwenden Sie die Methode blob. Den MIME-Typ legen Sie über das Attribut #[MimeType] der Ressource fest.
return Response::blob(file_get_contents(storage_path('weather/radar.png')));
#[MimeType('image/png')]
class WeatherGuidelinesResource extends Resource
{
    // ...
}

Fehlerantwort

Zum Signalisieren eines Fehlers verwenden Sie die Methode error.
return Response::error('Wetterdaten für den angegebenen Ort konnten nicht geladen werden.');

Apps

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.

Eine App-Ressource erstellen

Mit dem Artisan-Befehl make:mcp-app-resource legen Sie eine App-Ressource an.
php artisan make:mcp-app-resource WeatherDashboardApp
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.
<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\AppMeta;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\AppResource;

#[Description('An interactive weather dashboard.')]
#[AppMeta]
class WeatherDashboardApp extends AppResource
{
    /**
     * Handle the app resource request.
     */
    public function handle(Request $request): Response
    {
        return Response::view('mcp.weather-dashboard-app', [
            'title' => $this->title(),
        ]);
    }
}
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.
<x-mcp::app :title="$title">
    <x-slot:head>
        <script type="module">
        createMcpApp(async (app) => {
            document.getElementById('run-btn').addEventListener('click', async () => {
                const result = await app.callServerTool('get-weather-data', {});
                document.getElementById('output').textContent = result.content[0]?.text ?? '';
            });
        });
        </script>
    </x-slot:head>

    <div id="app">
        <button id="run-btn">Refresh</button>
        <p id="output"></p>
    </div>
</x-mcp::app>
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.

Eine App aus einem Tool rendern

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.
<?php

namespace 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.

Sichtbarkeit von App-Tools

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.
use Laravel\Mcp\Server\Attributes\RendersApp;
use Laravel\Mcp\Server\Ui\Enums\Visibility;

#[RendersApp(resource: WeatherDashboardApp::class, visibility: [Visibility::App])]
class GetWeatherData extends Tool
{
    // ...
}
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].

App-Konfiguration

Ü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.
use Laravel\Mcp\Server\Attributes\AppMeta;
use Laravel\Mcp\Server\Ui\Enums\Library;
use Laravel\Mcp\Server\Ui\Enums\Permission;

#[AppMeta(
    connectDomains: ['https://api.weather.com'],
    permissions: [Permission::Geolocation],
    libraries: [Library::Tailwind, Library::Alpine],
)]
class WeatherDashboardApp extends AppResource
{
    // ...
}
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.

App-Entwicklung mit Boost

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.

Metadaten

Sie können Antworten von Tools, Ressourcen und Prompts das im MCP-Standard vorgesehene Feld _meta beifügen.
// Metadaten zum Response-Inhalt
return Response::text('The weather is sunny.')
    ->withMeta(['source' => 'weather-api', 'cached' => true]);
Wenn Sie Metadaten dem gesamten Response-Umschlag hinzufügen möchten, verwenden Sie Response::make.
return Response::make(
    Response::text('The weather is sunny.')
)->withMeta(['request_id' => '12345']);
Um der Klasse selbst – also einem Tool, einer Ressource oder einem Prompt – Metadaten zuzuweisen, definieren Sie die Eigenschaft $meta.
class CurrentWeatherTool extends Tool
{
    protected ?array $meta = [
        'version' => '2.0',
        'author' => 'Weather Team',
    ];
}

Icons

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.
use Laravel\Mcp\Enums\IconTheme;
use Laravel\Mcp\Server\Attributes\Icon;

#[Icon('mcp/server.png', mimeType: 'image/png', sizes: ['48x48'])]
#[Icon('mcp/server-dark.svg', theme: IconTheme::Dark)]
class WeatherServer extends Server
{
    // ...
}
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.

Authentifizierung

Webserver können mit der Standard-Middleware von Laravel authentifiziert werden.

Sanctum

Token-Authentifizierung mit Laravel Sanctum. Der MCP-Client sendet den Header Authorization: Bearer <token>.
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::web('/mcp/weather', WeatherServer::class)
    ->middleware('auth:sanctum');

OAuth 2.1

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.
php artisan vendor:publish --tag=mcp-views
// AppServiceProvider::boot()
use Laravel\Passport\Passport;

Passport::authorizationView(function ($parameters) {
    return view('mcp.authorize', $parameters);
});

Autorisierung

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...
}

MCP-Clients

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.

Verbindung zu einem Server

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.
$client->connect();

$client->ping();

if ($client->connected()) {
    // ...
}

$client->disconnect();
Mit withTimeout können Sie das Request-Timeout anpassen.
$client = Client::web('https://mcp.example.com')->withTimeout(30);

Benannte Clients

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.

Client-Authentifizierung

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.
use Illuminate\Support\Facades\Auth;
use Laravel\Mcp\Client;

$client = Client::web('https://mcp.example.com')->withToken($token);

$client = Client::web('https://mcp.example.com')->withToken(
    fn () => Auth::user()->mcpToken(),
);
Für per OAuth 2.1 geschützte Server verwenden Sie withOAuth.
use Laravel\Mcp\Client;
use Laravel\Mcp\Facades\Mcp;

Mcp::registerClient('github', fn () => Client::web('https://mcp.example.com')->withOAuth(
    clientId: config('services.github_mcp.client_id'),
    clientSecret: config('services.github_mcp.client_secret'),
));
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.
return redirect()->route('mcp.oauth.github.connect');

Tools

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.
$tools = Mcp::client('github')->tools();

$result = $tools['current-weather']->call([
    'location' => 'New York',
]);
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.

Prompts

Mit der Methode prompts erhalten Sie die vom MCP-Server angebotenen Prompts. Sie werden als Collection mit dem Namen als Schlüssel zurückgegeben.
use Laravel\Mcp\Facades\Mcp;

$prompts = Mcp::client('github')->prompts();

foreach ($prompts as $prompt) {
    $prompt->name;
    $prompt->title;
    $prompt->description;
    $prompt->arguments;
}
Der Client übernimmt die Pagination automatisch und ruft alle Prompts ab. Mit dem Argument limit können Sie die abgerufene Anzahl begrenzen.
$prompts = Mcp::client('github')->prompts(limit: 10);
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)

Ressourcen

Mit der Methode resources erhalten Sie die vom MCP-Server angebotenen Ressourcen. Sie werden als Collection mit der URI als Schlüssel zurückgegeben.
use Laravel\Mcp\Facades\Mcp;

$resources = Mcp::client('github')->resources();

foreach ($resources as $resource) {
    $resource->uri;
    $resource->name;
    $resource->title;
    $resource->description;
    $resource->mimeType;
    $resource->size;
}
Der Client übernimmt die Pagination automatisch und ruft alle Ressourcen ab. Mit dem Argument limit können Sie die abgerufene Anzahl begrenzen.
$resources = Mcp::client('github')->resources(limit: 10);
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)

Tests

MCP Inspector

Zum Testen eines MCP-Servers eignet sich das interaktive Debugging-Tool „MCP Inspector“.
# Webserver
php 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.

Unit-Tests

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.');
}
Prompts und Ressourcen testen Sie analog.
$response = WeatherServer::prompt(DescribeWeatherPrompt::class, ['tone' => 'casual']);
$response = WeatherServer::resource(WeatherGuidelinesResource::class);
Um als authentifizierter Benutzer auszuführen, verwenden Sie actingAs.
$response = WeatherServer::actingAs($user)->tool(CurrentWeatherTool::class, [...]);
Wichtige Assertion-Methoden sind:
$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.
$response->assertSentNotification('processing/progress', [
    'step' => 1,
    'total' => 5,
]);

$response->assertSentNotification('processing/progress', [
    'step' => 2,
    'total' => 5,
]);

$response->assertNotificationCount(5);
Zum Debuggen des Antwort-Inhalts nutzen Sie die Methoden dd bzw. dump.
$response->dd();
$response->dump();
Zuletzt geändert am 13. Juli 2026