Grounding: 2026-02-06
You are Agent Zero, the L5 Protocol Bridge for terminals.tech. Your mission is to provide high-fidelity research and deterministic structural reduction across the AXON 5-layer architecture.
- Deterministic Hashing: Every Signal/Token must be reduced to its
shapeHash(L1). - Isomorphic Transport: Data flows through bidirectional
RailsasMeshEvents(L3). - Cognitive Context: Research loops are steered by
L4 CognitiveContextandHVMcombinator bias. - Persistent Storage: Data persists to
~/Library/Application Support/zeroby default.
Research reports, jobs, and knowledge graph now persist across CLI sessions by default.
Storage Location: ~/Library/Application Support/zero (macOS)
Node 25/macOS Note: A cosmetic libc++abi: mutex lock failed error may appear on shutdown. This is harmless - data is already checkpointed before shutdown. Exit code 134 does not indicate data loss.
Environment Variables:
| Variable | Default | Description |
|---|---|---|
DB_AUTO_HEAL |
false |
Set to true to use in-memory DB (no persistence, no shutdown error) |
DB_AUTO_CLOSE |
false |
Set to true for auto-close on beforeExit |
./bin/zero status: Verify system health and resilience status../bin/zero research "query": Execute high-fidelity ensemble research.npm run stdio: Start the MCP server bridge.
CRITICAL: Always test from the end-user perspective before considering any fix complete.
When developing or debugging, adopt multiple observational viewpoints:
| Observer | Perspective | Key Questions |
|---|---|---|
| End User | CLI/MCP consumer | Does ./bin/zero research "query" work? Is output useful? |
| LLM Client | Claude/GPT using MCP | Do tools respond correctly? Are errors clear? |
| Operator | System administrator | Are logs informative? Can I diagnose failures? |
| Developer | Code maintainer | Is the fix correct? Are there edge cases? |
Before marking ANY fix complete, execute these end-user journeys:
# 1. Health Check (Operator view)
./bin/zero status
# 2. Quick Research (End User view)
./bin/zero research "simple query here"
# 3. Verify Output (LLM Client view)
# - Check report has citations
# - Check URLs are valid
# - Check confidence labels present
# 4. Error Recovery (All views)
# - What happens with no API key?
# - What happens with network failure?
# - Is the error message actionable?WRONG (testing internals only):
// This tests the function but NOT the user experience
const mesh = new UnifiedSearchMesh();
const results = await mesh.perception("query");
console.log(results.length); // "Works!"RIGHT (testing actual user journey):
# This tests what the user actually experiences
./bin/zero research "query"
# Observe: startup time, progress indicators, final output qualityWhen Claude is debugging this codebase:
- First: Run
./bin/zero statusto understand current system state - Then: Run the actual user command that's failing
- Observe: The full output including logs, timing, errors
- Only then: Dive into code to understand why
| Phase | End User Sees | LLM Client Sees | Operator Sees |
|---|---|---|---|
| Startup | Spinner/progress | JSON-RPC ready | Init logs |
| Research | Progress updates | Streaming events | Model calls |
| Completion | Formatted report | Tool result | Duration, cost |
| Error | Actionable message | Error code | Stack trace |
# Full end-to-end (End User)
./bin/zero research "WebAssembly performance 2024"
# MCP tool test (LLM Client simulation)
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"ping"}}' | npm run stdio
# Status check (Operator)
./bin/zero status
# With debug logging (Developer)
LOG_LEVEL=debug ./bin/zero research "query"This document provides Claude and other LLMs with everything needed to effectively use the OpenRouter Agents MCP server as an extension of their own capabilities.
| Tool | Purpose | Example |
|---|---|---|
ping |
Health check | {} → {"pong":true} |
get_server_status |
Full server health | {} → DB, embedder, jobs, cache status |
job_status |
Check async job | {"job_id":"job_xxx"} |
get_job_status |
Alias for job_status | Same as above |
cancel_job |
Cancel running job | {"job_id":"job_xxx"} |
| Tool | Sync/Async | Parameters |
|---|---|---|
research |
Async (default) | {"query":"...", "async":true} returns job_id |
conduct_research |
Sync | {"query":"...", "async":false} streams results |
batch_research |
Both | {"queries":[...], "waitForCompletion":true} (NEW in v1.8.1) |
agent |
Auto-routes | {"action":"research|follow_up|retrieve|query", ...} |
| Tool | Purpose | Parameters |
|---|---|---|
search |
Hybrid BM25+vector search | {"q":"...", "k":10, "scope":"both|reports|docs"} |
retrieve |
Index or SQL query | {"mode":"index|sql", "query":"...|sql":"..."} |
query |
SQL only | {"sql":"SELECT...", "params":[], "explain":true} |
get_report |
Get report by ID | {"reportId":"2", "mode":"full|summary|truncate"} |
history |
List recent reports | {"limit":10, "queryFilter":"..."} |
| Tool | Purpose | Example |
|---|---|---|
date_time |
Current timestamp | {"format":"iso|rfc|epoch"} |
calc |
Math evaluation | {"expr":"2+2*3", "precision":2} |
list_tools |
List all tools | {} |
search_tools |
Semantic tool search | {"query":"find research"} |
| Tool | Purpose | Parameters |
|---|---|---|
undo |
Undo last action | {"sessionId":"default"} |
redo |
Redo undone action | {"sessionId":"default"} |
fork_session |
Create alternate timeline | {"sessionId":"...", "newSessionId":"..."} |
time_travel |
Navigate to timestamp | {"timestamp":"2025-12-04T..."} |
session_state |
Get current state | {"sessionId":"default"} |
checkpoint |
Create named checkpoint | {"name":"before refactor"} |
| Tool | Purpose | Parameters |
|---|---|---|
graph_traverse |
Explore graph from node | {"startNode":"report:5", "depth":3, "strategy":"semantic"} |
graph_path |
Find path between nodes | {"from":"report:1", "to":"report:5"} |
graph_clusters |
Find node clusters | {} |
graph_pagerank |
Get importance rankings | {"topK":20} |
graph_patterns |
Find event patterns | {"n":3} |
graph_stats |
Get graph statistics | {} |
| Tool | Purpose | Parameters |
|---|---|---|
list_rails |
List all rails, tunnels, routes, consensus | {"includeStats":true, "filter":"active|idle|all"} |
explain_rail |
Show detailed rail/tunnel configuration | {"railId":"uuid", "verbose":false} |
list_routes |
List all defined routes | {"includePredicates":false} |
list_tunnels |
List active agent-to-agent tunnels | {} |
list_consensus |
List streaming consensus sessions | {"includeSignals":false} |
| URI | Purpose | Format |
|---|---|---|
rail://routes |
Route registry with predicates | JSON array |
rail://tunnels |
Active tunnel connections | JSON array |
rail://consensus |
Consensus sessions state | JSON array |
rail://config |
Rail configuration settings | JSON object |
The server is highly permissive with parameter formats. All of these work:
// Structured (preferred)
{"query": "AI safety research", "costPreference": "low"}
// Shorthand aliases
{"q": "AI safety research", "cost": "low"}
// Mixed
{"query": "AI safety research", "cost": "low", "async": true}| Full Name | Short Alias |
|---|---|
query |
q |
costPreference |
cost |
audienceLevel |
aud |
outputFormat |
fmt |
includeSources |
src |
images |
imgs |
textDocuments |
docs |
structuredData |
data |
1. conduct_research {"query": "...", "costPreference": "low"}
→ Streams results, returns report ID
2. get_report {"reportId": "<id>"}
→ Get full report content
1. research {"query": "...", "async": true}
→ Returns {"job_id": "job_xxx", "sse_url": "...", "ui_url": "..."}
2. job_status {"job_id": "job_xxx"}
→ Returns status, progress %, artifacts
3. (Optional) Stream SSE at sse_url for real-time updates
4. get_report {"reportId": "<id from job result>"}
1. search {"q": "previous research on X", "k": 5}
→ Returns matching indexed content
2. retrieve {"mode": "sql", "sql": "SELECT * FROM reports WHERE query ILIKE '%X%'"}
→ Direct SQL access (SELECT only)
1. research_follow_up {
"originalQuery": "...",
"followUpQuestion": "...",
"costPreference": "low"
}
→ Context-aware follow-up using prior research
For multiple research queries, use batch_research instead of dispatching individual jobs:
// Bad: N tool calls + polling loop burns tokens
const job1 = await research({q: "topic1", async: true});
const job2 = await research({q: "topic2", async: true});
// ... synchronous polling wastes context
// Good: Single call with batching
batch_research {
"queries": [
"Non-Euclidean geometry rendering techniques",
"Brainwave entrainment for digital art",
{"query": "Las Vegas Sphere technology", "costPreference": "high"}
],
"waitForCompletion": true, // Block until all complete (up to 10 min)
"timeoutMs": 600000
}
→ Returns {"success": true, "results": [...], "reportIds": ["5","6","7"]}Hybrid Options:
waitForCompletion: true- Blocks and returns all results (best for ≤5 queries)waitForCompletion: false- Returns job IDs + SSE URL for background monitoring
SSE Batch Monitoring:
GET /jobs/batch/events?ids=job_1,job_2,job_3
→ SSE stream with batch_progress and batch_complete events
Session Recovery: Batch dispatches are tracked in session state. Query via:
session_state {"sessionId": "default"}
→ state.batchJobs contains pending/completed batches with reportIds// Get task details
task_get {"taskId": "job_xxx"}
// Get task result
task_result {"taskId": "job_xxx"}
// List all tasks
task_list {"limit": 20, "cursor": "..."}
// Cancel task
task_cancel {"taskId": "job_xxx"}sample_message {
"messages": [{"role": "user", "content": "What is 2+2?"}],
"model": "google/gemini-3-pro-preview",
"maxTokens": 1000
}Note: Enables server-side agentic loops using client sampling.
elicitation_respond {
"requestId": "elicit_xxx",
"response": {"field": "value"}
}-- Research reports
SELECT id, query, cost_preference, audience_level, final_report,
created_at, rating, rating_comment
FROM research_reports;
-- Async jobs
SELECT id, type, status, params, result, progress,
created_at, started_at, finished_at
FROM jobs;
-- Job events (for SSE streaming)
SELECT id, job_id, event_type, payload, ts
FROM job_events;
-- Vector index (hybrid search)
SELECT id, source_type, source_id, title, content, embedding
FROM doc_index;// Recent reports with ratings
query {"sql": "SELECT id, query, rating FROM research_reports ORDER BY created_at DESC LIMIT 10"}
// Job statistics
query {"sql": "SELECT status, COUNT(*) FROM jobs GROUP BY status"}
// Search indexed documents
search {"q": "MCP protocol", "k": 5, "scope": "docs"}| Error | Cause | Solution |
|---|---|---|
Report ID undefined not found |
Missing or malformed reportId | Ensure {"reportId": "2"} (string) |
query is required when mode="index" |
Missing search query | Provide {"q": "..."} or {"query": "..."} |
Invalid characters (calc) |
Missing expression | Provide {"expr": "2+2"} |
Job unknown: Not found |
Invalid job ID or job expired | Jobs have 1-hour TTL |
- Always provide required parameters - the server will error on missing params
- Use string types for IDs (
"2"not2) - Check
get_server_statusfor current server state before complex operations
// From src/config/constants.js - embedding-routed selection
HIGH_COST: ["anthropic/claude-sonnet-4.5", "anthropic/claude-opus-4.6", "openai/gpt-5.2-chat", "openai/gpt-5.3-codex", "google/gemini-3-pro-preview", "qwen/qwen3-coder"]
LOW_COST: ["google/gemini-3-flash-preview", "anthropic/claude-haiku-4.5", "deepseek/deepseek-chat-v3.1", "deepseek/deepseek-v3.2", "openai/gpt-oss-120b"]
VERY_LOW_COST: ["openai/gpt-5-nano"]
PLANNING_MODEL: "google/gemini-3-flash-preview" // Fast planning"high": Uses premium models, better quality, higher token cost"low": Uses efficient models, good quality, lower cost (default)
get_server_status {}
// Verify: database.initialized, embedder.ready, jobs counts// For queries expecting >30s processing:
research {"query": "comprehensive analysis of...", "async": true}
// Then poll: job_status {"job_id": "..."}// Before new research, check existing:
search {"q": "topic of interest", "k": 5}
// Reuse or reference existing reports// Always extract report ID from job result:
const jobResult = await job_status({job_id});
const reportId = jobResult.match(/Report ID: (\d+)/)?.[1];
if (reportId) await get_report({reportId});// Preferred: explicit parameter names
{"query": "research topic", "costPreference": "low", "outputFormat": "report"}
// Avoid: ambiguous single values (may work but less reliable)
"research topic"The server operates in one of three modes (set via MODE env var):
| Mode | Available Tools |
|---|---|
AGENT |
agent + always-on tools only |
MANUAL |
Individual tools (research, search, query, etc.) + always-on |
ALL |
Everything (default) |
Always-on tools (available in all modes): ping, get_server_status, job_status, get_job_status, cancel_job
- Server responding:
ping {}returnspong - Database initialized:
get_server_statusshowsdatabase.initialized: true - Embedder ready:
get_server_statusshowsembedder.ready: true - API key configured:
OPENROUTER_API_KEYset in environment - Correct mode: Check
MODEmatches your use case
When the server updates, check:
docs/CHANGELOG.md- Feature additionsdocs/MCP-COMPLIANCE-REPORT.md- Spec compliance statuslist_tools {}- Current tool inventory
- Server Version: 2.0.0
- MCP SDK: 1.27.1
- Zod: 4.x (upgraded from 3.x)
- Express: 5.x (upgraded from 4.x)
- MCP Spec: 2025-11-25 (stable, under AAIF/Linux Foundation governance)
- Transport: Streamable HTTP (primary), SSE (deprecated legacy)
- Circuit Breaker: Integrated for model API fault tolerance
- Protocol Features: Task Protocol (SEP-1686), Sampling (SEP-1577), Elicitation (SEP-1036), MCP Apps (SEP-1865), Enterprise Auth (SEP-990), Client Metadata (SEP-991)
- Zero Protocol: Self-referential MCP architecture (
zero://URI scheme, dual-role nodes) - Package Integrations: @terminals-tech/embeddings, @terminals-tech/graph, @terminals-tech/core
| Feature | Spec Version | Status |
|---|---|---|
| JSON-RPC 2.0 | Core | Compliant |
| Tools/Resources/Prompts | 2025-11-25 stable | Compliant |
| Task Protocol (SEP-1686) | 2025-11-25 stable | Compliant |
| Sampling with Tools (SEP-1577) | 2025-11-25 stable | Compliant |
| Elicitation (SEP-1036) | 2025-11-25 stable | Compliant |
| MCP Apps (SEP-1865) | 2025-11-25 stable | Compliant |
| Enterprise Auth (SEP-990) | 2025-11-25 stable | Compliant |
| Client Metadata (SEP-991) | 2025-11-25 stable | Compliant |
The server includes a unified core abstraction layer for advanced use cases:
Unified message type for inter-agent communication with confidence scoring and crystallization analysis.
const { Signal, SignalBus, ConsensusCalculator } = require('./src/core');
// Create signals
const query = Signal.query("What is quantum computing?", "claude");
const response = Signal.response(answer, "gemini", 0.95);
// Calculate consensus from multiple model responses
const calc = new ConsensusCalculator({ minAgreement: 0.6 });
const consensus = calc.calculate([signal1, signal2, signal3]);Research results now generate Signal objects for multi-model consensus:
- Each model response creates a Signal with confidence scoring
- Signals are collected during research iterations via
allSignals.push() - Persisted to
ensemble_signalsJSONB column in reports table - Retrieved for CLI verification:
dbClient.getReportSignals(reportId)
Events emitted:
model_signal- Individual model signal created (per model response)ensemble_signals- Batch of signals collected per iteration
Data flow:
ResearchAgent._executeSingleResearch() → Signal.response()
↓
tools.conductResearch() → allSignals collection
↓
dbClient.saveResearchReport({ensembleSignals}) → DB
↓
CLI: getReportSignals(reportId) → verification.verify({signals})
Seamless inter-agent communication with backpressure, provenance tracking, and consensus.
const { Rail, Token, tokenFromSignal, signalFromToken } = require('./src/core/rail');
// Wrap signals with provenance tracking
const token = tokenFromSignal(signal);
console.log(token.trace); // ['ResearchAgent:agent-1', 'ConsensusCalculator']
// Create connected rail pairs for bidirectional communication
const [sender, receiver] = Rail.pair();
await sender.send(token);
for await (const msg of receiver.receive()) {
console.log(msg.value, msg.origin, msg.trace);
}Core Components:
- Token: Unit of data with provenance (id, value, origin, trace)
- Rail: Lazy bidirectional channel with backpressure (
send,receive,pause,resume) - Switch: Dynamic routing based on predicates
- Ok/Err: Railway-oriented error handling (no exceptions)
Advanced Features:
- Tunnel (
TunnelRegistry): Agent-to-agent message passing with TTL and acknowledgments - StreamingConsensus (
ConsensusManager): Real-time multi-model agreement calculation - Routes (
RouteRegistry): User-definable routing predicates for model selection - Pipeline (
PipelineBuilder): DAG-based stage execution with automatic parallelism
Events emitted:
notifications/rail.tunnel- Agent-to-agent message flownotifications/rail.consensus- Streaming consensus updates
Deterministic error classification with auto-semanticization for runtime learning and circuit breaker integration.
const {
classify, wrapError, recordTrace, getTraces,
learnPattern, exportTaxonomyState
} = require('./src/core/errors');
// Classify any error
const classification = classify(new Error('Connection refused'));
// → { category: 'network', severity: 'error', pattern: 'ECONNREFUSED|...' }
// Wrap with semantic classification
const semantic = wrapError(error);
console.log(semantic.category); // 'rate_limit'
console.log(semantic.tripDecision); // { decision: 'trip', reason: '...' }
// Record for debugging (persisted to PGlite)
await recordTrace(error, { context: 'research' }, sessionId);
// Learn new patterns at runtime
await learnPattern('custom.*error', 'execution', 'error', { source: 'manual' });
// Export full state for AI-assisted improvement
const state = exportTaxonomyState();
// → { stats, learnedPatterns, recentTraces, categories, severities, ... }Error Categories:
| Category | Description | Circuit Action |
|---|---|---|
network |
Connection failures | Trip after 3 in 1 min |
rate_limit |
API throttling (429) | Always trip |
service_unavailable |
5xx errors | Trip after 3 in 1 min |
auth |
401/403, invalid keys | Escalate (fatal) |
config |
Misconfiguration | Escalate (fatal) |
validation |
Bad input params | Ignore |
schema |
Schema validation | Ignore |
timeout |
Operation timeout | Trip after 5 in 1 min |
resource |
Memory/disk exhaustion | Escalate (fatal) |
execution |
Model execution failures | Trip after 3 in 1 min |
not_found |
404 errors | Ignore |
logic |
Application logic | Warn |
unknown |
Auto-semanticized | Warn, trip after 10 |
Trip Decisions:
trip: Close the circuit breakerwarn: Log but don't tripignore: Transient, ignoreescalate: Alert operator (fatal)
DB Tables Created:
error_patterns: Learned patterns with hit countserror_trace: Full error trace history with suggested fixes
Self-Improvement Flow:
Error → classify() → recordTrace() → auto-learn pattern → DB persist
↓
AI reads exportTaxonomyState()
↓
AI suggests fix or new pattern
↓
learnPattern() → next error classified correctly
Declarative alias system for flexible tool parameter handling.
Centralized Zod schemas with composable building blocks.
Bidirectional communication enabling server → client requests via sampling/elicitation.
| Variable | Default | Description |
|---|---|---|
CORE_HANDLERS_ENABLED |
false |
Enable new consolidated handlers |
SIGNAL_PROTOCOL_ENABLED |
false |
Enable Signal protocol |
ROLESHIFT_ENABLED |
false |
Enable bidirectional protocol |
STRICT_SCHEMA_VALIDATION |
false |
Enforce strict schema validation |
RAIL_ENABLED |
true |
Enable Rail Protocol (default on) |
RAIL_DEBUG |
false |
Enable Rail debug logging |
RAIL_DEBUG_RAILS |
"" |
Comma-separated rail IDs to debug |
The server uses a structured logging system with MCP SDK integration for proper channel semantics.
| Variable | Default | Description |
|---|---|---|
LOG_LEVEL |
info |
Minimum log level: debug, info, warn, error |
LOG_OUTPUT |
stderr |
Output mode: stderr, mcp, both |
LOG_JSON |
false |
Enable JSON format for log aggregation |
stderr: Traditional stderr logging (compatible with all clients)mcp: Use MCP SDKsendLoggingMessage()notifications (client can filter by level)both: Output to both channels
| Level | Usage |
|---|---|
debug |
Detailed diagnostic info (disabled by default) |
info |
General operational messages |
warn |
Degraded functionality, non-fatal issues |
error |
Operation failures, exceptions |
When using --stdio transport, non-error logs are automatically suppressed to prevent JSON-RPC protocol corruption. Only errors are written to stderr.
The server declares ui:// resources that clients can render as interactive UI components:
| URI | Purpose | Linked Tools |
|---|---|---|
ui://research/viewer |
Interactive report viewer | research, get_report, research_follow_up |
ui://knowledge/graph |
Force-directed graph explorer | search, graph_traverse, graph_clusters |
ui://timeline/session |
Session timeline with undo/redo | history, undo, redo, time_travel |
// Read a UI resource to get HTML template
read_resource {"uri": "ui://research/viewer"}
// Returns HTML with embedded JSON-RPC bridge for tool calls
// The UI communicates via postMessage JSON-RPC:
window.parent.postMessage({
jsonrpc: '2.0',
id: 1,
method: 'tools/call',
params: { name: 'get_report', arguments: { reportId: '5' } }
}, '*');- Upgrade
@modelcontextprotocol/sdkfrom 1.24.3 → 1.27.1 - Address security fix for shared server/transport instances (v1.26.0)
- Fix ReDoS in UriTemplate regex patterns (v1.25.2 backport)
- Validate client credentials provider scope support
- Migrate to registerTool/registerPrompt/registerResource APIs
- Zod 3 → 4 migration (z.record() syntax, config schema fixes)
- Express 4 → 5 (path pattern changes, req.query handling)
- SSE transport deprecated, Streamable HTTP is primary
- Circuit breaker implemented for model API fault tolerance
- Track
@modelcontextprotocol/sdkv2 RC releases (split packages not yet on npm) - Test against v2 RC when available
- Add
anthropic/claude-opus-4.6to HIGH_COST tier - Add
openai/gpt-5.3-codexto HIGH_COST tier - Add
deepseek/deepseek-v3.2to LOW_COST tier - Update embedding routing profiles for new models
- Refresh MODEL_WEIGHTS consensus scores
- Update spec references from "draft" to "stable" (2025-11-25)
- Track AAIF working group outputs for domain extensions
- Evaluate industry-specific protocol extensions (if applicable)
- Publish
@terminals-tech/openrouter-agents@2.0.0with SDK 1.27.1 - Update README model lists and version references
- Ensure postinstall verification works on Node 25.x
- Clean up duplicate files from git status (
2suffixed files)
| File | Purpose |
|---|---|
docs/TOOL-PATTERNS.md |
Detailed tool patterns, gotchas, and error recovery |
docs/CHANGELOG.md |
Version history and feature additions |
docs/MCP-COMPLIANCE-REPORT.md |
MCP specification compliance status |
docs/TESTING-GUIDE.md |
Comprehensive testing procedures |
.claude/commands/ |
Slash commands for common workflows |
.claude/settings.json |
Pre-configured tool permissions |
/mcp-status - Check server health and recent activity
/mcp-research - Run a research query (sync)
/mcp-async-research - Run research asynchronously
/mcp-search - Search the knowledge base
/mcp-query - Execute SQL query
| Symptom | Check | Action |
|---|---|---|
| Tools not responding | ping {} |
Restart server |
| Research fails | get_server_status |
Check OPENROUTER_API_KEY |
| Embeddings slow | embedder.ready |
Wait for model load |
| Job not found | task_list {} |
Jobs expire after 1hr |
| Report not found | history {} |
Check valid report IDs |