Minimal MCP server that exposes opencode-style file tools
(read / edit / write) over stdio. Designed for models that handle a
small, structured schema better than a free-form patch grammar.
Alternatives tried:
codex-rsapply_patchshell command — the model has to compose a custom multi-line patch grammar (*** Begin Patch/*** Update File:/-old/+new/*** End Patch) inside a shell argument. Smaller models stumble on this format.mcp-workspace(Python, stdio) — exposes the same opencode shape but enforces two extra boundaries the host already covers: a--project-dirfilter and a.gitignorefilter that blocks reading common ignored files (target/,.env, etc.).
This server keeps the opencode tool shape and drops the extra boundaries, so models get a familiar three-field schema and can reach every file the OS sandbox allows.
| Name | Args | Notes |
|---|---|---|
read |
path, offset?, limit? |
Line-numbered output; records file hash for must-read-first. |
edit |
filePath / file_path, oldString / old_string, newString / new_string |
Exact match; must be unique; must-read-first enforced. |
write |
filePath / file_path, content |
Atomic full-file write; creates parent dirs. |
edit mirrors opencode's contract:
- You must call
readon the same file first in the session. oldStringmust appear exactly once in the current file content.- The file must not have changed since the last
reador since the last successfuledit/writeon that file (hash mismatch → fail).
The hash check runs before the oldString check, so a stale edit
against a changed file reports "file changed" rather than the
misleading "oldString not found".
Tool calls accept either casing — pick whichever feels natural. The
schema tools/list reports uses camelCase (MCP convention), but the
server also accepts snake_case at call time, so all of these are
equivalent for edit:
filePath/file_patholdString/old_stringnewString/new_string
If a Pydantic error names a field as missing, check both spellings before assuming truncation — that's the diagnostic FastMCP surfaces when an unknown key is dropped.
None at the application layer. The host (e.g. Codex) provides the
OS-level sandbox (Seatbelt/landlock) which already wraps the spawned
subprocess. This server intentionally does not enforce a
--project-dir boundary or a .gitignore filter.
# First install
uv tool install .
# After source changes — use --reinstall (not --force)
uv tool install --reinstall .Both commands write outside the default Codex sandbox scope
(~/.local/share/uv/tools/, ~/.cache/uv/), so run with
sandbox_permissions: "require_escalated". See AGENTS.md for the
why behind --reinstall.
Entry point: mcp-fstools (stdio MCP server).
In ~/.codex/config.toml:
[mcp_servers.mcp_fstools]
command = "mcp-fstools"
default_tools_approval_mode = "approve"Restart Codex after the install. To verify, run a read and an edit
in a fresh session — both should succeed without a permission prompt.