MCPとは
Model Context Protocol(MCP) は、AIクライアント(Claude、Cursor、GitHub Copilotなど)とアプリケーションが標準化されたプロトコルで通信するための仕様です。MCPサーバーを実装することで、AIエージェントはあなたのLaravelアプリケーションのデータにアクセスしたり、アクションを実行したりできるようになります。
Laravel MCP は Laravel 13 で追加された公式パッケージです。laravel/mcp として提供されており、MCPサーバーの構築に必要な一連の機能を提供します。
MCPサーバーが提供できる機能は主に3つです。
機能 説明 ツール(Tools) AIクライアントが呼び出せる関数。検索・更新・外部API連携など リソース(Resources) AIクライアントが読み込めるデータやコンテキスト情報 プロンプト(Prompts) 再利用可能なプロンプトテンプレート
インストール
Composerでパッケージをインストールします。
composer require laravel/mcp
インストール後、vendor:publish Artisanコマンドを実行して routes/ai.php ファイルを生成します。
php artisan vendor:publish --tag=ai-routes
このコマンドにより routes/ai.php ファイルが作成されます。ここにMCPサーバーの登録を記述します。
サーバーの作成
make:mcp-server Artisanコマンドでサーバークラスを生成します。
php artisan make:mcp-server WeatherServer
app/Mcp/Servers ディレクトリにサーバークラスが生成されます。
<? 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ベースの連携に最適です。
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コマンドとして動作します。Claude DesktopなどのローカルAIクライアントとの連携に使います。
use App\Mcp\Servers\ WeatherServer ;
use Laravel\Mcp\Facades\ Mcp ;
Mcp :: local ( 'weather' , WeatherServer :: class );
ローカルサーバーは通常、MCPクライアントが自動的に起動します。手動で mcp:start Artisanコマンドを実行する必要はありません。
ツール
ツールはAIクライアントが呼び出せる関数です。データの取得、外部APIとの連携、データベースの操作などを実装できます。
ツールの作成
make:mcp-tool Artisanコマンドでツールクラスを生成します。
php artisan make:mcp-tool CurrentWeatherTool
作成したツールをサーバーの $tools プロパティに登録します。
use App\Mcp\Tools\ CurrentWeatherTool ;
use Laravel\Mcp\ Server ;
class WeatherServer extends Server
{
protected array $tools = [
CurrentWeatherTool :: class ,
];
}
基本的なツールクラスの実装例です。
<? 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 属性でカスタマイズできます。
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
{
// ...
}
ツールの説明(Description)は自動生成されません。AIモデルがツールの使い方を理解するために必須なので、必ず意味のある説明を設定してください。
入力スキーマ
schema メソッドで入力パラメーターのスキーマを定義します。LaravelのJSONスキーマビルダーを使って型や制約を指定できます。
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クライアントがレスポンスを解析しやすくなります。
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の標準バリデーション機能を使えます。
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.' ,
]);
// バリデーション済みのデータを使って処理を行う...
}
バリデーション失敗時にAIクライアントはエラーメッセージを参考に再試行します。具体的で実行可能なエラーメッセージを提供してください。
依存性注入
Laravelのサービスコンテナを通じてツールが解決されるため、コンストラクタや 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 __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クライアントにツールの振る舞いに関する追加情報を提供できます。
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 メソッドを実装することで、実行時にツールを条件付きで登録できます。
public function shouldRegister ( Request $request ) : bool
{
return $request ?-> user () ?-> subscribed () ?? false ;
}
false を返すとそのツールはAIクライアントから見えなくなります。
レスポンス
ツールは Laravel\Mcp\Response のインスタンスを返す必要があります。
return Response :: text ( 'Weather Summary: Sunny, 22°C' );
return Response :: error ( 'Unable to fetch weather data. Please try again.' );
return Response :: image ( file_get_contents ( storage_path ( 'weather/radar.png' )), 'image/png' );
return Response :: audio ( file_get_contents ( storage_path ( 'weather/alert.mp3' )), 'audio/mp3' );
// ストレージから直接読み込む(MIMEタイプは自動検出)
return Response :: fromStorage ( 'weather/radar.png' );
public function handle ( Request $request ) : array
{
return [
Response :: text ( 'Weather Summary: Sunny, 22°C' ),
Response :: text ( "**Detailed Forecast** \n - Morning: 18°C \n - Afternoon: 25°C" ),
];
}
AIクライアントが解析しやすい構造化データを返します。 return Response :: structured ([
'temperature' => 22.5 ,
'conditions' => 'Partly cloudy' ,
'humidity' => 65 ,
]);
長時間かかる処理で途中経過をリアルタイム送信します。 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 ));
}
}
プロンプト
プロンプトは再利用可能なプロンプトテンプレートです。AIクライアントが言語モデルと対話する際に使う定型的なクエリを標準化した形で提供できます。
プロンプトの作成
php artisan make:mcp-prompt DescribeWeatherPrompt
サーバーの $prompts プロパティに登録します。
use App\Mcp\Prompts\ DescribeWeatherPrompt ;
class WeatherServer extends Server
{
protected array $prompts = [
DescribeWeatherPrompt :: class ,
];
}
プロンプトの引数
arguments メソッドでプロンプトのパラメーターを定義します。
<? 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 のバリデーション機能 とシームレスに連携します。プロンプトの 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' ];
// 指定された tone を使ってプロンプトを生成する...
}
}
バリデーション失敗時、AIクライアントはエラーメッセージを参考に再試行します。具体的で実行可能なメッセージを提供してください。
$validated = $request -> validate ([
'tone' => [ 'required' , 'string' , 'max:50' ],
], [
'tone.*' => 'toneを指定してください。例: "formal"、"casual"、"humorous"。' ,
]);
依存性注入
Laravelのサービスコンテナを通じてプロンプトが解決されるため、コンストラクタや handle メソッドで依存関係を型ヒントできます。
<? 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 メソッドでも型ヒントでき、サービスコンテナが自動的に解決・注入します。
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 ;
}
}
false を返すとそのプロンプトはAIクライアントから見えなくなり、呼び出しもできなくなります。
プロンプトのレスポンス
プロンプトの handle メソッドではユーザーメッセージとアシスタントメッセージを返せます。asAssistant() でアシスタント側のメッセージとして扱います。
<? 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の応答品質を向上させる情報を提供できます。
リソースの作成
php artisan make:mcp-resource WeatherGuidelinesResource
サーバーの $resources プロパティに登録します。
use App\Mcp\Resources\ WeatherGuidelinesResource ;
class WeatherServer extends Server
{
protected array $resources = [
WeatherGuidelinesResource :: class ,
];
}
<? php
namespace App\Mcp\Resources ;
use Laravel\Mcp\ Request ;
use Laravel\Mcp\ Response ;
use Laravel\Mcp\Server\Attributes\ Description ;
use Laravel\Mcp\Server\ Resource ;
#[ Description ( 'Comprehensive guidelines for using the Weather API.' )]
class WeatherGuidelinesResource extends Resource
{
public function handle ( Request $request ) : Response
{
$guidelines = "# Weather API Guidelines \n\n - Always specify a location..." ;
return Response :: text ( $guidelines );
}
}
URIとMIMEタイプ
デフォルトではクラス名からURIが自動生成されます(例: weather://resources/weather-guidelines)。Uri と MimeType 属性でカスタマイズできます。
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
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
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
namespace App\Mcp\Resources ;
use App\Repositories\ WeatherRepository ;
use Laravel\Mcp\Server\ Resource ;
class WeatherGuidelinesResource extends Resource
{
public function __construct (
protected WeatherRepository $weather ,
) {}
}
handle メソッドでも型ヒントでき、サービスコンテナが自動的に解決・注入します。
public function handle ( WeatherRepository $weather ) : Response
{
return Response :: text ( $weather -> guidelines ());
}
リソースのアノテーション
リソースにはオーディエンス、優先度、最終更新日などのアノテーションを付けられます。
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
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 メソッドを使います。
return Response :: text ( $weatherData );
リソースリンクレスポンス
resourceLink メソッドでリソースリンクを返せます。埋め込みリソースとは異なり、AIクライアントが独立して取得するURIポインターを返します。
return Response :: resourceLink (
uri : 'file:///data/report.json' ,
name : 'monthly-report' ,
mimeType : 'application/json' ,
);
登録済みのリソースクラスまたはインスタンスを渡すこともできます。URI、名前、タイトル、説明、MIMEタイプを自動継承します。
return Response :: resourceLink ( new WeatherForecastResource );
Blobレスポンス
バイナリコンテンツを返すには blob メソッドを使います。MIMEタイプはリソースの #[MimeType] 属性で設定します。
return Response :: blob ( file_get_contents ( storage_path ( 'weather/radar.png' )));
#[ MimeType ( 'image/png' )]
class WeatherGuidelinesResource extends Resource
{
// ...
}
エラーレスポンス
エラーを示す場合は error メソッドを使います。
return Response :: error ( '指定された場所の天気データを取得できませんでした。' );
アプリ
Laravel MCPはMCP Apps をサポートしています。これはModel Context Protocolの拡張機能で、ツールがサポートされたホスト内のサンドボックスiframe上でインタラクティブなHTMLアプリケーションをレンダリングできるようにします。これにより、プレーンテキストのレスポンスを超えた、ダッシュボード、フォーム、ビジュアライゼーション、その他のリッチな体験を構築できます。
MCPアプリは以下の2つのパーツが連携して動作します。
アプリリソース — アプリケーションの自己完結型HTMLを返します。
ツール — #[RendersApp] アトリビュートを使ってアプリリソースにリンクされます。ツールが呼び出されると、ホストがリンクされたリソースを取得してレンダリングします。
アプリリソースの作成
make:mcp-app-resource Artisanコマンドでアプリリソースを作成できます。
php artisan make:mcp-app-resource WeatherDashboardApp
このコマンドは2つのファイルを作成します。app/Mcp/Resources 内のPHPクラスと resources/views/mcp 内のBladeビューです。ビュー名はクラス名から自動的に推測されます。たとえば WeatherDashboardApp は mcp.weather-dashboard-app にマッピングされます。
<? php
namespace App\Mcp\Resources ;
use Laravel\Mcp\ Request ;
use Laravel\Mcp\ Response ;
use Laravel\Mcp\Server\Attributes\ AppMeta ;
use Laravel\Mcp\Server\Attributes\ Description ;
use Laravel\Mcp\Server\ AppResource ;
#[ Description ( 'An interactive weather dashboard.' )]
#[ AppMeta ]
class WeatherDashboardApp extends AppResource
{
/**
* Handle the app resource request.
*/
public function handle ( Request $request ) : Response
{
return Response :: view ( 'mcp.weather-dashboard-app' , [
'title' => $this -> title (),
]);
}
}
AppResource はベースの Resource クラスを継承し、MCP Apps仕様で要求される ui:// URIスキームと text/html;profile=mcp-app MIMEタイプを自動的に設定します。他のリソースと同様に、サーバーの $resources 配列に登録する必要があります。
生成されたBladeビューは <x-mcp::app> コンポーネントを使用します。このコンポーネントはクライアントサイドのMCP SDKがバンドルされた完全なHTMLドキュメントをレンダリングします。
< 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仕様 を参照してください。
ツールからアプリをレンダリング
アプリリソースを表示するには、#[RendersApp] アトリビュートを使ってツールにリンクします。ツールが呼び出されると、Laravel MCPはリソースのURIをツールのメタデータに含め、ホストがサンドボックスiframe内でアプリをレンダリングできるようにします。
<? 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.' );
}
}
AppResource が登録されると、Laravel MCPは自動的に io.modelcontextprotocol/ui ケイパビリティをアドバタイズします。追加のサーバー設定は不要です。
アプリツールの可視性
各 #[RendersApp] ツールは visibility 引数で呼び出し元を制限できます。これは、UIがデータを読み込み・更新するために呼び出すプライベートなアプリ専用ツールを、モデルからは見えないようにする場合に便利です。
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> に含めるライブラリスクリプトを設定します。
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 などのブラウザ権限をカバーします。
動的な設定が必要な場合は、Laravel\Mcp\Server\Ui 名前空間の AppMeta・Csp・Permissions フルエントビルダーを使ってリソースの appMeta メソッドをオーバーライドします。
Boostを使ったアプリ開発
Laravel MCPにはMCP Appsを構築するための専用Boost スキルリファレンスが含まれています。Laravel Boostがインストールされていれば、AIコーディングエージェントが mcp-development スキルを呼び出し、アプリリソース、Bladeビュー、リンクされたツールを自動生成できます。
プロトコルの完全なリファレンス(クライアントサイドAPIやスキーマの詳細を含む)については、公式のMCP Appsドキュメント を参照してください。
メタデータ
MCP仕様の _meta フィールドをツール、リソース、プロンプトのレスポンスに付加できます。
// レスポンスコンテンツへのメタデータ
return Response :: text ( 'The weather is sunny.' )
-> withMeta ([ 'source' => 'weather-api' , 'cached' => true ]);
レスポンスエンベロープ全体にメタデータを付ける場合は Response::make を使います。
return Response :: make (
Response :: text ( 'The weather is sunny.' )
) -> withMeta ([ 'request_id' => '12345' ]);
ツール・リソース・プロンプトのクラス自体にメタデータを付けるには $meta プロパティを定義します。
class CurrentWeatherTool extends Tool
{
protected ? array $meta = [
'version' => '2.0' ,
'author' => 'Weather Team' ,
];
}
アイコン
MCP クライアントはサーバーとそのプリミティブのアイコンを表示できます。Icon 属性を使ってサーバー、ツール、リソース、プロンプトにアイコンを宣言できます。
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 メソッドをオーバーライドしてプログラムでアイコンを定義することもできます。これはアイコンがランタイムの条件に依存する場合に便利です。
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 を使ったトークン認証です。MCPクライアントは Authorization: Bearer <token> ヘッダーを送信します。
use App\Mcp\Servers\ WeatherServer ;
use Laravel\Mcp\Facades\ Mcp ;
Mcp :: web ( '/mcp/weather' , WeatherServer :: class )
-> middleware ( 'auth:sanctum' );
OAuth 2.1
Laravel Passport を使ったOAuth認証です。より堅牢なセキュリティが必要な場合に適しています。
use App\Mcp\Servers\ WeatherServer ;
use Laravel\Mcp\Facades\ Mcp ;
Mcp :: oauthRoutes ();
Mcp :: web ( '/mcp/weather' , WeatherServer :: class )
-> middleware ( 'auth:api' );
OAuth認証を使う場合は、Passportの認可ビューを公開してサービスプロバイダーに設定します。
php artisan vendor:publish --tag=mcp-views
// AppServiceProvider::boot()
use Laravel\Passport\ Passport ;
Passport :: authorizationView ( function ( $parameters ) {
return view ( 'mcp.authorize' , $parameters );
});
$request->user() で認証済みユーザーを取得し、ツールやリソース内で認可チェックを行えます。
public function handle ( Request $request ) : Response
{
if ( ! $request -> user () -> can ( 'read-weather' )) {
return Response :: error ( 'Permission denied.' );
}
// 処理を続行...
}
MCP クライアント
Laravel MCP はサーバーの構築だけでなく、他の MCP サーバーへ接続するためのクライアントも提供しています。クライアントを使うことで、外部 MCP サーバーが公開するツールを発見・呼び出しできます。AI エージェント に外部 MCP サーバーの機能を提供する際にとくに有用です。
サーバーへの接続
HTTP でアクセスできる MCP サーバーには Client::web メソッドを使い、サーバーの URL を渡します。
use Laravel\Mcp\ Client ;
$client = Client :: web ( 'https://mcp.example.com' );
コマンドとして起動するローカル MCP サーバーへは Client::local メソッドを使い、コマンドと引数を渡します。
use Laravel\Mcp\ Client ;
$client = Client :: local ( 'php' , [ 'artisan' , 'mcp:start' ]);
クライアントは遅延接続(lazy connect)し、ツールの一覧取得や呼び出しを初めて行うときに自動的に接続を確立します。接続を手動で管理する場合は connect、connected、ping、disconnect メソッドを使います。
$client -> connect ();
$client -> ping ();
if ( $client -> connected ()) {
// ...
}
$client -> disconnect ();
withTimeout メソッドでリクエストタイムアウトをカスタマイズできます。
$client = Client :: web ( 'https://mcp.example.com' ) -> withTimeout ( 30 );
名前付きクライアント
クライアントを毎回構築するかわりに、再利用可能な名前付きクライアントを登録できます。通常は Mcp ファサードを使い、サービスプロバイダーの boot メソッド内で行います。
use Laravel\Mcp\ Client ;
use Laravel\Mcp\Facades\ Mcp ;
Mcp :: registerClient ( 'github' , fn () => Client :: web ( 'https://mcp.example.com' ));
登録後は名前でクライアントを解決できます。
use Laravel\Mcp\Facades\ Mcp ;
$client = Mcp :: client ( 'github' );
名前付きクライアントはリクエストごとに一度だけ解決され、リクエストライフサイクルの終了時に自動的に切断されます。
クライアント認証
Bearer トークンで保護されている Web MCP サーバーへ接続するには withToken メソッドを使います。トークン文字列または遅延解決するクロージャを渡せます。
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 で保護されているサーバーには withOAuth メソッドを使います。
use Laravel\Mcp\ Client ;
use Laravel\Mcp\Facades\ Mcp ;
Mcp :: registerClient ( 'github' , fn () => Client :: web ( 'https://mcp.example.com' ) -> withOAuth (
clientId : config ( 'services.github_mcp.client_id' ),
clientSecret : config ( 'services.github_mcp.client_secret' ),
));
MCP サーバーが動的クライアント登録 をサポートしている場合、clientId と clientSecret は省略できます。クライアントが自動登録します。
次に、routes/ai.php ファイルで名前付きクライアントの OAuth ルートを oAuthRoutesFor メソッドを使って登録します。渡すクロージャは認可コードとアクセストークンが交換された後にクライアント名と TokenSet を受け取ります。
use Illuminate\Support\Facades\ Auth ;
use Laravel\Mcp\Client\OAuth\ TokenSet ;
use Laravel\Mcp\Facades\ Mcp ;
Mcp :: oAuthRoutesFor ( 'github' , function ( string $client , TokenSet $token ) {
Auth :: user () -> update ([
'github_mcp_token' => $token -> accessToken ,
]);
return redirect ( '/dashboard' );
});
これにより2つの名前付きルートが登録されます。ユーザーを認可サーバーへリダイレクトする connect ルート(mcp.oauth.{client}.connect)と、認可コードを交換してハンドラーを呼び出す callback ルート(mcp.oauth.{client}.callback)です。どちらも web ミドルウェアグループを使います(middleware 引数で上書き可能)。
認可フローを開始するには、ユーザーを connect ルートへリダイレクトします。
return redirect () -> route ( 'mcp.oauth.github.connect' );
ツール
tools メソッドで MCP サーバーが公開するツールを取得します。名前をキーにしたコレクションとして返されます。
use Laravel\Mcp\Facades\ Mcp ;
$tools = Mcp :: client ( 'github' ) -> tools ();
foreach ( $tools as $tool ) {
$tool -> name ;
$tool -> title ;
$tool -> description ;
$tool -> inputSchema ;
}
クライアントは自動的にページネーションを処理してすべてのツールを取得します。limit 引数で取得数を制限できます。
$tools = Mcp :: client ( 'github' ) -> tools ( limit : 10 );
ツールを呼び出すには callTool メソッドを使い、ツール名と引数の配列を渡します。返される ToolResult インスタンスでレスポンスを取得します。
use Laravel\Mcp\Facades\ Mcp ;
$result = Mcp :: client ( 'github' ) -> callTool ( 'current-weather' , [
'location' => 'New York' ,
]);
$result -> text (); // レスポンスのテキストコンテンツ
( string ) $result ; // text() と同等
$result -> isError ; // ツールがエラーを報告したか
$result -> structuredContent ; // 構造化コンテンツ(存在する場合)
一覧取得したツールインスタンスから直接呼び出すこともできます。
$tools = Mcp :: client ( 'github' ) -> tools ();
$result = $tools [ 'current-weather' ] -> call ([
'location' => 'New York' ,
]);
Laravel AI SDK でエージェントを構築している場合は、MCP クライアントのツールをエージェントに直接渡すことで、モデルがプロンプトへの応答中にそれらを呼び出せるようになります。詳細は AI SDK の MCP ツール セクションを参照してください。
プロンプト
prompts メソッドで MCP サーバーが公開するプロンプトを取得します。名前をキーにしたコレクションとして返されます。
use Laravel\Mcp\Facades\ Mcp ;
$prompts = Mcp :: client ( 'github' ) -> prompts ();
foreach ( $prompts as $prompt ) {
$prompt -> name ;
$prompt -> title ;
$prompt -> description ;
$prompt -> arguments ;
}
クライアントは自動的にページネーションを処理してすべてのプロンプトを取得します。limit 引数で取得数を制限できます。
$prompts = Mcp :: client ( 'github' ) -> prompts ( limit : 10 );
プロンプトを取得するには getPrompt メソッドを使い、プロンプト名と引数の配列を渡します。返される PromptResult インスタンスで生成されたメッセージを取得できます。
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 をキーにしたコレクションとして返されます。
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 引数で取得数を制限できます。
$resources = Mcp :: client ( 'github' ) -> resources ( limit : 10 );
リソースを読み込むには readResource メソッドを使い、リソースの URI を渡します。返される ResourceReadResult インスタンスでリソースのコンテンツを取得できます。
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」を使います。
# Webサーバー
php artisan mcp:inspector mcp/weather
# ローカルサーバー(名前が "weather" の場合)
php artisan mcp:inspector weather
コマンドを実行するとMCP Inspectorが起動し、クライアント設定をコピーできます。認証ミドルウェアを設定している場合は、Authorizationヘッダーを含めて接続してください。
ユニットテスト
ツール・リソース・プロンプトに対してユニットテストを書けます。
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.' );
});
プロンプトとリソースも同様にテストできます。
$response = WeatherServer :: prompt ( DescribeWeatherPrompt :: class , [ 'tone' => 'casual' ]);
$response = WeatherServer :: resource ( WeatherGuidelinesResource :: class );
認証済みユーザーとして実行するには actingAs を使います。
$response = WeatherServer :: actingAs ( $user ) -> tool ( CurrentWeatherTool :: class , [ ... ]);
主なアサーションメソッドは次のとおりです。
$response -> assertOk (); // エラーがないことを確認
$response -> assertSee ( '...' ); // 特定のテキストが含まれることを確認
エラーの有無を検証するには assertHasErrors / assertHasNoErrors を使います。
$response -> assertHasErrors ();
$response -> assertHasErrors ([
'Something went wrong.' ,
]);
$response -> assertHasNoErrors ();
ツール・リソース・プロンプトの名前やタイトル、説明を検証できます。
$response -> assertName ( 'current-weather' );
$response -> assertTitle ( 'Current Weather Tool' );
$response -> assertDescription ( 'Fetches the current weather forecast for a specified location.' );
ストリーミングレスポンスのノーティフィケーションを検証するには assertSentNotification と assertNotificationCount を使います。
$response -> assertSentNotification ( 'processing/progress' , [
'step' => 1 ,
'total' => 5 ,
]);
$response -> assertSentNotification ( 'processing/progress' , [
'step' => 2 ,
'total' => 5 ,
]);
$response -> assertNotificationCount ( 5 );
レスポンスの内容をデバッグするには dd または dump メソッドを使います。
$response -> dd ();
$response -> dump ();