About Datalaga
Datalaga is an MCP server in the Other category: mCP with Datalog/EAV memory for coding agents backed by Datalevin.
Install
git clone https://github.com/enajski/datalagaThis server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.
Ask AI
Ask AI about Datalaga
Powered by Claude · Grounded in docs
Security checks
- ✓README presentThe repository ships documentation.
5 checks have not been run yet.
README
Datalaga: Agentic Coding Memory Behind MCP
This repository is a working exploration of Datalevin as a backend for coding-agent memory exposed through an MCP server. The prototype focuses on a thin, honest vertical slice:
- structured EAV/Datalog memory with provenance
- MCP tools/resources that operate on high-value coding memory actions
- ingestion + normalization from realistic coding artifacts
- reproducible evaluation scenarios and report generation
The goal is to answer one question with evidence: should Datalevin be used for coding-agent memory behind MCP?
Why Datalevin
Datalevin gives:
- ACID local transactions on LMDB
- Datalog queries for relationship-heavy retrieval
- full-text search over normalized memory bodies
- flexible EAV schema evolution for a prototype phase
That combination is promising for coding memory where facts and relationships matter more than document blobs.
Repository Layout
src/datalaga/memoryschema, transaction logic, query logicsrc/datalaga/mcp/server.cljMCP stdio server (tools + resources)src/datalaga/cli.cljstandalone CLI — all tools without MCP protocolsrc/datalaga/ingest.cljingestion and normalization pipelinesrc/datalaga/eval.cljevaluation harness + MCP smoke testsrc/datalaga/inspect.cljinspection CLI for debugging memorysrc/datalaga/maintenance.cljmaintenance CLI for normalization/backfillsrc/datalaga/memory/maintenance.cljshared normalization enginebuild.cljuberjar build script for native-image compilationexamples/seed-data.ednsynthetic but realistic linked coding-memory dataseteval/report.mdgenerated evaluation reportdocs/architecture.mdarchitecture details
Local Setup
Prerequisites
- Clojure CLI
- Java 21+
- On macOS x86_64 only:
libomp(for Datalevin native dependency)
Dependency + Native Handling
Use the wrapper script so Datalevin native loading works consistently:
./bin/clojure-with-dtlv ...
The wrapper:
- ensures local Maven cache under
.m2/repository - extracts required Datalevin native libraries into
.native/dtlvon macOS x86_64 - sets
DYLD_LIBRARY_PATHappropriately
If you run clojure directly and the native libraries are not configured, startup and tests can fail with errors like UnsatisfiedLinkError: no jniDTLV in java.library.path or Library not loaded: 'libdtlv.dylib'. In that case, rerun the command through ./bin/clojure-with-dtlv ... or another entrypoint that uses the wrapper.
Run the Prototype
CLI (without MCP)
The datalaga CLI exposes every MCP tool as a direct command. No JSON-RPC protocol — just command-line arguments and structured output.
# List all available tools
./bin/datalaga tools
# Get help for a specific tool
./bin/datalaga help ensure_project
# Create a project
./bin/datalaga ensure_project --project_id project:myapp --name "My App"
# List projects
./bin/datalaga list_projects
# Search entities
./bin/datalaga search_entities --project_id project:myapp --query "auth"
# Record a tool run
./bin/datalaga record_tool_run \
--run_id run:001 \
--project_id project:myapp \
--command "npm test" \
--exit_code 0
# Remember a fact
./bin/datalaga remember_fact \
--entity_id note:finding-1 \
--entity_type note \
--summary "Auth flow needs refactor" \
--project_id project:myapp
# Run a raw Datalog query
./bin/datalaga memory_query \
--query_edn '[:find [?id ...] :where [?e :entity/id ?id]]'
# Summarize project memory
./bin/datalaga summarize_project_memory --project_id project:myapp
# Output as JSON instead of pretty-printed EDN
./bin/datalaga list_projects -f json
# Pass arguments as a JSON object
./bin/datalaga ensure_project --json '{"project_id":"project:myapp","name":"My App"}'
# Read arguments from stdin
echo '{"project_id":"project:myapp"}' | ./bin/datalaga list_projects -
Global options:
-d, --db-path PATH— Datalevin database path (default:.data/memory)-s, --seed-file FILE— Seed data file (default:examples/seed-data.edn)--seed-on-start— Seed the database before running the command-f, --format FORMAT— Output format:pretty(default),json,edn--json JSON— Pass tool arguments as a JSON object-h, --help
Hyphens and underscores are interchangeable in tool names (list-projects = list_projects).
MCP Server
Start MCP server (JSON-RPC over stdio):
./bin/start-mcp --db-path .data/memory --seed-file examples/seed-data.edn
Seed on startup only when requested:
./bin/start-mcp --db-path .data/memory --seed-file examples/seed-data.edn --seed-on-start
Other Tools
Run local demo/evaluation:
./bin/run-eval
Inspect memory directly:
./bin/inspect-memory summary project:phoenix-auth
./bin/inspect-memory task task:AUTH-142
./bin/inspect-memory symbol symbol:src/auth/session.clj#refresh-session
./bin/inspect-memory prior-decisions task:AUTH-160
./bin/inspect-memory --seed-before-run summary project:phoenix-auth
Run housekeeping normalization:
./bin/normalize-memory --project-id project:yoyo-evolve --mode dry_run
./bin/normalize-memory --project-id project:yoyo-evolve --mode apply --migration-id migration:v1
./bin/normalize-memory --seed-before-run --project-id project:yoyo-evolve --mode dry_run
Native Image
Build a standalone native binary (requires GraalVM 25+ with native-image):
# For native-image builds, replace datalevin/datalevin with
# org.clojars.huahaiy/datalevin-native in deps.edn first.
./bin/build-native
This produces target/datalaga — a single binary that starts instantly, no JVM required.
# Run the native binary
./target/datalaga list_projects
./target/datalaga search_entities --project_id project:myapp --query "auth" -f json
To build just the uberjar (for JVM deployment without native-image):
clj -T:build uber
java -jar target/datalaga.jar list_projects
MCP Surface
Implemented tools:
ensure_projectensure_tasklist_projectsupsert_code_entitysearch_entitiesremember_factrecord_eventrecord_tool_runrecord_errorlink_entitiessearch_notesfind_related_contextget_symbol_memoryget_task_timelinesummarize_project_memorymemory_query(raw Datalevin Datalog query via EDN payload)memory_pull(entity pull with optional EDN pull pattern)normalize_project_memory(admin maintenance)
Notable behavior:
record_tool_runinfers a readable run name fromcommandwhennameis omitted, setsentity/statusfromexit_code(successorfailed), and can track replacement lineage viasupersedes_run_ids/retries_of_run_ids.record_tool_runrejects compound shell commands (&&,||,;,|) so each run record maps to one executable command.record_tool_runacceptstouched_file_pathsin addition totouched_file_ids, auto-upsertsfile:*entities for those paths, and returnsnormalized_touched_file_refs.record_eventacceptssubject_file_paths, auto-upserts file entities for those paths, and returnsnormalized_subject_file_refs.record_erroracceptsrelated_file_paths, auto-upserts file entities for those paths, and returnsnormalized_related_file_refs.- path-based file normalization now defaults to project-scoped file ids:
file:<project_id>:<path>. search_entitiesapplies path-intent ranking for path-like queries (for examplescripts/evolve.sh), biasing file entities to the top when available.search_entities,search_notes, andfind_related_contextsupport pagination viaoffsetand boundedlimitdefaults to keep responses predictable for agent loops.search_entitiesreturns file-onboarding guidance on path-like file misses (suggested_file_id,suggested_action=upsert_code_entity,suggested_arguments) whenentity_type=fileand no file match exists.- when a path-like file miss corresponds to an existing global
file:*entity,search_entitiesalso returnsexisting_file_idandexisting_file_project_idto explain the miss and guide re-association/upsert. - when
record_tool_runis called without lineage fields, the server auto-links to the most recent matching run in the same project/task/session (auto_lineage_inferredin response). - write tools enforce strict argument validation; unsupported fields are rejected with remediation data.
- unsupported-argument rejections may include
did_you_meanremediation hints for common mistakes (for examplerecord_tool_run.status-> useexit_code). - tool errors include
error_typeandretry_bootstrap_recommendedto separate validation fixes from true bootstrap retries. - tool calls also enforce required argument presence (missing required args are rejected early).
- write transactions now prevalidate references before commit, preventing partial entity writes on missing refs.
ensure_tasknormalizes statuses (completed->done,in-progress->in_progress) and active task views treatdone|completed|closed|cancelledas non-active.list_projectsis sorted by recent project activity (latest entity update/create), and each project brief includesproject/last-activity-at.record_errordefaults toentity/status = openunless provided explicitly.link_entitieswithlink_type = resolved_byauto-marks the source error asresolvedand attaches the resolver run as a reference.link_entitiescan also model cross-project service topology explicitly (for examplelink_type = depends_onorlink_type = calls_apibetweenproject:*entities).summarize_project_memoryandmemory://project/{project_id}/recent-failuresexclude errors already markedresolved/closed.remember_factauto-normalizesattributes.files/attributes.file_pathsintofile:*refs, upserts missing file entities, and returnsnormalized_file_refs;external_refs(orattributes.external_refs) are normalized intoentity/external-refs.search_notessupportsproject_idsfor cross-project recall, andfind_related_contextacceptsproject_idsto broaden graph traversal beyond the anchor entity's project.memory_querynow supports boundedtimeout_ms,max_results, andoffset, returningtotal_count/truncatedmetadata for collection results.normalize_project_memorysupports project-scopeddry_run|applyhousekeeping with operation filters:normalize_entity_typesbackfill_error_resolutionlink_run_supersessionscope_file_ids_by_project(backfill project-scopedfile:<project_id>:<path>IDs and refs) and writes a maintenance event on apply for auditability.
Exposed resources:
memory://project/{project_id}/summarymemory://project/{project_id}/recent-failuresmemory://project/{project_id}/active-tasksmemory://session/{session_id}/timelinememory://task/{task_id}/timelinememory://symbol/{symbol_id}/dossier
Example Memory Records
Seed data includes linked entities for:
Project,File,SymbolTask,Session,ToolRun,Error,ObservationDecision,Patch,Note,Event,Link
Example relation chains in the dataset:
- failing test tool run -> error -> observation -> decision -> patch -> follow-up notes
- task touched files -> prior decisions related files -> context recommendation
- file contains symbol -> symbol related failures/patches/decisions/notes
Custom entity type policy:
- core types are stored as-is (for example
project,task,error) - non-core types with no namespace are promoted to
custom/<type>(for examplefinding->custom/finding) - non-core types with a namespace are kept as provided
- original custom type is retained in
entity/kind
Code entity onboarding flow (for non-seeded repos):
upsert_code_entityfor file/symbol anchors as you discover code structure.search_entitiesto discover valid IDs before creating refs inrecord_error/link_entities.find_related_context/get_symbol_memorybecome useful once anchors exist.
Query Patterns Demonstrated
- exact entity lookup
- graph/EAV traversal around files/symbols/tasks
- full-text note/body search
- hybrid text + graph context retrieval
High-signal example:
- “What prior decisions touched files that this task is about to modify?”
- implemented in
prior-decisions-for-taskvia joins over task touched files and decision related files
- implemented in
Tradeoffs Observed So Far
Strengths:
- graph/EAV retrieval materially outperforms plain text for anchored coding workflows
- provenance is explicit and inspectable
- memory model is readable and extensible
Pain points:
- integration glue is still required between MCP payloads and normalized entities
- full-text alone underperforms graph traversal for relationship-heavy coding questions
Evaluation Output
Run ./bin/run-eval to regenerate:
eval/report.mdwith retrieval metrics by scenario- MCP smoke test evidence (tools/resources reachable via stdio protocol)
- recommendation and rationale
AGENTS.md Snippet (Enforce datalaga Usage)
Use this snippet in a repo-level AGENTS.md to enforce consistent memory behavior:
## Required MCP Memory Policy (`datalaga`)
For all non-trivial coding tasks in this repository, you MUST use the `datalaga` MCP server.
### 1) Bootstrap
- At the start of work, call `list_projects`.
- Do not assume demo seed data exists; the server may run without `--seed-on-start`.
- If the current repo project is missing, call `ensure_project` with `project_id` and a short summary.
- If you will write task-scoped records (`task_id` on runs/events/errors/facts), call `ensure_task` first.
- `ensure_task` also updates an existing task’s mutable fields (`status`, `summary`, `priority`, `description`) when reused.
### 2) Read Before Acting
- Before edits or major analysis, call `summarize_project_memory`.
- When touching a symbol/file/task, load context first with:
- `get_symbol_memory` (symbol-focused work),
- `find_related_context` (file/entity neighborhood),
- `get_task_timeline` (task-focused work), as applicable.
- Use `memory_pull` for targeted entity inspection when high-level tools are insufficient.
- Use `memory_query` for advanced recall/debugging (EDN Datalog), and prefer high-level read tools by default.
### 3) Write During/After Work
- Record command executions with `record_tool_run` (build/test/lint/tool output).
- Use one command per `record_tool_run` entry; do not combine commands with `&&` in a single run record.
- Do not send unknown or incomplete tool arguments. MCP calls with unsupported fields or missing required fields are rejected.
- If you rerun the same command for a task/session, keep `command` stable so lineage inference can chain retries automatically (or pass explicit `supersedes_run_ids` / `retries_of_run_ids`).
- Prefer `touched_file_paths` when file ids are unknown; the server will normalize and upsert file refs for run logging.
- Prefer `subject_file_paths` / `related_file_paths` when event/error references include files but file ids are unknown.
- Record failures with `record_error` and link to related runs/symbols.
- Persist key findings/decisions with `remember_fact` (or `record_event` for timeline milestones).
- When a fact references files, include `attributes.files` or `attributes.file_paths`; the server will auto-upsert `file:*` entities and attach them via `entity/refs`.
- Create explicit causality with `link_entities` when useful (e.g. error -> decision -> patch).
### 4) End-of-Task Memory Writeback
- Before final response, store a concise evaluation event containing:
- what was checked,
- what failed/passed,
- risks,
- follow-up opportunities.
- If memory write fails, inspect `error_type` / `retry_bootstrap_recommended` in the error data:
- when `retry_bootstrap_recommended=true`, retry once after `list_projects`/`ensure_project` (and `ensure_task` if needed),
- when `false` (for example validation errors), fix arguments and retry directly without bootstrap steps.
### 5) Scope and Quality
- Keep `project_id` consistent for all writes in a session.
- Prefer structured fields over blob text.
- Do not skip memory logging for convenience.
License
MIT. See LICENSE.
README copy captured when this server was indexed; it may lag the repository. The original is authoritative.