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
{
    /**
     * 在此 MCP 服务器注册的工具。
     *
     * @var array<int, class-string<\Laravel\Mcp\Server\Tool>>
     */
    protected array $tools = [
        // ExampleTool::class,
    ];

    /**
     * 在此 MCP 服务器注册的资源。
     *
     * @var array<int, class-string<\Laravel\Mcp\Server\Resource>>
     */
    protected array $resources = [
        // ExampleResource::class,
    ];

    /**
     * 在此 MCP 服务器注册的提示。
     *
     * @var array<int, class-string<\Laravel\Mcp\Server\Prompt>>
     */
    protected array $prompts = [
        // ExamplePrompt::class,
    ];
}

服务器注册 ​

创建服务器后,您必须将其注册到您的 routes/ai.php 文件中以使其可访问。Laravel MCP 提供了两种注册服务器的方法: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 代理)来启动服务器。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
{
    /**
     * 在此 MCP 服务器注册的工具。
     *
     * @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
{
    /**
     * 工具的名称。
     */
    protected string $name = 'get-optimistic-weather';

    /**
     * 工具的标题。
     */
    protected string $title = '获取乐观天气预报';

    // ...
}

工具描述不会自动生成。您应始终通过在工具上定义 $description 属性来提供有意义的描述:

php
class CurrentWeatherTool extends Tool
{
    /**
     * 工具的描述。
     */
    protected string $description = '获取指定位置的当前天气预报。';

    //
}

注意

描述是工具元数据的关键部分,因为它帮助 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
{
    /**
     * 获取工具的输入模式。
     *
     * @return array<string, JsonSchema>
     */
    public function schema(JsonSchema $schema): array
    {
        return [
            'location' => $schema->string()
                ->description('要获取天气的位置。')
                ->required(),

            'units' => $schema->enum(['celsius', 'fahrenheit'])
                ->description('要使用的温度单位。')
                ->default('celsius'),
        ];
    }
}

验证工具参数 ​

JSON 模式定义提供了工具参数的基本结构,但您可能还想强制执行更复杂的验证规则。

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
{
    /**
     * 处理工具请求。
     */
    public function handle(Request $request): Response
    {
        $validated = $request->validate([
            'location' => 'required|string|max:100',
            'units' => 'in:celsius,fahrenheit',
        ]);

        // 使用经过验证的参数获取天气数据...
    }
}

验证失败时,AI 客户端将根据您提供的错误消息采取行动。因此,提供清晰且可操作的错误消息至关重要:

php
$validated = $request->validate([
    'location' => ['required','string','max:100'],
    'units' => 'in:celsius,fahrenheit',
],[
    'location.required' => '您必须指定一个位置来获取天气。例如,"New York City" 或 "Tokyo"。',
    'units.in' => '您必须为单位指定 "celsius" 或 "fahrenheit"。',
]);

工具依赖注入 ​

Laravel 服务容器用于解析所有工具。因此,您可以在工具的构造函数中类型提示您的工具可能需要的任何依赖项。声明的依赖项将自动解析并注入到工具实例中:

php
<?php

namespace App\Mcp\Tools;

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

class CurrentWeatherTool extends Tool
{
    /**
     * 创建新的工具实例。
     */
    public function __construct(
        protected WeatherRepository $weather,
    ) {}

    // ...
}

除了构造函数注入,您还可以在工具的 handle() 方法中类型提示依赖项。服务容器将在调用该方法时自动解析并注入依赖项:

php
<?php

namespace App\Mcp\Tools;

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

class CurrentWeatherTool extends Tool
{
    /**
     * 处理工具请求。
     */
    public function handle(Request $request, WeatherRepository $weather): Response
    {
        $location = $request->get('location');

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

        // ...
    }
}

工具注解 ​

您可以使用注解来增强您的工具,以向 AI 客户端提供额外的元数据。这些注解帮助 AI 模型理解工具的行为和能力。通过属性将注解添加到工具中:

php
<?php

namespace App\Mcp\Tools;

use Laravel\Mcp\Server\Tools\Annotations\IsIdempotent;
use Laravel\Mcp\Server\Tools\Annotations\IsReadOnly;
use Laravel\Mcp\Server\Tool;

#[IsIdempotent]
#[IsReadOnly]
class CurrentWeatherTool extends Tool
{
    //
}

可用的注解包括:

注解类型描述
#[IsReadOnly]boolean表示该工具不会修改其环境。
#[IsDestructive]boolean表示该工具可能执行破坏性更新(仅在非只读时有意义)。
#[IsIdempotent]boolean表示使用相同参数的重复调用不会产生额外效果(当非只读时)。
#[IsOpenWorld]boolean表示该工具可能与外部实体交互。

条件工具注册 ​

您可以通过在工具类中实现 shouldRegister 方法,在运行时有条件地注册工具。此方法允许您根据应用程序状态、配置或请求参数来确定工具是否应可用:

php
<?php

namespace App\Mcp\Tools;

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

class CurrentWeatherTool extends Tool
{
    /**
     * 确定是否应注册该工具。
     */
    public function shouldRegister(Request $request): bool
    {
        return $request?->user()?->subscribed() ?? false;
    }
}

当工具的 shouldRegister 方法返回 false 时,它将不会出现在可用工具列表中,并且不能被 AI 客户端调用。

工具响应 ​

工具必须返回一个 Laravel\Mcp\Response 实例。Response 类提供了几个方便的方法来创建不同类型的响应:

对于简单的文本响应,使用 text 方法:

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

/**
 * 处理工具请求。
 */
public function handle(Request $request): Response
{
    // ...

    return Response::text('天气摘要:晴朗,72°F');
}

要指示工具执行期间发生错误,请使用 error 方法:

php
return Response::error('无法获取天气数据。请重试。');

多内容响应 ​

工具可以通过返回 Response 实例数组来返回多个内容片段:

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

/**
 * 处理工具请求。
 *
 * @return array<int, \Laravel\Mcp\Response>
 */
public function handle(Request $request): array
{
    // ...

    return [
        Response::text('天气摘要:晴朗,72°F'),
        Response::text('**详细预报**\n- 早晨:65°F\n- 下午:78°F\n- 晚上:70°F')
    ];
}

流式响应 ​

对于长时间运行的操作或实时数据流,工具可以从其 handle 方法返回一个生成器 (generator)。这使得在最终响应之前可以向客户端发送中间更新:

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
{
    /**
     * 处理工具请求。
     *
     * @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(服务器发送事件)流,将每个产生的消息作为事件发送到客户端。

提示 ​

提示 (Prompts) 使您的服务器能够共享可重用的提示模板,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
{
    /**
     * 在此 MCP 服务器注册的提示。
     *
     * @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
{
    /**
     * 提示的名称。
     */
    protected string $name = 'weather-assistant';

    /**
     * 提示的标题。
     */
    protected string $title = '天气助手提示';

    // ...
}

提示描述不会自动生成。您应始终通过在提示上定义 $description 属性来提供有意义的描述:

php
class DescribeWeatherPrompt extends Prompt
{
    /**
     * 提示的描述。
     */
    protected string $description = '为给定位置生成天气的自然语言解释。';

    //
}

注意

描述是提示元数据的关键部分,因为它帮助 AI 模型理解何时以及如何最好地利用该提示。

提示参数 ​

提示可以定义参数,允许 AI 客户端使用特定值自定义提示模板。使用 arguments 方法定义您的提示接受哪些参数:

php
<?php

namespace App\Mcp\Prompts;

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

class DescribeWeatherPrompt extends Prompt
{
    /**
     * 获取提示的参数。
     *
     * @return array<int, \Laravel\Mcp\Server\Prompts\Argument>
     */
    public function arguments(): array
    {
        return [
            new Argument(
                name: 'tone',
                description: '在天气描述中使用的语气(例如,正式、随意、幽默)。',
                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
{
    /**
     * 处理提示请求。
     */
    public function handle(Request $request): Response
    {
        $validated = $request->validate([
            'tone' => 'required|string|max:50',
        ]);

        $tone = $validated['tone'];

        // 使用给定的语气生成提示响应...
    }
}

验证失败时,AI 客户端将根据您提供的错误消息采取行动。因此,提供清晰且可操作的错误消息至关重要:

php
$validated = $request->validate([
    'tone' => ['required','string','max:50'],
],[
    'tone.*' => '您必须为天气描述指定一个语气。示例包括 "正式"、"随意" 或 "幽默"。',
]);

提示依赖注入 ​

Laravel 服务容器用于解析所有提示。因此,您可以在提示的构造函数中类型提示您的提示可能需要的任何依赖项。声明的依赖项将自动解析并注入到提示实例中:

php
<?php

namespace App\Mcp\Prompts;

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

class DescribeWeatherPrompt extends Prompt
{
    /**
     * 创建新的提示实例。
     */
    public function __construct(
        protected WeatherRepository $weather,
    ) {}

    //
}

除了构造函数注入,您还可以在提示的 handle 方法中类型提示依赖项。服务容器将在调用该方法时自动解析并注入依赖项:

php
<?php

namespace App\Mcp\Prompts;

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

class DescribeWeatherPrompt extends Prompt
{
    /**
     * 处理提示请求。
     */
    public function handle(Request $request, WeatherRepository $weather): Response
    {
        $isAvailable = $weather->isServiceAvailable();

        // ...
    }
}

条件提示注册 ​

您可以通过在提示类中实现 shouldRegister 方法,在运行时有条件地注册提示。此方法允许您根据应用程序状态、配置或请求参数来确定提示是否应可用:

php
<?php

namespace App\Mcp\Prompts;

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

class CurrentWeatherPrompt extends Prompt
{
    /**
     * 确定是否应注册该提示。
     */
    public function shouldRegister(Request $request): bool
    {
        return $request?->user()?->subscribed() ?? false;
    }
}

当提示的 shouldRegister 方法返回 false 时,它将不会出现在可用提示列表中,并且不能被 AI 客户端调用。

提示响应 ​

提示可以返回单个 Laravel\Mcp\Response 或 Laravel\Mcp\Response 实例的可迭代对象。这些响应封装了将发送给 AI 客户端的内容:

php
<?php

namespace App\Mcp\Prompts;

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

class DescribeWeatherPrompt extends Prompt
{
    /**
     * 处理提示请求。
     *
     * @return array<int, \Laravel\Mcp\Response>
     */
    public function handle(Request $request): array
    {
        $tone = $request->string('tone');

        $systemMessage = "你是一个有用的天气助手。请以{$tone}的语气提供天气描述。";

        $userMessage = "纽约市现在的天气怎么样?";

        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
{
    /**
     * 在此 MCP 服务器注册的资源。
     *
     * @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
{
    /**
     * 资源的名称。
     */
    protected string $name = 'weather-api-docs';

    /**
     * 资源的标题。
     */
    protected string $title = '天气 API 文档';

    // ...
}

资源描述不会自动生成。您应始终通过在资源上定义 $description 属性来提供有意义的描述:

php
class WeatherGuidelinesResource extends Resource
{
    /**
     * 资源的描述。
     */
    protected string $description = '使用天气 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
{
    /**
     * 资源的 URI。
     */
    protected string $uri = 'weather://resources/guidelines';

    /**
     * 资源的 MIME 类型。
     */
    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
{
    /**
     * 处理资源请求。
     */
    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
{
    /**
     * 创建新的资源实例。
     */
    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
{
    /**
     * 处理资源请求。
     */
    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
{
    /**
     * 确定是否应注册该资源。
     */
    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;

/**
 * 处理资源请求。
 */
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
{
    /**
     * 资源的 MIME 类型。
     */
    protected string $mimeType = 'image/png';

    //
}

错误响应 ​

要指示资源检索期间发生错误,请使用 error() 方法:

php
return Response::error('无法获取指定位置的天气数据。');

认证 ​

您可以像对路由一样使用中间件对 Web MCP 服务器进行身份验证。这将要求用户在使用服务器的任何功能之前进行身份验证。

有两种方法可以对您的 MCP 服务器的访问进行身份验证:通过 Laravel Sanctum 进行简单的、基于令牌的身份验证,或通过 Authorization HTTP 头传递的任何其他任意 API 令牌。或者,您可以使用 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 路由:

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::authorizationView 方法指示 Passport 使用此视图。通常,此方法应在您的应用程序的 AppServiceProvider 的 boot 方法中调用:

php
use Laravel\Passport\Passport;

/**
 * 启动任何应用程序服务。
 */
public function boot(): void
{
    Passport::authorizationView(function ($parameters) {
        return view('mcp.authorize', $parameters);
    });
}

该视图将在身份验证期间显示给最终用户,以拒绝或批准 AI 代理的身份验证尝试。

Authorization screen example

注意

在这种情况下,我们只是将 OAuth 用作底层可认证模型的转换层。我们忽略了 OAuth 的许多方面,例如作用域 (scopes)。

使用现有的 Passport 安装 ​

如果您的应用程序已经在使用 Laravel Passport,Laravel MCP 应该在您现有的 Passport 安装中无缝工作,但目前不支持自定义作用域,因为 OAuth 主要用作底层可认证模型的转换层。

Laravel MCP 通过上面讨论的 Mcp::oauthRoutes() 方法添加、通告和使用单个 mcp:use 作用域。

Passport 与 Sanctum ​

OAuth2.1 是模型上下文协议规范中记录的认证机制,并且在 MCP 客户端中得到最广泛的支持。因此,我们建议尽可能使用 Passport。

如果您的应用程序已经在使用 Sanctum,那么添加 Passport 可能会很麻烦。在这种情况下,我们建议在没有 Passport 的情况下使用 Sanctum,直到您有明确、必要的要求需要使用仅支持 OAuth 的 MCP 客户端。

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;

/**
 * 处理工具请求。
 */
public function handle(Request $request): Response
{
    if (! $request->user()->can('read-weather')) {
        return Response::error('权限被拒绝。');
    }

    // ...
}

测试服务器 ​

您可以使用内置的 MCP 检查器或编写单元测试来测试您的 MCP 服务器。

MCP 检查器 ​

MCP 检查器 (MCP Inspector) 是一个用于测试和调试 MCP 服务器的交互式工具。使用它连接到您的服务器,验证身份验证,并试用工具、资源和提示。

您可以为任何已注册的服务器运行检查器(例如,名为 "weather" 的本地服务器):

shell
php artisan mcp:inspector weather

此命令启动 MCP 检查器,并提供客户端设置,您可以将其复制到 MCP 客户端中以确保一切配置正确。如果您的 Web 服务器受身份验证中间件保护,请确保在连接时包含所需的标头,例如 Authorization 承载令牌。

单元测试 ​

您可以为您的 MCP 服务器、工具、资源和提示编写单元测试。

首先,创建一个新的测试用例并调用服务器上注册的所需原语。例如,要测试 WeatherServer 上的工具:

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

    $response
        ->assertOk()
        ->assertSee('纽约市现在的天气是 72°F,晴朗。');
});
php
/**
 * 测试一个工具。
 */
public function test_tool(): void
{
    $response = WeatherServer::tool(CurrentWeatherTool::class, [
        'location' => 'New York City',
        'units' => 'fahrenheit',
    ]);

    $response
        ->assertOk()
        ->assertSee('纽约市现在的天气是 72°F,晴朗。');
}

同样,您可以测试提示和资源:

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

您还可以通过在调用原语之前链接 actingAs 方法来充当经过身份验证的用户:

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

一旦收到响应,您可以使用各种断言方法来验证响应的内容和状态。

您可以使用 assertOk 方法断言响应是成功的。这会检查响应没有任何错误:

php
$response->assertOk();

您可以使用 assertSee 方法断言响应包含特定文本:

php
$response->assertSee('纽约市现在的天气是 72°F,晴朗。');

您可以使用 assertHasErrors 方法断言响应包含错误:

php
$response->assertHasErrors();

$response->assertHasErrors([
    '出了点问题。',
]);

您可以使用 assertHasNoErrors 方法断言响应不包含错误:

php
$response->assertHasNoErrors();

您可以使用 assertName()、assertTitle() 和 assertDescription() 方法断言响应包含特定元数据:

php
$response->assertName('current-weather');
$response->assertTitle('当前天气工具');
$response->assertDescription('获取指定位置的当前天气预报。');

您可以使用 assertSentNotification 和 assertNotificationCount 方法断言已发送通知:

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

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

$response->assertNotificationCount(5);

最后,如果您希望检查原始响应内容,可以使用 dd 或 dump 方法输出响应以进行调试:

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