🛰️ MCP
SunAdmin 接入 Laravel MCP,提供 MCP Server 工具自动发现、管理端调试入口和 AI Agent 工具适配能力。本页讲 MCP 工具创建与对接;AI 模型、知识库、用量记录等 AI 侧内容详见 AI。
🎯 本页目标
读完本章节后,应能掌握:
- MCP Server 入口和管理端调试接口分别提供哪些能力。
- MCP Tool 与 AI Agent Tool 适配的差异、各自放在哪里。
- 如何在模块中新增一个 MCP 工具并验证注册。
- 如何让管理端 AI 助手在对话中按需调用模块工具。
🔧 代码地图
| 文件 | 说明 |
|---|---|
AiMcpController.php | MCP 工具、调用、聊天和 RPC |
McpService.php | MCP 工具服务 |
SunAdminMcpServer.php | 官方 MCP Server |
modules/{Module}/Mcp/Tools | MCP Server 工具目录,自动发现 |
modules/{Module}/Ai/Tools | Laravel AI Agent 工具适配目录,自动发现 |
🛰️ MCP 能力入口
SunAdmin 同时提供两类 MCP 入口:
| 入口 | 用途 |
|---|---|
/mcp/sunadmin | 官方 Laravel MCP Server,给外部 MCP 客户端调用 |
/adminapi/system/ai/mcp/* | 后台管理端调试、工具调用、AI 助手聊天和 RPC |
官方 MCP Server 路由:
/mcp/sunadmin外部 MCP 客户端需要携带有效后台管理员令牌。后台调试接口同样需要管理员登录。
后台调试接口:
| URI | 说明 |
|---|---|
GET /adminapi/system/ai/mcp/tools | 查看已自动发现的 MCP 工具 |
POST /adminapi/system/ai/mcp/call | 直接调用某个 MCP 工具 |
POST /adminapi/system/ai/mcp/chat | 管理端 AI 助手对话,模型可按需调用工具 |
POST /adminapi/system/ai/mcp/rpc | 简化 JSON-RPC 入口,支持 tools/list、tools/call、ai/chat |
内置工具:
| 工具 | 说明 |
|---|---|
system.overview | 获取系统后台概览,包括用户、管理员、文章、AI 助手配置与最近 7 天 AI 用量 |
system.user_count | 获取用户数量统计 |
system.latest_articles | 获取最新文章 |
system.ai_settings | 获取 AI 助手基础配置 |
system.ai_usage_stats | 获取最近 N 天 AI 用量统计 |
🧭 工具目录规范
MCP Server 工具目录:
modules/{Module}/Mcp/ToolsLaravel AI Agent 工具适配类位于:
modules/{Module}/Ai/Tools两类工具的区别:
| 类型 | 目录 | 作用 | 必须实现 |
|---|---|---|---|
| MCP Tool | modules/{Module}/Mcp/Tools | 给 MCP Server、后台工具调试接口调用 | 继承 Laravel\Mcp\Server\Tool |
| AI Tool | modules/{Module}/Ai/Tools | 给 Laravel AI Agent 注入,让模型在对话中按需调用 | 实现 Laravel\Ai\Contracts\Tool |
如果工具只需要给 MCP 客户端或后台调试接口调用,只创建 MCP Tool 即可。如果希望“管理端 AI 助手聊天”也能让模型主动调用该能力,需要额外创建 AI Tool 适配类。
自动发现是宽松模式:
- 模块下没有
Mcp/Tools目录时会自动跳过,不需要创建空目录。 - 模块下没有
Ai/Tools目录时也会自动跳过,不影响 MCP 工具使用。 - 目录存在但没有符合继承或接口要求的工具类时,会返回空工具列表,不会抛异常。
- 只有调用某个不存在的工具名时,才会返回
MCP Tool [name] 未注册。
♻️ 工具发现缓存
MCP 工具和 AI Agent 工具会在运行时按模块目录自动发现。为了避免同一请求内反复扫描模块目录,框架会缓存本次运行周期内的工具类列表。
正常通过应用中心或命令行执行模块生命周期操作时,框架会自动清理模块运行时缓存:
# 安装或覆盖安装模块后会刷新模块注册、路由和 MCP 工具发现缓存
php artisan module:install {Module}
php artisan module:install {Module} --force
# 启用或停用模块后会刷新 MCP 工具发现缓存
php artisan module:enable {Module}
php artisan module:disable {Module}
# 卸载、删除或清理残留记录后会刷新 MCP 工具发现缓存
php artisan module:uninstall {Module}
php artisan module:delete {Module}开发时如果只是手动往已启用模块中新增 Mcp/Tools 或 Ai/Tools 文件,而没有执行模块安装、启用或停用命令,普通 PHP-FPM 下一次请求通常可以重新发现;Octane、队列 Worker、Tinker、Swoole 等常驻进程需要重启对应进程,或重新执行一次模块启用命令触发运行时缓存刷新:
# 常驻进程中新增工具后,可重新启用模块触发运行时缓存刷新
php artisan module:enable {Module}🧑💻 创建 MCP 工具示例
下面以 Shop 模块新增一个“订单数量统计”工具为例。实际项目中把 Shop 替换成你的模块名即可。
1. 创建 MCP Tool
文件路径:
modules/Shop/Mcp/Tools/ShopOrderCountTool.php示例代码:
<?php
namespace Modules\Shop\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\Attributes\Name;
use Laravel\Mcp\Server\Tool;
use Modules\Shop\Dao\OrderDao;
#[Name('shop.order_count')]
#[Description('获取商城订单数量统计,可按订单状态筛选。')]
class ShopOrderCountTool extends Tool
{
public function handle(Request $request, OrderDao $orderDao): Response
{
$status = $request->get('status');
$filters = [];
if ($status !== null && $status !== '') {
$filters['status'] = $status;
}
return Response::json([
'total' => $orderDao->count($filters),
]);
}
public function schema(JsonSchema $schema): array
{
return [
'status' => $schema->string()->description('订单状态,可选。不传则统计全部订单。'),
];
}
}说明:
#[Name('shop.order_count')]是工具对外暴露的唯一名称,建议使用module.ability格式。schema()用来告诉 MCP 客户端和模型这个工具接受哪些参数。handle()可以通过 Laravel 容器自动注入 Dao、Service 等依赖。- 返回值建议使用
Response::json(),只输出必要字段,不返回敏感数据。
2. 验证 MCP Tool 是否注册
登录后台后调用:
GET /adminapi/system/ai/mcp/tools返回的 data.tools 中应该能看到:
{
"name": "shop.order_count",
"description": "获取商城订单数量统计,可按订单状态筛选。"
}也可以直接调用工具:
POST /adminapi/system/ai/mcp/call
Content-Type: application/json
{
"name": "shop.order_count",
"arguments": {
"status": "paid"
}
}🤝 创建 AI Agent 工具适配示例
如果希望后台 AI 助手在聊天时自动调用 shop.order_count,需要再创建一个 Laravel AI Tool 适配类。
文件路径:
modules/Shop/Ai/Tools/ShopOrderCount.php示例代码:
<?php
namespace Modules\Shop\Ai\Tools;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Tool;
use Laravel\Ai\Tools\Request;
use Modules\Shop\Dao\OrderDao;
use Stringable;
class ShopOrderCount implements Tool
{
public function __construct(
protected OrderDao $orderDao,
) {
}
public function description(): Stringable|string
{
return '获取商城订单数量统计,可按订单状态筛选。';
}
public function handle(Request $request): Stringable|string
{
$arguments = $request->all();
$status = $arguments['status'] ?? null;
$filters = [];
if ($status !== null && $status !== '') {
$filters['status'] = $status;
}
return json_encode([
'total' => $this->orderDao->count($filters),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
public function schema(JsonSchema $schema): array
{
return [
'status' => $schema->string()->description('订单状态,可选。不传则统计全部订单。'),
];
}
}说明:
- MCP Tool 和 AI Tool 可以复用同一个 Dao / Service,避免两套业务逻辑。
- AI Tool 的
description()要写清楚工具能力,模型会根据描述判断是否调用。 - AI Tool 的
schema()要和 MCP Tool 保持一致,减少调试成本。 - AI Tool 返回字符串即可,推荐返回 JSON 字符串,方便模型理解结构化结果。
✅ 新增工具检查清单
- 工具名称使用
module.ability格式,例如system.user_count。 - MCP Tool 放在
modules/{Module}/Mcp/Tools。 - AI Tool 适配类放在
modules/{Module}/Ai/Tools,只有需要 AI 助手对话调用时才需要创建。 - 命名空间必须和目录一致,例如
Modules\Shop\Mcp\Tools。 - 入参 schema 要明确,避免模型猜字段。
- 工具只返回必要数据,不返回敏感字段。
- 涉及后台数据的工具必须走管理员认证。
- 工具执行失败要返回可理解错误,不吞异常。
- 管理端
/adminapi/system/ai/mcp/tools能列出工具后,再接入聊天。
🗂️ 安全与治理
- 管理端工具调用需要管理员 token。
- 工具只返回必要数据,不输出敏感字段(密钥、明文密码、未脱敏的个人信息)。
- 工具执行涉及写入或外部副作用时,应在
handle()中显式校验权限与业务前置条件。 - 模型输出不能直接作为高风险业务动作依据,例如支付、提现、权限变更。
🧯 排查顺序
| 问题 | 排查 |
|---|---|
| MCP 工具不出现 | 工具目录、类命名空间、继承类型、tools 接口、管理员认证 |
| 工具调用失败 | 工具入参 schema、handle 实现、异常日志 |
| AI 助手对话不调用工具 | 检查 Ai/Tools 适配类是否存在、description() 是否清晰 |
| 外部 MCP 客户端无法连接 | 检查 /mcp/sunadmin 路由、管理员令牌、CORS |
