Skip to main content
Glama
MartinSchoeler

read-only-mcp

readonly-mcp

A minimal Model Context Protocol server that exposes read-only access to a single directory tree over the stdio transport.

Usage

npm install
npm run build
node dist/index.js /path/to/allowed/root

The allowed root directory is the sole command-line argument. It must exist and be a directory, or the server exits with an error before accepting any request.

MCP client configuration:

{
  "mcpServers": {
    "readonly": {
      "command": "node",
      "args": ["/absolute/path/to/readonly-mcp/dist/index.js", "/path/to/allowed/root"]
    }
  }
}

Related MCP server: mcp-readonly-code-server

Tools

All path arguments may be absolute or relative to the allowed root.

list_directory(path)

Lists the entries of a directory. Each line is prefixed with [DIR], [FILE], [LINK], or [OTHER]. Errors if the path is not a directory.

read_file(path)

Returns the full UTF-8 contents of a file. The file is opened with O_RDONLY | O_NOFOLLOW. Directories, non-regular files (sockets, devices, FIFOs), and files larger than 10 MB are rejected.

search_files(path, pattern)

Recursively walks the tree from path and returns the relative paths of entries whose name matches pattern. A pattern containing * or ? is treated as a case-insensitive glob over the entry name (*.ts); otherwise it is a case-insensitive substring match (config). Symlinks are never followed, which prevents both traversal escapes and directory cycles. Results are capped at 1000 and the output says so when truncated.

get_file_info(path)

Returns metadata for a file or directory: relative path, absolute path, type, size in bytes, modified/created/accessed timestamps (ISO 8601), and octal permission bits.

Path containment

Every path argument passes through resolveWithinRoot() before any filesystem operation:

  1. Reject empty paths and paths containing a null byte.

  2. Expand a leading ~, then resolve to an absolute path (relative paths resolve against the allowed root). This collapses .. segments.

  3. Reject if the absolute path is not the root itself or a descendant of it.

  4. realpath() the result to resolve every symlink in the path, including the final component.

  5. Reject again if the real path escapes the root.

Containment is a prefix check against ALLOWED_ROOT + path.sep, so a sibling directory whose name merely starts with the root's name (/data/rootabc vs /data/root) does not pass. The root itself is stored as its own realpath() at startup, so the comparison is real-path against real-path.

Verified behaviors: ../outside/secret.txt, /etc/passwd, sub/../../outside/secret.txt, a symlinked file pointing outside the root, and a symlinked directory pointing outside the root are all rejected with access denied.

No write capability

This server cannot modify the filesystem. There is no write_file, edit_file, delete_file, move_file, or mkdir tool — not as a stub, not disabled, not anywhere. An unrecognized tool name returns unknown tool: <name>.

The entire implementation is one file, src/index.ts. The only filesystem calls it makes are:

Call

Purpose

fs.realpath

resolve symlinks for containment checks

fs.stat / handle.stat

type, size, timestamps, permissions

fs.readdir

list directory entries

fs.open(p, O_RDONLY | O_NOFOLLOW)

open files for reading only

handle.readFile / handle.close

read contents, release the descriptor

No writeFile, appendFile, unlink, rm, rmdir, mkdir, rename, copyFile, truncate, chmod, chown, utimes, link, symlink, or createWriteStream appears in the source. You can confirm this yourself:

grep -nE '\b(writeFile|appendFile|unlink|rm|rmdir|mkdir|rename|copyFile|truncate|ftruncate|chmod|chown|utimes|link|symlink|createWriteStream|write)\(|O_WRONLY|O_RDWR|O_CREAT|O_TRUNC|O_APPEND' src/index.ts

The only hits are the four process.stderr.write(...) calls used for the usage message, the two startup errors, and the startup banner. Nothing touches the filesystem. Run the same grep over dist/index.js after npm run build to audit the compiled output.

Files are opened with the O_RDONLY flag at the OS call level, so even a bug elsewhere in the process cannot write through a descriptor this server holds.

The server writes exactly one line to stderr at startup (the root it is serving) and otherwise speaks only MCP over stdout.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Stdio MCP server for sandboxed file access — read files, search content, safely edit with checksums, and manage file structure.
    4 npm
    ISC
  • F
    license
    B
    quality
    C
    maintenance
    A read-only MCP server that exposes a local code workspace to AI clients via stdio, providing file browsing and text search capabilities with path safety rules.
    1
    -
  • A
    license
    Not graded
    quality
    A
    maintenance
    A small filesystem MCP server that reads and writes files under one confined root over stdio, with paginated tools and strict change-detection. It enables safe, deterministic file access for agentic runtimes and MCP clients.
    113 npm
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Enables AI clients to inspect and carefully edit a bounded local workspace via stdio, with a read-only default, conflict-safe updates, and no network listener or telemetry.
    10
    1
    Apache 2.0