How it works
Memory is stored in two places simultaneously:
-
Turtle files on disk (the source of truth):
.fluree-memory/repo.ttl— repo-scoped memories, git-tracked, shared with the team.fluree-memory/.local/user.ttl— user-scoped memories, gitignored, private to the developer
-
A
__memoryFluree ledger (a derived cache): The ledger is rebuilt automatically from the.ttlfiles whenever they change. This meansgit pullthat modifiesrepo.ttltriggers a rebuild on the next memory command — teammates' memories appear automatically.
The sync works via SHA-256 content hashing. On every operation, the CLI computes a hash of repo.ttl + user.ttl (salted with MEMORY_SCHEMA_V1) and compares it to .local/build-hash. If they differ, the ledger is dropped and rebuilt from the files.
Recall uses BM25 keyword scoring against the mem:content field, plus tag matching. This runs as a SPARQL query against the __memory ledger.
Setup
Initialize
fluree memory init
Creates the __memory ledger, the .fluree-memory/ directory structure, and optionally configures MCP for detected AI coding tools (Claude Code, Cursor, VS Code, Windsurf, Zed).
Use --yes for non-interactive mode. Use --no-mcp to skip IDE detection. Idempotent — safe to run multiple times.
MCP installation
To configure a specific IDE:
fluree memory mcp-install --ide cursor
This writes the MCP config file (e.g., .cursor/mcp.json) and installs agent rules (e.g., .cursor/rules/fluree_rules.md). The config tells the IDE to spawn fluree mcp serve --transport stdio.
For Cursor, the config sets FLUREE_HOME to scope memory to the workspace:
{
"mcpServers": {
"fluree-memory": {
"type": "stdio",
"command": "fluree",
"args": ["mcp", "serve", "--transport", "stdio"],
"env": {
"FLUREE_HOME": "${workspaceFolder}/.fluree"
}
}
}
}
Without FLUREE_HOME, the MCP server may use global directories instead of the repo's .fluree-memory/.
Memory kinds
| Kind | What it's for | Example |
|---|---|---|
fact | Something learned about how the system works | "The index format uses postcard encoding with delta compression" |
decision | A design choice that was made | "Chose keyword matching over embeddings for Phase 1 to avoid cloud dependency" |
constraint | A rule that must be followed | "Never suppress dead code warnings with underscore prefix" |
preference | How something should be done | "Use thiserror for error types, not anyhow" |
artifact | An important file, module, or resource | "fluree-db-core/src/error.rs defines the base error pattern for all crates" |
Kind-specific fields
- decision:
--rationale(why this choice was made),--alternatives(what else was considered) - constraint:
--severity(must= violation is a bug,should= strong preference,prefer= soft preference) - fact:
--fact-kind(command,architecture,dependency,configuration,api) - preference:
--pref-scope(user,team,repo) - artifact:
--artifact-kind(file,symbol,crate,module,config,endpoint)
Scope and sensitivity
Scope determines where the memory is stored:
repo(default):.fluree-memory/repo.ttl— committed to git, visible to all team members and agents working on this repo.user:.fluree-memory/.local/user.ttl— gitignored, follows the developer, not shared.
Sensitivity controls how content is handled:
public(default),internal,client: Metadata labels only — no behavior change.secret: Triggers automatic redaction. The CLI scans content for API keys, passwords, tokens, and connection strings and replaces them with[REDACTED]before storing.
CLI usage
Adding memories
fluree memory add --kind fact --text "Tests use cargo nextest" --tags testing,cargo
fluree memory add --kind constraint --text "Never suppress dead code with underscore prefix" \
--tags code-style --severity must
fluree memory add --kind decision --text "Use postcard for compact index encoding" \
--rationale "no_std compatible, smaller output than bincode" \
--alternatives "bincode, CBOR, MessagePack" --refs fluree-db-indexer/
fluree memory add --kind artifact --text "Error pattern defined here" \
--refs fluree-db-core/src/error.rs --artifact-kind file
# From stdin
echo "The index format uses postcard encoding" | fluree memory add --kind fact --tags indexer
Output: Stored memory: mem:fact-01JDXYZ5A2B3C4D5E6F7G8H9J0
IDs use the format mem:<kind>-<ulid>. ULID provides time-sortable uniqueness.
Recalling memories
fluree memory recall "how to run tests"
fluree memory recall "error handling" -n 10
fluree memory recall "testing patterns" --kind constraint --tags errors
fluree memory recall "testing patterns" --offset 3 # pagination
fluree memory recall "testing patterns" --format context # XML for LLM context injection
Text output:
Recall: "how to run tests" (2 matches)
1. [score: 13.0] mem:fact-01JDXYZ...
Tests use cargo nextest
Tags: testing, cargo
2. [score: 8.0] mem:fact-01JDABC...
Integration tests use assert_cmd + predicates
Tags: testing
Context output (what the MCP server returns to agents):
<memory-context>
<memory id="mem:fact-01JDXYZ..." kind="fact" score="13.0">
<content>Tests use cargo nextest</content>
<tags>testing, cargo</tags>
</memory>
<pagination shown="2" offset="0" total_in_store="13" />
</memory-context>
When results are truncated, the pagination element tells the agent how to get more:
<pagination shown="3" offset="0" limit="3" total_in_store="13">
Results 1–3, next score: 5.2. Use offset=3 to retrieve more.
</pagination>
The next_score hint lets the agent decide whether requesting more pages is worthwhile.
Updating (superseding)
fluree memory update mem:fact-01JDXYZ... --text "Tests use cargo nextest with --no-fail-fast"
This creates a new memory linked to the old one via mem:supersedes. The old memory stops appearing in recall results, but the chain is preserved and viewable with explain.
Forgetting
fluree memory forget mem:fact-01JDXYZ...
Retracts all triples for the memory. Use update for evolving information; use forget only when a memory is clearly wrong or permanently obsolete.
Explaining the history of a memory
fluree memory explain mem:fact-01JDNEW...
Supersession chain (newest first):
1. mem:fact-01JDNEW... (current)
Kind: fact
Content: Tests use cargo nextest with --no-fail-fast
Created: 2026-02-22T15:30:00Z
Supersedes: mem:fact-01JDXYZ...
2. mem:fact-01JDXYZ...
Kind: fact
Content: Tests use cargo nextest
Created: 2026-02-22T14:00:00Z
Status
fluree memory status
Memory Store Status
Total memories: 12
Total tags: 25
By kind:
fact: 7
decision: 2
constraint: 3
Export / Import
fluree memory export > memories.json
fluree memory import memories.json
Storage format
File layout
.fluree-memory/
├── repo.ttl # Repo-scoped memories (git-tracked)
├── .gitignore # Ignores .local/
└── .local/
├── user.ttl # User-scoped memories (gitignored)
├── build-hash # SHA-256 content hash (sync marker)
└── mcp.log # MCP server log
Turtle format
Memories are standard RDF using the mem: namespace (https://ns.flur.ee/memory#):
@prefix mem: <https://ns.flur.ee/memory#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
mem:fact-01JDXYZ5A2B3C4D5E6F7G8H9J0 a mem:Fact ;
mem:content "Tests use cargo nextest with --no-fail-fast" ;
mem:tag "testing" ;
mem:tag "cargo" ;
mem:scope mem:repo ;
mem:sensitivity "public" ;
mem:branch "main" ;
mem:createdAt "2026-02-22T15:30:00Z"^^xsd:dateTime ;
mem:supersedes mem:fact-01JDXYZ... .
RDF vocabulary
| Predicate | Description |
|---|---|
rdf:type | Memory class: mem:Fact, mem:Decision, mem:Constraint, mem:Preference, mem:Artifact |
mem:content | Content text (BM25-indexed for recall) |
mem:tag | Tag string (repeatable) |
mem:scope | mem:repo or mem:user |
mem:sensitivity | "public", "internal", "client", "secret" |
mem:severity | "must", "should", "prefer" (constraints only) |
mem:artifactRef | File or resource reference (repeatable) |
mem:branch | Git branch at time of creation (auto-detected) |
mem:createdAt | ISO 8601 datetime |
mem:supersedes | IRI of the memory this one replaces |
mem:rationale | Why a decision was made |
mem:alternatives | What alternatives were considered |
mem:factKind | Sub-type for facts |
mem:prefScope | Convention scope for preferences |
mem:artifactKind | Sub-type for artifacts |
Since the backing store is a Fluree ledger, you can also query the memory graph directly with SPARQL using the kg_query MCP tool or the CLI.
MCP server
fluree mcp serve starts a JSON-RPC server over stdio that exposes memory tools to IDE agents.
Tools
| Tool | Description |
|---|---|
memory_add | Store a new memory |
memory_recall | Search memories by keyword (returns XML context with pagination) |
memory_update | Supersede an existing memory |
memory_forget | Delete a memory |
memory_status | Show store summary |
kg_query | Execute raw SPARQL against the __memory ledger |
The server auto-initializes the __memory ledger on first tool call. Tracing output is suppressed to avoid interfering with JSON-RPC on stderr.
How agents are expected to use it
The fluree_rules.md file (installed alongside MCP config) describes the expected workflow:
- Start of task: Call
memory_recallwith a query describing what you're about to do. This surfaces relevant facts, decisions, constraints from previous sessions. - During work: When you discover something non-obvious (a gotcha, an invariant, a pattern), call
memory_add. - When information changes: Call
memory_updateto supersede the old memory. - Rarely: Call
memory_forgetonly for clearly incorrect information.
Testing directly
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0.0"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
| fluree mcp serve --transport stdio
Tag conventions
Use consistent, lowercase tags:
- Module names:
indexer,query,transact,api,cli,memory - Topics:
testing,errors,performance,storage,schema - Actions:
debugging,refactoring,migration