使用说明
fileio-mcp
A small, fast, and modular Rust MCP server (plus library) that exposes common filesystem operations over a simple IPC/RPC transport. It is primarily intended for use by LLM agents and other automated clients that need safe, auditable file system access.
Who this is for
- LLM agent runtimes that need deterministic, tool-driven file I/O (read, write, edit, list, find).
- Automation frameworks that prefer a single, auditable service boundary for filesystem access.
- Editors/CI/sandboxes that want to delegate file operations to a hardened server.
Why use fileio-mcp
- Automate file operations safely: Centralize file system actions (copy, move, read, write, mkdir, remove, stat, etc.) behind a single, auditable service.
- Built for LLM agents: Provide a stable MCP tool surface that LLMs can call deterministically for file I/O.
- Integrate with tools and editors: Expose file operations to editors, CI systems, or sandboxes that can't perform certain filesystem tasks directly.
- Reduce duplication: Reuse a single, well-tested implementation of file primitives instead of reimplementing cross-platform behavior in multiple tools.
- Rust safety and performance: Implemented in Rust for predictable performance, strong error handling, and minimal runtime overhead.
End-user benefits
- Single integration point for file I/O: fewer edge cases and consistent error semantics.
- Clear separation of responsibility: the service performs file work while clients remain lightweight.
- Easier permission and audit controls: operations are centralized and can be observed or limited at the transport layer.
- Extensible operation set: add or override operations as requirements evolve.
Key capabilities
- Deterministic edits: Structured edit operations (
fileio_edit_file) avoid fragile patch diffs. - Line-aware reads: Flexible read APIs with explicit 1-based line numbers.
- Search utilities: File and content search with filters and regex support.
- Safe defaults: Clear error semantics and explicit control over destructive operations.
What it is
fileio-mcp is both a library and a small MCP server/CLI. It implements a set of canonical filesystem operations as modular, testable units (see the operations/ folder), and exposes those operations as MCP tools over IPC/RPC. The primary use case is LLM agents that need reliable, deterministic file operations.
Key components:
src/main.rs- CLI entrypoint / server runner (binaryfileio-mcp).src/server.rs- Server orchestration and request handling.src/transport.rs- Abstractions for the transport mechanism used to accept client requests.src/lib.rs- Library interface and shared types.src/operations/- Individual operation implementations (cp, mv, rm, mkdir, stat, read/write, etc.).src/error.rs- Centralized error types and conversion utilities.
How it works (high level)
- A client sends a request over the configured transport to perform an operation (for example, copy a file or read lines).
- The server receives the request and dispatches it to the matching operation handler in
operations/. - The handler performs the filesystem action with careful error handling and returns a structured response.
- The transport layer serializes the response back to the client.
Design points:
- Operations are implemented as focused modules so they're easy to test and reason about.
- Transport and server layers are separated from filesystem logic to allow different IPC/RPC mechanisms to be plugged in.
- Errors are structured and propagated so clients can make programmatic decisions based on failure modes.
Build & run
Build the project (requires Rust toolchain):
cargo build --release
Run the server binary (example):
# from repository root
./target/release/fileio-mcp --help
The exact transport and runtime flags depend on how you embed or deploy the server; consult src/main.rs for CLI options and src/transport.rs for supported transports.
Using the library
The crate can be included as a dependency to call operations directly from Rust code. The library surface is in src/lib.rs and the operations are available as modules under src/operations for programmatic use.
Logging
Telemetry comes from mcp-core, which installs the subscriber and owns the request and tool-call spans. See the mcp-core README for the full picture: the console layer, the mcp.* metrics recorded on every call, and the complete OTEL_* variable reference. This server adds a nested span per operation (read_lines, write_file, cp, and so on, one per function under src/operations) and the two things documented below.
Every operation span uses #[tracing::instrument(skip_all)]: it carries no fields at all, only a name and a duration. skip_all is required, not cosmetic - a plain #[tracing::instrument] captures every argument into the span at creation time, and a span field reaches an OTLP export on close whether or not any event ever prints it to the console. Adding an argument-bearing field to one of these spans is the kind of change that needs the level contract read again first.
Where it goes. stderr, always, at every RUST_LOG level. The stdio transport frames JSON-RPC on stdout, so a log line there would corrupt the protocol stream.
RUST_LOG=debug fileio-mcp serve
RUST_LOG=info,fileio_mcp=debug fileio-mcp serve
The level contract. INFO carries ids, counts, durations and tool names, never content. DEBUG carries tool arguments, including every path this server touches. A refused path is no exception: a refusal reads to the caller as an absent file (see the path_guard module doc), so it is exactly where a path would most tempt a future change to log it. Never do that above DEBUG.
This server's own metric. fileio.guard.rejections, labelled by reason (outside_allowlist, blocked or unresolvable, never the root, the entry or the path that matched it). mcp-core's own mcp.tools.call counter cannot see a guard refusal: the service answers "not found", so the call looks ordinary from the dispatch layer's side. This counter is the one place a refusal becomes observable, without telling the caller it happened.
Exporting to a collector. Off by default.
[features]
otel = ["mcp-core/otel"]
cargo build --features otel
OTEL_EXPORTER_OTLP_ENDPOINT=http://collector.example.com:4318 \
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
./target/debug/fileio-mcp serve
With the feature off, cargo tree resolves no opentelemetry* crate and a default build pays nothing for it. With no collector configured, the process still writes a periodic metrics summary to stderr.
Path safety
This server reaches only the directories it is told to reach. PathGuard holds
an allowlist of roots. A path inside a root is reachable. Every other path does
not exist, as far as this server is concerned: an argument outside the set comes
back as "not found", and an entry outside the set is dropped from a listing
before the result leaves the server.
Name the roots with --allow-path, repeated once per directory:
fileio-mcp serve --allow-path "$HOME/Projects" --allow-path /tmp
Or with the environment variable, separated by ::
FILEIO_MCP_ALLOW_PATHS="$HOME/Projects:/tmp" fileio-mcp serve
With neither set, a built-in default covers the system temporary directory and
~/Documents, ~/Downloads, ~/Desktop and ~/Projects. That is a starting
point for a desktop install, not a recommendation. Name what the work needs.
--block-path and --block-file are deprecated. They still subtract from the
allowlist, so an existing deployment keeps its restrictions, and the server logs
one deprecation warning at startup. A later release refuses the flags.
See docs/path_safety.md for the design: the
resolve-then-compare order that handles symlinks and .., the fail-closed
rules, and the check-then-use race the guard does not close.
Extending operations
To add an operation:
- Add a new module in
src/operations/that implements the operation logic and a small handler interface. - Register the operation in the server dispatch so it can be invoked over the transport.
- Add unit tests for happy and error paths.
Testing
Run the test suite with cargo test. For containerized, reproducible runs, see .github/instructions/testing.instructions.md.
Contributing
Contributions are welcome. Please follow the repository coding style and include tests for new operations or behavior changes.
License
This project uses the Apache license. See LICENSE-APACHE and NOTICE for details.