claude-orchestrator
by lilyjem
README.md
# claude-orchestrator
> 让 Cursor 主 Agent 通过 MCP 协议调度 Claude CLI 作为子代理
## 这是什么?
一个 MCP Server,它在 Cursor IDE 和 Claude CLI 之间架起桥梁。Cursor 的主 Agent 作为**规划者和协调者**,通过 MCP 工具将实际开发任务派发给 Claude CLI(子代理)执行。
```
┌──────────────────────────────────┐
│ Cursor 主 Agent │
│ 理解需求 → 拆分任务 → 汇总结果 │
└────────────┬─────────────────────┘
│ MCP Protocol (stdio)
┌────────────▼─────────────────────┐
│ claude-orchestrator MCP Server │
│ 任务注册表 + CLI 封装 + 8个工具 │
└────────────┬─────────────────────┘
│ spawn: claude -p --output-format stream-json
┌────────────▼─────────────────────┐
│ Claude CLI (子代理) │
│ 编写代码 / 运行测试 / 代码审查 │
└──────────────────────────────────┘
```
## 为什么需要它?
Cursor 内置的 subagent 能力有限。Claude CLI(Claude Code)拥有完整的开发工具集:
- ✅ 完整的文件系统操作
- ✅ 会话可续接(`--resume`,对应 `send_instruction`)
- ✅ 自定义系统提示和模型选择
- ✅ 结构化结果指标(cost / duration / turns)
- ✅ 灵活的工具配置
## 安装
```bash
git clone https://github.com/lilyjem/cursor-subagent.git
cd cursor-subagent
npm install
```
**前置要求:**
- Node.js >= 20
- [Claude CLI](https://docs.anthropic.com/en/docs/claude-code) 已安装并认证
## 配置 Cursor
将以下内容添加到 Cursor 的 MCP 配置中(`~/.cursor/mcp.json`):
```json
{
"mcpServers": {
"claude-orchestrator": {
"command": "npx",
"args": ["tsx", "/path/to/cursor-subagent/src/index.ts"]
}
}
}
```
重启 Cursor 后,MCP Server 会自动启动。
## 8 个 MCP 工具
| 工具 | 用途 |
|------|------|
| `dispatch_task` | 派发单个任务,立即返回 `task_id`(不等待) |
| `dispatch_goal` | 派发目标驱动任务,子代理持续工作到条件达成为止 |
| `dispatch_parallel` | 并行派发多个任务(每项 `prompt` 或 `goal` 二选一) |
| `get_task_status` | 查询任务状态与最近日志(纯读取,不阻塞) |
| `get_task_result` | 取任务结果快照(含 cost / duration / turns),不等待 |
| `list_tasks` | 列出全部任务(`kind` 区分普通任务与 goal 任务) |
| `send_instruction` | 向已有会话追加指令,返回**新任务**的 `task_id` |
| `stop_task` | 停止运行中的任务,可选清理记录 |
没有 `mode` / `wait` 参数:派发类工具立即返回 `task_id`,查询类工具只读快照,全部零阻塞。
## 使用示例
### 派发任务(立即返回)
```
dispatch_task({
prompt: "编写一个 Express REST API 的用户注册端点",
system_prompt: "你是 Node.js 后端开发专家"
})
// → { task_id, pid, status: "running" }
// 注意:派发返回值里【没有】session_id —— init 事件到达后由 get_task_status 取
```
### 目标驱动任务(dispatch_goal)
派发一个完成条件,让子代理自己干到条件达成为止:
```
dispatch_goal({
goal: "npm test 退出码为 0,且在输出中贴出测试通过数;不得修改 test/ 与 package.json;or stop after 20 turns"
})
```
写条件的四条要诀:
1. **一个可测量的终态** —— 「`npm test` 退出码为 0」「`git status` 干净」「无 TS 编译错误」,而不是「代码更好」
2. **说明怎么证明** —— 评估器不读文件、不跑命令,只看会话里出现过的证据,所以要让子代理把证据留在输出里
3. **附上约束** —— 「且未修改 `test/` 与 `package.json`」。别写「不修改 `test/` 之外的文件」:那等于允许改 `test/`,子代理只要把断言改弱就能「达成」条件
4. **加轮次上限** —— `or stop after 20 turns`(Claude Code 原生支持的子句)
条件上限 **4000 字符**,超长会被直接拒绝(不会启动 CLI)。主观目标(「让代码更优雅」)不适用。
### 跟踪与取结果
```
get_task_status({ task_id: "...", include_logs: true }) // 进度行,运行中的任务也能看
get_task_result({ task_id: "..." }) // 结果快照,任务还在跑就返回 running
```
### 并行任务
```
dispatch_parallel({
tasks: [
{ prompt: "实现用户注册 API", system_prompt: "后端专家" },
{ prompt: "实现用户登录 API", system_prompt: "后端专家" },
{ goal: "认证中间件全部单测通过,且贴出测试输出", system_prompt: "安全专家" }
]
})
```
### 追加指令
```
send_instruction({ task_id: "...", message: "把错误处理补上" })
// → 返回的是【新任务】的 task_id;原任务保留自己的结果
```
### 代码审查
```
dispatch_task({
prompt: "审查 src/api/ 目录下的所有代码,关注安全性和性能",
system_prompt: "你是资深代码审查专家,关注 OWASP Top 10 安全风险"
})
```
## Cursor Rule
项目包含 `.cursor/rules/claude-orchestrator.mdc`,它会自动教 Cursor 主 Agent:
- 何时使用子代理 vs 自己做
- 如何构造有效的 dispatch 调用
- 何时用 `dispatch_goal`、怎么写完成条件
- 最佳实践(提供完整上下文、并行独立任务等)
## 技术架构
```
src/
├── index.ts # 入口:stdio 传输
├── server.ts # MCP Server:注册 8 个工具
├── types.ts # 共享类型定义
├── constants.ts # 常量(goal 字符上限、日志缓冲上限等)
├── claude-cli/
│ └── cli-adapter.ts # 纯函数:argv 构建 + stream-json 行解析
├── runtime/
│ └── task-runner.ts # 子进程生命周期 + 日志环形缓冲
├── registry/
│ └── task-registry.ts # 内存任务注册表
└── tools/
├── dispatch.ts # dispatch_task / dispatch_goal / dispatch_parallel
├── monitor.ts # get_task_status / list_tasks
├── result.ts # get_task_result
└── control.ts # stop_task / send_instruction
```
## 测试
```bash
npm test # 运行所有测试(95 个用例)
npm run test:watch # 监听模式
npm run smoke # 真实 CLI 端到端冒烟(会调用 Claude CLI 并产生费用)
```
## 工作原理
只有一条执行通道:MCP Server 自己 `spawn` 一个 `claude -p` 子进程,**不等待**,立即返回 `task_id`,任务状态此后由子进程退出事件驱动。
1. **派发**:`claude -p --output-format stream-json --verbose --dangerously-skip-permissions -- "<prompt>"`
- `--` 终止选项解析:`--allowedTools` 是变长参数,实测会连同后面的 prompt 一起吞掉(claude 2.1.272)
- stdout 逐行是 JSON,解析成事件;`system/thinking_tokens`(实测占输出体积 86.4%)与 thinking 块直接丢弃
- 最后的 `result` 事件给出 result / cost / duration_ms / num_turns / session_id
2. **Goal 任务**:把条件包装成 `/goal <条件>` 作为位置参数传入
- `/goal` 是 Claude Code 的 slash 命令(v2.1.139+),`-p` 下一次调用内跑完整个循环
- 评估器是 session 级 prompt Stop hook,由独立的小模型判定条件是否达成
3. **会话续接**:`claude -p --output-format stream-json --verbose --dangerously-skip-permissions --resume <session_id> -- "<message>"`
- `send_instruction` 用它续接会话,产生**新任务记录**(`resumedFrom` 指回原任务),不用重复说明上下文
4. **停止**:kill 子进程(SIGTERM),会话保留,仍可 `--resume`
## 注意事项
- ⚠️ 使用 `--dangerously-skip-permissions` 跳过权限确认,子代理拥有完整文件系统访问权限
- ⚠️ **失去 `claude attach` / `claude agents` 可见性**:子代理不再出现在 Claude Code 的后台会话列表里,人类无法 attach 接管
- ⚠️ **Server 退出(含 Cursor 重载)会中断在跑的子代理**:任务注册表在内存中,重启后全部丢失
- ⚠️ **每个子代理任务的成本不可忽略**:四组真实任务的实测成本为 $0.0357(goal 任务)/ $0.0405(带 `allowed_tools` 的普通任务)/ $0.0986、$0.13(更早的探针);按 **$0.04–0.13** 估算,`dispatch_parallel` 扇出 3 个任务约 **$0.12–0.40**(3 × 上述区间)
- ⚠️ `dispatch_goal` 依赖 Claude Code 的 hooks 系统与 workspace 信任:`/goal` 的评估器是 settings 里的 session 级 prompt Stop hook,`disableAllHooks` 或 `allowManagedHooksOnly` 会让它不可用,未信任的目录同理
- ⚠️ 需要有效的 Claude CLI 认证(API Key 或 OAuth)
## License
MIT
TDQS
A3.8/5.0
Scored across 7 tools
Disambiguation5/5
每个工具都有明确且独特的职责:派发任务(单/并行)、查询状态、获取结果、列出任务、发送指令、停止任务,没有重叠或模糊之处,代理可以清晰区分。
Naming Consistency5/5
所有工具名遵循一致的动词_名词模式(如dispatch_task, get_task_status, stop_task),风格统一,可预测性强。
Tool Count5/5
7个工具数量适中,覆盖了任务编排的核心操作,没有冗余或缺失,每个工具都有明确用途。
Completeness4/5
覆盖了任务生命周期的主要阶段(创建、查询、结果、停止、续接),但缺少显式的删除/清理任务工具(虽然stop_task有cleanup选项),整体完整度较高。
Maintenance
ActivityMaintained
ResponsivenessNo issues