Skip to main content
Glama
urcades

context-archive

by urcades

Context Archive

A local macOS evidence archive for agents: Knowledge activity and the history databases of Safari, Chrome, Dia, and Arc, behind one CLI and read-only stdio MCP.

The collector preserves every observed table, column, blob, and revision in the selected databases. Searchable events are rebuildable interpretations with links to original rows. It does not capture screenshots, keystrokes, page contents, cookies, or passwords. Collection uploads nothing, opens no listening port, and requires no model or API key.

Install

Requires macOS. Clone and install:

git clone https://github.com/urcades/context-archive.git && cd context-archive && ./install.sh

Or, from an existing checkout:

./install.sh

The installer uses uv, managed Python 3.12, and the committed dependency lock. It installs a non-editable runtime outside the checkout and a per-user LaunchAgent for five-minute collection. Installation starts collection unless --no-start is passed. The project source alone does not start anything.

./install.sh --no-start       # prepare runtime and disabled LaunchAgent
./install.sh --codex          # also register the stdio MCP with Codex

Runtime, configuration, archive, and logs live under ~/Library/Application Support/ContextArchive/. Set CONTEXT_ARCHIVE_HOME to an absolute alternate directory. The original Knowledge Archive and Browser History Receiver repositories/services are independent and are not changed.

Full Disk Access may be required for Safari and Knowledge. The installer/doctor prints the permission executable: grant it access under System Settings → Privacy & Security → Full Disk Access. The actual launch command retains the virtual-environment executable so dependencies remain available. An interpreter upgrade may require a new grant.

CA_ROOT="${CONTEXT_ARCHIVE_HOME:-$HOME/Library/Application Support/ContextArchive}"
CA="$CA_ROOT/runtime/bin/context-archive"
"$CA" doctor
"$CA" status

Interactive readability is not proof of background access. After intentionally starting the service, inspect launchctl print gui/$(id -u)/local.context-archive, the collector logs, and a newly successful capture in status. An idle service between scheduled runs is normal. Missing/unreadable profiles and failed captures appear in diagnostics.

Related MCP server: memory-bank-mcp

Preserve existing archives first

Import Knowledge history before starting native collection for the matching store. Its old observation history cannot safely be appended behind newer native snapshots; that case is rejected atomically with an explanation. Existing input archives are read-only and remain untouched.

./install.sh --no-start
"$CA" import knowledge "/path/to/KnowledgeArchive/data/archive.sqlite3"
"$CA" import browser "/path/to/browser-history.jsonl"
"$CA" install             # explicitly enable background collection after imports

Knowledge imports retain schemas, revisions, blobs, deletions, observation times, and legacy ID mappings. Repeating an import adds only unseen captures. A matching native Knowledge store continues the same evidence history.

Browser JSONL imports retain every original line, including malformed lines and unknown fields. Malformed lines are reported; valid records are still imported. Identical visits on different lines remain distinct. Reimporting the same artifact adds no visits; a verified complete-line prefix of an appended file at the same path is reused. Rewritten/truncated files or an extended unterminated line form a separate artifact lineage. Such imported records may overlap: provenance remains explicit rather than guessed away.

Legacy browser records contain selected visit fields, not full source databases. legacy_visit fidelity distinguishes these from new raw_database captures. The old receiver did not persist all origin metadata; imports do not invent it.

Discovery and configuration

Each cycle discovers Knowledge and browser profiles again. Safari includes its main history database and supported Safari/container profile roots; Chromium-family browsers include all profiles containing a history database under their known roots. Full profile identifiers survive discovery. Canonical aliases are deduplicated. Sources found only after an earlier visit timestamp are still fully scanned, so older history arriving through Safari sync is captured.

Optional config.json under the runtime root:

{
  "sources": [
    {"path": "/absolute/path/to/History.db", "adapter": "safari", "browser": "Safari", "profile": "custom-profile"}
  ],
  "exclusions": ["/absolute/path/to/excluded-profile/*"]
}

Explicit paths supplement discovery. Supported adapters are knowledge, safari, and chromium; legacy_browser is used by imports. Exclusions match absolute path globs. Use --config /absolute/config.json for a separate configuration.

Safari sync can bring mobile browsing into these local databases. Completeness means everything observed in readable databases on this Mac, not a guarantee of complete iCloud history or all mobile activity. Raw origin metadata is preserved without claiming it identifies an iPhone, iPad, or this Mac. Private browsing, upstream retention, sync delays, and missed capture windows remain outside that guarantee.

CLI and MCP

All query commands emit JSON and accept --params with the same arguments as the corresponding MCP tool. --archive PATH and --config PATH can precede or follow the command. Command --help lists the underlying parameter signature.

"$CA" capture
"$CA" timeline --params '{"start":"2026-01-01T00:00:00Z","end":"2026-01-02T00:00:00Z","limit":50}'
"$CA" search --params '{"text":"research","browser":"Safari"}'
"$CA" get-record --params '{"version_id":1}'
"$CA" app-usage-summary --params '{"start":"2026-01-01T00:00:00Z","end":"2026-01-02T00:00:00Z"}'
"$CA" domain-summary --params '{"fidelity":"raw_database"}'
"$CA" query --params '{"sql":"SELECT table_name, count(*) FROM records GROUP BY table_name"}'
"$CA" mcp

MCP tool

Result

status, sources

Capture health, discovery failures, source identities and fidelity

timeline, search

Source-linked events; indexed literal-token search over URL, title, domain, app and kind

get_record, get_blob

Original row fields and binary values in bounded chunks

decode_blob

Bounded plist/JSON interpretation; original bytes remain retrievable

describe_schema, query

Captured schemas and bounded read-only SQL

app_usage_summary, domain_summary

Recorded/union app intervals and recorded domain visits

Timeline and search support time range, source, profile, browser, kind, exact URL/domain/app, fidelity, and provenance filters. Ranges require explicit UTC offsets; end times are exclusive. Pagination returns an opaque next_cursor; repeat the same filters to continue. Each cursor fixes an observation boundary so newly collected data cannot shift its results. Reindexing invalidates outstanding cursors.

Events expose their original-record references and normalizer version. collected_by is the local archive installation UUID, independent of source device hints and raw Safari origin fields; legacy captures leave it unknown. It is not a hardware identifier. Search is token-based, not semantic similarity or arbitrary substring matching. Invalid source dates remain available in raw records; without time filters they can also appear in the timeline with null dates. Timeline fields may be previews; use original-record retrieval for full values.

Summaries retain the latest observed event revisions, including events pruned upstream. App summaries distinguish summed recorded intervals from their per-app union. Neither measures attention, and union across apps is not a total screen-time measurement. Browser and app totals are separated by fidelity; overlapping legacy/native records are not silently deduplicated. Coverage notes accompany results. Large summaries require narrower filters; query output and execution are bounded.

All raw fields are queryable. Collection remains local, but data returned through MCP becomes visible to the calling agent and its provider. Source titles, URLs, and decoded text are untrusted evidence, not instructions. The MCP exposes retrieval only; it cannot collect, import, install, or modify archives.

Archive, exports, and backups

SQLite is authoritative. A consistent source snapshot is fully scanned, then rows, blobs, observations, and tombstones commit together. Failures do not establish deletions. Row contents are deduplicated while observation order preserves A → B → A reversions. Distinct browser visits are never removed merely for sharing a URL or timestamp.

"$CA" export "/path/to/history.jsonl"
"$CA" backup "/path/to/new-backup.sqlite3"
"$CA" reindex
"$CA" uninstall

Exports contain a versioned manifest, archive schema, rows from all authoritative tables, normalized event versions, binary content, imported original lines, and a final marker with the preceding-line count and SHA-256. Raw source rows retain tagged cell values in payload_json; exported archive BLOB cells use $bytes_base64. Original source BLOB values use content-addressed $blob references. Large integers and invalid text retain explicit tags. FTS tables are rebuildable and omitted.

Exports are written atomically. Backups use a consistent SQLite snapshot and require a new destination without SQLite sidecars. Source paths and their known aliases, imported artifact paths, the archive, and their sidecars are protected against overwrite. Keep independent backups: exports on the same disk do not protect against disk failure.

Uninstall removes only this toolkit's LaunchAgent and preserves data/runtime. Explicit MCP registration is preserved; remove it separately with codex mcp remove context-archive. For updates, first run the installed context-archive uninstall to stop its service, then rerun the installer from the newer checkout. The installer refuses to replace a runtime while its collector is loaded, including with --no-start. Data is preserved throughout.

Development and extension

uv sync --locked
uv run pytest -q
uv build

Tests use invented databases and URLs, including Safari profiles, WAL commits, late-arriving visits, revisions, imports, snapshot pagination, exports, and real stdio MCP calls. Runtime tests use isolated homes and fake service commands. No test requires personal history or enables a real collector.

The adapter interface is Source, discover(), and normalize(). An adapter supplies source-specific fields and stable event identities linked to raw row versions; shared storage owns snapshots, database-store identity, transactions, and evidence history. Add an adapter with synthetic schema/discovery/normalization tests. Keep schema/normalizer changes versioned and rebuild indexes from preserved observations.

This first version retains history indefinitely. Full snapshots cost disk I/O proportional to source size, and archive growth depends on revisions. Database layouts may change across macOS/browser versions; explicit path additions and visible diagnostics support investigating those changes. Source fidelity does not imply every undocumented field has a known interpretation.

Possible applications include a custom Screen Time-style dashboard, a daily report, or reconstructing a research session from source-linked events. No report automation or inferred session/project model is included.

License

MIT. Knowledge Archive and Browser History Receiver informed this implementation; the original projects remain independent.

Correlate activity across sources

episodes, projects, and project-timeline are available through both the JSON CLI and read-only MCP. They add explanations above the evidence archive; they do not modify collected records or require a model.

context-archive episodes --params '{"start":"2026-09-07T00:00:00-04:00","end":"2026-09-14T00:00:00-04:00"}'

Episodes group event onsets within a five-minute gap, with at most one hour between the first and last onset by default. Change gap_seconds and max_span_seconds to explore different grouping assumptions. An app interval beginning before the requested range is clipped to the range for grouping; its original timestamps remain in the event. A long interval never bridges otherwise separate episodes. span_seconds measures first-to-last onset, not active time or website dwell time.

Each episode returns its events, raw evidence references, and distinct lanes for source, collecting installation, and provenance. A lane is not an inferred originating device. Safari sync and imported browsing remain visibly separate, even when their timestamps are close. Temporal proximity alone does not establish shared intent, attention, or causation.

For explicit project or subject associations, create a local JSON definition:

{
  "id": "example-project",
  "name": "Example project",
  "rules": [
    {"field": "url", "operator": "prefix", "value": "https://github.com/example/project"},
    {"field": "title", "operator": "contains", "value": "Example project"}
  ]
}
context-archive put-project /path/to/project.json
context-archive projects
context-archive project-timeline --params '{"project_id":"example-project","start":"2026-09-07T00:00:00-04:00","end":"2026-09-14T00:00:00-04:00"}'
context-archive episodes --params '{"project_id":"example-project","start":"2026-09-07T00:00:00-04:00","end":"2026-09-14T00:00:00-04:00"}'

Rules are OR alternatives and literal, case-sensitive matches. Supported fields/operators: url exact/prefix; title exact/contains; domain, app_id, source_id, and profile exact. URL prefixes respect /, ?, and # boundaries, so /project does not match /project-other. Anchoring a shared app or domain may associate unrelated activity; use specific URLs or titles where possible. Events can match several projects, and every match names its project and zero-based rule indexes. These associations are user-authored interpretations, not verified work duration or exclusive classifications.

put-project atomically replaces the definition with the same ID. Set "enabled": false to disable it. Definitions are stored in an optional projects table inside the archive, survive reindexing, and are included in backups and JSONL exports made by this version. No personal definitions ship in the source. Reading an older archive requires no migration; the optional table is created only by the explicit editor. MCP cannot edit the registry.

Correlation queries require an explicit timezone-aware range of at most 31 days. They support source_id, profile, kind, fidelity, and provenance filters. Pagination fixes the capture boundary and rejects cursors after registry edits or reindexing. Episodes are assembled before pagination, so page boundaries do not split a group. Queries exceeding 10,000 events, 20 MB of input payloads, five seconds of scanning, or the response budget fail explicitly: narrow the range/filter, or use the existing paginated timeline for individual events. Invalid dates remain available as original evidence but cannot participate in temporal grouping.

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    B
    maintenance
    Provides a read-only MCP interface to query and retrieve verifiable evidence from a local memory bank, supporting search, dossier, chronology, source, and evidence tools.
    6
    BSD Zero Clause
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables read-only querying of a local SQLite database via MCP, with tools to list tables, retrieve schema, and execute SELECT/WITH/EXPLAIN queries.
    -
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables AI clients to read and search macOS Messages history through a read-only MCP interface.
    -