emperor-folder
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@emperor-foldersearch my notes for anything mentioning the project deadline"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
emperor-folder
An MCP server confined to one folder. You name a directory (or a few); the model can
list, read, search, and (if you allow it) write, edit and move files inside it, and cannot reach
anything outside it: not with an absolute path, not with .., not through a symlink.
emperor-folder serve --root ~/notes # read-only, Streamable HTTP on 127.0.0.1:8031/mcp
emperor-folder serve --root ~/notes --writable # also write, edit and move
emperor-folder serve --root ~/notes --stdio # for desktop clients
emperor-folder serve --root notes=~/notes --root code=~/src/app # two roots: notes/..., code/...Why
Giving an agent "the filesystem" is the fastest way to give it your SSH keys. Most file tools
check paths with string prefixes and then open them with the full authority of the process; a
symlink, a .. the check missed, or a race between check and open, and the model is out.
emperor-folder does not check-then-open. It opens the root once as a
cap-std directory handle and does every file
operation through that handle with a relative path, so resolution happens under the
operating system's (or cap-std's) beneath-the-root rules. There is no code path that opens a
file by absolute path.
It is built on emperor-mcp: stateless Streamable HTTP
(no session id, load-balancer friendly), output framed so file content reaches the model as
data rather than instructions, and a doctor subcommand. It follows
Emperor Profile P1.1: no write
tool is offered unless the operator switches writing on at start-up, and tool definitions are
lean (short descriptions, compact input schemas), so every request carries less of them.
Related MCP server: @modelcontextprotocol/server-filesystem
Install
Release binaries for Linux (x86_64), macOS (Apple Silicon and Intel) and Windows are attached
to each GitHub release, with a
SHA256SUMS file and build provenance
attestations:
check a download with sha256sum -c SHA256SUMS --ignore-missing and
gh attestation verify <file> --repo yarenty/emperor-folder. From source (Rust 1.88 or newer):
cargo install emperor-folderOr as a container (stdio, the folder mounted at /data, read-only unless you add --writable):
docker build -t emperor-folder https://github.com/yarenty/emperor-folder.git
docker run -i --rm -v ~/notes:/data emperor-folder # read-only
docker run -i --rm -v ~/notes:/data emperor-folder --root /data --writable # also write, edit, move
docker run -i --rm -v ~/notes:/data -e FOLDER_WRITABLE=true emperor-folder # the same, from the environmentRun
emperor-folder doctor --root ~/notes # check first: exits 1 if something is wrong
emperor-folder serve --root ~/notesFlag | Default | Meaning |
| (required) | A folder the server can see. Repeat for several roots; |
|
| Streamable HTTP address; MCP endpoint at |
| off | Offer |
| off | Speak MCP over stdin/stdout instead of HTTP. Logs go to stderr. |
| off | Include hidden files and directories in |
| off | Do not honour |
|
| Largest file |
|
| Most entries |
|
| Most characters of text one |
| see below | Accepted |
| none | Accepted |
RUST_LOG sets the log level (default info).
Connect
Claude Code
claude mcp add --transport http folder http://127.0.0.1:8031/mcp # server already running
claude mcp add folder -- emperor-folder serve --stdio --root ~/notes # or let Claude Code start itWith --writable (and several roots) in the stdio form:
claude mcp add folder -- emperor-folder serve --stdio --writable --root notes=$HOME/notes --root code=$HOME/src/appClaude Desktop (claude_desktop_config.json, stdio):
{
"mcpServers": {
"folder": {
"command": "/usr/local/bin/emperor-folder",
"args": ["serve", "--stdio", "--root", "/Users/me/notes"]
}
}
}kowalski (config.toml):
[[mcp.servers]]
name = "folder"
transport = "http"
url = "http://127.0.0.1:8031/mcp"or over stdio:
[[mcp.servers]]
name = "folder"
transport = "stdio"
command = ["emperor-folder", "serve", "--stdio", "--root", "/home/me/notes"]Any other MCP client that speaks Streamable HTTP or stdio works the same way.
Tools
Seven tools, each a verb. Every path is relative to the root (. or omitted means the root).
The three that change files are offered only with --writable; a read-only server lists four.
Tool | Arguments | Returns | Annotations |
|
| Entries with path, kind ( | read-only, idempotent |
|
| The text, with a | read-only, idempotent |
|
| Matches with path, line number and line (cut at 240 characters); counts of files searched, skipped as binary, skipped as too large. | read-only, idempotent |
|
| Kind, size, modified time (RFC 3339, UTC), read-only flag; for text files within the size limit, line count; for images, the image type. | read-only, idempotent |
|
| Path and bytes written. Creates parent directories. | destructive, |
|
| Path, replacements made, bytes written. | destructive, |
|
| The new path. Renames or moves inside the root (or between roots); refuses to replace an existing target unless | destructive, |
Every tool sets openWorldHint: false: it touches the named folders and nothing else.
list and search use ignore, ripgrep's walker:
.gitignore (even outside a git checkout), .ignore and hidden files are skipped unless
--no-ignore / --hidden say otherwise, and symlinks are never followed. Regexes use the
regex crate: linear-time matching, no catastrophic
backtracking, compiled size capped at 1 MiB. Globs match the path as the tools show it
(*.rs matches src/main.rs).
Output. File content from read is framed as a document of unknown authorship
(FrameKind::Document); listings, matches and metadata are JSON framed as computed
results (FrameKind::Computed). The frame's per-call nonce means a file containing a fake
end-of-data marker cannot break out of the block. Refusals come back as tool errors
(isError: true) with a message that says what to do instead.
Several roots
--root can be given more than once. With one root nothing changes: paths are relative to it.
With several, every path starts with the root's name, and . is a virtual folder whose entries
are the roots:
emperor-folder serve --root notes=~/notes --root code=~/src/applist . -> notes, code
read notes/todo.md
search pattern=TODO -> matches in notes/... and code/...
move notes/a.md -> code/docs/a.md (with --writable)A root without a name is named after its folder (--root ~/notes is notes); two roots with
the same name are refused at start-up. In NAME=DIR the name may not contain / or \, so
./a=b is a folder called a=b. Each root is its own cap-std handle: a path, .. or symlink
cannot get from one root into another except through move, which names both ends.
Security model
What the model can do: see and read files under the roots that the ignore rules do not
hide; with --writable, create, edit and rename files under the roots.
What it cannot do:
Leave the root. Absolute paths (
/etc/passwd,C:\on Windows) are refused, and so is any path with a..component, even one that would stay inside (a/../b). That check gives a clear error; the enforcement underneath is cap-std, which resolves every path beneath the root handle (openat2(RESOLVE_BENEATH)on Linux, component-by-component resolution elsewhere) and refuses any resolution that leaves it.Escape through symlinks. A symlink inside a root that points outside it (also into another root) is refused for
read,write,edit,move,listandsearchalike; a symlink that stays inside works. The walker never follows links, and every path it yields is re-opened through the root handle, so a directory swapped for an escaping link mid-walk is skipped, not read.Write by default. The server starts read-only:
write,editandmoveare removed from the tool list, and the file operations refuse writes as well, until the operator passes--writable(or setsFOLDER_WRITABLE), as Emperor Profile P1.1 requires. Tool annotations (readOnlyHint,destructiveHint) tell clients which tools change files, so they can ask before running them.Exhaust the host or the context. Whole-file reads stop at
--max-file-bytes(line-range reads stream and return at most that much) and at--max-charscharacters,searchskips larger files,listandsearchstop at--max-results, binary files other than small images are never returned.Be reached from a web page. On a loopback bind the
Hostheader must belocalhost,127.0.0.1or::1(DNS-rebinding protection). On a non-loopback bind the check is off, as the Emperor Profile expects a validating ingress in front;--allowed-hostturns it back on for named hosts, and--allowed-originrefuses browser requests from any other origin.
Not in scope: authentication (put the HTTP endpoint behind your gateway or keep it on loopback), and hard links (a hard link inside the root to a file outside is the same file; do not point the server at a folder where untrusted users can create hard links). Other local processes can still change the folder while the server runs; confinement holds regardless, because every operation re-resolves beneath the root.
doctor
$ emperor-folder doctor --root ~/notes --writable
emperor-folder 0.1.3 doctor
[ok ] dir /Users/me/notes readable (42 entries)
[ok ] dir /Users/me/notes writable
[ok ] write access on: write, edit and move are offered
[ok ] roots notes = /Users/me/notes
[ok ] listen address 127.0.0.1:8031 is free
[ok ] tool budget 7 of at most 8 tools
[ok ] files 1234 visible files under the roots
all good (0 warning(s))It checks each root exists and can be listed, can be written when --writable is set (a probe
file is created and removed), says whether writing is on, the roots open and have distinct names, the listen address is free (skipped
with --stdio), the tool count is within the emperor-mcp budget, and counts the files the
walker sees (a warning above 100 000: listing and search get slow). Exit code 0 when nothing
failed, 1 otherwise, so scripts and container health checks can use it.
Build
cargo fmt --all --check
cargo clippy --all-targets -- -D warnings
cargo test # unit tests + every tool over real HTTP and stdio, escape attempts included
cargo deny checkLinks
Crate: crates.io/crates/emperor-folder, API docs on docs.rs
Framework: emperor-mcp
How it compares with other folder-access MCP servers: docs/COMPARISON.md
Official MCP Registry entry, mcp-name: io.github.yarenty/emperor-folder
Contributing
See CONTRIBUTING.md: run the checks, keep the docs in step, and sign off
your commits (DCO, git commit -s).
License
Licensed under either of
Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in emperor-folder by you shall be dual licensed as above, without any additional terms or conditions.
Available Tools
4 toolslistARead-onlyIdempotent
List files and directories in a folder. Skips hidden and .gitignored entries. Optional glob filter; recursive for subfolders.
| Name | Required | Description | Default |
|---|---|---|---|
| glob | No | Glob on paths as shown, e.g. "*.md" or "src/**/*.rs". | |
| path | No | Directory to list; omit or "." for the top. | |
| recursive | No | Include subdirectories (default false: one level). | |
| max_entries | No | Most entries to return; capped by the server limit. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent and non-destructive, so safety is covered. The description adds genuine context beyond them: hidden and .gitignored entries are skipped, glob applies to displayed paths, and max_entries is capped by a server limit — the last point being a behavior an agent cannot see elsewhere.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences with zero waste, and the core action is front-loaded before the filtering and recursion caveats.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a low-complexity, no-output-schema listing tool, the description covers filtering, recursion, limits and skip rules, which is enough to call it correctly. Only return ordering or output shape is unspecified, a minor gap given the tool's simplicity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all four parameters are already documented in the schema. The description only echoes the glob filter and recursion at a high level without adding syntax or format detail beyond what the schema provides; baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List files and directories in a folder') and adds scope details (hidden/gitignored skipping, glob filter, recursion). It does not explicitly name why an agent would pick this over the sibling 'search', so it stops short of full sibling differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: an agent infers this is for enumerating a directory's contents. There is no explicit 'use this instead of search when...' or statement of prerequisites, and the relationship to siblings read/search/stat is left entirely to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
readARead-onlyIdempotent
Read a text file, optionally only lines start_line..end_line (1-based, inclusive). Small PNG, JPEG, GIF and WebP images come back as images; other binary files are reported, not returned.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | File to read. | |
| end_line | No | Last line to return, inclusive (default: end of file). | |
| start_line | No | First line to return, 1-based (default 1). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly, idempotent, and non-destructive behavior, so the bar is lower. The description still earns credit for actionable traits beyond structured data: 1-based inclusive line ranges, images returned as images, and other binaries reported rather than returned. It omits error behavior (missing file, permission failures) and encoding concerns, so not a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two dense sentences with zero waste. The core purpose and line range are front-loaded, and the edge-case behavior for images and binaries follows without filler or repetition of annotation data.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a three-parameter, fully annotated read tool with no output schema, the description covers the important decision points: what is returned for text, images, and other binaries. Missing error conditions and file-size or encoding caveats are real but minor gaps, so it falls just short of fully complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents path, start_line, and end_line including 1-based indexing and defaults. The description mostly restates the range semantics and adds context about non-text inputs rather than deepening parameter behavior, which matches the baseline 3 for high schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb and resource ('Read a text file') and sharpens it with line-slicing and non-text handling. It is clearly distinguishable in spirit from list/search/stat, but it never names those siblings or states the contrast explicitly, which keeps it at 4 rather than 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The use case (fetch file contents, optionally a line window) is implied clearly enough to act on. However, there is no explicit guidance on when to prefer this over stat, list, or search, and no stated prerequisites or exclusions, so it remains implied usage rather than real routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchARead-onlyIdempotent
Search text files for a regular expression; returns path, line number and line for each match. Optional start path, glob filter, ignore_case.
| Name | Required | Description | Default |
|---|---|---|---|
| glob | No | Glob on paths as shown, e.g. "*.rs". | |
| path | No | Directory or file to search; omit for everything. | |
| pattern | Yes | Regular expression (Rust regex syntax) matched against each line. | |
| ignore_case | No | Match case-insensitively (default false). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive, and closed-world behavior. The description adds useful return-format detail (path, line number, line per match) and scopes it to text files, but does not cover edge cases such as binary files, recursion, or hidden files.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single compact sentence front-loads the purpose, then return values, then optional parameters. Every clause earns its place and no filler is present.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple, the input schema is fully documented, annotations cover safety, and the description supplies the missing output format because no output schema exists. It is complete enough for correct invocation, though minor search-scope details could be richer.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already explains all four parameters. The description merely repeats optional start path, glob filter, and ignore_case without adding syntax or behavioral nuance beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: search text files for a regular expression, and specifies the return shape. This distinguishes it from sibling tools like read and stat, which retrieve files or metadata rather than matching content.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear context for when to use it: searching text for a regex with optional path and glob narrowing. It does not explicitly name alternatives or when-not-to-use cases, but the distinction from read/list/stat is inferable from the purpose.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
statARead-onlyIdempotent
Describe a path: kind (file, dir, symlink), size, modified time, line count for text files, image type for images.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Path to describe; "." for the top. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and closed-world, so safety is covered. The description adds genuine value by specifying the returned attributes (kind classification, line count for text, image type for images), which is the only place the return contract is documented since no output schema exists. It omits edge behavior such as what happens for a nonexistent path or whether symlinks are followed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One dense sentence, front-loaded with the verb and resource, then the returned fields in a compact parenthetical list. No filler, no repetition of the tool name.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter read-only inspection tool with full annotation coverage, the description supplies the essential return-field contract. The remaining gap is error/edge behavior (missing paths, symlink traversal, cost of line counting on large files), which an agent may need but is minor relative to overall completeness.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single 'path' parameter is documented in the schema including the '"." for the top' convention. The description adds no path semantics (relative vs absolute, symlink resolution) beyond what the schema already provides, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('describe') and resource ('a path') and enumerates exactly what is reported: kind, size, modified time, line count, image type. This makes it distinguishable from siblings like read (contents) and list (directory enumeration), though it never names those siblings to draw the contrast explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: an agent can infer this is the metadata-inspection tool, but the description never says when to prefer it over read or list, nor any precondition such as the path existing. No exclusions or alternatives are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
4 tool updates
v0.1.3- First observed
list - First observed
read - First observed
search - First observed
stat
TDQS
Scored across 4 tools
Each tool has a clearly distinct purpose: list enumerates directory contents, read returns file contents, search finds regex matches, and stat describes metadata. There is little risk of confusing these operations, even though list and search can both traverse folders.
All four tools use short, lowercase verb-style names with a consistent convention. The pattern is predictable and readable throughout the set.
Four tools are well-scoped for a folder/filesystem inspection server. Each tool covers a distinct core operation without unnecessary bloat.
The surface covers read-only filesystem workflows well: listing, reading, searching, and metadata inspection. If write operations such as create, delete, or move are expected for a folder server, those are missing, but the read-only scope appears intentional.
Maintenance
Related MCP Connectors
Browse and manage files in your Moxt AI workspace from any MCP client.
Hosted remote MCP for protected account access, permissions, activity history and supported undo.
Read-only MCP server exposing a user ORANO library to their own AI agent.
Read-only MCP server over the developer docs for the portfolio's JSON API, MCP, A2A and NLWeb.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceMCP server for filesystem operations, enabling reading, writing, editing, moving, and searching files with dynamic directory access control via MCP Roots.1,304,784 npm-
- FlicenseNot gradedqualityCmaintenanceEnables secure filesystem operations (read, write, list, move, search files/directories) through the Model Context Protocol, with dynamic directory access control via MCP Roots.-
- AlicenseNot gradedqualityAmaintenanceEnables remote filesystem access and command/process control through MCP, with safe-by-default permission profiles that restrict file operations to configured roots and make command execution an explicit opt-in.MIT
- AlicenseNot gradedqualityBmaintenanceEnables MCP clients to browse selected folders, search filenames, inspect metadata, and read bounded UTF-8 text files through a read-only, token-authenticated interface.1MIT