memalyze
Uses a local Ollama instance as the embedding provider (e.g. qwen3-embedding:0.6b) for vector search, so memories are embedded and queried fully offline without a cloud API or runtime model download.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@memalyzeRemember that the client prefers email over phone calls"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
memalyze
MCP-native, self-hosted memory for AI agents — with the governance an enterprise will actually ask for.
One static binary. Local embeddings. No cloud, no graph database, no Python stack. Redaction happens before anything is written; every read is audited.
Website · MCP Registry · v0.1.0
./memalyze # MCP over stdio, for Claude Code / Cline / any harnessv0.1.0 is out — static binaries for linux/darwin/windows, checksums published alongside.

The problem
Every agent framework ships its own memory: a vector table here, a JSON file there, a "notes" folder somewhere else. Three things follow, and all three hurt in production:
You cannot audit it. "Which customer's data did the agent read last Tuesday?" has no answer, so security review stops the project.
You cannot deploy it offline. Most options either call a cloud API or download model weights from HuggingFace at runtime — both fail inside a bank, a hospital, or a factory.
You cannot keep it small. Twenty-three tools, a graph database, and a Python dependency tree later, the "memory feature" is a second product to operate.
memalyze is the middleware that fixes those three, and nothing else.
Related MCP server: Centralaizer
What is different
Cloud memory APIs | Framework built-ins | Personal note apps | memalyze | |
Where data lives | vendor cloud | inside the framework | local Markdown | your server / your laptop |
How to connect | proprietary API | tied to one framework | MCP | MCP, any harness, stdio or HTTP |
Redaction before write | ❌ | ❌ | ❌ | ✅ 8 rule families, configurable, irreversible by default |
Read audit | partial | ❌ | ❌ | ✅ append-only*, per-actor, exportable |
Offline | ❌ | partial | ⚠️ model download needed | ✅ local embeddings, no network |
Deployment | SaaS | with the framework | 173 Python packages | ✅ one static binary / one compose file |
Tool surface | — | — | 23 tools | 4 tools |
* A database trigger rejects UPDATE on audit_log; DELETE is not blocked, so anyone
with the database file can remove rows. It is append-only at the SQL level, not WORM — export
to object-lock or syslog storage if deletions are inside your threat model
(SECURITY.md).
Not another mem0, and not Basic Memory either: those are good at remembering. memalyze assumes remembering is becoming a commodity and competes on governance, offline operation, and a deliberately small surface.
30-second demo
No Ollama, no network, no MCP client:
./examples/demo/demo.sh
# or: go run ./cmd/memalyze demoIt writes a memory containing a phone number, an e-mail and a resident ID, then shows:
what is actually stored (
[REDACTED:phone], not13800138000);that searching for the original number returns 0 hits;
that a business-word search still hits, returning the redacted text;
the audit rows for the two writes and the two reads.
Details and transcripts: docs/s15-redaction-audit.md.
Quick start
1. Binary + local embedding model
Prebuilt, checksum-verified (Linux/macOS; Windows: take the .exe from the release page):
curl -fsSL https://raw.githubusercontent.com/mahaoboy/memalyze/main/scripts/install.sh | sh
ollama pull qwen3-embedding:0.6b # ~640 MB, 1024-dim, strong on Chinese
memalyze --versionFrom source (Go 1.27+): make build, then ./memalyze --version.
2. Wire it into a harness
Claude Code (project scope):
claude mcp add --scope project memalyze -- "$(pwd)/memalyze"
cp examples/claude-code/CLAUDE.md ./CLAUDE.md # makes Claude search before answeringCline: copy examples/cline/cline_mcp_settings.json and .clinerules.
Generic harness: examples/custom-harness/ — one prompt contract
plus pseudocode. All options: examples/README.md.
3. Or one-click in Claude Desktop (MCPB)
Download the bundle for your platform from the
v0.1.0 release and open it.
Claude Desktop shows an install sheet and asks where to keep memalyze.db.
Platform | File |
macOS Apple Silicon |
|
macOS Intel |
|
Windows x64 |
|
Linux x64 |
|
The binary inside is the same v0.1.0 build as above. These bundles are new and have not
been opened on a real macOS/Windows desktop yet — if the install sheet does not appear,
use the plain binary. The server is also listed in the
MCP Registry.
4. Or run it in Docker (memalyze + Ollama + model pull)
printf 'MEMALYZE_HTTP_TOKEN=%s\n' "$(openssl rand -hex 24)" > .env
docker compose up -d --build
curl -s http://127.0.0.1:8080/healthz # {"status":"ok","version":"dev"}Point a Streamable-HTTP MCP client at http://127.0.0.1:8080/mcp with
Authorization: Bearer $MEMALYZE_HTTP_TOKEN. See docs/s16-docker.md.
Tools
Four, on purpose. Every extra tool is a decision the model has to make.
Tool | What it does | Notable semantics |
| store a memory | duplicates return the existing id; embedding failure keeps the memory ( |
| retrieve | vector first, keyword backfill, |
| soft delete | idempotent for already-deleted ids; unknown id returns a tool error |
| wiring check | proves the MCP link works; no business logic |
There is no memory_context tool: memory_search already returns text you can paste
into a prompt, and two overlapping tools make model choice worse (D-017).
Governance
Redaction (write path). Rules run in memory.Service.Add before dedup, FTS and
embedding — so plaintext never reaches the main table, the full-text index or a vector.
Rule | Example input | Stored as |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Modes: enforce (default), dry-run (count only, for tuning), off (debugging).
Custom rule packs are JSON: examples/redaction-rules.json.
Audit (read path). Append-only audit_log (a database trigger rejects UPDATE):
actor, tenant, scope, action, memory id, timestamp, result count, degraded, redaction
count. The search text itself is stored as SHA-256 + length unless you explicitly set
MEMALYZE_AUDIT_QUERY_PLAIN=1.
memalyze audit export --from 2026-10-01 --to 2026-10-31 --format csv --out audit.csv
memalyze audit export --tenant acme --format jsonWhat is not here yet: per-user authentication (one token per process, actor from
env), audit retention/purge, reversible redaction, audit UI.
Retrieval, measured
Numbers from S8 on a 60-memory Chinese corpus, 16 queries, measured not assumed
(docs/s8-retrieval-report.md):
Approach | recall@3 | MRR |
| 0.94 | 0.92 |
CJK bigram FTS5 (keyword backfill) | 0.88 | — |
| 0.47 | — |
Two counter-intuitive findings that shaped the design: SQLite FTS5's default tokenizer cannot search Chinese at all (0.19 recall until we stored bigram text), and RRF fusion scored worse than vector-only, so v0.1 does not fuse. See D-012.
Latency (S14, cold start fixed): warmup 37.8 s in background, then search 1.14 s with
degraded=false; after five idle minutes a fresh process answers in 1.52 s. If the vector
path is unavailable, search degrades to keyword in ≤3 s instead of hanging (D-015/D-018).
How it works
harness (Claude Code / Cline / your agent)
│ MCP: stdio or Streamable HTTP
▼
internal/mcp 4 tools, argument conversion only
▼
internal/memory redact → dedup → store → embed; search: vector → keyword → degraded
├── internal/govern rule engine (8 families, JSON configurable)
├── internal/store interface; sqlite impl (memories, embeddings, audit_log)
├── internal/embed float32 BLOB codec, cosine; Ollama client
└── internal/search CJK bigram tokenizer + FTS5 MATCH builderSQLite (WAL) with ULID ids, partial unique index for dedup, soft delete, TTL.
Vectors are
float32little-endian BLOBs; search is brute-force cosine (fine under ~10k memories, and the honest limit of a single-binary design).Schema migrations are versioned via
PRAGMA user_version(v1 → v2 adds the audit table).
Operations
memalyze audit export --from 2026-10-01 --format csv --out audit.csv # who read what
memalyze backup --out /var/backups/memalyze-$(date +%F).db # consistent snapshot
memalyze health --url http://127.0.0.1:8080/healthz # probesystemd:
deploy/systemd/memalyze.service+docs/s16-systemd.md(hardened unit, env file, backup timer).Docker:
docs/s16-docker.md.backupusesVACUUM INTO, so snapshots are consistent under WAL and include the audit table; it refuses to overwrite an existing file.
Configuration
All through environment variables; defaults work out of the box.
Variable | Default | Meaning |
|
| SQLite path |
|
| default tenant for tool calls |
|
| local embedding endpoint |
|
| embedding model |
|
| vector-path budget before degrading |
|
| Ollama model residency |
|
|
|
| (built-in) | path to a JSON rule pack |
|
| append audit rows |
|
| actor recorded in audit |
|
| store query text (default: hash only) |
| (empty = stdio) | e.g. |
| (empty) | Bearer token; required when binding off-loopback |
Status
Honest snapshot — this is an early-stage project, not a 1.0.
Milestone | State |
M0 立项与定位 (S1–S4) | ✅ |
M1 备料与技术验证 (S5–S8) | ✅ |
M2 核心链路 (S9–S14) | ✅ |
M3 打磨与文档 (S15–S19) | ✅ S15 governance, S16 Docker, S17 docs, S18 tests+CI, S19 examples |
M4 发布 (S20–S23) | 🟡 S20 ✅, S21 ✅ (v0.1.0 public), S22 GitHub ✅ / HN·Reddit·V2EX pending, S23 ⬜ |
M5 复盘 (S24) | ⬜ needs real users |
M5 复盘 (S24) | ⬜ |
Known gaps: no per-user auth, no audit retention, the Docker image has not been built in CI (no daemon in the dev environment), brute-force vector scan, Chinese-first defaults (English works, but the tokenizer is tuned for CJK).
Docs
Doc | What it covers |
rules, modes, audit schema, demo transcript | |
compose stack, token policy, verification status | |
hardened systemd unit, env file, backup/restore | |
the 11 retrieval approaches and why this one won | |
the prompt contract that makes models search | |
real harness, two sessions, cold-start numbers | |
release artifacts, channels, post-release checklist | |
copy-paste entries for MCP directories and ClawHub | |
live feedback log (currently empty, honestly) | |
the milestone review template (not yet filled) | |
threat model: what is defended, what is out of scope |
Pilot
Two-week pilot: we install memalyze in your environment, tune the redaction rules to your data, review the audit export with you, and write down the threat-model gaps we find. No fee; you keep the deployment and the notes.
Request a pilot · Integration notes
Non-goals
Not a SaaS: you self-host, we do not hold your data.
Not an agent framework: it never takes over the harness.
Not a knowledge graph: no entity extraction pipeline, no graph database.
Not feature-maximal: four tools, one binary, and a hard "no" to anything that needs an operator.
License
AGPL-3.0-or-later (LICENSE). Commercial licensing for embedding in closed products:
open an issue titled commercial license.
Contributions: see CONTRIBUTING.md. Security reports: see
SECURITY.md.
Available Tools
4 toolsechoA
Echo the given text back unchanged. Used to verify the MCP wiring.
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | the text to echo back |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does state the key behavioral fact: the input is returned unchanged, so the tool is side-effect free and deterministic. It doesn't discuss failure modes or payload limits, but for a trivial echo this covers the essential contract.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, zero filler, with the behavior stated first and the diagnostic purpose second. Nothing could be removed without losing information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter, no-side-effect tool with no annotations and no output schema, the description supplies everything an agent needs: what it returns and why it exists.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There is a single parameter with 100% schema description coverage ("the text to echo back"), so the schema already fully documents it. The description adds nothing beyond that — "the given text" merely restates it — so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (echo) and resource (the given text) plus the exact transformation — returned unchanged — and adds that it exists to verify MCP wiring, which separates it completely from the memory_* siblings. An agent can identify the tool without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"Used to verify the MCP wiring" gives a clear triggering context: call this to test connectivity/plumbing. It doesn't explicitly exclude other uses or name alternatives, but for a single-purpose diagnostic tool the guidance is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_addA
Store a memory for later retrieval. Returns duplicate=true when identical content already exists (no new record), and embedding_failed=true when the memory was saved but vectorization failed (keyword search still works).
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | optional kind: fact, preference, episode, or note; defaults to note | |
| scope | No | optional scope, for example project:opc; defaults to 'default' | |
| content | Yes | the memory text to store | |
| ttl_hours | No | optional time-to-live in hours; 0 means the memory never expires |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | the id of the stored (or already existing) memory |
| dry_run | Yes | true when redaction ran in dry-run mode and the content was stored unchanged |
| embedded | Yes | true when a vector was written for this memory |
| redacted | Yes | true when the content hit redaction rules before being stored |
| duplicate | Yes | true when identical content already exists in the same tenant/scope; no new record was written |
| redactions | Yes | names of the redaction rules that matched; never contains the original text |
| embedding_error | No | the vectorization error, present only when embedding_failed is true |
| embedding_failed | Yes | true when the memory was saved but vectorization failed; keyword search still works |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does disclose genuinely useful behavior beyond the schema: duplicate detection yields duplicate=true with no new record, and partial failure yields embedding_failed=true while keyword search still works. What's missing is dedup semantics (is identity based on content alone or content+scope?), whether the call is idempotent, and any permission or rate-limit expectations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, zero filler, with the core purpose front-loaded and the return-flag caveats following. Every clause conveys actionable information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so explaining return values isn't strictly required (though the flags provided are useful). For a mutation tool with no annotations, the description adequately conveys the failure model, but it stops short of stating whether writes are irreversible, how duplicates are keyed, or how expiration behaves — gaps an agent would want before writing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so kind, scope, content, and ttl_hours are already documented in the schema. The description adds no additional meaning about parameter behavior — notably nothing on how scope interacts with duplicate detection or on ttl_hours expiry semantics. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: "Store a memory for later retrieval." It's immediately distinguishable from memory_search and memory_delete by name and verb. However, it never explicitly contrasts itself with those siblings or clarifies boundaries (e.g., add vs. update semantics).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"for later retrieval" implies the write-then-search workflow with memory_search, but there is no explicit when-to-use/when-not, no naming of alternatives, and no guidance on prerequisites such as whether a matching memory should be searched for first. Usage is implied rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_deleteA
Soft-delete a memory by id. Already-deleted ids are idempotent; an unknown id returns an error instead of pretending to succeed.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | the id of the memory to delete |
Output Schema
| Name | Required | Description |
|---|---|---|
| deleted | Yes | true when the memory is deleted (already-deleted ids are idempotent) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and does well: it discloses that deletion is soft (recoverable), that repeated calls on an already-deleted id are idempotent, and that an unknown id raises an error rather than silently succeeding. What's missing is auth/permission requirements and whether the memory can be restored and how.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two compact sentences with the core action front-loaded and the edge-case semantics immediately after. No filler, no restatement of the name.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Output schema exists, so return values need no explanation, and the description covers the two failure modes an agent must anticipate (idempotent re-delete, unknown-id error). For an unannotated mutation tool, the only real gap is permission/authorization context and recoverability.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single parameter is already documented as "the id of the memory to delete", so the schema does the work. The description only echoes "by id" without adding format, source, or example values, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and qualifier ("Soft-delete") plus the resource ("a memory") and the lookup key ("by id"). This is immediately distinguishable from siblings memory_add and memory_search, and the soft-delete qualifier alone tells an agent this is not a hard purge.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the verb — an agent knows to call this when removing a memory — but there is no explicit statement of when to prefer it, no prerequisites, and no mention of alternatives or how to recover a soft-deleted memory. The idempotency note is behavioral rather than usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_searchA
Search memories with vector search first and keyword fallback. degraded=true means the vector path was unavailable; degrade_reason explains why. An empty query lists recent memories.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | optional maximum number of hits; defaults to 10 | |
| query | No | optional search text; an empty query lists recent memories | |
| scope | No | optional scope filter; empty means all scopes | |
| include_expired | No | optional; when true, expired memories are included |
Output Schema
| Name | Required | Description |
|---|---|---|
| hits | Yes | |
| degraded | Yes | true when the vector path was unavailable and results came only from keyword search or the recent list |
| degrade_reason | Yes | why degradation happened: no_embedder, query_timeout, or vector_error; empty when not degraded |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries behavioral disclosure and does a good job explaining the vector-first keyword-fallback behavior. It also clarifies the meaning of degraded=true and degrade_reason, which are important operational states, though it does not discuss permissions or side effects.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three tight sentences with no waste, and the core purpose and fallback behavior are front-loaded. Every sentence contributes useful information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be fully explained, and the description still helpfully explains degraded and degrade_reason semantics. For a read-only search tool, this is largely complete, though it leaves implicit that the operation has no side effects.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so all four parameters are already documented in the input schema. The description only repeats the empty-query behavior already stated in the query parameter description, adding no new parameter-level semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Search memories', and adds implementation context with 'vector search first and keyword fallback'. It is clearly distinct from siblings like memory_add and memory_delete, though it does not explicitly name those siblings for routing.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear usage context: it explains the fallback order and notes that 'An empty query lists recent memories'. There are no competing search tools among the siblings, so explicit alternative routing is less necessary, but exclusions and prerequisites are not stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
4 tool updates
- First observed
echo - First observed
memory_add - First observed
memory_delete - First observed
memory_search
TDQS
Scored across 4 tools
Each tool has a clearly distinct purpose: echo is a wiring check, while memory_add, memory_delete, and memory_search map to separate lifecycle operations on a single resource. Descriptions reinforce the boundaries (e.g., soft-delete vs. store vs. hybrid search).
The three core tools follow a clean memory_verb snake_case pattern. echo is the only deviation, but as a conventional diagnostic utility it does not create confusion.
Four tools is slim but reasonable for a focused memory store; each earns its place except arguably echo, which is a wiring diagnostic rather than a memory operation. Not thin enough to be a mismatch.
Add, search (with list-recent fallback), and delete cover the core lifecycle, but there is no get-by-id or update/edit operation, so an agent cannot correct or inspect a specific memory without going through search. These are minor workaround-able gaps assuming memories are effectively immutable.
Maintenance
Related MCP Connectors
Persistent, inspectable memory for AI agents with lineage, correction, and a hosted MCP endpoint.
Persistent memory for AI agents. Semantic search, memory graph, W3C DID identity.
Hosted memory for AI agents over MCP. One memory shared by all your agents.
Private-by-default, local-first memory/context/task orchestrator for MCP apps and agents.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceSelf-hosted semantic memory for AI agents. Save worklogs, decisions, and notes via MCP, then recall them across sessions by meaning rather than keyword. Backed by Postgres + pgvector with local embeddings (multilingual-e5-base).1MIT
- FlicenseNot gradedqualityCmaintenanceA local-first, privacy-preserving centralized memory hub for AI agents — MCP-compatible, zero cloud egress.3-
- AlicenseAqualityCmaintenanceEmbedded memory and retrieval engine for AI agents, providing local-first memory with MCP support for multi-agent access control.316 PyPI2MIT
- AlicenseNot gradedqualityAmaintenanceAn offline-first, governed memory and knowledge server for AI agents that provides Remember, Search, Update, and Forget operations with hybrid retrieval, semantic embeddings, and NID-based authentication. It can be used as an MCP server via stdio or Streamable HTTP, enabling agents to persist and query memories and wiki knowledge.Apache 2.0