Laravel MCP
简介
Laravel MCP 为 AI 客户端通过 模型上下文协议 提供了一种简单而优雅的方式与 Laravel 应用程序进行交互。它提供了一个富有表现力、流畅的界面,用于定义服务器、工具、资源和提示,从而实现与应用程序的人工智能驱动的交互。
安装
首先,使用 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;
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 服务器:
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;
Mcp::web('/mcp/weather', WeatherServer::class);就像普通路由一样,你可以应用中间件来保护你的网络服务器:
Mcp::web('/mcp/weather', WeatherServer::class)
->middleware(['throttle:mcp']);本地服务器
本地服务器作为 Artisan 命令运行,非常适合开发、测试或本地 AI 助手集成。使用 local 方法注册本地服务器:
use App\Mcp\Servers\WeatherServer;
use Laravel\Mcp\Facades\Mcp;
Mcp::local('weather', WeatherServer::class);注册后,你通常不需要自己手动运行 mcp:start。相反,请配置 MCP 客户端(AI 代理)来启动服务器。mcp:start 命令设计为由客户端调用,由客户端按需处理服务器的启动与停止:
php artisan mcp:start weather工具
工具使你的服务器能够公开 AI 客户端可以调用的功能。它们允许语言模型执行操作、运行代码或与外部系统交互:
创建工具
要创建工具,请运行 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.';
//
}INFO
描述是该工具元数据的关键部分,因为它可以帮助 AI 模型了解何时以及如何有效地使用该工具。
工具输入 Schema
工具可以定义输入模式来指定它们从 AI 客户端接受哪些参数。使用 Laravel 的 Illuminate\Contracts\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->enum(['celsius', 'fahrenheit'])
->description('The temperature units to use.')
->default('celsius'),
];
}
}验证工具参数
JSON 模式定义为工具参数提供了基本结构,但你可能还想强制执行更复杂的验证规则。
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 客户端提供额外的元数据。这些注释有助于人工智能模型理解该工具的行为和功能。通过属性将注释添加到工具中:
<?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 | 描述 |
|---|---|---|
#[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 方法返回 generator。这使得可以在最终响应之前向客户端发送中间更新:
<?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(服务器发送事件)流,将每条生成的消息作为事件发送到客户端。
提示词
Prompts 使你的服务器能够共享可重用的提示模板,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.';
//
}INFO
描述是提示元数据的关键部分,因为它可以帮助 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助手,而将常规消息视为用户输入。
资源
Resources 使你的服务器能够公开 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.';
//
}INFO
描述是资源元数据的关键部分,因为它有助于 AI 模型了解何时以及如何有效地使用资源。
资源 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\Resource;
class WeatherGuidelinesResource extends Resource
{
/**
* The resource's URI.
*/
protected string $uri = 'weather://resources/guidelines';
/**
* The resource's MIME type.
*/
protected string $mimeType = 'application/pdf';
}URI 和 MIME 类型帮助 AI 客户端确定如何正确处理和解释资源内容。
资源请求
与工具和提示不同,资源不能定义输入 schema 或参数。不过,你仍可在资源的 handle 方法中与请求对象交互:
<?php
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方法返回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);
}Blob 响应
要返回 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.');身份验证
你可以像对待路由一样,使用中间件对 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 路由:
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 授权视图:
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 代理的身份验证尝试。
INFO
在这种情况下,我们只是使用 OAuth 作为底层可验证模型的转换层。我们忽略了 OAuth 的许多方面,例如范围。
使用已有 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 服务器,只需将 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');自定义 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 Inspector 或通过编写单元测试来测试 MCP 服务器。
MCP Inspector
MCP Inspector 是一个用于测试和调试 MCP 服务器的交互式工具。使用它连接到你的服务器、验证身份验证并尝试工具、资源和提示。
你可以为任何已注册的服务器运行检查器(例如名为「weather」的本地服务器):
php artisan mcp:inspector weather此命令启动 MCP Inspector 并提供客户端设置,你可以将其复制到 MCP 客户端以确保所有内容均配置正确。如果你的 Web 服务器受身份验证中间件保护,请确保在连接时包含所需的标头,例如 Authorization 不记名令牌。
单元测试
你可以为 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();