Skip to content

filesystem: edit_file newText interpreted as String.prototype.replace replacement-pattern (literal $ corrupted) #4157

@salazaralan73-eng

Description

@salazaralan73-eng

Summary

edit_file in @modelcontextprotocol/server-filesystem silently corrupts content containing literal $ characters. The root cause is that applyFileEdits (in src/filesystem/lib.ts) invokes JavaScript's String.prototype.replace(searchString, replacementString) where the second argument is interpreted as a replacement string pattern, not as a literal string.

Pattern Replaced by
$$ a single $
$& the matched substring
$` the substring preceding the match
$' the substring following the match

Affected file & line

src/filesystem/lib.ts, inside applyFileEdits():

if (modifiedContent.includes(normalizedOld)) {
  modifiedContent = modifiedContent.replace(normalizedOld, normalizedNew); // ← bug
  continue;
}

The fallback line-by-line path (using splice / join) is not affected.

Minimal reproduction

mkdir /tmp/mcp-repro && cd /tmp/mcp-repro
printf 'price: PLACEHOLDER\n' > sample.txt
# from any MCP client, invoke edit_file:
# oldText: "PLACEHOLDER"
# newText: "$$100 USD"
# expected: price: $$100 USD
# actual:   price: $100 USD   ← $$ collapsed to single $

Same corruption with $&, $`, $'.

Proposed fix (one line)

modifiedContent = modifiedContent.replace(normalizedOld, () => normalizedNew);

The callback form treats the return value as literal, disabling replacement-pattern interpretation while preserving "replace first occurrence only" semantics.

Impact

  • Affects ~297k weekly downloads of @modelcontextprotocol/server-filesystem
  • Silent corruption: tool reports success and returns a diff, but on-disk content differs from intent
  • Particularly affects JS/TS/PHP code ($ variables), currency, regex source, shell scripts

Workaround (until fix lands)

Escape $ in newText by doubling it ($$$).

Related

#2033 — different manifestation of $ handling in newText (that issue reports a parse error; this one reports silent content corruption on successful edits).

Willing to open a PR with the one-line fix + parametric tests if maintainers approve direction.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    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