Skip to content

Laravel MCP

簡介

Laravel MCP 提供了一種簡單優雅的方式,讓 AI 客戶端透過 Model Context Protocol 與你的 Laravel 應用程式互動。它提供了一個富有表現力、流暢的介面,用於定義伺服器、工具、資源和提示,以啟用 AI 驅動的應用程式互動。

安裝

若要開始,請使用 Composer 套件管理工具將 Laravel MCP 安裝到你的專案中:

shell
composer require laravel/mcp

發佈路由

安裝 Laravel MCP 後,執行 vendor:publish Artisan 命令,以發佈 routes/ai.php 檔案,你將在此定義你的 MCP 伺服器:

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

此命令會在你的應用程式的 routes 目錄中建立 routes/ai.php 檔案,你將使用它來註冊你的 MCP 伺服器。

建立伺服器

你可以使用 make:mcp-server Artisan 命令建立 MCP 伺服器。伺服器充當中央通訊點,將 MCP 功能(例如工具、資源和提示)暴露給 AI 客戶端:

shell
php artisan make:mcp-server WeatherServer

此命令將在 app/Mcp/Servers 目錄中建立一個新的伺服器類別。所產生的伺服器類別會擴充 Laravel MCP 的基礎 Laravel\Mcp\Server 類別,並提供用於註冊工具、資源和提示的屬性:

php
<?php

namespace App\Mcp\Servers;

use Laravel\Mcp\Server;

class WeatherServer extends Server
{
    /**
     * The MCP server's name.
     */
    protected string $name = 'Weather Server';

    /**
     * The MCP server's version.
     */
    protected string $version = '1.0.0';

    /**
     * The MCP server's instructions for the LLM.
     */
    protected string $instructions = 'This server provides weather information and forecasts.';

    /**
     * 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 伺服器:

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

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

就像一般的路由一樣,你可以應用中介層來保護你的 Web 伺服器:

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

本地伺服器

本地伺服器作為 Artisan 命令運行,非常適合建立本地 AI 助理整合,例如 Laravel Boost。使用 local 方法註冊一個本地伺服器:

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

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

註冊後,你通常不需要手動運行 mcp:start Artisan 命令。相反地,請配置你的 MCP 客戶端 (AI 代理) 來啟動伺服器或使用 MCP Inspector

工具

Tools 讓您的伺服器能夠公開功能,供 AI 用戶端呼叫。它們允許語言模型執行動作、執行程式碼或與外部系統互動:

php
<?php

namespace App\Mcp\Tools;

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

class CurrentWeatherTool extends Tool
{
    /**
     * The tool's description.
     */
    protected string $description = 'Fetches the current weather forecast for a specified location.';

    /**
     * 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\JsonSchema>
     */
    public function schema(JsonSchema $schema): array
    {
        return [
            'location' => $schema->string()
                ->description('The location to get the weather for.')
                ->required(),
        ];
    }
}

建立工具

要建立工具,請執行 make:mcp-tool Artisan 命令:

shell
php artisan make:mcp-tool CurrentWeatherTool

建立工具後,將其註冊到您的伺服器的 $tools 屬性中:

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

工具名稱、標題與說明

依預設,工具的名稱和標題是衍生自類別名稱。例如,CurrentWeatherTool 的名稱將為 current-weather,標題為 Current Weather Tool。您可以透過定義工具的 $name$title 屬性來客製化這些值:

php
class CurrentWeatherTool extends Tool
{
    /**
     * The tool's name.
     */
    protected string $name = 'get-optimistic-weather';

    /**
     * The tool's title.
     */
    protected string $title = 'Get Optimistic Weather Forecast';

    // ...
}

工具說明不會自動產生。您應該始終透過在工具上定義 $description 屬性來提供有意義的說明:

php
class CurrentWeatherTool extends Tool
{
    /**
     * The tool's description.
     */
    protected string $description = 'Fetches the current weather forecast for a specified location.';

    //
}

📌 備註

說明是工具元資料的關鍵部分,因為它有助於 AI 模型了解何時以及如何有效地使用工具。

工具輸入 Schema

工具可以定義輸入 Schema 來指定它們從 AI 用戶端接受哪些引數。使用 Laravel 的 Illuminate\JsonSchema\JsonSchema 產生器來定義工具的輸入要求:

php
<?php

namespace App\Mcp\Tools;

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

class CurrentWeatherTool extends Tool
{
    /**
     * Get the tool's input schema.
     *
     * @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->array()
                ->enum(['celsius', 'fahrenheit'])
                ->description('The temperature units to use.')
                ->default('celsius'),
        ];
    }
}

驗證工具引數

JSON Schema 定義為工具引數提供了基本結構,但您可能也想強制執行更複雜的驗證規則。

Laravel MCP 與 Laravel 的 驗證功能 無縫整合。您可以在工具的 handle 方法中驗證傳入的工具引數:

php
<?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 用戶端將根據您提供的錯誤訊息採取行動。因此,提供清晰且可操作的錯誤訊息至關重要:

php
$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
<?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
<?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);

        // ...
    }
}

工具註解

您可以使用 註解 來增強您的工具,為 AI 用戶端提供額外的元資料。這些註解有助於 AI 模型理解工具的行為和功能。註解透過屬性新增到工具中:

php
<?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
{
    //
}

可用的註解包括:

AnnotationTypeDescription
#[IsReadOnly]boolean表示工具不會修改其環境。
#[IsDestructive]boolean表示工具可能會執行破壞性更新 (僅在非唯讀時有意義)。
#[IsIdempotent]boolean表示使用相同引數重複呼叫不會產生額外效果 (僅在非唯讀時有意義)。
#[IsOpenWorld]boolean表示工具可能會與外部實體互動。

條件式工具註冊

您可以透過在工具類別中實作 shouldRegister 方法,在執行時條件式地註冊工具。此方法允許您根據應用程式狀態、配置或請求參數來判斷工具是否應該可用:

php
<?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 方法:

php
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 方法:

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

多內容回應

工具可以透過返回 Response 實例的陣列來返回多個內容片段:

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

串流回應

對於長時間執行的操作或即時資料串流,工具可以從其 handle 方法返回一個 產生器。這使得在最終回應之前可以向客戶端發送中間更新:

php
<?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 指令:

shell
php artisan make:mcp-prompt DescribeWeatherPrompt

建立提示後,將其註冊到您伺服器的 $prompts 屬性中:

php
<?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 屬性來自訂這些值:

php
class DescribeWeatherPrompt extends Prompt
{
    /**
     * The prompt's name.
     */
    protected string $name = 'weather-assistant';

    /**
     * The prompt's title.
     */
    protected string $title = 'Weather Assistant Prompt';

    // ...
}

提示描述不會自動生成。您應該始終透過在提示上定義 $description 屬性來提供有意義的描述:

php
class DescribeWeatherPrompt extends Prompt
{
    /**
     * The prompt's description.
     */
    protected string $description = 'Generates a natural-language explanation of the weather for a given location.';

    //
}

注意

描述是提示元資料的關鍵部分,因為它有助於 AI 模型理解何時以及如何最佳利用該提示。

提示引數

提示可以定義引數,讓 AI 客戶端能夠使用特定值自訂提示模板。使用 arguments 方法來定義您的提示接受哪些引數:

php
<?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
<?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 客戶端將根據您提供的錯誤訊息採取行動。因此,提供清晰且可操作的錯誤訊息至關重要:

php
$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
<?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
<?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
<?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
<?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 客戶端在與語言模型互動時讀取並用作上下文。它們提供了一種方式來分享靜態或動態資訊,例如文件、配置或任何有助於提供 AI 回應的資料。

建立資源

若要建立資源,請執行 make:mcp-resource Artisan 指令:

shell
php artisan make:mcp-resource WeatherGuidelinesResource

建立資源後,請在伺服器的 $resources 屬性中註冊它:

php
<?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 屬性來自訂這些值:

php
class WeatherGuidelinesResource extends Resource
{
    /**
     * The resource's name.
     */
    protected string $name = 'weather-api-docs';

    /**
     * The resource's title.
     */
    protected string $title = 'Weather API Documentation';

    // ...
}

資源描述不會自動生成。您應該始終透過在資源上定義 $description 屬性來提供有意義的描述:

php
class WeatherGuidelinesResource extends Resource
{
    /**
     * The resource's description.
     */
    protected string $description = 'Comprehensive guidelines for using the Weather API.';

    //
}

📌 備註

描述是資源元資料的關鍵部分,因為它有助於 AI 模型理解何時以及如何有效地使用該資源。

資源 URI 與 MIME 類型

每個資源都由唯一的 URI 識別,並具有相關聯的 MIME 類型,可幫助 AI 用戶端理解資源的格式。

預設情況下,資源的 URI 是根據資源的名稱生成的,因此 WeatherGuidelinesResource 將具有 weather://resources/weather-guidelines 的 URI。預設的 MIME 類型為 text/plain

您可以透過在資源上定義 $uri$mimeType 屬性來自訂這些值:

php
<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Server\Resource;

class WeatherGuidelinesResource extends Resource
{
    /**
     * The resource's URI.
     */
    protected string $uri = 'weather://resources/guidelines';

    /**
     * The resource's MIME type.
     */
    protected string $mimeType = 'application/pdf';
}

URI 和 MIME 類型可幫助 AI 用戶端確定如何適當地處理和解釋資源內容。

資源請求

與工具和提示不同,資源無法定義輸入 Schema 或引數。然而,您仍然可以在資源的 handle 方法中與請求物件互動:

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

條件式資源註冊

您可以透過在資源類別中實作 shouldRegister 方法來在執行時條件式地註冊資源。此方法允許您判斷資源是否應該可用,基於應用程式狀態、設定或請求參數:

php
<?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 方法:

php
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 內容:

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

回傳 blob 內容時,MIME 類型將由資源類別上的 $mimeType 屬性值決定:

php
<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Server\Resource;

class WeatherGuidelinesResource extends Resource
{
    /**
     * The resource's MIME type.
     */
    protected string $mimeType = 'image/png';

    //
}

錯誤回應

若要指示在資源擷取過程中發生錯誤,請使用 error() 方法:

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

身份驗證

您可以像對路由一樣,使用中介層來驗證 Web MCP 伺服器。這將要求使用者在伺服器使用任何功能之前進行身份驗證。

有兩種方式可以驗證對 MCP 伺服器的存取:一種是透過 Laravel Sanctum 進行簡單、基於 token 的身份驗證,或是透過 Authorization HTTP 標頭傳遞的任何其他任意 API token。另一種是使用 Laravel Passport 透過 OAuth 進行身份驗證。

OAuth 2.1

保護您的基於網頁的 MCP 伺服器最穩健的方式是透過 Laravel Passport 使用 OAuth。

當透過 OAuth 對您的 MCP 伺服器進行身份驗證時,您將在 routes/ai.php 檔案中呼叫 Mcp::oauthRoutes 方法,以註冊所需的 OAuth2 發現與用戶端註冊路由。接著,將 Passport 的 auth:api 中介層套用至您在 routes/ai.php 檔案中的 Mcp::web 路由:

php
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 模型、新的身份驗證守衛 (authentication guard) 和 Passport 金鑰。

接著,您應該發佈 Laravel MCP 提供的 Passport 授權視圖:

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

然後,指示 Passport 使用此視圖,方式是透過 Passport::authorizationView 方法。通常,此方法應在應用程式的 AppServiceProviderboot 方法中呼叫:

php
use Laravel\Passport\Passport;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Passport::authorizationView(function ($parameters) {
        return view('mcp.authorize', $parameters);
    });
}

在身份驗證期間,此視圖將會顯示給終端使用者,以拒絕或批准 AI 代理的身份驗證嘗試。

授權畫面範例

📌 備註

在此情境下,我們僅將 OAuth 作為底層可驗證模型 (authenticatable model) 的轉換層。我們忽略了 OAuth 的許多方面,例如作用域 (scopes)。

使用現有的 Passport 安裝

如果您的應用程式已經使用 Laravel Passport,Laravel MCP 應該能夠在您現有的 Passport 安裝中無縫運作,但目前不支援自訂作用域 (custom scopes),因為 OAuth 主要被用作底層可驗證模型 (authenticatable model) 的轉換層。

Laravel MCP 透過上述討論的 Mcp::oauthRoutes() 方法,會新增、宣傳並使用單一的 mcp:use 作用域 (scope)。

Passport 與 Sanctum 的比較

OAuth2.1 是 Model Context Protocol 規範中記載的身份驗證機制,也是在 MCP 用戶端中最廣泛支援的機制。基於此原因,我們建議盡可能使用 Passport。

如果您的應用程式已經使用 Sanctum,那麼新增 Passport 可能會很麻煩。在此情況下,我們建議在您有明確且必要的理由必須使用僅支援 OAuth 的 MCP 用戶端之前,繼續使用 Sanctum 而不使用 Passport。

Sanctum

如果您想使用 Sanctum 保護您的 MCP 伺服器,只需在 routes/ai.php 檔案中為您的伺服器新增 Sanctum 的身份驗證中介層。然後,請確保您的 MCP 客戶端提供 Authorization: Bearer <token> 標頭以確保成功驗證身份:

php
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 工具和資源中執行 授權檢查

php
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 伺服器。使用它來連接到您的伺服器、驗證身份、並試用工具、資源和提示。

您可以為任何已註冊的伺服器執行此 Inspector:

shell
# 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 上測試一個工具:

php
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.');
});
php
/**
 * 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.');
}

同樣地,您可以測試提示和資源:

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

您也可以透過在呼叫原語之前鏈接 actingAs 方法來扮演一個已驗證的使用者:

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

一旦您收到回應,您可以使用各種斷言方法來驗證回應的內容和狀態。

您可以使用 assertOk 方法斷言回應是否成功。這會檢查回應是否沒有任何錯誤:

php
$response->assertOk();

您可以使用 assertSee 方法斷言回應是否包含特定文字:

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

您可以使用 assertHasErrors 方法斷言回應是否包含錯誤:

php
$response->assertHasErrors();

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

您可以使用 assertHasNoErrors 方法斷言回應是否不包含錯誤:

php
$response->assertHasNoErrors();

您可以使用 assertName()assertTitle()assertDescription() 方法斷言回應是否包含特定的元資料:

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

您可以使用 assertSentNotificationassertNotificationCount 方法斷言通知已發送:

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

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

$response->assertNotificationCount(5);

最後,如果您希望檢查原始回應內容,您可以使用 dddump 方法輸出回應以進行除錯:

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