Skip to main content

MCP stdio server bridging eidetic-daemon's UDS API to MCP clients (Cursor, Claude Code, Cline, ...)

Project description

eidetic-mcp — Python MCP bridge for eidetic-daemon

Thin MCP stdio server that exposes the eidetic-daemon UDS API as MCP tools. Any MCP client (Cursor, Claude Code, Cline, Cody, etc.) can list + call these tools to query the daemon's local engram store.

Per spec § 7 Open Q #5: this is the "separate Python wrapper" path — the daemon binary itself ships independently. The bridge is co-located in this repo for convenience; eventual fold into mcp-server-nucleus (the existing Nucleus MCP server) is an architecture decision deferred to W2+.

Tools

Tool Args Returns
query_engrams surface (optional v0.0.23+, omit for all surfaces), limit (default 50, cap 500), since (unix ns, default 0), before (unix ns, default 0, v0.0.21+), asc (bool, default false, v0.0.22+), raw_chunks (bool, default false) JSON array of engrams ordered by timestamp descending (or ascending when asc=true). Omit surface to retrieve across all surfaces. since+before define a half-open time window. Chunked records (per ADR-018) are reassembled by default; raw_chunks=true returns chunks as separate engrams.
daemon_status (none) {"healthy": bool} from /healthz round-trip
daemon_metrics (none) Daemon's /metrics JSON (v0.0.7+): version, uptime_seconds, engram_total, engram_by_surface, capture_skipped, db_path, db_size_bytes. Schema additive-only across versions. Daemons predating v0.0.7 return error: metrics not configured.
list_surfaces (none) {"surface": count, ...} — every surface the daemon has seen with its engram count (v0.0.13+). Empty store → {}. Use for discovery before query_engrams.
purge_engrams surface (required), before (unix ns, default 0) {"deleted": N}. Removes all engrams for the surface when before=0; only removes engrams with ts < before when set. Irreversible (v0.0.13+).
search_engrams q (required — FTS5 expression), surface (default all), limit (default 50, cap 500) JSON array of engrams ordered by relevance rank (best match first). Same shape as query_engrams. q accepts bare keywords, "phrase queries", OR/AND/NOT boolean operators (v0.0.14+).
recent_engrams since (unix ns, default 0), before (unix ns, default 0, v0.0.21+), limit (default 50, cap 500) JSON array of newest engrams across all surfaces, newest-first. since+before define a sliding time window. Poll diff: pass last-seen ts as since. Same []Engram shape (v0.0.15+).
insert_engram surface (required), payload (required), ts (unix ns, default server-now), meta (optional string) Directly insert an engram, bypassing fsnotify. Returns {"id": N}. Immediately searchable + retrievable. Use for mobile, webhooks, relay pipelines, manual annotations (v0.0.16+).
insert_engrams_batch items (required — array of {surface, payload, ts?, meta?}) Bulk insert N engrams in one atomic transaction. Returns {"inserted": N}. Efficient for relay sync, session replay, bulk import (v0.0.17+).
get_engram_by_id id (required — positive integer) Fetch a single engram by primary key. Returns full Engram JSON. Error on non-existent ID or non-positive integer (v0.0.18+).
count_engrams surface (optional), since (unix ns, optional) Return {"count": N} for engrams matching optional surface and time filters. Never fetches rows (v0.0.20+).
delete_engram_by_id id (required — positive integer) Remove a single engram by primary key. Returns {"deleted": 1}. Error on non-existent ID or non-positive integer. Irreversible (v0.0.19+).

Chunked-record reassembly (ADR-018)

The daemon's JSONL parser splits records exceeding 7 MiB into chunks tagged with chunk_id/chunk_seq/chunk_total in meta. The bridge's query_engrams runs reassemble_chunks() by default — clients see ONE row per logical record regardless of chunking. Set raw_chunks=true to bypass reassembly (useful for debugging or when your consumer handles the contract directly).

reassemble_chunks() is also exported as a public function for callers that want to reassemble a tuple[Engram, ...] from the lower-level DaemonClient.query_engrams():

from eidetic_mcp.client import DaemonClient
from eidetic_mcp.reassemble import reassemble_chunks

client = DaemonClient()
raw_rows = client.query_engrams(surface="claude_code", limit=20)
merged = reassemble_chunks(raw_rows)  # returns tuple[Engram, ...] with chunked records merged

Idempotent — safe to call on already-reassembled output. Best-effort on incomplete chunk groups (warns + emits partial; does NOT silent-drop).

P95 retrieval round-trip on a 10K-row store: ~0.27 ms (daemon-side; per ADR-016 + spec § 3 measurement).

Install

Requires Python ≥ 3.11 + a running eideticd (the daemon, separate package).

# From a checkout of this repo:
cd bridge/python
pip install -e .          # editable install (or: pip install .)

# Verify:
python -m eidetic_mcp.server  # starts MCP stdio server; ^C to exit

The bridge is not yet on PyPI. Install from this repo until the architecture decision on mcp-server-nucleus fold-in lands (W2+).

Configure your MCP client

Add to your client's MCP config (paths vary):

Client Config path
Claude Code ~/.claude/mcp.json
Cursor Settings → MCP Servers → New
Cline VS Code settings → Cline: MCP servers

Add an entry:

{
  "eidetic": {
    "command": "python",
    "args": ["-m", "eidetic_mcp.server"],
    "env": {}
  }
}

Override transport:

  • EIDETIC_TCP=1 — talk to daemon's TCP loopback (127.0.0.1:9876) instead of UDS.
  • EIDETIC_UDS_PATH=/some/path — override default UDS path (/tmp/eidetic-daemon.sock on macOS, /var/run/eidetic.sock on Linux).

Usage from a tool-calling AI

Once the MCP client picks up the server, your AI can call:

query_engrams(surface="claude_code", limit=20)
→ [{"id":N, "surface":"claude_code", "ts":..., "payload":"...", "meta":"..."}, ...]

daemon_status()
→ {"healthy": true}

daemon_metrics()
→ {
    "version": "v0.0.13",
    "uptime_seconds": 142,
    "engram_total": 139751,
    "engram_by_surface": {"claude_code": 141314},
    "capture_skipped": 0,
    "db_path": "/Users/.../engrams.db",
    "db_size_bytes": 659046400
  }

list_surfaces()
→ {"claude_code": 141314, "cursor": 23, "cowork": 414}

purge_engrams(surface="cursor")
→ {"deleted": 23}

purge_engrams(surface="claude_code", before=1715000000000000000)
→ {"deleted": 98214}   # only engrams older than the timestamp

search_engrams(q="benchmark latency")
→ [{"id":N, "surface":"claude_code", "ts":..., "payload":"...", "meta":"..."}, ...]

search_engrams(q='"meetup tomorrow"', surface="cursor", limit=3)
→ [...]   # phrase query, surface-filtered, top 3 by relevance

recent_engrams()
→ [{"id":N, "surface":"cursor", "ts":..., "payload":"...", "meta":"..."}, ...]  # 50 newest, all surfaces

recent_engrams(since=1747500000000000000, limit=20)
→ [...]   # only engrams with ts > since, newest-first

insert_engram(surface="mobile", payload="noted from phone")
→ 1234   # integer engram ID

insert_engram(surface="webhook", payload="stripe event body", meta='{"source":"stripe"}')
→ 1235

insert_engrams_batch([
    {"surface": "mobile", "payload": "note 1"},
    {"surface": "mobile", "payload": "note 2"},
])
→ 2   # integer count inserted

get_engram_by_id(id=1234)
→ Engram(id=1234, surface='mobile', ts=..., payload='noted from phone', meta='')

count_engrams()                               # → 42  (all surfaces, all time)
count_engrams(surface="claude_code")          # → 17  (one surface)
count_engrams(since=1747500000000000000)      # → 5   (since timestamp)

delete_engram_by_id(id=1234)
→ True   # boolean — True on success; DaemonError on 404

Surfaces depend on what the daemon is watching (default: claude_code, cowork, cursor). Use list_surfaces() to discover what's currently in the store.

daemon_metrics() is the v0.0.7+ verifiability surface — your AI can introspect what's been captured without raw curl, useful for "what's in scope right now?" pre-flight before a query_engrams call. (The bridge wraps the JSON response from the daemon's /metrics. The daemon also speaks Prometheus exposition (v0.0.10+) and OpenMetrics 1.0.0 (v0.0.11+) on the same endpoint via Accept header, but the bridge tool surface stays JSON.)

Caller auth (v0.0.9+, opt-in)

If the daemon is running with EIDETIC_AUTH=1 (or -auth flag), the bridge auto-discovers the rotating Bearer token. Resolution order:

  1. Explicit auth_token=<hex> kwarg (test injection)
  2. EIDETIC_AUTH_TOKEN env var
  3. <EIDETIC_DATA_DIR>/auth-token file (default ~/.eidetic/auth-token)

Daemons NOT in auth-mode pass through transparently — no bridge config change required either way.

Development

cd bridge/python
pip install -e '.[dev]'
pytest tests/

tests/test_client.py spins up a real Unix-domain HTTP server backed by a tmp socket and verifies parser + transport + env-var defaults. Server-side end-to-end is covered by scripts/demo-smoke.sh at the daemon repo root.

Architecture

                  stdio                       HTTP over UDS
  AI client  ←─────────→  eidetic_mcp.server  ←─────────→  eideticd (Go)
  (Cursor /                (this package)                   (separate)
   Claude Code /
   Cline / ...)

The bridge is process-isolated from the daemon: bridge crashes don't take the daemon down, and vice versa. Bridge has zero local state (the daemon owns the SQLite store).

Security

The bridge has the same trust boundary as the daemon (single-user, single-host, UDS chmod 0600). It does not introduce auth, TLS, or rate-limiting. Anything callable from the daemon's /engrams endpoint is callable through the bridge — including reads of every captured engram (which may contain pasted secrets per SECURITY.md § 1). Treat MCP-bridge access the same way you'd treat shell access to the same uid.

License

MIT — same as the parent eidetic-daemon repo. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

eidetic_mcp-0.0.4.tar.gz (27.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

eidetic_mcp-0.0.4-py3-none-any.whl (19.8 kB view details)

Uploaded Python 3

File details

Details for the file eidetic_mcp-0.0.4.tar.gz.

File metadata

  • Download URL: eidetic_mcp-0.0.4.tar.gz
  • Upload date:
  • Size: 27.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for eidetic_mcp-0.0.4.tar.gz
Algorithm Hash digest
SHA256 838602bdf47d26a532f237c881c55471a033712baa5653fee711de8ac361b95a
MD5 67f1ec5fb6a96000a273252a48cf16e4
BLAKE2b-256 b1598d7abc4e482be231abc16a0c02c2c7006f1c8837bb57bde7199d291c6178

See more details on using hashes here.

File details

Details for the file eidetic_mcp-0.0.4-py3-none-any.whl.

File metadata

  • Download URL: eidetic_mcp-0.0.4-py3-none-any.whl
  • Upload date:
  • Size: 19.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for eidetic_mcp-0.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 e29edbb69332766268cb742c05f908f01d737f0993dd750ab85c081fdfe5322f
MD5 3c1f32ce01ae01143b3cb939572ad271
BLAKE2b-256 171e3578a8b9fee7923b3e1a8ed859e489e935967243e6aca4d820221425667a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page