MCP server that lets Claude Code agents delegate tasks to agents in other project directories.
Each agent runs as a separate claude -p session in its own project directory — inheriting that project's MCP servers, CLAUDE.md, and tools. The calling agent just gets the result back.
Works with OAuth, API key, and Claude subscription authentication.
pip install agent-dispatch
# Initialize: creates config + registers MCP server with Claude Code
agent-dispatch init
# Add agents (description auto-generated from project files)
agent-dispatch add infra ~/projects/infra
agent-dispatch add backend ~/projects/backend
# Test it works
agent-dispatch test infra
# If agents hit permission errors, grant tool access:
agent-dispatch update infra --permission-mode bypassPermissionsDone. Every Claude Code session now has access to all dispatch tools.
Do dispatch when a task needs tools, files, or context from another project:
- Check container logs via infra agent's Portainer MCP
- Query a database via db agent's postgres MCP
- Read code or run tests in another repository
Don't dispatch when you can do it yourself — dispatching spawns a full Claude session.
Lists all configured agents. Call this first to see what's available.
// Response (permission fields shown only when configured)
[
{
"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,
"permission_mode": "bypassPermissions",
"allowed_tools": ["Bash", "Read", "Grep"]
}
]One-shot task delegation. Results are cached — identical requests within TTL return instantly.
| Parameter | Type | Required | Description |
|---|---|---|---|
agent |
string | yes | Agent name from list_agents |
task |
string | yes | What to do — be specific, the agent has no context from your conversation |
context |
string | no | Extra context: error messages, code snippets, stack traces |
caller |
string | no | Your project/role — helps the agent understand who's asking |
goal |
string | no | Broader objective — helps the agent make better trade-offs |
// 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
}
// 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 values: permission (tool/action denied), timeout, recursion (dispatch depth exceeded), not_found (missing directory or CLI), cli_error (other failures). Permission errors include an actionable hint.
Always pass caller and goal — the dispatched agent sees a structured prompt:
## 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 serviceMulti-turn: continue a conversation with an agent. First call starts a session, pass session_id back to continue. Never cached.
| Parameter | Type | Required | Description |
|---|---|---|---|
agent |
string | yes | Agent name |
task |
string | yes | Task or follow-up message |
session_id |
string | no | From previous response — empty for new session |
context |
string | no | Extra context |
caller |
string | no | Who is dispatching |
goal |
string | no | Broader objective |
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
Run multiple tasks concurrently. Much faster than sequential dispatch calls.
| Parameter | Type | Required | Description |
|---|---|---|---|
dispatches |
string (JSON) | yes | JSON array of {"agent", "task", "context?", "caller?", "goal?"} |
aggregate |
string | no | Agent name to synthesize all results into one answer |
Important: dispatches is a JSON string, not a list.
// 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."
}
}Same as dispatch but shows live progress while the agent works. Use for long-running tasks. Not cached.
Parameters are identical to dispatch.
Two agents collaborate through multi-turn conversation. Never cached.
| Parameter | Type | Required | Description |
|---|---|---|---|
requester |
string | yes | Agent with the problem/context |
responder |
string | yes | Agent with the expertise/tools |
topic |
string | yes | Problem or question to discuss |
max_rounds |
int | no | Max back-and-forth rounds (default: 3, max: 10) |
Each round costs up to 2 dispatches. Agents signal completion with [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}
]
}Register a new project directory as an agent. Description is auto-generated from project files if omitted.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Agent name (letters, digits, hyphens, underscores) |
directory |
string | yes | Absolute path to project directory |
description |
string | no | What this agent can do — auto-generated if empty |
timeout |
int | no | Timeout in seconds (0 = use global default) |
permission_mode |
string | no | Permission mode (e.g. default, plan, bypassPermissions) |
allowed_tools |
string | no | Comma-separated allowed tools (e.g. "Bash,Read,Edit") |
disallowed_tools |
string | no | Comma-separated disallowed tools |
Update an existing agent's configuration. Only non-empty fields are changed. Pass "none" to clear a field.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Agent name to update |
description |
string | no | New description |
timeout |
int | no | New timeout (0 = don't change) |
model |
string | no | Model override. "none" to clear |
permission_mode |
string | no | Permission mode. "none" to clear |
allowed_tools |
string | no | Comma-separated. "none" to clear |
disallowed_tools |
string | no | Comma-separated. "none" to clear |
Remove an agent from config.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Agent name to remove |
View cache hit rate and size, or clear all cached results.
All tools return errors as:
{"error": "Unknown agent: 'foo'. Available: infra, db, monitoring"}| Scenario | Tool |
|---|---|
| Quick one-off question to another project | dispatch |
| Multi-step workflow with follow-ups | dispatch_session |
| Need answers from several agents at once | dispatch_parallel |
| Long task, want to see progress | dispatch_stream |
| Two agents need to collaborate | dispatch_dialogue |
| Need a combined summary from multiple agents | dispatch_parallel with aggregate |
Config at ~/.config/agent-dispatch/agents.yaml (override: AGENT_DISPATCH_CONFIG env var):
agents:
infra:
directory: ~/projects/infra
description: "Infrastructure agent. MCP: portainer."
timeout: 300 # seconds, default: 300
# model: sonnet # optional model override
# max_budget_usd: 1.0 # cost limit per dispatch
# permission_mode: auto # permission mode for the agent
# allowed_tools: # restrict which tools the agent can use
# - Read
# - Grep
# disallowed_tools: # block specific tools
# - Write
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
cache:
enabled: true
ttl: 300 # secondsConfig is reloaded on every tool call — add agents without restarting.
agent-dispatch add without --description generates one from:
CLAUDE.md— first meaningful paragraph (priority)README.md— first substantial line (fallback)pyproject.toml/package.json— project description.mcp.json— lists MCP server names- Stack indicators — Docker, Rust, Go, Python, Node.js
- DB indicators — Prisma, Alembic, migrations
Your Claude Code session
│
├─ dispatch("infra", "find errors", caller="backend", goal="debug crash")
│
▼
agent-dispatch MCP server
├─ cache check → hit? return cached result
├─ semaphore → limit concurrent processes
└─ subprocess.run("claude -p ...", cwd=~/projects/infra/)
│
▼
New Claude Code session in ~/projects/infra/
├─ Inherits: CLAUDE.md, .mcp.json, project tools
├─ Receives structured prompt with goal/caller/context/task
└─ Returns result → cached for future identical requests
- Recursion protection —
AGENT_DISPATCH_DEPTHenv var tracks nesting. Default limit: 3. - Cost control —
max_budget_usdper agent or globally. - Concurrency —
max_concurrency(default: 5) limits parallelclaude -pprocesses. - Timeout — per-agent or global (default: 300s). Orphaned processes are cleaned up.
- Caching — identical
(agent, task, context)requests return cached results. Only successes are cached. Sessions and dialogues are never cached.
| Command | Description |
|---|---|
agent-dispatch init |
Create config + register MCP server with Claude Code |
agent-dispatch add <name> <dir> |
Add an agent (auto-generates description) |
agent-dispatch update <name> |
Update agent config (permissions, timeout, model, etc.) |
agent-dispatch remove <name> |
Remove an agent |
agent-dispatch list |
List agents with health status and permissions |
agent-dispatch test <name> [task] |
Test an agent with a dispatch |
agent-dispatch serve |
Start MCP server (stdio, used by Claude Code) |
- Python >= 3.10
- Claude Code CLI installed and authenticated
MIT
