Adds a root `ccam` plugin (`.claude-plugin/plugin.json`, `"source": "./"`) so
`/plugin marketplace add` + `/plugin install ccam@...` is enough on a machine
with nothing but Claude Code: no clone, no npm run setup, no manual npm start.
- scripts/plugin-bootstrap.js: SessionStart hook. Fast-path exit, Node >=22.5
gate (node:sqlite), mkdir lock with stale reclaim, deps installed into
~/.claude/agent-dashboard/runtime/ (never the plugin cache), legacy
checkout-hook cleanup (backed up), ~/.local/bin/ccam launcher, eager UI
build so client routes like /run work immediately, detached server spawn.
- scripts/plugin-open.js, scripts/plugin-doctor.js: /ccam-open, /ccam-doctor.
- server/index.js: DASHBOARD_CLIENT_DIST override (plugin cache is read-only).
- mcp/build/ is committed (plugin MCP servers start before any bootstrap could
build them) and kept honest by scripts/check-mcp-build.js (content hash,
not mtime), enforced by pre-commit when mcp/src changes.
- plugins/ccam-dashboard/.mcp.json moved under plugins/ccam/ with a working
${CLAUDE_PLUGIN_ROOT} path (the old relative path never resolved from a
marketplace-cached subdir).
- Docs: README, INSTALL, SETUP, ARCHITECTURE, CLAUDE.md, docs/PLUGINS.md,
docs/MCP.md, docs/CLI.md, docs/HOOKS.md.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
16 KiB
MCP Integration Guide
Model Context Protocol (MCP) server integration for programmatic dashboard access.
Table of Contents
- Overview
- MCP Architecture
- Setup & Installation
- Available Tools
- Client Configuration
- Usage Examples
- Tool Reference
- Error Handling
- Performance
- Development
- Deployment
Overview
The Agent Dashboard MCP server exposes dashboard functionality as tools that can be used by Claude Desktop, Cline, and other MCP clients.
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
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
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
# 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 ccam plugin needs none of this: it ships the built server and
wires it up itself (see 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/ccam/.mcp.json points at
${CLAUDE_PLUGIN_ROOT}/mcp/build/index.js.
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:
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 /ccam-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/ # Compiled JavaScript (COMMITTED — see above)
│ └── .srchash # hash of mcp/src the build was produced from
├── package.json
├── tsconfig.json
└── README.md
Available Tools
Tool Catalog
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):
{
"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):
{
"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:
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:
{
"name": "get_sessions",
"arguments": {
"limit": 5
}
}
Response:
{
"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:
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:
{
"name": "create_pricing_rule",
"arguments": {
"pattern": "my-custom-model",
"input_cost_per_1m": 5.0,
"output_cost_per_1m": 20.0
}
}
Response:
{
"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:
{
limit?: number; // Max sessions to return (1-1000)
status?: 'active' | 'completed';
}
Output Schema:
{
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:
{
session_id: string; // Required
}
Output Schema:
{
session: Session;
}
Errors:
404- Session not found
get_agents
List agents for a session.
Input Schema:
{
session_id: string; // Required
}
Output Schema:
{
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:
{
agent_id: string; // Required
}
Output Schema:
{
agent: Agent;
}
Errors:
404- Agent not found
get_tools
List tool executions for an agent.
Input Schema:
{
agent_id: string; // Required
}
Output Schema:
{
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:
{} // No parameters
Output Schema:
{
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:
{
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:
{
rule: PricingRule;
}
Errors:
400- Invalid input409- Pattern already exists
delete_pricing_rule
Delete pricing rule.
Input Schema:
{
pattern: string; // Pattern to delete
}
Output Schema:
{
deleted: true;
}
Errors:
404- Pattern not found403- Cannot delete default rule
get_stats
Get dashboard statistics.
Input Schema:
{} // No parameters
Output Schema:
{
total_sessions: number;
active_sessions: number;
total_agents: number;
total_tools: number;
total_cost: number;
avg_session_cost: number;
}
Error Handling
Error Response Format
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
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
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
# 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
// 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:
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
# 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"]
# 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
# 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.