Skip to main content
Glama
README.md
# 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.

## Requirements

- **Node ≥ 23** (the server imports the TypeScript library directly via native type-stripping).

## Tools

| Tool | Purpose |
|---|---|
| `viewFile` | Read a file with 1-based line numbers and bounded slicing (`startLine`/`endLine`, byte/line caps). Always read before editing. |
| `replaceFileContent` | Replace exact `targetContent` within `[startLine, endLine]`. Preserves CRLF/LF and indentation, rejects ambiguous matches, returns a unified diff, and emits a self-correction hint on failure. |
| `writeToFile` | 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:
```bash
node --version
```
This server imports a TypeScript file directly, which needs **Node ≥ 23**.
- `v23` or 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):
```bash
git clone https://github.com/testAccountDeltas/fileTools.git
cd fileTools
npm install
```

### 3. Get the absolute path

```bash
# macOS / Linux
pwd            # -> e.g. /Users/alex/tools/fileTools
# Windows (PowerShell)
(Get-Location).Path   # -> e.g. C:\Users\alex\tools\fileTools
```
The 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.json`
- Windows: `%USERPROFILE%\.config\opencode\opencode.json`

**Merge** these two keys into the existing JSON — do not overwrite the whole file:

```jsonc
{
  "mcp": {
    "filetools": {
      "type": "local",
      "command": ["node", "/ABSOLUTE/PATH/TO/fileTools/mcp-server.mjs"],
      "enabled": true
    }
  },
  "permission": {
    "*": "allow",
    "edit": "deny"
  }
}
```

Merge rules:
- If an `mcp` object already exists, add the `filetools` entry inside it (keep the others).
- If a `permission` object 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.mjs` with 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

```bash
node mcp-server.mjs
```
Expected 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:
```bash
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):

```markdown
- 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)

```jsonc
{
  "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

```ts
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

```bash
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 protocol
```

## License

MIT

TDQS

B3.4/5.0

Scored across 3 tools

Disambiguation5/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness3/5

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

ActivityMaintained
ResponsivenessNo issues