Skip to main content
Glama
README.md
# Beads Viz

[![npm version](https://img.shields.io/npm/v/beads-viz)](https://www.npmjs.com/package/beads-viz)
[![license](https://img.shields.io/npm/l/beads-viz)](https://github.com/pyros-projects/beads-viz/blob/main/LICENSE)

Read-only task graph visualizer for [Beads](https://github.com/pyros-projects/beads) projects. Runs as an MCP App inside Claude Desktop or VS Code Copilot, showing an interactive DAG, list view, and stats dashboard.

```
Claude Desktop / VS Code Copilot
  ├── Chat: "Show me the task graph"
  │     → calls visualize-tasks tool
  ├── MCP Server (Node.js, stdio)
  │     ├── visualize-tasks → bd list --all --json
  │     ├── poll-tasks      → fresh data every 3s
  │     ├── show-task        → task detail by ID
  │     └── ui://beads-viz  → serves the UI bundle
  └── Sandboxed iframe (MCP App)
        ├── DAG view (ELK.js layered layout)
        ├── List view (status-grouped)
        ├── Stats dashboard (progress, velocity)
        └── Task detail drawer
```

The UI is **read-only** — all task mutations (create, claim, close) happen through agent chat.

## Prerequisites

- **Node.js** >= 18 (22+ recommended)
- **npm** >= 9
- **Beads CLI** (`bd`) installed and on PATH — [install instructions](https://github.com/pyros-projects/beads)
- A Beads project (directory containing `.beads/config.yaml`)

Verify your setup:

```bash
node --version    # v18.0.0+
bd --version      # any version
bd list --json    # should output JSON (run from a Beads project)
```

## Quick Start (npx)

The fastest way — no cloning, no building. Just configure your MCP host to use `npx`:

```json
{
  "mcpServers": {
    "beads-viz": {
      "command": "npx",
      "args": ["-y", "beads-viz"],
      "cwd": "/path/to/your/beads-project"
    }
  }
}
```

`npx` downloads and caches the package on first run. The `-y` flag skips the install confirmation prompt.

> **Where does this go?** See the [Installation](#installation) section below for the config file location for your setup (Claude Desktop, VS Code Copilot, WSL, etc.)

## Global Install

If you prefer a permanent install over `npx`:

```bash
npm install -g beads-viz
```

Then use `beads-viz` as the command directly:

```json
{
  "mcpServers": {
    "beads-viz": {
      "command": "beads-viz",
      "cwd": "/path/to/your/beads-project"
    }
  }
}
```

## Building from Source

If you prefer to build locally:

```bash
git clone https://github.com/pyros-projects/beads-viz.git
cd beads-viz
npm install
npm run build
```

This produces:

| Output | Description |
|--------|-------------|
| `dist/index.html` | Self-contained UI bundle (Svelte + ELK.js, single file) |
| `dist/server/index.js` | MCP server entry point (Node.js, stdio transport) |

Build commands:

| Command | What it does |
|---------|-------------|
| `npm run build` | Build everything (UI + server) |
| `npm run build:ui` | Vite build — produces `dist/index.html` |
| `npm run build:server` | TypeScript compile — produces `dist/server/*.js` |
| `npm run dev` | Vite dev server at localhost:5173 (standalone UI testing) |

## Installation

The MCP server runs via **stdio** — the host application (Claude Desktop or VS Code Copilot) starts it as a child process. Configuration depends on where the server runs relative to the host.

> **Key concept**: The `cwd` field determines which Beads project to visualize. The server walks up from `cwd` to find `.beads/config.yaml`.

All examples below show both the **npx** approach (recommended) and the **local build** approach. Use whichever you prefer.

---

### Claude Desktop — macOS / Linux (Native)

The simplest setup. Both Claude Desktop and the server run on the same machine.

Edit `~/.config/claude-desktop/config.json` (Linux) or `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):

**Using npx (recommended)**:

```json
{
  "mcpServers": {
    "beads-viz": {
      "command": "npx",
      "args": ["-y", "beads-viz"],
      "cwd": "/home/dev/projects/myapp"
    }
  }
}
```

**Using a local build**:

```json
{
  "mcpServers": {
    "beads-viz": {
      "command": "node",
      "args": ["/home/dev/tools/beads-viz/dist/server/index.js"],
      "cwd": "/home/dev/projects/myapp"
    }
  }
}
```

If you use **nvm** and `node`/`npx` isn't on PATH for GUI apps, use full paths:

```json
{
  "mcpServers": {
    "beads-viz": {
      "command": "/home/dev/.nvm/versions/node/v22.21.0/bin/npx",
      "args": ["-y", "beads-viz"],
      "cwd": "/home/dev/projects/myapp"
    }
  }
}
```

---

### Claude Desktop — Windows (Native Node.js)

Node.js and the Beads CLI are installed natively on Windows.

Edit `%APPDATA%\Claude\claude_desktop_config.json`:

**Using npx (recommended)**:

```json
{
  "mcpServers": {
    "beads-viz": {
      "command": "npx",
      "args": ["-y", "beads-viz"],
      "cwd": "C:\\Users\\you\\projects\\myapp"
    }
  }
}
```

**Using a local build**:

```json
{
  "mcpServers": {
    "beads-viz": {
      "command": "node",
      "args": ["C:\\Users\\you\\tools\\beads-viz\\dist\\server\\index.js"],
      "cwd": "C:\\Users\\you\\projects\\myapp"
    }
  }
}
```

> Use double backslashes (`\\`) in JSON paths, or forward slashes (`/`) — Node.js accepts both on Windows.

---

### Claude Desktop — Windows Host + WSL Server

**This is the recommended setup for WSL users.** Claude Desktop runs on Windows, but your Node.js, Beads CLI, and projects live inside WSL.

The trick: use `wsl.exe` as the command, which bridges into WSL and runs the server there.

Edit `%APPDATA%\Claude\claude_desktop_config.json`:

**Using npx (recommended)**:

```json
{
  "mcpServers": {
    "beads-viz": {
      "command": "wsl.exe",
      "args": [
        "bash", "-lc",
        "cd /home/dev/projects/myapp && npx -y beads-viz"
      ]
    }
  }
}
```

**Using a local build**:

```json
{
  "mcpServers": {
    "beads-viz": {
      "command": "wsl.exe",
      "args": [
        "bash", "-lc",
        "cd /home/dev/projects/myapp && node /home/dev/tools/beads-viz/dist/server/index.js"
      ]
    }
  }
}
```

**Why `bash -lc`?** The `-l` flag loads your login shell profile (`~/.bashrc`, `~/.profile`), which sets up nvm, PATH, and other environment variables. Without it, `node` and `bd` may not be found.

**Why `cd ... &&`?** The `cwd` field in config.json is a Windows path and won't work inside WSL. Instead, we `cd` to the project directory inside the bash command.

**If you have a specific WSL distro** (not the default):

```json
{
  "mcpServers": {
    "beads-viz": {
      "command": "wsl.exe",
      "args": [
        "-d", "Ubuntu-24.04",
        "bash", "-lc",
        "cd /home/dev/projects/myapp && npx -y beads-viz"
      ]
    }
  }
}
```

**Troubleshooting WSL**:

- If `node` isn't found, check that nvm loads in `~/.bashrc` (not just `~/.bash_profile`)
- If `bd` isn't found, verify it's on your WSL PATH: `wsl.exe bash -lc "which bd"`
- Test the full command from PowerShell first:
  ```powershell
  wsl.exe bash -lc "cd /home/dev/projects/myapp && node /home/dev/tools/beads-viz/dist/server/index.js"
  ```
  You should see the server start (it reads from stdin, so it will hang — that's normal). Press `Ctrl+C` to stop.

---

### VS Code Copilot — macOS / Linux (Native)

VS Code with GitHub Copilot Chat can also host MCP Apps, displaying them as interactive panels alongside the chat.

Add to your VS Code `settings.json` (`Ctrl+,` → search "mcp" → Edit in settings.json):

**Using npx (recommended)**:

```json
{
  "github.copilot.chat.mcp.servers": {
    "beads-viz": {
      "command": "npx",
      "args": ["-y", "beads-viz"],
      "cwd": "/absolute/path/to/your/beads-project"
    }
  }
}
```

**Using a local build**:

```json
{
  "github.copilot.chat.mcp.servers": {
    "beads-viz": {
      "command": "node",
      "args": ["/absolute/path/to/beads-viz/dist/server/index.js"],
      "cwd": "/absolute/path/to/your/beads-project"
    }
  }
}
```

You can also add this to **workspace settings** (`.vscode/settings.json`) to scope it per project:

```json
{
  "github.copilot.chat.mcp.servers": {
    "beads-viz": {
      "command": "npx",
      "args": ["-y", "beads-viz"],
      "cwd": "${workspaceFolder}"
    }
  }
}
```

---

### VS Code Copilot — Windows Host + WSL Server

Your VS Code runs on Windows, but the project and toolchain live in WSL. Same `wsl.exe` bridge technique.

Add to your VS Code `settings.json`:

**Using npx (recommended)**:

```json
{
  "github.copilot.chat.mcp.servers": {
    "beads-viz": {
      "command": "wsl.exe",
      "args": [
        "bash", "-lc",
        "cd /home/dev/projects/myapp && npx -y beads-viz"
      ]
    }
  }
}
```

**Using a local build**:

```json
{
  "github.copilot.chat.mcp.servers": {
    "beads-viz": {
      "command": "wsl.exe",
      "args": [
        "bash", "-lc",
        "cd /home/dev/projects/myapp && node /home/dev/tools/beads-viz/dist/server/index.js"
      ]
    }
  }
}
```

> **Workspace settings in WSL projects**: If you open a WSL folder in VS Code (via `code .` from WSL terminal or the Remote-WSL extension), VS Code may resolve paths differently. See the Remote-WSL section below.

---

### VS Code with Remote-WSL Extension

When using the **Remote - WSL** extension (or the newer **WSL** extension), VS Code runs its extension host _inside_ WSL. This means MCP servers configured in workspace settings run natively in WSL — no `wsl.exe` bridge needed.

In your WSL project's `.vscode/settings.json`:

**Using npx (recommended)**:

```json
{
  "github.copilot.chat.mcp.servers": {
    "beads-viz": {
      "command": "npx",
      "args": ["-y", "beads-viz"],
      "cwd": "${workspaceFolder}"
    }
  }
}
```

**Using a local build**:

```json
{
  "github.copilot.chat.mcp.servers": {
    "beads-viz": {
      "command": "node",
      "args": ["/home/dev/tools/beads-viz/dist/server/index.js"],
      "cwd": "${workspaceFolder}"
    }
  }
}
```

This is the cleanest approach for WSL users who already use the Remote-WSL workflow. No bridge, no path translation — everything runs natively inside WSL.

---

### Configuration Reference

| Field | Type | Description |
|-------|------|-------------|
| `command` | string | Executable to run (`npx`, `beads-viz`, `node`, or `wsl.exe`) |
| `args` | string[] | Arguments passed to the command |
| `cwd` | string | Working directory — the server discovers the Beads project from here |
| `env` | object | Optional environment variables to set |

The server discovers the Beads project by walking up from `cwd` to find `.beads/config.yaml`. If no project is found, the `visualize-tasks` tool returns an error message.

---

## Usage

Once configured, restart your host application (Claude Desktop or VS Code) and ask the agent:

> "Show me the task graph"

The agent calls the `visualize-tasks` tool, which returns a task summary and opens the interactive visualization in a sandboxed iframe.

### Views

| View | Description |
|------|-------------|
| **DAG** | Dependency graph with ELK.js layered layout. Nodes colored by phase, edges show dependencies. Click a node to see details. |
| **List** | Status-grouped task list: Ready (unblocked), In Progress, Blocked, Done. Click a row for details. |
| **Stats** | Progress ring, status breakdown, phase completion bars, 7-day velocity chart. |

### Keyboard

| Key | Action |
|-----|--------|
| `Escape` | Close the task detail drawer |
| `1` | Switch to DAG view |
| `2` | Switch to List view |
| `3` | Switch to Stats view |

### MCP Tools

| Tool | Visibility | Description |
|------|-----------|-------------|
| `visualize-tasks` | Agent (model) | Opens the visualization. Returns task summary + UI reference. |
| `poll-tasks` | App only | Returns current task data. Called by the UI every 3 seconds. |
| `show-task` | App only | Returns detailed info for a single task (description, comments, deps). |

"App only" tools are called by the UI iframe via the MCP Apps SDK, not by the agent.

## Development

### Standalone UI Development

```bash
npm run dev
```

Opens the Vite dev server at `http://localhost:5173`. The UI runs in **standalone mode** — no MCP host, no data. The bridge logs a warning and the UI shows an empty state. Useful for styling and layout work.

### Testing with a Beads Project

Build the server, then run it manually:

```bash
npm run build
cd /path/to/your/beads-project
node /path/to/beads-viz/dist/server/index.js
```

The server communicates via stdio (JSON-RPC over stdin/stdout). To test tool calls, you'd need an MCP client or the [MCP Inspector](https://github.com/modelcontextprotocol/inspector).

### Project Structure

```
src/
├── server/                 # MCP server (Node.js, compiled with tsc)
│   ├── index.ts            # Entry: McpServer + StdioServerTransport
│   ├── tools.ts            # Tool registrations (visualize, poll, show)
│   ├── beads-client.ts     # bd CLI wrapper (execFile + JSON parse)
│   └── types.ts            # Server-side TypeScript types
└── ui/                     # Svelte app (browser, bundled with Vite)
    ├── App.svelte           # Root: view switching, layout, keyboard
    ├── main.ts              # Svelte mount + MCP bridge init
    ├── app.css              # CSS variables, theme, animations
    ├── index.html           # Vite entry point
    ├── components/
    │   ├── TopStrip.svelte  # 32px strip: project, stats, tabs
    │   ├── DagView.svelte   # ELK.js DAG canvas + SVG edges
    │   ├── DagNode.svelte   # 156x42 compact node cards
    │   ├── ListView.svelte  # Status-grouped task list
    │   ├── StatsView.svelte # Progress ring, phase bars, velocity
    │   └── TaskDrawer.svelte # Bottom drawer with task details
    └── lib/
        ├── elk-layout.ts    # ELK.js layout computation
        ├── phase.ts         # 9-phase color system (dark + light)
        ├── stores.ts        # Svelte writable stores
        ├── mcp-bridge.ts    # MCP Apps SDK bridge
        └── types.ts         # UI-side TypeScript types
```

### Tech Stack

| Component | Technology |
|-----------|-----------|
| MCP server | TypeScript, `@modelcontextprotocol/sdk`, stdio transport |
| MCP App bridge | `@modelcontextprotocol/ext-apps` (host theme, tool calls) |
| UI framework | Svelte 5 (runes + stores) |
| DAG layout | ELK.js (layered algorithm) |
| Build | Vite + `vite-plugin-singlefile` (single HTML output) |
| Bundled UI | ~557 KB gzipped (ELK.js is ~180 KB of that) |

## How It Works

1. The host starts the MCP server as a child process with stdio transport
2. User asks the agent to show the task graph
3. Agent calls `visualize-tasks` — server runs `bd list --all --json`
4. Server returns a markdown summary + task data + `_meta.ui.resourceUri: "ui://beads-viz"`
5. Host opens `ui://beads-viz` in a sandboxed iframe, passing the tool result to the UI
6. The UI receives initial task data via `ontoolresult` callback
7. The UI polls `poll-tasks` every 3 seconds via `app.callServerTool()` for live updates
8. Host theme changes propagate to the UI via `onhostcontextchanged`

### Phase Color System

Nodes are colored by their DAG layer using a 9-phase palette (ported from Hangar IDE):

| Phase | Color | Hex |
|-------|-------|-----|
| P1 | Cyan | `#38bdf8` |
| P2 | Indigo | `#818cf8` |
| P3 | Purple | `#c084fc` |
| P4 | Pink | `#f472b6` |
| P5 | Orange | `#fb923c` |
| P6 | Yellow | `#facc15` |
| P7 | Emerald | `#34d399` |
| P8 | Red | `#f87171` |
| P9 | Slate | `#94a3b8` |

Layer-to-phase mapping: `phase = (layer % 9) + 1`. Deep graphs wrap around.

## Troubleshooting

### "No Beads project found"

The server walks up from `cwd` looking for `.beads/config.yaml`. Make sure:
- Your `cwd` points to a directory inside a Beads project
- The `.beads/config.yaml` file exists
- For WSL setups, use Linux paths (not Windows paths) inside the bash command

### "Beads CLI (bd) not found"

The server calls `bd` via `execFile`. Ensure:
- `bd` is installed and on PATH
- For WSL + Windows setups, `bd` must be on the WSL PATH (not Windows PATH)
- Test: `which bd` (or `wsl.exe bash -lc "which bd"` from Windows)

### "Beads CLI timed out"

The CLI has a 10-second timeout. This can happen with very large projects. Check:
- `bd list --all --json` runs successfully from the command line
- The Beads database isn't locked by another process

### UI shows empty state

- Check that the server is running (look for errors in the host's MCP logs)
- Verify `npm run build` completed without errors
- Check that `dist/index.html` exists (the UI bundle)
- In standalone dev mode (`npm run dev`), empty state is expected — no MCP host

### WSL: "node not found" or "bd not found"

Your login shell profile isn't loading. Ensure:
- nvm initialization is in `~/.bashrc` (not just `~/.bash_profile` or `~/.zshrc`)
- The `bash -lc` flag is present in the args (the `-l` loads the profile)
- Test from PowerShell: `wsl.exe bash -lc "which node && which bd"`

### Theme not matching host

The UI adapts to the host's theme via `onhostcontextchanged`. If colors look wrong:
- Dark theme is the default fallback
- Ensure your host supports MCP App theme propagation
- Check browser DevTools for `[data-theme]` attribute on `<html>`

## License

MIT

TDQS

A3.7/5.0

Scored across 3 tools

Disambiguation4/5

visualize-tasks is clearly the entry point for opening the DAG, while poll-tasks returns current task data and show-task targets one task's details. Poll-tasks and show-task could be confused at a glance, but the singular-vs-plural and detail-vs-refresh descriptions separate them.

Naming Consistency4/5

All tool names follow a verb-noun pattern with hyphen separators (visualize-tasks, poll-tasks, show-task). The only inconsistency is plural 'tasks' in two names versus singular 'task' in show-task.

Tool Count4/5

Three tools is a reasonable, focused set for a visualization server: one user-facing visualization tool plus two support tools for data refresh and detail lookup. It is not bloated, though two app-only tools reduce the agent-facing surface.

Completeness3/5

The set covers the main visualization workflow (open DAG, refresh data, view task detail), but two tools are marked app-only and there is no explicit task-list/search tool or way to act on tasks beyond viewing, which may leave agents without a clear path from visualization to action.

Maintenance

ActivityInactive
ResponsivenessNo issues