mcpField
A
Agent Dispatch

Agent Dispatch

本地服务
github.com/ginkidaAI 与智能体304 周前更新

关于 Agent Dispatch

Agent Dispatch 是供 Claude Code 使用的 MCP 服务,让 Agent 将任务委派给其他项目目录中的独立 Claude 会话。它支持并行与异步任务、会话恢复和跨项目分组,并继承目标项目的工具与配置。

主要功能

  1. 01向其他项目目录委派任务
  2. 02并行及异步调度
  3. 03恢复已分派会话
  4. 04按分组共享项目上下文
  5. 05检查 Agent 配置与能力

适用场景

  1. 01委派任务以读取其他仓库的代码或运行测试
  2. 02通过基础设施 Agent 查询容器日志
  3. 03并行处理跨项目工作
  4. 04超时后继续未完成的分派会话

MCP 相关

Agent Dispatch 是 MCP 服务,向 Claude Code 暴露 Agent 列表、检查和任务分派等工具。它在各自项目目录中启动 Claude Code 会话,并返回执行结果。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

agent-dispatch

PyPI CI Python License

MCP 服务,让 Claude Code Agent 可以将任务委派给其他项目目录中的 Agent。

agent-dispatch 吉祥物

每个 Agent 都会在自己的项目目录中作为独立的 claude -p 会话运行,并继承该项目的 MCP 服务、CLAUDE.md 和工具。调用方 Agent 只需接收结果。

相关项目可以打包成一个**组**——包含共享的任务说明和成员列表——让一个会话能够协调这些项目间的工作(例如,代码仓库 + infra/Portainer 网关 + analytics 网关)。

支持 OAuth、API 密钥和 Claude 订阅认证。

**AI Agents:**此 README 是使用该工具的权威文档——设置方法见快速入门(每一步都有确定性的验证方法);首次调用见dispatch;工具选择见选择哪个工具;故障处理见错误恢复。如果你要参与开发此仓库,请参阅 AGENTS.md。

快速入门

**前置条件:**必须安装并完成身份验证的 Claude Code CLI。先检查:

claude --version   # must print a version — if it fails, install Claude Code before continuing

然后:

pip install agent-dispatch   # or: pipx install agent-dispatch

# 1. Create config + register the MCP server with Claude Code (user scope)
agent-dispatch init

# 2. Register project directories as agents — REPLACE the example paths with
#    real directories on your machine; they must exist (~ is expanded, relative
#    paths are resolved). Descriptions are auto-generated from project files.
#    No second project handy? Use the zero-setup block below instead.
agent-dispatch add infra ~/projects/infra
agent-dispatch add backend ~/projects/backend

# 3. Smoke test — dispatches a real task to the agent added in step 2 and prints
#    the answer; exit 0 on success. Default task when none given:
#    "What project is this? Describe in one sentence."
agent-dispatch test infra

# 4. Verify the whole install — prints "All checks passed." and exits 0 on success
agent-dispatch doctor

步骤 2–3 的零配置替代方案(无需第二个项目——注册当前目录):

agent-dispatch add self . && agent-dispatch test self "Say hello"

现在,每个 Claude Code 会话都能使用 dispatch 工具。独立验证:claude mcp list 必须输出一行以 agent-dispatch: 开头的内容。在 Claude Code 会话中,首先调用 MCP 工具 list_agents(),然后调用 dispatch(...)。

如果 init 未能注册 MCP 服务(输出警告而非 Registered MCP server),请手动注册:

claude mcp add-json agent-dispatch "{\"type\":\"stdio\",\"command\":\"$(which agent-dispatch)\",\"args\":[\"serve\"]}" --scope user

如果 test 因权限错误而失败(error_type: "permission"),请授予工具访问权限并重新测试:

agent-dispatch update infra --allowed-tools "Bash,Read,Grep"      # least privilege
# or, if the agent needs everything (see SECURITY.md for the trade-off):
agent-dispatch update infra --permission-mode bypassPermissions

何时委派

**适合委派的情况:**任务需要使用其他项目中的工具、文件或上下文:

  • 通过 infra Agent 的 Portainer MCP 查看容器日志
  • 通过 db Agent 的 postgres MCP 查询数据库
  • 在其他代码仓库中读取代码或运行测试

**不适合委派的情况:**你能够自行完成任务——委派会启动一个完整的 Claude 会话。

MCP 工具参考

list_agents

列出所有已配置的 Agent。请先调用此工具,查看可用项。

// Response (capability + permission fields shown only when populated)
[
  {
    "name": "infra",
    "directory": "/home/user/projects/infra",
    "description": "Infrastructure agent. MCP: portainer. Stack: Python, Docker",
    "healthy": true,
    "has_claude_md": true,
    "has_mcp_config": true,
    "mcp_servers": ["portainer", "postgres"],
    "stacks": ["Python", "Docker"],
    "dbs": ["Alembic"],
    "capabilities": ["docker_logs", "deploy_debug"],
    "risky_capabilities": ["restart_services"],
    "permission_mode": "bypassPermissions",
    "allowed_tools": ["Bash", "Read", "Grep"],
    "typical": {"median_seconds": 143.3, "p90_seconds": 178.3, "median_cost_usd": 0.2666}
  }
]

mcp_servers、stacks 和 dbs 会根据 Agent 的项目文件(.mcp.json、Dockerfile、pyproject.toml、Cargo.toml、prisma/、alembic.ini 等)自动检测,以便调用方无需先委派探测任务,就能选择合适的 Agent。

typical 是实测值,而非预先声明的值:它取自此 Agent 最近的委派记录,保存在使用日志——了解你的 Agent 群组实际花费中,并且只会在运行几次后显示。可用它来设置 timeout_seconds,并在调用前了解调用成本。如果日志功能关闭,或 Agent 的历史记录太少,则不会显示该值——与其根据两次运行推算出典型耗时,不如不提供数据。

inspect_agent

低成本的详细查询——读取 Agent 的文件,而不启动 claude 会话。返回完整配置(超时、模型、预算、权限模式、允许/禁止使用的工具)、检测到的 MCP/技术栈/数据库,以及存在时 CLAUDE.md 和 README.md 的简短预览。

参数类型必填描述
namestring是来自 list_agents 的 Agent 名称
preview_linesint否CLAUDE.md/README.md 的最大行数(默认值为 40,最大值为 200,设为 0 可禁用)

在调用 dispatch_async/dispatch之前使用此工具,确认 Agent 具备完成任务所需的工具和上下文——这比探测式分派便宜得多。

它还会包含 Agent 的 instructions(长期指令)和完整的 typical 区块——即 list_agents 中经过精简的内容,以及 dispatches、failed 和 outcomes 分类:

"typical": {
  "dispatches": 24, "median_seconds": 143.3, "p90_seconds": 178.3,
  "median_cost_usd": 0.2666, "failed": 1, "outcomes": {"done": 21, "partial": 2}
}
分组

分组会将相关 Agent 组合成跨项目工作集——通常包含几个代码仓库和能力网关(例如,带有 Portainer MCP 的 infra Agent,以及配备浏览器和 Yandex Metrica 的 analytics Agent)。它能让一个编排会话协调跨代码、部署和验证的工作。

分组是一个描述层,而非执行引擎——没有路由器,也没有状态机。你需要查看 Agent 的提示信息来选择成员,并使用常规分派工具进行协调。有两个面向不同受众的文本字段:

  • description——面向编排者:如何协调该分组(步骤顺序、各成员的职责)。通过 list_groups/inspect_group 显示,绝不会注入成员的提示词。
  • shared_context——面向成员的事实信息(技术栈名称、计数器 ID、约定),无论由哪个成员读取都适用。传入 group= 时,会自动添加到成员的 context 前面。

成员通过名称引用 Agent;成员关系为多对多(共享网关可以属于多个分组)。使用 agent-dispatch group CLI(add/list/inspect/update/remove)管理分组,也可以编辑 agents.yaml。

list_groups()——低成本读取所有分组的信息,不会启动子进程:显示描述、成员数量,以及每个成员的 use_for 提示和健康状态。若成员对应的 Agent 已被移除,则会标记为 "unknown": true,而不会报错。

inspect_group(name)——查看一个分组的完整简介:description、完整的 shared_context 和成员列表。若要深入查看特定成员,请调用 inspect_agent(member)——inspect_group 有意保持为低成本的成员关系查询。

使用分组——将 group= 传给 dispatch(或在 dispatch_parallel 中为每个项目单独传入)。该 Agent 必须是分组成员;所属分组的 shared_context 会自动随请求传递:

# From the shop-web codebase, hand the deploy to the infra gateway.
# The "shop" group's facts (stack name, counter id) are auto-attached.
dispatch(
    agent="infra",
    task="Redeploy the shop-web container",
    caller="shop-web",
    goal="ship the checkout fix",
    group="shop",
)

group=""(默认值)与普通分派逐字节完全相同——共享事实会并入 context 字符串,因此结果缓存会自动区分不同分组,不带分组的调用也不受影响。

dispatch

一次性任务委派。结果会被缓存——在 TTL 内发出相同请求会立即返回。

参数类型必填描述
agentstring是来自 list_agents 的 Agent 名称
taskstring是要执行的操作——请具体说明,Agent 不会获得你们对话中的上下文
contextstring否额外上下文:错误消息、代码片段、堆栈跟踪
callerstring否你的项目/角色——帮助 Agent 理解请求者
goalstring否更广泛的目标——帮助 Agent 做出更好的权衡
response_formatstring否"json" 表示请求单个 JSON 值;解析后的结果存入 parsed_result。留空表示自由格式文本。
return_refbool否为 true 时,仅返回 ref 和摘要预览,而不返回完整结果文本。使用 fetch_result(ref) 可按需加载完整文本。
summary_charsint否ref 响应中包含的结果文本最大字符数(默认值为 500)。
timeout_secondsint否此次调用的一次性超时设置(0 = Agent 配置的超时时间;限制在 10–7200 之间)。无需修改配置即可处理已知的长任务。
groupstring否组名(来自 list_groups)。Agent 必须是该组成员;组的 shared_context(面向成员的事实信息)会自动添加到 context 前。留空表示直接分派。参见 Groups。
# Call — recommended form (always include caller and goal)
dispatch(
    agent="infra",                # must exist in list_agents()
    task="Check container logs for errors related to the scheduler service",
    context="Error: TypeError at scheduler.py:42",
    caller="backend",             # your project/role
    goal="debug production crash" # the broader objective
)
// Response (success)
{
  "agent": "infra",
  "success": true,
  "result": "Found 3 errors in container logs: TypeError in scheduler.py:42...",
  "session_id": "sess-abc-123",
  "cost_usd": 0.02,
  "duration_ms": 5000,
  "num_turns": 2,
  "outcome": "done"
}

// Response (failure — error_type helps you handle programmatically)
{
  "agent": "infra",
  "success": false,
  "result": "",
  "error": "Tool_use is not allowed in this permission mode\n\nHint: ...",
  "error_type": "permission"
}

error_type 的取值: permission(工具/操作被拒绝)、timeout、recursion(超过分派深度)、not_found(目录或 CLI 不存在)、budget(claude CLI 在达到 max_budget_usd 时停止会话)、usage_limit(Claude 账户 达到速率/会话限制)、cli_error(其他故障)。权限、预算、使用量限制和超时错误均包含可执行的提示。

可恢复的超时: 每次新的分派都会预先分配一个会话 UUID(--session-id),因此超时的分派仍会返回 session_id——部分对话记录会在终止后保留。超时错误会明确说明如何恢复:使用 dispatch_session(agent, "Continue where you left off", session_id=...) 继续,使用更大的 timeout_seconds 重试,或使用 dispatch_async。

被拒绝工具的可见性: 在非交互模式下,claude CLI 会自动拒绝 Agent 无权使用的工具——此时 Agent 往往会“成功”给出类似 “我需要你授权一次只读查询” 的回答。发生这种情况时,响应会包含确定性信号:denied_tools(从 CLI 的 permission_denials 中解析)以及解释结果可能不完整及如何授予访问权限的 hint。success 仍为 true——这是软性信号,并非失败。

// Response (success, but a tool was blocked)
{
  "agent": "analysis",
  "success": true,
  "result": "Here is the offline mapping. To finish I'd need to run one read-only query...",
  "denied_tools": ["Bash"],
  "hint": "1 tool call(s) were denied by permissions: Bash. The result may be incomplete..."
}

结构化 JSON 输出: 传入 response_format="json",即可要求 Agent 返回单个 JSON 值。运行器会附加一段指令尾注(“仅回复单个有效 JSON 值,不要使用代码围栏或添加说明文字”),并在成功时解析响应——解析后的值存入 parsed_result。原始文本始终保存在 result 中。解析失败时,parsed_result=None,但不会导致分派失败(软模式)。

// Response with response_format="json"
{
  "agent": "infra",
  "success": true,
  "result": "{\"errors\": 3, \"first_at\": \"14:02\"}",
  "parsed_result": {"errors": 3, "first_at": "14:02"}
}

始终传入 caller 和 goal——被分派的 Agent 会看到结构化提示词:

## Goal
debug production crash

## Dispatched by
backend

## Context
Error: TypeError at scheduler.py:42

## Task
Check container logs for recent errors related to the scheduler service

分派协议与 outcome。 单独使用 claude -p 时,它并不知道自己正由另一个 Agent 驱动:面对含糊的任务,它很可能会以 “你指的是哪个服务?” 结束——这次调用既计费又没有回答问题,而且普通缓存随后会在整个 TTL 期间再次返回这个结果。因此,每次分派还会向 Agent 的系统提示词追加一段简短的分派协议(使用 --append-system-prompt,绝不会混入任务文本):

  • 它以非交互方式运行,由 caller 派发,不会有人回答问题或批准操作——说明假设并继续;如果两种理解有差异,则采用更安全的理解;
  • 被拒绝的工具或缺失的内容是需要报告的事项,而不是停止的理由——完成所有其他可行的工作;
  • 它的时间预算约为 Agent 的超时时间(以及设定的支出上限)——将工作范围控制在预算内;完整的部分答案胜过未完成的完美答案;
  • 先给出结果,再提供依据;列出无法完成的事项及原因(这正是让 return_ref 摘要——文本的开头——值得一读的原因);
  • 最后单独一行写上 STATUS: done、STATUS: partial 或 STATUS: blocked。

最后一行会从 result 提取到 outcome 中——这是 Agent 自己给出的判定,可确定性地检查:"done" 表示已完成;"partial" / "blocked" 表示 Agent 认为工作尚未完成,结果会说明缺少什么,并由 hint 指明后续操作(通常是 dispatch_session(..., session_id=...))。阅读正文前先查看 outcome。它不会改变 success,而且 partial / blocked 结果不会被缓存。dispatch_parallel(..., aggregate=...) 会为聚合器标记这类成员,避免将受阻报告合成为已完成的结果。如果 Agent 未报告 outcome,则该项不存在——协议已关闭(settings.dispatch_protocol: false)、请求了 response_format="json"(此时 JSON 尾部决定回复格式),或者 Agent 直接省略了它。

常设指令。 每次派发时,都会在同一条系统提示词中遵循每个 Agent 的 instructions(通过 add_agent / update_agent 或 agent-dispatch update <name> --instructions "..." 设置)——“只允许只读 SQL”“除非任务明确要求,否则绝不重启服务栈”“使用精确的日志行作答”。这些指令不同于每次调用单独提供的 context,也不同于面向交互式会话编写的项目自身 CLAUDE.md;它们是被派发执行时必须遵守的规则。

// Response (success, but the agent says it did not finish)
{
  "agent": "infra",
  "success": true,
  "result": "Restarted horizon. Could not verify the queue drained: the redis container is not reachable from here.",
  "session_id": "sess-abc-123",
  "outcome": "partial",
  "hint": "The agent reports its work is PARTIAL — read the result for what is missing, then continue in the same session via dispatch_session(agent='infra', task='Continue where you left off', session_id='sess-abc-123') or re-dispatch with what it needed."
}
dispatch_session

多轮对话:继续与 Agent 的对话。首次调用会启动会话,后续调用传入 session_id 即可继续。结果永不缓存。

参数类型必填说明
agentstring是Agent 名称
taskstring是任务或后续消息
session_idstring否来自先前响应;新会话留空
contextstring否额外上下文
callerstring否派发者
goalstring否更广泛的目标
timeout_secondsint否单次超时覆盖值(0 = Agent 默认值;限制在 10–7200 之间)

dispatch_session 也是超时恢复途径:超时的 dispatch 会返回 session_id——将其传入此处,并设置 task="Continue where you left off",即可挽救未完成的工作,而不必从头开始。

Turn 1: dispatch_session("infra", "List running containers")
         → session_id: "sess-abc"

Turn 2: dispatch_session("infra", "Restart the nginx one", session_id="sess-abc")
         → agent remembers previous context
dispatch_parallel

并发运行多个任务。比按顺序调用 dispatch 快得多。

参数类型必填说明
dispatchesstring (JSON)是JSON 数组,包含 {"agent", "task", "context?", "caller?", "goal?", "response_format?", "return_ref?", "summary_chars?", "timeout_seconds?", "group?"}(每项中的 group 会预先验证成员资格,并自动注入其 shared_context)
aggregatestring否用于将所有结果综合为一个答案的 Agent 名称

重要: dispatches 是 JSON 字符串,不是列表。

// Input
[
  {"agent": "infra", "task": "check pod logs for errors", "caller": "backend", "goal": "debug crash"},
  {"agent": "db", "task": "are all migrations applied?", "caller": "backend", "goal": "debug crash"}
]
// Response (without aggregate)
[
  {"agent": "infra", "success": true, "result": "No errors in pod logs", ...},
  {"agent": "db", "success": true, "result": "All migrations applied", ...}
]
// Response (with aggregate="backend")
{
  "individual_results": [
    {"agent": "infra", "success": true, "result": "No errors in pod logs", ...},
    {"agent": "db", "success": true, "result": "All migrations applied", ...}
  ],
  "aggregated": {
    "agent": "backend",
    "success": true,
    "result": "Summary: all systems nominal. No pod errors, all migrations applied."
  }
}
dispatch_stream

与 dispatch 相同,但会显示 Agent 工作时的实时进度。适用于长时间运行的任务。不缓存。

进度通知会保留最新的 100 条待处理消息,每条最多 300 个字符。如果输出速度快于客户端接收速度,较早的通知会被省略,并显示省略数量;最终结果不受影响。 断开连接或取消工具调用会丢弃待处理通知,并停止缓冲新通知。工作进程仍会占用并发槽位,直到任务完成。

参数与 dispatch 相同,但不包括 return_ref/summary_chars(流式传输与 ref 模式不兼容)和 group(仅 dispatch 和 dispatch_parallel 中的每个项目支持注入组上下文)。

dispatch_dialogue

两个 Agent 通过多轮对话协作。不会缓存。

参数类型必填描述
requesterstring是提出问题或提供上下文的 Agent
responderstring是具备相关专业知识或工具的 Agent
topicstring是要讨论的问题或疑问
max_roundsint否最多往返轮数(默认:3,最多:10)

每轮最多消耗 2 次调度。Agent 使用 [RESOLVED] 表示已完成。

// Response
{
  "resolved": true,
  "rounds": 2,
  "total_cost_usd": 0.04,
  "total_duration_ms": 12000,
  "final_answer": "Staging had 1 pending migration. Applied successfully.",
  "conversation": [
    {"agent": "db", "role": "responder", "round": 1, "message": "Which environment?", "cost_usd": 0.01},
    {"agent": "backend", "role": "requester", "round": 1, "message": "Staging", "cost_usd": 0.01},
    {"agent": "db", "role": "responder", "round": 2, "message": "Applied. [RESOLVED]", "cost_usd": 0.01}
  ]
}
add_agent

将新的项目目录注册为 Agent。如果未提供描述,则根据项目文件自动生成。

参数类型必填描述
namestring是Agent 名称(字母、数字、连字符、下划线)
directorystring是现有项目目录的路径(会展开 ~,相对路径会解析为绝对路径)
descriptionstring否此 Agent 的功能 — 为空时自动生成
timeoutint否超时时间,单位为秒(0 = 300;这是一个字面默认值,不是 settings.default_timeout)
max_budget_usdfloat否每次调度的最高费用(美元),必须为有限值(0 = 继承 settings.default_max_budget_usd;只有该设置也未设定时才不设上限)
permission_modestring否权限模式(例如 default、plan、bypassPermissions)
allowed_toolsstring否以逗号分隔的允许使用的工具(例如 "Bash,Read,Edit")
disallowed_toolsstring否以逗号分隔的禁止使用的工具
capabilitiesstring否以逗号分隔的能力标签(例如 "docker_logs,deploy_debug")
risky_capabilitiesstring否以逗号分隔的高风险标签(例如 "restart_services")
instructionsstring否每次调度时附加到 Agent 系统提示词中的常驻指令(参见调度协议)
update_agent

更新现有 Agent 的配置。只有非空字段会被更改。传入 "none" 可清除字段。

参数类型必填说明
namestring是要更新的 Agent 名称
descriptionstring否新描述
timeoutint否新超时时间(0 = 不更改)
max_budget_usdfloat否新预算上限(0 = 不更改;负数会清除 单个 Agent 的上限,之后应用 settings.default_max_budget_usd)
modelstring否模型覆盖值。传入 "none" 可清除
permission_modestring否权限模式。传入 "none" 可清除
allowed_toolsstring否逗号分隔。传入 "none" 可清除
disallowed_toolsstring否逗号分隔。传入 "none" 可清除
capabilitiesstring否逗号分隔。传入 "none" 可清除
risky_capabilitiesstring否逗号分隔。传入 "none" 可清除
instructionsstring否每次派发都适用的常设指令(替换原有文本)。传入 "none" 可清除

通过 MCP 修改工具更改 Agent 配置时,会立即丢弃其缓存结果。缓存键还包含 Agent 配置和派发默认值,因此通过 CLI、其他服务或 YAML 进行的编辑会在下次请求时生效。使用旧配置完成工作的执行器无法覆盖新配置的答案。未更改的 Agent 会保留其缓存。

remove_agent

从配置中移除 Agent。

参数类型必填说明
namestring是要移除的 Agent 名称
cache_stats / cache_clear

查看缓存命中率和大小,或清除所有缓存结果。

结果引用 — return_ref + fetch_result

对于结果文本较大的派发任务(审计、日志转储、代码搜索),返回完整文本会增大调用方 Agent 的上下文。使用 return_ref=True 可仅获取简短引用:

dispatch(agent="infra", task="audit every container", return_ref=True, summary_chars=200)
  -> {"ref": "8f3a...e1", "agent": "infra", "success": true,
      "size": 14823, "summary_chars": 200,
      "summary": "Inspected 32 containers. Found 3 OOM kills in the last hour:\n- worker-3...",
      "cost_usd": 0.08, "duration_ms": 9200}

// Later, when you actually need to read the result:
fetch_result(ref="8f3a...e1")              -> full DispatchResult JSON
fetch_result(ref="8f3a...e1", max_chars=2000)  -> truncated, plus {"truncated": true, "full_size": 14823}

引用使用与 dispatch_async 任务相同的存储空间(位于 ~/.config/agent-dispatch/jobs/),因此 dispatch_async 返回的任何 job_id 也都是 fetch_result 的有效 ref。parsed_result(设置了 response_format="json" 时)内容较少,始终会直接内嵌在引用响应中,无需再次获取。

异步调度 — dispatch_async、dispatch_status、dispatch_wait、dispatch_cancel、dispatch_jobs、dispatch_gc

当调度的任务需要较长时间时,你不会想让自己的工具槽位空等几分钟。异步调度会立即返回 job_id,让你在准备好时再来查看。

// 1. fire and forget (timeout_seconds= works here too for known-long tasks)
dispatch_async(agent="infra", task="audit every container log for OOM kills today")
  -> {"job_id": "8f3a...e1", "status": "pending", "agent": "infra"}

// 2. do other work, then check progress (non-blocking)
//    `progress` is a rolling tail of what the agent is doing right now
dispatch_status(job_id="8f3a...e1")
  -> {"id": "8f3a...e1", "status": "running", "started_at": 1730000123.4,
      "progress": ["Using tool: Bash", "Scanning container logs for OOM events..."], ...}

// 3. or block until done (timeout_seconds default: 60, capped at 3600)
dispatch_wait(job_id="8f3a...e1", timeout_seconds=120)
  -> {"id": "8f3a...e1", "status": "done", "result": {"agent": "infra", "success": true, ...}}

// If the timeout fires, the job keeps running:
  -> {"id": "...", "status": "running", "timed_out_waiting": true}

dispatch_cancel(job_id) 会取消待处理的任务;如果运行中的任务由同一个服务实例启动,也会终止其 claude 进程组(任务会先标记为 cancelled,因此工作进程最后的写入不会撤销该状态;部分工作会丢失,但进度尾记录会保留)。在 POSIX 上,该进程组中的子进程也会被终止,从而释放继承的管道和并发槽位。如果任务已被认领、但进程尚未启动,此时收到的取消请求也会生效:进程一启动就会被终止。与取消请求发生竞争的兼容性重试会在注册时被终止。由之前的服务运行实例启动的运行中任务无法安全终止,会继续运行直至完成。响应会包含一个 outcome,其值为 cancelled、cancelled_running、running(不归当前服务所有)、already_terminal 或 not_found。

异步工作进程在底层以流式方式运行:任务文件会保留一段滚动尾记录(最近 20 行,约每秒写入一次),其中包含助手文本和工具使用事件。任务运行期间,dispatch_status 会通过 progress 显示该记录,并在任务结束后继续保留,作为事后追踪记录;dispatch_jobs 会为运行中的任务显示 last_progress。

dispatch_jobs(status?, limit=50, agent?) 会以摘要形式列出最近的任务(可按 pending / running / done / failed / cancelled 筛选)。agent 会匹配确切名称,包括已从配置中移除的 Agent;两个筛选条件都会在限制数量之前生效。dispatch_gc(max_age_days=7) 会清除超过阈值的终态任务——待处理和运行中的任务永远不会被删除。使用 dry_run=True 可预览符合条件的任务 ID,而不实际删除。

任务状态会持久化到磁盘上的 ~/.config/agent-dispatch/jobs/(可通过 AGENT_DISPATCH_JOBS_DIR 覆盖)。每个任务对应一个 JSON 文件,采用仅所有者可读写的权限(0o600)和原子写入,因此任务运行期间也可以安全读取或执行 ls。调用者提供的 job_id 会在访问任何文件之前验证是否为 32 位十六进制字符串(防止路径遍历)。服务启动时会恢复崩溃实例遗留的任务:卡住超过一小时的 running 任务和超过 24 小时的 pending 任务会被标记为 failed,这样它们就不会永远被轮询,并且可由 dispatch_gc 清除。(pending 的阈值特意设得较长——任务目录由所有正在运行的服务共享,因此不能清除排在另一台服务的并发限制之后的任务。)

适合使用异步调度的情况适合使用 dispatch 的情况
长任务(几分钟)——你想继续处理其他工作短任务——你需要立即得到答案
有多个长任务,打算稍后统一收集结果有多个短任务 → dispatch_parallel
不需要缓存(每次调用都会创建新任务)默认启用缓存——相同请求无需付费

使用哪个工具

场景工具
向另一个项目快速提问一次dispatch
需要多步工作流并进行后续跟进dispatch_session
需要同时获取多个 Agent 的回答dispatch_parallel
任务耗时较长,需要查看进度dispatch_stream
两个 Agent 需要协作dispatch_dialogue
需要汇总多个 Agent 的回答使用 aggregate 的 dispatch_parallel
任务耗时较长,不想占用工具时段dispatch_async + dispatch_wait
查看进度而不阻塞dispatch_status
已知耗时较长的一次性任务任意 dispatch 工具,并设置 timeout_seconds=...
dispatch 超时使用错误信息中的 session_id 调用 dispatch_session
协调一组相关项目定义一个 组,然后调用 dispatch(..., group=name)
查看哪些 Agent 组成了一个工作集list_groups / inspect_group

错误恢复

失败是确定性的:先检查 success,然后根据 error_type 分支处理。

error_type含义恢复方法
permission工具调用遭到拒绝使用 update_agent(name, allowed_tools="Bash,Read")(遵循最小权限原则),或使用 update_agent(name, permission_mode="bypassPermissions"),然后重新分发。error 文本中包含具体修复方法的提示。
timeout进程组在超时时被终止继续未完成的工作:dispatch_session(agent, "Continue where you left off", session_id=<from the error text>)。也可以使用更大的 timeout_seconds= 重试——日志记录有历史后,错误会指出足以覆盖该 Agent 第 90 百分位耗时的值——或者使用 dispatch_async。普通分发和流式分发都会保留终止前收到的完整 CLI 结果;成功结果会包含清理用的 hint。部分输出仍属于超时。
not_found找不到 Agent 目录或 claude CLI运行 list_agents() → 检查 healthy。使用现有路径重新添加 Agent,或运行 agent-dispatch doctor 查找缺失项。
recursion分发嵌套层数超过 max_dispatch_depth(默认值为 3)不要从被分发的 Agent 再发起分发;如果确实需要嵌套,请在设置中提高 max_dispatch_depth。
budgetclaude CLI 在达到 max_budget_usd 支出上限时结束了会话——答案不完整提高上限(update_agent(name, max_budget_usd=2.0))、切换到更便宜的 model,或拆分任务。可以恢复部分会话:dispatch_session(agent, "Continue where you left off", session_id=<from the result>)。
usage_limitClaude 账号触及使用量/速率限制——并非此 Agent 的限制,也未产生任何费用等待 error 文本中注明的重置时间。分发给其他 Agent 并不能绕过限制:所有 Agent 都使用同一个账号。如果负载较高时此问题反复出现,请降低 settings.max_concurrency。
cli_errorclaude 子进程返回的其他错误阅读 error 文本;如果是环境问题,运行 agent-dispatch doctor;如果是暂时性错误,重试一次。

以下三种软信号会与 success: true 一起返回:

  • denied_tools + hint — Agent 已完成,但部分工具调用遭到拦截;结果可能不完整。授予访问权限(参见 permission 行)并重新分发。
  • outcome: "partial" / "blocked" — Agent 判定自身未能完成;结果会指出缺少的内容,hint 会提供在同一会话中继续执行的 dispatch_session(...) 调用。结果不会缓存,因此修复原因后重新分发会从头运行。
  • parsed_result: null 且 response_format="json" — 回复不是有效的 JSON;原始文本仍在 result 中。注意:无法按要求执行的 Agent 会返回 {"error": "<reason>"}——该内容可以成功解析——因此还要检查 parsed_result 中是否有 "error" 键。
  • budget_exceeded: true — cost_usd 超过了 Agent 的 max_budget_usd(或设置中的默认值),但 CLI 并未停止运行(最后一轮可能超出上限)。此次分发并未失败——费用已经产生——但超支的 Agent 现在已可见。请缩紧任务范围、选择更便宜的模型,或提高预算。如果 CLI 确实停止了运行,则会以 error_type: "budget" 标记失败。

工具级错误(未知 Agent、输入格式错误)会返回普通封装,而非 DispatchResult:

{"error": "Unknown agent: 'foo'. Available: infra, db, monitoring"}

配置

配置文件位于 ~/.config/agent-dispatch/agents.yaml(覆盖项:AGENT_DISPATCH_CONFIG 环境变量):

agents:
  infra:
    directory: ~/projects/infra
    description: "Infrastructure agent. MCP: portainer."
    timeout: 300            # seconds, default: 300
    capabilities:           # capability labels, shown in list_agents
      - docker_logs
      - deploy_debug
    risky_capabilities:     # high-risk labels, surfaced for visibility
      - restart_services
    # instructions: |       # standing orders, appended to the system prompt on every dispatch
    #   Read-only: never restart or redeploy unless the task says so.
    #   Quote exact log lines with timestamps.
    # model: sonnet         # optional model override
    # max_budget_usd: 1.0   # cost limit per dispatch
    # permission_mode: bypassPermissions  # one of: default | plan | bypassPermissions
    # allowed_tools:        # restrict which tools the agent can use
    #   - Read
    #   - Grep
    # disallowed_tools:     # block specific tools
    #   - Write

# Optional: bundle related agents into a cross-project working set.
# A descriptive layer — no router; the orchestrating session coordinates
# with the normal dispatch tools. See the Groups section above.
groups:
  shop:
    # ORCHESTRATOR-facing: how to coordinate the group. Surfaced by
    # list_groups/inspect_group, NEVER injected into a member's prompt.
    description: "After a code change: deploy via infra, then verify via analytics."
    # MEMBER-facing facts, auto-prepended to dispatch(..., group="shop").
    shared_context: |
      Prod runs in Portainer stack "shop". Metrica counter 12345.
    members:                # reference agents above (many-to-many)
      - agent: infra
        use_for: deploy, restart, container logs
      # - agent: backend
      #   use_for: orders/payments endpoints

settings:
  default_timeout: 300
  # default_permission_mode: bypassPermissions  # inherited by all agents
  # default_allowed_tools:                      # inherited when agent has none
  #   - Bash
  #   - Read
  #   - Edit
  max_dispatch_depth: 3     # recursion protection
  max_concurrency: 5        # max parallel claude -p processes per server, across all tools
  # dispatch_protocol: true # send every agent the dispatch protocol (non-interactive,
  #                         # time budget, STATUS line → `outcome`). false = raw claude -p.
  # usage_log: true         # record every dispatch in usage.jsonl (cost, duration, outcome).
  #                         # Powers `agent-dispatch stats`, the `typical` block and the
  #                         # measured timeout suggestion. false = record nothing.
  # job_retention_days: 30  # 0 (default) = never prune. See "Job retention" below.
  cache:
    enabled: true
    ttl: 300                # seconds
    max_size: 1000          # max cached entries; oldest evicted first (FIFO)

每次调用工具时都会重新加载配置——添加 Agent 无需重启。

使用日志——实际了解你的 Agent 集群成本

每次调度都会向 ~/.config/agent-dispatch/usage.jsonl 追加一行(可通过 AGENT_DISPATCH_USAGE_LOG 覆盖):Agent、成功状态、成本、耗时、 轮次、outcome、错误类型、调用方。缓存命中也会记录,并标记为 cached,因此报告可以显示缓存节省了多少。

$ agent-dispatch stats --days 7
Usage (last 7 day(s))
  dispatches: 33, 1 served from cache
  spend:      $13.0390
  duration:   median 87s, p90 2m40s, max 10m00s
  outcomes:   blocked 1, done 27, partial 3
  failures:   timeout 1, usage_limit 1

Per agent
  analytic           18 runs  $  8.9441  median  2m04s  p90  2m51s
      done 15, partial 3
  gitlab             12 runs  $  3.7949  median    60s  p90    86s
      done 12  |  1 cached

--agent NAME 可缩小范围,--json 会以 JSON 格式输出相同的报告。

日志让另外三项功能得以实现:list_agents / inspect_agent 中的 typical 区块、 超时错误会根据 Agent 实际的 p90 值命名,而不是采用翻倍猜测值;以及回答 “哪个 Agent 成本高”。在设置中将 usage_log: false 即可关闭日志。

值得了解的属性:文件仅所有者可访问(0o600);每条记录通过一次 O_APPEND 写入, 大小远低于 PIPE_BUF,因此 CLI 和所有运行中的服务可以无锁共享,不会产生不完整行; 文件约为 2 MB 时会轮转为 usage.jsonl.1,并保留两代,因此总大小永久限制在约 4 MB。 写入失败绝不会导致调度失败。

作业保留

每次 dispatch_async 以及每次 dispatch(..., return_ref=True) 都会在 ~/.config/agent-dispatch/jobs/ 中写入一条记录,且不会自动删除任何记录—— 必须手动运行 dispatch_gc。因此,该目录会无限增长,而 dispatch_jobs 以及每次服务 启动时运行的过期作业恢复流程,都会读取并解析其中的每个文件。

将 job_retention_days 设为 N 天,即可在服务启动时清理早于该期限的终态(已完成/失败/已取消)记录:

settings:
  job_retention_days: 30

默认值为 0——关闭——因为这些记录是你过去调度的历史记录,删除后无法恢复。 待处理和运行中的作业永远不会被清理。

agent-dispatch gc --days N 和 dispatch_gc 工具可按同一规则执行一次性清理。删除前可预览符合条件的记录:

agent-dispatch gc --days 30 --dry-run
agent-dispatch gc --days 30

--dry-run 也可与 --all 搭配使用。MCP 对应的调用为 dispatch_gc(max_age_days=30, dry_run=True),会返回 dry_run: true、 purged: 0、would_purge、job_ids 和 max_age_days。 预览只是一个快照;删除时会再次检查是否符合条件,因此作业完成后计数可能发生变化。 无法读取的记录、时间戳无效的记录,以及内部 ID 与文件名不一致的记录都会跳过并保留,以便检查。

自动描述

不带 --description 的 agent-dispatch add 会根据以下内容生成描述:

  • CLAUDE.md——第一个有实质内容的段落(优先)
  • README.md——第一行有实质内容的文字(备用)
  • pyproject.toml / package.json——项目描述
  • .mcp.json——列出 MCP 服务名称
  • 技术栈指标——Docker、Rust、Go、Python、Node.js
  • 数据库指标——Prisma、Alembic、迁移
显式能力

自动描述很有用,但显式设置 capabilities 能更清楚地说明各 Agent 的用途。为 Agent 添加简短的 snake_case 任务标签:

agent-dispatch update infra \
  --capabilities docker_logs,deploy_debug \
  --risky-capabilities restart_services

list_agents 和 inspect_agent 会显示 capabilities 和 risky_capabilities,让调用方一眼选出合适的 Agent——risky_capabilities 会标记风险较高的能力(例如重启服务),以便重点审查。

工作原理

Your Claude Code session
  │
  ├─ dispatch("infra", "find errors", caller="backend", goal="debug crash")
  │
  ▼
agent-dispatch MCP server
  ├─ cache check → hit? return cached result
  ├─ shared process limit → bound concurrency across all dispatch tools
  └─ subprocess.Popen(["claude", "-p", ...], cwd=~/projects/infra/)
       │
       ▼
     New Claude Code session in ~/projects/infra/
       ├─ Inherits: CLAUDE.md, .mcp.json, project tools
       ├─ System prompt += dispatch protocol + the agent's standing `instructions`
       ├─ Receives structured prompt with goal/caller/context/task
       └─ Returns result (+ its own STATUS → `outcome`) → cached when complete
                    │
                    └─ one line appended to usage.jsonl (cost, duration, outcome)

安全性

  • 递归保护 — AGENT_DISPATCH_DEPTH 环境变量用于跟踪嵌套层级。默认上限:3。跨子进程边界时尽力保证(参见 SECURITY.md)。
  • 参数注入防护 — 以 - 开头的结构化 CLI 字段(session_id、model、permission_mode、工具名称)会被拒绝,防止它们偷偷注入额外的 claude 标志。
  • 路径遍历防护 — 调用方提供的 job_id/ref 值会先验证是否为 32 位十六进制字符串,然后才进行任何文件系统访问。
  • 仅所有者可访问的状态文件 — 作业文件、agents.yaml、用量日志和服务注册表的权限均设为 0o600;其目录权限均设为 0o700。
  • 成本控制 — 每个 Agent 或全局的 max_budget_usd 会作为 --max-budget-usd 传给 claude CLI,因此失控的派发会在达到上限时停止,并返回带有可恢复 session_id 的 error_type: "budget"。如果未能及时停止而超出预算,则会在事后标记 budget_exceeded: true 并附带提示。
  • 并发 — max_concurrency(默认值:5)限制单个服务进程中所有派发工具可并行运行的 claude -p 进程数。普通、流式和后台派发共用同一上限;等待中的调用按到达顺序处理,因此后台作业队列不会让交互式调用无法获得处理机会。取消等待中的调用不会启动其进程;取消已启动的普通或流式调用后,其占用的并发名额会一直保留到工作进程结束。重新加载已更改的上限时,已占用的名额会保留:降低上限会让现有任务完成,并在容量可用前阻止启动新任务。每个独立的服务进程都有自己的上限。
  • 超时 — 可按 Agent 设置或全局设置(默认值:300 秒)。普通和流式派发都会在独立的进程组中运行 Agent。在 POSIX 系统上,达到截止时间后会终止该进程组,包括继承了输出管道的子进程。如果某个子进程故意脱离进程组,普通派发还会将清理时间限制在额外 1 秒内;该脱离的进程不受进程组控制。完整的 CLI 结果不会因清理超时而丢失;成功结果会附带提示。
  • 缓存 — 在相同的 Agent 配置和派发默认设置下,完全相同的 (agent, task, context, caller, goal, response_format) 请求会返回缓存结果,缓存大小受 cache.max_size 限制(优先淘汰最旧的条目)。仅缓存无异常的成功结果:失败结果、含有 denied_tools 的结果、标记了 budget_exceeded 的结果,以及 Agent 自身报告为 partial / blocked 的结果均不会缓存,因此文档所述的“授予访问权限 / 提高上限,然后重新派发”恢复方式绝不会返回过时的不完整答案。更改 Agent 配置会使其缓存条目失效。会话和对话绝不缓存。group= 派发会将组的 shared_context 合并到 context,因此不同组会分别缓存,普通派发则不受影响。
  • 持久化配置 — agents.yaml 会以原子方式写入(临时文件 + 重命名),因此中断的写入绝不会截断该文件。CLI 和 MCP 服务的每种变更路径都会获取跨进程咨询锁,因此并发编辑不会导致彼此的 Agent 配置丢失。该锁按设计尽力保证:等待 10 秒后会记录警告并继续执行,因为卡住的锁持有者不应冻结 MCP 服务——因此,在配置竞争激烈时可能发生更新丢失,但不会发生文件截断。

完整的威胁模型(包括 bypassPermissions 权限提升风险和磁盘上的作业文件)请参见 SECURITY.md。

CLI

CommandDescription
agent-dispatch init创建配置,并向 Claude Code 注册 MCP 服务
agent-dispatch add <name> <dir>添加 Agent(自动生成描述)
agent-dispatch update <name>更新 Agent 配置(权限、超时、模型、--instructions 等)
agent-dispatch remove <name>移除 Agent
agent-dispatch list列出 Agent 及其运行状况和权限
agent-dispatch group <add|list|inspect|update|remove>管理群组——跨项目的 Agent 工作集
agent-dispatch describe <name>显示单个 Agent 的完整配置(三态工具、项目文件)
agent-dispatch test <name> [task] [--stream]通过一次调度测试 Agent(使用 --stream 查看实时进度)
agent-dispatch stats [--days N --agent X --json]查看调度开销:各 Agent 的费用、耗时、结果和失败情况
agent-dispatch doctor [--json --strict]诊断安装、过时的服务、MCP 注册、Agent 运行状况和群组成员;--strict 还会在有警告时返回失败
agent-dispatch jobs [--status STATUS --agent NAME --limit N --json]列出最近的作业摘要,包括以 return_ref 存储的结果
agent-dispatch job <id> [--json]显示单个作业;--json 可导出未截断的完整记录
agent-dispatch cancel <id>取消待处理的作业(对于运行中的作业,请使用 dispatch_cancel MCP 工具)
agent-dispatch gc [--days N | --all] [--dry-run]清除早于 N 天的终态作业(默认 7 天;--all 会清除所有时长的作业);--dry-run 可预览而不删除
agent-dispatch serve启动 MCP 服务(stdio,由 Claude Code 使用)

若要通过脚本检查安装情况:

agent-dispatch doctor --json --strict > diagnostics.json

报告包含 schema_version: 1、总体 status(ok、warn 或 fail)、issues 和 warnings 计数,以及一个 checks 数组。每项检查都有 section、status、message,以及包含补救步骤或支持信息的 details 数组。 即使检查失败,也会输出 JSON。 失败时退出代码为 1;除非设置了 --strict,否则有警告时退出代码为 0。 常规的人类可读输出也采用相同的检查和退出策略。 此命令检查安装和配置;不会执行会产生费用的调度。

若要通过脚本检查历史记录或保存完整结果:

agent-dispatch jobs --agent infra --status failed --limit 10 --json
agent-dispatch job <job_id> --json > job.json

jobs --json 会返回与 dispatch_jobs 格式相同的精简摘要数组;如果没有匹配的作业,则返回 []。每个摘要都包含 ID、Agent、状态、任务的前 120 个字符和时间戳;如果有可用信息,还会包含结果费用、成功状态、结果、错误类型和最新的运行进度。 job --json 会返回存储的完整作业,包括任务/上下文、进度、结果文本、结构化结果、会话 ID,以及可用时的恢复提示。 这两种格式都会保留 Unicode。读取失败的作业仍会成功退出: 请检查其 status 和 result.success 以确定调度结果。 缺失或无效的作业 ID,以及存储打开失败时,会返回 {"error": "..."}, 退出代码为 1。无效的命令行选项由 Click 输出到 stderr。

无效或非有限的费用元数据会被视为未知,并从结果导出中省略;不会因此隐藏已存储的结果文本。使用情况摘要会忽略不可用的数值测量;如果服务的启动时间戳无法读取,doctor 会省略运行时长。

Requirements

  • Python >= 3.10
  • 已安装、完成身份验证并位于 PATH 中的 Claude Code CLI(验证方法:claude --version)

License

MIT

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。