This MCP server provides “file tools” focused on handling non-UTF-8 text encodings. It auto-detects and preserves common legacy encodings, including CP1251, CP1252, KOI8, ISO-8859, UTF-16, and GBK, and exposes related functionality as 19 tools.
MCP server for file operations on text that isn't UTF-8. It detects the encoding from the
file's bytes rather than its extension, hands the model UTF-8, and writes back in the
original encoding — BOM and CRLF/LF intact, still byte-compatible with whatever legacy
tool owns the file.
Encoding-aware across the whole tool set — edit_file, grep_text_files and search_files decode the same way, not just read and write
Detection you can inspect — detect_encoding reports the charset, a confidence score and any BOM, so garbled text becomes diagnosable
BOM and line endings are first-class — including on UTF-16, where a naive byte-level rewrite corrupts the file
Sandboxed — every path, symlink and junction targets included, is checked against the directories you allowed
Built for: Delphi/Pascal units with Cyrillic UI text, VB6 forms, legacy PHP/HTML with
localized content, and INI or data files whose encoding you can't tell from the filename.
code
User: Read config.ini and change the title to "Настройки"
Claude: [read_text_file → cp1251 detected] → [edits UTF-8] → [write_file → back to cp1251]
PRs welcome and merged fast — no CLA, no style review, one-line fixes count. Forked this to fix something? Please send it back instead.
Installation
Claude Code plugin (recommended)
bash
claude plugin marketplace add dimitar-grigorov/mcp-file-tools
claude plugin install mcp-file-tools
Inside a session: /plugin marketplace add … and /plugin install ….
Requires Node.js 18+ on your PATH. The launcher is a Node script
and Claude Code does not bundle Node; without it /mcp shows the server as not connected.
First launch downloads the binary for your OS at a pinned version, verifies its SHA-256 and
caches it. It is scoped to the folder you have open, so there is nothing to configure. For
directories outside the workspace, or a machine without Node, use a
manual install.
Coming from a manual install
bash
claude mcp list # find the old file-tools entry
claude mcp remove file-tools
claude plugin marketplace add dimitar-grigorov/mcp-file-tools
claude plugin install mcp-file-tools
# Old binary, once /mcp shows the plugin connected:# Windows Remove-Item "$env:LOCALAPPDATA\Programs\mcp-file-tools\mcp-file-tools.exe"# Linux/macOS rm ~/.local/bin/mcp-file-tools
Tool names change too: mcp__file-tools__* becomes
mcp__plugin_mcp-file-tools_file-tools__*, so update your permission rules, see
Auto-approve tools.
Updating the plugin
bash
claude plugin marketplace update mcp-file-tools
claude plugin update mcp-file-tools@mcp-file-tools
Use the full plugin@marketplace id, not the bare name, or turn on auto-update in
/plugin → Marketplaces.
Registries and directories
This server is listed in the Official MCP Registry for discovery by any MCP client, and indexed on
Glama, which scores it A
for license, quality and maintenance.
Manual install (other MCP clients, or access outside your workspace)
Download the binary for your platform, then register it with the directories it may access.
mkdir -Force "$env:LOCALAPPDATA\Programs\mcp-file-tools"
iwr "https://github.com/dimitar-grigorov/mcp-file-tools/releases/latest/download/mcp-file-tools_windows_amd64.exe" -OutFile "$env:LOCALAPPDATA\Programs\mcp-file-tools\mcp-file-tools.exe"
claude mcp add --scope user file-tools -- "$env:LOCALAPPDATA\Programs\mcp-file-tools\mcp-file-tools.exe" "C:\Projects"
Linux / macOS (swap the asset name from the table for your platform):
bash
mkdir -p ~/.local/bin
curl -L "https://github.com/dimitar-grigorov/mcp-file-tools/releases/latest/download/mcp-file-tools_linux_amd64" -o ~/.local/bin/mcp-file-tools
chmod +x ~/.local/bin/mcp-file-tools
claude mcp add --scope user file-tools -- ~/.local/bin/mcp-file-tools ~/Projects
Go install (all platforms)
bash
# Requires Go 1.27+
go install github.com/dimitar-grigorov/mcp-file-tools/v4/cmd/mcp-file-tools@latest
# Linux / macOS
claude mcp add --scope user file-tools -- $(go env GOPATH)/bin/mcp-file-tools ~/Projects
powershell
# Windows PowerShell
claude mcp add --scope user file-tools -- "$(go env GOPATH)\bin\mcp-file-tools.exe" "C:\Projects"
Other Clients
For Claude Desktop, VSCode, or Cursor, use the downloaded binary path in your config:
Claude Desktop (%APPDATA%\Claude\claude_desktop_config.json on Windows, ~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
Note:type: "stdio" is required here. The VSCode extension does not add the workspace
directory by itself, so args must list every directory you want reachable. Adding one
later means re-running claude mcp add with the full list — it overwrites the previous
config rather than appending.
OpenAI Codex CLI
Codex takes a direct MCP command, so no manual TOML editing is needed.
That prefix is the plugin install; a manual one registered as file-tools is
mcp__file-tools__* instead, and a rule with the wrong prefix matches nothing and fails
quietly. Which modes the rules affect, and keeping delete_file / move_file behind a
prompt, are in docs/extra.md.
Update
The server checks for updates automatically and notifies you through tool responses when a
newer version is available; the notice carries the steps for your install. Plugin installs
update through Updating the plugin — a re-downloaded binary is
ignored there.
For a manual install, re-download the binary over the existing one — the registration does
not need repeating:
Close all Claude Code sessions (the binary is locked while running)
To disable update checks, set the environment variable MCP_NO_UPDATE_CHECK=1.
Verify & Uninstall
bash
# Check which file-tools server is connected (plugin or manual)
claude mcp list
# Remove a manual install
claude mcp remove file-tools
# Remove the plugin
claude plugin uninstall mcp-file-tools
How to Use
Once installed, just ask Claude:
"List all .pas files in this directory"
"Read config.ini and detect its encoding"
"Show all supported encodings"
"Read MainForm.dfm using CP1251 encoding"
Security: the server reaches only the directories you allowed. It takes them from
args: ["/path/to/project"] first, then MCP_FILE_TOOLS_ALLOWED_DIRS, and failing both
from the directory it was started in — the workspace, when a client launches it there.
Clients that still speak the MCP roots protocol add their roots on top. A drive root or
your home directory is never granted by that last fallback; name it explicitly instead.
Paths are resolved before the check, so a symlink or Windows junction pointing outside is
rejected rather than followed.
Tools
20 tools — every one that touches text content is encoding-aware:
read_text_file - Read files with encoding auto-detection and conversion
read_multiple_files - Read multiple files concurrently with encoding support
Plus three prompts — audit_encodings, fix_mojibake,
migrate_to_utf8 — surfaced by clients as user commands.
See TOOLS.md for detailed parameters and examples. Calls shaped like
Claude Code's built-in Read/Write/Edit/Grep are accepted too — the
alias layer translates them where the semantics
match exactly, so a model's habits don't fail the call.
Out of scope: binary/media reading (read_media_file). This is a text
tool; agents read images with their built-in tools.
Supported encodings
Every one below reads and writes. Name one explicitly via the encoding parameter, or
leave it to auto-detection.
Common aliases are accepted (cp1251, latin1, gb2312, tis-620, …) —
list_encodings prints the whole table with aliases.
Auto-detection never guesses MacRoman, the DOS pages or the rarer ISO tables; name them
explicitly. UTF-32 is found only by its BOM.
Configuration
The server can be configured via environment variables:
Variable
Description
Default
MCP_DEFAULT_ENCODING
Default encoding for write_file on new files when none specified. Existing files keep their detected encoding. Set to cp1251 to restore the pre-2.0.0 default.
utf-8
MCP_DEFAULT_LINE_ENDINGS
Line endings for write_file on new files (crlf/lf). Existing files keep their own style regardless.
unset (write unchanged)
MCP_MEMORY_THRESHOLD
Memory threshold in bytes. Files smaller are loaded into memory for faster I/O; larger files use streaming. Also affects encoding detection mode.
67108864 (64MB)
MCP_DETECTION_CANDIDATES
Comma-separated list pinning what detection may answer, in priority order — e.g. utf-8,windows-1252. See Pinning the encodings.
unset (detection unrestricted)
MCP_FILE_TOOLS_ALLOWED_DIRS
Allowed directories as an OS path list (; on Windows, : elsewhere). For clients where env is the only block you control, such as the Claude Code plugin. Overridden by args.
unset
MCP_FILE_TOOLS_NO_CWD_FALLBACK
Set to turn off granting the working directory when neither args nor MCP_FILE_TOOLS_ALLOWED_DIRS names one.
unset (fallback on)
Set them with an env block in your config (Claude Desktop example):
Detection is a guess, and guesses have blind spots: Spanish CP1252 like MÓDULO FÍSICAMENTE ÚNICO is plausible GBK — every uppercase accent before an ASCII letter is a
valid hanzi pair — so it reads back as Chinese and edits fail with "gbk cannot represent
2 characters". If you know what the repo contains, say so:
A BOM still wins. A guess inside the list keeps its confidence; one outside it is dropped
and the listed encoding that reads most like text takes over, list order breaking ties. A
file that fits none of them is read as the default and reported as an ODD ENCODING
in read_text_file's hint, so a stray file gets said out loud rather than
guessed at. Unlisted encodings stop appearing in detect_encoding's candidates too. UTF-16/32 are named only by a BOM
or the structural classifier, so listing them cannot make them a catch-all.
Legacy teams (pre-2.0.0 behaviour)
Before 2.0.0 new files defaulted to cp1251; they now default to utf-8. Existing files
are unaffected — their encoding is detected and preserved — so this only matters if your
team creates new non-UTF-8 files, e.g. new Delphi units with Cyrillic literals. To keep
the old behaviour:
json
"env":{"MCP_DEFAULT_ENCODING":"cp1251"}
Commit that in the legacy repo's .mcp.json rather than setting it per machine, and
everyone working in that repo gets the right default with no local setup.
Delphi 2007 and older read UTF-8 only when it carries a BOM, so a UTF-8 file without one
is silently treated as ANSI. Set cp1251 (or your own ANSI code page) for such a repo and
new Cyrillic literals land in the encoding the IDE expects. Files that already exist keep
their own encoding either way, and no tool adds a BOM to them.
Development
Prerequisites: Go 1.27+
bash
make test# go test -race ./...
make lint # go vet, go fmt, staticcheck (same pinned version as CI)
make build
test_server.go is an end-to-end smoke test over every tool, run by CI on each push:
bash
go run test_server.go
Debugging
MCP Inspector gives a web UI for calling tools and inspecting responses (needs Node.js 18+):
bash
npx @modelcontextprotocol/inspector go run ./cmd/mcp-file-tools -- /path/to/allowed/dir
Or pipe JSON-RPC straight to stdin:
bash
echo'{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | go run ./cmd/mcp-file-tools /path/to/project
Contributing
If it fits the scope and works, it gets merged. Don't ask first — just send the PR.
No CLA, no style review: make test and make lint passing is enough, and tests are
welcome but never required. One-line fixes and half-finished features behind a flag both
count. Out of scope, or breaking a tool contract other people's agents rely on, gets a
comment rather than a close. Not writing the fix yourself?
Open an issue with the file,
its encoding, and what the tool did.
Details in CONTRIBUTING.md. Found a way out of an allowed
directory? That one goes to SECURITY.md, privately, not to an issue.
Forking
Forking is fine. That's what GPL-3.0 is for. Taking the project over is not.
GPL-3.0 is a license, not a preference. Distribute your fork in any form (public repo,
release binary, registry listing, product you ship to customers) and you must:
Keep GPL-3.0 and LICENSE, copyright notice intact (§4, §5c)
Say what you changed and when, prominently (§5a)
Give the source to everyone you gave the binary to (§6)
WARNING
Deleting the license or the notice, relicensing as MIT or proprietary, or shipping
only a binary is a license violation, and §8 ends your rights the moment you do it.
It will be enforced, in this order: a request to comply, then a DMCA takedown plus
delisting from whichever registry or marketplace carries it, then legal action. Complying
costs one license file, one notice and one source link.
Ask in an issue if you are
unsure whether what you ship complies.
Asked, not enforced: leave the credit in — the copyright notice is the legal minimum,
one line saying "Fork of mcp-file-tools"
is what tells a reader where it came from. Give your fork its own name, so a registry
listing under this one with the author swapped doesn't read as if the project moved and
send its bugs here. And try upstream first: a PR beats carrying merge conflicts forever,
and puts your name on the commit rather than in a credits list.
Credits
Ideas that started in someone else's fork and were reimplemented here:
@skyispainted - GBK/GB18030, JSON-string array args, edit_file retry hint