FlureeLabs
GuideMarch 30, 2026

Fluree Memory

A persistent, searchable knowledge store for developer context. Stores facts, decisions, constraints, preferences, and artifact references as RDF triples and exposes them to AI coding agents through an MCP server.

AI AgentsMemoryMCP

How it works

Memory is stored in two places simultaneously:

  1. 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
  2. A __memory Fluree ledger (a derived cache): The ledger is rebuilt automatically from the .ttl files whenever they change. This means git pull that modifies repo.ttl triggers 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

KindWhat it's forExample
factSomething learned about how the system works"The index format uses postcard encoding with delta compression"
decisionA design choice that was made"Chose keyword matching over embeddings for Phase 1 to avoid cloud dependency"
constraintA rule that must be followed"Never suppress dead code warnings with underscore prefix"
preferenceHow something should be done"Use thiserror for error types, not anyhow"
artifactAn 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

PredicateDescription
rdf:typeMemory class: mem:Fact, mem:Decision, mem:Constraint, mem:Preference, mem:Artifact
mem:contentContent text (BM25-indexed for recall)
mem:tagTag string (repeatable)
mem:scopemem:repo or mem:user
mem:sensitivity"public", "internal", "client", "secret"
mem:severity"must", "should", "prefer" (constraints only)
mem:artifactRefFile or resource reference (repeatable)
mem:branchGit branch at time of creation (auto-detected)
mem:createdAtISO 8601 datetime
mem:supersedesIRI of the memory this one replaces
mem:rationaleWhy a decision was made
mem:alternativesWhat alternatives were considered
mem:factKindSub-type for facts
mem:prefScopeConvention scope for preferences
mem:artifactKindSub-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

ToolDescription
memory_addStore a new memory
memory_recallSearch memories by keyword (returns XML context with pagination)
memory_updateSupersede an existing memory
memory_forgetDelete a memory
memory_statusShow store summary
kg_queryExecute 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:

  1. Start of task: Call memory_recall with a query describing what you're about to do. This surfaces relevant facts, decisions, constraints from previous sessions.
  2. During work: When you discover something non-obvious (a gotcha, an invariant, a pattern), call memory_add.
  3. When information changes: Call memory_update to supersede the old memory.
  4. Rarely: Call memory_forget only 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