You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
~/.memtomem/config.json (written by mm init) silently overrides any MEMTOMEM_* env var at server startup. Every integration doc we ship tells users to configure per-client settings like MEMTOMEM_INDEXING__MEMORY_DIRS / MEMTOMEM_STORAGE__SQLITE_PATH in an .mcp.json / claude_desktop_config.json / mcp_config.jsonenv block — but for any field persisted to config.json by the init wizard, those env entries are ignored. Users get the wizard-saved value regardless.
This is the opposite of the convention most pydantic-settings-based tools follow (env wins over file) and is undocumented.
Repro
mm init → wizard writes ~/.memtomem/config.json with indexing.memory_dirs = ["/tmp/memories"] (or whatever the user picked).
Register the server with a per-project env override, e.g.:
load_config_overrides (packages/memtomem/src/memtomem/config.py:542-588) iterates every section/key in config.json and unconditionally setattr(section_obj, key, value), so any field present in the file clobbers the env-var-bound value. There is no "env wins" path.
Impact — docs vs reality
Env-var overrides in MCP env blocks are the documented way to configure per-client paths. Pages that currently promise this works:
docs/guides/mcp-clients.md — Cursor / Windsurf / Claude Desktop / Gemini CLI / Antigravity sections all show MEMTOMEM_INDEXING__MEMORY_DIRS examples
docs/guides/integrations/claude-desktop.md:41-62 — both macOS and Windows blocks
docs/guides/cloud-sync.md:8 — explicitly tells users to point MEMTOMEM_INDEXING__MEMORY_DIRS at a cloud-synced folder
docs/guides/use-cases.md:54,74
For any user who ran mm init first (the recommended path), none of these examples work as documented for the fields the wizard persists (memory_dirs, sqlite_path, embedding.*, etc.).
Two real user scenarios this breaks:
Per-project memory_dirs: users with multiple MCP clients pointed at the same memtomem-server with different .mcp.json env blocks — all collapse to the wizard's single path.
Cloud-sync onboarding (cloud-sync.md): the doc tells users to set MEMTOMEM_INDEXING__MEMORY_DIRS to their Drive folder; post-mm init this is a no-op.
Separately but related: the bare-string-vs-JSON-array gotcha for list[Path] env vars (mcp-clients.md:274-278) becomes a secondary footgun — users who notice their env has no effect will try to "fix" it by unquoting the array, and then get a pydantic-settings crash instead of the same silent override.
Options for resolution
Not prescribing a fix — listing the alternatives for discussion:
Invert precedence: env wins over config.json. Most intuitive, matches pydantic-settings default semantics. Breaking change for anyone currently relying on config.json as the authoritative source after a mm config set followed by a stale env var — but in practice that combo seems rare (env vars in .mcp.json are long-lived; users don't typically leave stale ones around).
Only persist wizard fields to config.json if no matching env var is set — shifts the precedence decision to write-time instead of load-time. Less invasive at runtime but makes mm init non-deterministic (depends on env at wizard time).
Merge semantics for list fields (memory_dirs = env ∪ config.json). Solves the per-project case but adds a new mental model users have to learn. Doesn't help scalar fields like sqlite_path.
Keep current semantics, document them loudly. Update every MEMTOMEM_* env-var example in docs with a "⚠ only takes effect if this key is absent from ~/.memtomem/config.json" warning, and add a mm config unset <key> CLI for users who want to fall back to env. Minimal code change, maximal docs churn.
Config isolation by scope — support MEMTOMEM_CONFIG_PATH env var so per-project .mcp.json can point at a project-local config.json. Enables real per-project configs but grows surface area.
Any of 1–3 also needs a migration / release-notes entry since mm init-driven setups would change behavior.
Acceptance criteria (once a direction is picked)
Behavior matches what docs claim (or docs are updated to match behavior, explicitly and per-page)
tests/test_config.py (or a new test) pins the chosen precedence so it can't silently flip again
mcp-clients.md / cloud-sync.md / integrations/*.md / getting-started.md examples are verified end-to-end against the chosen behavior before PR merge (docs-as-tests)
Summary
~/.memtomem/config.json(written bymm init) silently overrides anyMEMTOMEM_*env var at server startup. Every integration doc we ship tells users to configure per-client settings likeMEMTOMEM_INDEXING__MEMORY_DIRS/MEMTOMEM_STORAGE__SQLITE_PATHin an.mcp.json/claude_desktop_config.json/mcp_config.jsonenvblock — but for any field persisted toconfig.jsonby the init wizard, those env entries are ignored. Users get the wizard-saved value regardless.This is the opposite of the convention most
pydantic-settings-based tools follow (env wins over file) and is undocumented.Repro
mm init→ wizard writes~/.memtomem/config.jsonwithindexing.memory_dirs = ["/tmp/memories"](or whatever the user picked).mem_status→ the server reports/tmp/memories, not the Drive path. The env override was silently dropped.Root cause
Startup sequence —
packages/memtomem/src/memtomem/server/component_factory.py:41-47:load_config_overrides(packages/memtomem/src/memtomem/config.py:542-588) iterates every section/key inconfig.jsonand unconditionallysetattr(section_obj, key, value), so any field present in the file clobbers the env-var-bound value. There is no "env wins" path.Impact — docs vs reality
Env-var overrides in MCP
envblocks are the documented way to configure per-client paths. Pages that currently promise this works:docs/guides/mcp-clients.md— Cursor / Windsurf / Claude Desktop / Gemini CLI / Antigravity sections all showMEMTOMEM_INDEXING__MEMORY_DIRSexamplesdocs/guides/integrations/claude-desktop.md:41-62— both macOS and Windows blocksdocs/guides/integrations/claude-code.md(after docs(mcp): document Claude Code install scopes (local/project/user) #247) —.mcp.jsonexampledocs/guides/getting-started.md:180,195docs/guides/cloud-sync.md:8— explicitly tells users to pointMEMTOMEM_INDEXING__MEMORY_DIRSat a cloud-synced folderdocs/guides/use-cases.md:54,74For any user who ran
mm initfirst (the recommended path), none of these examples work as documented for the fields the wizard persists (memory_dirs,sqlite_path,embedding.*, etc.).Two real user scenarios this breaks:
memtomem-serverwith different.mcp.jsonenv blocks — all collapse to the wizard's single path.cloud-sync.md): the doc tells users to setMEMTOMEM_INDEXING__MEMORY_DIRSto their Drive folder; post-mm initthis is a no-op.Separately but related: the bare-string-vs-JSON-array gotcha for
list[Path]env vars (mcp-clients.md:274-278) becomes a secondary footgun — users who notice their env has no effect will try to "fix" it by unquoting the array, and then get a pydantic-settings crash instead of the same silent override.Options for resolution
Not prescribing a fix — listing the alternatives for discussion:
config.json. Most intuitive, matches pydantic-settings default semantics. Breaking change for anyone currently relying onconfig.jsonas the authoritative source after amm config setfollowed by a stale env var — but in practice that combo seems rare (env vars in.mcp.jsonare long-lived; users don't typically leave stale ones around).config.jsonif no matching env var is set — shifts the precedence decision to write-time instead of load-time. Less invasive at runtime but makesmm initnon-deterministic (depends on env at wizard time).memory_dirs= env ∪ config.json). Solves the per-project case but adds a new mental model users have to learn. Doesn't help scalar fields likesqlite_path.MEMTOMEM_*env-var example in docs with a "⚠ only takes effect if this key is absent from~/.memtomem/config.json" warning, and add amm config unset <key>CLI for users who want to fall back to env. Minimal code change, maximal docs churn.MEMTOMEM_CONFIG_PATHenv var so per-project.mcp.jsoncan point at a project-localconfig.json. Enables real per-project configs but grows surface area.Any of 1–3 also needs a migration / release-notes entry since
mm init-driven setups would change behavior.Acceptance criteria (once a direction is picked)
tests/test_config.py(or a new test) pins the chosen precedence so it can't silently flip againmcp-clients.md/cloud-sync.md/integrations/*.md/getting-started.mdexamples are verified end-to-end against the chosen behavior before PR merge (docs-as-tests)