> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel MCP

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

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

<Info>
  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.
</Info>

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                                          |
| **Prompts**                | Wiederverwendbare Prompt-Vorlagen                                                                      |

## Installation

Installieren Sie das Paket mit Composer.

```shell theme={null}
composer require laravel/mcp
```

Nach der Installation führen Sie den Artisan-Befehl `vendor:publish` aus, um die Datei `routes/ai.php` zu erzeugen.

```shell theme={null}
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`.

```shell theme={null}
php artisan make:mcp-server WeatherServer
```

Die Serverklasse wird im Verzeichnis `app/Mcp/Servers` angelegt.

```php theme={null}
<?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.

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::local('weather', WeatherServer::class);
```

<Tip>
  Ein lokaler Server wird üblicherweise vom MCP-Client automatisch gestartet. Sie müssen den Artisan-Befehl `mcp:start` nicht manuell ausführen.
</Tip>

## 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`.

```shell theme={null}
php artisan make:mcp-tool CurrentWeatherTool
```

Registrieren Sie das erzeugte Tool anschließend in der Eigenschaft `$tools` des Servers.

```php theme={null}
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 theme={null}
<?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.

```php theme={null}
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
{
    // ...
}
```

<Warning>
  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.
</Warning>

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

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
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...
}
```

<Tip>
  Schlägt die Validierung fehl, versucht es der KI-Client anhand der Fehlermeldung erneut. Stellen Sie konkrete, umsetzbare Fehlermeldungen bereit.
</Tip>

### 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 theme={null}
<?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.

```php theme={null}
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:

| Annotation         | Beschreibung                                                                                 |
| ------------------ | -------------------------------------------------------------------------------------------- |
| `#[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.

```php theme={null}
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.

<AccordionGroup>
  <Accordion title="Textantwort">
    ```php theme={null}
    return Response::text('Weather Summary: Sunny, 22°C');
    ```
  </Accordion>

  <Accordion title="Fehlerantwort">
    ```php theme={null}
    return Response::error('Unable to fetch weather data. Please try again.');
    ```
  </Accordion>

  <Accordion title="Bild- oder Audioantwort">
    ```php theme={null}
    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');
    ```
  </Accordion>

  <Accordion title="Antwort mit mehreren Inhalten">
    ```php theme={null}
    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"),
        ];
    }
    ```
  </Accordion>

  <Accordion title="Strukturierte Antwort">
    Sie geben strukturierte Daten zurück, die vom KI-Client leicht geparst werden können.

    ```php theme={null}
    return Response::structured([
        'temperature' => 22.5,
        'conditions' => 'Partly cloudy',
        'humidity' => 65,
    ]);
    ```
  </Accordion>

  <Accordion title="Streaming-Antwort">
    Bei länger laufenden Vorgängen können Sie den Fortschritt in Echtzeit übertragen.

    ```php theme={null}
    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));
        }
    }
    ```
  </Accordion>
</AccordionGroup>

## Prompts

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

### Einen Prompt erstellen

```shell theme={null}
php artisan make:mcp-prompt DescribeWeatherPrompt
```

Registrieren Sie den Prompt in der Eigenschaft `$prompts` des Servers.

```php theme={null}
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 theme={null}
<?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](/de/validation) von Laravel zusammen. Innerhalb der Methode `handle` können Sie die Argumente validieren.

```php theme={null}
<?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.

```php theme={null}
$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 theme={null}
<?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.

```php theme={null}
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 theme={null}
<?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 theme={null}
<?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

```shell theme={null}
php artisan make:mcp-resource WeatherGuidelinesResource
```

Registrieren Sie die Ressource in der Eigenschaft `$resources` des Servers.

```php theme={null}
use App\Mcp\Resources\WeatherGuidelinesResource;

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

```php theme={null}
<?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.

```php theme={null}
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 theme={null}
<?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 theme={null}
<?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 theme={null}
<?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.

```php theme={null}
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.

```php theme={null}
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
{
    // ...
}
```

| Annotation        | Typ             | Beschreibung                                            |
| ----------------- | --------------- | ------------------------------------------------------- |
| `#[Audience]`     | Role oder Array | Zielgruppe (`Role::User`, `Role::Assistant` oder beide) |
| `#[Priority]`     | float           | Wichtigkeitswert (0.0–1.0)                              |
| `#[LastModified]` | string          | Letztes Änderungsdatum im ISO-8601-Format               |

### Bedingte Registrierung von Ressourcen

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

```php theme={null}
<?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`.

```php theme={null}
return Response::text($weatherData);
```

#### Antworten mit Ressourcen-Link

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.

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
return Response::blob(file_get_contents(storage_path('weather/radar.png')));
```

```php theme={null}
#[MimeType('image/png')]
class WeatherGuidelinesResource extends Resource
{
    // ...
}
```

#### Fehlerantwort

Zum Signalisieren eines Fehlers verwenden Sie die Methode `error`.

```php theme={null}
return Response::error('Wetterdaten für den angegebenen Ort konnten nicht geladen werden.');
```

## Apps

Laravel MCP unterstützt [MCP Apps](https://modelcontextprotocol.io/extensions/apps/overview). 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.

```shell theme={null}
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 theme={null}
<?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.

```blade theme={null}
<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](https://modelcontextprotocol.io/extensions/apps/overview).

### 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 theme={null}
<?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.');
    }
}
```

<Info>
  Sobald eine `AppResource` registriert ist, wirbt Laravel MCP automatisch mit der Capability `io.modelcontextprotocol/ui`. Eine zusätzliche Server-Konfiguration ist nicht nötig.
</Info>

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

```php theme={null}
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.

```php theme={null}
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.

<Tip>
  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`.
</Tip>

### App-Entwicklung mit Boost

Laravel MCP enthält eine spezielle [Boost](/de/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](https://modelcontextprotocol.io/extensions/apps/overview).

## Metadaten

Sie können Antworten von Tools, Ressourcen und Prompts das im MCP-Standard vorgesehene Feld `_meta` beifügen.

```php theme={null}
// 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`.

```php theme={null}
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`.

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
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](https://laravel.com/docs/sanctum). Der MCP-Client sendet den Header `Authorization: Bearer <token>`.

```php theme={null}
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](https://laravel.com/docs/passport). Geeignet, wenn Sie eine robustere Sicherheit benötigen.

```php theme={null}
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.

```shell theme={null}
php artisan vendor:publish --tag=mcp-views
```

```php theme={null}
// 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.

```php theme={null}
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](/de/ai-sdk#mcp-tools) 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.

```php theme={null}
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.

```php theme={null}
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`.

```php theme={null}
$client->connect();

$client->ping();

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

$client->disconnect();
```

Mit `withTimeout` können Sie das Request-Timeout anpassen.

```php theme={null}
$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`.

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
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](#oauth) geschützte Server verwenden Sie `withOAuth`.

```php theme={null}
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'),
));
```

<Info>
  Wenn der MCP-Server [Dynamic Client Registration](https://datatracker.ietf.org/doc/html/rfc7591) unterstützt, können Sie `clientId` und `clientSecret` weglassen. Der Client registriert sich dann automatisch.
</Info>

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

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
$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.

```php theme={null}
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.

```php theme={null}
$tools = Mcp::client('github')->tools();

$result = $tools['current-weather']->call([
    'location' => 'New York',
]);
```

Wenn Sie mit dem [Laravel AI SDK](/de/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](/de/ai-sdk#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.

```php theme={null}
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.

```php theme={null}
$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.

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
$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.

```php theme={null}
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“.

```shell theme={null}
# 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.

<CodeGroup>
  ```php Pest theme={null}
  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.');
  });
  ```

  ```php PHPUnit theme={null}
  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.');
  }
  ```
</CodeGroup>

Prompts und Ressourcen testen Sie analog.

```php theme={null}
$response = WeatherServer::prompt(DescribeWeatherPrompt::class, ['tone' => 'casual']);
$response = WeatherServer::resource(WeatherGuidelinesResource::class);
```

Um als authentifizierter Benutzer auszuführen, verwenden Sie `actingAs`.

```php theme={null}
$response = WeatherServer::actingAs($user)->tool(CurrentWeatherTool::class, [...]);
```

Wichtige Assertion-Methoden sind:

```php theme={null}
$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`.

```php theme={null}
$response->assertHasErrors();

$response->assertHasErrors([
    'Something went wrong.',
]);

$response->assertHasNoErrors();
```

Sie können Name, Titel und Beschreibung eines Tools, einer Ressource oder eines Prompts verifizieren.

```php theme={null}
$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`.

```php theme={null}
$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`.

```php theme={null}
$response->dd();
$response->dump();
```


## Related topics

- [Einen MCP-Server mit Laravel bauen](/de/advanced/mcp-server.md)
- [Laravel AI SDK](/de/ai-sdk.md)
- [Laravel Console Starter](/de/packages/laravel-console-starter/index.md)
- [MCP](/de/packages/laravel-copilot-sdk/mcp.md)
- [Laravel Boost](/de/boost.md)
