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

> LaravelアプリケーションにModel Context Protocol(MCP)サーバーを組み込む方法を学びましょう。AI コーディングエージェントがあなたのアプリケーションと対話できるツール、リソース、プロンプトを定義できます。

## MCPとは

**Model Context Protocol（MCP）** は、AIクライアント（Claude、Cursor、GitHub Copilotなど）とアプリケーションが標準化されたプロトコルで通信するための仕様です。MCPサーバーを実装することで、AIエージェントはあなたのLaravelアプリケーションのデータにアクセスしたり、アクションを実行したりできるようになります。

<Info>
  Laravel MCP は Laravel 13 で追加された公式パッケージです。`laravel/mcp` として提供されており、MCPサーバーの構築に必要な一連の機能を提供します。
</Info>

MCPサーバーが提供できる機能は主に3つです。

| 機能                  | 説明                               |
| ------------------- | -------------------------------- |
| **ツール（Tools）**      | AIクライアントが呼び出せる関数。検索・更新・外部API連携など |
| **リソース（Resources）** | AIクライアントが読み込めるデータやコンテキスト情報       |
| **プロンプト（Prompts）**  | 再利用可能なプロンプトテンプレート                |

## インストール

Composerでパッケージをインストールします。

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

インストール後、`vendor:publish` Artisanコマンドを実行して `routes/ai.php` ファイルを生成します。

```shell theme={null}
php artisan vendor:publish --tag=ai-routes
```

このコマンドにより `routes/ai.php` ファイルが作成されます。ここにMCPサーバーの登録を記述します。

## サーバーの作成

`make:mcp-server` Artisanコマンドでサーバークラスを生成します。

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

`app/Mcp/Servers` ディレクトリにサーバークラスが生成されます。

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

### サーバーの登録

サーバーを作成したら `routes/ai.php` で登録します。登録方法には **Webサーバー** と **ローカルサーバー** の2種類があります。

#### Webサーバー

WebサーバーはHTTP POSTリクエストでアクセスできます。リモートのAIクライアントやWebベースの連携に最適です。

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

Mcp::web('/mcp/weather', WeatherServer::class);
```

通常のルートと同様にミドルウェアを適用できます。

```php theme={null}
Mcp::web('/mcp/weather', WeatherServer::class)
    ->middleware(['throttle:mcp']);
```

#### ローカルサーバー

ローカルサーバーはArtisanコマンドとして動作します。Claude DesktopなどのローカルAIクライアントとの連携に使います。

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

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

<Tip>
  ローカルサーバーは通常、MCPクライアントが自動的に起動します。手動で `mcp:start` Artisanコマンドを実行する必要はありません。
</Tip>

## ツール

ツールはAIクライアントが呼び出せる関数です。データの取得、外部APIとの連携、データベースの操作などを実装できます。

### ツールの作成

`make:mcp-tool` Artisanコマンドでツールクラスを生成します。

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

作成したツールをサーバーの `$tools` プロパティに登録します。

```php theme={null}
use App\Mcp\Tools\CurrentWeatherTool;
use Laravel\Mcp\Server;

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

基本的なツールクラスの実装例です。

```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');

        // 天気データを取得する...

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

### ツールの名前と説明

クラス名からデフォルトの名前とタイトルが自動生成されます。`CurrentWeatherTool` であれば名前は `current-weather`、タイトルは `Current Weather Tool` になります。`Name` と `Title` 属性でカスタマイズできます。

```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>
  ツールの説明（`Description`）は自動生成されません。AIモデルがツールの使い方を理解するために必須なので、必ず意味のある説明を設定してください。
</Warning>

### 入力スキーマ

`schema` メソッドで入力パラメーターのスキーマを定義します。LaravelのJSONスキーマビルダーを使って型や制約を指定できます。

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

### 出力スキーマ

`outputSchema` メソッドでレスポンスの構造を定義できます。AIクライアントがレスポンスを解析しやすくなります。

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

### バリデーション

`handle` メソッド内でLaravelの標準バリデーション機能を使えます。

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

    // バリデーション済みのデータを使って処理を行う...
}
```

<Tip>
  バリデーション失敗時にAIクライアントはエラーメッセージを参考に再試行します。具体的で実行可能なエラーメッセージを提供してください。
</Tip>

### 依存性注入

Laravelのサービスコンテナを通じてツールが解決されるため、コンストラクタや `handle` メソッドで依存関係を型ヒントできます。

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

### アノテーション

ツールにアノテーションを追加することで、AIクライアントにツールの振る舞いに関する追加情報を提供できます。

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

利用可能なアノテーションは次のとおりです。

| アノテーション            | 説明                           |
| ------------------ | ---------------------------- |
| `#[IsReadOnly]`    | ツールが環境を変更しないことを示す            |
| `#[IsDestructive]` | ツールが破壊的な更新を行う可能性があることを示す     |
| `#[IsIdempotent]`  | 同じ引数で繰り返し呼び出しても副作用がないことを示す   |
| `#[IsOpenWorld]`   | ツールが外部エンティティと対話する可能性があることを示す |

### 条件付き登録

`shouldRegister` メソッドを実装することで、実行時にツールを条件付きで登録できます。

```php theme={null}
public function shouldRegister(Request $request): bool
{
    return $request?->user()?->subscribed() ?? false;
}
```

`false` を返すとそのツールはAIクライアントから見えなくなります。

### レスポンス

ツールは `Laravel\Mcp\Response` のインスタンスを返す必要があります。

<AccordionGroup>
  <Accordion title="テキストレスポンス">
    ```php theme={null}
    return Response::text('Weather Summary: Sunny, 22°C');
    ```
  </Accordion>

  <Accordion title="エラーレスポンス">
    ```php theme={null}
    return Response::error('Unable to fetch weather data. Please try again.');
    ```
  </Accordion>

  <Accordion title="画像・音声レスポンス">
    ```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');

    // ストレージから直接読み込む（MIMEタイプは自動検出）
    return Response::fromStorage('weather/radar.png');
    ```
  </Accordion>

  <Accordion title="複数コンテンツのレスポンス">
    ```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="構造化レスポンス">
    AIクライアントが解析しやすい構造化データを返します。

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

  <Accordion title="ストリーミングレスポンス">
    長時間かかる処理で途中経過をリアルタイム送信します。

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

## プロンプト

プロンプトは再利用可能なプロンプトテンプレートです。AIクライアントが言語モデルと対話する際に使う定型的なクエリを標準化した形で提供できます。

### プロンプトの作成

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

サーバーの `$prompts` プロパティに登録します。

```php theme={null}
use App\Mcp\Prompts\DescribeWeatherPrompt;

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

### プロンプトの引数

`arguments` メソッドでプロンプトのパラメーターを定義します。

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

### バリデーション

プロンプトの引数は定義に基づいて自動的にバリデーションされますが、より複雑なバリデーションルールを適用することもできます。

Laravel MCP は Laravel の[バリデーション機能](/validation) とシームレスに連携します。プロンプトの `handle` メソッド内で引数をバリデーションできます。

```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'];

        // 指定された tone を使ってプロンプトを生成する...
    }
}
```

バリデーション失敗時、AIクライアントはエラーメッセージを参考に再試行します。具体的で実行可能なメッセージを提供してください。

```php theme={null}
$validated = $request->validate([
    'tone' => ['required', 'string', 'max:50'],
], [
    'tone.*' => 'toneを指定してください。例: "formal"、"casual"、"humorous"。',
]);
```

### 依存性注入

Laravelのサービスコンテナを通じてプロンプトが解決されるため、コンストラクタや `handle` メソッドで依存関係を型ヒントできます。

```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,
    ) {}
}
```

`handle` メソッドでも型ヒントでき、サービスコンテナが自動的に解決・注入します。

```php theme={null}
public function handle(Request $request, WeatherRepository $weather): Response
{
    $isAvailable = $weather->isServiceAvailable();

    // ...
}
```

### 条件付き登録

`shouldRegister` メソッドを実装することで、実行時にプロンプトを条件付きで登録できます。

```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;
    }
}
```

`false` を返すとそのプロンプトはAIクライアントから見えなくなり、呼び出しもできなくなります。

### プロンプトのレスポンス

プロンプトの `handle` メソッドではユーザーメッセージとアシスタントメッセージを返せます。`asAssistant()` でアシスタント側のメッセージとして扱います。

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

## リソース

リソースはAIクライアントがコンテキストとして読み込めるデータや情報です。ドキュメント、設定情報、動的データなど、AIの応答品質を向上させる情報を提供できます。

### リソースの作成

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

サーバーの `$resources` プロパティに登録します。

```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とMIMEタイプ

デフォルトではクラス名からURIが自動生成されます（例: `weather://resources/weather-guidelines`）。`Uri` と `MimeType` 属性でカスタマイズできます。

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

### リソーステンプレート

URI変数を持つ動的リソースを定義するには `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');

        // ファイルコンテンツを取得して返す...

        return Response::text("File {$fileId} for user {$userId}");
    }
}
```

URIからの変数は自動的にリクエストに取り込まれ、`get` メソッドで取得できます。

### リソースのリクエスト

ツールやプロンプトとは異なり、リソースは入力スキーマや引数を定義できません。ただし、`handle` メソッド内でリクエストオブジェクトを通じてリクエスト情報にアクセスできます。

```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
    {
        // リクエスト情報にアクセスする...
    }
}
```

### リソースの依存性注入

Laravelのサービスコンテナを通じてリソースが解決されるため、コンストラクタや `handle` メソッドで依存関係を型ヒントできます。

```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,
    ) {}
}
```

`handle` メソッドでも型ヒントでき、サービスコンテナが自動的に解決・注入します。

```php theme={null}
public function handle(WeatherRepository $weather): Response
{
    return Response::text($weather->guidelines());
}
```

### リソースのアノテーション

リソースにはオーディエンス、優先度、最終更新日などのアノテーションを付けられます。

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

| アノテーション           | 型          | 説明                                              |
| ----------------- | ---------- | ----------------------------------------------- |
| `#[Audience]`     | Role または配列 | 対象オーディエンス（`Role::User`、`Role::Assistant`、または両方） |
| `#[Priority]`     | float      | 重要度スコア（0.0〜1.0）                                 |
| `#[LastModified]` | string     | ISO 8601形式の最終更新日時                               |

### リソースの条件付き登録

`shouldRegister` メソッドを実装することで、実行時にリソースを条件付きで登録できます。

```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;
    }
}
```

`false` を返すとそのリソースはAIクライアントから見えなくなり、アクセスもできなくなります。

### リソースのレスポンス

リソースは `Laravel\Mcp\Response` のインスタンスを返す必要があります。

テキストコンテンツには `text` メソッドを使います。

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

#### リソースリンクレスポンス

`resourceLink` メソッドでリソースリンクを返せます。埋め込みリソースとは異なり、AIクライアントが独立して取得するURIポインターを返します。

```php theme={null}
return Response::resourceLink(
    uri: 'file:///data/report.json',
    name: 'monthly-report',
    mimeType: 'application/json',
);
```

登録済みのリソースクラスまたはインスタンスを渡すこともできます。URI、名前、タイトル、説明、MIMEタイプを自動継承します。

```php theme={null}
return Response::resourceLink(new WeatherForecastResource);
```

#### Blobレスポンス

バイナリコンテンツを返すには `blob` メソッドを使います。MIMEタイプはリソースの `#[MimeType]` 属性で設定します。

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

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

#### エラーレスポンス

エラーを示す場合は `error` メソッドを使います。

```php theme={null}
return Response::error('指定された場所の天気データを取得できませんでした。');
```

## アプリ

Laravel MCPは[MCP Apps](https://modelcontextprotocol.io/extensions/apps/overview)をサポートしています。これはModel Context Protocolの拡張機能で、ツールがサポートされたホスト内のサンドボックスiframe上でインタラクティブなHTMLアプリケーションをレンダリングできるようにします。これにより、プレーンテキストのレスポンスを超えた、ダッシュボード、フォーム、ビジュアライゼーション、その他のリッチな体験を構築できます。

MCPアプリは以下の2つのパーツが連携して動作します。

* **アプリリソース** — アプリケーションの自己完結型HTMLを返します。
* **ツール** — `#[RendersApp]` アトリビュートを使ってアプリリソースにリンクされます。ツールが呼び出されると、ホストがリンクされたリソースを取得してレンダリングします。

### アプリリソースの作成

`make:mcp-app-resource` Artisanコマンドでアプリリソースを作成できます。

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

このコマンドは2つのファイルを作成します。`app/Mcp/Resources` 内のPHPクラスと `resources/views/mcp` 内のBladeビューです。ビュー名はクラス名から自動的に推測されます。たとえば `WeatherDashboardApp` は `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` はベースの `Resource` クラスを継承し、MCP Apps仕様で要求される `ui://` URIスキームと `text/html;profile=mcp-app` MIMEタイプを自動的に設定します。他のリソースと同様に、サーバーの `$resources` 配列に登録する必要があります。

生成されたBladeビューは `<x-mcp::app>` コンポーネントを使用します。このコンポーネントはクライアントサイドのMCP SDKがバンドルされた完全なHTMLドキュメントをレンダリングします。

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

`createMcpApp` グローバル関数はバンドルされたSDKが提供します。iframeのサーバーへの接続、ホストテーマの適用、`callServerTool`・`sendMessage`・`openLink`などのヘルパーやイベントコールバックの公開を処理します。完全なクライアントサイドAPIについては[MCP Apps仕様](https://modelcontextprotocol.io/extensions/apps/overview)を参照してください。

### ツールからアプリをレンダリング

アプリリソースを表示するには、`#[RendersApp]` アトリビュートを使ってツールにリンクします。ツールが呼び出されると、Laravel MCPはリソースのURIをツールのメタデータに含め、ホストがサンドボックスiframe内でアプリをレンダリングできるようにします。

```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>
  `AppResource` が登録されると、Laravel MCPは自動的に `io.modelcontextprotocol/ui` ケイパビリティをアドバタイズします。追加のサーバー設定は不要です。
</Info>

### アプリツールの可視性

各 `#[RendersApp]` ツールは `visibility` 引数で呼び出し元を制限できます。これは、UIがデータを読み込み・更新するために呼び出すプライベートなアプリ専用ツールを、モデルからは見えないようにする場合に便利です。

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

`Visibility` enumには `Model` と `App` の2つのケースがあり、デフォルトは両方です。UIが直接呼び出すバックエンドアクションには `[Visibility::App]` を、UIからツールを利用不可にするには `[Visibility::Model]` を使います。

### アプリの設定

アプリリソースの `#[AppMeta]` アトリビュートで、iframeのContent Security Policy、ブラウザ権限、ビューの `<head>` に含めるライブラリスクリプトを設定します。

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

`Library` enumには `Library::Tailwind` や `Library::Alpine` など一般的なフロントエンドライブラリのCDNスクリプトが事前設定されており、CDNオリジンは自動的にCSPにマージされます。`Permission` enumは `Camera`・`Microphone`・`Geolocation`・`ClipboardWrite` などのブラウザ権限をカバーします。

<Tip>
  動的な設定が必要な場合は、`Laravel\Mcp\Server\Ui` 名前空間の `AppMeta`・`Csp`・`Permissions` フルエントビルダーを使ってリソースの `appMeta` メソッドをオーバーライドします。
</Tip>

### Boostを使ったアプリ開発

Laravel MCPにはMCP Appsを構築するための専用[Boost](/jp/boost)スキルリファレンスが含まれています。Laravel Boostがインストールされていれば、AIコーディングエージェントが `mcp-development` スキルを呼び出し、アプリリソース、Bladeビュー、リンクされたツールを自動生成できます。

プロトコルの完全なリファレンス（クライアントサイドAPIやスキーマの詳細を含む）については、公式の[MCP Appsドキュメント](https://modelcontextprotocol.io/extensions/apps/overview)を参照してください。

## メタデータ

MCP仕様の `_meta` フィールドをツール、リソース、プロンプトのレスポンスに付加できます。

```php theme={null}
// レスポンスコンテンツへのメタデータ
return Response::text('The weather is sunny.')
    ->withMeta(['source' => 'weather-api', 'cached' => true]);
```

レスポンスエンベロープ全体にメタデータを付ける場合は `Response::make` を使います。

```php theme={null}
return Response::make(
    Response::text('The weather is sunny.')
)->withMeta(['request_id' => '12345']);
```

ツール・リソース・プロンプトのクラス自体にメタデータを付けるには `$meta` プロパティを定義します。

```php theme={null}
class CurrentWeatherTool extends Tool
{
    protected ?array $meta = [
        'version' => '2.0',
        'author' => 'Weather Team',
    ];
}
```

## アイコン

MCP クライアントはサーバーとそのプリミティブのアイコンを表示できます。`Icon` 属性を使ってサーバー、ツール、リソース、プロンプトにアイコンを宣言できます。

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

`Icon` 属性は繰り返し使用可能なため、異なるサイズやライト・ダークテーマのバリアントを提供するために複数のアイコンを宣言できます。

または、`icons` メソッドをオーバーライドしてプログラムでアイコンを定義することもできます。これはアイコンがランタイムの条件に依存する場合に便利です。

```php theme={null}
use Laravel\Mcp\Schema\Icon;

class CurrentWeatherTool extends Tool
{
    /**
     * ツールのアイコンを取得します。
     *
     * @return array<int, Icon>
     */
    public function icons(): array
    {
        return [
            Icon::from('mcp/tool.png', mimeType: 'image/png'),
        ];
    }
}
```

属性と `icons` メソッドで定義されたアイコンは自動的に結合されます。アイコンパスは以下のように解決されます。

* `https:` や `data:` などの URI スキームを持つパスはそのまま使用されます。
* 相対パスは Laravel の `asset` ヘルパーを使って URL に解決されます。

## 認証

WebサーバーはLaravelの標準的なミドルウェアで認証できます。

### Sanctum

[Laravel Sanctum](https://laravel.com/docs/sanctum) を使ったトークン認証です。MCPクライアントは `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

[Laravel Passport](https://laravel.com/docs/passport) を使ったOAuth認証です。より堅牢なセキュリティが必要な場合に適しています。

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

Mcp::oauthRoutes();

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

OAuth認証を使う場合は、Passportの認可ビューを公開してサービスプロバイダーに設定します。

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

## 認可

`$request->user()` で認証済みユーザーを取得し、ツールやリソース内で認可チェックを行えます。

```php theme={null}
public function handle(Request $request): Response
{
    if (! $request->user()->can('read-weather')) {
        return Response::error('Permission denied.');
    }

    // 処理を続行...
}
```

## MCP クライアント

Laravel MCP はサーバーの構築だけでなく、他の MCP サーバーへ接続するためのクライアントも提供しています。クライアントを使うことで、外部 MCP サーバーが公開するツールを発見・呼び出しできます。[AI エージェント](/ai-sdk#mcp-tools) に外部 MCP サーバーの機能を提供する際にとくに有用です。

### サーバーへの接続

HTTP でアクセスできる MCP サーバーには `Client::web` メソッドを使い、サーバーの URL を渡します。

```php theme={null}
use Laravel\Mcp\Client;

$client = Client::web('https://mcp.example.com');
```

コマンドとして起動するローカル MCP サーバーへは `Client::local` メソッドを使い、コマンドと引数を渡します。

```php theme={null}
use Laravel\Mcp\Client;

$client = Client::local('php', ['artisan', 'mcp:start']);
```

クライアントは遅延接続（lazy connect）し、ツールの一覧取得や呼び出しを初めて行うときに自動的に接続を確立します。接続を手動で管理する場合は `connect`、`connected`、`ping`、`disconnect` メソッドを使います。

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

$client->ping();

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

$client->disconnect();
```

`withTimeout` メソッドでリクエストタイムアウトをカスタマイズできます。

```php theme={null}
$client = Client::web('https://mcp.example.com')->withTimeout(30);
```

### 名前付きクライアント

クライアントを毎回構築するかわりに、再利用可能な名前付きクライアントを登録できます。通常は `Mcp` ファサードを使い、サービスプロバイダーの `boot` メソッド内で行います。

```php theme={null}
use Laravel\Mcp\Client;
use Laravel\Mcp\Facades\Mcp;

Mcp::registerClient('github', fn () => Client::web('https://mcp.example.com'));
```

登録後は名前でクライアントを解決できます。

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

$client = Mcp::client('github');
```

名前付きクライアントはリクエストごとに一度だけ解決され、リクエストライフサイクルの終了時に自動的に切断されます。

### クライアント認証

Bearer トークンで保護されている Web MCP サーバーへ接続するには `withToken` メソッドを使います。トークン文字列または遅延解決するクロージャを渡せます。

```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(),
);
```

[OAuth 2.1](#oauth) で保護されているサーバーには `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>
  MCP サーバーが[動的クライアント登録](https://datatracker.ietf.org/doc/html/rfc7591)をサポートしている場合、`clientId` と `clientSecret` は省略できます。クライアントが自動登録します。
</Info>

次に、`routes/ai.php` ファイルで名前付きクライアントの OAuth ルートを `oAuthRoutesFor` メソッドを使って登録します。渡すクロージャは認可コードとアクセストークンが交換された後にクライアント名と `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');
});
```

これにより2つの名前付きルートが登録されます。ユーザーを認可サーバーへリダイレクトする connect ルート（`mcp.oauth.{client}.connect`）と、認可コードを交換してハンドラーを呼び出す callback ルート（`mcp.oauth.{client}.callback`）です。どちらも `web` ミドルウェアグループを使います（`middleware` 引数で上書き可能）。

認可フローを開始するには、ユーザーを connect ルートへリダイレクトします。

```php theme={null}
return redirect()->route('mcp.oauth.github.connect');
```

### ツール

`tools` メソッドで MCP サーバーが公開するツールを取得します。名前をキーにしたコレクションとして返されます。

```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;
}
```

クライアントは自動的にページネーションを処理してすべてのツールを取得します。`limit` 引数で取得数を制限できます。

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

ツールを呼び出すには `callTool` メソッドを使い、ツール名と引数の配列を渡します。返される `ToolResult` インスタンスでレスポンスを取得します。

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

$result = Mcp::client('github')->callTool('current-weather', [
    'location' => 'New York',
]);

$result->text();             // レスポンスのテキストコンテンツ
(string) $result;            // text() と同等
$result->isError;            // ツールがエラーを報告したか
$result->structuredContent;  // 構造化コンテンツ（存在する場合）
```

一覧取得したツールインスタンスから直接呼び出すこともできます。

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

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

[Laravel AI SDK](/ai-sdk) でエージェントを構築している場合は、MCP クライアントのツールをエージェントに直接渡すことで、モデルがプロンプトへの応答中にそれらを呼び出せるようになります。詳細は AI SDK の [MCP ツール](/ai-sdk#mcp-tools) セクションを参照してください。

### プロンプト

`prompts` メソッドで MCP サーバーが公開するプロンプトを取得します。名前をキーにしたコレクションとして返されます。

```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;
}
```

クライアントは自動的にページネーションを処理してすべてのプロンプトを取得します。`limit` 引数で取得数を制限できます。

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

プロンプトを取得するには `getPrompt` メソッドを使い、プロンプト名と引数の配列を渡します。返される `PromptResult` インスタンスで生成されたメッセージを取得できます。

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

$result = Mcp::client('github')->getPrompt('describe-weather', [
    'location' => 'New York',
]);

$result->text();        // メッセージのテキストコンテンツ
(string) $result;       // text() と同等
$result->messages;      // プロンプトが返したメッセージ（生データ）
$result->description;   // プロンプトの説明（存在する場合）
```

### リソース

`resources` メソッドで MCP サーバーが公開するリソースを取得します。URI をキーにしたコレクションとして返されます。

```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;
}
```

クライアントは自動的にページネーションを処理してすべてのリソースを取得します。`limit` 引数で取得数を制限できます。

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

リソースを読み込むには `readResource` メソッドを使い、リソースの URI を渡します。返される `ResourceReadResult` インスタンスでリソースのコンテンツを取得できます。

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

$result = Mcp::client('github')->readResource('weather://guidelines');

$result->content();   // リソースのコンテンツ（base64 の blob は自動でデコード）
(string) $result;     // content() と同等
$result->mimeType();  // リソースの MIME タイプ（存在する場合）
$result->contents;    // リソースが返したコンテンツ（生データ）
```

## テスト

### MCP Inspector

MCPサーバーの動作確認には、インタラクティブなデバッグツール「MCP Inspector」を使います。

```shell theme={null}
# Webサーバー
php artisan mcp:inspector mcp/weather

# ローカルサーバー（名前が "weather" の場合）
php artisan mcp:inspector weather
```

コマンドを実行するとMCP Inspectorが起動し、クライアント設定をコピーできます。認証ミドルウェアを設定している場合は、Authorizationヘッダーを含めて接続してください。

### ユニットテスト

ツール・リソース・プロンプトに対してユニットテストを書けます。

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

プロンプトとリソースも同様にテストできます。

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

認証済みユーザーとして実行するには `actingAs` を使います。

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

主なアサーションメソッドは次のとおりです。

```php theme={null}
$response->assertOk();           // エラーがないことを確認
$response->assertSee('...');     // 特定のテキストが含まれることを確認
```

エラーの有無を検証するには `assertHasErrors` / `assertHasNoErrors` を使います。

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

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

$response->assertHasNoErrors();
```

ツール・リソース・プロンプトの名前やタイトル、説明を検証できます。

```php theme={null}
$response->assertName('current-weather');
$response->assertTitle('Current Weather Tool');
$response->assertDescription('Fetches the current weather forecast for a specified location.');
```

ストリーミングレスポンスのノーティフィケーションを検証するには `assertSentNotification` と `assertNotificationCount` を使います。

```php theme={null}
$response->assertSentNotification('processing/progress', [
    'step' => 1,
    'total' => 5,
]);

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

$response->assertNotificationCount(5);
```

レスポンスの内容をデバッグするには `dd` または `dump` メソッドを使います。

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


## Related topics

- [Laravel AI SDK](/jp/ai-sdk.md)
- [Laravel Console Starter](/jp/packages/laravel-console-starter/index.md)
- [MCP](/jp/packages/laravel-copilot-sdk/mcp.md)
- [Laravel Boost Custom Agent for PhpStorm with GitHub Copilot](/jp/packages/laravel-boost-phpstorm-copilot.md)
- [ツール](/jp/packages/laravel-copilot-sdk/tools.md)
