A specification-building tool that models specs as directed acyclic graphs (DAGs) of questions and answers. Uses entropy-based prioritization to surface the most impactful unanswered questions first.
1. Start the server
cargo run --release2. Start the web UI (in a separate terminal)
cd web
npm install
npm run build
npm run previewThe server runs at http://localhost:8080 and the web UI at http://localhost:4173.
- Rust toolchain (1.80+)
- Node.js (v18+) for the web UI
cargo build --releaseThe binary is at target/release/spec-forest.
# Default: SQLite at ~/spec-forest/spec-forest.db, HTTP on 127.0.0.1:8080
cargo run --release
# Custom database path and port
cargo run --release -- --db-path /path/to/spec-forest.db --port 3000 --host 0.0.0.0The server exposes two interfaces on a single HTTP port:
- MCP at
/mcp— Streamable HTTP transport for Claude Code and other MCP clients - REST API at
/api/— read-only endpoints for the web UI
claude mcp add --transport http spec-forest --scope project http://127.0.0.1:8080/mcpOr add globally with --scope global to use across all projects.
| Tool | Description |
|---|---|
create_spec |
Create a new specification |
seed_spec |
Initialize a spec with a root document |
list_specs |
List all specifications |
get_spec_summary |
Get spec metadata and node counts |
get_next_question |
Get the highest-entropy unanswered question |
answer_question |
Answer a question |
add_children |
Create child questions under a node |
update_answer |
Update an existing answer |
trigger_review |
Mark descendants for review |
get_review_candidate |
Get next node needing review |
delete_node |
Soft-delete a node and its descendants |
get_ancestors |
Get ancestor chain to root |
get_descendants |
Get subtree from a node |
search_nodes |
Semantic search across nodes |
From the repo root, run:
cd web
npm install
npm run build
npm run previewOpens at http://localhost:4173. Requires the backend running on port 8080.
The sync server (spec-forest-sync) enables real-time collaborative editing of specs across multiple clients via WebSocket.
# Start with a persistent JWT secret (recommended)
cargo run --release -p spec-forest-sync -- --jwt-secret "your-secret-here"
# Or via environment variable
JWT_SECRET="your-secret-here" cargo run --release -p spec-forest-sync
# Custom port and host
cargo run --release -p spec-forest-sync -- --jwt-secret "secret" --port 9090 --host 0.0.0.0
# Custom token lifetime (default: 86400 seconds / 24 hours)
cargo run --release -p spec-forest-sync -- --jwt-secret "secret" --token-lifetime 3600If no secret is provided, a random one is generated at startup (tokens won't survive restarts).
The sync server listens on 127.0.0.1:9090 by default and stores its database at ~/.spec-forest/sync.db.
All sync server interactions use JWT-based authentication over WebSocket.
1. Connect and register/login
Connect to ws://<host>:<port>/ws without a token. Send a Register or Login message:
{"type": "register", "username": "alice", "password": "secret123"}{"type": "login", "username": "alice", "password": "secret123"}The server responds with an AuthToken:
{"type": "auth_token", "token": "eyJ..."}2. Reconnect with token
On subsequent connections, pass the JWT as a query parameter to skip the login step:
ws://localhost:9090/ws?token=eyJ...
Invalid tokens are rejected with a 401 before the WebSocket handshake completes.
3. Token refresh
The server sends a TokenExpiring warning when the token has less than 20% of its lifetime remaining. The client should re-authenticate via Login to get a fresh token.
- Specs use binary access control: full read-write or no access
- The user who creates a spec is its owner and the only one who can manage the access list
- Owners grant/revoke access via WebSocket messages:
{"type": "grant_access", "spec_name": "my-spec", "username": "bob"}
{"type": "revoke_access", "spec_name": "my-spec", "username": "bob"}ListSpecsonly returns specs the authenticated user has access to- Revoking access immediately disconnects affected users
Start the main spec-forest server with --sync-url to configure a sync server:
cargo run --release -- --sync-url ws://localhost:9090/wsThe sync connection is deferred until credentials are unlocked. The main server stores credentials in an encrypted SQLite database at ~/.spec-forest/cred.db (protected by a master password using SQLCipher/AES-256).
Unlock credentials via the REST API:
# Unlock the credential store (also connects to the sync server)
curl -X POST http://localhost:8080/api/credentials/unlock \
-H 'Content-Type: application/json' \
-d '{"master_password": "my-master-pass"}'
# Store sync server credentials
curl -X POST http://localhost:8080/api/credentials/store \
-H 'Content-Type: application/json' \
-d '{"server_url": "ws://localhost:9090/ws", "username": "alice", "password": "secret123"}'
# Check connection status
curl http://localhost:8080/api/statusFailed login attempts trigger per-username exponential backoff (2^n seconds, capped at 15 minutes). Successful login resets the counter.