filetools
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@filetoolsCan you replace lines 10-12 in src/app.ts with the new handler code?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
fileTools
A robust file–editing tool suite for AI agents, exposed as a Model Context Protocol (MCP) server (and usable as a plain TypeScript library). It fixes the editing failures that plague LLM coding agents — Could not find oldString, Windows CRLF mismatches, line-number drift, ambiguous duplicate matches, and regex corruption of replacement text.
Point any MCP-capable client (opencode, Claude Desktop, …) at it and the model gets three reliable tools: viewFile, replaceFileContent, writeToFile.
Why
The built-in edit tool of most agents does an exact string match of an oldString the model reproduces from memory. On real projects that fails constantly:
Indentation / whitespace drift — the model guesses 2 spaces where the file has 4.
Windows CRLF — the read path strips
\r, so the model sends LF and the exact match misses.Line drift — a previous edit shifted the lines the model was targeting.
Ambiguity —
return true;appears ten times and the wrong one gets replaced.Regex corruption —
String.replace()mangles replacements containing$1,$&,$`.
replaceFileContent removes all of these: matching is scoped to a [startLine, endLine] window (read first, then edit from ground truth), replacement is byte-literal, line endings are detected and preserved, writes are atomic, and on a mismatch it returns the actual current content of the range so the model corrects from reality instead of guessing.
Related MCP server: mcp-hashline-edit-server
Requirements
Node ≥ 23 (the server imports the TypeScript library directly via native type-stripping).
Tools
Tool | Purpose |
| Read a file with 1-based line numbers and bounded slicing ( |
| Replace exact |
| Create a new file or atomically overwrite/append an existing one (with overwrite protection). |
Install into opencode
AI agent: this is a runbook. Do steps 1–5 exactly, in order. Do NOT skip the preflight. You CANNOT finish the install yourself — step 6 (restarting opencode) must be done by the human, because opencode loads MCP servers and permissions only at startup. End your turn by telling them to do it.
1. Preflight — Node version (hard requirement)
Run:
node --versionThis server imports a TypeScript file directly, which needs Node ≥ 23.
v23or newer → continue.v22.6–v22.x→ it works but the command must pass--experimental-strip-types(see note in step 4).Older than
v22.6→ STOP. Tell the human to upgrade Node, then restart this setup. Do not continue.
2. Clone and install
Pick a permanent location (NOT a temp dir — opencode will launch it on every start):
git clone https://github.com/testAccountDeltas/fileTools.git
cd fileTools
npm install3. Get the absolute path
# macOS / Linux
pwd # -> e.g. /Users/alex/tools/fileTools
# Windows (PowerShell)
(Get-Location).Path # -> e.g. C:\Users\alex\tools\fileToolsThe server file is <that path>/mcp-server.mjs. Use the absolute path in step 4 — never a relative one.
4. Edit the opencode config
Config file location:
macOS / Linux:
~/.config/opencode/opencode.jsonWindows:
%USERPROFILE%\.config\opencode\opencode.json
Merge these two keys into the existing JSON — do not overwrite the whole file:
{
"mcp": {
"filetools": {
"type": "local",
"command": ["node", "/ABSOLUTE/PATH/TO/fileTools/mcp-server.mjs"],
"enabled": true
}
},
"permission": {
"*": "allow",
"edit": "deny"
}
}Merge rules:
If an
mcpobject already exists, add thefiletoolsentry inside it (keep the others).If a
permissionobject already exists, just add"edit": "deny"to it (keep the rest). Only add"*": "allow"if there is no permission block at all.Replace
/ABSOLUTE/PATH/TO/fileTools/mcp-server.mjswith the real path from step 3. On Windows use forward slashes, e.g.C:/Users/alex/tools/fileTools/mcp-server.mjs.Node 22.6–22.x only: change the command to
["node", "--experimental-strip-types", "/ABSOLUTE/PATH/.../mcp-server.mjs"].
Why "edit": "deny": it removes opencode's built-in edit from the model's toolset so the model uses filetools_replaceFileContent instead. Without it, both exist and the brittle built-in may still be chosen.
5. Sanity-check the server runs
node mcp-server.mjsExpected on stderr: filetools MCP server ready (root: ...). It then waits for stdin — press Ctrl+C to exit. If instead you see Cannot find package '@modelcontextprotocol/sdk', step 2's npm install didn't run. If you see a TypeScript/ERR_UNKNOWN_FILE_EXTENSION error, the Node version is too old (go back to step 1).
Optionally run the full smoke test:
npm run smoke # expects "5 passed, 0 failed"6. Hand off — the human restarts opencode
Config and MCP servers are read only at startup. Fully quit and reopen opencode (the desktop app, or stop the running opencode process and start a new session) — a window reload is not enough.
After restart, the model edits via filetools_replaceFileContent: line-range scoped, CRLF-safe, no more Could not find oldString. The server resolves relative paths against the project directory opencode launches it in.
Recommended agent rules
The tools are robust on their own, but a couple of habits make the model use them
flawlessly. Copy these into your agent's instructions — for opencode, your global
AGENTS.md (~/.config/opencode/AGENTS.md), not a file inside this repo (a repo-local
AGENTS.md would only apply while editing this repo):
- Edit files ONLY via `filetools_replaceFileContent` (and `filetools_viewFile` to read a
range). If the built-in `edit`/`grep`/`glob` are disabled, don't call them — search file
contents through the shell instead.
- Ground truth before every edit: immediately before `filetools_replaceFileContent`, call
`filetools_viewFile` on the exact range — even if you read the file earlier. Context-saving
("don't re-read unchanged files") does NOT apply to editing; editing from memory causes
"Could not find oldString" and line drift.
- Copy `targetContent` byte-for-byte from the `filetools_viewFile` output; never type it from
memory. Set `startLine`/`endLine` to that range.
- On a mismatch the tool returns the real current content of the range — fix from that, don't
guess.Use it with other MCP clients (e.g. Claude Desktop)
{
"mcpServers": {
"filetools": {
"command": "node",
"args": ["/absolute/path/to/fileTools/mcp-server.mjs"],
"env": { "FILETOOLS_ROOT": "/absolute/path/to/your/project" }
}
}
}FILETOOLS_ROOT (or the first CLI argument) overrides the workspace root; it defaults to the process working directory.
Use it as a library
import { FileToolSuite } from "./fileTools.ts";
const tools = new FileToolSuite(process.cwd());
tools.viewFile({ targetFile: "src/app.ts", startLine: 1, endLine: 50 });
tools.replaceFileContent({
targetFile: "src/app.ts",
startLine: 15, endLine: 16,
targetContent: " const port = 3000;",
replacementContent: " const port = process.env.PORT || 8080;",
});FileToolSuite.getToolDefinitions() returns JSON schemas ready for OpenAI, Anthropic, Gemini function-calling, or MCP.
Develop / test
npm test # 17 unit tests (node:test)
npm run demo # scripted agent walkthrough with diffs + self-correction
npm run smoke # spins up the MCP server and exercises it over the protocolLicense
MIT
Available Tools
3 toolsreplaceFileContentC
Surgically replace exact targetContent inside bounded lines [startLine, endLine].
| Name | Required | Description | Default |
|---|---|---|---|
| endLine | Yes | End line of search window. | |
| startLine | Yes | Start line of search window. | |
| targetFile | Yes | Path to target file. | |
| instruction | No | What this edit does. | |
| allowMultiple | No | Allow replacing multiple matches. | |
| targetContent | Yes | Exact string to be replaced. | |
| replacementContent | Yes | New string to insert. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and falls short. It does not say what happens when targetContent is not found, when it matches multiple times (despite an allowMultiple parameter existing), whether the file must exist, or what the result looks like. Only the replacement mechanics are conveyed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no padding; the core action and line-bounded scope come first. 'Surgically' is the only non-earning word, and the sentence is arguably too terse given the unimplemented behavioral gaps.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with 7 parameters, no annotations and no output schema, the description omits the essential behavior around multiple matches, no-match/failure handling, and the two optional parameters (instruction, allowMultiple). An agent cannot confidently predict the outcome of a call from this text alone.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all seven parameters, including targetContent, startLine/endLine, allowMultiple and instruction. The description adds no syntax, format, or constraint detail beyond the schema; baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (replace) on a specific resource (file content) and adds scope: exact targetContent, bounded by [startLine, endLine]. This implicitly separates it from writeToFile (wholesale write) and viewFile (read), though it never names those siblings. 'Surgically' is flavor rather than clarification.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit when-to-use or when-not-to-use guidance, and no alternative tool is named despite two close siblings (writeToFile for full rewrites, viewFile for reads). The bounded-window scope hints at targeted edits, but the agent must infer that replaceFileContent is preferred over writeToFile.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
viewFileB
Read contents of a file with line numbering and bounded slicing.
| Name | Required | Description | Default |
|---|---|---|---|
| endLine | No | Ending line (1-indexed, inclusive). | |
| maxBytes | No | Max bytes allowed in output. | |
| maxLines | No | Max lines to view at once. Default 800. | |
| startLine | No | Starting line (1-indexed, inclusive). | |
| targetFile | Yes | Absolute or workspace-relative path. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It does disclose two real behavioral traits beyond the schema: output is line-numbered and slicing is bounded. However, it says nothing about truncation when bounds are exceeded, error behavior for missing/binary files, or the default line cap (that detail lives only in the schema).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with zero filler; the core action comes first and the qualifiers follow. It is efficient, though arguably it errs on the terse side for a 5-parameter tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a no-annotation, no-output-schema read tool with five parameters, the description is minimally adequate. It omits the interaction between maxBytes/maxLines, what happens on truncation, and any error semantics, which an agent would need to call it confidently at the edges.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the parameters (startLine, endLine, maxLines, maxBytes, targetFile) are already fully documented and the baseline is 3. The description's 'line numbering' and 'bounded slicing' phrasing only loosely connects to those params and adds no new syntax or constraint detail.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
'Read contents of a file' states a specific verb and resource, and the modifiers 'line numbering' and 'bounded slicing' narrow the behavior meaningfully. It doesn't name the siblings writeToFile/replaceFileContent, but the read-vs-write distinction is self-evident from the verb.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No when-to-use guidance, no exclusions, and no mention of the sibling tools. An agent gets no help deciding between an unbounded read and a sliced read, or when to prefer this over other file operations.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
writeToFileB
Create a new file or atomically overwrite/append to an existing file.
| Name | Required | Description | Default |
|---|---|---|---|
| append | No | Set true to append. | |
| overwrite | No | Set true to overwrite existing file. | |
| targetFile | Yes | Path to target file. | |
| codeContent | Yes | File content to write. | |
| description | No | Summary of file creation. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does add one genuine behavioral fact: writes are atomic. However it omits permission requirements, directory-creation behavior, error/failure semantics, and the precedence rule between append and overwrite, which are the traits an agent most needs for a destructive mutation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with zero filler, and the create-vs-modify distinction leads. It is arguably too terse for a five-parameter mutation tool, but nothing in it is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-parameter, no-annotation, no-output-schema mutation tool, the description leaves too much unspecified: flag interaction, behavior when the target does not exist and append is set, and failure behavior. The atomicity note is the only added behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all five parameters and the baseline is 3. The description ties 'atomic overwrite/append' to the two boolean flags, but adds no precedence or conflict semantics beyond the terse schema prose.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb+resource ('Create a new file or ... overwrite/append to an existing file') and even discloses the atomic write behavior. It is clear in isolation, but it never differentiates itself from the sibling replaceFileContent, so an agent can't tell from the description alone which mutation path to pick.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use guidance and no mention of the alternatives viewFile or replaceFileContent. The description also never explains how the append and overwrite flags interact or what happens when both are false, leaving the selection logic entirely to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v1.0.0- First observed
replaceFileContent - First observed
viewFile - First observed
writeToFile
TDQS
Scored across 3 tools
Each tool has a clearly distinct purpose: writeToFile creates or overwrites/appends whole files, viewFile reads with line numbering, and replaceFileContent performs surgical edits. The overlap between write and replace is minimal and well-differentiated by granularity.
All names use camelCase and start with a verb, which is consistent. However, writeToFile includes a preposition ('To') while the others do not, creating a minor structural deviation.
Three tools is within the ideal 3-15 range and each tool covers a core file content operation (create/overwrite, read, modify). The set is well-scoped for a focused file manipulation server.
The surface covers create, read, and update operations, but lacks a delete operation and basic file management tools like listing or moving. This is a notable gap for a general filetools server, though the existing tools cover common content editing workflows.
Maintenance
Related MCP Connectors
Work on your own computers from Claude or ChatGPT: run commands, edit and search files.
- flockfsOAuthcom.flockfs
A real-time, multiplayer filesystem for agents and humans: shared files, live edits, history.
Path-scoped team memories, rules and skills for Claude Code, Cursor, Codex and other MCP clients.
Read and write your Fresh Jots notes from Claude, Cursor, and any MCP client.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables 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.13 npm75ISC
- AlicenseNot gradedqualityDmaintenanceProvides hashline-based file editing using line-addressed edits and content hashes for integrity verification. It enables LLMs to perform precise file modifications while ensuring edits are rejected if the file content has changed since the last read.20 npm9MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to perform advanced file editing operations with enhanced security, automatic backups, and undo functionality.MIT
- AlicenseAqualityDmaintenanceExposes hash-anchored file edits to MCP-compatible AI coding agents, enabling safe read and write operations with stale edit detection.156 npm1MIT