# Task Graph MCP Server
**Agent task workflows that actually work.**
When you have AI agents working on complex tasks, things go wrong fast. Agents lose context, skip steps, forget to coordinate. Task Graph solves this with structured workflows: phases to guide work, prompts for automatic guidance, gates to enforce quality, and coordination primitives for multi-agent scenarios—all through the Model Context Protocol.
## Why Task Graph?
**The problem**: You've got complex tasks that need structured execution. Maybe a single agent working through phases, or multiple agents coordinating in parallel. Without proper workflows, agents lose track, skip steps, and produce inconsistent results.
**What you get**:
- **Structured workflows** — Phases (explore, implement, review, test) guide agents through work. Transition prompts provide automatic guidance at each step.
- **Quality gates** — Require tests to pass, code to be committed, or reviews to complete before transitions. Enforce your standards automatically.
- **Ready-to-use topologies** — Pre-built workflows for solo work, parallel swarms, specialist relays, or hierarchical delegation. Start immediately, customize later.
- **Configurable workflows** — Define your own states, phases, prompts, and gates. Match your process, not ours.
- **Multi-agent coordination** — Advisory file locks, DAG dependencies, atomic claiming. No more conflicts or duplicate work.
- **Token-efficient** — Designed for LLM context limits. Compact queries, minimal round-trips, structured outputs.
- **Built-in accounting** — Track tokens, cost, and time per task. Know exactly what your agents are spending.
- **Zero infrastructure** — SQLite with WAL mode. No database server to run. Just point at a file.
## Features
| **Task Hierarchy** | Unlimited nesting with parent/child relationships |
| **DAG Dependencies** | Typed edges (blocks, follows, contains) with cycle detection |
| **Phases** | Categorize work type (explore, implement, review, test, deploy) |
| **Workflows** | Named workflow topologies (solo, swarm, relay, hierarchical) |
| **Transition Prompts** | Automatic agent guidance on status/phase changes |
| **Gates** | Exit requirements for status/phase transitions |
| **Atomic Claiming** | Strict locking with limits and tag-based routing |
| **File Coordination** | Advisory locks with reasons and change polling |
| **Cost Tracking** | Token usage and USD cost per task |
| **Time Tracking** | Automatic accumulation from state transitions |
| **Live Status** | Real-time "current thought" visible to other agents |
| **Full-text Search** | FTS5-powered search across tasks and attachments |
| **Attachments** | Inline content, file references, or media storage |
## Quick Start
```bash
# Install
cargo install task-graph-mcp
# Add to your MCP client (Claude Code, etc.)
```
```json
{
"mcpServers": {
"task-graph": {
"command": "task-graph-mcp"
}
}
}
```
```
# Agent workflow (worker_id auto-generated if omitted)
connect(workflow="swarm", tags=["code"]) → "bright-lunar-swift-fox"
list_tasks(ready=true, agent="bright-lunar-swift-fox") → claimable work
claim(worker_id="bright-lunar-swift-fox", task="add-auth") → you own it
update(..., phase="implement") → enter implementation phase
thinking(agent="bright-lunar-swift-fox", thought="Adding JWT...") → visible to others
update(worker_id="bright-lunar-swift-fox", task="add-auth",
status="completed",
attachments=[{type:"commit", content:"abc123"}]) → done
```
## Installation
### From crates.io (Recommended)
```bash
cargo install task-graph-mcp
```
### Pre-built Binaries
Download the latest release for your platform from [GitHub Releases](https://github.com/Oortonaut/task-graph-mcp/releases):
| Linux (x64) | `task-graph-mcp-x86_64-unknown-linux-gnu.tar.gz` |
| macOS (Intel) | `task-graph-mcp-x86_64-apple-darwin.tar.gz` |
| macOS (Apple Silicon) | `task-graph-mcp-aarch64-apple-darwin.tar.gz` |
| Windows (x64) | `task-graph-mcp-x86_64-pc-windows-msvc.zip` |
Extract and place the binary in your PATH.
### From Source
```bash
git clone https://github.com/Oortonaut/task-graph-mcp.git
cd task-graph-mcp
cargo build --release
```
The binary will be at `target/release/task-graph-mcp`.
## Usage
### As an MCP Server
Add to your MCP client configuration:
```json
{
"mcpServers": {
"task-graph": {
"command": "task-graph-mcp",
"args": []
}
}
}
```
### CLI Options
```
task-graph-mcp [OPTIONS]
Options:
-c, --config <FILE> Path to configuration file
-d, --database <FILE> Path to database file (overrides config)
-v, --verbose Enable verbose logging
-h, --help Print help
-V, --version Print version
```
## Configuration
> **Full reference**: See [docs/CONFIGURATION.md](docs/CONFIGURATION.md) for complete configuration documentation including workflows, prompts, gates, roles, and tags.
Create `.task-graph/config.yaml`:
```yaml
server:
db_path: .task-graph/tasks.db
media_dir: .task-graph/media # Directory for file attachments
skills_dir: .task-graph/skills # Custom skill overrides
stale_timeout_seconds: 900
default_format: json # or markdown
paths:
style: relative # or project_prefixed
auto_advance:
enabled: false # Auto-transition unblocked tasks
target_state: ready # Target state (requires custom state in states config)
```
### States Configuration
Task states are configurable. Default states: `pending`, `working`, `completed`, `failed`, `cancelled`.
To add a `ready` state for auto-advance:
```yaml
states:
initial: pending
disconnect_state: pending # State for tasks when owner disconnects (must be untimed)
blocking_states: [pending, working]
definitions:
pending:
exits: [ready, working, cancelled]
ready:
exits: [working, cancelled]
working:
exits: [completed, failed, pending]
timed: true # Time in this state counts toward time_actual_ms
completed:
exits: []
failed:
exits: [pending]
cancelled:
exits: []
auto_advance:
enabled: true
target_state: ready
```
See [SCHEMA.md](SCHEMA.md#states-configuration) for full documentation on state definitions.
### Dependencies Configuration
Dependency types define how tasks relate to each other. Default types: `blocks`, `follows`, `contains`, `duplicate`, `see-also`.
```yaml
dependencies:
definitions:
blocks:
display: horizontal # Same-level relationship
blocks: start # Blocks claiming the dependent task
follows:
display: horizontal
blocks: start
contains:
display: vertical # Parent-child relationship
blocks: completion # Blocks completing the parent
duplicate:
display: horizontal
blocks: none # Informational only
see-also:
display: horizontal
blocks: none
```
| `display` | `horizontal`, `vertical` | Visual relationship (same-level vs parent-child) |
| `blocks` | `none`, `start`, `completion` | What the dependency blocks |
### Attachments Configuration
Preconfigured attachment keys provide default MIME types and modes, reducing boilerplate when attaching common content types.
```yaml
attachments:
unknown_key: warn # allow | warn (default) | reject
definitions:
commit:
mime: text/git.hash
mode: append
checkin:
mime: text/p4.changelist
mode: append
meta:
mime: application/json
mode: replace
note:
mime: text/plain
mode: append
```
| `unknown_key` | `allow`, `warn`, `reject` | Behavior for undefined attachment keys |
| `definitions.<key>.mime` | MIME type string | Default MIME type for this key |
| `definitions.<key>.mode` | `append`, `replace` | Default mode (append keeps existing, replace overwrites) |
**Built-in defaults**:
| `commit` | text/git.hash | append | Git commit hashes |
| `checkin` | text/p4.changelist | append | Perforce changelists |
| `changelist` | text/plain | append | Files changed |
| `meta` | application/json | replace | Structured metadata |
| `note` | text/plain | append | General notes |
| `log` | text/plain | append | Log output |
| `error` | text/plain | append | Error messages |
| `output` | text/plain | append | Command/tool output |
| `diff` | text/x-diff | append | Patches and diffs |
| `plan` | text/markdown | replace | Plans and specs |
| `result` | application/json | replace | Structured results |
| `context` | text/plain | replace | Current context/state |
**Usage**:
```
# MIME and mode auto-filled from config:
attach(task="123", name="commit", content="abc1234")
# → mime=text/git.hash, mode=append
attach(task="123", name="meta", content='{"v":1}')
# → mime=application/json, mode=replace (overwrites existing meta)
# Explicit values override defaults:
attach(task="123", name="commit", mime="text/plain", content="override")
```
Environment variables:
- `TASK_GRAPH_CONFIG_PATH`: Path to configuration file (takes precedence over `.task-graph/config.yaml`)
- `TASK_GRAPH_DB_PATH`: Database file path (fallback if no config file)
- `TASK_GRAPH_MEDIA_DIR`: Media directory for file attachments (fallback if no config file)
- `TASK_GRAPH_LOG_DIR`: Log directory path (fallback if no config file)
## MCP Tools
### Worker Management
| `connect(worker_id?, tags?, workflow?, force?, db_path?, media_dir?, log_dir?, config_path?)` | Register a worker. Optional `workflow` selects named workflow (solo, swarm, relay, hierarchical). Returns `worker_id` and active `paths`. |
| `disconnect(worker_id: worker_str, final_status?: status_str = "pending")` | Unregister worker and release all claims/locks. |
| `list_workers(tags?: str[], file?: filename, task?: task_str, depth?: int)` | List connected workers with filters. |
### Task CRUD
| `create(description: str, id?: task_str, parent?: task_str, priority?: int = 5, points?: int, time_estimate_ms?: int, tags?: str[])` | Create a task. Priority 0-10 (higher = more important). |
| `create_tree(tree, parent?, child_type?, sibling_type?)` | Create nested task tree. `child_type` (default: "contains") for parent→child deps, `sibling_type` for sibling deps. |
| `get(task: task_str)` | Get task by ID with attachment metadata and counts. |
| `list_tasks(status?: status_str[], ready?: bool, blocked?: bool, claimed?: bool, owner?: worker_str, parent?: task_str, worker_id?: worker_str, tags_any?: str[], tags_all?: str[], sort_by?: str, sort_order?: str, limit?: int)` | Query tasks with filters. Use `ready=true` for claimable tasks. |
| `update(worker_id: worker_str, task: task_str, status?: status_str, phase?: str, assignee?: worker_str, title?: str, description?: str, priority?: int, points?: int, tags?: str[], needed_tags?: str[], wanted_tags?: str[], time_estimate_ms?: int, reason?: str, force?: bool, attachments?: object[])` | Update task. Status/phase changes auto-manage ownership and trigger prompts. Include `attachments` to record commits/changelists. |
| `delete(worker_id: worker_str, task: task_str, cascade?: bool, reason?: str, obliterate?: bool, force?: bool)` | Delete task. Soft delete by default; `obliterate=true` for permanent. |
| `scan(task: task_str, before?: int, after?: int, above?: int, below?: int)` | Scan task graph in multiple directions. Depth: 0=none, N=levels, -1=all. |
| `search(query: str, limit?: int = 20, include_attachments?: bool, status_filter?: status_str)` | FTS5 search. Supports phrases, prefix*, AND/OR/NOT, title:word. |
| `rename(worker_id: worker_str, task: task_str, new_id: task_str)` | Atomically rename a task ID across all referencing tables. |
### Task Claiming
| `claim(worker_id: worker_str, task: task_str, force?: bool)` | Claim a task. Fails if deps unsatisfied, at limit, or lacks tags. Use `force` to steal. |
**Note**: Release via `update(status="pending")`. Complete via `update(status="completed")`. Status changes auto-manage ownership.
### Dependencies
| `link(from: task_str\|task_str[], to: task_str\|task_str[], type?: dep_str = "blocks")` | Create dependencies. Types: blocks, follows, contains, duplicate, see-also. |
| `unlink(from: task_str\|"*", to: task_str\|"*", type?: dep_str)` | Remove dependencies. Use `*` as wildcard. |
| `relink(prev_from: task_str[], prev_to: task_str[], from: task_str[], to: task_str[], type?: dep_str = "contains")` | Atomically move dependencies (unlink then link). |
### Tracking
| `thinking(worker_id: worker_str, thought: str, tasks?: task_str[])` | Broadcast live status. Visible to other workers. Refreshes heartbeat. |
| `task_history(task: task_str, states?: status_str[])` | Get status transition history with time tracking. |
| `project_history(from?: datetime_str, to?: datetime_str, states?: status_str[], limit?: int = 100)` | Project-wide history with date range filters. |
| `log_metrics(worker_id: worker_str, task: task_str, cost_usd?: float, values?: int[8])` | Log metrics (aggregated). |
| `get_metrics(task: task_str\|task_str[])` | Get metrics for task(s). |
### File Coordination
| `mark_file(worker_id: worker_str, file: filename\|filename[], task?: task_str, reason?: str)` | Mark file(s) to signal intent. Advisory, non-blocking. |
| `unmark_file(worker_id: worker_str, file?: filename\|filename[]\|"*", task?: task_str, reason?: str)` | Remove marks. Use `*` for all. |
| `list_marks(files?: filename[], worker_id?: worker_str, task?: task_str)` | Get current file marks. |
| `mark_updates(worker_id: worker_str)` | Poll for mark changes since last call. |
### Attachments
| `attach(task: task_str\|task_str[], name: str, content?: str, mime?: mime_str, file?: filename, store_as_file?: bool, mode?: str)` | Add attachment. Use `file` for reference, `store_as_file` for media storage. |
| `attachments(task: task_str, name?: str, mime?: mime_str)` | Get attachment metadata. Glob patterns supported for name. |
| `detach(worker_id: worker_str, task: task_str, name: str, delete_file?: bool)` | Delete attachment by name. |
### Advanced
| `check_gates(task: task_str)` | Check gate requirements before status/phase transition. Returns unsatisfied gates with pass/warn/fail status. |
| `query(sql: str, params?: str[], limit?: int = 100, format?: str)` | Execute read-only SQL. SELECT only. Requires permission. |
| `get_schema(table?: str, include_sql?: bool)` | Get database schema. Returns table names, columns, types, and foreign keys. |
| `list_workflows()` | List available workflow configurations (solo, swarm, relay, hierarchical, etc.). |
## MCP Resources
| `query://tasks/all` | Full task graph with dependencies |
| `query://tasks/ready` | Tasks ready to claim |
| `query://tasks/blocked` | Tasks blocked by dependencies |
| `query://tasks/claimed` | All claimed tasks |
| `query://tasks/agent/{id}` | Tasks owned by an agent |
| `query://tasks/tree/{id}` | Task with all descendants |
| `query://files/marks` | All file marks |
| `query://agents/all` | Registered agents |
| `query://stats/summary` | Aggregate statistics |
| `config://current` | All configuration in one response |
| `config://states` | Task state definitions |
| `config://phases` | Phase definitions |
| `config://dependencies` | Dependency type definitions |
| `config://tags` | Tag definitions |
| `docs://index` | List all available documentation files |
| `docs://search/{query}` | Full-text search across documentation |
| `docs://skills/list` | List available skills |
| `docs://skills/{name}` | Get specific skill content |
| `docs://workflows/list` | List available workflows |
| `docs://workflows/{name}` | Get workflow details |
| `docs://{path}` | Specific documentation file content |
## Task Tree Structure
Create hierarchical tasks with `create_tree`:
```json
{
"tree": {
"title": "Implement auth",
"children": [
{ "title": "Design schema" },
{ "title": "Write migrations" },
{ "title": "Implement endpoints", "children": [
{ "title": "Login endpoint" },
{ "title": "Logout endpoint" },
{ "title": "Refresh endpoint" }
]},
{ "title": "Write tests" }
]
},
"sibling_type": "follows"
}
```
### Tree Node Fields
| `title` | Task title (required for new tasks) |
| `description` | Task description |
| `id` | Custom task ID (UUID7 generated if omitted) |
| `ref` | Reference existing task by ID (other fields ignored when set) |
| `priority` | Priority 0-10 (default 5) |
| `points` | Story points / complexity estimate |
| `time_estimate_ms` | Estimated duration in milliseconds |
| `tags` | Categorization tags for the task |
| `needed_tags` | Agent must have ALL of these tags to claim (AND) |
| `wanted_tags` | Agent must have AT LEAST ONE of these tags to claim (OR) |
| `children` | Nested child nodes |
### Top-Level Parameters
| `tree` | required | Root node of the task tree |
| `parent` | null | Attach tree root to existing parent task |
| `child_type` | "contains" | Dependency type from parent to children |
| `sibling_type` | null | Dependency type between siblings ("follows" for sequential, null for parallel) |
### Referencing Existing Tasks
Use `ref` to integrate existing tasks into a tree structure:
```json
{
"tree": {
"title": "Sprint 5",
"children": [
{ "title": "New feature" },
{ "ref": "existing-task-id" },
{ "title": "Another task" }
]
},
"sibling_type": "follows"
}
```
## Tag-Based Affinity
Workers declare capabilities via tags when connecting. Tasks can require specific tags to control which workers can claim them.
**Example tag categories:**
- **Model capabilities**: `image-in`, `audio-out`, `video-in`, `code`, `bulk`
- **Access levels**: `prod-access`, `admin`, `external`
- **Specializations**: `rust`, `python`, `frontend`, `database`
*Note: Roles like coordinator/reviewer/deployer are better represented using phases.*
**Task requirements:**
- `needed_tags` (AND): Agent must have ALL of these
- `wanted_tags` (OR): Agent must have AT LEAST ONE
```json
{
"title": "Analyze screenshot and generate code",
"needed_tags": ["image-in", "code"],
"wanted_tags": ["bulk"]
}
```
```json
{
"title": "Deploy to production",
"phase": "deploy",
"needed_tags": ["prod-access"],
"wanted_tags": ["aws", "gcp"]
}
```
## Workflows and Phases
### Phases
Tasks can have a `phase` to categorize the type of work being performed:
```json
{
"title": "Add authentication",
"phase": "implement"
}
```
Built-in phases: `explore`, `implement`, `review`, `test`, `security`, `deploy`, `triage`, `diagnose`, `design`, `plan`, `doc`, `integrate`, `monitor`, `optimize`
Phases enable:
- **Transition prompts** — Automatic guidance when entering/exiting phases
- **Gates** — Requirements that must be satisfied before phase transitions
- **Role-based routing** — In relay workflows, specialists own specific phases
### Named Workflows
Pre-built workflow topologies optimize for different coordination patterns:
| `solo` | Single agent, full autonomy | Simple tasks, prototyping |
| `swarm` | Parallel generalists, pull-based | High throughput, independent tasks |
| `relay` | Sequential specialists, handoffs | Complex tasks, domain expertise |
| `hierarchical` | Lead/worker delegation | Large projects, team coordination |
Select a workflow on connect:
```
connect(worker_id="agent-1", workflow="swarm")
```
Each workflow provides tailored prompts and coordination guidance. See [WORKFLOW_TOPOLOGIES.md](docs/WORKFLOW_TOPOLOGIES.md) for detailed patterns.
### Transition Prompts
Agents receive automatic guidance when status or phase changes:
```yaml
# workflows.yaml
states:
working:
prompts:
enter: |
You are now working on this task.
From {{current_status}} you can transition to: {{valid_exits}}
exit: |
Before leaving:
- [ ] Attach results
- [ ] Log costs
```
Prompts support template variables: `{{current_status}}`, `{{valid_exits}}`, `{{current_phase}}`, `{{valid_phases}}`
### Gates
Gates are requirements that must be satisfied before status or phase transitions:
```yaml
gates:
status:working:
- type: gate/tests
enforcement: warn
description: "Tests must pass"
```
Satisfy a gate by attaching evidence:
```
attach(task="123", type="gate/tests", content="All tests passing")
```
Enforcement levels: `allow` (advisory), `warn` (blocks unless `force=true`), `reject` (hard block)
## File Coordination
Agents can coordinate file edits using advisory marks with change tracking:
```
Worker A: connect() -> "worker-a"
Worker A: mark_file("worker-a", "src/main.rs", "refactoring")
Worker B: connect() -> "worker-b"
Worker B: mark_updates("worker-b") -> sees worker-a's mark
Worker A: unmark_file("worker-a", "src/main.rs", "ready for review")
Worker B: mark_updates("worker-b") -> sees removal with reason
Worker B: mark_file("worker-b", "src/main.rs", "adding tests")
```
## Architecture
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Agent A │ │ Agent B │ │ Agent C │
│ (Claude) │ │ (GPT-4) │ │ (Worker) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ stdio │ stdio │ stdio
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ task-graph │ │ task-graph │ │ task-graph │
│ MCP │ │ MCP │ │ MCP │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└───────────────────┼───────────────────┘
▼
┌─────────────────┐
│ SQLite + WAL │
│ .task-graph/ │
│ tasks.db │
└─────────────────┘
```
- **Transport**: Stdio — each worker spawns its own server process
- **Database**: SQLite with WAL mode for concurrent access across processes
- **Deployment**: Single binary, no external dependencies, works offline
## Compared to Alternatives
| Workflow phases | ✓ Built-in with prompts | ✗ Manual tracking | DIY |
| Quality gates | ✓ Configurable enforcement | ✗ | DIY |
| Multi-agent safe | ✓ Atomic claims, file locks | ✗ Race conditions | Maybe, DIY |
| Dependency tracking | ✓ DAG with cycle detection | ✗ Manual ordering | DIY |
| MCP native | ✓ First-class | ✗ Wrapper needed | ✗ Wrapper needed |
| Token accounting | ✓ Built-in | ✗ | DIY |
| Setup required | None | None | Database server |
## Documentation
| [CONFIGURATION.md](docs/CONFIGURATION.md) | Complete configuration reference (config.yaml, workflows, prompts, gates, tags) |
| [SCHEMA.md](docs/SCHEMA.md) | Database schema and state machine documentation |
| [DESIGN.md](docs/DESIGN.md) | Architecture and design decisions |
| [WORKFLOW_TOPOLOGIES.md](docs/WORKFLOW_TOPOLOGIES.md) | Multi-agent workflow patterns (solo, swarm, relay, hierarchical) |
| [EXPORT_IMPORT.md](docs/EXPORT_IMPORT.md) | Data export and import functionality |
| [PROCESSES.md](docs/PROCESSES.md) | Release process, changelog maintenance |
## License
Apache 2.0
---
Built for AI agents that need structured workflows and reliable coordination.