Laravel MCP 提供了一种简单而优雅的方式,供 AI 客户端通过 Model Context Protocol 与您的 Laravel 应用程序进行交互。它提供了一个富有表现力、流畅的接口,用于定义服务器、工具、资源和提示,以实现与您的应用程序的 AI 驱动交互。
要开始使用,使用 Composer 包管理器将 Laravel MCP 安装到你的项目中:
composer require laravel/mcp安装 Laravel MCP 后,执行 vendor:publish Artisan 命令以发布 routes/ai.php 文件,您将在该文件中定义您的 MCP 服务器:
php artisan vendor:publish --tag=ai-routes此命令创建您应用程序的 routes 目录中的 routes/ai.php 文件,您将使用它来注册您的 MCP 服务器。
您可以使用 make:mcp-server Artisan 命令创建一个 MCP 服务器。服务器作为中央通信点,将 MCP 功能(如工具、资源和提示)暴露给 AI 客户端:
php artisan make:mcp-server WeatherServer此命令将在 app/Mcp/Servers 目录下创建一个新的服务器类。生成的服务器类扩展了 Laravel MCP 的基础 Laravel\Mcp\Server 类,并提供用于注册工具、资源和提示的属性:
<?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 服务器是最常见的服务器类型,可通过 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 检查器。
工具使您的服务器能够提供供 AI 客户端调用的功能。它们允许语言模型执行操作、运行代码或与外部系统交互:
<?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 命令:
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 属性来自定义这些值:
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 属性来提供有意义的描述:
class CurrentWeatherTool extends Tool
{
/**
* The tool's description.
*/
protected string $description = 'Fetches the current weather forecast for a specified location.';
//
}[!NOTE]
描述是工具元数据的关键部分,因为它有助于AI模型理解何时以及如何有效地使用该工具。
工具可以定义输入模式,以指定它们从 AI 客户端接受哪些参数。 使用 Laravel 的 Illuminate\JsonSchema\JsonSchema 构建器来定义你的工具的输入要求:
<?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->string()
->enum(['celsius', 'fahrenheit'])
->description('The temperature units to use.')
->default('celsius'),
];
}
}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
{
//
}可用的注解包括:
| Annotation | Type | Description |
|---|---|---|
#[IsReadOnly] | boolean | Indicates the tool does not modify its environment. |
#[IsDestructive] | boolean | Indicates the tool may perform destructive updates (only meaningful when not read-only). |
#[IsIdempotent] | boolean | Indicates repeated calls with same arguments have no additional effect (when not read-only). |
#[IsOpenWorld] | boolean | Indicates the tool may interact with external entities. |
您可以通过在工具类中实现 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.');工具可以通过返回 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')
];
}对于长时间运行的操作或实时数据流,工具可以从其 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));
}
}
}当使用基于网络的服务器时,流式响应会自动打开一个SSE(服务器发送事件)流,将每个生成的消息作为事件发送给客户端。
提示词 使你的服务器能够共享可复用的提示词模板,供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 属性来定制这些值:
class DescribeWeatherPrompt extends Prompt
{
/**
* The prompt's name.
*/
protected string $name = 'weather-assistant';
/**
* The prompt's title.
*/
protected string $title = 'Weather Assistant Prompt';
// ...
}提示描述不会自动生成。你应该始终通过在你的提示上定义 $description 属性来提供有意义的描述:
class DescribeWeatherPrompt extends Prompt
{
/**
* The prompt's description.
*/
protected string $description = 'Generates a natural-language explanation of the weather for a given location.';
//
}[!NOTE]
描述是提示元数据的关键部分,因为它有助于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 属性来自定义这些值:
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 属性来提供一个有意义的描述:
class WeatherGuidelinesResource extends Resource
{
/**
* The resource's description.
*/
protected string $description = 'Comprehensive guidelines for using the Weather API.';
//
}[!NOTE]
描述是资源元数据的关键部分,因为它有助于AI模型理解何时以及如何有效地使用该资源。
每个资源都由一个唯一的URI标识,并具有一个关联的MIME类型,该类型帮助AI客户端理解资源的格式。
默认情况下, 资源的 URI 是根据资源名称生成的, 因此 WeatherGuidelinesResource 的 URI 将是 weather://resources/weather-guidelines. 默认的 MIME 类型是 text/plain.
您可以自定义这些值,通过在您的资源上定义 $uri 和 $mimeType 属性:
<?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
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);
}
}您可以通过在您的资源类中实现 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 方法返回假时,它将不会出现在可用资源列表中,并且无法被 AI 客户端访问。
资源必须返回 Laravel\Mcp\Response 的一个实例。Response 类提供了多个便捷方法用于创建不同类型的响应:
对于简单的文本内容,请使用 text 方法:
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
/**
* Handle the resource request.
*/
public function handle(Request $request): Response
{
// ...
return Response::text($weatherData);
}要返回 blob 内容,请使用 blob 方法,并提供 blob 内容:
return Response::blob(file_get_contents(storage_path('weather/radar.png')));当返回 blob 内容时,MIME 类型将由资源类上 $mimeType 属性的值决定:
<?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()方法:
return Response::error('Unable to fetch weather data for the specified location.');Laravel MCP 也支持 _meta 字段,该字段如 MCP 规范中所定义,并且某些 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\Tool;
class CurrentWeatherTool extends Tool
{
protected string $description = 'Fetches the current weather forecast.';
protected ?array $meta = [
'version' => '2.0',
'author' => 'Weather Team',
];
// ...
}就像路由一样,你可以使用中间件来验证web MCP服务器。为你的MCP服务器添加认证将要求用户在使用服务器的任何功能之前进行认证。
有两种方式可以验证对 MCP 服务器的访问:通过 Laravel Sanctum 或通过 Authorization HTTP 标头传递的任何令牌进行简单的基于令牌的身份验证。或者,您可以使用 Laravel Passport 进行 OAuth 身份验证。
保护您的基于网络的MCP服务器最稳健的方式是通过OAuth,使用Laravel Passport。
当通过 OAuth 认证您的 MCP 服务器时,在您的 routes/ai.php 文件中调用 Mcp::oauthRoutes 方法,以注册所需的 OAuth2 发现和客户端注册路由。然后,在您的 routes/ai.php 文件中,将 Passport 的 auth:api 中间件应用于您的 Mcp::web 路由:
use App\Mcp\Servers\WeatherExample;
use Laravel\Mcp\Facades\Mcp;
Mcp::oauthRoutes();
Mcp::web('/mcp/weather', WeatherExample::class)
->middleware('auth:api');如果您的应用程序尚未开始使用 Laravel Passport,请遵循 Passport 的 安装和部署指南 将 Passport 添加到您的应用程序。您应该拥有一个 OAuthenticatable 模型、新的身份验证守卫,以及 Passport 密钥,然后才能继续。
接下来,你应该发布 Laravel MCP 提供的 Passport 授权视图:
php artisan vendor:publish --tag=mcp-views然后,指示 Passport 使用 Passport::authorizationView 方法来使用此视图。通常,此方法应该在您的应用程序的 AppServiceProvider 的 boot 方法中调用:
use Laravel\Passport\Passport;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Passport::authorizationView(function ($parameters) {
return view('mcp.authorize', $parameters);
});
}此视图将在认证期间向最终用户显示,以拒绝或批准AI代理的认证尝试。
[!NOTE]
在这种情况下,我们只是将 OAuth 用作与底层可认证模型交互的转换层。我们忽略了 OAuth 的许多方面,例如作用域。
如果您的应用程序已在使用 Laravel Passport,Laravel MCP 应能与您现有的 Passport 安装无缝协作,但目前不支持自定义作用域,因为 OAuth 主要用作底层可认证模型的转换层。
Laravel MCP, 通过上面讨论的 Mcp::oauthRoutes 方法,添加、通告并使用一个 mcp:use 作用域。
OAuth2.1 是 Model Context Protocol 规范中记录的认证机制,并且是 MCP 客户端中最广泛支持的。因此,我们建议在可能的情况下使用 Passport。
如果您的应用程序已在使用 Sanctum 那么添加 Passport 可能会很麻烦。在这种情况下,我们建议使用 Sanctum 而不使用 Passport,直到您有明确的、必要的仅支持 OAuth 的 MCP 客户端使用需求。
如果您想使用 Sanctum, 只需将 Sanctum 的认证中间件添加到您的服务器中,在您的 routes/ai.php 文件. 然后, 确保您的 MCP 客户端提供一个 Authorization: Bearer <token> 标头,以确保成功认证:
use App\Mcp\Servers\WeatherExample;
use Laravel\Mcp\Facades\Mcp;
Mcp::web('/mcp/demo', WeatherExample::class)
->middleware('auth:sanctum');如果您的应用程序发布自己的自定义 API 令牌,您可以通过向您的 Mcp::web 路由分配任何您想要的中间件来认证您的 MCP 服务器。您的自定义中间件可以手动检查 Authorization 头部,以认证传入的 MCP 请求。
您可以通过 $request->user() 方法访问当前已认证的用户,从而可以在您的 授权检查 MCP 工具和资源中执行:
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
/**
* Handle the tool request.
*/
public function handle(Request $request): Response
{
if (! $request->user()->can('read-weather')) {
return Response::error('Permission denied.');
}
// ...
}您可以使用内置的 MCP Inspector 或通过编写单元测试来测试您的 MCP 服务器。
MCP 检查器 是一个用于测试和调试您的 MCP 服务器的交互式工具。使用它来连接到您的服务器、验证身份验证,并试用工具、资源和提示。
你可以为任何已注册的服务器运行检查器:
# Web server...
php artisan mcp:inspector mcp/weather
# Local server named "weather"...
php artisan mcp:inspector weather此命令启动 MCP Inspector 并提供客户端设置,您可以将其复制到您的 MCP 客户端中以确保一切都配置正确。如果您的 Web 服务器受身份验证中间件保护,请确保在连接时包含所需的请求头,例如 Authorization bearer token。
你可以为你的 MCP 服务器、工具、资源和提示编写单元测试。
要开始,创建一个新的测试用例,并在注册它的服务器上调用所需的原语。例如,要在 WeatherServer 上测试一个工具:
test('tool', function () {
$response = WeatherServer::tool(CurrentWeatherTool::class, [
'location' => 'New York City',
'units' => 'fahrenheit',
]);
$response
->assertOk()
->assertSee('The current weather in New York City is 72°F and sunny.');
});/**
* Test a tool.
*/
public function test_tool(): void
{
$response = WeatherServer::tool(CurrentWeatherTool::class, [
'location' => 'New York City',
'units' => 'fahrenheit',
]);
$response
->assertOk()
->assertSee('The current weather in New York City is 72°F and sunny.');
}同样,您可以测试提示和资源:
$response = WeatherServer::prompt(...);
$response = WeatherServer::resource(...);您也可以作为已认证用户进行操作,通过在调用原始操作之前链式调用 actingAs 方法:
$response = WeatherServer::actingAs($user)->tool(...);一旦您收到响应,您可以使用各种断言方法来验证响应的内容和状态。
您可以使用 assertOk 方法断言响应成功。这会检查响应是否没有任何错误:
$response->assertOk();您可以使用 assertSee 方法断言响应包含特定文本:
$response->assertSee('The current weather in New York City is 72°F and sunny.');你可以使用 assertHasErrors 方法断言响应包含错误:
$response->assertHasErrors();
$response->assertHasErrors([
'Something went wrong.',
]);您可以使用 assertHasNoErrors 方法断言响应不包含错误:
$response->assertHasNoErrors();你可以断言响应包含特定的元数据,使用 assertName()、assertTitle() 和 assertDescription() 方法:
$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();