Skip to content

memory: safer persistence defaults, atomic writes, quotas, redaction, and destructive-operation guardrails #4117

@lcv-leo

Description

@lcv-leo

Summary

While auditing and hardening local MCP server usage, I reviewed @modelcontextprotocol/server-memory and built a local hardened wrapper for our environment. I am opening this issue to share findings and possible upstream improvements.

This is not a report about a known data loss incident in the upstream package. It is a defense-in-depth report about persistence defaults and operational safety for a memory tool exposed to autonomous agents.

Package/runtime reviewed

  • Package: @modelcontextprotocol/server-memory@2026.1.26
  • License observed: MIT
  • Author observed: Anthropic, PBC
  • Upstream repo/issue tracker: modelcontextprotocol/servers
  • npm maintainers observed from registry during local review: jspahrsummers, pcarleton, thedsp, ashwin-ant, ochafik

Findings

  1. Default persistence path can land inside the package directory.
    Local source evidence showed a default path based on path.dirname(fileURLToPath(import.meta.url)), with memory.jsonl under the package dist directory when MEMORY_FILE_PATH is not configured. This creates operational fragility:

    • package reinstall/update can remove or overwrite memory state
    • global/package directories are not intuitive user data locations
    • backup/sync behavior is unclear
  2. Writes appear to be whole-file writes without explicit atomic replace and lock discipline.
    For agent memory, interrupted writes or concurrent host usage can corrupt or lose state. This is especially relevant when the same memory server is configured across multiple MCP hosts.

  3. No obvious mutation journal or backup trail.
    Debugging accidental writes or deletes is hard without append-only audit entries or backup snapshots.

  4. Destructive tools are available by default.
    delete_entities, delete_observations, and delete_relations are useful, but high-impact. In an agent context, accidental deletion should require explicit operator opt-in or confirmation mode.

  5. No obvious quotas/caps.
    Large observations or graphs can become a memory/latency issue. Whole-graph reads and writes are also sensitive to unbounded growth.

  6. No obvious secret redaction before persistence.
    Memory can become a persistent prompt-injection and secret-retention surface if fetched web content, logs, stack traces, or credentials are persisted without sanitization.

  7. No namespace isolation by default.
    A single global memory file shared across workspace/host contexts can mix unrelated operational memories unless the user manually configures paths.

Local hardening behavior that worked well

For our local wrapper, we preserved the upstream-compatible 9 tool names:

create_entities, create_relations, add_observations, delete_entities,
delete_observations, delete_relations, read_graph, search_nodes, open_nodes

We added:

  • explicit persistence outside node_modules, defaulting to a user data path
  • refusal to persist under node_modules
  • namespace support for workspace separation
  • cross-process lock with stale-lock cleanup
  • temp-write plus rename, with backup file
  • append-only mutation audit journal
  • quotas for entity/relation counts and observation sizes
  • secret redaction before writes
  • destructive tools disabled unless LCV_MCP_MEMORY_ALLOW_DESTRUCTIVE=1

Validation from local wrapper

Smoke output from the hardened local wrapper:

memory tools: add_observations,create_entities,create_relations,delete_entities,delete_observations,delete_relations,open_nodes,read_graph,search_nodes
memory read_graph entities=0 relations=0

A redaction/destructive-operation check also behaved as intended in local testing:

create {"entities":[{"name":"redaction-proof","entityType":"test","observations":["token [REDACTED]"]}]}
delete [{"type":"text","text":"delete_entities is disabled by default. Set LCV_MCP_MEMORY_ALLOW_DESTRUCTIVE=1 to enable destructive memory mutations."}]

Suggested upstream changes

  • Default persistence to an OS/user data directory instead of the installed package directory.
  • Refuse or strongly warn when MEMORY_FILE_PATH resolves inside node_modules or another package-managed path.
  • Use atomic write/replace and cross-process locking.
  • Add backup and append-only mutation audit journal options.
  • Add quotas for graph size, entity count, relation count, and observation length.
  • Redact common secret patterns before persistence.
  • Add namespace support or document recommended per-workspace file paths.
  • Gate destructive tools behind an explicit environment flag or confirmation mechanism.

Compatibility note

Most of these can be additive and opt-in, except the safer default persistence path. If changing the default is too disruptive, a migration warning plus documented MEMORY_FILE_PATH recommendation would still help users avoid package-dir persistence.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions