Skip to main content
Glama

简体中文

paddock — Local filesystem workbench (MCP stdio server)

paddock gives agents a complete, constrained set of local file operations: text read/write, directory management, filename search, content search, and metadata queries — all operations are confined to configurable "paddock" directories, and out-of-bounds access is always rejected.

  • Zero runtime dependencies: pure Node.js standard library, no npm install needed

  • MCP protocol: line-delimited JSON-RPC 2.0 over stdio, loadable by any MCP client

  • dsh-ready: ships a Cordis bridge plugin, one-line bundle integration with the harness

  • Read-only mode: --read-only blocks all write operations in one shot

  • Large files & binaries: streaming head/tail, byte slicing, base64 reads, binary sniffing

  • Search: a self-built glob engine (* ** ? {a,b} [abc]) plus regex/fixed-string content search


Quick start

# No dependency installation needed, run directly
node src/entry.js "C:/Users/me/projects"

Once started, the MCP session begins: JSON-RPC messages are read line by line from stdin, written line by line to stdout, and logs go only to stderr.

Connect from any MCP client (example with a standard MCP client config):

{
  "mcpServers": {
    "paddock": {
      "command": "node",
      "args": ["/path/to/fs-mcp/src/entry.js", "C:/Users/me/projects"]
    }
  }
}

You can also run a smoke session directly to verify:

# Write an initialize request to stdin and observe the response
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}' | node src/entry.js "C:/Users/me/projects"

Related MCP server: MCP Local Filesystem Server

Installation

  • As an MCP server: copy this directory, run node src/entry.js <paddock dirs…>, no build step.

  • As a dsh plugin: see docs/INTEGRATION.md.

  • Requirements: Node.js ≥ 18.17 (check with node --version).

Installing in DSH

dsh plugin --profile demo add github:JohnXu22786/fs-mcp
  • demo is a dsh profile: it is created automatically on first use, and the package is added to dsh.profile.bundles;

  • The cordis.patch.yml inside the package defines the paddock/bridge plugin, which starts this MCP server within the dsh process and registers all 16 tools into ctx.tools after the handshake — no manual configuration needed;

  • Removal:

dsh plugin --profile demo remove paddock

Configuration

Configuration is merged as "defaults < config file < environment variables < command-line arguments"; paddock directories are unioned across all three sources.

Command line

node src/entry.js [options] [paddock dirs…]

  --config <path>   config file (defaults to paddock.config.json in the working directory, loaded only if present)
  --read-only       read-only mode: blocks all write operations
  --zones <path>    additional paddock directories (repeatable)
  --verbose         debug logging (stderr)
  --version / --help

Positional arguments are treated as paddock directories. At least one paddock is required, otherwise startup fails (exit code 2).

Config file (paddock.config.json)

{
  "zones": ["C:/Users/me/projects", "C:/Users/me/data"],
  "readOnly": false,
  "limits": {
    "peekBytes": 1048576,
    "readBytes": 16777216,
    "sliceBytes": 65536,
    "grepBytes": 262144,
    "grepHits": 200,
    "findHits": 1000,
    "treeDepth": 6
  },
  "behavior": {
    "includeHidden": false
  }
}

A full example is in examples/paddock.config.example.json.

Environment variables

Variable

Description

PADDOCK_CONFIG

config file path (takes precedence over the default path)

PADDOCK_ZONES

paddock directories: a JSON array string (["/a","/b"]) or a single path

PADDOCK_READ_ONLY

1 / true / yes / on count as enabled

PADDOCK_VERBOSE

same as above (1 / true / yes / on)

limits semantics

Key

Default

Effect

peekBytes

1 MiB

upper bound for pdk_peek full reads (head/tail are not limited by this, but an over-long single line is capped by the internal 8 MiB line-buffer limit)

readBytes

16 MiB

upper bound for pdk_read_bytes base64 reads

sliceBytes

64 KiB

upper bound for a single pdk_slice slice

grepBytes

256 KiB

per-file scan limit for pdk_grep (deep: true can bypass; lines over 1 MiB are skipped wholesale)

grepHits

200

hit-count limit for pdk_grep

findHits

1000

result-count limit for pdk_find

treeDepth

6

default depth limit for pdk_tree

treeEntries

20000

total node limit for pdk_tree (truncated with a truncated marker when exceeded)


Tool interface

16 tools; write operations carry a mutating marker (gated by read-only mode). All path arguments must be absolute paths inside a paddock; relative paths are resolved against the process working directory and validated the same way.

Read-only tools

Tool

Parameters

Description

pdk_zones

list paddocks (given paths + resolved real paths)

pdk_ls

path, sortBy?

list directory: entry type (file/dir/link), size, sorting, counts and totals

pdk_tree

path, depth?, exclude?, includeHidden?

recursive directory tree (JSON); overly deep directories are marked cut: true, node-limit overruns are marked truncated

pdk_peek

path, head?, tail?

read text files; head/tail stream the first/last N lines; binary and over-limit files are rejected

pdk_read_many

paths[]

batch text reads (1–64 files); a single file failure is folded without aborting

pdk_slice

path, offset?, length?

read large files by byte range; binary content returned as base64; eof: true when the slice reaches the end of the file

pdk_read_bytes

path

base64 read of binaries + MIME guessing (png/jpg/mp3/…)

pdk_find

path, pattern, exclude?, includeHidden?

recursive filename glob search

pdk_grep

path, needle, mode?, caseSensitive?, glob?, exclude?, includeHidden?, deep?

content search: regex (case-insensitive by default) or fixed string; line numbers + truncated snippets

pdk_meta

path

metadata: type/size/permissions/times; symlinks report their target

Write tools

Tool

Parameters

Description

pdk_write

path, content

write/overwrite (UTF-8), parent directories created automatically

pdk_rewrite

path, edits[], dryRun?

precise replacement: {oldText, newText, all?} applied in order; fails on any miss; dryRun previews -/+ changes; newText is treated literally ($ sequences are never expanded)

pdk_mkdir

path

recursive directory creation, idempotent

pdk_move

source, destination

move/rename; refuses to overwrite an existing destination; cross-device copies + deletes automatically

pdk_copy

source, destination

copy files/directories; symlinks are not followed (links are recreated as-is)

pdk_remove

path, recursive?

delete; non-empty directories need recursive: true; paddock roots can never be deleted

Error model

Tool-level failures do not produce protocol errors; they return structured isError: true results:

{
  "isError": true,
  "content": [{ "type": "text", "text": "[not-found] File does not exist: C:/…" }],
  "structuredContent": { "error": { "code": "not-found", "message": "File does not exist: C:/…" } }
}

Error codes: config readonly validation fence (out of bounds) not-found conflict limit binary wrong-kind io internal (fallback). network / timeout / jsonrpc appear only in the bridge client (the communication layer with the server subprocess). Error messages carry actionable follow-up suggestions (e.g. large files are pointed to pdk_slice).


Security model

  • Paddock boundary: every path passes "paddock" validation before any IO (see docs/SECURITY.md).

  • Symlinks: existing paths are realpath-resolved then validated; new paths validate by resolving the "nearest existing ancestor" and re-checking — links inside a paddock that point outside can never become a springboard.

  • Component-level checks: string-prefix misjudgments such as zone vs zone_extra are impossible; win32 comparisons fold case.

  • Windows hardening: rejects invalid path segments, trailing dots/spaces, and reserved device names (NUL/CON/COM1…).

  • Read-only mode: with --read-only or config readOnly: true, all 6 write tools are gated.

  • Delete protection: paddock root directories cannot be removed by pdk_remove.

  • Large files/binaries: text-read limits, slice reads, base64, NUL-byte sniffing — the model is never slammed with gigantic or binary content.


dsh integration

Pick either of the two ways; details in docs/INTEGRATION.md:

  1. Bundle bridge (recommended, zero dependencies): this package's dsh.bundle.patch points to cordis.patch.yml, and dsh merges the paddock/bridge plugin line into the config tree; the plugin starts the server subprocess inside the harness process and, after the handshake, registers all 16 tools into ctx.tools — model-side tool names look like mcp__paddock__pdk_peek.

  2. Official MCP client: connect directly to node src/entry.js <paddock> with the @deepseek-ai/dsh-mcp-client config line.

Development

npm test        # node --test: 100+ cases (4 symlink cases skip on win32 as they need developer mode)
npm run smoke   # sequential end-to-end smoke test (real subprocess + stdio session, 16 checks)
node src/entry.js --help

Test coverage: paddock escapes/symlink escapes/ancestor-chain protection, read-write round-trips, large-file and binary policies, glob and content search, three-source config merging, protocol engine, end-to-end subprocess sessions.

License

MIT, see LICENSE.

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/JohnXu22786/fs-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server