# peon-mcp

An MCP server for tracking software project tasks, features, and work logs in SQLite. Designed for AI-agent-driven development workflows. Includes a Python backend (FastMCP + FastAPI) and a React/TypeScript frontend.

## What is peon-mcp?

peon-mcp is a task management system built specifically for AI-driven development. It provides:

- **MCP Server** - Tools for Claude (or other MCP clients) to create, track, and execute tasks
- **Web UI** - A React-based dashboard to visualize projects, features, tasks, and work logs
- **Agent Loop** - An autonomous loop that spawns Claude to continuously work through your task queue

The name comes from Warcraft's peons - diligent workers who respond with "Work work!" and "Zug zug!" when given tasks.

## Key Features

- **Structured Task Management** - Organize work into projects, features, and tasks with priorities
- **Autonomous Execution** - The `peon-loop` command runs Claude continuously to work through tasks
- **Git Integration** - Tracks commit SHAs for audit trails (base and head commits per task)
- **Work Logging** - Records what was done, files changed, and test results for each task
- **Agent Memories** - Persistent project-scoped learnings with LRU eviction and access-count ranking
- **Loop Detection** - Sliding-window pattern matching to catch stuck agents (read-only loops, polling without progress)
- **Web Dashboard** - Monitor progress, create tasks, and view work logs in real-time
- **SQLite Backend** - Lightweight, file-based storage with no external database required

## Installation

### From PyPI

```bash
pip install peon-mcp
```

### From source

```bash
git clone https://github.com/pyrabbit/peon-mcp.git
cd peon-mcp
uv sync
```

## Quick Start

### 1. Run the MCP Server

For use with Claude Desktop or Claude Code:

```bash
peon-mcp
# or
uv run python -m peon_mcp.server
```

### 2. Run the Web UI

Launch the web interface and REST API:

```bash
peon-ui
# or
uv run python -m peon_mcp.web
```

Then open http://127.0.0.1:8420 in your browser.

### 3. Run the Agent Loop

Start an autonomous loop that continuously works through tasks:

```bash
peon-loop peon-mcp /path/to/your/repo --once
# or for continuous operation:
peon-loop peon-mcp /path/to/your/repo
```

Options:
- `--once` - Run a single iteration then exit
- `--model sonnet` - Choose Claude model (sonnet, opus, haiku)
- `--timeout 1800` - Max seconds per agent run (default: 1800)
- `--sleep 30` - Seconds between iterations (default: 30)
- `--max-budget 5.0` - Max USD budget per agent invocation
- `--permission-mode bypassPermissions` - Claude permission mode

## Configuration

### Claude Desktop / Claude Code

Add to your MCP config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):

```json
{
  "mcpServers": {
    "peon-mcp": {
      "command": "peon-mcp"
    }
  }
}
```

Or if running from source:

```json
{
  "mcpServers": {
    "peon-mcp": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/peon-mcp", "python", "-m", "peon_mcp.server"]
    }
  }
}
```

### Environment Variables

| Variable | Default | Description |
|---|---|---|
| `PEON_DB_PATH` | `~/.peon/peon_tasks.db` | SQLite database path |
| `PEON_UI_HOST` | `127.0.0.1` | Web UI bind address |
| `PEON_UI_PORT` | `8420` | Web UI port (auto-fallback if taken) |
| `PEON_API_KEY` | *(unset)* | If set, requires `X-API-Key` header for programmatic API access (not used by the UI) |

## Architecture

### Three Entry Points

peon-mcp provides three command-line tools:

1. **peon-mcp** - MCP server exposing tools + prompts via stdio for Claude integration
2. **peon-ui** - FastAPI REST API on port 8420 (API only — UI served separately)
3. **peon-loop** - CLI that repeatedly spawns Claude to work through the task queue

### Backend (`peon_mcp/`)

The backend is organized by **feature domain**. Each feature package contains its own schemas, routes, tools, and API client:

| Package | Domain | Key responsibilities |
|---|---|---|
| `common/` | Shared | Base HTTP client, FastAPI dependencies, generic schemas |
| `projects/` | Projects | CRUD, stats |
| `features/` | Features | CRUD, branch tracking, completion workflow |
| `tasks/` | Tasks | CRUD, next_task, progress tracking, recovery |
| `work_logs/` | Work Logs | CRUD audit trail |
| `memories/` | Memories | Persistent learnings, access-count ranking, LRU eviction |
| `system/` | System | System info, loop heartbeats |
| `discord/` | Discord | Discord bot integration |

Top-level files:
- **`server.py`** — FastMCP server: lifespan, prompts, and tool registration (imports from each feature's `tools.py`)
- **`web.py`** — FastAPI app factory, middleware, and router mounting (imports from each feature's `routes.py`)
- **`db.py`** — SQLite schema, `init_db()`, auto-migration for schema evolution
- **`api_client.py`** — `PeonAPIClient` composed from all feature clients via multiple inheritance
- **`loop.py`** — Agent loop: spawns `claude` CLI, heartbeats, sleeps, repeats

### Frontend (`ui/`)

- React 19 + React Router v7 + Tailwind CSS v4 + Vite 6 + TypeScript 5.7 + Orval (OpenAPI codegen)
- `src/generated/` — Orval-generated React Query hooks and TypeScript models from the OpenAPI spec
- `src/hooks/` — Wrapper hooks adding sound effects, cache invalidation, and polling
- Routes: `/` (projects), `/projects/:id` (dashboard), features, tasks, logs, policies
- Built assets output to `ui/dist/` (gitignored)
- Warcraft-inspired dark theme with green accent colors

## Data Model

```
projects
 +-- features       (high-level planned work areas)
 +-- tasks          (actionable units of work)
 +-- work_logs      (audit trail of completed work)
 +-- peon_memories  (persistent agent learnings)
 +-- tool_policies  (tool permission profiles)
```

### Projects

Represent a repository or codebase.

| Column | Type | Description |
|---|---|---|
| `id` | TEXT PK | Repo name (e.g. `peon-mcp`) |
| `path` | TEXT | Absolute path on disk |
| `github_url` | TEXT | GitHub repository URL |
| `created_at` | TIMESTAMP | Auto-set on insert |

### Features

High-level groupings of related work (optional). Tasks can be linked to a feature.

| Column | Type | Description |
|---|---|---|
| `id` | INTEGER PK | Auto-increment |
| `project_id` | TEXT FK | References `projects.id` |
| `name` | TEXT | Short feature name |
| `description` | TEXT | Scope and goals |
| `status` | TEXT | `planned`, `in_progress`, `done`, `cancelled` |
| `branch` | TEXT | Git branch name for the feature |
| `pr_url` | TEXT | URL of the PR merging this feature |
| `created_at` | TIMESTAMP | Auto-set on insert |
| `updated_at` | TIMESTAMP | Updated on modification |

### Tasks

Actionable units of work with priority, status, and progress tracking.

| Column | Type | Description |
|---|---|---|
| `id` | INTEGER PK | Auto-increment |
| `project_id` | TEXT FK | References `projects.id` |
| `feature_id` | INTEGER FK | References `features.id` (optional) |
| `title` | TEXT | Short summary |
| `description` | TEXT | Detailed description |
| `priority` | TEXT | `low`, `medium`, `high`, `critical` |
| `status` | TEXT | `grooming`, `todo`, `in_progress`, `done`, `cancelled`, `timeout` |
| `progress` | REAL | 0.0 – 1.0 progress indicator |
| `commit_base` | TEXT | Git commit SHA when work began |
| `commit_head` | TEXT | Git commit SHA after work completed |
| `branch` | TEXT | Git branch for this task |
| `worktree_path` | TEXT | Path to git worktree |
| `pr_url` | TEXT | Pull request URL |
| `lines_added` | INTEGER | Lines added (set on completion) |
| `lines_deleted` | INTEGER | Lines deleted (set on completion) |
| `tokens_used` | INTEGER | Tokens consumed (set on completion) |
| `started_at` | TIMESTAMP | Set when status becomes `in_progress` |
| `completed_at` | TIMESTAMP | Set when status becomes `done` |
| `created_at` | TIMESTAMP | Auto-set on insert |
| `updated_at` | TIMESTAMP | Updated on modification |

### Work Logs

Audit trail of work done on tasks.

| Column | Type | Description |
|---|---|---|
| `id` | INTEGER PK | Auto-increment |
| `task_id` | INTEGER FK | References `tasks.id` |
| `project_id` | TEXT FK | References `projects.id` |
| `summary` | TEXT | What was done and the outcome |
| `files_changed` | TEXT | Comma-separated list of files touched |
| `test_result` | TEXT | `pass`, `fail`, `skip`, or empty |
| `created_at` | TIMESTAMP | Auto-set on insert |

### Memories

Persistent project-scoped learnings for agent context injection.

| Column | Type | Description |
|---|---|---|
| `id` | INTEGER PK | Auto-increment |
| `project_id` | TEXT FK | References `projects.id` |
| `category` | TEXT | `env`, `pattern`, `gotcha`, `test`, `dependency`, `general` |
| `content` | TEXT | Memory content (max 500 chars) |
| `source_task_id` | INTEGER FK | Task that produced this learning (optional) |
| `access_count` | INTEGER | Incremented on recall (affects eviction priority) |
| `created_at` | TIMESTAMP | Auto-set on insert |
| `updated_at` | TIMESTAMP | Updated on access |

### Additional Tables

- **`loop_heartbeats`** — Agent loop liveness tracking

## MCP Tools

peon-mcp exposes tools via the MCP protocol:

### Projects (4)

| Tool | Description |
|---|---|
| `create_project` | Register a project (repo name + path) |
| `list_projects` | List all registered projects |
| `get_project` | Get a project by ID |
| `update_project` | Update project path or github_url |

### Features (6)

| Tool | Description |
|---|---|
| `create_feature` | Create a high-level feature for a project |
| `list_features` | List features, optionally filtered by status |
| `get_feature` | Get a feature by ID |
| `update_feature` | Update feature name, description, status, or branch |
| `delete_feature` | Delete a feature |
| `complete_feature` | Mark a feature done (validates all tasks complete, auto-creates PR) |

### Tasks (9)

| Tool | Description |
|---|---|
| `create_task` | Create a task scoped to a project |
| `list_tasks` | List tasks, filterable by priority, status, and feature |
| `get_task` | Get a task by ID |
| `next_task` | Pick up the highest-priority `todo` task and mark it `in_progress` |
| `update_task` | Update task fields (title, status, git commits, etc.) |
| `delete_task` | Delete a task |
| `update_task_progress` | Update progress (0.0–1.0) for a task |
| `bulk_update_task_status` | Batch-update status on multiple tasks |
| `find_recoverable_tasks` | Find tasks that are stuck and may need recovery |

### Work Logs (2)

| Tool | Description |
|---|---|
| `log_work` | Log the results of work done on a task |
| `list_work_logs` | List work logs, filterable by task or project |

### Memories (4)

| Tool | Description |
|---|---|
| `save_memory` | Save a persistent learning for future agents (6 categories, 500-char cap) |
| `recall_memories` | Recall memories ranked by usefulness (increments access count) |
| `list_memories` | List memories without affecting access counts |
| `forget_memory` | Delete a specific memory |

### Policies (6)

| Tool | Description |
|---|---|
| `create_policy` | Create a tool permission policy (profile + allow/deny) |
| `list_policies` | List policies for a project |
| `get_policy` | Get a policy by ID |
| `update_policy` | Update policy fields |
| `delete_policy` | Delete a policy |
| `resolve_policy` | Expand profile and compute final permitted tool list |

**Key behaviors:**
- `next_task` returns tasks in priority order (critical > high > medium > low), oldest first within the same priority, using optimistic concurrency control
- `complete_feature` auto-creates a PR via `gh` CLI if no `pr_url` is provided
- `save_memory` auto-evicts the least-accessed oldest memory when the 50-per-project cap is reached
## MCP Prompts

peon-mcp provides four prompts for agent orchestration:

### `start_work`

Bootstrap prompt for an agent beginning work on a project. Guides the agent to:

1. Register the project if needed
2. Ensure a clean git working tree (stash any uncommitted changes and log it)
3. Call `next_task` to pick up work
4. Use the `execute_task` prompt for full instructions

**Parameters:** `project_id`, `repo_path`

### `execute_task`

Instructions for executing a single task. Covers:

- Scoping changes to the task
- Reading code before modifying it
- Running tests and keeping the suite green
- Reverting if things break
- Capturing follow-up work as new tasks
- Committing and pushing on success
- Logging all work via `log_work` (including failures)

**Parameters:** `project_id`, `task_title`, `task_description`, `feature_branch`, `policy_context`

### `recover_task`

Instructions for recovering a stuck or incomplete task. Use this when:

- A task is in `in_progress` status for too long (agent crashed, timed out, etc.)
- A task has 100% progress but was never marked as `done`
- A task has status `timeout`
- Work exists in a git worktree/branch but the task didn't complete

The prompt guides the agent to:

1. Assess existing work (check worktree, branch, commits, PR status)
2. Resume or restart from the appropriate point
3. Complete the work following normal task execution rules
4. Finish by creating/updating the PR and marking the task done
5. Log the recovery with details about what was found and recovered

**Parameters:** `project_id`, `task_id`, `task_title`, `task_description`, `repo_path`, `branch`, `worktree_path`, `commit_base`, `progress`

**Finding stuck tasks:** Use the `find_recoverable_tasks` tool to identify tasks that need recovery. It returns tasks that are:
- In `in_progress` status for more than N hours (default: 24)
- Have progress >= 1.0 but not marked done
- Have status `timeout`

Each task includes `recovery_hints` to help diagnose the issue.

## Usage Workflow

### Manual Workflow (with Claude Desktop/Code)

1. Register your project:
   ```
   Call create_project with your repo name and path
   ```

2. Create tasks:
   ```
   Call create_task to add work items
   ```

3. Pick up tasks:
   ```
   Call next_task to claim the highest-priority task
   ```

4. Work on the task following the execute_task prompt guidance

5. Log your work:
   ```
   Call log_work to record what was done
   ```

### Autonomous Workflow (with peon-loop)

1. Create tasks via the web UI or MCP tools

2. Start the loop:
   ```bash
   peon-loop my-project /path/to/repo
   ```

3. The loop will:
   - Check for todo tasks
   - Spawn Claude with the start_work prompt
   - Let Claude work through the task
   - Sleep and repeat

4. Monitor progress in the web UI at http://127.0.0.1:8420

## Development

A `Makefile` at the project root provides shortcuts for all common workflows.
Run `make help` to list all available targets.

### Install Dependencies

```bash
make install        # Install both backend (uv) and frontend (npm) dependencies
make install-backend
make install-ui
```

### Run in Development Mode

```bash
# Terminal 1: Start the backend API
make dev-backend    # Runs on http://127.0.0.1:8420

# Terminal 2: Start Vite dev server with hot reload
make dev-ui         # Runs on http://localhost:5173, proxies /api to backend
```

### Build

```bash
make build          # Build UI then Python wheel (full release build)
make build-ui       # Build React frontend only (outputs to peon_mcp/static/)
make build-backend  # Build Python wheel only
make generate       # Regenerate TypeScript types/hooks from OpenAPI spec
```

### Test

```bash
make test           # Run all tests
make test-unit      # Run unit tests only
make test-integration  # Run integration tests only
make test-cov       # Run with HTML coverage report (outputs to htmlcov/)
```

### Deploy to PyPI

```bash
make deploy         # Build UI + wheel and publish to PyPI
make deploy-testpypi  # Build UI + wheel and publish to TestPyPI
```

### Docker

```bash
make docker-build   # Build Docker images
make docker-up      # Start services (peon-ui + peon-loop) in the background
make docker-down    # Stop services
```

### Maintenance

```bash
make clean          # Remove build artifacts (dist/, ui/dist/, htmlcov/)
```

## Testing the MCP Server

Use the MCP inspector for interactive testing:

```bash
uv run mcp dev peon_mcp/server.py
```

## Common Commands Reference

```bash
# Install all dependencies
make install

# Start development servers (run in separate terminals)
make dev-backend   # Backend API on :8420
make dev-ui        # Vite dev server on :5173

# Build everything for release
make build

# Run tests
make test

# Deploy
make deploy         # PyPI
make deploy-testpypi

# Run MCP server directly (stdio transport)
uv run python -m peon_mcp.server

# Run agent loop (single iteration)
peon-loop PROJECT_ID /path/to/repo --once

# Run agent loop (continuous)
peon-loop PROJECT_ID /path/to/repo --model sonnet --timeout 1800
```

## Project Structure

```
peon-mcp/
├── peon_mcp/
│   ├── server.py           # MCP server: lifespan, prompts, tool registration
│   ├── web.py              # FastAPI app factory, middleware, router mounting
│   ├── db.py               # SQLite schema, init_db(), auto-migration
│   ├── api_client.py       # Aggregated HTTP client (PeonAPIClient)
│   ├── schemas.py          # Re-exports all feature schemas
│   ├── loop.py             # Agent loop CLI
│   ├── github.py           # GitHub CLI helpers
│   ├── common/             # Shared: base client, dependencies, generic schemas
│   ├── projects/           # schemas, routes, tools, client
│   ├── features/           # schemas, routes, tools, client
│   ├── tasks/              # schemas, routes, tools, client
│   ├── work_logs/          # schemas, routes, tools, client
│   ├── memories/           # schemas, routes, tools, client
│   └── system/             # schemas, routes
├── ui/
│   ├── src/
│   │   ├── components/     # React page and shared components
│   │   ├── generated/      # Orval-generated hooks and models
│   │   ├── hooks/          # Wrapper hooks (polling, sounds, cache)
│   │   └── main.tsx        # App entry point
│   ├── package.json
│   └── vite.config.ts
├── tests/
│   ├── unit/               # Unit tests
│   └── integration/        # Integration tests
├── pyproject.toml
├── Makefile
├── deploy.sh
└── README.md
```

## License

MIT

## Contributing

Contributions welcome! Please open an issue or PR on GitHub.

## Support

- GitHub Issues: https://github.com/pyrabbit/peon-mcp/issues
- MCP Documentation: https://modelcontextprotocol.io

---

**Work work!** Ready to serve, master.
