MCP

简介

Laravel MCP 为 AI 客户端通过模型上下文协议与你的 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 服务器。服务器作为中心通信点,向 AI 客户端暴露工具、资源和提示等 MCP 能力:

php artisan make:mcp-server WeatherServer

此命令将在 app/Mcp/Servers 目录中创建一个新的服务器类。生成的服务器类继承 Laravel MCP 的基础 Laravel\Mcp\Server 类,并提供用于配置服务器和注册工具、资源和提示的属性和特性:

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

工具名称、标题和描述

默认情况下,工具的名称和标题从类名派生。例如,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
{
    //
}

信息

描述是工具元数据的关键部分,因为它帮助 AI 模型理解何时以及如何有效地使用该工具。

工具输入模式

工具可以定义输入模式来指定它们从 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'),
        ];
    }
}

工具输出模式

工具可以定义输出模式来指定其响应的结构。这可以更好地与需要可解析工具结果的 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 服务容器用于解析所有工具。因此,你可以在工具的构造函数中类型提示其可能需要的任何依赖。声明的依赖将被自动解析并注入到工具实例中:

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

        // ...
    }
}

工具注解

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

<?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 方法返回一个生成器。这允许在最终响应之前向客户端发送中间更新:

<?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(服务器发送事件)流,将每个 yield 的消息作为事件发送给客户端。

提示

提示使你的服务器能够共享可重用的提示模板,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
{
    //
}

信息

描述是提示元数据的关键部分,因为它帮助 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 服务容器用于解析所有提示。因此,你可以在提示的构造函数中类型提示其可能需要的任何依赖。声明的依赖将被自动解析并注入到提示实例中:

<?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 客户端可以读取并用作与语言模型交互时上下文的数据和内容。它们提供了一种共享静态或动态信息的方式,如文档、配置或任何有助于为 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 的 URI 为 weather://resources/weather-guidelines。默认 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);
    }
}

资源注解

你可以使用注解来增强资源,为 AI 客户端提供额外的元数据。注解通过属性添加到资源:

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

要返回资源链接,使用 resourceLink 方法,提供 URI 和名称。与嵌入资源不同,资源链接返回一个 URI 指针,AI 客户端独立获取:

return Response::resourceLink(
    uri: 'file:///data/report.json',
    name: 'monthly-report',
    mimeType: 'application/json',
);

你还可以传递已注册的资源类或实例,它将自动继承资源的 URI、名称、标题、描述和 MIME 类型:

return Response::resourceLink(new WeatherForecastResource);

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.');

应用

Laravel MCP 支持 MCP Apps,这是模型上下文协议的扩展,允许工具在受支持的宿主中的沙箱 iframe 内渲染交互式 HTML 应用。这允许你构建仪表板、表单、可视化和其他超越纯文本响应的丰富体验。

MCP 应用由两部分协同工作:

  • 一个应用资源,返回你应用的自包含 HTML。
  • 一个工具,使用 #[RendersApp] 属性链接到应用资源。当工具被调用时,宿主获取并渲染链接的资源。

创建应用资源

你可以使用 make:mcp-app-resource Artisan 命令创建应用资源:

php artisan make:mcp-app-resource WeatherDashboardApp

此命令创建两个文件:app/Mcp/Resources 中的 PHP 类和 resources/views/mcp 中的 Blade 视图。视图名称自动从类名推断。例如,WeatherDashboardApp 映射到 mcp.weather-dashboard-app:

<?php

namespace App\Mcp\Resources;

use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\AppMeta;
use Laravel\Mcp\Server\Attributes\Description;
use Laravel\Mcp\Server\AppResource;

#[Description('An interactive weather dashboard.')]
#[AppMeta]
class WeatherDashboardApp extends AppResource
{
    /**
     * Handle the app resource request.
     */
    public function handle(Request $request): Response
    {
        return Response::view('mcp.weather-dashboard-app', [
            'title' => $this->title(),
        ]);
    }
}

AppResource 继承基础 Resource 类,并自动配置 MCP Apps 规范所需的 ui:// URI 方案和 text/html;profile=mcp-app MIME 类型。与任何其他资源一样,你必须在服务器的 $resources 数组中注册它。

生成的 Blade 视图使用 <x-mcp::app> 组件,它渲染一个完整的 HTML 文档,其中捆绑了客户端 MCP SDK 并准备使用:

<x-mcp::app :title="$title">
    <x-slot:head>
        <script type="module">
        createMcpApp(async (app) => {
            document.getElementById('run-btn').addEventListener('click', async () => {
                const result = await app.callServerTool('get-weather-data', {});
                document.getElementById('output').textContent = result.content[0]?.text ?? '';
            });
        });
        </script>
    </x-slot:head>

    <div id="app">
        <button id="run-btn">Refresh</button>
        <p id="output"></p>
    </div>
</x-mcp::app>

createMcpApp 全局变量由捆绑的 SDK 提供,负责将 iframe 连接到服务器、应用宿主主题,并暴露 callServerTool、sendMessage、openLink 和事件回调等辅助工具。有关完整的客户端 API,请参阅 MCP Apps 规范。

从工具渲染应用

要显示应用资源,使用 #[RendersApp] 属性将工具链接到它。当工具被调用时,Laravel MCP 在工具元数据中包含资源的 URI,以便宿主可以在沙箱 iframe 中渲染应用:

<?php

namespace App\Mcp\Tools;

use App\Mcp\Resources\WeatherDashboardApp;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Laravel\Mcp\Server\Attributes\RendersApp;
use Laravel\Mcp\Server\Tool;

#[RendersApp(resource: WeatherDashboardApp::class)]
class ShowWeatherDashboard extends Tool
{
    /**
     * Handle the tool request.
     */
    public function handle(Request $request): Response
    {
        return Response::text('Weather dashboard loaded.');
    }
}

只要注册了任何 AppResource,Laravel MCP 就会自动通告 io.modelcontextprotocol/ui 能力,因此不需要额外的服务器配置。

应用工具可见性

每个 #[RendersApp] 工具可以通过 visibility 参数限制谁可以调用它。这对于暴露私有的、仅供应用使用的工具很有用,UI 调用这些工具来加载或刷新数据,而不让模型看到这些工具:

use Laravel\Mcp\Server\Attributes\RendersApp;
use Laravel\Mcp\Server\Ui\Enums\Visibility;

#[RendersApp(resource: WeatherDashboardApp::class, visibility: [Visibility::App])]
class GetWeatherData extends Tool
{
    // ...
}

Visibility 枚举有两个案例,Model 和 App,默认为两者。使用 [Visibility::App] 用于 UI 直接调用的后端操作,或使用 [Visibility::Model] 使工具对 UI 不可用。

应用配置

应用资源上的 #[AppMeta] 属性配置 iframe 的内容安全策略、浏览器权限以及应包含在视图 <head> 中的库脚本:

use Laravel\Mcp\Server\Attributes\AppMeta;
use Laravel\Mcp\Server\Ui\Enums\Library;
use Laravel\Mcp\Server\Ui\Enums\Permission;

#[AppMeta(
    connectDomains: ['https://api.weather.com'],
    permissions: [Permission::Geolocation],
    libraries: [Library::Tailwind, Library::Alpine],
)]
class WeatherDashboardApp extends AppResource
{
    // ...
}

Library 枚举包含常见前端库的预配置 CDN 脚本,如 Library::Tailwind 和 Library::Alpine,它们的 CDN 源会自动合并到 CSP 中。Permission 枚举涵盖浏览器权限,如 Camera、Microphone、Geolocation 和 ClipboardWrite。

对于计算或动态配置,使用 Laravel\Mcp\Server\Ui 命名空间中的流式 AppMeta、Csp 和 Permissions 构建器覆盖资源上的 appMeta 方法。

使用 Boost 构建应用

Laravel MCP 包含一个专门的 Boost 技能参考,用于构建 MCP Apps。如果你安装了 Laravel Boost,你的 AI 编码代理可以调用 mcp-development 技能,让它为你搭建应用资源、Blade 视图和链接工具。

有关完整的协议参考,包括完整的客户端 API 和模式详情,请参阅官方 MCP Apps 文档。

元数据

Laravel MCP 还支持 MCP 规范中定义的 _meta 字段,某些 MCP 客户端或集成需要该字段。元数据可以应用于所有 MCP 原语,包括工具、资源和提示,以及它们的响应。

你可以使用 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::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',
    ];

    // ...
}

图标

MCP 客户端可以为你的服务器及其原语显示图标。你可以使用 Icon 属性在服务器、工具、资源或提示上声明图标:

use Laravel\Mcp\Enums\IconTheme;
use Laravel\Mcp\Server\Attributes\Icon;

#[Icon('mcp/server.png', mimeType: 'image/png', sizes: ['48x48'])]
#[Icon('mcp/server-dark.svg', theme: IconTheme::Dark)]
class WeatherServer extends Server
{
    // ...
}

Icon 属性是可重复的,因此你可以声明多个图标来提供不同的尺寸或明暗主题变体。

或者,你可以通过覆盖 icons 方法以编程方式定义图标,这在图标取决于运行时条件时很有用:

use Laravel\Mcp\Schema\Icon;

class CurrentWeatherTool extends Tool
{
    /**
     * Get the tool's icons.
     *
     * @return array<int, Icon>
     */
    public function icons(): array
    {
        return [
            Icon::from('mcp/tool.png', mimeType: 'image/png'),
        ];
    }
}

通过属性和 icons 方法定义的图标会自动合并。图标路径按如下方式解析:

  • 带有 URI 方案的路径(如 https: 或 data:)按原样使用。
  • 相对路径使用 Laravel 的 asset 辅助函数解析为 URL。

认证

就像路由一样,你可以使用中间件认证 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 的许多方面,如作用域。

使用现有 Passport 安装

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

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

Passport 与 Sanctum

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

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

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

自定义 MCP 认证

如果你的应用颁发自己的自定义 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 客户端

除了构建服务器外,Laravel MCP 还包含一个用于连接到其他 MCP 服务器的客户端,无论是第一方还是第三方。客户端让你的应用发现并调用 MCP 服务器暴露的工具,这对于让你的 AI 代理访问外部 MCP 服务器提供的能力特别有用。

连接到服务器

你可以使用 Client::web 方法连接到 HTTP 可访问的 MCP 服务器,传递服务器的 URL:

use Laravel\Mcp\Client;

$client = Client::web('https://mcp.example.com');

要连接到作为命令运行的本地 MCP 服务器,使用 Client::local 方法,提供启动服务器所需的命令和参数:

use Laravel\Mcp\Client;

$client = Client::local('php', ['artisan', 'mcp:start']);

客户端延迟连接,在你第一次列出或调用工具时自动建立连接。如果你需要手动管理连接,可以使用 connect、connected、ping 和 disconnect 方法:

$client->connect();

$client->ping();

if ($client->connected()) {
    // ...
}

$client->disconnect();

你可以使用 withTimeout 方法自定义请求超时:

$client = Client::web('https://mcp.example.com')->withTimeout(30);

命名客户端

你可以注册可重用的命名客户端,而不是每次需要时都构造客户端。这通常在服务提供者的 boot 方法中使用 Mcp 门面完成:

use Laravel\Mcp\Client;
use Laravel\Mcp\Facades\Mcp;

Mcp::registerClient('github', fn () => Client::web('https://mcp.example.com'));

注册后,你可以在应用的任何地方按名称解析客户端:

use Laravel\Mcp\Facades\Mcp;

$client = Mcp::client('github');

命名客户端每个请求解析一次,并在请求生命周期结束时自动断开连接。

客户端认证

要连接到受 bearer 令牌保护的 Web MCP 服务器,使用 withToken 方法。你可以传递令牌字符串或延迟解析令牌的闭包:

use Illuminate\Support\Facades\Auth;
use Laravel\Mcp\Client;

$client = Client::web('https://mcp.example.com')->withToken($token);

$client = Client::web('https://mcp.example.com')->withToken(
    fn () => Auth::user()->mcpToken(),
);

对于受 OAuth 2.1 保护的服务器,使用 withOAuth 方法配置客户端。这是用 OAuth 保护你自己服务器的客户端对应部分:

use Laravel\Mcp\Client;
use Laravel\Mcp\Facades\Mcp;

Mcp::registerClient('github', fn () => Client::web('https://mcp.example.com')->withOAuth(
    clientId: config('services.github_mcp.client_id'),
    clientSecret: config('services.github_mcp.client_secret'),
));

信息

当 MCP 服务器支持动态客户端注册时,可以省略 clientId 和 clientSecret 参数,在这种情况下客户端会自动注册自己。

接下来,使用 oAuthRoutesFor 方法在 routes/ai.php 文件中为命名客户端注册 OAuth 路由。你提供的闭包接收客户端名称和授权码交换为访问令牌后的 TokenSet:

use Illuminate\Support\Facades\Auth;
use Laravel\Mcp\Client\OAuth\TokenSet;
use Laravel\Mcp\Facades\Mcp;

Mcp::oAuthRoutesFor('github', function (string $client, TokenSet $token) {
    Auth::user()->update([
        'github_mcp_token' => $token->accessToken,
    ]);

    return redirect('/dashboard');
});

这注册了两个命名路由:一个将用户重定向到授权服务器的连接路由(mcp.oauth.{client}.connect),和一个交换授权码并调用你的处理程序的回调路由(mcp.oauth.{client}.callback)。两个路由默认使用 web 中间件组,你可以使用 middleware 参数覆盖。

要开始授权流程,将用户重定向到连接路由:

return redirect()->route('mcp.oauth.github.connect');

工具

你可以使用 tools 方法检索 MCP 服务器暴露的工具,它返回按名称键控的工具集合:

use Laravel\Mcp\Facades\Mcp;

$tools = Mcp::client('github')->tools();

foreach ($tools as $tool) {
    $tool->name;
    $tool->title;
    $tool->description;
    $tool->inputSchema;
}

客户端自动分页遍历所有可用工具。你可以使用 limit 参数限制返回的工具数量:

$tools = Mcp::client('github')->tools(limit: 10);

要调用工具,使用 callTool 方法,传递工具名称和参数数组。返回的 ToolResult 实例暴露工具响应:

use Laravel\Mcp\Facades\Mcp;

$result = Mcp::client('github')->callTool('current-weather', [
    'location' => 'New York',
]);

$result->text(); // The text content of the response...
(string) $result; // Equivalent to calling text()...
$result->isError; // Whether the tool reported an error...
$result->structuredContent;  // Structured content, if any...

或者,你可以直接从列出的工具实例调用工具:

$tools = Mcp::client('github')->tools();

$result = $tools['current-weather']->call([
    'location' => 'New York',
]);

如果你正在使用 Laravel AI SDK 构建代理,你还可以将 MCP 客户端的工具直接提供给代理,允许模型在响应提示时调用它们。有关更多信息,请参阅 AI SDK 文档的 MCP 工具部分。

提示

你可以使用 prompts 方法检索 MCP 服务器暴露的提示,它返回按名称键控的提示集合:

use Laravel\Mcp\Facades\Mcp;

$prompts = Mcp::client('github')->prompts();

foreach ($prompts as $prompt) {
    $prompt->name;
    $prompt->title;
    $prompt->description;
    $prompt->arguments;
}

客户端自动分页遍历所有可用提示。你可以使用 limit 参数限制返回的提示数量:

$prompts = Mcp::client('github')->prompts(limit: 10);

要检索提示,使用 getPrompt 方法,传递提示名称和参数数组。返回的 PromptResult 实例暴露生成的消息:

use Laravel\Mcp\Facades\Mcp;

$result = Mcp::client('github')->getPrompt('describe-weather', [
    'location' => 'New York',
]);

$result->text(); // The text content of the messages...
(string) $result; // Equivalent to calling text()...
$result->messages; // The raw messages returned by the prompt...
$result->description; // The prompt description, if any...

资源

你可以使用 resources 方法检索 MCP 服务器暴露的资源,它返回按 URI 键控的资源集合:

use Laravel\Mcp\Facades\Mcp;

$resources = Mcp::client('github')->resources();

foreach ($resources as $resource) {
    $resource->uri;
    $resource->name;
    $resource->title;
    $resource->description;
    $resource->mimeType;
    $resource->size;
}

客户端自动分页遍历所有可用资源。你可以使用 limit 参数限制返回的资源数量:

$resources = Mcp::client('github')->resources(limit: 10);

要读取资源,使用 readResource 方法,传递资源 URI。返回的 ResourceReadResult 实例暴露资源内容:

use Laravel\Mcp\Facades\Mcp;

$result = Mcp::client('github')->readResource('weather://guidelines');

$result->content(); // The content of the resource, decoding base64 blobs as needed...
(string) $result; // Equivalent to calling content()...
$result->mimeType(); // The MIME type of the resource, if any...
$result->contents; // The raw contents returned by the resource...

测试服务器

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

MCP Inspector

MCP Inspector 是一个用于测试和调试 MCP 服务器的交互式工具。使用它连接到你的服务器、验证认证并试用工具、资源和提示。

你可以为任何已注册的服务器运行 inspector:

# Web server...
php artisan mcp:inspector mcp/weather

# Local server named "weather"...
php artisan mcp:inspector weather

此命令启动 MCP Inspector 并提供客户端设置,你可以将其复制到 MCP 客户端以确保一切配置正确。如果你的 Web 服务器受认证中间件保护,连接时请确保包含所需的请求头,如 Authorization bearer 令牌。

单元测试

你可以为 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() 方法断言响应包含特定元数据:

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