Laravel MCP
簡介
Laravel MCP 提供了一種簡單且優雅的方式,讓 AI 用戶端能透過模型上下文協議 (Model Context Protocol) 與您的 Laravel 應用程式進行互動。它提供了一個具表現力且流暢的介面來定義伺服器、工具、資源與提示詞,從而實現由 AI 驅動的應用程式互動。
安裝
要開始使用,請使用 Composer 套件管理工具將 Laravel MCP 安裝到您的專案中:
composer require laravel/mcp發布路由
安裝 Laravel MCP 後,請執行 vendor:publish Artisan 命令來發布 routes/ai.php 檔案,您將在此檔案中定義您的 MCP 伺服器:
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 類別,並提供了用於配置伺服器以及註冊工具、資源與提示詞的屬性 (Attributes) 和屬性 (Properties):
<?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
{
/**
* The tools registered with this MCP server.
*
* @var array<int, class-string<\Laravel\Mcp\Server\Tool>>
*/
protected array $tools = [
// GetCurrentWeatherTool::class,
];
/**
* The resources registered with this MCP server.
*
* @var array<int, class-string<\Laravel\Mcp\Server\Resource>>
*/
protected array $resources = [
// WeatherGuidelinesResource::class,
];
/**
* The prompts registered with this MCP server.
*
* @var array<int, class-string<\Laravel\Mcp\Server\Prompt>>
*/
protected array $prompts = [
// DescribeWeatherPrompt::class,
];
}伺服器註冊
建立伺服器後,您必須在 routes/ai.php 檔案中註冊它才能使其可被存取。Laravel MCP 提供了兩種註冊伺服器的方法:web 用於可透過 HTTP 存取的伺服器,而 local 則用於命令列伺服器。
Web 伺服器
Web 伺服器是最常見的伺服器類型,可透過 HTTP POST 請求存取,因此非常適合遠端 AI 用戶端或基於 Web 的整合。請使用 web 方法來註冊 Web 伺服器:
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;
Mcp::web('/mcp/weather', WeatherServer::class);就像一般的路由一樣,您可以套用中介層來保護您的 Web 伺服器:
Mcp::web('/mcp/weather', WeatherServer::class)
->middleware(['throttle:mcp']);本地伺服器
本地伺服器以 Artisan 命令的形式執行,非常適合建立本地 AI 助手整合,例如 Laravel Boost。請使用 local 方法來註冊本地伺服器:
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;
Mcp::local('weather', WeatherServer::class);註冊完成後,您通常不需要手動執行 mcp:start Artisan 命令。相反地,請配置您的 MCP 用戶端 (AI 代理) 來啟動伺服器,或使用 MCP Inspector。
工具
工具讓您的伺服器能夠公開 AI 客戶端可以呼叫的功能。它們允許語言模型執行動作、執行程式碼或與外部系統互動:
<?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
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$location = $request->get('location');
// Get weather...
return Response::text('The weather is...');
}
/**
* Get the tool's input schema.
*
* @return array<string, \Illuminate\JsonSchema\Types\Type>
*/
public function schema(JsonSchema $schema): array
{
return [
'location' => $schema->string()
->description('The location to get the weather for.')
->required(),
];
}
}建立工具
要建立工具,請執行 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
{
/**
* The tools registered with this MCP server.
*
* @var array<int, class-string<\Laravel\Mcp\Server\Tool>>
*/
protected array $tools = [
CurrentWeatherTool::class,
];
}工具名稱、標題與描述
預設情況下,工具的名稱 (name) 和標題 (title) 是根據類別名稱衍生而來的。例如,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 屬性提供具意義的描述:
use Laravel\Mcp\Server\Attributes\Description;
#[Description('Fetches the current weather forecast for a specified location.')]
class CurrentWeatherTool extends Tool
{
//
}📌 備註
描述是工具元數據 (metadata) 的關鍵部分,因為它能幫助 AI 模型理解何時以及如何有效地使用該工具。
工具輸入結構 (Input Schemas)
工具可以定義輸入結構 (input schemas) 來指定它們接受來自 AI 客戶端的哪些引數。請使用 Laravel 的 Illuminate\Contracts\JsonSchema\JsonSchema 構建器來定義工具的輸入需求:
<?php
namespace App\Mcp\Tools;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Server\Tool;
class CurrentWeatherTool extends Tool
{
/**
* Get the tool's input schema.
*
* @return array<string, \Illuminate\JsonSchema\Types\Type>
*/
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'),
];
}
}工具輸出結構 (Output Schemas)
工具可以定義 輸出結構 (output schemas) 來指定回應的結構。這能讓需要可解析工具結果的 AI 客戶端獲得更好的整合體驗。使用 outputSchema 方法來定義工具的輸出結構:
<?php
namespace App\Mcp\Tools;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Server\Tool;
class CurrentWeatherTool extends Tool
{
/**
* Get the tool's output schema.
*
* @return array<string, \Illuminate\JsonSchema\Types\Type>
*/
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(),
];
}
}驗證工具引數
JSON Schema 定義為工具引數提供了基本結構,但您可能還希望強制執行更複雜的驗證規則。
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
{
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
$validated = $request->validate([
'location' => 'required|string|max:100',
'units' => 'in:celsius,fahrenheit',
]);
// Fetch weather data using the validated arguments...
}
}當驗證失敗時,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 服務容器 被用來解析所有工具。因此,您可以在建構子中對工具可能需要的任何依賴進行型別提示 (type-hint)。宣告的依賴將會自動被解析並注入到工具實例中:
<?php
namespace App\Mcp\Tools;
use App\Repositories\WeatherRepository;
use Laravel\Mcp\Server\Tool;
class CurrentWeatherTool extends Tool
{
/**
* Create a new tool instance.
*/
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
{
/**
* Handle the tool request.
*/
public function handle(Request $request, WeatherRepository $weather): Response
{
$location = $request->get('location');
$forecast = $weather->getForecastFor($location);
// ...
}
}工具註解 (Annotations)
您可以使用 註解 (Annotations) 來強化您的工具,以為 AI 客戶端提供額外的元數據。這些註解能幫助 AI 模型理解工具的行為與能力。註解透過屬性 (attributes) 添加到工具中:
<?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 | 表示該工具可能會與外部實體互動。 |
註解值可以使用布林引數明確設定:
use Laravel\Mcp\Server\Tools\Annotations\IsReadOnly;
use Laravel\Mcp\Server\Tools\Annotations\IsDestructive;
use Laravel\Mcp\Server\Tools\Annotations\IsOpenWorld;
use Laravel\Mcp\Server\Tools\Annotations\IsIdempotent;
use Laravel\Mcp\Server\Tool;
#[IsReadOnly(true)]
#[IsDestructive(false)]
#[IsOpenWorld(false)]
#[IsIdempotent(true)]
class CurrentWeatherTool extends Tool
{
//
}條件式工具註冊
您可以透過在工具類別中實作 shouldRegister 方法,在執行時條件式地註冊工具。此方法允許您根據應用程式狀態、配置或請求參數來決定工具是否應可用:
<?php
namespace App\Mcp\Tools;
use Laravel\Mcp\Request;
use Laravel\Mcp\Server\Tool;
class CurrentWeatherTool extends Tool
{
/**
* Determine if the tool should be registered.
*/
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;
/**
* Handle the tool request.
*/
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.');若要回傳圖片或音訊內容,請使用 image 與 audio 方法:
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');您也可以使用 fromStorage 方法直接從 Laravel 檔案系統磁碟載入圖片與音訊內容。MIME 類型將會從檔案中自動偵測:
return Response::fromStorage('weather/radar.png');如果需要,您可以指定特定的磁碟或覆寫 MIME 類型:
return Response::fromStorage('weather/radar.png', disk: 's3');
return Response::fromStorage('weather/radar.png', mimeType: 'image/webp');多重內容回應
工具可以透過回傳 Response 實例的陣列來回傳多個內容片段:
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
/**
* Handle the tool request.
*
* @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")
];
}結構化回應
工具可以使用 structured 方法回傳 結構化內容。這能為 AI 客戶端提供可解析的數據,同時保持與 JSON 編碼文字表示法的向下相容性:
return Response::structured([
'temperature' => 22.5,
'conditions' => 'Partly cloudy',
'humidity' => 65,
]);如果您需要在結構化內容旁提供自定義文字,請在回應工廠上使用 withStructuredContent 方法:
return Response::make(
Response::text('Weather is 22.5°C and sunny')
)->withStructuredContent([
'temperature' => 22.5,
'conditions' => 'Sunny',
]);串流回應
對於執行時間較長的操作或即時數據串流,工具可以在其 handle 方法中回傳一個 產生器 (generator)。這使得在最終回應之前,能向客戶端發送中間更新:
<?php
namespace App\Mcp\Tools;
use Generator;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Tool;
class CurrentWeatherTool extends Tool
{
/**
* Handle the tool request.
*
* @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));
}
}
}當使用 Web 伺服器時,串流回應會自動開啟一個 SSE (Server-Sent Events) 串流,將每個產生的訊息作為事件發送到客戶端。
提示詞
提示詞 讓您的伺服器能夠分享可重複使用的提示詞模板,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
{
/**
* The prompts registered with this MCP server.
*
* @var array<int, class-string<\Laravel\Mcp\Server\Prompt>>
*/
protected array $prompts = [
DescribeWeatherPrompt::class,
];
}提示詞名稱、標題與描述
預設情況下,提示詞的名稱與標題是根據類別名稱而來的。例如,DescribeWeatherPrompt 的名稱將會是 describe-weather,標題則是 Describe Weather Prompt。您可以使用 Name 與 Title 屬性來自定義這些值:
use Laravel\Mcp\Server\Attributes\Name;
use Laravel\Mcp\Server\Attributes\Title;
#[Name('weather-assistant')]
#[Title('Weather Assistant Prompt')]
class DescribeWeatherPrompt extends Prompt
{
// ...
}提示詞描述不會自動生成。您應該始終使用 Description 屬性提供具備意義的描述:
use Laravel\Mcp\Server\Attributes\Description;
#[Description('Generates a natural-language explanation of the weather for a given location.')]
class DescribeWeatherPrompt extends Prompt
{
//
}📌 備註
描述是提示詞元數據 (metadata) 的關鍵部分,因為它能幫助 AI 模型理解何時以及如何最有效地使用該提示詞。
提示詞引數
提示詞可以定義引數,讓 AI 用戶端能夠使用特定值來自定義提示詞模板。使用 arguments 方法來定義您的提示詞接受哪些引數:
<?php
namespace App\Mcp\Prompts;
use Laravel\Mcp\Server\Prompt;
use Laravel\Mcp\Server\Prompts\Argument;
class DescribeWeatherPrompt extends Prompt
{
/**
* Get the prompt's arguments.
*
* @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
{
/**
* Handle the prompt request.
*/
public function handle(Request $request): Response
{
$validated = $request->validate([
'tone' => 'required|string|max:50',
]);
$tone = $validated['tone'];
// Generate the prompt response using the given 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 服務容器 用於解析所有提示詞。因此,您可以在建構子中對提示詞可能需要的任何依賴進行類型提示 (type-hint)。宣告的依賴將會被自動解析並注入到提示詞實例中:
<?php
namespace App\Mcp\Prompts;
use App\Repositories\WeatherRepository;
use Laravel\Mcp\Server\Prompt;
class DescribeWeatherPrompt extends Prompt
{
/**
* Create a new prompt instance.
*/
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
{
/**
* Handle the prompt request.
*/
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
{
/**
* Determine if the prompt should be registered.
*/
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
{
/**
* Handle the prompt request.
*
* @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 用戶端在與語言模型互動時可以讀取並將其作為上下文 (context) 使用的資料與內容。它們提供了一種分享靜態或動態資訊的方式,例如文件、設定或任何有助於提供 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
{
/**
* The resources registered with this MCP server.
*
* @var array<int, class-string<\Laravel\Mcp\Server\Resource>>
*/
protected array $resources = [
WeatherGuidelinesResource::class,
];
}資源名稱、標題與描述
預設情況下,資源的名稱和標題是根據類別名稱衍生而來的。例如,WeatherGuidelinesResource 將具有 weather-guidelines 的名稱和 Weather Guidelines Resource 的標題。您可以使用 Name 和 Title 屬性來自定義這些值:
use Laravel\Mcp\Server\Attributes\Name;
use Laravel\Mcp\Server\Attributes\Title;
#[Name('weather-api-docs')]
#[Title('Weather API Documentation')]
class WeatherGuidelinesResource extends Resource
{
// ...
}資源描述不會自動生成。您應該始終使用 Description 屬性提供有意義的描述:
use Laravel\Mcp\Server\Attributes\Description;
#[Description('Comprehensive guidelines for using the Weather API.')]
class WeatherGuidelinesResource extends Resource
{
//
}📌 備註
描述是資源元數據的關鍵部分,因為它能幫助 AI 模型理解何時以及如何有效地使用該資源。
資源模板
資源模板 使您的伺服器能夠公開與帶有變數的 URI 模式相匹配的動態資源。您不需要為每個資源定義靜態 URI,而是可以建立一個單一資源,根據模板模式處理多個 URI。
建立資源模板
要建立資源模板,請在您的資源類別中實作 HasUriTemplate 介面,並定義一個返回 UriTemplate 實例的 uriTemplate 方法:
<?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
{
/**
* Get the URI template for this resource.
*/
public function uriTemplate(): UriTemplate
{
return new UriTemplate('file://users/{userId}/files/{fileId}');
}
/**
* Handle the resource request.
*/
public function handle(Request $request): Response
{
$userId = $request->get('userId');
$fileId = $request->get('fileId');
// Fetch and return the file content...
return Response::text($content);
}
}當資源實作 HasUriTemplate 介面時,它將被註冊為資源模板而非靜態資源。AI 客戶端隨後可以使用與模板模式相匹配的 URI 來請求資源,且 URI 中的變數將被自動提取,並可在資源的 handle 方法中使用。
URI 模板語法
URI 模板使用包含在花括號中的佔位符來定義 URI 中的變數片段:
new UriTemplate('file://users/{userId}');
new UriTemplate('file://users/{userId}/files/{fileId}');
new UriTemplate('https://api.example.com/{version}/{resource}/{id}');存取模板變數
當 URI 與您的資源模板匹配時,提取的變數會自動合併到請求中,並可以使用 get 方法來存取:
<?php
namespace App\Mcp\Resources;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Contracts\HasUriTemplate;
use Laravel\Mcp\Server\Resource;
use Laravel\Mcp\Support\UriTemplate;
class UserProfileResource extends Resource implements HasUriTemplate
{
public function uriTemplate(): UriTemplate
{
return new UriTemplate('file://users/{userId}/profile');
}
public function handle(Request $request): Response
{
// Access the extracted variable
$userId = $request->get('userId');
// Access the full URI if needed
$uri = $request->uri();
// Fetch user profile...
return Response::text("Profile for user {$userId}");
}
}Request 物件同時提供了提取的變數和被請求的原始 URI,讓您在處理資源請求時擁有完整的上下文。
資源 URI 與 MIME 類型
每個資源都由唯一的 URI 識別,並具有相關聯的 MIME 類型,以幫助 AI 客戶端理解資源的格式。
預設情況下,資源的 URI 是根據資源名稱生成的,因此 WeatherGuidelinesResource 將具有 weather://resources/weather-guidelines 的 URI。預設的 MIME 類型為 text/plain。
您可以使用 Uri 和 MimeType 屬性來自定義這些值:
<?php
namespace App\Mcp\Resources;
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 和 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
{
/**
* Handle the resource request.
*/
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
{
/**
* Create a new resource instance.
*/
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
{
/**
* Handle the resource request.
*/
public function handle(WeatherRepository $weather): Response
{
$guidelines = $weather->guidelines();
return Response::text($guidelines);
}
}資源註解 (Annotations)
您可以使用 註解 (annotations) 來增強您的資源,以向 AI 用戶端提供額外的元數據。註解透過屬性 (attributes) 新增至資源中:
<?php
namespace App\Mcp\Resources;
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
{
/**
* Determine if the resource should be registered.
*/
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;
/**
* Handle the resource request.
*/
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 類型將由您的資源所設定的 MIME 類型決定:
<?php
namespace App\Mcp\Resources;
use Laravel\Mcp\Server\Attributes\MimeType;
use Laravel\Mcp\Server\Resource;
#[MimeType('image/png')]
class WeatherGuidelinesResource extends Resource
{
//
}錯誤回應
若要表示在資源擷取過程中發生錯誤,請使用 error() 方法:
return Response::error('Unable to fetch weather data for the specified location.');元數據 (Metadata)
Laravel MCP 同樣支援 MCP 規範 中定義的 _meta 欄位,這是一些 MCP 用戶端或整合功能所要求的。元數據可以應用於所有 MCP 基礎元件 (primitives),包括工具、資源和提示詞,以及它們的回應。
您可以使用 withMeta 方法將元數據附加到單個回應內容中:
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
return Response::text('The weather is sunny.')
->withMeta(['source' => 'weather-api', 'cached' => true]);
}對於適用於整個回應封包 (response envelope) 的結果級元數據,請使用 Response::make 包裝您的回應,並在回傳的回應工廠實例上呼叫 withMeta:
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\ResponseFactory;
/**
* Handle the tool request.
*/
public function handle(Request $request): ResponseFactory
{
return Response::make(
Response::text('The weather is sunny.')
)->withMeta(['request_id' => '12345']);
}若要將元數據附加到工具、資源或提示詞本身,請在類別中定義 $meta 屬性:
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\Tool;
#[Description('Fetches the current weather forecast.')]
class CurrentWeatherTool extends Tool
{
protected ?array $meta = [
'version' => '2.0',
'author' => 'Weather Team',
];
// ...
}認證
就像路由一樣,您可以使用中介層來為 Web MCP 伺服器進行認證。為您的 MCP 伺服器加入認證後,使用者在利用伺服器的任何功能之前都必須先通過認證。
認證 MCP 伺服器存取權限有兩種方式:一種是透過 Laravel Sanctum 進行簡單的令牌認證,或是使用透過 Authorization HTTP 標頭傳遞的任何令牌。或者,您可以使用 Laravel Passport 透過 OAuth 進行認證。
OAuth 2.1
保護 Web MCP 伺服器最穩健的方法是使用 Laravel Passport 進行 OAuth 認證。
當您透過 OAuth 對 MCP 伺服器進行認證時,請在 routes/ai.php 檔案中呼叫 Mcp::oauthRoutes 方法,以註冊所需的 OAuth2 探索與用戶端註冊路由。接著,將 Passport 的 auth:api 中介層應用至 routes/ai.php 檔案中的 Mcp::web 路由:
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 的 安裝與部署指南 將 Passport 加入您的應用程式。在繼續之前,您應該已經擁有 OAuthenticatable 模型、新的認證守衛以及 Passport 金鑰。
接下來,您應該發布 Laravel MCP 提供的 Passport 授權視圖:
php artisan vendor:publish --tag=mcp-views然後,使用 Passport::authorizationView 方法指示 Passport 使用此視圖。通常,此方法應該在應用程式 AppServiceProvider 的 boot 方法中呼叫:
use Laravel\Passport\Passport;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::authorizationView(function ($parameters) {
return view('mcp.authorize', $parameters);
});
}此視圖將在認證期間顯示給最終使用者,以拒絕或批准 AI 代理的認證嘗試。
📌 備註
在此情境中,我們僅將 OAuth 作為底層可認證模型的轉換層。我們忽略了 OAuth 的許多面向,例如範圍 (scopes)。
使用現有的 Passport 安裝
如果您的應用程式已經在使用 Laravel Passport,Laravel MCP 應該能與您現有的 Passport 安裝無縫協作,但目前不支援自定義範圍 (custom scopes),因為 OAuth 主要被用作底層可認證模型的轉換層。
Laravel MCP 透過上述的 Mcp::oauthRoutes 方法,新增、宣告並使用單一的 mcp:use 範圍。
Passport vs. 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');Custom MCP Authentication
如果您的應用程式發行自定義的 API 令牌,您可以透過將任何您想要的中介層分配給 Mcp::web 路由來對您的 MCP 伺服器進行認證。您的自定義中介層可以手動檢查 Authorization 標頭,以認證傳入的 MCP 請求。
授權
您可以透過 $request->user() 方法存取目前已認證的使用者,讓您能在 MCP 工具和資源中執行 授權檢查:
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
/**
* Handle the tool request.
*/
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 伺服器的互動式工具。您可以使用它來連接伺服器、驗證認證,並嘗試使用工具、資源和提示詞。
您可以針對任何已註冊的伺服器執行檢查器:
# Web server...
php artisan mcp:inspector mcp/weather
# Local server named "weather"...
php artisan mcp:inspector weather此命令會啟動 MCP Inspector 並提供客戶端設定,您可以將其複製到您的 MCP 客戶端中,以確保所有設定正確無誤。如果您的 Web 伺服器受到認證中介層保護,請確保在連接時包含必要的標頭,例如 Authorization 承載令牌 (bearer token)。
單元測試
您可以為您的 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.');
});/**
* Test a tool.
*/
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() 方法斷言回應是否包含特定的元數據 (metadata):
$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();