beads-viz
# Beads Viz
[](https://www.npmjs.com/package/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
Scored across 3 tools
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.
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.
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.
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.