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 伺服器。伺服器作為中央通訊點,向 AI 客戶端暴露 MCP 功能,如工具、資源和提示:

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 tools registered with this MCP server.
     *
     * @var array<int, class-string<\Laravel\Mcp\Server\Tool>>
     */
    protected array $tools = [
        // ExampleTool::class,
    ];

    /**
     * The resources registered with this MCP server.
     *
     * @var array<int, class-string<\Laravel\Mcp\Server\Resource>>
     */
    protected array $resources = [
        // ExampleResource::class,
    ];

    /**
     * The prompts registered with this MCP server.
     *
     * @var array<int, class-string<\Laravel\Mcp\Server\Prompt>>
     */
    protected array $prompts = [
        // ExamplePrompt::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 助理整合。使用 local 方法註冊本地伺服器:

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

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

一旦註冊,您通常不需要手動執行 mcp:start。相反,請設定您的 MCP 客戶端 (AI agent) 來啟動伺服器。mcp:start 命令旨在由客戶端呼叫,客戶端將根據需要處理伺服器的啟動和停止:

shell
php artisan mcp:start weather

工具

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

建立工具

若要建立工具,請執行 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 模型理解何時以及如何有效地使用此工具。

工具輸入結構描述

工具可以定義輸入結構描述,以指定它們從 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->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);

        // ...
    }
}

工具註解

您可以透過 annotations 來增強工具,以向 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) 串流,將每個產生的訊息作為事件發送給客戶端。

提示

Prompts 讓你的伺服器能夠分享可重複使用的提示模板,供 AI 客戶端用於與語言模型互動。它們提供了一種標準化的方式來建構常見的查詢和互動。

建立提示

要建立一個 Prompt,請執行 make:mcp-prompt Artisan 指令:

shell
php artisan make:mcp-prompt DescribeWeatherPrompt

建立 Prompt 後,請將其註冊到你伺服器的 $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,
    ];
}

提示名稱、標題與描述

預設情況下,Prompt 的名稱和標題是從類別名稱派生而來。例如,DescribeWeatherPrompt 的名稱將是 describe-weather,標題則是 Describe Weather Prompt。你可以透過在 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';

    // ...
}

Prompt 的描述不會自動產生。你應該始終透過在 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.';

    //
}

📌 備註

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

提示引數

Prompts 可以定義引數,允許 AI 客戶端使用特定值來客製化提示模板。使用 arguments 方法來定義你的 Prompt 接受哪些引數:

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

驗證提示引數

Prompt 引數會根據其定義自動驗證,但你也可能想強制執行更複雜的驗證規則。

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

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 的 service container 用於解析所有 Prompts。因此,你可以在 Prompt 的建構函式中類型提示任何它可能需要的依賴。宣告的依賴將會自動解析並注入到 Prompt 實例中:

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

    //
}

除了建構函式注入之外,你還可以在 Prompt 的 handle 方法中類型提示依賴。當該方法被呼叫時,service container 將自動解析並注入這些依賴:

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

        // ...
    }
}

條件式提示註冊

你可以透過在 Prompt 類別中實作 shouldRegister 方法,在執行時條件式地註冊 Prompts。此方法允許你根據應用程式狀態、設定或請求參數來決定 Prompt 是否可用:

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

當 Prompt 的 shouldRegister 方法回傳 false 時,它將不會出現在可用 Prompts 的列表中,也無法被 AI 客戶端呼叫。

提示回應

Prompts 可以回傳單一的 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 助理,而一般訊息則被視為使用者輸入。

資源

Resources 讓你的伺服器能夠公開資料和內容,供 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 將具有 URI weather://resources/weather-guidelines。預設的 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 用戶端確定如何適當地處理和解釋資源內容。

資源請求

與工具和提示不同,資源無法定義輸入結構描述或引數。但是,您仍然可以在資源的 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,或任何透過 Authorization HTTP 標頭傳遞的任意 API 權杖。或者,您可以透過 OAuth 使用 Laravel Passport 進行認證。

OAuth 2.1

保護您的 Web 型 MCP 伺服器最穩健的方式是透過 OAuth 與 Laravel Passport

透過 OAuth 認證您的 MCP 伺服器時,您將在 routes/ai.php 檔案中呼叫 Mcp::oauthRoutes 方法來註冊所需的 OAuth2 探索與客戶端註冊路由。然後,在您的 routes/ai.php 檔案中,將 Passport 的 auth:api 中介層應用到您的 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 模型、新的認證守衛,以及 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 安裝中運作,但目前不支援自訂 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 伺服器,只需將 Sanctum 的認證中介層加入到您伺服器的 routes/ai.php 檔案中即可。然後,確保您的 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 伺服器。您可以使用它來連接到您的伺服器,驗證認證,並試用工具、資源和提示。

您可以為任何已註冊的伺服器執行檢查器 (例如,名為 "weather" 的本地伺服器):

shell
php artisan mcp:inspector weather

此指令會啟動 MCP Inspector,並提供用戶端設定,您可以將其複製到您的 MCP 用戶端以確保一切設定正確。如果您的 Web 伺服器受到認證中介層保護,請務必在連接時包含所需的標頭,例如 Authorization Bearer 權杖。

單元測試

您可以為您的 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();