# Paddock

> Self-hosted web UI for Claude Code. Persistent, resumable chats on your own server — fork, rewind, and reach them from any browser.

- **Type:** MCP server
- **Install:** `agentstack add mcp-edspencer-paddock`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [edspencer](https://agentstack.voostack.com/s/edspencer)
- **Installs:** 0
- **Category:** [Web & Browser](https://agentstack.voostack.com/c/web-and-browser)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [edspencer](https://github.com/edspencer)
- **Source:** https://github.com/edspencer/paddock
- **Website:** https://paddock.edspencer.net

## Install

```sh
agentstack add mcp-edspencer-paddock
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

🐎 Paddock

  Your Claude Code chats, hosted and organized by project.
  Persistent, resumable Claude Code sessions with a web UI — from your desk or your phone.

  
  
  
  
  

  Docs •
  Quickstart •
  Management API •
  Configuration •
  How it works •
  herdctl •
  Issues

---

  

## Why Paddock

**Paddock** is a project-first launchpad for [herdctl](https://github.com/edspencer/herdctl).
It turns Claude Code into something you run on a server and reach from a browser:
long-lived chats, grouped by project, that persist and resume — instead of a
laptop full of terminal tabs you can't get back to from your phone.

A **project** is a directory plus a `project.yaml`. Paddock runs **Claude Code**
in that directory, and the project *is* the collection of chats you start there —
each one a Claude Code session, persisted on disk and resumable across reloads,
reconnects, and devices — together with per-project settings and an `OVERVIEW.md`
and `CHANGELOG.md` that a post-turn sweeper keeps current out of band. There are
two kinds:

- **Notebook** — a directory in your data repo for planning, notes, and light work.
- **Repo-backed** — an external git repo cloned as the project's working directory,
  so the repo's own `CLAUDE.md`, branches, and PR flow apply. The natural unit for
  doing real engineering.

The instance **root** is a workspace too, so a chat that belongs to no particular
project just lives there — and can be promoted into a project later, keeping its
history. The whole UI is responsive — the same launchpad works from a phone.

It also runs without you watching. Turns are fired by schedules, lifecycle
events and other chats; background work a chat starts survives the turn that
started it and wakes Claude when it lands. And the boundary now opens the
other way: an instance **exposes itself as an MCP server**, so Claude Code on
your laptop — or CI, or a peer Paddock — can drive it from outside. Less an app
you visit, more a service your other tools talk to.

## Highlights

- 🗂️ **Project-first** — every project is a set of chats, with its own files, settings, and generated changelog
- 💬 **Persistent, resumable chats** — server-hosted sessions survive reloads, reconnects, and devices
- ⌨️ **Token-by-token streaming** — replies, real tool calls, and subagents render live as they run, with rich tool cards (Edit diffs, Bash exit codes, Grep counts)
- 🛰️ **Drive it from outside** — an [external Management API](#drive-it-from-outside) serves the management tools as MCP at `/mcp`, so Claude Code on your laptop, CI, or a peer Paddock can list projects, read chats, and (with the scope for it) start turns
- ⏰ **Triggers & automation** — run a turn on a schedule, on a lifecycle event, or on demand; each trigger can carry its own scoped toolset
- 🤖 **Self-driving** — an opt-in, depth-gated in-process MCP lets Claude list projects, read chats, spawn and fork chats to fan work out across parallel sessions, manage a project's triggers, and — behind a further flag — provision new projects
- ⏳ **Background work outlives the turn** — a build, deploy, or sub-agent Claude backgrounds keeps running after the turn ends and wakes it with the result
- 📎 **Send files & images** — pick, drag-drop, or paste into the composer; Claude reads images and PDFs natively
- 📁 **Files & Changes** — browse rendered project files and review Claude's work as git diffs
- 🧩 **Two project types** — notebook (data-repo subdir) or repo-backed (clone an external repo as cwd)
- 📱 **Works from your phone** — the same launchpad, fully responsive
- 🔀 **Chat ergonomics** — star to pin, mark unread, fork or rewind from any message, queue-while-streaming, stop, search, archive; spawned chats nest under the chat that created them
- 🎛️ **Settings, per project and per instance** — model, permission mode, curation budgets and more per project; an instance-wide **Config** screen edits `paddock.config.yaml` from the UI
- 🧠 **Claude Opus 5 by default** — with Opus 4.8, Fable 5, Sonnet 5 and Haiku 4.5 selectable, and an allow-list if you'd rather offer fewer
- 📈 **Token & cost tracking** — per-chat context meter and estimated API cost, live
- 🎙️ **Voice dictation & slash commands** — mic-to-text in the composer, `/`-autocomplete for skills
- 🔌 **Built on herdctl** — anything the fleet engine can do, Paddock can wire in

## Quickstart

**One command, from anywhere:**

```bash
npx @edspencer/paddock -o
```

That starts the server on **http://127.0.0.1:7233** and opens a browser at it. Data
lives in `~/.paddock`; the directory you run it from makes no difference.

**A new instance opens on Discover.** It reads your Claude Code history, works out
which directories on this machine you have actually been using `claude` in, and
offers them as projects — with conversation counts, last-used dates and git remotes,
so you can tell them apart. Tick the ones you want, press Import, and instead of an
empty instance you are looking at your own work, resumable. It stays in the sidebar
afterwards.

**Nothing is written into your directories.** No `.paddock/`, no `.chats/`, no
`.gitignore` edit, no `CLAUDE.md` — the project record and the copied transcripts
both live in `~/.paddock`, and the project just points at the path. Your `~/.claude`
transcripts are *copied*, never moved or deleted, so your terminal `claude` keeps
working exactly as before.

Needs **Node 22+**. First run downloads ~250 MB — Paddock drives Claude Code, and
the Agent SDK ships a per-platform binary of that size; later runs reuse the npm
cache. For repeated use, `npm i -g @edspencer/paddock` beats bare `npx`.

There are two subcommands, and both are optional detail: bare `paddock` starts the
server, `paddock start` is the same thing said out loud, and
[`paddock service`](https://paddock.edspencer.net/guides/running-as-a-service/)
registers it as a background service that comes back at login. Anything else in
leading position is an error.

The full flag set — they parse the same after a subcommand as without one, and
`--help` is the canonical short-form reference:

| Flag | Purpose |
|---|---|
| `-p`, `--port ` | Listen port. Overrides `PORT` and `port:`. Use this when 7233 is taken. |
| `--host ` | Bind address. Overrides `HOST`. |
| `-d`, `--data-dir ` | Data root. Overrides `PADDOCK_DATA_DIR`. The only flag that picks *which instance* you get. |
| `-o`, `--open` | Open a browser once the server is listening. |
| `--verbose` | Restore normal logging; the CLI is quiet by default. |
| `-h`, `--help` / `-v`, `--version` | Usage / version. |

**Port 7233 already in use?** `npx @edspencer/paddock --port 7234`. (7233 is also
Temporal's default frontend port, which is the usual collision. The failure is
loud — Paddock names the port and the flag and exits.)

### Always-on: your own laptop

`npx` is a terminal tab. To keep the same `~/.paddock` instance running in the
background instead:

```bash
npm i -g @edspencer/paddock
paddock service install     # uninstall | status
```

A launchd **LaunchAgent** on macOS, a **`systemd --user`** unit on Linux. It starts
**at login, not at boot** — a per-user agent is what lets it read the Claude login
you already have, and on macOS a boot-time daemon structurally cannot (the login
Keychain is unlocked by your password *at login*). On Linux you also want
`loginctl enable-linger $USER`, or logging out stops it. Full detail, including
what the generated unit contains and what is still unverified:
[Keeping Paddock running on your laptop](https://paddock.edspencer.net/guides/running-as-a-service/).

### Always-on: Docker

For a server rather than a laptop, run the published image, point it at a data
volume, and give it a Claude token:

```bash
docker run -d --name paddock -p 127.0.0.1:7233:7233 \
  -e CLAUDE_CODE_OAUTH_TOKEN=…       `# Claude Max/Pro plan (OAuth)` \
  -e PADDOCK_DATA_DIR=/data \
  -e PADDOCK_DANGEROUSLY_ALLOW_OPEN=1 `# see below — required inside a container` \
  -v paddock-data:/data \
  ghcr.io/edspencer/paddock:latest
```

`PADDOCK_DANGEROUSLY_ALLOW_OPEN=1` is **required** here and the container will
refuse to start without it. The image binds `0.0.0.0` (it has to, to be reachable
from outside the container) and Paddock's default auth mode is `none`, and
Paddock will not bind a routable interface unauthenticated without being told to.
It is safe in *this* command because `-p 127.0.0.1:7233:7233` publishes the port
on loopback only — the container namespace is the boundary. Drop the `127.0.0.1:`
and you have handed an unauthenticated, code-executing Paddock to your whole
network, so put an auth mode or a reverse proxy in front of it first.

Then open **http://127.0.0.1:7233** and click **New Project**.

Two images ship from the same source: **`:latest`** is the lean base (app plus
`git`, `gh`, and the `claude` CLI), and **`:devbox`** layers on a coding-agent
toolbox — PM2-backed preview servers, `ffmpeg`, a headless Playwright browser,
the Docker CLI, `kubectl`, `python3`/`uv`, `jq`, `rsync`. Same app and same
`/data` layout, so you can swap tags against
one volume. Ready-made self-hosting recipes (Compose, Kubernetes, Proxmox,
a Basic Auth sidecar) live in
[**`paddock-deploy`**](https://github.com/edspencer/paddock-deploy).

docker-compose

```yaml
services:
  paddock:
    image: ghcr.io/edspencer/paddock:latest   # or :devbox for the coding-agent toolbox
    ports:
      - "127.0.0.1:7233:7233"
    environment:
      CLAUDE_CODE_OAUTH_TOKEN: ${CLAUDE_CODE_OAUTH_TOKEN} # Claude Max/Pro (OAuth); or ANTHROPIC_API_KEY for API-key billing
      PADDOCK_DATA_DIR: /data
      PADDOCK_DANGEROUSLY_ALLOW_OPEN: "1"                 # required in a container; safe because the port is published on loopback
    volumes:
      - paddock-data:/data
volumes:
  paddock-data:
```

> **The web UI has no login of its own** — run it behind a reverse proxy / auth
> layer you trust (see [AUTH.md](AUTH.md)). Paddock reads credentials from the
> environment and from files the host provides; it never stores secrets itself.
> It also fails closed: an `npx`, source or tarball run binds loopback by default,
> and refuses to start on a routable interface with `PADDOCK_AUTH_MODE=none` unless
> you explicitly set `PADDOCK_DANGEROUSLY_ALLOW_OPEN`. None of that is a concern for
> a local `npx` run, which is reachable only from your own machine. (The
> [Management API](#drive-it-from-outside) is the one surface that authenticates
> itself rather than delegating to your proxy.)

## A tour

_These are real screenshots — Paddock is dogfooded on its own dev stack: **Paddock**, **herdctl** (the engine underneath it), and **Warren** (an agentic PR reviewer) all live here as projects that build one another._

**Every project is a set of chats, organized on one page.**

**Each project keeps dozens of persistent, resumable chats — searchable, forkable, archivable.**

**Chat with Claude — real tool calls and subagents stream in, with a live context + cost meter.**

**Built for real, long-running work** — persistent sessions track their own context window and estimated cost as they grow (this one's 42% of a 1M-token window):

Repo-backed projectsClone an external repo as the project's working directory — its own CLAUDE.md, branches, and PR flow apply.
Rendered project filesMarkdown, Mermaid, code, images, PDF and video render inline; pin files as tabs.

Slash-command autocompleteType / to discover and run the project's skills.
Per-project settingsIdentity, model, permission mode, links, curation budgets, and drive mode — deep-linkable.

…and it all works from your phone.

## Drive it from outside

Paddock isn't only something you open in a browser. An instance can expose its
**Management API** as an MCP server over streamable HTTP at **`/mcp`** — so a
Claude Code session on your laptop, a CI job, or a peer Paddock can list
projects, read chats, and, if you grant it, start turns. External callers get
the *same* toolset Claude receives in-process, so the two surfaces can't
drift.

Clients are declared in `paddock.config.yaml`, and their tokens are
**referenced, never inlined** — a literal secret in the config file is a hard
error:

```yaml
managementApi:
  instanceId: my-paddock
  publicUrl: https://paddock.example.com
  clients:
    my-laptop:
      auth:
        ref: env:PADDOCK_MCP_TOKEN_MY_LAPTOP   # value lives in the environment
      # no scope ⇒ read-only, across all projects
```

```sh
claude mcp add --transport http --scope user paddock \
  https://paddock.example.com/mcp \
  --header "Authorization: Bearer $PADDOCK_MCP_TOKEN_MY_LAPTOP"
```

A few things worth knowing before you widen that scope:

- **Read-only is the default, and it should usually stay that way.** A client
  configured without an explicit scope gets `list_projects`, `list_chats`,
  `list_triggers` and `read_chat` — nothing else. This is not timidity: any
  write scope can start a turn, and Claude has `Bash`. **Granting write
  access to the Management API is equivalent to granting remote code execution
  on the host.** Treat such a token like an SSH key, scope it to specific
  projects, and expect the boot log to warn you by name when a client holds one.
- **Static bearer tokens are the only credential that works today.** OAuth is
  not implemented — `auth.type` accepts only `"token"`, and anything else is a
  config error. Paddock will publish RFC 9728 protected-resource metadata once an
  authorization server is configured, but there is no OAuth path to configure one
  against yet, so mint a token and use it.
- **It authenticates itself.** `/mcp` is credential-gated independently of
  `PADDOCK_AUTH_MODE` and of any reverse proxy, so it stays closed even on an
  instance running `auth.mode: none`, and a bad token gets a `401` rather than a
  login redirect no MCP client could follow.
- **It fails closed.** The endpoint `404`s entirely until you've configured both
  clients *and* a `publicUrl`; if every client's token resolves to nothing, it
  goes back to `404`ing rather than opening up.

Full setup, the scope grammar, and the per-tool reference:
[Management API](https://paddock.edspencer.net/reference/mcp/) and the
[self-management MCP](https://paddock.edspencer.net/reference/self-mcp/).

## Configuration

An instance is configured by a single **`paddock.config.yaml`**, with per-project
overrides in each project's `project.yaml`. The file lives at
**`/paddock.config.yaml`** (or wherever `PADDOCK_CONFIG` points),
and it is entirely optional — every key has a built-in default, so an instance
with no file at all is a working instance.

```yaml
# /paddock.config.yaml — every key below is optional.
schemaVersion: 1              # the version of THIS format the file is written in

# --- Core ---
port: 7233
host: 127.0.0.1               # loopback by default; a routable bind needs the opt-in below
logLevel: info                # trace | debug | info | warn | error | fatal | silent

# --- How turns run ---
driveMode: session            # session = SDK runtime (streaming + cross-turn autonomy)
                              # batch   = legacy one-shot CLI runtime
nativeSystemPrompt: true      # use Claude Code's native prompt + CLAUDE.md hierarchy
models:                       # which catalog models the picker offers; omit to offer all
  - claude-opus-5
  - claude-sonnet-5

# --- Capabilities & safety gates (every switch defaults OFF; maxSpawnDepth to 1) ---
selfMcpEnabled: true          # let Claude list/read projects and other chats
selfMcpWriteEnabled: true     # + create, fork, message and archive chats
selfMcpProjectsEnabled: false # + provision whole new projects
maxSpawnDepth: 1              # how deep a spawn tree may grow before children lose the self-MCP
hooksMcpEnabled: false        # let chats declare and edit their own triggers
browserMcp: false             # give chats a headless Chromium browser MCP

# --- The post-turn sweeper that curates OVERVIEW.md and CHANGELOG.md ---
sweepMinIntervalMs: 3

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [edspencer](https://github.com/edspencer)
- **Source:** [edspencer/paddock](https://github.com/edspencer/paddock)
- **License:** MIT
- **Homepage:** https://paddock.edspencer.net

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-edspencer-paddock
- Seller: https://agentstack.voostack.com/s/edspencer
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
