文件操作MCP服务器

一个基于Rust的小型、快速、模块化的MCP服务器和库,通过简单IPC/RPC传输暴露常见的文件系统操作,主要用于LLM代理和其他需要安全、可审计文件系统访问的自动化客户端。

  • by adelie-ai
  • Rust
  • 本地部署
  • 更新于
在 GitHub 查看 dspadea/fileio-mcp

使用说明

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 (binary fileio-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)

  1. A client sends a request over the configured transport to perform an operation (for example, copy a file or read lines).
  2. The server receives the request and dispatches it to the matching operation handler in operations/.
  3. The handler performs the filesystem action with careful error handling and returns a structured response.
  4. 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:

  1. Add a new module in src/operations/ that implements the operation logic and a small handler interface.
  2. Register the operation in the server dispatch so it can be invoked over the transport.
  3. 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.