什么是 MCP
Model Context Protocol(MCP) 是一个规范,让 AI 客户端(Claude、Cursor、GitHub Copilot 等)与应用之间以标准化协议通信。通过实现 MCP 服务器,AI 代理就可以访问你 Laravel 应用中的数据,或者执行相应的操作。
Laravel MCP 是在 Laravel 13 中新增的官方扩展包。它以 laravel/mcp 形式提供,覆盖构建 MCP 服务器所需的一整套能力。
MCP 服务器主要提供三类能力:
能力 说明 工具(Tools) AI 客户端可调用的函数。用于搜索、更新、对接外部 API 等 资源(Resources) AI 客户端可读取的数据或上下文信息 提示(Prompts) 可复用的提示模板
通过 Composer 安装扩展包。
composer require laravel/mcp
安装后运行 vendor:publish Artisan 命令来生成 routes/ai.php 文件。
php artisan vendor:publish --tag=ai-routes
该命令会创建 routes/ai.php,你可以在里面注册 MCP 服务器。
创建服务器
用 make:mcp-server Artisan 命令生成服务器类。
php artisan make:mcp-server WeatherServer
服务器类会生成到 app/Mcp/Servers 目录下。
<? 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
{
protected array $tools = [
// GetCurrentWeatherTool::class,
];
protected array $resources = [
// WeatherGuidelinesResource::class,
];
protected array $prompts = [
// DescribeWeatherPrompt::class,
];
}
注册服务器
创建服务器后,在 routes/ai.php 中注册。有 Web 服务器 和 本地服务器 两种注册方式。
Web 服务器
Web 服务器通过 HTTP POST 请求访问,适合远程 AI 客户端或基于 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 命令的形式运行,用于与 Claude Desktop 等本地 AI 客户端对接。
use App\Mcp\Servers\ WeatherServer ;
use Laravel\Mcp\Facades\ Mcp ;
Mcp :: local ( 'weather' , WeatherServer :: class );
本地服务器通常由 MCP 客户端自动启动。无需手动执行 mcp:start Artisan 命令。
工具是 AI 客户端可以调用的函数,用来获取数据、对接外部 API、操作数据库等。
创建工具
用 make:mcp-tool Artisan 命令生成工具类。
php artisan make:mcp-tool CurrentWeatherTool
将工具注册到服务器的 $tools 属性中。
use App\Mcp\Tools\ CurrentWeatherTool ;
use Laravel\Mcp\ Server ;
class WeatherServer extends Server
{
protected array $tools = [
CurrentWeatherTool :: class ,
];
}
一个基础的工具类示例:
<? 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
{
public function handle ( Request $request ) : Response
{
$location = $request -> get ( 'location' );
// 获取天气数据...
return Response :: text ( 'The weather is sunny, 22°C.' );
}
public function schema ( JsonSchema $schema ) : array
{
return [
'location' => $schema -> string ()
-> description ( 'The location to get the weather for.' )
-> required (),
];
}
}
工具的名称与说明
默认的名称与标题会根据类名自动推断。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)不会自动生成。它对 AI 模型理解如何调用工具至关重要,请务必写清晰、有意义的说明。
输入 Schema
schema 方法用来定义输入参数的 schema。可以使用 Laravel 提供的 JSON schema builder 指定类型与约束。
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 方法可以定义响应结构,帮助 AI 客户端更好地解析响应。
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 (),
];
}
handle 方法内可以使用 Laravel 的标准校验能力。
public function handle ( Request $request ) : Response
{
$validated = $request -> validate ([
'location' => 'required|string|max:100' ,
'units' => 'in:celsius,fahrenheit' ,
], [
'location.required' => 'You must specify a location. For example, "New York City" or "Tokyo".' ,
'units.in' => 'You must specify either "celsius" or "fahrenheit" for the units.' ,
]);
// 使用校验后的数据继续处理...
}
校验失败时,AI 客户端会根据错误信息重试。请提供具体、可执行的错误消息。
依赖注入
工具通过 Laravel 服务容器解析,因此可以在构造函数或 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
{
public function __construct (
protected WeatherRepository $weather ,
) {}
public function handle ( Request $request , WeatherRepository $weather ) : Response
{
$location = $request -> get ( 'location' );
$forecast = $weather -> getForecastFor ( $location );
return Response :: text ( "Forecast: { $forecast }" );
}
}
给工具加上注解,可以向 AI 客户端提供关于工具行为的额外提示。
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]表示工具可能会与外部实体交互
条件注册
通过实现 shouldRegister 方法可以在运行时按条件注册工具。
public function shouldRegister ( Request $request ) : bool
{
return $request ?-> user () ?-> subscribed () ?? false ;
}
返回 false 时该工具对 AI 客户端不可见。
工具需要返回一个 Laravel\Mcp\Response 实例。
return Response :: text ( 'Weather Summary: Sunny, 22°C' );
return Response :: error ( 'Unable to fetch weather data. Please try again.' );
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' );
// 直接从 storage 读取(MIME 类型自动识别)
return Response :: fromStorage ( 'weather/radar.png' );
public function handle ( Request $request ) : array
{
return [
Response :: text ( 'Weather Summary: Sunny, 22°C' ),
Response :: text ( "**Detailed Forecast** \n - Morning: 18°C \n - Afternoon: 25°C" ),
];
}
返回便于 AI 客户端解析的结构化数据。 return Response :: structured ([
'temperature' => 22.5 ,
'conditions' => 'Partly cloudy' ,
'humidity' => 65 ,
]);
长耗时处理中实时推送进度。 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 ));
}
}
提示(Prompts)
提示是可复用的提示模板,可以为 AI 客户端与语言模型的交互提供标准化的常用查询。
创建提示
php artisan make:mcp-prompt DescribeWeatherPrompt
将其注册到服务器的 $prompts 属性中。
use App\Mcp\Prompts\ DescribeWeatherPrompt ;
class WeatherServer extends Server
{
protected array $prompts = [
DescribeWeatherPrompt :: class ,
];
}
提示参数
arguments 方法用来定义提示的参数。
<? php
namespace App\Mcp\Prompts ;
use Laravel\Mcp\Server\ Prompt ;
use Laravel\Mcp\Server\Prompts\ Argument ;
class DescribeWeatherPrompt extends Prompt
{
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
{
public function handle ( Request $request ) : Response
{
$validated = $request -> validate ([
'tone' => 'required|string|max:50' ,
]);
$tone = $validated [ 'tone' ];
// 使用指定的 tone 生成提示...
}
}
校验失败时 AI 客户端会参考错误消息重试。请提供具体、可执行的消息。
$validated = $request -> validate ([
'tone' => [ 'required' , 'string' , 'max:50' ],
], [
'tone.*' => '请指定 tone,例如 "formal"、"casual"、"humorous"。' ,
]);
依赖注入
提示通过 Laravel 服务容器解析,构造函数与 handle 方法都可以类型提示注入依赖。
<? php
namespace App\Mcp\Prompts ;
use App\Repositories\ WeatherRepository ;
use Laravel\Mcp\Server\ Prompt ;
class DescribeWeatherPrompt extends Prompt
{
public function __construct (
protected WeatherRepository $weather ,
) {}
}
handle 方法同样支持依赖注入。
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
{
public function shouldRegister ( Request $request ) : bool
{
return $request ?-> user () ?-> subscribed () ?? false ;
}
}
返回 false 时提示对 AI 客户端不可见,也无法调用。
提示响应
提示的 handle 方法可以返回用户消息与助手消息,通过 asAssistant() 标记为助手侧消息。
<? php
namespace App\Mcp\Prompts ;
use Laravel\Mcp\ Request ;
use Laravel\Mcp\ Response ;
use Laravel\Mcp\Server\ Prompt ;
class DescribeWeatherPrompt extends Prompt
{
public function handle ( Request $request ) : 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 Tokyo?' ;
return [
Response :: text ( $systemMessage ) -> asAssistant (),
Response :: text ( $userMessage ),
];
}
}
资源是 AI 客户端可以作为上下文读取的数据或信息。可以提供文档、配置、动态数据等,帮助提升 AI 回答的质量。
创建资源
php artisan make:mcp-resource WeatherGuidelinesResource
注册到服务器的 $resources 属性中。
use App\Mcp\Resources\ WeatherGuidelinesResource ;
class WeatherServer extends Server
{
protected array $resources = [
WeatherGuidelinesResource :: class ,
];
}
<? php
namespace App\Mcp\Resources ;
use Laravel\Mcp\ Request ;
use Laravel\Mcp\ Response ;
use Laravel\Mcp\Server\Attributes\ Description ;
use Laravel\Mcp\Server\ Resource ;
#[ Description ( 'Comprehensive guidelines for using the Weather API.' )]
class WeatherGuidelinesResource extends Resource
{
public function handle ( Request $request ) : Response
{
$guidelines = "# Weather API Guidelines \n\n - Always specify a location..." ;
return Response :: text ( $guidelines );
}
}
URI 与 MIME 类型
默认会根据类名生成 URI(例如 weather://resources/weather-guidelines)。可通过 Uri 与 MimeType 属性自定义。
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 变量的动态资源,可实现 HasUriTemplate 接口。
<? 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
{
public function uriTemplate () : UriTemplate
{
return new UriTemplate ( 'file://users/{userId}/files/{fileId}' );
}
public function handle ( Request $request ) : Response
{
$userId = $request -> get ( 'userId' );
$fileId = $request -> get ( 'fileId' );
// 读取并返回文件内容...
return Response :: text ( "File { $fileId } for user { $userId }" );
}
}
URI 中的变量会自动写入请求中,可以通过 get 方法取出。
资源请求
与工具或提示不同,资源不能定义输入 schema 或参数。但在 handle 方法中依然可以通过请求对象访问请求信息。
<? php
namespace App\Mcp\Resources ;
use Laravel\Mcp\ Request ;
use Laravel\Mcp\ Response ;
use Laravel\Mcp\Server\ Resource ;
class WeatherGuidelinesResource extends Resource
{
public function handle ( Request $request ) : Response
{
// 访问请求信息...
}
}
资源的依赖注入
资源同样通过 Laravel 服务容器解析,构造函数或 handle 方法都可以类型提示。
<? php
namespace App\Mcp\Resources ;
use App\Repositories\ WeatherRepository ;
use Laravel\Mcp\Server\ Resource ;
class WeatherGuidelinesResource extends Resource
{
public function __construct (
protected WeatherRepository $weather ,
) {}
}
public function handle ( WeatherRepository $weather ) : Response
{
return Response :: text ( $weather -> guidelines ());
}
资源注解
资源可以带上受众、优先级、最后修改时间等注解。
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
{
public function shouldRegister ( Request $request ) : bool
{
return $request ?-> user () ?-> subscribed () ?? false ;
}
}
返回 false 时资源对 AI 客户端不可见,也无法访问。
资源响应
资源同样需要返回 Laravel\Mcp\Response 实例。
文本内容用 text 方法。
return Response :: text ( $weatherData );
资源链接响应
resourceLink 方法可以返回一个资源链接。与嵌入式资源不同,它返回的是 AI 客户端可独立获取的 URI 指针。
return Response :: resourceLink (
uri : 'file:///data/report.json' ,
name : 'monthly-report' ,
mimeType : 'application/json' ,
);
也可以传入已注册的资源类或实例,会自动继承其 URI、name、title、description、MIME 类型。
return Response :: resourceLink ( new WeatherForecastResource );
Blob 响应
返回二进制内容用 blob 方法。MIME 类型通过资源的 #[MimeType] 属性设置。
return Response :: blob ( file_get_contents ( storage_path ( 'weather/radar.png' )));
#[ MimeType ( 'image/png' )]
class WeatherGuidelinesResource extends Resource
{
// ...
}
错误响应
要返回错误,使用 error 方法。
return Response :: error ( '无法获取指定地点的天气数据。' );
应用(Apps)
Laravel MCP 支持 MCP Apps 。这是 Model Context Protocol 的一个扩展,允许工具在受支持的宿主中,通过沙箱 iframe 渲染交互式的 HTML 应用。你可以基于此构建仪表盘、表单、可视化和其他富交互体验,而不局限于纯文本响应。
MCP 应用由两个协作的部分组成:
App 资源 — 返回应用的独立 HTML。
工具 — 通过 #[RendersApp] 属性关联到 App 资源。工具被调用时,宿主会拉取并渲染关联的资源。
创建 App 资源
用 make:mcp-app-resource Artisan 命令生成 App 资源。
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 scheme 与 text/html;profile=mcp-app MIME 类型。与其他资源一样,也需要注册到服务器的 $resources 数组中。
生成的 Blade 视图使用 <x-mcp::app> 组件。它会渲染一个包含客户端 MCP SDK 的完整 HTML 文档。
< 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 规范 。
通过工具渲染应用
要展示 App 资源,需要在工具上使用 #[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 两个成员,默认二者都启用。给 UI 直接调用的后端 action 用 [Visibility::App];如果不希望 UI 调用工具,就用 [Visibility::Model]。
应用的配置
App 资源上的 #[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 枚举预置了 Library::Tailwind、Library::Alpine 等常用前端库的 CDN 脚本,其 CDN 源会自动合并到 CSP 中。Permission 枚举覆盖 Camera、Microphone、Geolocation、ClipboardWrite 等浏览器权限。
如果需要动态配置,可以重写资源的 appMeta 方法,使用 Laravel\Mcp\Server\Ui 命名空间下的 AppMeta、Csp、Permissions 流式构建器。
使用 Boost 开发应用
Laravel MCP 提供了专门用于构建 MCP Apps 的 Boost skill 参考。安装 Laravel Boost 后,AI 编码代理可以调用 mcp-development skill,自动生成 App 资源、Blade 视图以及关联的工具。
关于协议的完整参考(包括客户端 API 与 schema 细节),请查看官方的 MCP Apps 文档 。
元数据
可以给工具、资源、提示的响应附加 MCP 规范中的 _meta 字段。
// 给响应内容附加元数据
return Response :: text ( 'The weather is sunny.' )
-> withMeta ([ 'source' => 'weather-api' , 'cached' => true ]);
要为整个响应外壳附加元数据,使用 Response::make。
return Response :: make (
Response :: text ( 'The weather is sunny.' )
) -> withMeta ([ 'request_id' => '12345' ]);
若要给工具、资源、提示的类本身附加元数据,定义 $meta 属性即可。
class CurrentWeatherTool extends Tool
{
protected ? array $meta = [
'version' => '2.0' ,
'author' => 'Weather Team' ,
];
}
MCP 客户端可以显示服务器及其原语的图标。可以通过 Icon 属性为 Server、Tool、Resource、Prompt 声明图标。
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
{
/**
* 获取工具的图标。
*
* @return array < int , Icon>
*/
public function icons () : array
{
return [
Icon :: from ( 'mcp/tool.png' , mimeType : 'image/png' ),
];
}
}
通过属性与 icons 方法定义的图标会自动合并。图标路径按下列规则解析:
带 https:、data: 等 URI scheme 的路径原样使用。
相对路径通过 Laravel 的 asset helper 解析成 URL。
Web 服务器可以使用 Laravel 标准的中间件来认证。
Sanctum
使用 Laravel Sanctum 的 token 认证。MCP 客户端会发送 Authorization: Bearer <token> 头。
use App\Mcp\Servers\ WeatherServer ;
use Laravel\Mcp\Facades\ Mcp ;
Mcp :: web ( '/mcp/weather' , WeatherServer :: class )
-> middleware ( 'auth:sanctum' );
OAuth 2.1
使用 Laravel Passport 的 OAuth 认证,适合需要更强安全性的场景。
use App\Mcp\Servers\ WeatherServer ;
use Laravel\Mcp\Facades\ Mcp ;
Mcp :: oauthRoutes ();
Mcp :: web ( '/mcp/weather' , WeatherServer :: class )
-> middleware ( 'auth:api' );
使用 OAuth 时,需要发布 Passport 的授权视图并在服务提供者中配置。
php artisan vendor:publish --tag=mcp-views
// AppServiceProvider::boot()
use Laravel\Passport\ Passport ;
Passport :: authorizationView ( function ( $parameters ) {
return view ( 'mcp.authorize' , $parameters );
});
通过 $request->user() 获取当前已认证用户,就可以在工具或资源中进行授权检查。
public function handle ( Request $request ) : Response
{
if ( ! $request -> user () -> can ( 'read-weather' )) {
return Response :: error ( 'Permission denied.' );
}
// 继续处理...
}
MCP 客户端
Laravel MCP 不只能构建服务器,也提供了连接其他 MCP 服务器的客户端。借助客户端,你可以发现并调用外部 MCP 服务器暴露的工具,特别适合把外部 MCP 服务器的能力提供给 AI 代理 。
连接到服务器
对于可以通过 HTTP 访问的 MCP 服务器,使用 Client::web 方法并传入服务器 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' ]);
客户端采用延迟连接(lazy connect),在首次获取或调用工具时自动建立连接。若要手动管理连接,可以使用 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 Facade 完成。
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 token 保护的 Web MCP 服务器,可以使用 withToken。它接收 token 字符串或延迟解析的闭包。
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。
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,客户端会自动注册。
然后在 routes/ai.php 中使用 oAuthRoutesFor 方法为命名客户端注册 OAuth 路由。传入的闭包会在授权码交换完 access token 后接收客户端名与 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' );
});
这样会注册两条命名路由:将用户重定向到授权服务器的 connect 路由(mcp.oauth.{client}.connect),以及交换授权码并调用处理器的 callback 路由(mcp.oauth.{client}.callback)。二者默认使用 web 中间件组(可通过 middleware 参数覆盖)。
开始授权流程时,把用户重定向到 connect 路由即可。
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 (); // 响应的文本内容
( string ) $result ; // 等同于 text()
$result -> isError ; // 工具是否报告了错误
$result -> structuredContent ; // 结构化内容(如果有)
也可以直接从列表中获取的工具实例上调用。
$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 (); // 消息文本内容
( string ) $result ; // 等同于 text()
$result -> messages ; // 提示返回的原始消息
$result -> description ; // 提示的说明(如有)
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 (); // 资源内容(base64 的 blob 会自动解码)
( string ) $result ; // 等同于 content()
$result -> mimeType (); // 资源的 MIME 类型(如有)
$result -> contents ; // 资源返回的原始内容
MCP Inspector
用交互式调试工具 “MCP Inspector” 可以验证 MCP 服务器的行为。
# Web 服务器
php artisan mcp:inspector mcp/weather
# 本地服务器(name 为 "weather" 时)
php artisan mcp:inspector weather
执行命令会启动 MCP Inspector,并允许复制客户端配置。若配置了认证中间件,请连接时带上 Authorization 头。
单元测试
你可以为工具、资源、提示编写单元测试。
test ( 'tool' , function () {
$response = WeatherServer :: tool ( CurrentWeatherTool :: class , [
'location' => 'Tokyo' ,
'units' => 'celsius' ,
]);
$response
-> assertOk ()
-> assertSee ( 'The current weather in Tokyo is 22°C and sunny.' );
});
public function test_tool () : void
{
$response = WeatherServer :: tool ( CurrentWeatherTool :: class , [
'location' => 'Tokyo' ,
'units' => 'celsius' ,
]);
$response
-> assertOk ()
-> assertSee ( 'The current weather in Tokyo is 22°C and sunny.' );
}
提示和资源同样可以测试。
$response = WeatherServer :: prompt ( DescribeWeatherPrompt :: class , [ 'tone' => 'casual' ]);
$response = WeatherServer :: resource ( WeatherGuidelinesResource :: class );
要以已认证用户的身份执行,使用 actingAs。
$response = WeatherServer :: actingAs ( $user ) -> tool ( CurrentWeatherTool :: class , [ ... ]);
主要断言方法如下:
$response -> assertOk (); // 断言没有错误
$response -> assertSee ( '...' ); // 断言包含特定文本
判断是否有错误,使用 assertHasErrors / assertHasNoErrors。
$response -> assertHasErrors ();
$response -> assertHasErrors ([
'Something went wrong.' ,
]);
$response -> assertHasNoErrors ();
也可以校验工具 / 资源 / 提示的名称、标题、说明。
$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 ();