On this page
Install
Download the pre-built binary for your platform from the latest GitHub release:
# macOS / Linux — replace OS and ARCH as needed (darwin/linux, amd64/arm64)
curl -sSL https://github.com/eunolabs/eunox/releases/latest/download/eunox-mcp_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz \
| tar -xz eunox-mcp
chmod +x eunox-mcp
sudo mv eunox-mcp /usr/local/bin/
eunox-mcp version
Or install from source with Go 1.25+:
go install github.com/eunolabs/eunox/cmd/eunox-mcp@latest
Write a policy
Save the following as eunox.policy.yaml. This is the
AgentCapabilityManifest format — the same schema used
by eunox-mcp validate, pkg/capability, and
pkg/enforcement:
schemaVersion: "0.1"
name: filesystem-agent
version: 0.1.0
capabilities:
- target: tool:read_file
actions: [call]
conditions:
- type: allowedExtensions
argument: path
extensions: [".csv", ".json", ".txt", ".md"]
- type: maxCalls
count: 50
windowSeconds: 60
- target: tool:write_file
actions: [call]
argumentSchema:
type: object
required: [path, content]
properties:
path:
type: string
pattern: "^/data/.*"
content:
type: string
additionalProperties: false
conditions:
- type: maxCalls
count: 20
windowSeconds: 60
- target: tool:list_directory
actions: [call]
Validate before running:
eunox-mcp validate ./eunox.policy.yaml
See the capability-manifest-guide.md for all condition types, or browse the full condition matrix with worked demos.
Run the proxy
Declare the upstream and its policy in an eunox.yaml:
schemaVersion: "0.1"
transport: stdio
upstreams:
- name: filesystem
transport: stdio
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "/data"]
policy: ["./eunox.policy.yaml"]
eunox-mcp proxy --config ./eunox.yaml
Key flags:
| Flag | Default | Description |
|---|---|---|
--config |
— | Path to the eunox config (YAML): host transport, upstream(s), per-route policy, and listen settings. Required. |
--audit-log |
~/.eunox/audit.jsonl |
OCSF audit log path. Overridden by the config's audit.log. |
--audit-key-path |
~/.eunox/audit.key |
HMAC signing key path (auto-created on first run). |
--shutdown-timeout |
5000 | Grace period (ms) before SIGKILL is sent to a subprocess upstream. |
Claude Desktop / Cursor / Windsurf
Add the proxy as an mcpServers entry in
claude_desktop_config.json (or the Cursor / Windsurf
equivalent). Use absolute paths:
{
"mcpServers": {
"filesystem-governed": {
"command": "eunox-mcp",
"args": ["proxy", "--config", "/absolute/path/to/eunox.yaml"]
}
}
}
Omit a route's policy: (or set enforcement: audit)
— useful for capturing an audit trail before you write any rules.
HTTP transport
For agents that connect over HTTP rather than stdio (LangChain, Go services, curl):
schemaVersion: "0.1"
transport: http
listen: { bind: 127.0.0.1, port: 7391, authToken: ${MY_SECRET} }
upstreams:
- name: local
transport: stdio
command: ./my-mcp-server
policy: ["./eunox.policy.yaml"]
eunox-mcp proxy --config ./eunox.yaml
Connect your client to http://127.0.0.1:7391/mcp/local with
Authorization: Bearer $MY_SECRET. The same policy file
works on both transports. ipRange conditions become
enforceable only on HTTP — stdio sessions have no source IP.
Audit log
Every decision — allow or deny — is written to the OCSF audit log as a signed JSON line:
# Stream live decisions
tail -F ~/.eunox/audit.jsonl | jq
# ASCII histogram of denials grouped by condition type
eunox-mcp stats
# Verify a specific event's HMAC signature
eunox-mcp audit-verify --audit-log ~/.eunox/audit.jsonl
The signing key is auto-generated at
~/.eunox/audit.key (mode 0600) on first run.
Override with --audit-key-path or the
EUNOX_AUDIT_KEY_PATH environment variable. Logs rotate at
100 MiB; rotated files are named
audit.jsonl.<ISO-timestamp>.
Optional: Redis for shared call counters
maxCalls conditions use in-process counters by default —
one counter per session, reset when the proxy restarts. For shared
counters across multiple proxy instances or persistent counters across
restarts, point the proxy at Redis:
eunox-mcp proxy \
--config ./eunox.yaml \
--redis-addr localhost:6379
Redis is entirely optional. A single developer or a CI pipeline never needs it.
Docker
The eunox-mcp image is published to the GitHub Container
Registry:
docker pull ghcr.io/eunolabs/eunox-mcp:latest
Run with a mounted config (which references the policy):
docker run --rm -it \
-v "$PWD/eunox.yaml:/eunox.yaml:ro" \
-v "$PWD/eunox.policy.yaml:/eunox.policy.yaml:ro" \
ghcr.io/eunolabs/eunox-mcp:latest \
proxy --config /eunox.yaml
For CI pipelines, the image is also available for
linux/amd64 and linux/arm64. See the
Dockerfile.mcp
for the build recipe.