Skip to main content
Glama
francodelportdev

surgical-editing-server

Surgical Editing

Transactional file editing for AI agents: atomic multi-file changes that protect your code, and self-correcting errors that save tokens.

An exact-match editing engine for any MCP client (Cline, Claude Desktop, Python agents, CI/CD pipelines…), exposed as a single tool: surgical_replace.

What is Surgical Editing?

Agents often rewrite whole lines or blocks just to change one word. That costs output tokens and time, and every retyped character is another chance to break something. With Surgical Editing (Edición Quirúrgica, also called Delta Generation) the model sends only the exact snippet to find and its replacement.

surgical_replace guarantees:

  • Exact and unique: every find must appear exactly once in its file. If it appears 0 times or more than once, the call fails. Edits never use fuzzy matching and never guess.

  • Transactional: one call can edit many files. If any replacement fails, no file is written.

  • Self-correcting errors: on a miss, the error points to the most similar region of the file, so the agent can fix its snippet without re-reading the file.

  • Dry run: validate everything and get a unified diff without touching the disk.

  • Efficient I/O: each file is read once, edited in memory and written once.

Related MCP server: Ketan-OS MCP Server

Usage

Multi-file transaction

{
  "operations": [
    {
      "file_path": "src/config.ts",
      "replacements": [{ "find": "port = 3000", "replace": "port = 8080" }]
    },
    {
      "file_path": "src/server.ts",
      "replacements": [{ "find": "listen(port)", "replace": "listen(port, host)" }]
    }
  ],
  "dry_run": false
}
  • Replacements run in order. Operations that target the same file are applied one after another on the same in-memory content.

  • A $ in replace is inserted as-is and is never read as a regex pattern ($&, $1…).

  • The legacy v1 form { "file_path": "...", "replacements": [...] } is still accepted. Don't combine it with operations in the same call.

Error diagnostics

When a find has no exact match, the tool looks for a region at least 80% similar and reports it. If the only difference is whitespace or line endings, the error adds a hint:

Operation #0, Replacement #0 (/repo/src/app.ts): No exact match found. Line 12 contains (96% similar): 'const x = 1'. Hint: File uses CRLF line endings. No files were written.

Ambiguous matches report how many times the snippet appears and on which lines (appears 3 times (lines 4, 18, 52)), so the agent knows which context to add.

Verifiable results

On success, each replacement comes back with its final line number and a numbered excerpt of the result, so the agent can check its edit without re-reading the file:

Applied 1 replacement(s) across 1 file(s):
  /repo/src/config.ts
    op #0 #0 (line 4): "retries = 3" → "retries = 5"
      3 | const host = "localhost";
      4 | const retries = 5;
      5 | export { port, host, retries };
``` File system errors (missing files, permission denied, read-only file systems) are returned as structured MCP errors (`isError: true`).

### Dry run

With `"dry_run": true`, the tool runs every validation and returns a unified diff per file, without writing anything:

```diff
--- /repo/src/config.ts
+++ /repo/src/config.ts
@@ -1,3 +1,3 @@
 import { env } from "./env";
-export const port = 3000;
+export const port = 8080;
 export const host = env.HOST;

How atomicity works

  1. Every file is read and every replacement is validated in memory. Any failure aborts the call before anything is written.

  2. Each new version is written to a temp file next to its target. Most I/O failures (permissions, a full disk) happen at this step, while the originals are still untouched. If one fails, all temp files are removed.

  3. The temp files replace the originals via rename(), which is atomic per file on POSIX. If a rename still fails, files that were already replaced are restored to their original content.

Installation

git clone <repo> surgical-editing
cd surgical-editing
npm install
npm run build

Once the package is on npm, you can run it with npx -y surgical-editing instead.

Configuration

Claude Code

claude mcp add surgical-editing-server -- node /absolute/path/to/surgical-editing/dist/index.js
# or, once it is on npm:
claude mcp add surgical-editing-server -- npx -y surgical-editing

Other MCP clients

Most clients (Cline, Claude Desktop via claude_desktop_config.json, project .mcp.json) accept this shape:

{
  "mcpServers": {
    "surgical-editing-server": {
      "command": "node",
      "args": ["/absolute/path/to/surgical-editing/dist/index.js"]
    }
  }
}

Relative file_path values resolve against the server's working directory. Use absolute paths when that is ambiguous.

Sandbox

Restrict edits to one directory tree with the SURGICAL_ROOT_DIR environment variable or the --root <dir> flag (the flag wins):

{
  "mcpServers": {
    "surgical-editing-server": {
      "command": "node",
      "args": ["/absolute/path/to/surgical-editing/dist/index.js", "--root", "/path/to/project"]
    }
  }
}

When a root is set, relative paths resolve against it. Any target outside it fails with Path traversal blocked, including through a symlink that points outside the root. If the root itself doesn't exist, the server refuses to start.

Safety guarantees

  • Symlinks: edits are applied to the file the link points to, and the link stays intact.

  • Encoding: invalid UTF-8 and binary files (with NUL bytes) are rejected before anything is written. A UTF-8 BOM is preserved.

  • Concurrent changes: if a file's mtime or size changes between the read and the commit, the whole transaction is aborted.

Known limitations

These come from Node.js and POSIX file semantics. They are documented on purpose rather than patched with non-portable workarounds.

  • Residual race window: the concurrent-modification check runs right before the renames, but a write that lands between that check and the rename() can still be lost. Closing that gap needs advisory locks (flock), which Node.js does not expose portably.

  • Hard links: a file is replaced through temp file + rename(), so a file with several hard links is split. The edited path gets the new content and the other links keep the old content.

  • File ownership: permission bits are preserved, but the owner and group are not. If the server runs as a different user than the file's owner, the edited file will belong to the server's user.

Development

npm run dev        # run from source with tsx
npm run build      # compile to dist/
npm run typecheck  # type-check src/ and tests/
npm test           # typecheck, then run the node:test suite

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    F
    maintenance
    Enables AI agents to safely explore directories, read files, search content by pattern or filename, and edit files with checksum verification and dry-run preview within sandboxed filesystem access.
    98 npm
    75
    ISC
  • A
    license
    A
    quality
    B
    maintenance
    Provides transactional intelligence for AI agents, enabling safe tool execution with pre-flight invariant checks, sub-second filesystem snapshots/rollback, causal tracing, belief contradiction detection, and 15 native MCP tools for Claude Code.
    15
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Snapshot-based file editing for AI agents. Enables safe file edits by sending only new content against immutable, content-addressed snapshots with 3-way merging and ambiguity protection.
    2
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables agents to read, write, edit, search, rename, move, and delete files, perform regex search-and-replace across trees, query and modify JSON documents, and inspect git status and diffs, all through structured tools instead of shell commands. Includes safeguards like dry-run previews, atomic multi-file edits, and protection for denied or sensitive paths.
    MIT