Security policies (OPA/Rego)¶
Complete reference for the --policies-json flag, the policy source JSON schema, and the exact input documents passed to Rego evaluators for each capability category.
Flag¶
--policies-json <VALUE>
<VALUE> is either:
- An inline JSON object (the value must start with
{). - A path to a file whose contents are a JSON object.
The JSON is deserialized into PoliciesConfig. Invalid JSON or an unsupported URL scheme is a fatal startup error.
Top-level JSON schema¶
{
"fetch": <OperationPolicies | null>,
"modules": <OperationPolicies | null>,
"filesystem": <OperationPolicies | null>,
"mcp_tools": <OperationPolicies | null>,
"subprocess": <OperationPolicies | null>
}
All five keys are optional. Omitting a key means the corresponding capability is not available.
OperationPolicies schema¶
{
"mode": "all" | "any",
"policies": [ <PolicySource>, ... ]
}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
mode |
string | no | "all" |
"all": every evaluator must allow. "any": at least one must allow. |
policies |
array | yes | — | Ordered list of policy sources. At least one entry is expected. |
An empty policies array produces a chain that always allows.
PolicySource schema¶
{
"url": "<string>",
"policy_path": "<string | null>",
"rule": "<string | null>"
}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
url |
string | yes | — | Scheme determines the evaluator type. See below. |
policy_path |
string | no | category default | Remote OPA only: path under /v1/data/ (e.g., "mcp/fetch"). |
rule |
string | no | category default | Local regorus only: fully-qualified Rego rule to evaluate (e.g., "data.mcp.fetch.allow"). |
URL schemes¶
| Prefix | Evaluator | Notes |
|---|---|---|
http:// or https:// |
Remote OPA | Posts to {url}/v1/data/{policy_path}, reads result.allow. Timeout: 5 seconds. |
file:// (file) |
Local regorus | Loads the .rego file into regorus. |
file:// (directory) |
Local regorus | Loads all .rego files in the directory (sorted alphabetically). Non-.rego files are ignored. Empty directory is a startup error. |
Any other scheme is a fatal startup error.
Category defaults¶
| Category | Remote default policy_path |
Local default rule |
|---|---|---|
fetch |
mcp/fetch |
data.mcp.fetch.allow |
modules |
mcp/modules |
data.mcp.modules.allow |
filesystem |
mcp/filesystem |
data.mcp.filesystem.allow |
mcp_tools |
mcp/tools |
data.mcp.tools.allow |
subprocess |
mcp/subprocess |
data.mcp.subprocess.allow |
Input documents¶
fetch¶
Passed to the policy on every fetch() call from JS.
| Field | Type | Description |
|---|---|---|
operation |
string | Always "fetch". |
url |
string | The raw URL string. |
method |
string | HTTP method in uppercase (e.g., "GET", "POST"). |
headers |
object | Request headers as {name: value} pairs, including any injected by --fetch-header rules. |
url_parsed.scheme |
string | URL scheme (e.g., "https"). |
url_parsed.host |
string | Hostname without port (e.g., "api.example.com"). |
url_parsed.port |
number|null | Explicit port number, or null if not present in the URL. |
url_parsed.path |
string | URL path (e.g., "/v1/data"). |
url_parsed.query |
string | Query string without ?, or "" if none. |
Example input:
{
"operation": "fetch",
"url": "https://api.example.com/v1/data?limit=10",
"method": "GET",
"headers": {"Accept": "application/json"},
"url_parsed": {
"scheme": "https",
"host": "api.example.com",
"port": null,
"path": "/v1/data",
"query": "limit=10"
}
}
Example Rego:
package mcp.fetch
default allow = false
allowed_hosts := {"api.example.com", "cdn.example.com"}
allow if {
allowed_hosts[input.url_parsed.host]
input.method == "GET"
}
filesystem¶
Passed to the policy on every fs.* operation.
| Field | Type | Description |
|---|---|---|
operation |
string | Operation name. See table below. |
path |
string | Absolute path of the file or directory being operated on. |
destination |
string|omitted | Present for rename and copyFile; the target path. Omitted for all other operations. |
recursive |
boolean|omitted | Present for mkdir; whether {recursive: true} was requested. |
encoding |
string|omitted | Present for text reads/writes; e.g., "utf8". |
mcp_headers |
object|omitted | X-MCP-* headers from session initialization, keyed by lowercased name with x-mcp- prefix stripped. Omitted when no headers were captured. |
Operation names:
operation value |
JS API |
|---|---|
readFile |
fs.readFile() (text or buffer) |
writeFile |
fs.writeFile() |
appendFile |
fs.appendFile() |
mkdir |
fs.mkdir() |
rm |
fs.rm() |
rename |
fs.rename() |
copyFile |
fs.copyFile() |
stat |
fs.stat() |
exists |
fs.exists() |
readdir |
fs.readdir() |
Example Rego:
package mcp.filesystem
default allow = false
# Operations with no destination (readFile, writeFile, mkdir, etc.)
allow if {
startswith(input.path, "/data/workspace/")
not input.destination
}
# Operations with a destination (rename, copyFile): check both paths.
allow if {
startswith(input.path, "/data/workspace/")
startswith(input.destination, "/data/workspace/")
}
subprocess¶
Passed to the policy on every Deno.Command or child_process.exec call.
| Field | Type | Description |
|---|---|---|
operation |
string | "command_output" for Deno.Command.output(); "exec" for child_process.exec(). |
command |
string | The executable name or path (e.g., "echo", "/bin/sh"). |
args |
array of strings | Arguments. For exec, args[0] is "-c" and args[1] is the shell command string. |
cwd |
string|omitted | Working directory, or omitted if not specified. |
env |
object|omitted | Environment variable overrides as {KEY: VALUE}, or omitted if not specified. |
Example Rego:
package mcp.subprocess
default allow = false
allowed_commands := {"echo", "cat"}
allow if {
input.operation == "command_output"
allowed_commands[input.command]
}
allow if {
input.operation == "exec"
some pattern in allowed_commands
startswith(input.args[1], pattern)
}
modules¶
Passed to the policy on every ES module import when --allow-external-modules is active and a modules policy is configured.
| Field | Type | Description |
|---|---|---|
specifier |
string | The raw module specifier as resolved to a URL. |
specifier_type |
string | "npm" for esm.sh-hosted npm packages; "jsr" for esm.sh-hosted JSR packages; "url" for all other URL imports. |
resolved_url |
string | The resolved module URL (currently identical to specifier). |
url_parsed.scheme |
string | URL scheme (e.g., "https"). |
url_parsed.host |
string | Hostname (e.g., "esm.sh"). |
url_parsed.path |
string | URL path (e.g., "/lodash-es"). |
specifier_type is determined by the resolved URL: "npm" if the URL contains esm.sh/ (but not esm.sh/jsr/); "jsr" if it contains esm.sh/jsr/; "url" otherwise.
Example Rego:
package mcp.modules
default allow = false
allowed_npm := {"lodash-es", "uuid", "zod"}
allow if {
input.specifier_type == "npm"
input.url_parsed.host == "esm.sh"
some pkg in allowed_npm
startswith(input.url_parsed.path, sprintf("/%s", [pkg]))
}
allow if {
input.specifier_type == "url"
input.url_parsed.host == "cdn.jsdelivr.net"
}
mcp_tools¶
Passed to the policy on every mcp.callTool() call.
| Field | Type | Description |
|---|---|---|
operation |
string | Always "mcp_call_tool". |
server |
string | The upstream MCP server name as given to --mcp-server. |
tool |
string | The tool name being called. |
arguments |
object|null | Tool call arguments as a JSON object, or null if none were provided. |
Example Rego:
package mcp.tools
default allow = false
allow if {
input.server == "math"
input.tool == "add"
}
allow if {
input.server == "utils"
}
Complete example configuration¶
{
"fetch": {
"mode": "all",
"policies": [
{
"url": "http://opa.internal:8181",
"policy_path": "mcp/fetch"
},
{
"url": "file:///etc/mcp/fetch-local.rego"
}
]
},
"filesystem": {
"policies": [
{
"url": "file:///etc/mcp/filesystem.rego",
"rule": "data.mcp.filesystem.allow"
}
]
},
"subprocess": {
"policies": [
{"url": "file:///etc/mcp/subprocess.rego"}
]
},
"modules": {
"policies": [
{"url": "file:///etc/mcp/modules/"}
]
},
"mcp_tools": {
"mode": "any",
"policies": [
{"url": "file:///etc/mcp/mcp_tools.rego"}
]
}
}