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\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);就像普通路由一样,你可以应用中间件来保护你的网络服务器:
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
{
//
}INFO
描述是该工具元数据的关键部分,因为它可以帮助 AI 模型了解何时以及如何有效地使用该工具。
工具输入 Schema
工具可以定义输入模式来指定它们从 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'),
];
}
}工具输出 Schema
工具可以定义输出模式来指定其响应的结构。这可以更好地与需要可解析工具结果的人工智能客户端集成。使用 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 模式定义为工具参数提供了基本结构,但你可能还想强制执行更复杂的验证规则。
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
{
//
}可用的注释包括:
| 注解 | 类型 | 描述 |
|---|---|---|
#[IsReadOnly] | 布尔值 | 指示该工具不会修改其环境。 |
#[IsDestructive] | 布尔值 | 指示该工具可能会执行破坏性更新(仅在非只读时才有意义)。 |
#[IsIdempotent] | 布尔值 | 表示使用相同参数重复调用没有附加效果(当非只读时)。 |
#[IsOpenWorld] | 布尔值 | 指示该工具可以与外部实体交互。 |
可以使用布尔参数显式设置注释值:
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 方法返回 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 属性自定义这些值:
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
{
//
}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 属性自定义这些值:
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
{
//
}INFO
描述是资源元数据的关键部分,因为它有助于 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::User、Role::Assistant 或两者)。 |
#[Priority] | 漂浮 | 0.0 到 1.0 之间的数值分数表示资源重要性。 |
#[LastModified] | 细绳 | 显示资源上次更新时间的 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 和名称。与嵌入资源不同,资源链接返回 AI 客户端独立获取的 URI 指针:
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 应用规范所需的 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 应用程序规范。
从工具渲染应用
要显示应用程序资源,请使用 #[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 包含用于构建 MCP 应用程序的专用 Boost 技能参考。如果你安装了 Laravel Boost,你的 AI 编码代理可以调用 mcp-development 技能并要求它为你构建应用程序资源、Blade 视图和链接工具。
有关完整的协议参考,包括完整的客户端 API 和架构详细信息,请参阅官方 MCP 应用文档。
元数据
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 模型、新的身份验证防护和护照钥匙。
接下来,你应该发布 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 客户端
除了构建服务器之外,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');命名客户端每个请求都会解析一次,并在请求生命周期结束时自动断开连接。
客户端认证
要连接到受不记名令牌保护的 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'),
));INFO
当 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 服务器的交互式工具。使用它连接到你的服务器、验证身份验证并尝试工具、资源和提示。
你可以为任何注册的服务器运行检查器:
# Web server...
php artisan mcp:inspector mcp/weather
# Local server named "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();