Grounded, repository-bound historical memory for MCP coding agents, backed by Google Gemini File Search.
The server stores deliberate project summaries and selected project files, then retrieves narrowly relevant passages with source metadata. Its output is historical, advisory, and untrusted: current repository files, tests, runtime state, and production evidence always take precedence.
- Binds project-specific reads and all writes to an explicit repository path.
- Stores redacted summaries with observation time, recording time, freshness, sensitivity, content hash, and (when available) Git commit metadata.
- Retrieves focused context with citations and abstains when File Search returns no grounded evidence.
- Reads existing legacy stores when their identity is unambiguous. On the first new write, it creates an embedding-2 shadow store, writes there, and searches the new and legacy stores together while the legacy store remains available.
- Deduplicates repeated session summaries and project files locally by content hash.
- Audits store counts, empty stores, legacy embeddings, aliases, and safety settings without reading document contents.
It does not provide live project truth, replace source control or backups, automatically create memory for every task, upload raw transcripts, or install compaction hooks. It is not a secret vault. Cross-project, name-only reads must be explicitly enabled by the caller.
MCP client or gmem CLI
|
v
repository identity + safety policy
|
+--> permission-restricted local registry
| (path bindings and content hashes)
|
v
async Gemini gateway with bounded retries
|
v
Gemini File Search store --> grounded answer + normalized citations
The local repository fingerprint is derived from the Git remote when available, or from the resolved local path otherwise. The path itself is not sent as the fingerprint. Stored content and metadata are sent to Google Gemini File Search.
All audited legacy stores use models/gemini-embedding-001, whose
published shutdown date
is 14 July 2026. V2 protects new writes with
models/gemini-embedding-2; it cannot losslessly re-embed old cloud-only
documents because File Search does not expose their original contents for
download. See Migration.
Requirements: Python 3.11+, uv, a Gemini API key,
and a supported operating-system keyring.
cd /path/to/Gemini-Memory-MCP-v2
uv sync --all-groups
uv run gmem key setgmem key set reads the key without echoing it and stores it under the
gemini-memory-mcp service in the OS keyring. GEMINI_API_KEY remains available
for ephemeral environments, but should not be written into client configuration
or committed files.
The migration script updates the clients to run this checkout, removes matching
legacy memory/compaction hooks, and removes plaintext Gemini keys from their MCP
entries. It is a dry run unless --apply is supplied.
uv run python scripts/configure_clients.py
uv run python scripts/configure_clients.py --applyApply mode creates a permission-restricted backup before changing client files. The Codex registration uses a 300-second tool timeout so bounded listing, fallback, ingestion, and retry sequences are not cancelled by the client first. Restart Codex and Claude after applying; already-running MCP processes do not reload their configuration. See Migration before replacing an existing installation.
For another MCP client, register this checkout's .venv/bin/python as the
command and the absolute path to server.py as its only argument. Do not put the
API key in that registration.
| Tool | Purpose |
|---|---|
gemini_memory_create_store |
Create repository-bound memory deliberately. |
gemini_memory_retrieve_context |
Retrieve focused, cited historical context. |
gemini_memory_save_session |
Save one meaningful, redacted summary. |
gemini_memory_upload_file |
Upload one secret-scanned UTF-8 text file inside the repository. |
gemini_memory_list_stores |
List store and document-state counts. |
gemini_memory_list_documents |
List document metadata, not document contents. |
gemini_memory_audit |
Run a read-only operational and safety audit. |
gemini_memory_health |
Check provider access and non-secret configuration. |
gemini_memory_consolidate |
Report eligibility only; unsafe v1 deletion is disabled. |
gemini_memory_delete_document |
Delete one path-bound document with exact-name confirmation. |
gemini_memory_delete_store |
Delete one path-bound store with exact-name confirmation. |
Normal calls should pass the active project_path. Name-only retrieval or
listing is rejected unless allow_cross_project=true is deliberately supplied.
Retrieval modes are focused, overview, and current_state; all three remain
historical and require live verification.
Create, save, upload, and delete tools are declared non-idempotent because the
provider does not expose an end-to-end idempotency key. Local locks and content
hashes suppress common duplicate writes, but clients must not blindly retry a
timed-out write. Inspect the resulting store or document state first. Destructive
calls additionally require an exact resource-name confirmation and a
project_path whose resolved binding contains that resource.
Retrieval results report searched_store_names. If a deprecated legacy shadow
is rejected, they also report candidate stores and
search_fallback_reason=legacy_store_unavailable_primary_only, making it clear
that only the embedding-2 primary was actually searched.
Returned context is assembled only from valid provider-attributed byte spans;
uncited Gemini prose is discarded. Accordingly, citation_coverage describes
the returned context and is 1.0 for a successful retrieval.
provider_response_citation_coverage records the raw Gemini answer ratio as a
non-gating diagnostic. That raw ratio can vary because of headings and
connective prose and is not used as a proxy for semantic relevance.
The CLI intentionally exposes common safe operations. Destructive deletion is available through the confirmation-gated MCP tools, not the CLI.
uv run gmem key status
uv run gmem init --path /absolute/path/to/repository
uv run gmem context "authentication decisions" --path /absolute/path/to/repository --tokens 3000
uv run gmem save --path /absolute/path/to/repository --label auth-fix \
--message "Recorded outcome, evidence, files changed, and remaining uncertainty."
uv run gmem upload README.md --path /absolute/path/to/repository
uv run gmem docs --path /absolute/path/to/repository
uv run gmem stores
uv run gmem audit
uv run gmem healthsave also accepts a summary on standard input. Save only durable decisions,
verified outcomes, and useful handoff context; do not pipe raw conversations or
logs into it.
| Variable | Default | Meaning |
|---|---|---|
GEMINI_API_KEY |
OS keyring fallback | Process-only credential override. |
GEMINI_RETRIEVAL_MODEL |
gemini-3.5-flash |
Grounded retrieval model. |
GEMINI_SUMMARY_MODEL |
gemini-3.5-flash |
Reserved summary model setting. |
GEMINI_FALLBACK_MODEL |
gemini-3.1-flash-lite |
Reserved fallback model setting. |
GEMINI_EMBEDDING_MODEL |
models/gemini-embedding-2 |
Embedding model for newly created stores. |
GEMINI_MEMORY_MAX_TOKENS |
3000 |
Default answer limit; valid range 500-16000. |
GEMINI_MEMORY_TOP_K |
8 |
Default File Search result count. |
GEMINI_MEMORY_TIMEOUT_SECONDS |
45 |
Request/ingestion timeout. |
GEMINI_MEMORY_MAX_RETRIES |
2 |
Bounded retries for transient failures. |
GEMINI_MEMORY_STATE_DIR |
platform state directory | Local registry location. |
GEMINI_MEMORY_REDACT |
1 |
Set to 0, false, or no only with care. |
GEMINI_MEMORY_RETRIEVAL_BACKEND |
generate_content |
generate_content or interactions. |
Provider availability, pricing, quotas, and retention terms can change. Check Google's current Gemini documentation rather than relying on this repository for commercial limits.
uv run ruff check .
uv run mypy src
uv run pytest
uv run gmem key status
uv run gmem healthThe first three checks are local and deterministic. health uses the configured
credential and makes a live provider request; it is the final configuration
check before restarting clients.
The final frozen-tree release gate passed 152 tests, Ruff, and strict mypy over 16 source modules.
For repeatable existing-store retrieval probes, copy
benchmark/cases.example.json, bind its project paths through environment
variables, and run:
uv run python benchmark/run_live.py --cases /path/to/cases.jsonThe harness validates its case schema, uses isolated temporary local state by default, and reports the case-file hash, runtime/model configuration, statuses, source titles, citation coverage, latency, and expected-term checks. It does not print retrieved context.
The committed benchmark/cases.fixed.json is the exact three-case legacy-account
probe used during the v2 evaluation. It deliberately contains no home-directory
paths or API keys. To replay it against the same controlled projects and Gemini
account:
export GEMINI_MEMORY_BENCH_APPSEED_ROOT=/path/to/appseed
export GEMINI_MEMORY_BENCH_VUELUM_ROOT=/path/to/vuelum
uv run python benchmark/run_live.py \
--cases benchmark/cases.fixed.json \
--output benchmark/fixed-live-results-2026-07-13.jsonThis live holdout depends on the named account's existing historical stores; it is reproducible evidence for this migration, not a provider-independent unit test. Empty-store behavior is covered by deterministic tests instead of a temporary cloud artifact. The deterministic suite remains the portable release gate.
The final fingerprinted legacy report is
benchmark/fixed-live-results-2026-07-13.json (3/3), bound to implementation
c3fdb6fc...e61b and corrected case file 3719f8fe...268. The known Appseed
answer was returned in 10.192 seconds with 1.0 returned-context coverage and
0.595 raw-provider coverage; the absent nonce abstained in 11.864 seconds; and
the explicit cross-project request was blocked without a provider search in
4.077 seconds.
This is post-failure corrected acceptance, not an untouched holdout. Two valid
2/3 failures are retained: the be3db68d...54eb4 positive-grounding run
suppressed a useful answer under the old raw-coverage gate, and the
b57b6d32...bd33 term-recall run showed that the case expected terms not
needed by the question: it unnecessarily required the generic query noun
mobile, even though a correct label answer need not repeat that noun. The
corrected case contract was then run once on b57b6d32...bd33 and again on the
final frozen tree. The 1/3
pre-offset run is also retained as earlier citation-offset and project-scope
failure evidence. These artifacts show the improvement sequence; only the
c3fdb6fc...e61b report is final legacy acceptance.
For a controlled provider contract check, run the synthetic holdout:
uv run python benchmark/run_synthetic.py \
--output benchmark/synthetic-live-results-2026-07-13.jsonIt creates a uniquely named temporary embedding-2 store, writes the committed
temporal/conflict and expired-evidence fixture, runs three case-level checks,
and exact-deletes that store in a finally cleanup. The result records the
fixture hash, models, source titles, citation coverage, latency, and cleanup
status without printing retrieved context.
The latest controlled result is
benchmark/synthetic-live-results-2026-07-13.json (3/3), bound to implementation
c7799a4e...32b. All three documents were accepted using explicit
text/markdown MIME metadata; temporal conflict retrieval returned both
decision codes from two sources, expired evidence was suppressed, and the
absent nonce abstained. Exact cleanup deleted the same store that setup created,
and an independent provider inventory found zero remaining synthetic benchmark
stores. The earlier fingerprinted MIME-setup failure is preserved separately;
its store was also deleted and it is not acceptance evidence.
New live reports include implementation_sha256, binding the result to every
Python package file, pyproject.toml, uv.lock, and the relevant runner. Case
and fixture hashes remain separate. See the exact framing algorithm in the
benchmark methodology.
These benchmarks test specific safety and retrieval contracts. They do not establish global answer quality or prove that memory pays for its latency on an arbitrary project. V2 is therefore intended for a guarded, focused opt-in trial; its practical value must be measured by reduced re-discovery in real work. See the full benchmark methodology.
Gemini File Search stores are remote resources. Removing this checkout, client
registration, hooks, or the local key does not delete them. Inventory stores
with gmem stores, then use the exact-confirmation MCP deletion tools (or the
Google API) only after reviewing the full resource names. Existing v1 stores are
left unchanged during migration.
gmem key delete removes only the local keyring entry. Client backups produced
by configure_clients.py --apply may contain historical plaintext credentials;
protect and remove them according to Security.
Use the global-agent snippet in Policy. The core rule is simple: retrieve once and narrowly when it will help, save only meaningful changes, and verify everything important against live evidence.
MIT