サイトを更新しています。 数日間、レイアウトや翻訳に不具合が出ることがあります。ドキュメントは引き続きご利用いただけます。表示が崩れている場合は、後ほど再読み込みしてください。

ホーム Laravel 10.x Laravel MCP

Laravel MCP

10.x 2026年3月7日

#はじめに

Laravel MCP は、AIクライアントが Model Context Protocol を通じてLaravelアプリケーションと簡潔かつ洗練された方法でやり取りできる仕組みを提供します。サーバー、ツール、リソース、プロンプトを定義するための表現力豊かで流暢なインターフェースを備え、AIによるアプリケーションとのインタラクションを可能にします。

#インストール

はじめに、Composerパッケージマネージャーを使ってLaravel MCPをプロジェクトにインストールしてください。

composer require laravel/mcp

#ルートの公開

Laravel MCPをインストールした後、vendor:publish Artisanコマンドを実行して、MCPサーバーを定義するための routes/ai.php ファイルを公開します。

php artisan vendor:publish --tag=ai-routes

このコマンドは、アプリケーションの routes ディレクトリに routes/ai.php ファイルを作成し、MCPサーバーの登録に使用します。

#サーバーの作成

make:mcp-server Artisanコマンドを使ってMCPサーバーを作成できます。サーバーは、ツール、リソース、プロンプトなどのMCP機能をAIクライアントに公開する中心的な通信ポイントとして機能します。

php artisan make:mcp-server WeatherServer

このコマンドは、app/Mcp/Servers ディレクトリに新しいサーバークラスを作成します。生成されたサーバークラスはLaravel MCPの基本クラス Laravel\Mcp\Server を継承し、ツール、リソース、プロンプトの登録用プロパティを提供します。

<?php

namespace App\Mcp\Servers;

use Laravel\Mcp\Server;

class WeatherServer extends Server
{
    /**
     * このMCPサーバーに登録されたツール。
     *
     * @var array<int, class-string<\Laravel\Mcp\Server\Tool>>
     */
    protected array $tools = [
        // ExampleTool::class,
    ];

    /**
     * このMCPサーバーに登録されたリソース。
     *
     * @var array<int, class-string<\Laravel\Mcp\Server\Resource>>
     */
    protected array $resources = [
        // ExampleResource::class,
    ];

    /**
     * このMCPサーバーに登録されたプロンプト。
     *
     * @var array<int, class-string<\Laravel\Mcp\Server\Prompt>>
     */
    protected array $prompts = [
        // ExamplePrompt::class,
    ];
}

#サーバー登録

サーバーを作成したら、routes/ai.php ファイルで登録してアクセス可能にする必要があります。Laravel MCPは、HTTPアクセス可能なサーバー用の web メソッドと、コマンドラインサーバー用の local メソッドの2つの登録方法を提供します。

#ウェブサーバー

ウェブサーバーは最も一般的なサーバータイプで、HTTP POSTリクエストでアクセス可能です。リモートのAIクライアントやウェブベースの統合に最適です。web メソッドを使ってウェブサーバーを登録します。

use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

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

通常のルートと同様に、ミドルウェアを適用してウェブサーバーを保護できます。

Mcp::web('/mcp/weather', WeatherServer::class)
    ->middleware(['throttle:mcp']);

#ローカルサーバー

ローカルサーバーはArtisanコマンドとして実行され、開発、テスト、ローカルAIアシスタント統合に適しています。local メソッドを使ってローカルサーバーを登録します。

use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;

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

登録後は通常、手動で mcp:start を実行する必要はありません。代わりにMCPクライアント(AIエージェント)にサーバーの起動を任せます。mcp:start コマンドはクライアントによって呼び出され、必要に応じてサーバーの起動と停止を管理します。

php artisan mcp:start weather

#ツール

ツールはサーバーがAIクライアントに提供する機能を公開します。言語モデルがアクションを実行したり、コードを動かしたり、外部システムと連携したりできます。

#ツールの作成

ツールを作成するには、make:mcp-tool Artisanコマンドを実行します。

php artisan make:mcp-tool CurrentWeatherTool

ツールを作成したら、サーバーの $tools プロパティに登録してください。

<?php

namespace App\Mcp\Servers;

use App\Mcp\Tools\CurrentWeatherTool;
use Laravel\Mcp\Server;

class WeatherServer extends Server
{
    /**
     * このMCPサーバーに登録されたツール。
     *
     * @var array<int, class-string<\Laravel\Mcp\Server\Tool>>
     */
    protected array $tools = [
        CurrentWeatherTool::class,
    ];
}

#ツール名、タイトル、説明

デフォルトでは、ツールの名前とタイトルはクラス名から派生します。例えば、CurrentWeatherTool は名前が current-weather、タイトルが Current Weather Tool になります。ツールの $name$title プロパティを定義してこれらをカスタマイズできます。

class CurrentWeatherTool extends Tool
{
    /**
     * ツールの名前。
     */
    protected string $name = 'get-optimistic-weather';

    /**
     * ツールのタイトル。
     */
    protected string $title = 'Get Optimistic Weather Forecast';

    // ...
}

ツールの説明は自動生成されません。必ず $description プロパティで意味のある説明を提供してください。

class CurrentWeatherTool extends Tool
{
    /**
     * ツールの説明。
     */
    protected string $description = 'Fetches the current weather forecast for a specified location.';

    //
}
Примечание

説明はツールのメタデータの重要な部分であり、AIモデルがツールの使用タイミングや方法を理解するのに役立ちます。

#ツール入力スキーマ

ツールは入力スキーマを定義して、AIクライアントから受け取る引数を指定できます。Laravelの Illuminate\JsonSchema\JsonSchema ビルダーを使って入力要件を定義してください。

<?php

namespace App\Mcp\Tools;

use Illuminate\JsonSchema\JsonSchema;
use Laravel\Mcp\Server\Tool;

class CurrentWeatherTool extends Tool
{
    /**
     * ツールの入力スキーマを取得します。
     *
     * @return array<string, JsonSchema>
     */
    public function schema(JsonSchema $schema): array
    {
        return [
            'location' => $schema->string()
                ->description('The location to get the weather for.')
                ->required(),

            'units' => $schema->enum(['celsius', 'fahrenheit'])
                ->description('The temperature units to use.')
                ->default('celsius'),
        ];
    }
}

#ツール引数のバリデーション

JSONスキーマ定義はツール引数の基本構造を提供しますが、より複雑なバリデーションルールを適用したい場合もあります。

Laravel MCPはLaravelのバリデーション機能とシームレスに統合されています。ツールの handle メソッド内で受け取った引数をバリデートできます。

<?php

namespace App\Mcp\Tools;

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Tool;

class CurrentWeatherTool extends Tool
{
    /**
     * ツールリクエストを処理します。
     */
    public function handle(Request $request): Response
    {
        $validated = $request->validate([
            'location' => 'required|string|max:100',
            'units' => 'in:celsius,fahrenheit',
        ]);

        // バリデート済みの引数を使って天気データを取得...
    }
}

バリデーションに失敗した場合、AIクライアントは提供したエラーメッセージに基づいて動作します。そのため、明確で実用的なエラーメッセージを提供することが重要です。

$validated = $request->validate([
    'location' => ['required','string','max:100'],
    'units' => 'in:celsius,fahrenheit',
],[
    'location.required' => 'You must specify a location to get the weather for. For example, "New York City" or "Tokyo".',
    'units.in' => 'You must specify either "celsius" or "fahrenheit" for the units.',
]);

#ツールの依存性注入

Laravelのサービスコンテナはすべてのツールの解決に使われます。そのため、ツールのコンストラクタで必要な依存関係を型宣言できます。宣言された依存関係は自動的に解決され、ツールインスタンスに注入されます。

<?php

namespace App\Mcp\Tools;

use App\Repositories\WeatherRepository;
use Laravel\Mcp\Server\Tool;

class CurrentWeatherTool extends Tool
{
    /**
     * 新しいツールインスタンスを作成します。
     */
    public function __construct(
        protected WeatherRepository $weather,
    ) {}

    // ...
}

コンストラクタ注入に加え、ツールの handle() メソッドでも依存関係を型宣言できます。サービスコンテナがメソッド呼び出し時に自動的に依存関係を解決し注入します。

<?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 handle(Request $request, WeatherRepository $weather): Response
    {
        $location = $request->get('location');

        $forecast = $weather->getForecastFor($location);

        // ...
    }
}

#ツールのアノテーション

ツールにアノテーションを追加して、AIクライアントに追加のメタデータを提供できます。これによりAIモデルがツールの動作や機能を理解しやすくなります。アノテーションは属性としてツールに付与します。

<?php

namespace App\Mcp\Tools;

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] boolean ツールが環境を変更しないことを示します。
#[IsDestructive] boolean ツールが破壊的な更新を行う可能性があることを示します(読み取り専用でない場合に意味があります)。
#[IsIdempotent] boolean 同じ引数で繰り返し呼び出しても追加の効果がないことを示します(読み取り専用でない場合)。
#[IsOpenWorld] boolean ツールが外部のエンティティとやり取りする可能性があることを示します。

#条件付きツール登録

ツールクラスで shouldRegister メソッドを実装することで、実行時に条件付きでツールを登録できます。このメソッドでアプリケーションの状態、設定、リクエストパラメータに基づいてツールの利用可否を判断します。

<?php

namespace App\Mcp\Tools;

use Laravel\Mcp\Request;
use Laravel\Mcp\Server\Tool;

class CurrentWeatherTool extends Tool
{
    /**
     * ツールを登録すべきか判定します。
     */
    public function shouldRegister(Request $request): bool
    {
        return $request?->user()?->subscribed() ?? false;
    }
}

ツールの shouldRegister メソッドが false を返す場合、そのツールは利用可能なツール一覧に表示されず、AIクライアントから呼び出せません。

#ツールのレスポンス

ツールは Laravel\Mcp\Response のインスタンスを返す必要があります。Responseクラスは様々なタイプのレスポンスを作成する便利なメソッドを提供します。

単純なテキストレスポンスには text メソッドを使います。

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;

/**
 * ツールリクエストを処理します。
 */
public function handle(Request $request): Response
{
    // ...

    return Response::text('Weather Summary: Sunny, 72°F');
}

ツール実行中にエラーが発生したことを示すには、error メソッドを使用します:

return Response::error('Unable to fetch weather data. Please try again.');

#複数コンテンツのレスポンス

ツールは複数のコンテンツを返す場合、Response インスタンスの配列を返すことで対応できます:

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;

/**
 * ツールリクエストを処理します。
 *
 * @return array<int, \Laravel\Mcp\Response>
 */
public function handle(Request $request): array
{
    // ...

    return [
        Response::text('Weather Summary: Sunny, 72°F'),
        Response::text('**Detailed Forecast**\n- Morning: 65°F\n- Afternoon: 78°F\n- Evening: 70°F')
    ];
}

#ストリーミングレスポンス

長時間実行される処理やリアルタイムデータのストリーミングには、handle メソッドから ジェネレーター を返すことができます。これにより、最終レスポンスの前に中間更新をクライアントに送信できます:

<?php

namespace App\Mcp\Tools;

use Generator;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Tool;

class CurrentWeatherTool extends Tool
{
    /**
     * ツールリクエストを処理します。
     *
     * @return \Generator<int, \Laravel\Mcp\Response>
     */
    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));
        }
    }
}

ウェブベースのサーバーを使用する場合、ストリーミングレスポンスは自動的に SSE(Server-Sent Events)ストリームを開き、各 yield されたメッセージをイベントとしてクライアントに送信します。

#プロンプト

プロンプト は、AIクライアントが言語モデルと対話するために使える再利用可能なプロンプトテンプレートをサーバーが共有できる機能です。共通のクエリや対話を構造化する標準的な方法を提供します。

#プロンプトの作成

プロンプトを作成するには、make:mcp-prompt Artisan コマンドを実行します:

php artisan make:mcp-prompt DescribeWeatherPrompt

プロンプトを作成したら、サーバーの $prompts プロパティに登録します:

<?php

namespace App\Mcp\Servers;

use App\Mcp\Prompts\DescribeWeatherPrompt;
use Laravel\Mcp\Server;

class WeatherServer extends Server
{
    /**
     * この MCP サーバーに登録されたプロンプト。
     *
     * @var array<int, class-string<\Laravel\Mcp\Server\Prompt>>
     */
    protected array $prompts = [
        DescribeWeatherPrompt::class,
    ];
}

#プロンプトの名前、タイトル、説明

デフォルトでは、プロンプトの名前とタイトルはクラス名から派生します。例えば、DescribeWeatherPrompt は名前が describe-weather、タイトルが Describe Weather Prompt になります。プロンプトの $name$title プロパティを定義してこれらをカスタマイズできます:

class DescribeWeatherPrompt extends Prompt
{
    /**
     * プロンプトの名前。
     */
    protected string $name = 'weather-assistant';

    /**
     * プロンプトのタイトル。
     */
    protected string $title = 'Weather Assistant Prompt';

    // ...
}

プロンプトの説明は自動生成されません。必ず $description プロパティを定義して意味のある説明を提供してください:

class DescribeWeatherPrompt extends Prompt
{
    /**
     * プロンプトの説明。
     */
    protected string $description = 'Generates a natural-language explanation of the weather for a given location.';

    //
}
Примечание

説明はプロンプトのメタデータの重要な部分であり、AIモデルがプロンプトの最適な使い方や利用タイミングを理解するのに役立ちます。

#プロンプト引数

プロンプトは、AIクライアントが特定の値でプロンプトテンプレートをカスタマイズできるように引数を定義できます。arguments メソッドで受け入れる引数を定義します:

<?php

namespace App\Mcp\Prompts;

use Laravel\Mcp\Server\Prompt;
use Laravel\Mcp\Server\Prompts\Argument;

class DescribeWeatherPrompt extends Prompt
{
    /**
     * プロンプトの引数を取得します。
     *
     * @return array<int, \Laravel\Mcp\Server\Prompts\Argument>
     */
    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 の バリデーション機能とシームレスに統合されています。プロンプトの handle メソッド内で受信した引数をバリデートできます:

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

        // 指定されたトーンを使ってプロンプトレスポンスを生成します...
    }
}

バリデーション失敗時、AIクライアントは提供されたエラーメッセージに基づいて動作します。そのため、明確で実用的なエラーメッセージを提供することが重要です:

$validated = $request->validate([
    'tone' => ['required','string','max:50'],
],[
    'tone.*' => 'You must specify a tone for the weather description. Examples include "formal", "casual", or "humorous".',
]);

#プロンプトの依存性注入

Laravel の サービスコンテナ はすべてのプロンプトの解決に使われます。そのため、プロンプトのコンストラクターで必要な依存性を型宣言できます。宣言された依存性は自動的に解決され、プロンプトインスタンスに注入されます:

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

namespace App\Mcp\Prompts;

use App\Repositories\WeatherRepository;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Prompt;

class DescribeWeatherPrompt extends Prompt
{
    /**
     * プロンプトリクエストを処理します。
     */
    public function handle(Request $request, WeatherRepository $weather): Response
    {
        $isAvailable = $weather->isServiceAvailable();

        // ...
    }
}

#条件付きプロンプト登録

プロンプトクラスに shouldRegister メソッドを実装することで、実行時に条件付きでプロンプトを登録できます。このメソッドでアプリケーションの状態や設定、リクエストパラメータに基づきプロンプトの利用可否を判断します:

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

プロンプトの shouldRegister メソッドが false を返す場合、そのプロンプトは利用可能なリストに表示されず、AIクライアントから呼び出せません。

#プロンプトレスポンス

プロンプトは単一の Laravel\Mcp\Response または Laravel\Mcp\Response インスタンスのイテラブルを返せます。これらのレスポンスはAIクライアントに送信されるコンテンツをカプセル化します:

<?php

namespace App\Mcp\Prompts;

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

class DescribeWeatherPrompt extends Prompt
{
    /**
     * プロンプトリクエストを処理します。
     *
     * @return array<int, \Laravel\Mcp\Response>
     */
    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 New York City?";

        return [
            Response::text($systemMessage)->asAssistant(),
            Response::text($userMessage),
        ];
    }
}

asAssistant() メソッドを使うと、レスポンスメッセージがAIアシスタントからのものとして扱われ、通常のメッセージはユーザー入力として扱われます。

#リソース

リソース は、AIクライアントが言語モデルと対話する際に参照できるデータやコンテンツをサーバーが公開するための機能です。ドキュメントや設定、AIレスポンスに役立つ静的・動的情報を共有できます。

#リソースの作成

リソースを作成するには、make:mcp-resource Artisan コマンドを実行します:

php artisan make:mcp-resource WeatherGuidelinesResource

リソースを作成したら、サーバーの $resources プロパティに登録します:

<?php

namespace App\Mcp\Servers;

use App\Mcp\Resources\WeatherGuidelinesResource;
use Laravel\Mcp\Server;

class WeatherServer extends Server
{
    /**
     * この MCP サーバーに登録されたリソース。
     *
     * @var array<int, class-string<\Laravel\Mcp\Server\Resource>>
     */
    protected array $resources = [
        WeatherGuidelinesResource::class,
    ];
}

#リソースの名前、タイトル、説明

デフォルトでは、リソースの名前とタイトルはクラス名から派生します。例えば、WeatherGuidelinesResource は名前が weather-guidelines、タイトルが Weather Guidelines Resource になります。リソースの $name$title プロパティを定義してこれらをカスタマイズできます:

class WeatherGuidelinesResource extends Resource
{
    /**
     * リソースの名前。
     */
    protected string $name = 'weather-api-docs';

    /**
     * リソースのタイトル。
     */
    protected string $title = 'Weather API Documentation';

    // ...
}

リソースの説明は自動生成されません。必ず $description プロパティを定義して意味のある説明を提供してください:

class WeatherGuidelinesResource extends Resource
{
    /**
     * リソースの説明。
     */
    protected string $description = 'Comprehensive guidelines for using the Weather API.';

    //
}
Примечание

説明はリソースのメタデータの重要な部分であり、AIモデルがリソースを効果的に使うタイミングや方法を理解するのに役立ちます。

#リソースのURIとMIMEタイプ

各リソースは一意のURIで識別され、AIクライアントがリソースのフォーマットを理解するためのMIMEタイプが関連付けられています。

デフォルトでは、リソースのURIは名前に基づいて生成されるため、WeatherGuidelinesResourceweather://resources/weather-guidelines というURIを持ちます。デフォルトのMIMEタイプは text/plain です。

リソースの $uri$mimeType プロパティを定義してこれらをカスタマイズできます:

<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Server\Resource;

class WeatherGuidelinesResource extends Resource
{
    /**
     * リソースのURI。
     */
    protected string $uri = 'weather://resources/guidelines';

    /**
     * リソースのMIMEタイプ。
     */
    protected string $mimeType = 'application/pdf';
}

URIとMIMEタイプは、AIクライアントがリソースの内容を適切に処理・解釈するのに役立ちます。

#リソースリクエスト

ツールやプロンプトとは異なり、リソースは入力スキーマや引数を定義できません。ただし、リソースの handle メソッド内でリクエストオブジェクトとやり取りすることは可能です。

<?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 の サービスコンテナ はすべてのリソースの解決に使われます。そのため、リソースのコンストラクタで必要な依存関係を型宣言できます。宣言された依存関係は自動的に解決され、リソースインスタンスに注入されます。

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

namespace App\Mcp\Resources;

use App\Repositories\WeatherRepository;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Resource;

class WeatherGuidelinesResource extends Resource
{
    /**
     * リソースリクエストを処理します。
     */
    public function handle(WeatherRepository $weather): Response
    {
        $guidelines = $weather->guidelines();

        return Response::text($guidelines);
    }
}

#条件付きリソース登録

リソースクラスに shouldRegister メソッドを実装することで、実行時に条件付きでリソースを登録できます。このメソッドでアプリケーションの状態や設定、リクエストパラメータに基づいてリソースの利用可否を判断できます。

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

リソースの shouldRegister メソッドが false を返す場合、そのリソースは利用可能なリソース一覧に表示されず、AIクライアントからアクセスできません。

#リソースのレスポンス

リソースは必ず Laravel\Mcp\Response のインスタンスを返す必要があります。Response クラスはさまざまなタイプのレスポンスを簡単に作成できるメソッドを提供します。

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

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;

/**
 * リソースリクエストを処理します。
 */
public function handle(Request $request): Response
{
    // ...

    return Response::text($weatherData);
}

#Blob レスポンス

Blob コンテンツを返すには、blob メソッドを使い、Blob コンテンツを渡します。

return Response::blob(file_get_contents(storage_path('weather/radar.png')));

Blob コンテンツを返す場合、MIMEタイプはリソースクラスの $mimeType プロパティの値によって決まります。

<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Server\Resource;

class WeatherGuidelinesResource extends Resource
{
    /**
     * リソースの MIME タイプ。
     */
    protected string $mimeType = 'image/png';

    //
}

#エラーレスポンス

リソース取得中にエラーが発生したことを示すには、error() メソッドを使います。

return Response::error('Unable to fetch weather data for the specified location.');

#認証

ウェブ MCP サーバーはルートと同様にミドルウェアで認証できます。これにより、サーバーの機能を使う前にユーザー認証が必要になります。

MCP サーバーへのアクセス認証には、Laravel Sanctum を使ったシンプルなトークン認証か、Authorization HTTP ヘッダー経由の任意の API トークン、または Laravel Passport を使った OAuth 認証のいずれかを利用できます。

#OAuth 2.1

ウェブベースの MCP サーバーを保護する最も堅牢な方法は、Laravel Passport を使った OAuth です。

OAuth で MCP サーバーを認証する場合、routes/ai.php ファイルで Mcp::oauthRoutes メソッドを呼び出して必要な OAuth2 のディスカバリとクライアント登録ルートを登録します。その後、routes/ai.phpMcp::web ルートに Passport の auth:api ミドルウェアを適用します。

use App\Mcp\Servers\WeatherExample;
use Laravel\Mcp\Facades\Mcp;

Mcp::oauthRoutes();

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

#新規 Passport インストール

アプリケーションでまだ Laravel Passport を使っていない場合は、まず Passport の インストールとデプロイ手順 に従ってください。OAuthenticatable モデル、新しい認証ガード、Passport キーが必要です。

次に、Laravel MCP が提供する Passport の認可ビューを公開します。

php artisan vendor:publish --tag=mcp-views

その後、Passport::authorizationView メソッドを使ってこのビューを Passport に設定します。通常、このメソッドはアプリケーションの AppServiceProviderboot メソッド内で呼び出します。

use Laravel\Passport\Passport;

/**
 * アプリケーションサービスをブートストラップします。
 */
public function boot(): void
{
    Passport::authorizationView(function ($parameters) {
        return view('mcp.authorize', $parameters);
    });
}

このビューは認証時にエンドユーザーに表示され、AIエージェントの認証試行を拒否または承認します。

Authorization screen example

Примечание

このケースでは、OAuth を基盤となる認証可能モデルへの変換レイヤーとして単純に使っています。スコープなど OAuth の多くの側面は無視しています。

#既存の Passport インストールを使う場合

アプリケーションで既に Laravel Passport を使っている場合、Laravel MCP は既存の Passport 環境で問題なく動作しますが、OAuth は主に認証可能モデルへの変換レイヤーとして使われるため、カスタムスコープは現在サポートされていません。

Laravel MCP は上記の Mcp::oauthRoutes() メソッドを通じて、単一の mcp:use スコープを追加、宣伝、使用します。

#Passport と Sanctum の比較

OAuth2.1 は Model Context Protocol 仕様で推奨されている認証方式で、MCP クライアント間で最も広くサポートされています。そのため、可能な限り Passport の使用を推奨します。

すでに Sanctum を使っている場合、Passport の追加は手間になるかもしれません。その場合は、OAuth のみをサポートする MCP クライアントを使う必要が明確になるまで、Passport を使わずに Sanctum を使うことを推奨します。

#Sanctum

Sanctum を使って MCP サーバーを保護したい場合は、routes/ai.php ファイルでサーバーに Sanctum の認証ミドルウェアを追加してください。その後、MCP クライアントが Authorization: Bearer <token> ヘッダーを送信して認証を成功させる必要があります。

use App\Mcp\Servers\WeatherExample;
use Laravel\Mcp\Facades\Mcp;

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

#カスタム MCP 認証

アプリケーションが独自のカスタム API トークンを発行している場合、Mcp::web ルートに任意のミドルウェアを割り当てて MCP サーバーを認証できます。カスタムミドルウェアで Authorization ヘッダーを手動で検査して MCP リクエストを認証してください。

#認可

現在認証されているユーザーには $request->user() メソッドでアクセスでき、MCP のツールやリソース内で 認可チェック を行えます。

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;

/**
 * ツールリクエストを処理します。
 */
public function handle(Request $request): Response
{
    if (! $request->user()->can('read-weather')) {
        return Response::error('Permission denied.');
    }

    // ...
}

#サーバーテスト

組み込みの MCP Inspector を使うか、ユニットテストを書くことで MCP サーバーをテストできます。

#MCP Inspector

MCP Inspector は MCP サーバーのテストとデバッグ用のインタラクティブツールです。サーバーに接続し、認証を確認し、ツール、リソース、プロンプトを試せます。

登録済みの任意のサーバー(例:ローカルサーバーの "weather")でインスペクターを起動できます。

php artisan mcp:inspector weather

このコマンドは MCP Inspector を起動し、MCP クライアントにコピーして設定できるクライアント設定を表示します。ウェブサーバーが認証ミドルウェアで保護されている場合は、接続時に Authorization ベアラートークンなど必要なヘッダーを含めてください。

#ユニットテスト

MCP サーバー、ツール、リソース、プロンプトのユニットテストを書けます。

はじめに、新しいテストケースを作成し、登録されているプリミティブを呼び出します。例えば、WeatherServer のツールをテストする場合:

test('tool', function () {
    $response = WeatherServer::tool(CurrentWeatherTool::class, [
        'location' => 'New York City',
        'units' => 'fahrenheit',
    ]);

    $response
        ->assertOk()
        ->assertSee('The current weather in New York City is 72°F and sunny.');
});
/**
 * ツールをテストします。
 */
public function test_tool(): void
{
    $response = WeatherServer::tool(CurrentWeatherTool::class, [
        'location' => 'New York City',
        'units' => 'fahrenheit',
    ]);

    $response
        ->assertOk()
        ->assertSee('The current weather in New York City is 72°F and sunny.');
}

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

$response = WeatherServer::prompt(...);
$response = WeatherServer::resource(...);

プリミティブを呼び出す前に actingAs メソッドをチェーンして認証済みユーザーとして振る舞うことも可能です。

$response = WeatherServer::actingAs($user)->tool(...);

レスポンスを受け取ったら、さまざまなアサーションメソッドで内容やステータスを検証できます。

レスポンスが成功していることは assertOk メソッドでアサートできます。これはレスポンスにエラーがないことを確認します。

$response->assertOk();

レスポンスに特定のテキストが含まれていることは assertSee メソッドでアサートできます。

$response->assertSee('The current weather in New York City is 72°F and sunny.');

レスポンスにエラーが含まれていることは assertHasErrors メソッドでアサートできます。

$response->assertHasErrors();

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

レスポンスにエラーが含まれていないことは assertHasNoErrors メソッドでアサートできます。

$response->assertHasNoErrors();

レスポンスに特定のメタデータが含まれていることは assertName(), assertTitle(), assertDescription() メソッドでアサートできます。

$response->assertName('current-weather');
$response->assertTitle('Current Weather Tool');
$response->assertDescription('Fetches the current weather forecast for a specified location.');

通知が送信されたことは assertSentNotificationassertNotificationCount メソッドでアサートできます。

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

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

$response->assertNotificationCount(5);

最後に、レスポンスの生の内容を調査したい場合は、dddump メソッドを使ってデバッグ出力できます。

$response->dd();
$response->dump();