Filesystem access¶
Recipes for enabling fs access, restricting it to a sandbox directory, and performing common file operations from JS.
Enable filesystem access¶
Filesystem access is off by default. To enable it, supply a filesystem entry in the --policies-json configuration. The value must be a JSON object (or a path to a JSON file) containing a filesystem key:
{
"filesystem": {
"policies": [
{"url": "file:///etc/mcp-v8/fs-policy.rego"}
]
}
}
Pass it on startup:
mcp-v8 --http-port 8080 --policies-json /etc/mcp-v8/policies.json
Or inline:
mcp-v8 --http-port 8080 \
--policies-json '{"filesystem":{"policies":[{"url":"file:///etc/mcp-v8/fs-policy.rego"}]}}'
The fs global becomes available to JS code as soon as the server starts with a valid policy chain. Without a filesystem entry the global is not injected and any attempt to access fs will throw a ReferenceError.
Restrict access to a directory¶
Use a Rego policy that checks input.path (and input.destination for operations such as rename and copyFile) against an allowed prefix. Create /etc/mcp-v8/fs-policy.rego:
package mcp.filesystem
default allow = false
allow if {
startswith(input.path, "/var/mcp-workspace/")
check_destination
}
# Operations with no destination (readFile, writeFile, stat, etc.) always pass
check_destination if {
not input.destination
}
# For rename and copyFile, the destination must also be within the sandbox
check_destination if {
input.destination
startswith(input.destination, "/var/mcp-workspace/")
}
Point your policies configuration at this file and restart the server. Any attempt to read or write outside /var/mcp-workspace/ will be denied with:
fs.<operation> denied by policy: <path> is not allowed
Restrict by operation¶
You can allow only specific operations by checking input.operation:
package mcp.filesystem
default allow = false
# Allow reads anywhere under the data directory but no writes
allow if {
input.operation in {"readFile", "readdir", "stat", "exists"}
startswith(input.path, "/data/")
}
Operation names match exactly what the JS wrapper passes: readFile, writeFile, appendFile, readdir, stat, mkdir, rm, rename, copyFile, exists.
Note: fs.unlink reuses the rm op internally, so its policy operation is "rm".
Use a remote OPA server¶
Replace the file:// URL with an http:// or https:// URL pointing to a running OPA instance:
{
"filesystem": {
"policies": [
{"url": "http://opa.internal:8181", "policy_path": "mcp/filesystem"}
]
}
}
The server POSTs to http://opa.internal:8181/v1/data/mcp/filesystem with an {"input": ...} body and expects {"result": {"allow": true|false}}.
Chain multiple evaluators¶
Set "mode": "all" (the default) to require every evaluator to return true, or "mode": "any" to allow if at least one returns true:
{
"filesystem": {
"mode": "all",
"policies": [
{"url": "file:///etc/mcp-v8/fs-path-check.rego"},
{"url": "http://opa.internal:8181", "policy_path": "mcp/filesystem"}
]
}
}
Common read/write/list operations¶
Read a text file¶
const text = await fs.readFile("/var/mcp-workspace/config.json");
const config = JSON.parse(text);
Read a binary file¶
const bytes = await fs.readFile("/var/mcp-workspace/image.png", "buffer");
// bytes is a Uint8Array
Write a text file (overwrites)¶
await fs.writeFile("/var/mcp-workspace/output.txt", "result: ok\n");
Write binary data¶
const data = new Uint8Array([0x48, 0x69]);
await fs.writeFile("/var/mcp-workspace/blob.bin", data);
Append to a file¶
await fs.appendFile("/var/mcp-workspace/log.txt", new Date().toISOString() + "\n");
The file is created if it does not exist.
List a directory¶
const names = await fs.readdir("/var/mcp-workspace");
// returns string[] of filenames (not full paths)
Get file metadata¶
const info = await fs.stat("/var/mcp-workspace/config.json");
// { size, isFile, isDirectory, isSymlink, readonly, mtimeMs, atimeMs, birthtimeMs }
Create a directory¶
await fs.mkdir("/var/mcp-workspace/reports", { recursive: true });
Check existence¶
const exists = await fs.exists("/var/mcp-workspace/config.json");
Remove a file¶
await fs.unlink("/var/mcp-workspace/temp.txt");
// or equivalently:
await fs.rm("/var/mcp-workspace/temp.txt");
Remove a directory tree¶
await fs.rm("/var/mcp-workspace/old-reports", { recursive: true });
Rename or move¶
await fs.rename("/var/mcp-workspace/draft.txt", "/var/mcp-workspace/final.txt");
Copy a file¶
await fs.copyFile("/var/mcp-workspace/template.txt", "/var/mcp-workspace/output.txt");