refactor: finish the AgentHub rename and drop the MCP server

Two leftovers from the rebrand, done together because they touch the same
docs and manifests.

**Data directory.** `agent-dashboard` could not simply be renamed in the
source: it names a real directory holding the live SQLite database. Startup
now moves it once, via `migrateLegacyDataDir()` in `server/lib/claude-home.js`
— a same-filesystem rename, so it is instant even at multiple gigabytes and
cannot leave two divergent copies. `getDataDir()` stays PURE and resolves to
the legacy path until the move happens; the first version of this put the
rename inside the getter, which meant `npm run test:server` relocated the
developer's real database as a side effect. The call sits behind
`require.main === module` and ahead of the router requires, because those pull
in db.js, which opens SQLite at module load. A failed rename is silent and the
old directory keeps being used: an unfashionable directory name beats a blank
database beside an unreachable one. Same treatment for the port-discovery file
(`.agent-dashboard.json` → `.agenthub.json`, old one dropped on write).

Not renamed on purpose: the Sagi localStorage keys (renaming silently resets
everyone's prefs) and the PWA manifest `id` (changing it orphans an installed
app).

**MCP server.** 4 tool calls in 46 days across 577,132 events, against 4,556
lines of TypeScript, a 2 MB committed build artifact, and a pre-commit guard
that had already blocked commits twice. Deleted: `mcp/`, the maintainer skills
and `mcp-reviewer` agent, the build check, `plugins/agenthub/.mcp.json`,
`docs/MCP.md`, 12 npm scripts, 5 Makefile targets, and every doc section
describing it.

Kept, because they are unrelated things sharing the name: the Config
Explorer's MCP tab and its API, per-lane Playwright MCP, the agenthub-config
plugin's MCP audit, and the historical `mcp__plugin_agenthub_*` events that
analytics must keep attributing.

Verified: server 1432/1432, client 459/459, tsc clean, prettier clean. Both
directions of the migration proven — a sandbox-HOME test run leaves the legacy
directory untouched, and a real boot moves it with its contents intact.
This commit is contained in:
2026-09-08 09:48:40 +07:00
parent 11dcc04ac4
commit c7dbaf2d9f
210 changed files with 405 additions and 59408 deletions
+2 -2
View File
@@ -40,7 +40,7 @@ agenthub <command> [options]
flowchart LR
U["Terminal\nagenthub <command>"] --> CLI["bin/agenthub.js\n(zero dependencies)"]
CLI -->|"env override"| ENV["CLAUDE_DASHBOARD_PORT /\nDASHBOARD_PORT"]
CLI -->|"else discovery"| REG["~/.claude/.agent-dashboard.json\n(PID-liveness-checked)"]
CLI -->|"else discovery"| REG["~/.claude/.agenthub.json\n(PID-liveness-checked)"]
CLI -->|"else fallback"| DEF["http://127.0.0.1:4820"]
ENV --> API["Dashboard REST API"]
REG --> API
@@ -76,7 +76,7 @@ The CLI finds your running dashboard the same way the Claude Code hook handler d
| Priority | Source | Notes |
| -------- | ------ | ----- |
| 1 | `CLAUDE_DASHBOARD_PORT` / `DASHBOARD_PORT` env vars | Explicit override wins |
| 2 | `~/.claude/.agent-dashboard.json` | Written by every running dashboard (`{port, pid, startedAt}` entries); stale entries are skipped via a PID liveness check |
| 2 | `~/.claude/.agenthub.json` | Written by every running dashboard (`{port, pid, startedAt}` entries); stale entries are skipped via a PID liveness check |
| 3 | `http://127.0.0.1:4820` | Default port fallback |
If no server answers, every API-backed command exits `1` with the `○ Dashboard server is NOT running` indicator and the ways to start one (see [Server Lifecycle](#server-lifecycle)).
+5 -4
View File
@@ -64,9 +64,10 @@ graph TB
```
**Database Location:**
- **Canonical (default):** `~/.claude/agent-dashboard/dashboard.db` — shared by `npm start`, `npm run dev`, Docker (bind mount), and the desktop app when it uses the same data dir
- **Canonical (default):** `~/.claude/agenthub/dashboard.db` — shared by `npm start`, `npm run dev`, Docker (bind mount), and the desktop app when it uses the same data dir
- **Override:** set `DASHBOARD_DATA_DIR` (directory) or `DASHBOARD_DB_PATH` (file path) for tests or custom deployments
- **Legacy:** repo-local `./data/dashboard.db` is migrated into the canonical location on first launch (see `server/db.js`)
- **Pre-rebrand:** an install that predates AgentHub keeps its data in `~/.claude/agent-dashboard/`. Server startup renames that directory to `~/.claude/agenthub/` once, before anything opens the database (`migrateLegacyDataDir()` in `server/lib/claude-home.js`). It is a same-filesystem rename, so it is instant even at multiple gigabytes and never leaves two copies. If the rename fails, or `DASHBOARD_DATA_DIR` is set, nothing moves and the old directory keeps being used.
---
@@ -813,13 +814,13 @@ VACUUM INTO '/backups/dashboard_20240318.db';
```bash
#!/bin/bash
# Stop application
systemctl stop agent-dashboard
systemctl stop agenthub
# Copy database file
cp /var/lib/agent-dashboard/dashboard.db /backups/dashboard_$(date +%Y%m%d).db
cp /var/lib/agenthub/dashboard.db /backups/dashboard_$(date +%Y%m%d).db
# Start application
systemctl start agent-dashboard
systemctl start agenthub
```
### Backup Schedule
+43 -44
View File
@@ -149,8 +149,8 @@ graph TB
```bash
# Clone repository
git clone https://github.com/your-org/agent-dashboard.git
cd agent-dashboard
git clone https://github.com/your-org/agenthub.git
cd agenthub
# Install dependencies
npm run setup
@@ -190,7 +190,6 @@ npm run dev:server
npm run dev:client
# Terminal 3: MCP server (optional)
npm run mcp:dev
```
---
@@ -225,10 +224,10 @@ npm run build
# 3. Set environment variables
export NODE_ENV=production
export PORT=4820
export DASHBOARD_DB_PATH=/var/lib/agent-dashboard/dashboard.db
export DASHBOARD_DB_PATH=/var/lib/agenthub/dashboard.db
# 4. Create data directory
mkdir -p /var/lib/agent-dashboard
mkdir -p /var/lib/agenthub
# 5. Start server
node server/index.js
@@ -250,7 +249,7 @@ DASHBOARD_TOKEN= # required on /api/* + WS when set; use
DASHBOARD_ALLOWED_HOSTS= # extra Host names (comma-sep) for a LAN bind
# Database
DASHBOARD_DB_PATH=/var/lib/agent-dashboard/dashboard.db
DASHBOARD_DB_PATH=/var/lib/agenthub/dashboard.db
# Ingestion polls (0 disables either one)
DASHBOARD_WORKFLOW_POLL_MS=12000 # Workflow-tool run journals
@@ -278,7 +277,7 @@ LOG_LEVEL=info # debug | info | warn | error
version: '3.8'
services:
agent-dashboard:
agenthub:
build: .
ports:
- "4820:4820"
@@ -382,7 +381,7 @@ graph TB
pip install awsebcli
# Initialize
eb init -p node.js agent-dashboard
eb init -p node.js agenthub
# Create environment
eb create production
@@ -401,20 +400,20 @@ eb open
apiVersion: apps/v1
kind: Deployment
metadata:
name: agent-dashboard
name: agenthub
spec:
replicas: 3
selector:
matchLabels:
app: agent-dashboard
app: agenthub
template:
metadata:
labels:
app: agent-dashboard
app: agenthub
spec:
containers:
- name: agent-dashboard
image: agent-dashboard:latest
- name: agenthub
image: agenthub:latest
ports:
- containerPort: 4820
env:
@@ -426,16 +425,16 @@ spec:
volumes:
- name: data
persistentVolumeClaim:
claimName: agent-dashboard-pvc
claimName: agenthub-pvc
---
apiVersion: v1
kind: Service
metadata:
name: agent-dashboard
name: agenthub
spec:
selector:
app: agent-dashboard
app: agenthub
ports:
- protocol: TCP
port: 80
@@ -453,9 +452,9 @@ graph TB
end
subgraph "Pods"
Pod1[agent-dashboard-1<br/>:4820]
Pod2[agent-dashboard-2<br/>:4820]
Pod3[agent-dashboard-3<br/>:4820]
Pod1[agenthub-1<br/>:4820]
Pod2[agenthub-2<br/>:4820]
Pod3[agenthub-3<br/>:4820]
end
subgraph "Storage"
@@ -488,22 +487,22 @@ graph TB
npm install -g pm2
# Start application
pm2 start server/index.js --name agent-dashboard
pm2 start server/index.js --name agenthub
# Start with environment
pm2 start server/index.js --name agent-dashboard --env production
pm2 start server/index.js --name agenthub --env production
# View logs
pm2 logs agent-dashboard
pm2 logs agenthub
# Monitor
pm2 monit
# Restart
pm2 restart agent-dashboard
pm2 restart agenthub
# Stop
pm2 stop agent-dashboard
pm2 stop agenthub
# Auto-start on system boot
pm2 startup
@@ -516,7 +515,7 @@ pm2 save
// ecosystem.config.js
module.exports = {
apps: [{
name: 'agent-dashboard',
name: 'agenthub',
script: './server/index.js',
instances: 2,
exec_mode: 'cluster',
@@ -527,11 +526,11 @@ module.exports = {
env_production: {
NODE_ENV: 'production',
PORT: 4820,
DASHBOARD_DB_PATH: '/var/lib/agent-dashboard/dashboard.db'
DASHBOARD_DB_PATH: '/var/lib/agenthub/dashboard.db'
},
max_memory_restart: '500M',
error_file: '/var/log/agent-dashboard/error.log',
out_file: '/var/log/agent-dashboard/out.log',
error_file: '/var/log/agenthub/error.log',
out_file: '/var/log/agenthub/out.log',
time: true
}]
};
@@ -545,18 +544,18 @@ pm2 start ecosystem.config.js --env production
### Systemd Service (Linux)
```ini
# /etc/systemd/system/agent-dashboard.service
# /etc/systemd/system/agenthub.service
[Unit]
Description=Agent Dashboard
After=network.target
[Service]
Type=simple
User=agent-dashboard
WorkingDirectory=/opt/agent-dashboard
User=agenthub
WorkingDirectory=/opt/agenthub
Environment=NODE_ENV=production
Environment=PORT=4820
Environment=DASHBOARD_DB_PATH=/var/lib/agent-dashboard/dashboard.db
Environment=DASHBOARD_DB_PATH=/var/lib/agenthub/dashboard.db
ExecStart=/usr/bin/node server/index.js
Restart=on-failure
RestartSec=10
@@ -569,14 +568,14 @@ WantedBy=multi-user.target
```bash
# Enable and start service
sudo systemctl enable agent-dashboard
sudo systemctl start agent-dashboard
sudo systemctl enable agenthub
sudo systemctl start agenthub
# Check status
sudo systemctl status agent-dashboard
sudo systemctl status agenthub
# View logs
sudo journalctl -u agent-dashboard -f
sudo journalctl -u agenthub -f
```
---
@@ -680,8 +679,8 @@ graph TB
#!/bin/bash
# scripts/backup.sh
BACKUP_DIR="/var/backups/agent-dashboard"
DB_PATH="/var/lib/agent-dashboard/dashboard.db"
BACKUP_DIR="/var/backups/agenthub"
DB_PATH="/var/lib/agenthub/dashboard.db"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/dashboard_$TIMESTAMP.db"
@@ -695,7 +694,7 @@ sqlite3 "$DB_PATH" "VACUUM INTO '$BACKUP_FILE'"
gzip "$BACKUP_FILE"
# Upload to S3 (optional)
aws s3 cp "$BACKUP_FILE.gz" s3://my-backups/agent-dashboard/
aws s3 cp "$BACKUP_FILE.gz" s3://my-backups/agenthub/
# Delete old backups (keep last 7 days)
find "$BACKUP_DIR" -name "dashboard_*.db.gz" -mtime +7 -delete
@@ -710,7 +709,7 @@ echo "Backup completed: $BACKUP_FILE.gz"
# scripts/restore.sh
BACKUP_FILE=$1
DB_PATH="/var/lib/agent-dashboard/dashboard.db"
DB_PATH="/var/lib/agenthub/dashboard.db"
if [ -z "$BACKUP_FILE" ]; then
echo "Usage: ./restore.sh <backup_file.db.gz>"
@@ -718,17 +717,17 @@ if [ -z "$BACKUP_FILE" ]; then
fi
# Stop application
systemctl stop agent-dashboard
systemctl stop agenthub
# Decompress backup
gunzip -c "$BACKUP_FILE" > /tmp/restore.db
# Restore database
cp /tmp/restore.db "$DB_PATH"
chown agent-dashboard:agent-dashboard "$DB_PATH"
chown agenthub:agenthub "$DB_PATH"
# Start application
systemctl start agent-dashboard
systemctl start agenthub
echo "Restore completed from $BACKUP_FILE"
```
@@ -767,7 +766,7 @@ graph TB
### TLS Configuration (Nginx Reverse Proxy)
```nginx
# /etc/nginx/sites-available/agent-dashboard
# /etc/nginx/sites-available/agenthub
server {
listen 443 ssl http2;
server_name dashboard.example.com;
+2 -2
View File
@@ -573,13 +573,13 @@ function sendToServer(hookType, payload) {
> 1. If `CLAUDE_DASHBOARD_PORT` is set in the environment, the handler treats
> it as an explicit operator override and POSTs to that single port —
> no discovery, no fan-out (useful for tests and container setups).
> 2. Otherwise it reads `~/.claude/.agent-dashboard.json`, a JSON document
> 2. Otherwise it reads `~/.claude/.agenthub.json`, a JSON document
> that lists every dashboard server currently running on the machine.
> Each server appends its `{port, pid, startedAt, dataDir}` entry on
> startup and removes it on a clean shutdown. The handler **prunes any
> entry whose PID is no longer alive** and POSTs the hook payload to one
> port per **unique SQLite data directory** (lowest port wins when Docker
> and `npm run dev` share `~/.claude/agent-dashboard`).
> and `npm run dev` share `~/.claude/agenthub`).
> 3. If neither yields a target, the handler falls back to `4820`.
>
> Dashboards with **different** databases (e.g. the packaged desktop app using
+1 -1
View File
@@ -1370,7 +1370,7 @@ The lane card shows a lock badge when the lane **holds** a named lock (its `lane
The **ship-feature-lane** skill is the ported Shipyard driving skill for autonomous end-to-end feature pipelines in a single AgentHub lane. It coordinates the entire development flow: implementation (TDD) → independent unit tests → pre-push CI gates + dev preflight → e2e testing → code review → local QC → senior gate authorization → PR push and publication → CI watch → final report.
**Every stage whose job is to judge the work runs in a fresh-context subagent, never in the driving session.** The plan debate (Stage 0b), unit tests (Stage 1b), the e2e run and its triage (Stage 3), code review (Stage 4), browser QC (Stage 6), the senior gate (Stage 7), and a re-triage of CI that stayed red after a fix (Stage 10) are each dispatched with the Agent tool to a subagent that starts with an empty context and receives only artifacts it can verify itself — working directory, lane spec, diff range, commands, and the other agents' reports verbatim, never the driving session's paraphrase of why a finding was fine. The stages the driving session keeps are the ones that *do* rather than judge: intake, implementation, the mechanical gates, publishing, the CI/PR watch, and applying every fix the judges ask for. `subagent_type: fork` is explicitly forbidden for these: a fork inherits the implementing session's context, so it inherits the belief that the code is already correct. The driving session implements and applies fixes; it never grades its own work. Stages 1b/3 use the generic `general-purpose` type (nothing to install). Stage 4 uses the generic `Code Reviewer` type as its catch-all, plus — in parallel, routed by which of `server/`, `client/`, `mcp/` the diff touches — the plugin-qualified `agenthub:backend-reviewer` / `agenthub:frontend-reviewer` / `agenthub:mcp-reviewer` domain reviewers (nothing to install; `agenthub:` addressing works from any lane). Stages 6/7 use the lane-installed `qc-local`/`senior-gate-reviewer` agents.
**Every stage whose job is to judge the work runs in a fresh-context subagent, never in the driving session.** The plan debate (Stage 0b), unit tests (Stage 1b), the e2e run and its triage (Stage 3), code review (Stage 4), browser QC (Stage 6), the senior gate (Stage 7), and a re-triage of CI that stayed red after a fix (Stage 10) are each dispatched with the Agent tool to a subagent that starts with an empty context and receives only artifacts it can verify itself — working directory, lane spec, diff range, commands, and the other agents' reports verbatim, never the driving session's paraphrase of why a finding was fine. The stages the driving session keeps are the ones that *do* rather than judge: intake, implementation, the mechanical gates, publishing, the CI/PR watch, and applying every fix the judges ask for. `subagent_type: fork` is explicitly forbidden for these: a fork inherits the implementing session's context, so it inherits the belief that the code is already correct. The driving session implements and applies fixes; it never grades its own work. Stages 1b/3 use the generic `general-purpose` type (nothing to install). Stage 4 uses the generic `Code Reviewer` type as its catch-all, plus — in parallel, routed by which of `server/`, `client/` the diff touches — the plugin-qualified `agenthub:backend-reviewer` / `agenthub:frontend-reviewer` domain reviewers (nothing to install; `agenthub:` addressing works from any lane). Stages 6/7 use the lane-installed `qc-local`/`senior-gate-reviewer` agents.
### Getting the skill (nothing to do, if you run the plugin)
-855
View File
@@ -1,855 +0,0 @@
# MCP Integration Guide
Model Context Protocol (MCP) server integration for programmatic dashboard access.
---
## Table of Contents
- [Overview](#overview)
- [MCP Architecture](#mcp-architecture)
- [Setup & Installation](#setup--installation)
- [Available Tools](#available-tools)
- [Client Configuration](#client-configuration)
- [Usage Examples](#usage-examples)
- [Tool Reference](#tool-reference)
- [Error Handling](#error-handling)
- [Performance](#performance)
- [Development](#development)
- [Deployment](#deployment)
---
## Overview
The Agent Dashboard MCP server exposes dashboard functionality as tools that can be used by Claude Desktop, Cline, and other MCP clients.
```mermaid
graph TB
subgraph "MCP Clients"
Claude[Claude Desktop]
Cline[Cline IDE]
Custom[Custom MCP Client]
end
subgraph "MCP Server"
MCPServer[Agent Dashboard<br/>MCP Server]
Tools[Tool Registry]
end
subgraph "Dashboard API"
API[Express API<br/>:4820]
DB[(SQLite DB)]
end
Claude -->|stdio| MCPServer
Cline -->|stdio| MCPServer
Custom -->|stdio| MCPServer
MCPServer --> Tools
Tools -->|HTTP| API
API --> DB
style MCPServer fill:#0f766e
style API fill:#3B82F6
style DB fill:#003B57,color:#fff
```
**Key Benefits:**
- 🤖 **AI-Native** - Claude can query sessions, agents, and costs
- 🔌 **Standardized** - Works with any MCP-compatible client
- 🚀 **Easy Setup** - One-command installation
- 🔒 **Local-First** - No cloud dependencies
---
## MCP Architecture
### MCP Protocol Flow
```mermaid
sequenceDiagram
participant Client as MCP Client<br/>(Claude Desktop)
participant Server as MCP Server
participant API as Dashboard API
participant DB as SQLite
Client->>Server: Initialize connection
Server-->>Client: Server info + capabilities
Client->>Server: List tools
Server-->>Client: Tool definitions
Client->>Server: Call tool (get_sessions)
Server->>API: GET /api/sessions
API->>DB: Query sessions
DB-->>API: Results
API-->>Server: JSON response
Server-->>Client: Tool result
Client->>Client: Process result
```
### MCP Server Structure
```mermaid
graph TB
subgraph "MCP Server (mcp/)"
Index[index.ts<br/>Entry point]
Config[config/app-config.ts<br/>Configuration]
Tools[tools/<br/>Tool implementations]
Types[types.ts<br/>TypeScript types]
end
subgraph "Tool Categories"
Sessions[Session Tools<br/>get_sessions, get_session]
Agents[Agent Tools<br/>get_agents, get_agent]
Pricing[Pricing Tools<br/>get_pricing, create_rule]
Stats[Stats Tools<br/>get_stats]
end
Index --> Config
Index --> Tools
Tools --> Sessions
Tools --> Agents
Tools --> Pricing
Tools --> Stats
style Index fill:#0f766e
style Tools fill:#10B981
```
---
## Setup & Installation
### Prerequisites
- Node.js >= 18.0.0
- Dashboard server running on `localhost:4820`
### Installation
```bash
# Install MCP server dependencies
npm run mcp:install
# Build MCP server (also stamps mcp/build/.srchash)
npm run mcp:build
# Test MCP server
npm run mcp:start
```
Installing the `agenthub` plugin needs none of this: it ships the built server and
wires it up itself (see [PLUGINS.md](PLUGINS.md)).
### Why `mcp/build/` is committed
Claude Code starts a plugin's MCP servers the moment a session opens and offers
no "not ready yet, retry" state, so an async bootstrap cannot win that race. The
build artifact is therefore committed, and `plugins/agenthub/.mcp.json` points at
`${CLAUDE_PLUGIN_ROOT}/mcp/build/index.js`.
`npm run mcp:build` bundles `mcp/src` and its runtime dependencies
(`esbuild --bundle`) into that single `build/index.js` file — the plugin cache
never gets an `npm install` for `mcp/`, so a build that still had bare
`node_modules` imports (plain `tsc` output) would throw `MODULE_NOT_FOUND` the
instant Claude Code spawned it after any plugin install or update. `express`
(only used by `--transport=http`, never by the stdio mode a real MCP host
uses) is pulled in lazily via `await import(...)` in `src/index.ts` instead of
a static import, so the stdio path never even evaluates it.
The cost is drift, so freshness is enforced by content hash — `mcp/src` plus the
MCP manifests and tsconfig are hashed into `mcp/build/.srchash`:
```bash
npm run mcp:check-build # fails when mcp/build is stale or unstamped
```
`npm run mcp:build` re-stamps it, the pre-commit hook runs the check whenever
`mcp/src` is part of the commit, and `/agenthub-doctor` reports it. Modification
times are deliberately not used: a fresh clone stamps every file at checkout
time in arbitrary order.
### Directory Structure
```
mcp/
├── src/
│ ├── index.ts # MCP server entry point
│ ├── config/
│ │ └── app-config.ts # Configuration + validation
│ ├── tools/
│ │ ├── sessions.ts # Session-related tools
│ │ ├── agents.ts # Agent-related tools
│ │ ├── pricing.ts # Pricing management tools
│ │ └── stats.ts # Statistics tools
│ └── types.ts # TypeScript type definitions
│
├── build/ # Bundled, dependency-free output (COMMITTED — see above)
│ ├── index.js # single esbuild bundle, no node_modules needed to run it
│ └── .srchash # hash of mcp/src the build was produced from
├── package.json
├── tsconfig.json
└── README.md
```
---
## Available Tools
### Tool Catalog
```mermaid
graph TB
subgraph "Session Management"
GetSessions[get_sessions<br/>List all sessions]
GetSession[get_session<br/>Get session details]
end
subgraph "Agent Management"
GetAgents[get_agents<br/>List session agents]
GetAgent[get_agent<br/>Get agent details]
GetTools[get_tools<br/>List agent tools]
end
subgraph "Pricing"
GetPricing[get_pricing<br/>List pricing rules]
CreateRule[create_pricing_rule<br/>Add custom rule]
DeleteRule[delete_pricing_rule<br/>Remove rule]
end
subgraph "Statistics"
GetStats[get_stats<br/>Dashboard statistics]
end
style GetSessions fill:#3B82F6
style GetAgents fill:#10B981
style GetPricing fill:#F59E0B
style GetStats fill:#8B5CF6
```
---
## Client Configuration
### Claude Desktop
Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):
```json
{
"mcpServers": {
"agent-dashboard": {
"command": "node",
"args": ["/path/to/agent-dashboard/mcp/dist/index.js"],
"env": {
"MCP_DASHBOARD_BASE_URL": "http://localhost:4820"
}
}
}
}
```
**Linux:**
```
~/.config/Claude/claude_desktop_config.json
```
**Windows:**
```
%APPDATA%\Claude\claude_desktop_config.json
```
### Cline (VS Code Extension)
Add to VS Code settings (`.vscode/settings.json`):
```json
{
"cline.mcpServers": {
"agent-dashboard": {
"command": "node",
"args": ["/path/to/agent-dashboard/mcp/dist/index.js"],
"env": {
"MCP_DASHBOARD_BASE_URL": "http://localhost:4820"
}
}
}
}
```
### Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `MCP_DASHBOARD_BASE_URL` | `http://localhost:4820` | Dashboard API base URL |
**URL Validation:**
```mermaid
graph TB
URL[MCP_DASHBOARD_BASE_URL] --> Validate{Valid?}
Validate -->|Invalid Protocol| Error1[Throw: Only http/https allowed]
Validate -->|Invalid Hostname| Error2[Throw: Only loopback allowed]
Validate -->|Valid| Accept[Accept URL]
subgraph "Valid Hostnames"
H1[127.0.0.1]
H2[localhost]
H3[::1]
end
Accept --> H1 & H2 & H3
style Error1 fill:#EF4444
style Error2 fill:#EF4444
style Accept fill:#10B981
```
---
## Usage Examples
### Example 1: List Recent Sessions
**User Prompt:**
> "Show me the 5 most recent Claude Code sessions"
**Tool Call:**
```json
{
"name": "get_sessions",
"arguments": {
"limit": 5
}
}
```
**Response:**
```json
{
"sessions": [
{
"session_id": "sess_abc123",
"model": "claude-sonnet-4",
"status": "active",
"total_cost": 1.23,
"agent_count": 3,
"tool_count": 12,
"created_at": "2024-03-18T12:00:00Z"
}
]
}
```
---
### Example 2: Analyze Session Cost
**User Prompt:**
> "What was the cost breakdown for session sess_abc123?"
**Tool Sequence:**
```mermaid
sequenceDiagram
participant User
participant Claude
participant MCP as MCP Server
participant API as Dashboard API
User->>Claude: Analyze session cost
Claude->>MCP: get_session(sess_abc123)
MCP->>API: GET /api/sessions/sess_abc123
API-->>MCP: Session data
MCP-->>Claude: Session result
Claude->>MCP: get_agents(sess_abc123)
MCP->>API: GET /api/sessions/sess_abc123/agents
API-->>MCP: Agents data
MCP-->>Claude: Agents result
Claude->>User: Analysis:<br/>Total: $1.23<br/>3 agents:<br/>- Main: $0.85<br/>- Explore: $0.25<br/>- Task: $0.13
```
---
### Example 3: Create Custom Pricing Rule
**User Prompt:**
> "Add a pricing rule for my-custom-model with input $5/1M and output $20/1M"
**Tool Call:**
```json
{
"name": "create_pricing_rule",
"arguments": {
"pattern": "my-custom-model",
"input_cost_per_1m": 5.0,
"output_cost_per_1m": 20.0
}
}
```
**Response:**
```json
{
"rule": {
"id": 10,
"pattern": "my-custom-model",
"input_cost_per_1m": 5.0,
"output_cost_per_1m": 20.0,
"created_at": "2024-03-18T14:30:00Z"
}
}
```
---
## Tool Reference
### get_sessions
List all sessions with optional filters.
**Input Schema:**
```typescript
{
limit?: number; // Max sessions to return (1-1000)
status?: 'active' | 'completed';
}
```
**Output Schema:**
```typescript
{
sessions: Session[];
total: number;
}
interface Session {
session_id: string;
model: string;
status: 'active' | 'completed';
total_cost: number;
agent_count: number;
tool_count: number;
created_at: string;
updated_at: string;
}
```
---
### get_session
Get single session details.
**Input Schema:**
```typescript
{
session_id: string; // Required
}
```
**Output Schema:**
```typescript
{
session: Session;
}
```
**Errors:**
- `404` - Session not found
---
### get_agents
List agents for a session.
**Input Schema:**
```typescript
{
session_id: string; // Required
}
```
**Output Schema:**
```typescript
{
agents: Agent[];
}
interface Agent {
agent_id: string;
session_id: string;
agent_type: string;
status: 'running' | 'completed' | 'failed';
current_tool: string | null;
input_tokens: number;
output_tokens: number;
cost: number;
tool_count: number;
created_at: string;
updated_at: string;
}
```
---
### get_agent
Get single agent details.
**Input Schema:**
```typescript
{
agent_id: string; // Required
}
```
**Output Schema:**
```typescript
{
agent: Agent;
}
```
**Errors:**
- `404` - Agent not found
---
### get_tools
List tool executions for an agent.
**Input Schema:**
```typescript
{
agent_id: string; // Required
}
```
**Output Schema:**
```typescript
{
tools: ToolExecution[];
}
interface ToolExecution {
id: number;
agent_id: string;
tool_name: string;
duration_ms: number;
success: boolean;
error_message: string | null;
created_at: string;
}
```
---
### get_pricing
List pricing rules.
**Input Schema:**
```typescript
{} // No parameters
```
**Output Schema:**
```typescript
{
rules: PricingRule[];
}
interface PricingRule {
id: number;
pattern: string;
input_cost_per_1m: number;
output_cost_per_1m: number;
is_default: boolean;
created_at: string;
}
```
---
### create_pricing_rule
Create custom pricing rule.
**Input Schema:**
```typescript
{
pattern: string; // Model pattern
input_cost_per_1m: number; // USD per 1M input tokens
output_cost_per_1m: number; // USD per 1M output tokens
}
```
**Output Schema:**
```typescript
{
rule: PricingRule;
}
```
**Errors:**
- `400` - Invalid input
- `409` - Pattern already exists
---
### delete_pricing_rule
Delete pricing rule.
**Input Schema:**
```typescript
{
pattern: string; // Pattern to delete
}
```
**Output Schema:**
```typescript
{
deleted: true;
}
```
**Errors:**
- `404` - Pattern not found
- `403` - Cannot delete default rule
---
### get_stats
Get dashboard statistics.
**Input Schema:**
```typescript
{} // No parameters
```
**Output Schema:**
```typescript
{
total_sessions: number;
active_sessions: number;
total_agents: number;
total_tools: number;
total_cost: number;
avg_session_cost: number;
}
```
---
## Error Handling
### Error Response Format
```typescript
interface MCPError {
code: string;
message: string;
details?: any;
}
```
### Error Codes
| Code | Description | Resolution |
|------|-------------|------------|
| `INVALID_INPUT` | Invalid tool arguments | Check input schema |
| `API_ERROR` | Dashboard API error | Check server is running |
| `NOT_FOUND` | Resource not found | Verify ID exists |
| `TIMEOUT` | Request timeout | Increase timeout, check network |
| `CONFIG_ERROR` | Invalid configuration | Check `MCP_DASHBOARD_BASE_URL` |
### Error Handling Flow
```mermaid
graph TB
Request[Tool Request] --> Validate{Input<br/>Valid?}
Validate -->|No| Error1[Return INVALID_INPUT]
Validate -->|Yes| API[Call API]
API --> Success{HTTP 200?}
Success -->|No| HTTPCode{Status Code}
HTTPCode -->|404| Error2[Return NOT_FOUND]
HTTPCode -->|500| Error3[Return API_ERROR]
HTTPCode -->|Timeout| Error4[Return TIMEOUT]
Success -->|Yes| Parse[Parse JSON]
Parse --> Result[Return Result]
style Error1 fill:#EF4444
style Error2 fill:#F59E0B
style Error3 fill:#EF4444
style Error4 fill:#EF4444
style Result fill:#10B981
```
---
## Performance
### Tool Execution Time
```mermaid
graph TB
subgraph "Execution Breakdown"
Validation[Input Validation<br/>~1ms]
HTTP[HTTP Request<br/>~20ms]
API[API Processing<br/>~5ms]
DB[Database Query<br/>~5ms]
Response[Response Serialization<br/>~5ms]
end
Total[Total: ~36ms]
Validation --> HTTP
HTTP --> API
API --> DB
DB --> Response
Response --> Total
style Total fill:#10B981
```
**Performance Benchmarks:**
| Tool | Avg Time | 95th Percentile |
|------|----------|-----------------|
| `get_sessions` | 25ms | 40ms |
| `get_session` | 15ms | 25ms |
| `get_agents` | 20ms | 35ms |
| `get_tools` | 30ms | 50ms |
| `get_pricing` | 10ms | 20ms |
| `get_stats` | 40ms | 60ms |
---
## Development
### Building from Source
```bash
# Install dependencies
cd mcp && npm install
# Build TypeScript
npm run build
# Watch mode (auto-rebuild)
npm run dev
# Type checking
npm run typecheck
```
### Adding New Tools
```typescript
// mcp/src/tools/my-tool.ts
import { z } from 'zod';
import { fetchFromAPI } from '../utils';
export const myTool = {
name: 'my_tool',
description: 'Description of what this tool does',
inputSchema: z.object({
param1: z.string(),
param2: z.number().optional()
}),
async execute(args: { param1: string; param2?: number }) {
const response = await fetchFromAPI(`/api/my-endpoint?param=${args.param1}`);
return response.data;
}
};
```
Register in `index.ts`:
```typescript
import { myTool } from './tools/my-tool';
server.setRequestHandler(CallToolRequestSchema, async (request) => {
switch (request.params.name) {
case 'my_tool':
return await myTool.execute(request.params.arguments);
// ... other tools
}
});
```
---
## Deployment
### Docker Deployment
```dockerfile
# mcp/Dockerfile
FROM node:18-alpine
WORKDIR /app
# Install dependencies
COPY package*.json ./
RUN npm ci --production
# Copy built files
COPY dist ./dist
CMD ["node", "dist/index.js"]
```
```bash
# Build Docker image
npm run mcp:docker:build
# Run container
docker run -e MCP_DASHBOARD_BASE_URL=http://localhost:4820 agent-dashboard-mcp:local
```
### Podman Deployment
```bash
# Build with Podman
npm run mcp:podman:build
# Run with Podman
podman run -e MCP_DASHBOARD_BASE_URL=http://localhost:4820 localhost/agent-dashboard-mcp:local
```
---
## Summary
The MCP server provides:
- ✅ **AI-native interface** - Claude can query dashboard data naturally
- ✅ **Complete tool coverage** - Sessions, agents, tools, pricing, stats
- ✅ **Type-safe** - Full TypeScript types with Zod validation
- ✅ **Standards-compliant** - Implements MCP protocol specification
- ✅ **Easy setup** - One-command installation and configuration
- ✅ **Local-first** - No cloud dependencies, runs entirely locally
- ✅ **Docker-ready** - Containerized deployment support
For API details, see [docs/API.md](./API.md).
+18 -20
View File
@@ -74,7 +74,7 @@ Skills and commands are invoked as `/agenthub-<plugin>:<name>`. Agents are dispa
## The `agenthub` plugin
The marketplace's root entry (`"source": "./"`) is the entire repository, so
`server/`, `client/`, `mcp/`, `scripts/` and `bin/agenthub.js` all land under
`server/`, `client/`, `scripts/` and `bin/agenthub.js` all land under
`${CLAUDE_PLUGIN_ROOT}` when Claude Code caches it. That is what makes a
checkout unnecessary.
@@ -85,12 +85,11 @@ checkout unnecessary.
| The eight event hooks | inline `hooks` in `.claude-plugin/plugin.json`, each running `scripts/hook-handler.js` |
| The dashboard server | started detached by the bootstrap, from the plugin cache |
| The `agenthub` CLI | a launcher written to `~/.local/bin/agenthub` |
| The MCP tools | `plugins/agenthub/.mcp.json`, pointing at the committed `mcp/build/index.js` |
| The dashboard UI (`/run` and every other client route) | built into the runtime dir by the bootstrap, so it works the moment `claude` starts |
| `/agenthub-doctor`, `/agenthub-update`, `/agenthub-open` | `plugins/agenthub/commands/` |
| `/ship-full`, `/ship-quick` and one command per pipeline node (`/intake`, `/plan`, `/implement`, `/tests`, `/e2e`, `/review`, `/qc`, `/gate`, `/publishing`, `/pr-open`, `/done`, …) | `plugins/agenthub/commands/`, one file per name |
| The skills (`ship-feature-lane`, `ship-feature`, `repo-onboarding`, `debug-live-issue`, `mcp-operations`, `update-project-docs`, `file-headers`) | `skills/` at the repo root — which IS the plugin root, so they are auto-discovered as plugin skills |
| The agents (`qc-local`, `senior-gate-reviewer`, `backend-reviewer`, `frontend-reviewer`, `mcp-reviewer`) | `agents/` at the repo root, auto-discovered the same way — exposed as `agenthub:<name>` |
| The skills (`ship-feature-lane`, `ship-feature`, `repo-onboarding`, `debug-live-issue`, `update-project-docs`, `file-headers`) | `skills/` at the repo root — which IS the plugin root, so they are auto-discovered as plugin skills |
| The agents (`qc-local`, `senior-gate-reviewer`, `backend-reviewer`, `frontend-reviewer`) | `agents/` at the repo root, auto-discovered the same way — exposed as `agenthub:<name>` |
Plugin skills are namespaced: they are invoked as `/agenthub:<name>`, with the bare
`/<name>` working as an alias unless another skill or command claims that name.
@@ -100,8 +99,8 @@ useless for a lane working in a different repo. `agenthub skills install` surviv
as the fallback for a plain checkout without the plugin.
**The agents ship the same way, from the same files.** `agents/` at the repo
root holds all five (`qc-local`, `senior-gate-reviewer`, `backend-reviewer`,
`frontend-reviewer`, `mcp-reviewer`), so the plugin exposes them as
root holds all four (`qc-local`, `senior-gate-reviewer`, `backend-reviewer`,
`frontend-reviewer`), so the plugin exposes them as
`agenthub:<name>`. `agenthub lanes agents install` copies `qc-local` +
`senior-gate-reviewer` from that same directory into a lane's own
`.claude/agents/` — the highest-priority agent source — which is what makes the
@@ -149,7 +148,7 @@ waits on an install:
The first run takes a few minutes — most of it is the UI build, which is what
makes client-only routes (`http://localhost:4820/run`, and every other page)
work the moment `claude` starts, with no manual `/agenthub-open` step. Progress
goes to `~/.claude/agent-dashboard/runtime/bootstrap.log`; `npm run build`'s own
goes to `~/.claude/agenthub/runtime/bootstrap.log`; `npm run build`'s own
output goes to `client-build.log` next to it; the server's own output goes to
`server.log`.
@@ -164,7 +163,7 @@ cache directory rewrites and restarts the unit on the next session start, the
same self-healing pattern the CLI launcher already uses. The bootstrap also
tries `loginctl enable-linger` so the service starts on a cold boot before
anyone logs in; when that needs elevated rights, it's skipped silently (check
`~/.claude/agent-dashboard/runtime/bootstrap.log`, or run
`~/.claude/agenthub/runtime/bootstrap.log`, or run
`sudo loginctl enable-linger <user>` once by hand). A pre-existing
`agenthub.service` unit not written by this bootstrap is left untouched, and the
server falls back to the old per-session detached spawn instead.
@@ -195,7 +194,7 @@ drive step by step (`brainstorming`, `writing-plans`,
`verification-before-completion`, `finishing-a-development-branch`). Install it
with `claude plugin install superpowers@claude-plugins-official`.
The row is a **WARN, never a FAIL**: the dashboard, hooks, CLI and MCP all work
The row is a **WARN, never a FAIL**: the dashboard, hooks and CLI all work
without it, and only those two pipeline skills stop partway. A plugin listed but
explicitly disabled counts as missing — Claude Code will not resolve its skills.
The skills present as plain user skills under `~/.claude/skills/` count as
@@ -215,7 +214,7 @@ install clones from the network. It runs `claude plugin install
superpowers@claude-plugins-official --scope user -y`; if that fails it registers
the marketplace (`claude plugin marketplace add anthropics/claude-plugins-official`)
and retries exactly once. Failure is logged with the manual command and nothing
throws — the dashboard, hooks, CLI and MCP do not need these plugins.
throws — the dashboard, hooks and CLI do not need these plugins.
The result is recorded as `skillPluginsSettled` in the bootstrap's `state.json`,
which is also part of `fastPathOk()`: the check runs **once** per install, then
@@ -240,9 +239,9 @@ without asking; it belongs in a review, not in a config file.
| Thing | Location | Survives a plugin update |
|---|---|---|
| SQLite DB, transcripts | `~/.claude/agent-dashboard/` | yes |
| `node_modules`, `client-dist`, logs, lock, `state.json` | `~/.claude/agent-dashboard/runtime/` | yes (re-verified on every session start) |
| Source, hooks, `mcp/build` | the plugin cache version directory | no — re-bootstrapped |
| SQLite DB, transcripts | `~/.claude/agenthub/` | yes |
| `node_modules`, `client-dist`, logs, lock, `state.json` | `~/.claude/agenthub/runtime/` | yes (re-verified on every session start) |
| Source, hooks | the plugin cache version directory | no — re-bootstrapped |
The bootstrap deliberately does **not** set `DASHBOARD_DATA_DIR`: leaving the
default keeps a plugin-run server and a developer's `npm run dev` server on one
@@ -264,7 +263,7 @@ not assumed. It does **not**:
`~/.claude/settings.json` (still pointing at that cache dir — they keep
firing, and keep working, until the cache directory is eventually GC'd by
Claude Code, at which point they silently start failing)
- remove `~/.local/bin/agenthub` or `~/.claude/agent-dashboard/runtime/`
- remove `~/.local/bin/agenthub` or `~/.claude/agenthub/runtime/`
For a clean machine, do all of this yourself:
@@ -274,10 +273,10 @@ For a clean machine, do all of this yourself:
systemctl --user disable --now agenthub.service 2>/dev/null
rm -f ~/.config/systemd/user/agenthub.service && systemctl --user daemon-reload 2>/dev/null
# Fallback for a detached (non-systemd) server, or if the unit above was absent:
kill "$(node -e 'console.log(JSON.parse(require("fs").readFileSync(require("os").homedir()+"/.claude/.agent-dashboard.json","utf8")).pid)')" 2>/dev/null
kill "$(node -e 'console.log(JSON.parse(require("fs").readFileSync(require("os").homedir()+"/.claude/.agenthub.json","utf8")).pid)')" 2>/dev/null
# 2. Runtime state and the CLI launcher
rm -rf ~/.claude/agent-dashboard/runtime # deps, UI bundle, logs, state
rm -rf ~/.claude/agenthub/runtime # deps, UI bundle, logs, state
rm -f ~/.local/bin/agenthub
# 3. The hook entries claude plugin install wrote (settings.json is not
@@ -296,7 +295,7 @@ fs.writeFileSync(p, JSON.stringify(d, null, 2) + "\n");
'
```
`~/.claude/agent-dashboard/` still holds the SQLite database after all of the
`~/.claude/agenthub/` still holds the SQLite database after all of the
above — delete that directory too only if you want the recorded history gone.
## Available Plugins
@@ -473,7 +472,7 @@ Audit your Claude Code configuration and curate the file-based memory store via
### 10. `agenthub-dashboard` — Dashboard Connector
Direct MCP integration, quick status, live watch, and endpoint probing.
Quick status, live watch, and endpoint probing.
| Skill | Command | Purpose |
|-------|---------|---------|
@@ -484,7 +483,7 @@ Direct MCP integration, quick status, live watch, and endpoint probing.
**Commands:** `/agenthub-dashboard:status` · `/agenthub-dashboard:ping` · `/agenthub-dashboard:open-dashboard`
**Agent:** `dashboard-operator` — verifies the dashboard is up and guides start/restart/import. **MCP Server:** direct tool access to the Agent Monitor API. **Settings:** default agent model.
**Agent:** `dashboard-operator` — verifies the dashboard is up and guides start/restart/import. **Access:** direct tool access to the Agent Monitor API. **Settings:** default agent model.
---
@@ -541,7 +540,6 @@ my-plugin/
│ └── hooks.json # Event hooks (fail-safe, non-blocking)
├── bin/
│ └── my-cli-tool # CLI scripts (added to PATH)
├── .mcp.json # MCP server configuration
└── settings.json # Plugin settings
```
+2 -2
View File
@@ -403,8 +403,8 @@ graph LR
### Getting Help
- **Issues:** [GitHub Issues](https://github.com/your-org/agent-dashboard/issues)
- **Discussions:** [GitHub Discussions](https://github.com/your-org/agent-dashboard/discussions)
- **Issues:** [GitHub Issues](https://github.com/your-org/agenthub/issues)
- **Discussions:** [GitHub Discussions](https://github.com/your-org/agenthub/discussions)
- **Documentation:** This folder
### Additional Resources