2 — Plugin: The Colibri Server
After a mutation enters through the transport (Section 1), it lands inside a stateful server plugin. Colibri is an MCP plugin implemented in TypeScript. It:
- Boots in a specific sequence to ensure the transport connects before the client times out.
- Opens a single SQLite database file (
data/colibri.db) that holds all state — tasks, audit trails, Merkle proofs, decision logs. - Runs in one of 4 runtime modes that determine which tools are available.
- Shuts down gracefully when signaled, draining in-flight requests and flushing the database.
This section is an overview. For detailed specs, see the linked sub-pages.
The Server at a Glance
┌─────────────────────────────────────┐
│ Colibri MCP Server (Node.js) │
│ TypeScript 5.3+ · ESM │
├─────────────────────────────────────┤
│ Transport Layer │
│ @modelcontextprotocol/sdk/stdio │
├─────────────────────────────────────┤
│ 5-Stage α Middleware Chain │
│ tool-lock → validate → audit → │
│ dispatch → audit-exit │
├─────────────────────────────────────┤
│ Domain Handlers (β ζ η ε α γ) │
│ Task Pipeline · Audit · Proof · │
│ Skill Registry · System │
├─────────────────────────────────────┤
│ SQLite Database (WAL mode) │
│ data/colibri.db · single writer │
└─────────────────────────────────────┘
Every tool call flows top-to-bottom through this stack. The database is the single source of truth.
Runtime Modes
The server operates in one of 4 modes, selected via the COLIBRI_MODE environment variable at startup. Different modes enable different tool subsets:
| Mode | Accepts | Use case |
|---|---|---|
FULL |
All 14 shipped tools | Normal operation after boot completes |
READONLY |
Read-only tools (task_list, task_get, skill_list, server_health, server_ping, thought_record_list) | Maintenance, audit review, diagnostics |
TEST |
All 14 shipped tools with deterministic random seed | Integration tests, scenario walkthroughs |
MINIMAL |
Only server_ping, server_health | Emergency, diagnostics, or bootstrap failure recovery |
Mode is frozen at startup. It cannot be changed in-flight. If you need a different mode, restart the server with a different COLIBRI_MODE setting.
Boot Sequence
The server boots in a specific 6-step sequence. Understanding this sequence is critical because it explains why transport connects first:
- Create Server — instantiate the MCP server object.
- Register Handlers — register the 19 tool handlers (β task, ζ audit, η proof, ε skill, α/γ system).
- Connect Transport — connect the stdio transport before opening the database. This ensures the client handshake completes immediately and the client does not time out.
- Resolve initReady — wait for MCP
initializehandshake to complete. - Load Database — open
data/colibri.dbwithbetter-sqlite3in WAL mode. Run migrations. All tool calls arriving before this step queue behind a Promise gate. - Load Domains — parse
.agents/skills/, register theskill_listtool, start the 30-second health check loop.
At this point the server enters FULL mode (or the mode specified by COLIBRI_MODE) and begins dispatching queued tool calls.
Why transport first? The client sends a tool initialization request immediately after opening the connection. If the database is not yet open, that request could arrive before the database is ready. By connecting transport before opening the database, we guarantee the handshake succeeds. Tool calls that arrive before the database is ready are queued and executed once the database is available. This prevents the client from timing out during boot.
For detailed boot steps and the Promise gate behavior, see Boot Sequence.
Database
All state lives in a single data/colibri.db file opened via better-sqlite3. The database:
- Uses WAL (Write-Ahead Log) mode for durability and concurrent read access without blocking writers.
- Is accessed by one process writer — the Colibri server. No horizontal scaling in Phase 0.
- Has three load-bearing tables for Phase 0:
thought_records(ζ decision trail),merkle_nodes(η proof tree), andaudit_events(every tool call). - Does not yet exist — it is a Phase 0 target (P0.2.2). When the server boots, it creates the database file and runs migrations.
Every mutation eventually writes to this database. A task lives in tasks. A decision record lives in thought_records. A Merkle leaf lives in merkle_nodes. If it is not in the database, it did not happen.
For schema details and the relationship between domains, see Database: data/colibri.db.
Middleware Chain
Between the transport boundary and the domain handler sits the 5-stage α middleware chain:
- Tool lock — serialize execution. Only one handler runs at a time per process.
- Schema validate — parse the request against Zod. Reject before the handler sees it.
- Audit enter — write the call to the decision trail with a monotonic sequence number.
- Dispatch — route the tool name to its handler.
- Audit exit — record the outcome and result hash.
The chain is ordered. Reordering is a breaking change. No other middleware (ACL, rate limits, reputation checks) is inserted in Phase 0 — they arrive with their concepts in Phase 1+.
For chain details, implementation targets, and the contract each stage enforces, see middleware.md (spec).
Health Checks
Every 30 seconds, the server runs a health check:
- Sample SQLite
PRAGMA integrity_check(not every second, to avoid disk churn). - Check memory usage against configured threshold.
- Measure event loop latency.
- Verify watcher status (if any file watchers are running).
- Check task queue depth.
If a health check fails, the server transitions to SAFE_MODE or DIAGNOSE depending on the failure. In SAFE_MODE, only read-only tools are accepted. This prevents corrupted or overloaded state from being written.
Graceful Shutdown
When the server receives SIGTERM or SIGINT (e.g., kill <pid>), it initiates a 6-stage shutdown:
- Announce — log that shutdown is starting.
- Quiesce router — stop accepting new tool calls.
- Drain tasks — wait for in-flight calls to complete (30-second timeout).
- Finalize proof — complete any pending Merkle operations.
- Close database — flush and close the SQLite connection.
- Release lock — clean up process-level locks.
Finally, the server exits with code 0 (success). If something goes wrong during shutdown, the exit code may be 1 (generic error), 73 (config), or 75 (resource).
Also: global handlers for unhandledRejection and uncaughtException trigger graceful shutdown instead of a crash. This ensures the database is closed cleanly even if the server encounters an unexpected error.
For exit codes and the full 6-stage drain, see Boot Sequence.
Navigation
This section (2 — Plugin) has four sub-pages:
| Page | Purpose |
|---|---|
| Boot Sequence | The 6-step boot, why transport connects first, Promise gate, domains load order, exit codes |
| Database: data/colibri.db | SQLite WAL mode, single-writer, earned tables, schema relationships |
| Middleware | 5-stage α chain details, validation boundaries, audit contract |
| Runtime Modes | FULL / READONLY / TEST / MINIMAL mode specs and switching (future) |
See Also
- 1 — Transport: How Mutations Enter the World — the MCP layer above this
- α System Core — the server’s middleware contract
- γ Server Lifecycle — boot and shutdown as a concept
- colibri-system.md — canonical vision