MCP server
The read-only MCP (Model Context Protocol) server exposed by tadoru mcp — tools, schemas, errors, and registration.
tadoru mcp exposes recorded activity to MCP clients — terminal agents (Claude Code, Codex, opencode, Hermes) and, crucially, shell-less clients (Claude Desktop, ChatGPT connectors) for which MCP is the only path.
| Server name | tadoru |
| Start command | tadoru mcp |
| Transport | stdio only (JSON-RPC 2.0 over stdin/stdout) — zero network surface |
| Capabilities | tools only; resources / prompts are not provided yet (see future) |
Role and boundary. The server is a read-only view over the local store. It runs as an independent process from the recording daemon and cannot start or stop recording, and cannot change configuration — including the capture-time filters. The app / bundle_id arguments of query_events are query-time narrowing over already-stored data, not a privacy boundary.
Shared conventions — time expressions, event types, the event envelope — match the CLI reference and event reference.
Tools
All three tools are read-only and side-effect free.
get_timeline
Returns the LLM-ready timeline for a range. Equivalent to tadoru timeline. The tool agents call most.
Input
| Parameter | Type | Default | Description |
|---|---|---|---|
since |
string | "1h" |
Start of range; relative (15m, 2h, 1d) or RFC3339 |
until |
string | now |
End of range |
format |
"markdown" or "structured" |
"markdown" |
Output shape |
token_budget |
integer | 4000 |
Approximate token cap; content is coarsened to fit |
granularity |
"coarse" or "fine" |
"coarse" |
Session-level or per-interaction |
Output (format: "markdown")
{
"range": { "since": "2026-08-16T08:00:00Z", "until": "2026-08-16T09:00:00Z" },
"format": "markdown",
"content": "## 08:12-08:31 Safari — Reviewing PR #42\n- Viewed 3 files on GitHub\n- Wrote 2 comments\n...",
"token_estimate": 3810,
"truncated": false
}
With format: "structured", the result matches the sessions[] structure of timeline --format json in the CLI reference.
query_events
Returns raw events matching the given conditions. Equivalent to tadoru query. For fine-grained verification or extracting specific apps/types.
Input
| Parameter | Type | Default | Description |
|---|---|---|---|
since |
string | "15m" |
Start of range |
until |
string | now |
End of range |
types |
string[] | — | Event types; wildcards like browser.* allowed |
app |
string | — | Filter by app name |
bundle_id |
string | — | Filter by bundle ID |
limit |
integer | 200 (max 1000) |
Max events |
Output
{
"range": { "since": "2026-08-16T08:45:00Z", "until": "2026-08-16T09:00:00Z" },
"count": 42,
"truncated": false,
"events": [
{
"v": 1, "id": "evt_01J...", "ts": "2026-08-16T08:46:12.120Z", "mono_ns": 128374651234,
"source": "macos.applescript", "type": "browser.navigate",
"app": { "name": "Google Chrome", "bundle_id": "com.google.Chrome", "pid": 501 },
"window": { "title": "PR #42", "id": 42 },
"element": null,
"data": { "url": "https://github.com/...", "tab_title": "PR #42", "mode": "normal" },
"redaction": { "applied": false, "rules": [] }
}
]
}
truncated: true when limit was exceeded. Event contents are already privacy-processed at capture time (secure-field exclusion, redaction) — the MCP layer never adds back anything that was excluded.
get_status
Lightweight check for agents: is recording running? A subset of tadoru status.
Input: none ({})
Output
{
"running": true,
"paused": false,
"last_event_ts": "2026-08-16T08:59:58Z",
"events_dropped": 2,
"degraded": { "eventtap": "1 degraded collector operation observed" },
"retention_hours": 48,
"oldest_event_ts": "2026-08-14T09:00:00Z",
"capture": { "sources": ["app", "window", "ui", "input", "browser"], "text_content": false },
"permissions_ok": true
}
| Field | Meaning |
|---|---|
events_dropped |
Cumulative events dropped by collectors or the recording pipeline |
degraded |
Collector or runtime degradation by component; empty when no degradation is known |
permissions_ok |
The running recorder’s own permission result while its heartbeat is fresh; a local probe when no recorder is running |
An agent that sees running: false can tell the user that activity is not currently being recorded, instead of answering from nothing.
Errors
| Situation | Behavior |
|---|---|
| Store not initialized / nothing recorded yet | get_status returns running: false (not an error); get_timeline / query_events return empty results |
| Empty because of missing permissions | Empty results; distinguishable via permissions_ok: false in get_status |
| Invalid arguments (bad time expression, …) | JSON-RPC error (invalid params) |
The MCP server never starts recording or grants permissions. The resolution path for permission problems is the CLI: tadoru doctor (the shipped skills say so explicitly).
Registration
tadoru setup --agent <name> automates all of the below — see agent setup for per-client walkthroughs. The generic stdio-server JSON shape:
{
"mcpServers": {
"tadoru": {
"command": "tadoru",
"args": ["mcp"]
}
}
}
Future extensions
Not provided today; all would sit on the same read-only store view, and write access to the recording daemon will never be added:
- resources — the recent timeline and current configuration as read-only MCP resources (some clients prefer resources over tools).
- prompts — a server-provided “resume my work” prompt template.
- notifications — push on new-session detection, if demand appears.
Security boundary
- The server itself performs no external transmission (stdio only, no network surface).
- Once an agent receives tool results, forwarding them to an LLM provider is the agent’s responsibility. Given the sensitivity of activity history, the shipped skill files instruct agents to narrow the requested range before sending.
- Stored content is privacy-processed at capture time; with the default
text_content: false, typed content simply does not exist in the store.