Last 12 weeks · 53 commits
5 of 6 standards met
Server: @modelcontextprotocol/server-filesystem OS: Linux (Ubuntu) Description: fails with ENOENT when the source path contains non-ASCII characters such as French accented letters (é, è, à), ligatures (œ), or typographic apostrophes ('). Other tools like , , and handle these characters correctly. Steps to reproduce: 1. Have a file named e.g. 2. Call with that path as source 3. → ENOENT: no such file or directory Expected: file is moved/renamed Actual: ENOENT error
Package: Version: Node: OS: Windows 11 (server process on Windows; reached over HTTP via supergateway) Summary overwrites an existing destination file without warning and reports success. The README states the opposite: move_file Move or rename files and directories Inputs: (string), (string) Fails if destination exists The tool description returned over the wire says the same thing: If the destination exists, the operation will fail. Because no existence check is performed, the destination file's contents are destroyed and unrecoverable. This is a data-loss bug, not just a documentation error: an agent that reads "fails if destination exists" will reasonably treat as a safe, non-clobbering operation and use it without first checking whether the target is occupied. The impact is amplified by the fact that this server ships no delete tool. Users (and agents) reasonably infer that the toolset cannot destroy existing files, only create and modify them. quietly provides arbitrary file deletion: move anything onto a target and the target is gone. That inference is actively encouraged by the README, which is itself inconsistent here. The Features section advertises: Read/write files · Create/list/delete directories · Move files/directories No delete tool exists in the API section or in the tool list returned at runtime. So a reader is told deletion is available, cannot find it, concludes the toolset is non-destructive by design — and then loses data to . Reproduction Two files with different names, different contents, and different sizes, so the result is unambiguous. 1. -> 2. -> 3. on : 4. with , Expected: error — destination exists. Actual: success, no warning: 5. on : 6. on returns ALPHA's contents. The marker is gone; 307 bytes were destroyed by an operation documented to refuse. Cause Presumably a bare with no prior existence check — which clobbers on every platform Node supports. POSIX replaces an existing destination by definition. On Windows, libuv's calls with (added in joyent/libuv#283 precisely to match Linux behaviour), and per the Win32 docs that flag replaces the contents of the destination file. The libuv docs accordingly describe as equivalent to . So the overwrite is the platform default in both cases, and the README documents an intent that was never implemented in code. Note on tool annotations currently carries , but the README's own rationale for it is "Deletes source file" — i.e. the annotation is justified by the loss of the source, and the destination is still assumed safe. See #2988, which proposed on the explicit grounds that "a repeat typically errors because source no longer exists and README says 'fails if destination exists'". So the reasoning around this tool's safety has been anchored on a README claim the code does not honour. Worth revisiting the note and rationale even though the flag itself happens to be correct. Suggested fix Either: (a) Implement the documented behaviour — the destination first and reject if it exists: Note this is TOCTOU-racy but adequate for this server's threat model; a stricter option on Linux is with . (b) Keep the overwrite behaviour but fix the README and the tool description to say so plainly, mirroring 's existing wording ("exercise caution with this"), and add an explicit input defaulting to . (a) is preferable: it matches every published description of the tool, and clients that gate on today do so believing the destination is protected.
Summary While auditing and hardening local MCP server usage, I reviewed 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: License observed: MIT Author observed: Anthropic, PBC Upstream repo/issue tracker: npm maintainers observed from registry during local review: , , , , Findings 1. Default persistence path can land inside the package directory. Local source evidence showed a default path based on , with under the package directory when 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. , , and 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: We added: explicit persistence outside , defaulting to a user data path refusal to persist under 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 Validation from local wrapper Smoke output from the hardened local wrapper: A redaction/destructive-operation check also behaved as intended in local testing: Suggested upstream changes Default persistence to an OS/user data directory instead of the installed package directory. Refuse or strongly warn when resolves inside 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 recommendation would still help users avoid package-dir persistence.
Repository: modelcontextprotocol/servers. Description: Model Context Protocol Servers Stars: 89528, Forks: 11437. Primary language: TypeScript. Languages: TypeScript (70.6%), Python (18%), JavaScript (10.2%), Dockerfile (1.1%). Homepage: https://modelcontextprotocol.io Latest release: 2026.7.10 (1mo ago). Open PRs: 100, open issues: 409. Last activity: 3d ago. Community health: 87%. Top contributors: olaservo, tadasant, cliffhall, jspahrsummers, dsp-ant, jerome3o-anthropic, maheshmurag, evalstate, baryhuang, marcelo-ochoa and others.