1. 项目概述:构建基于Laravel Workflow的MCP服务
在现代化应用开发中,我们经常需要处理长时间运行的异步任务,同时还要为AI客户端提供结构化的访问能力。这正是Laravel Workflow与MCP(Machine Client Protocol)结合的绝佳场景。我最近在实际项目中实现了这套方案,它完美解决了异步任务管理和AI集成两大痛点。
Laravel Workflow是一个强大的工作流引擎,擅长处理持久化、有状态的任务执行。而MCP则是专门为AI客户端设计的协议,能够将外部能力封装成结构化工具。当二者结合时,我们可以实现:
- 异步AI操作:启动工作流后继续对话,稍后再检查结果
- 可靠执行:工作流能够在崩溃、重试和长时间等待中保持状态
- 完整可观测性:通过Waterline仪表板跟踪每个工作流状态
- 无状态服务:MCP服务器不保存状态,由客户端跟踪工作流ID
这种模式特别适合需要AI辅助完成复杂任务的场景,比如数据处理、报告生成或系统管理。接下来,我将详细分享这套系统的实现细节和实战经验。
2. 核心架构设计
2.1 技术选型考量
选择Laravel Workflow作为底层引擎有几个关键优势:
- 状态持久化:工作流状态自动保存到数据库,即使服务器重启也不会丢失进度
- 内置重试机制:任务失败后可以自动重试,非常适合不稳定的外部API调用
- 可视化监控:与Laravel Waterline无缝集成,提供直观的工作流状态展示
而MCP协议则为AI交互提供了标准化接口,使得Copilot等AI助手能够:
- 发现可用工具(工作流)
- 获取工具使用说明和参数结构
- 以标准化格式调用工具并获取结果
2.2 系统组件设计
我们需要构建三个核心工具组件:
| 工具名称 | 功能描述 | 使用场景 |
|---|---|---|
| list_workflows | 列出可用工作流及最近执行记录 | 发现可用功能,检查历史任务 |
| start_workflow | 启动指定工作流并返回跟踪ID | 初始化异步任务 |
| get_workflow_result | 检查工作流状态并获取结果 | 轮询任务进度,获取最终输出 |
这种设计模拟了人类委派任务的自然模式:"开始这个报告,我稍后再检查。"AI助手可以像人类助手一样启动任务,然后在适当的时候检查完成情况。
3. 详细实现步骤
3.1 环境准备与安装
首先确保你的Laravel项目已经配置好队列系统(推荐使用Redis或数据库驱动):
bash复制composer require laravel/mcp
composer require workflow/laravel-workflow
php artisan vendor:publish --tag=workflow-config
php artisan vendor:publish --tag=ai-routes
这组命令会:
- 安装MCP核心包
- 添加Laravel Workflow支持
- 发布工作流配置文件
- 创建AI路由文件(routes/ai.php)
提示:在生产环境中,记得配置好队列工作者(php artisan queue:work)并设置进程管理工具如Supervisor,确保任务能够持续处理。
3.2 创建MCP服务器
生成工作流服务器骨架:
bash复制php artisan make:mcp-server WorkflowServer
然后完善服务器逻辑:
php复制namespace App\Mcp\Servers;
use App\Mcp\Tools\GetWorkflowResultTool;
use App\Mcp\Tools\ListWorkflowsTool;
use App\Mcp\Tools\StartWorkflowTool;
use Laravel\Mcp\Server;
class WorkflowServer extends Server
{
protected string $name = 'Laravel Workflow Server';
protected string $version = '1.0.0';
protected string $instructions = <<<'MARKDOWN'
## 典型使用模式
1. 调用`list_workflows`查看可用工作流
2. 调用`start_workflow`启动工作流
3. 保存返回的`workflow_id`
4. 定期调用`get_workflow_result`检查状态
5. 当状态为`WorkflowCompletedStatus`时获取输出
## 状态值说明
- `WorkflowCreatedStatus` - 已创建
- `WorkflowRunningStatus` - 执行中
- `WorkflowCompletedStatus` - 已完成
- `WorkflowFailedStatus` - 已失败
MARKDOWN;
protected array $tools = [
ListWorkflowsTool::class,
StartWorkflowTool::class,
GetWorkflowResultTool::class,
];
}
这段代码定义了:
- 服务器元信息(名称、版本)
- AI使用说明(Markdown格式)
- 提供的工具列表
3.3 实现启动工作流工具
创建启动工具:
bash复制php artisan make:mcp-tool StartWorkflowTool
关键实现逻辑:
php复制public function handle(Request $request): Response
{
$data = $request->validate([
'workflow' => ['required', 'string'],
'args' => ['nullable', 'array'],
'external_id' => ['nullable', 'string', 'max:255'],
]);
$workflowClass = $this->resolveWorkflowClass($data['workflow']);
if (!$workflowClass) {
return Response::error("未知工作流: {$data['workflow']}");
}
$stub = WorkflowStub::make($workflowClass);
$stub->start(...array_values($data['args'] ?? []));
return Response::json([
'workflow_id' => $stub->id(),
'status' => class_basename($stub->status()),
'message' => '工作流已启动,请使用get_workflow_result检查状态',
]);
}
这个工具负责:
- 验证输入参数
- 解析工作流类名(通过白名单检查)
- 创建工作流实例并异步启动
- 返回工作流ID和初始状态
安全提示:务必配置工作流白名单,防止任意类执行。我们将在3.6节详细说明配置方法。
3.4 实现结果查询工具
创建结果查询工具:
bash复制php artisan make:mcp-tool GetWorkflowResultTool
核心逻辑实现:
php复制public function handle(Request $request): Response
{
$workflowId = $request->validate(['workflow_id' => ['required']])['workflow_id'];
$stored = StoredWorkflow::find($workflowId);
if (!$stored) {
return Response::json(['found' => false]);
}
$workflow = WorkflowStub::load($workflowId);
$status = class_basename($workflow->status());
$response = [
'found' => true,
'status' => $status,
'running' => $workflow->running(),
];
if (str_contains($status, 'Completed')) {
$response['output'] = $workflow->output();
} elseif (str_contains($status, 'Failed')) {
$response['error'] = $stored->exceptions()->latest()->first()?->exception;
}
return Response::json($response);
}
这个工具提供:
- 工作流状态查询(运行中/已完成/已失败)
- 成功时的输出结果
- 失败时的错误信息
- 工作流创建/更新时间戳
3.5 实现工作流列表工具
创建列表工具:
bash复制php artisan make:mcp-tool ListWorkflowsTool
实现代码:
php复制public function handle(Request $request): Response
{
$data = $request->validate([
'show_recent' => ['nullable', 'boolean'],
'limit' => ['nullable', 'integer', 'min:1', 'max:50'],
'status' => ['nullable', 'string'],
]);
$response = [
'available_workflows' => array_map(
fn($k, $v) => ['key' => $k, 'class' => $v],
array_keys(config('workflow_mcp.workflows', [])),
array_values(config('workflow_mcp.workflows', []))
),
];
if ($data['show_recent'] ?? false) {
$query = StoredWorkflow::query()
->orderBy('created_at', 'desc')
->limit($data['limit'] ?? 10);
if ($data['status'] ?? null) {
$query->where('status', 'like', "%{$data['status']}%");
}
$response['recent_workflows'] = $query->get()->map(function ($w) {
return [
'id' => $w->id,
'class' => $w->class,
'status' => class_basename($w->status),
'created_at' => $w->created_at?->toIso8601String(),
];
});
}
return Response::json($response);
}
此工具提供两大功能:
- 列出所有可用工作流(来自白名单配置)
- 可选显示最近执行的工作流实例(支持状态过滤)
3.6 工作流白名单配置
创建配置文件config/workflow_mcp.php:
php复制return [
'allow_fqcn' => env('WORKFLOW_MCP_ALLOW_FQCN', false),
'workflows' => [
'data-export' => App\Workflows\DataExportWorkflow::class,
'report-generator' => App\Workflows\ReportGeneratorWorkflow::class,
'user-sync' => App\Workflows\UserSyncWorkflow::class,
],
];
这个配置实现了:
- 工作流别名映射(AI客户端使用别名而非完整类名)
- 安全限制(只有列出的工作流可以被执行)
- 可选完全限定类名支持(allow_fqcn)
安全建议:生产环境应将allow_fqcn设为false,只允许通过别名调用预定义工作流。
4. 客户端集成与使用
4.1 VS Code/Copilot配置
在项目根目录创建.vscode/mcp.json:
json复制{
"servers": {
"laravel-workflow": {
"type": "http",
"url": "http://localhost/mcp/workflows"
}
}
}
配置说明:
- 本地开发使用localhost
- GitHub Codespaces中,VS Code服务器在容器内运行,localhost也能正确访问
- 无需暴露公共端口,保障开发安全性
配置完成后,在VS Code中执行"Developer: Reload Window"重新加载窗口,Copilot就能识别这些工具了。
4.2 典型使用场景
场景一:数据导出工作流
用户: "帮我导出上个月的销售数据"
AI:调用list_workflows → 发现data-export工作流
AI:调用start_workflow → 启动导出任务,返回ID 123
AI: "已开始导出任务(ID:123),完成后我会通知你"
...(一段时间后)
AI:调用get_workflow_result → 检查ID 123状态
AI: "导出已完成!下载链接:..."
场景二:报告生成工作流
用户: "生成Q2季度报告,包含图表和数据分析"
AI:调用start_workflow → 启动report-generator工作流
AI: "报告生成中,这可能需要几分钟..."
(每30秒轮询一次状态)
AI: "报告已完成!主要内容摘要:..."
4.3 浏览器直接测试
如果使用Laravel Workflow Sample App,可以直接:
- 在GitHub创建Codespace
- 初始化应用:php artisan app:init
- 启动队列工作者:php artisan queue:work
- 通过内置UI测试工作流
- 观察Waterline仪表板中的执行情况
5. 高级技巧与问题排查
5.1 性能优化建议
-
轮询间隔控制:
- 快速轮询(初始5秒)→ 中等轮询(30秒)→ 慢速轮询(2分钟)
- 根据工作流平均执行时间动态调整
-
结果缓存:
php复制// 在GetWorkflowResultTool中 $result = Cache::remember("workflow:result:{$workflowId}", 3600, function() use ($workflow) { return $workflow->output(); }); -
批量查询优化:
- 当需要检查多个工作流状态时,实现batch_get_workflow_status工具
5.2 常见问题解决
问题一:工作流卡在Pending状态
- 检查队列工作者是否运行:php artisan queue:work
- 确认数据库连接正常
- 检查队列驱动配置(推荐Redis)
问题二:AI客户端无法连接
- 确认路由已注册:routes/ai.php
- 检查MCP端点可访问性:curl http://localhost/mcp/workflows/tools
- 验证CORS配置(如果跨域)
问题三:工作流执行失败
- 检查StoredWorkflow的exceptions关联表
- 实现重试逻辑:
php复制// 在工作流类中 public function handle($attempts = 3) { try { // 业务逻辑 } catch (\Exception $e) { if ($attempts > 0) { sleep(5); $this->handle($attempts - 1); } throw $e; } }
5.3 监控与日志
-
Waterline集成:
- 实时监控工作流状态
- 查看执行时间线
- 分析失败原因
-
自定义日志:
php复制// 在工作流类中 public function execute() { $this->log('开始执行数据导出'); // ... $this->log('导出完成,共处理'.$count.'条记录'); } -
Prometheus监控:
php复制// 在工具类中 public function handle() { $start = microtime(true); // ...处理逻辑 $duration = microtime(true) - $start; prometheus_histogram_observe('mcp_tool_duration_seconds', $duration, ['tool' => 'start_workflow']); }
6. 扩展与进阶
6.1 参数化工作流
允许用户输入动态参数:
php复制// 在StartWorkflowTool中
public function schema(JsonSchema $schema): array
{
return [
'time_range' => $schema->object()
->properties([
'start' => $schema->string()->format('date'),
'end' => $schema->string()->format('date'),
])
->required(['start', 'end']),
// 其他参数...
];
}
AI客户端会根据schema自动生成参数输入界面。
6.2 Webhook通知
替代轮询,实现推送通知:
-
在工作流完成时触发webhook:
php复制// 在工作流类中 public function completed() { Http::post($this->webhook_url, [ 'workflow_id' => $this->id(), 'status' => 'completed', 'output' => $this->output(), ]); } -
客户端注册webhook:
json复制{ "workflow_id": "123", "webhook_url": "https://your-app.com/webhooks/workflow" }
6.3 多步骤工作流链
实现复杂业务流程编排:
php复制// 复合工作流示例
class OrderFulfillmentWorkflow extends Workflow
{
public function execute($orderId)
{
$payment = yield new ProcessPaymentWorkflow($orderId);
$inventory = yield new UpdateInventoryWorkflow($orderId);
$shipping = yield new CreateShippingWorkflow($orderId);
return [
'payment' => $payment,
'inventory' => $inventory,
'shipping' => $shipping,
];
}
}
AI客户端只需启动主工作流,子步骤会自动执行。
6.4 进度实时推送
使用Server-Sent Events(SSE)推送进度:
php复制// 在控制器中
public function streamProgress($workflowId)
{
return response()->stream(function() use ($workflowId) {
while (true) {
$progress = WorkflowProgress::where('workflow_id', $workflowId)->latest()->first();
echo "data: " . json_encode($progress) . "\n\n";
ob_flush();
flush();
sleep(1);
}
}, 200, [
'Content-Type' => 'text/event-stream',
'Cache-Control' => 'no-cache',
]);
}
在工作流中更新进度:
php复制$this->log('开始处理数据', ['progress' => 10]);
// ...
$this->log('数据处理完成', ['progress' => 50]);
这套系统在实际项目中表现出色,特别是在需要AI辅助完成复杂、长时间运行任务的场景。通过合理的工作流设计和MCP工具封装,我们实现了:
- 复杂任务的简单化接口
- 可靠的异步执行
- 完整的可观测性
- 自然的AI交互体验
对于想要进一步优化的开发者,我建议:
- 实现工作流版本控制,兼容不同版本的AI客户端
- 添加细粒度的权限控制,基于用户角色限制可访问的工作流
- 开发工作流模板系统,支持动态工作流配置