Tool Surface
Chronos exposes three MCP tools to agents: alarm_set, alarm_list, and alarm_cancel. The MCP stdio proxy (tools/chronos/chronos.mjs) mirrors the tachyon/uwac pattern: initialize/...
Chronos exposes three MCP tools to agents: alarm_set, alarm_list, and alarm_cancel. The MCP stdio proxy (tools/chronos/chronos.mjs) mirrors the tachyon/uwac pattern: initialize/tools/list are answered locally from chronos-tools.json (so an unreachable chronosd never bricks daemon boot), and tools/call is forwarded to MATRIX_CHRONOS_URL.
Source files: tools/chronos/chronos.mjs, tools/chronos/chronos-tools.json.
Design decisions
Local tool listing. The proxy answers tools/list from a baked JSON file. If chronosd is unreachable, the daemon still boots — tools just fail at call time with a clear error.
Agent-DID auth per call. The proxy mints a fresh agent-DID principal token for every tools/call by signing a challenge with the daemon's executor.key. No long-lived tokens are stored.
Strict bijection. The tool set declared in chronos-tools.json MUST exactly match the tools declared in agents/default.json and agents/neo.json. The daemon's Manager.verifyTools enforces this at boot — any mismatch is fatal (invariant i8).
The three tools
alarm_set
Schedule a new alarm.
Input:
{
"label": "daily portfolio summary",
"kind": "once",
"delay_seconds": 600,
"wake_message": "Check if the transaction confirmed on Paxeer chain 125…",
"payload": {"tx_hash": "0xABC…", "chain_id": 125},
"conversation_id": "conv_abc123",
"idempotency_key": "check-tx-0xABC"
}
For cron:
{
"label": "hourly health check",
"kind": "cron",
"cron_expr": "@hourly",
"timezone": "UTC",
"wake_message": "Run the system health check: verify RPC, check balances…",
"payload": {"check": "health", "thresholds": {}},
"idempotency_key": "health-check-v1"
}
Output:
{
"id": "uuid-of-the-alarm",
"next_fire_at": "2026-06-15T08:10:00Z",
"status": "active"
}
alarm_list
List the caller's own alarms.
Input:
{
"limit": 50
}
Output:
{
"alarms": [ … ],
"count": 3
}
alarm_cancel
Cancel one of the caller's alarms by ID.
Input:
{
"alarm_id": "uuid-of-the-alarm"
}
Output: the alarm view with status: "cancelled".
Proxy architecture
┌─────────────────────────────────────────┐
│ chronos.mjs (stdio MCP proxy) │
│ │
│ initialize() → {protocolVersion, │
│ serverInfo, │
│ capabilities} │
│ │
│ tools/list() → reads chronos-tools.json │
│ from disk (local) │
│ │
│ tools/call(name, args) │
│ │ │
│ ├── 1. Read executor.key │
│ ├── 2. POST /v1/agent/auth/ │
│ │ challenge → nonce │
│ ├── 3. Sign challenge message │
│ ├── 4. POST /v1/agent/auth/ │
│ │ verify → principal token │
│ ├── 5. POST /v1/alarms (or GET/ │
│ │ DELETE) with transport │
│ │ bearer + X-Chronos-Agent │
│ └── 6. Return result to MCP client │
└─────────────────────────────────────────┘
Selftest
node tools/chronos/chronos.mjs --selftest
The selftest is an offline drift guard. It:
- Reads
chronos-tools.json - Reads each
agents/*.jsonthat declares achronosserver - Asserts the tool sets are identical (same names, same count)
- Exits 0 on match, non-zero on mismatch
This is the same pattern as tachyon.mjs --selftest. The daemon's Manager.verifyTools runs it at boot.
Manifest wiring
agents/default.json
{
"servers": [
{
"alias": "chronos",
"command": "node /root/matrix/tools/chronos/chronos.mjs",
"env": {
"MATRIX_CHRONOS_URL": "${MATRIX_CHRONOS_URL}",
"MATRIX_CHRONOS_TOKEN": "${MATRIX_CHRONOS_TOKEN}"
}
}
]
}
agents/neo.json
Same server entry. Neo is the primary consumer — it schedules tasks via alarm_set and resumes via the wake delivery path.
skills/paxeer-assistant SKILL.mtx
The three tool URIs should be added to §TOOLS so the freeform hero path can schedule:
§TOOLS
matrix://tool/mcp/chronos/alarm_set@0.1.0
matrix://tool/mcp/chronos/alarm_list@0.1.0
matrix://tool/mcp/chronos/alarm_cancel@0.1.0
end
chronos-tools.json
The baked tool definitions:
{
"tools": [
{
"name": "alarm_set",
"description": "Schedule an alarm…",
"inputSchema": { … }
},
{
"name": "alarm_list",
"description": "List your scheduled alarms…",
"inputSchema": { … }
},
{
"name": "alarm_cancel",
"description": "Cancel a scheduled alarm by ID…",
"inputSchema": { … }
}
]
}
Exactly three tools in v1. The proxy, the manifest, and the skill must all agree on this set.
Available to
Chronos tools are available to BOTH Neo and the MCL pipeline — it's an ordinary MCP server. Any agent with a valid transport bearer + agent DID can schedule alarms. The wake always delivers into the agent's /chat endpoint, so the receiving agent must be Neo (or Neo-compatible).
Error handling at the proxy level
If chronosd is unreachable:
tools/liststill succeeds (local)tools/callfails with a clear transport error surfaced to the LLM
If the agent auth handshake fails:
- The error is surfaced to the LLM (the proxy does not retry auth)
- The LLM can decide to re-attempt or report the failure
If chronosd returns a non-2xx:
- The error code + message are passed through to the LLM
- The LLM sees structured errors (
invalid_request,not_found, etc.)
