Deploy

Deploy eunox-mcp in minutes

One Go binary. One YAML policy file. No Redis, no Postgres, no gateway process — everything runs locally, in-process, on your machine.

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.