Skip to main content
Glama

OPA MCP Server

CI CodeQL npm version Docker pulls OPA Ecosystem License: MIT Node.js opa-mcp-server MCP server

A Model Context Protocol (MCP) server that turns any MCP-compatible client (Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, Zed, and others) into a first-class Open Policy Agent and Rego authoring environment.

+--------------------+ MCP/stdio  +-----------------+ spawn/HTTP +---------------------+
|  Claude · Cursor · |----------> | @orygn/opa-mcp  |----------> | opa · regal         |
|   VS Code · ...    |<---------- |                 |<---------- | conftest · REST API  |
+--------------------+  52 tools  +-----------------+            +---------------------+

Status: v0.9.1. Tool surface, error codes, and environment variables follow SemVer from v0.1.0 forward.

Upgrading to 0.9.0: a tool refuses an argument it does not declare, and the error names it; it used to be dropped. A policy or query that does not compile is INVALID_REGO in every tool that runs opa, where several returned EVAL_ERROR. rego_explain_decision returns the trace as readable lines, not OPA's raw events. conftest_test reports a run that evaluated no rule as passed: false, and rego_generate_test_skeleton writes todo_test_ stubs, which opa test skips until they are filled in. Responses are compact JSON.

Upgrading to 0.8.0: opa_exec loads dataPaths as opa eval --data does, so a directory that also holds test fixtures, or JSON and YAML that is not data, can now fail to load; pass it as bundle to load it as before. A policy that does not load is INVALID_REGO in opa_exec and the conftest tools, where it was EVAL_ERROR and UNKNOWN_ERROR.

Upgrading to 0.7.0: Node.js 22 or later is required. The bundled OPA is 1.21, which reads YAML against the 1.2 schema: bare yes, no, on and off in data files are strings now, not booleans. If you supply your own binary via OPA_BINARY or PATH, only the Node requirement applies.

Upgrading to 0.6.0: rego_bench reports iterations, nsPerOp, allocsPerOp and bytesPerOp. The fields opa prints (N, T, Bytes, MemAllocs, MemBytes, Extra) were top-level and now sit under raw for a single run, so anything that read them from the top level has to look there. With count above one, raw is omitted: every document is in runs, and fastest indexes the one the top-level figures come from.

Upgrading to 0.4.0: subprocesses no longer inherit the server's environment. A policy that read a variable through opa.runtime().env will no longer see it; name the variable in OPA_MCP_PASSTHROUGH_ENV if it is genuinely needed. See the security section for why.

Upgrading to 0.3.0: the bundled OPA is now 1.19, so Rego v0 policies no longer parse (if is required before a rule body, contains before a partial set). Run rego_migrate_v1 to convert them, or pass v0Compatible: true to load one as it is. If you supply your own binary via OPA_BINARY or PATH, nothing changes.


Table of contents

Related MCP server: kubernetes-mcp

What you can do with it

Once an MCP client is connected, an agent can:

  • Author Rego. Generate, format, and refactor policies. The server runs the real opa fmt and opa parse so output is byte-identical to what you'd get on the command line, and regal (optional) surfaces idiomatic suggestions.

  • Evaluate against data. Run a query against a policy and an input document. Optional --explain, --profile, and --coverage flags surface execution traces, hot rules, and per-line coverage.

  • Debug a deny. rego_explain_decision walks the agent through every rule that fired (and every one that didn't), so it can answer "why was this rejected" without you reading the trace by hand.

  • Manage policies on a running OPA. List, get, put, delete policies on an OPA server through its REST API. Works against a local opa run --server or a production deployment with bearer-token auth.

  • Build & sign bundles. Package a directory of policies into a deployable bundle, optionally signing it. Output is a regular .tar.gz the agent can hand to your delivery system.

  • Lint. rego_lint runs Regal across a directory or a single file and returns each finding with its category, level and location.

A walk-through of a typical session lives in Cookbook.

Why this MCP

OPA already has a perfectly good CLI and REST API. So why an MCP wrapper?

  • Schema-shaped tool surface. An agent calling rego_eval gets a validated input schema, a structured output envelope, and stable error codes, instead of parsing free-form CLI text and inventing its own failure taxonomy. That alone makes Rego usable to an agent the way a language server makes a language usable to an IDE.

  • Higher-level helpers. rego_explain_decision, rego_generate_test_skeleton, rego_describe_policy, and rego_suggest_fix compose the lower-level primitives into the tasks agents are actually asked to do. They don't exist in the OPA CLI.

  • Curated knowledge. The bundled MCP resources expose the OPA built-in function catalog, the official Rego style guide (formatted for LLMs), and a curated pattern library covering RBAC, ABAC, Kubernetes admission, IaC gates, API authz, and rate limiting, so the agent has authoritative context without needing to scrape it.

  • Safety boundaries the agent can rely on. Path allow-list, subprocess timeouts, and response-size caps. Defaults are conservative; running the server doesn't quietly grant the agent more reach than the operator intended.

If you've ever watched an agent fight opa eval's argument order, you'll recognize the gap this fills.

Install

The server runs locally over stdio. Pick the install path that matches your client.

Claude Desktop

Edit claude_desktop_config.json directly (or copy from examples/claude-desktop.json):

{
  "mcpServers": {
    "opa": {
      "command": "npx",
      "args": ["-y", "@orygn/opa-mcp"],
      "env": {
        "OPA_BINARY": "/usr/local/bin/opa",
        "REGAL_BINARY": "/usr/local/bin/regal",
        "OPA_URL": "http://localhost:8181",
        "OPA_MCP_ALLOWED_PATHS": "/path/to/your/policies"
      }
    }
  }
}

Replace the /usr/local/bin/... paths with your real ones. See the first-time install gotcha below. Windows users substitute C:\\path\\to\\opa.exe.

Or download opa-mcp.mcpb from the latest release and double-click it.

Claude Code (CLI)

Register the server for the current project with claude mcp add:

claude mcp add \
  --env OPA_BINARY=/usr/local/bin/opa \
  --env REGAL_BINARY=/usr/local/bin/regal \
  --env OPA_MCP_ALLOWED_PATHS=/path/to/your/policies \
  opa -- npx -y @orygn/opa-mcp

This writes the config into .mcp.json at your project root and is picked up automatically on every claude session in that directory. Add --scope user to register it globally instead.

Replace the paths with your real absolute paths (same caveat as Claude Desktop above). On Windows use C:\path\to\opa.exe syntax.

Persistent context and auto-checks for policy repos. If you work in an OPA policy repo regularly, two extra files remove repetitive setup from every session:

  • examples/CLAUDE.md -- copy to your repo root or .claude/CLAUDE.md. Claude Code loads it every session, so the agent always knows which tools to use and what conventions apply.

  • examples/claude-code-hook.json -- merge the hooks block into .claude/settings.json. Runs opa check after Claude writes or edits a .rego file and hands any error back to Claude, without a manual tool call.

Cursor

Drop examples/cursor.json into either .cursor/mcp.json (project-scoped) or ~/.cursor/mcp.json (user-scoped).

VS Code (GitHub Copilot Chat)

Drop examples/vscode.json into .vscode/mcp.json, or paste the servers block into your user settings.json under mcp.servers.

Windsurf, Zed, and others

See examples/ for a full set of drop-in configs.

Manual install (any MCP client)

npm install -g @orygn/opa-mcp
opa-mcp --version

then point your client at the opa-mcp binary.

Docker

docker pull orygn/opa-mcp:latest
docker run --rm -i \
  -v /path/to/your/policies:/policies:ro \
  -e OPA_MCP_ALLOWED_PATHS=/policies \
  orygn/opa-mcp

The image is multi-arch (linux/amd64, linux/arm64), bundles pinned versions of opa and regal, and runs as a non-root user. No host install of OPA or Regal is required.

⚠ If every tool call returns OPA_BINARY_NOT_FOUND

The npm package carries its own opa for the five platforms it is built for, so a client PATH without opa on it does not matter there. The MCPB has no bundled copy, and on any other platform neither does npm: then the server boots but every tool call returns OPA_BINARY_NOT_FOUND. Neither the npm package nor the MCPB bundles regal or conftest, and the Docker image ships regal but not conftest, so their tools need a PATH entry or an explicit path either way.

Fix: add OPA_BINARY and REGAL_BINARY env entries to your client config with the absolute path to each binary. The example configs under examples/ ship with placeholder paths you replace. Find the real paths with:

which opa && which regal                                    # macOS / Linux
Get-Command opa, regal | Select-Object Source              # Windows

This does not affect the Docker install path, which ships opa and regal in the image and bypasses PATH entirely. The MCPB bundle carries neither, and unlike the npm install has no bundled fallback: set OPA_BINARY or put opa on PATH. See Troubleshooting for full detail.

Configuration

The server reads its configuration from environment variables. Every variable is optional; defaults are sensible for a local OPA on http://localhost:8181.

Variable

Default

Purpose

OPA_URL

http://localhost:8181

Base URL of an OPA REST endpoint, used by opa_* tools.

OPA_TOKEN

(unset)

Bearer token for OPA, if your instance requires auth. Treated as a secret. Never echoed in logs or tool responses.

OPA_BINARY

opa (on PATH)

Path to the opa CLI, used by rego_* tools.

REGAL_BINARY

regal (on PATH)

Path to the regal linter. Required by rego_lint, rego_fix, and rego_security_audit.

CONFTEST_BINARY

conftest (on PATH)

Path to the conftest binary. Only required by conftest_* tools. Returns CONFTEST_NOT_FOUND if absent.

OPA_MCP_ALLOWED_PATHS

(unset)

Comma- or semicolon-separated list of directories the server is allowed to read policies from. When unset, file-based tools refuse to read from disk.

OPA_MCP_LOG_FILE

<tmpdir>/orygn-opa-mcp.log

Path the server appends logs to. The server never writes to stdout; that channel is reserved for the MCP protocol.

OPA_MCP_LOG_LEVEL

info

One of debug, info, warn, error.

OPA_MCP_MAX_RESPONSE_BYTES

100000

Hard cap on a single tool response. Larger payloads are truncated with a __truncated: true marker. Values below 512 are refused.

OPA_MCP_TIMEOUT_MS

30000

Hard timeout for any spawned subprocess (opa, regal). After this, the child gets SIGTERM and then SIGKILL.

OPA_MCP_HTTP_TIMEOUT_MS

15000

Timeout for each request to the OPA REST API, from the connection attempt to the last byte of the response; reported as TIMEOUT.

OPA_MCP_NO_TELEMETRY

(unset)

Set to 1 to disable the anonymous startup ping. The ping sends the server version, OS platform, and a random install ID, stored at ~/.orygn/opa-mcp/install-id; containers and CI jobs send a container or ci label instead. No policy content or file paths are ever sent.

OPA_MCP_MAX_SUBPROCESS_BYTES

33554432 (32 MiB)

Maximum bytes captured from a subprocess's stdout and stderr, counted separately. On overflow the stream is clamped, the child is stopped, and the tool returns OUTPUT_TOO_LARGE. Distinct from OPA_MCP_MAX_RESPONSE_BYTES, which trims the reply after the output is already in memory.

OPA_MCP_PASSTHROUGH_ENV

(unset)

Comma-separated variable names to pass through to opa, regal and conftest. Everything else is withheld. Anything named here is readable by any policy the server evaluates, via opa.runtime().env, so use it only for values that are safe in that position.

OPA_MCP_BLOCK_ENV

(unset)

Comma-separated variable names to withhold from opa, regal and conftest even when they are on the built-in allow-list. Applied last, so it also overrides OPA_MCP_PASSTHROUGH_ENV. Use it to drop the proxy variables, which can carry credentials, at the cost of proxy support.

Paths in OPA_MCP_ALLOWED_PATHS must be absolute, and a *_BINARY value is either a bare command name looked up on PATH or an absolute path; anything else stops the server at startup. A binary that cannot be run is reported by each tool call with a structured error.

Tool reference

Every tool returns a JSON envelope:

{ "ok": true, "data": { ... }, "warnings": [ ... ] }
{ "ok": false, "error": { "code": "INVALID_REGO", "message": "...", "hint": "...", "details": { ... } } }

Stable error codes: INVALID_INPUT, INVALID_REGO, INVALID_BUNDLE, EVAL_ERROR, OPA_BINARY_NOT_FOUND, REGAL_NOT_FOUND, CONFTEST_NOT_FOUND, OPA_UNREACHABLE, OPA_AUTH_FAILED, POLICY_NOT_FOUND, DATA_NOT_FOUND, PATH_NOT_ALLOWED, PATH_NOT_FOUND, NO_TESTS_FOUND, COVERAGE_BELOW_THRESHOLD, OPA_VERSION_UNSUPPORTED, GITHUB_TOKEN_MISSING, GIST_CREATE_FAILED, OUTPUT_TOO_LARGE, SUBPROCESS_KILLED, OPA_URL_INVALID, TIMEOUT, CANCELLED, UNKNOWN_ERROR. A rego_eval batch can also give an entry NOT_EVALUATED: an input the call stopped before reaching, after another timed out.

Category A: Authoring & static analysis

Operate on Rego source code without needing a running OPA server. Wrap opa fmt, opa parse, opa check, opa inspect, opa capabilities, opa deps, and regal.

Tool

What it does

rego_format

Format Rego source. Wraps opa fmt. Idempotent.

rego_check

Type-check and validate Rego. Wraps opa check.

rego_lint

Run Regal across a file or directory. Returns each violation with its category, level and location. Requires regal on PATH or REGAL_BINARY set.

rego_parse_ast

Parse Rego to AST JSON. Wraps opa parse.

rego_inspect

Inspect a bundle or directory: packages, rules, annotations. Wraps opa inspect.

rego_capabilities

List the built-ins and features the resolved opa binary understands (OPA_BINARY, then PATH, then the bundled copy); builtins names up to 100 to return full records for

rego_deps

Static dependency analysis: rule-level data references and cross-package calls.

rego_migrate_v1

Migrate Rego v0 source to v1. Renames a rule v1 reserves the name of (contains, every, if, in) and replaces the built-ins v1 removed, with exact equivalents, before opa fmt --rego-v1 converts the syntax and opa check validates it. Given inputs, evaluates the original as v0 and the result as v1 on each and reports any rule that differs, plus any queries, which is how functions are compared. Returns { original, migrated, changed, valid, errors, rewrites, notes, equivalence }. dataPaths loads the data the policy reads for that comparison.

rego_check_schema

Check Rego against a JSON Schema. Validates that every input.* field the policy reads exists in the schema using opa check --schema. Accepts an inline schema, a path to a JSON Schema file, or a schema directory when the policy declares schemas: annotations.

// Input
{
  "source": "package x\nallow if input.user==\"admin\""
}

// Output (ok)
{
  "ok": true,
  "data": {
    "formatted": "package x\n\nallow if input.user == \"admin\"\n",
    "changed": true
  }
}
// Input
{
  "source": "package x\nallow if y",
  "strict": true
}

// Output (error path; the JSON diagnostics arrive on stderr from opa)
{
  "ok": true,
  "data": {
    "valid": false,
    "errors": [
      {
        "code": "rego_unsafe_var_error",
        "message": "var y is unsafe",
        "location": { "row": 2, "col": 11 }
      }
    ]
  }
}

Category B: Evaluation & testing

Run a query against a policy and input. Wrap opa eval, opa test, and opa bench. Each of these tools takes v0Compatible to load a policy written before OPA 1.0 without migrating it, and so does every other tool that reads a policy through opa or conftest, from rego_check to rego_verify and conftest_test. OPA then reads the query as v0 too, so the future keywords are imported for it and in and every still work there. rego_policy_diff takes it per side (v0CompatibleA, v0CompatibleB), to compare a legacy policy with its migrated copy. The exceptions are rego_deps, since opa deps has no such option, and the Regal tools, which need none, since Regal reads either version.

Tool

What it does

rego_eval

Evaluate a query against a policy and input. The bread-and-butter tool. inputs evaluates the query against up to 50 input documents in one call and reports each; with no policy, a query alone tries out a built-in or an expression. Each result says whether the query was defined, and print() output comes back in printed.

rego_eval_with_explain

Evaluate with --explain=full and return OPA's raw trace events. rego_explain_decision renders the same trace as readable lines.

rego_eval_with_profile

Evaluate with --profile and return per-rule timing and evaluation counts.

rego_eval_with_coverage

Evaluate with --coverage and return per-line coverage.

rego_test

Run Rego unit tests with opa test. Returns pass, fail, skip and error counts plus per-test records; errored counts tests OPA could not evaluate. coverage or threshold also adds the coverage report, unless a test fails or errors. allPassed is true only when at least one test ran and none failed, errored or was skipped.

rego_bench

Run opa bench and return statistical timing data.

rego_compile_query

Partially evaluate a query against a policy.

opa_exec

Batch-evaluate a decision against multiple input files. Returns per-file results with successCount and errorCount. dataPaths load as opa eval --data loads them; bundle takes a bundle.

rego_test_multiroot

Run opa test once per root and aggregate. Use when opa test . hits package conflicts. Totals include totalErrored.

// Input
{
  "query": "data.rbac.allow",
  "source": "package rbac\nimport rego.v1\nallow if input.role == \"admin\"",
  "input": { "role": "admin" }
}

// Output
{
  "ok": true,
  "data": {
    "result": [{ "expressions": [{ "value": true, "text": "data.rbac.allow", "location": { "row": 1, "col": 1 } }] }]
  }
}

Category C: Bundle operations

Package, sign, and verify deployable bundles. Wrap opa build, opa sign, and opa build --verification-key.

Tool

What it does

opa_bundle_build

Build a .tar.gz bundle from a policy directory. Supports optimize and revision.

opa_bundle_sign

Sign a bundle directory in place with a private key; an archive is refused, since OPA reads the signature from inside it, and comes signed from opa_bundle_build. A directory signature stays valid wherever the directory is placed under the same name. Returns the path, algorithm, and file count.

opa_bundle_verify

Verify a signed bundle with a public key through opa build --verification-key. Failures name the reason: wrong key, scope, modified, added, missing or unparseable file, unsigned, or a bundle that does not load.

Category D: OPA server management

Talk to a running OPA server over its REST API. Require OPA_URL to point at a reachable server.

Tool

What it does

opa_list_policies

List the policy IDs registered on the server, with a count. includeSource and includeAst add the Rego text or the parsed AST.

opa_get_policy

Get a single policy by ID. Returns the Rego source; includeAst adds OPA's parsed AST.

opa_put_policy

Upload or replace a policy; replaced says whether one existed. A policy that does not compile returns INVALID_REGO with OPA's errors and is not stored.

opa_delete_policy

Delete a policy by ID.

opa_get_data

Read a path from the data hierarchy.

opa_put_data

Write to a path in the data hierarchy.

opa_patch_data

Apply a JSON Patch to the data hierarchy.

opa_delete_data

Delete a document from the data hierarchy.

opa_query_decision

POST to a /v1/data/... decision endpoint with input. defined: false means the path produced no value, which is not the same as false.

opa_compile_query

Partially evaluate a query against the running server.

opa_health

Liveness / readiness check. A server that answers reports healthy: true or healthy: false with OPA's reason; OPA_UNREACHABLE means it could not be reached at all.

opa_status

The same GET /v1/config document as opa_config, under a status key. Bundle and decision-log status (/v1/status) is not exposed. Service header values are redacted.

opa_config

Server configuration from GET /v1/config. OPA drops the credentials block but returns service headers verbatim, so header values are redacted here and the names kept.

Category E: Higher-level helpers

The differentiation surface. These compose lower-level primitives into the tasks agents are actually asked to do.

Tool

What it does

rego_explain_decision

Evaluate with full tracing and return the rules entered and fired, plus the trace as readable lines. The Fail lines name the condition that stopped a rule.

rego_generate_test_skeleton

Given a policy, generate a _test.rego skeleton with one stub per rule. Stubs are todo_test_ rules, which opa test reports as skipped until they are filled in and renamed test_.

rego_describe_policy

Summarize a policy's package, imports and per-rule structure from its AST. For the input references a policy reads, use rego_infer_input_schema

rego_suggest_fix

Pass errors from rego_check or violations from rego_lint unchanged; returns a fix suggestion per diagnostic with a confidence level, and the rule's documentation link where there is one.

rego_coverage_gaps

Run opa test --coverage and return per-file uncovered line ranges, sorted worst first. Use threshold to focus on files below a target percentage.

rego_security_audit

Run regal lint restricted to its bugs category, plus any custom rules in a security category, across a directory. Returns severity-grouped findings with remediation guidance. Modules that do not parse are listed in unparseable and the rest are audited.

rego_infer_input_schema

Statically analyse a policy (or directory of policies) with opa parse and return a JSON Schema describing every input.* field the policy reads. Follows loop variables, rules and function parameters bound to part of the input, so some c in input.containers then c.image gives input.containers[].image. A type is set only where the policy shows it. No running OPA required. A starting point for test inputs or a rego_check_schema schema; review it first.

rego_fix

Run regal fix to auto-apply mechanical fixes. Regal 0.42 and later fix opa-fmt, use-rego-v1, use-assignment-operator, no-whitespace-comment, directory-package-mismatch, non-raw-regex-pattern, prefer-equals-comparison, redundant-existence-check and constant-condition; older releases fix a subset. Use dryRun: true to preview changes first. Returns a per-file breakdown of which rules were applied and, for directory-package-mismatch, the new path the file was moved to.

rego_format_write

Run opa fmt --write to canonically format one or more Rego files or directories in place. Use dryRun: true to list which files would change without modifying them. Validates all files parse successfully before writing any. Supports regoV1, v0Compatible, and v1Compatible flags. Only requires opa.

rego_policy_diff

Evaluate the same query against two policies in parallel and compare the results. Returns equal: true/false, the raw value from each side (resultA/resultB), and changedPaths -- dot/bracket JSON paths that differ. Each side takes inline source or a file/directory path. Useful for verifying refactor equivalence or mapping divergence between two policy versions. inputs compares the two on up to 50 input documents in one call and returns batch, one entry per input, with differing counting the inputs on which they disagree.

rego_verify

Formally verify a property about a Rego rule using SMT solving (Microsoft Z3 via WASM). Unlike testing, this checks ALL possible inputs mathematically and either proves the property holds or returns a concrete counterexample. The kind field takes always_true, never_true or satisfiable. Handles equality, comparison, string built-ins (startswith, endswith, contains, regex.match), multi-clause rules, rule defaults, non-boolean head values, and cross-rule inlining. Reports INCONCLUSIVE rather than guessing for negation-as-failure, comprehensions, partial set and object rules, functions, else chains, and complex regex. A body reading an absent field is undefined rather than true, so always_true requires the rule to hold for an empty input too.

rego_explain_undefined

Explain why a Rego query is undefined, fell back to its default, or (for a set such as deny) holds no element. Combines a plain eval, a full-trace eval, and per-condition AST analysis to identify the exact body expression blocking each rule. Returns a structured breakdown of which conditions blocked each rule plus a human-readable summary.

rego_playground_share

Publish a policy (and optional input) as a secret GitHub Gist (pass public: true to list it) and return the link, for sharing a reproduction. Requires GITHUB_TOKEN with the gist scope; returns GITHUB_TOKEN_MISSING otherwise.

Category F: Conftest (configuration policy testing)

Test Kubernetes manifests, Terraform plans, Dockerfiles, Helm charts, and any YAML/JSON/HCL/TOML/INI against Rego policies using conftest. Requires conftest on PATH or CONFTEST_BINARY set; all four tools return CONFTEST_NOT_FOUND otherwise.

Tool

What it does

conftest_test

Evaluate config files or an inline document against Rego policies with conftest test. Per-file, per-namespace results with arrays always present, and a summary that counts files by name. Parser names are a closed set. A run that evaluated no rule (wrong namespace, or no deny/violation/warn rules) is reported as passed: false with nothingEvaluated: true, where conftest itself exits 0.

conftest_verify

Run the test_* rules in a conftest policy directory with conftest verify. Reports per-rule results and NO_TESTS_FOUND when there are none.

conftest_pull

Pull a policy bundle from an OCI registry or Git repo into a local directory with conftest pull. The target directory need not exist; conftest creates it, and empties it first, so do not point it at one holding anything else. Omitting policy uses the conftest default, which must itself sit inside an allowed root.

conftest_push

Package a local policy directory as an OCI artifact and push to a registry with conftest push. Registry credentials come from the host environment (docker login, ORAS keychain, etc.) -- credentials are never passed through tools.

// Input
{
  "inlineConfig": "apiVersion: v1\nkind: Pod\nspec:\n  containers:\n  - name: app\n    image: nginx:latest",
  "inlinePolicy": "package main\ndeny contains msg if { input.spec.containers[_].image == \"nginx:latest\"; msg := \"pin your image tag\" }"
}

// Output
{
  "ok": true,
  "data": {
    "passed": false,
    "results": [
      {
        "filename": "<inline>",
        "namespace": "main",
        "successes": 0,
        "failures": [{ "msg": "pin your image tag" }],
        "warnings": [],
        "skipped": [],
        "exceptions": []
      }
    ],
    "summary": {
      "passed": 0,
      "failed": 1,
      "warnings": 0,
      "skipped": 0,
      "successes": 0,
      "failures": 1
    }
  }
}

Category G: Meta

Tool

What it does

mcp_server_info

Return server name, version, resolved opa/regal/conftest versions, transport type, and Node.js version in one call. Useful for verifying which server instance the agent is connected to.

Prompts

Three MCP prompts ship with the server. Clients surface them as slash commands or workflow templates.

Prompt

Purpose

policy_authoring_assistant

Walks the agent through writing a new policy: ask about the decision surface, draft, review, format, lint, test.

policy_review_checklist

Review checklist for an existing policy: completeness, edge cases, performance, security pitfalls.

decision_debugging_workflow

Diagnostic flow when a decision is unexpected: gather input, run with explain, isolate the rule, propose a fix.

Resources

Three MCP resources expose curated reference data the agent can read at any time.

Resource URI

What's there

opa://builtins

Categorized OPA built-in function reference, derived at read time from opa capabilities --current. Security-sensitive functions (http.send, crypto.x509.*, opa.runtime) are flagged.

opa://style-guide

Condensed Rego style guide, formatted for LLM consumption.

opa://patterns

Curated common-pattern library: RBAC, ABAC, Kubernetes admission, IaC gates, API authz, rate limiting. Each pattern includes when-to-use, full Rego, a test, and common pitfalls.

Cookbook

A few session shapes that the tool set was designed for.

"Help me write a policy"

You: I need an authz policy: editors can read/write, viewers can only read,
     admins can do anything.

Agent: I'll draft it. (calls rego_format on a draft, then rego_check, then
       rego_lint)

Agent: (calls rego_eval with an `inputs` batch: each role, no role at all,
       a role that is not a string, an unknown method)

Agent: Here's the policy. A request with no role, or a role that is not a
       string, is denied. I kept those probes as tests next to one case per
       role. (calls rego_generate_test_skeleton, fills in the stubs, then
       rego_test)

Agent: All 9 tests pass. Want me to save it to <path>?

"Why was this denied?"

You: This API call is being denied and I don't know why.
     [pastes input.json]

Agent: (calls rego_explain_decision against your local policy with that input)

Agent: The deny comes from rule `forbid_anonymous_writes` at line 17.
       Specifically, `input.user` is null and the request method is "POST".
       The rule fires, which causes the default deny. To allow this, you'd
       need either an authenticated user or a policy exception for this
       endpoint.

"Push this policy to staging OPA"

You: Push policies/rbac.rego to the staging OPA server, but first lint and
     test it.

Agent: (rego_lint → 2 style warnings, no errors)
       (rego_test on policies/ → all pass)
       (opa_put_policy with id="rbac" against $OPA_URL)
       (opa_get_policy to verify)

Agent: Done. Policy `rbac` is live on staging at $OPA_URL.

Architecture

┌──────────────────────────────────── @orygn/opa-mcp ───────────────────────────────────┐
│                                                                                       │
│   src/server.ts ──── McpServer (stdio) ─── tool / prompt / resource registries        │
│                          │                                                            │
│                          ├── tools/authoring/         ─┐                              │
│                          ├── tools/evaluation/        ─┤                              │
│                          ├── tools/bundles/           ─┼─── lib/opa-cli.ts ──┐        │
│                          ├── tools/server-management/ ─┤                     │        │
│                          ├── tools/helpers/           ─┤                     │        │
│                          ├── tools/conftest/          ─┤                     │        │
│                          ├── tools/meta/              ─┘                     │        │
│                          │                                                   ▼        │
│                          │                              lib/subprocess.ts ──┴── opa   │
│                          │                              lib/regal-cli.ts   ───── regal│
│                          │                              lib/conftest-cli.ts ─ conftest│
│                          │                              lib/opa-client.ts  ───── HTTP │
│                          │                                                            │
│                          └── lib/output.ts (envelope + truncation)                    │
│                              lib/security.ts (path allow-list)                        │
│                              lib/errors.ts (structured failures)                      │
│                              lib/logger.ts (file-only, never stdout)                  │
└───────────────────────────────────────────────────────────────────────────────────────┘

Four things worth knowing if you're going to operate this:

  1. stdout is the protocol channel. The server logs to a file via lib/logger.ts and never writes to stdout. If you see stray stdout bytes, the client disconnects; the MCP transport layer is strict.

  2. No tool handler throws. Every handler catches its own exceptions and returns a structured { ok: false, error: ... } envelope, so the agent sees a stable error vocabulary, not a stack trace. An argument that fails the tool's input schema never reaches the handler: the MCP layer rejects it and returns a tool result with isError: true whose text begins MCP error -32602: Input validation error:, rather than the envelope. Decoding subprocess output happens inside an async callback, where a throw would bypass those handlers entirely, so that path is bounded by bytes rather than left to a try/catch that could not see it.

  3. Subprocesses are bounded in time, size, and environment. lib/subprocess.ts runs the binaries with shell: false, a hard timeout with SIGTERM-then-SIGKILL escalation, and a per-stream byte cap. There is no path through the server where an agent can construct a shell command. The timeout alone is not enough: opa buffers a result in memory and writes it in one burst at exit, so a command that finishes well inside the timeout can still deliver hundreds of megabytes.

  4. Children do not inherit the server's environment. lib/child-env.ts builds an explicit allow-list instead. Rego can read its interpreter's environment through opa.runtime().env, so anything passed down is readable by any policy the server evaluates, the proxy variables on the list included.

Security

This server is designed to run locally, started by an MCP client on the user's own machine, communicating over stdio. It is not designed to be exposed on the network.

  • File-based tools refuse to read anything outside OPA_MCP_ALLOWED_PATHS. When that variable is unset, file tools return PATH_NOT_ALLOWED.

  • Subprocesses run with shell: false, a hard timeout, and a byte cap on captured output.

  • Evaluated policy cannot read the server's environment. Rego exposes the environment of the opa process through opa.runtime().env, so a child that inherited process.env would hand OPA_TOKEN, GITHUB_TOKEN, and every other variable to any policy it evaluated. Since rego_eval accepts inline source, no filesystem access is needed to reach that, which puts it one prompt injection away from any untrusted Rego an agent reads. Children get an explicit allow-list instead (lib/child-env.ts). The list holds no cloud or repository token, but it is not free of credentials: HTTP_PROXY and its siblings are on it, and a proxy URL can embed a username and password. They are there because dropping them breaks everyone behind a corporate proxy. Name them in OPA_MCP_BLOCK_ENV to withhold them anyway. OPA_MCP_PASSTHROUGH_ENV opts individual variables back in.

  • OPA_TOKEN is never echoed in tool responses or log entries, and is not passed to any child process.

  • Tools that evaluate Rego are annotated open-world and not read-only. rego_eval and its variants, rego_test, rego_test_multiroot, rego_bench, rego_compile_query, opa_exec, rego_migrate_v1 (when given inputs), the explain, diff and coverage helpers, the conftest tools, and the Regal tools (rego_lint, rego_security_audit, rego_fix, which run a project's custom rules) all run Rego, and OPA's http.send lets a policy reach, and write to, any network address. A client that gates on the hints will ask before running one. opa_query_decision and opa_compile_query are the exception: the remote OPA evaluates a policy it already holds, and their hints describe what the call does to that server. No evaluating tool passes or accepts a capabilities file, so http.send cannot be restricted for evaluation; rego_check and opa_bundle_build accept one, which affects only checking and building.

  • Releases are published with npm provenance; the Docker image is built from the committed Dockerfile, with pinned versions of opa and regal checked against their published digests.

To report a vulnerability, follow SECURITY.md. Please do not open a public issue for security problems.

Troubleshooting

Common issues, fast fixes.

OPA_BINARY_NOT_FOUND (or REGAL_NOT_FOUND / CONFTEST_NOT_FOUND) even though the binary is installed. (most common first-day issue, read this first)

MCP clients (notably Claude Desktop on Windows and macOS) launch the server with a deliberately reduced PATH that omits user-local bin directories, even ones that work fine in your interactive shell. The binary is on your machine; the spawned MCP server just can't see it.

Find the absolute path to opa:

# macOS / Linux
which opa
# → /usr/local/bin/opa  (or /opt/homebrew/bin/opa, or ~/.local/bin/opa)
# Windows
Get-Command opa | Select-Object -ExpandProperty Source
# → C:\Users\you\bin\opa.exe  (or wherever)

Then set OPA_BINARY to that absolute path in your client's MCP env block. The same cause and fix apply to the other binaries: if rego_lint / rego_security_audit / rego_fix report REGAL_NOT_FOUND, or the conftest_* tools report CONFTEST_NOT_FOUND, set REGAL_BINARY / CONFTEST_BINARY to the absolute path the same way (find it with which regal / which conftest). Having the binary on your shell PATH is not enough -- the spawned server gets a reduced PATH. The examples/ configs already include these env vars; just edit the placeholder paths.

This issue does not affect the Docker install path, which bundles opa and regal and bypasses PATH entirely. The MCPB bundle resolves opa from OPA_BINARY or PATH, so it can hit this.

The server starts, then the client says "disconnected."

The most likely cause is something in the process writing to stdout besides MCP frames. If you've added a custom tool, check that no library it calls prints to stdout. The fixed-position safety net is lib/logger.ts. Use it, not console.log.

PATH_NOT_ALLOWED on a file under my project.

OPA_MCP_ALLOWED_PATHS is empty by default. Set it to the absolute path(s) you want the server to read from, comma-separated.

OPA_UNREACHABLE when calling opa_* tools.

OPA_URL (default http://localhost:8181) must point at a running OPA server (opa run --server ...). Check with curl $OPA_URL/health.

TIMEOUT when calling opa_* tools.

The request did not finish within OPA_MCP_HTTP_TIMEOUT_MS (default 15 s). Either OPA is up but slow, or nothing is answering at OPA_URL and the connection attempt is being dropped rather than refused, which looks the same from here. Check OPA_URL and the server's load, or raise the limit.

directory-package-mismatch violation when linting inline source.

Since v0.1.1, the server auto-disables this rule for inline-source calls. If you see it, you are running an older version -- upgrade to v0.1.1 or later. To get canonical signal on this rule, lint via paths against the real on-disk file instead of passing source directly.

Where are the logs?

Default location is <OS-tmpdir>/orygn-opa-mcp.log. That's typically /tmp/orygn-opa-mcp.log on Linux/macOS or %TEMP%\orygn-opa-mcp.log on Windows. Set OPA_MCP_LOG_FILE to override, and OPA_MCP_LOG_LEVEL=debug to widen the firehose.

Development

git clone https://github.com/OrygnsCode/opa-mcp-server.git
cd opa-mcp-server
npm install
npm run dev

Common commands:

npm run lint              # ESLint
npm run typecheck         # tsc --noEmit
npm test                  # unit tests (Vitest)
npm run test:coverage     # unit + coverage report
npm run test:integration  # against real opa + regal binaries
npm run build             # compile to dist/

CI runs lint, typecheck, build, and unit tests on every push and PR across Ubuntu and Windows on Node 22, 24 and 26, plus macOS on Node 22. Integration tests run on Linux, and on Windows as a non-required check, against pinned opa, regal and conftest releases.

For the full contributor workflow (adding tools, naming conventions, logging discipline, release process), see CONTRIBUTING.md.

Versioning & support

This project follows Semantic Versioning. The public surface for SemVer purposes is the set of registered tools, prompts, and resources, their input/output schemas, the recognized environment variables, and the CLI entry point.

Breaking changes will be:

  • announced in CHANGELOG.md under a new major version,

  • preceded by at least one minor release with a deprecation warning,

  • accompanied by a migration note in the release announcement.

Pinned versions of the upstream toolchain (opa and regal) are treated as part of the build, not as a dependency the operator manages. The Dockerfile and CI use the same pin; bumps go through Dependabot or a manual PR.

License

MIT © Orygn LLC

@orygn/opa-mcp is an independent project. It is not affiliated with, endorsed by, or sponsored by the Open Policy Agent project, the Cloud Native Computing Foundation, Styra, or Anthropic. "Open Policy Agent" and "Rego" are trademarks of their respective owners. "Model Context Protocol" is a trademark of Anthropic, PBC.

Listed in the OPA Ecosystem.

Available Tools

52 tools
conftest_pullConftest pullA
DestructiveIdempotent

Download Rego policies from an OCI registry or Git repository into a local directory using conftest pull. Use this to hydrate a local policy/ directory before running conftest_test. Requires conftest on PATH or CONFTEST_BINARY set. The policy directory must be inside OPA_MCP_ALLOWED_PATHS. SECURITY: pulled policies are arbitrary Rego source that will be executed by conftest_test. Only pull from registries or repositories you own or explicitly trust -- malicious policy code can use OPA built-ins (http.send, opa.runtime) to exfiltrate data or make outbound network requests when the tests run.

Inputs: url (required), policy.

conftest_pushConftest pushA
Destructive

Package the local Rego policy directory as an OCI artifact and push it to a registry using conftest push. Registry credentials must be pre-configured in the host environment (docker login, ORAS keychain, etc.) -- this tool never handles credentials. The policy directory must be inside OPA_MCP_ALLOWED_PATHS. Requires conftest on PATH or CONFTEST_BINARY set.

Inputs: repository (required), policy, v0Compatible.

conftest_testConftest testA

Evaluate configuration files (Kubernetes manifests, Terraform plans, Dockerfiles, Helm charts, or any YAML/JSON/HCL/TOML/INI) against Rego policies using conftest test. Returns per-file, per-namespace pass/fail/warn results so you can pinpoint exactly which policy rules fired. Requires conftest on PATH or CONFTEST_BINARY set; returns CONFTEST_NOT_FOUND otherwise. Provide config via files (disk paths) or inlineConfig (inline string). Provide policy via policy (disk path) or inlinePolicy (inline Rego source). Omit policy and inlinePolicy to use conftest's default ./policy directory. Policies are executed by conftest and can call OPA built-ins such as http.send. A run in which no rule was evaluated (wrong namespace, or no deny/violation/warn rules) is reported as passed: false with nothingEvaluated: true and the namespaces that do hold rules, where conftest itself would exit 0.

Inputs: data, files, parser, policy, +8 more.

conftest_verifyConftest verifyA

Run the test_* rules inside *_test.rego files within a conftest policy directory, verifying that the policies themselves are correct. Equivalent to opa test but using conftest's policy-loading machinery. Returns per-file pass/fail results, and NO_TESTS_FOUND when the directory holds no test rules. Requires conftest on PATH or CONFTEST_BINARY set; returns CONFTEST_NOT_FOUND otherwise.

Inputs: data, policy, namespace, v0Compatible.

mcp_server_infoMCP server infoA
Read-onlyIdempotent

Return the name, version, and runtime details of this opa-mcp server instance. Use this when you need to confirm which version of opa-mcp is running, or to verify that the OPA, Regal, and Conftest binaries are reachable.

Inputs: no named parameters.

opa_bundle_buildBuild OPA bundleA
DestructiveIdempotent

Build a deployable bundle from policy / data paths using opa build. Output is a .tar.gz archive with optional inline signing. Supports optimization, custom revision strings, and the WASM target.

Inputs: paths (required), output (required), bundle, ignore, +13 more.

opa_bundle_signSign OPA bundleA
DestructiveIdempotent

Sign a bundle directory with opa sign. A directory is signed in place: .signatures.json is written into it and files are recorded as <directory name>/<file>, so the signed directory verifies wherever it is placed as long as its name is unchanged, with opa_bundle_verify or with opa build or opa run --bundle <name> from its parent. An archive is refused: OPA reads the signature from inside it, so a signed archive comes from opa_bundle_build with signingKey. The key is a PEM private key (RSA or ECDSA); for HMAC algorithms pass a file holding the secret. Extra claims such as keyid and scope come from claimsFile. Returns the path written, the algorithm, and the number of files covered.

Inputs: bundle (required), signingKey (required), claimsFile, signingAlg.

opa_bundle_verifyVerify OPA bundle signatureA
Read-onlyIdempotent

Verify the signature of a signed bundle directory or .tar.gz archive with the public key. OPA has no standalone verify command, so this runs opa build --verification-key into a private temp file that is discarded. A directory is verified by name from its parent, matching how opa_bundle_sign signs it. OPA reads the key, checks the JWT in .signatures.json, compares the scope claim, then checks every file: Rego files by digest before parsing, data files and .manifest by parsed value, so an unparseable data file fails before its digest is compared. Failures return INVALID_BUNDLE with details.reason set to one of signature_invalid, scope_mismatch, file_modified, file_added, file_missing, file_unparseable, unsigned, signatures_malformed, not_a_bundle, bundle_load_error, or unknown when the message is not recognised; the raw output is in details. A key or algorithm OPA cannot use returns INVALID_INPUT. Pass scope exactly as the bundle was signed with. With a single key OPA does not check verificationKeyId against the signature keyid claim. verified: true is returned only when OPA loaded the bundle with its signature intact.

Inputs: bundle (required), verificationKey (required), scope, signingAlg, +2 more.

opa_compile_queryCompile (partially evaluate) a query on OPAA
Read-onlyIdempotent

Send a query to the OPA server's /v1/compile endpoint for partial evaluation. Returns the residual query -- what remains after substituting in everything that's known.

Inputs: query (required), input, unknowns.

opa_configOPA configurationA
Read-onlyIdempotent

Return the running OPA server configuration from GET /v1/config. OPA drops the credentials block but returns services.*.headers verbatim, which is the ordinary place to put an API key or a bearer token, so those values are redacted here and the header names kept.

Inputs: no named parameters.

opa_delete_dataDelete a data document from OPAA
Destructive

Remove a document from OPA's data store at the given path. A path is read as dotted (users.alice) unless it contains a slash, in which case slash is the only separator (users/alice), so a key such as example.com is addressable as hosts/example.com. Pass segments instead when a key contains both. OPA responds with 204 No Content on success; if no document exists at the path, OPA returns 404 which is mapped to DATA_NOT_FOUND. Root-path deletion (/v1/data/ itself) is intentionally excluded -- supply at least one path segment.

Inputs: path, segments.

opa_delete_policyDelete OPA policyA
Destructive

Delete a policy by ID from the running OPA server.

Inputs: id (required).

opa_execBatch-evaluate OPA policy against input filesA

Evaluate a policy decision against one or more input files using opa exec --format=json. Unlike rego_eval (single input), opa exec processes every file independently and returns a per-file result -- ideal for CI pipelines that check many config files against a policy in one call. Supply bundle for a bundle, or dataPaths for plain .rego, JSON and YAML files and directories, which are loaded as opa eval --data loads them; the two are mutually exclusive. Each file that fails evaluation appears in results with an error field rather than a result field. Set one of fail/failDefined/failNonEmpty to turn the call into a CI gate: the result then reports failed: true (instead of erroring) when the gate condition is met.

Inputs: decision (required), inputPaths (required), fail, bundle, +6 more.

opa_get_dataRead data from OPAA
Read-onlyIdempotent

Read a path from OPA's data hierarchy. A path is read as dotted (users.alice) unless it contains a slash, in which case slash is the only separator (users/alice), so a key such as example.com is addressable as hosts/example.com. Pass segments instead when a key contains both.

Inputs: path, segments.

opa_get_policyGet OPA policy by IDA
Read-onlyIdempotent

Fetch a single policy by ID from the running OPA server. Returns the Rego source; the parsed AST is omitted unless asked for, since it is roughly forty times the size of the source it came from. Use rego_parse_ast on the source when an AST is what's wanted.

Inputs: id (required), includeAst.

opa_healthOPA health checkA
Read-onlyIdempotent

Hit the OPA /health endpoint. A server that answers reports { healthy: true } on 200 and { healthy: false } with OPA's own reason otherwise, so an unactivated bundle is a health result rather than a tool error. OPA_UNREACHABLE means the server could not be reached at all. Supports bundles and plugins query flags to require those subsystems to also be healthy.

Inputs: bundles, plugins.

opa_list_policiesList OPA policiesA
Read-onlyIdempotent

List policies registered on the running OPA server. Returns the policy IDs and a count. Set includeSource for the Rego text of every policy, or includeAst for the parsed AST of every policy; both are off by default because either one pushes a list of any real size past the response cap.

Inputs: includeAst, includeSource.

opa_patch_dataPatch data on OPAA
Destructive

Apply a JSON Patch (RFC 6902) to the data document. Each operation is { op, path, value? }. Omit both path and segments to patch the root of the data hierarchy, which is how a whole new top-level document is added.

Inputs: operations (required), path, segments.

opa_put_dataWrite data to OPAA
DestructiveIdempotent

Write or replace a value at the given data path. Body is sent as JSON. A path is read as dotted (users.alice) unless it contains a slash, in which case slash is the only separator (users/alice), so a key such as example.com is addressable as hosts/example.com. Pass segments instead when a key contains both.

Inputs: path, value, segments.

opa_put_policyUpload or replace OPA policyA
DestructiveIdempotent

Upload a Rego policy under the given ID. Replaces any existing policy with that ID; replaced says whether one existed. The policy is uploaded as raw text/plain and OPA compiles it on the server, so a policy that does not compile comes back as INVALID_REGO with OPA's errors, and nothing is stored.

Inputs: id (required), source (required).

opa_query_decisionQuery OPA decisionA
Read-onlyIdempotent

Evaluate a decision against the running OPA server. POSTs to the data path with {input} and returns whatever the rule produces, plus defined: false when the path produced no value, which OPA reports by leaving result out. Undefined is not false; a caller that treats it as false or as allow decides on something the policy never said. Use this to ask the server "given this input, what does data.X.allow say?"

Inputs: path, input, explain, metrics, +1 more.

opa_statusOPA statusA
Read-onlyIdempotent

Return the running OPA server configuration via GET /v1/config. Returns the same underlying document as opa_config but presented under a status key as a convenience for agents that want to check "what is running" rather than "what was the server configured with". The response includes bundle settings, decision-log settings, and plugin configuration as OPA reported them at startup. Service header values are redacted, since OPA returns them verbatim and a header is the ordinary place to put an API key.

Inputs: no named parameters.

rego_benchBenchmark Rego queryA

Benchmark a Rego query against a policy + input with opa bench. Returns statistical timing data: iterations, ns/op, and allocation counts. Use this to spot slow rules.

Inputs: query (required), count, input, paths, +2 more.

rego_capabilitiesOPA capabilitiesA
Read-onlyIdempotent

Return OPA capabilities -- the available builtins, future keywords, features, and WASM ABI versions. With current: true, returns the running OPA's capabilities. With version: "v1.19.0", returns those of a specific version. With neither, lists available named versions. By default (names_only: true), returns only builtin names and count to stay within response size limits. Pass builtins: [...] for the full type signatures and documentation of a few named builtins; names_only: false returns every full record, which needs OPA_MCP_MAX_RESPONSE_BYTES raised above its default.

Inputs: current, version, builtins, names_only.

rego_checkCheck RegoA
Read-onlyIdempotent

Type-check Rego with opa check. Returns { valid: true, errors: [] } on success, or a list of structured diagnostics with file/line locations on failure. Provide either source for inline checking or paths for file/directory checking.

Inputs: paths, bundle, source, strict, +4 more.

rego_check_schemaCheck Rego against a JSON SchemaA
Read-onlyIdempotent

Validate that a Rego policy's input.* field references are consistent with a JSON Schema using opa check --schema. Every field the policy reads from input must exist in the schema; mismatches surface as rego_type_error diagnostics with file/line locations. Returns { valid: true, errors: [] } when all references match the schema, or { valid: false, errors: [...] } with structured diagnostics when they do not. Accepts the schema inline (pass the schema output of rego_infer_input_schema directly as inlineSchema) or as a path to a JSON Schema file on disk, or to a schema directory when the policy declares schemas: annotations (schemaPath). Provide source for inline Rego or paths for file/directory checking.

Inputs: paths, source, strict, schemaPath, +2 more.

rego_compile_queryPartially evaluate a Rego queryA

Run partial evaluation on a query -- substitute known values and return the residual policy. Defaults unknowns to ["input"] (treat input as unknown), so the residual encodes "given input X, this is what would have to be true." Use this for offline policy slicing or pre-computing decision sets.

Inputs: query (required), input, paths, source, +5 more.

rego_coverage_gapsRego test coverage gapsA

Run opa test --coverage and return a per-file breakdown of uncovered line ranges. Identifies which rules or branches are not yet exercised by tests. Files are sorted by coverage ascending so the worst-covered files appear first. Use threshold to limit the report to files below a target coverage percentage.

Inputs: paths (required), threshold, runPattern, v0Compatible.

rego_depsRego dependency analysisA
Read-onlyIdempotent

Static dependency analysis for a Rego reference. Given a target ref like "data.example.allow", returns the base document references (input/data leaves) and virtual document references (rules) it depends on, transitively.

Inputs: ref (required), paths (required).

rego_describe_policyDescribe Rego policyA
Read-onlyIdempotent

Parse a Rego policy and return a structured summary: package, imports, and rules. Each rule reports clauseCount (how many definitions share the name), isDefault (true if any clause is a default), hasArgs, bodyLength (total body expressions across all clauses), and inline annotations. Useful as the first step in any "what does this policy do" workflow.

Inputs: source (required), v0Compatible.

rego_evalEvaluate Rego queryA

Evaluate a Rego query against a policy and an input document using opa eval. Returns the standard {result: [...]} shape plus defined, which is false when the query produced no value (undefined is not false), and printed, the lines the policy wrote with print(). The bread-and-butter authoring tool. The policy is optional, so a query alone tries out a built-in or an expression. Pass inputs to evaluate one query against many input documents in one call, and v0Compatible for a policy still written in pre-1.0 Rego.

Inputs: query (required), input, paths, inputs, +6 more.

rego_eval_with_coverageEvaluate Rego with coverageA

Evaluate with --coverage and return per-line coverage data. Useful for verifying that tests actually exercise the rules they're meant to.

Inputs: query (required), input, paths, source, +5 more.

rego_eval_with_explainEvaluate Rego with execution traceA

Evaluate with --explain=full and return OPA's raw trace events (with their AST nodes) alongside the result. For a readable trace use rego_explain_decision, which renders the same events as lines. Raw events are large: over the response cap the bound-variable values are dropped first, then trailing events (explanationTruncated), and the result is kept.

Inputs: query (required), input, paths, source, +5 more.

rego_eval_with_profileEvaluate Rego with profilingA

Evaluate with --profile and return per-rule timing and evaluation counts. Use this to find hot rules in slow policies.

Inputs: query (required), input, paths, source, +5 more.

rego_explain_decisionExplain Rego decisionA

Evaluate a Rego query with full tracing and return the result, the rules entered (rulesEvaluated) and those that produced a value (rulesFired), and the trace as readable lines: location, nesting, operation, the expression in Rego syntax, and the values of the variables it names. Backtracking (Redo) events are left out. Use it to answer "why was this denied?" or "why did this rule not match?": the Fail lines name the condition that stopped a rule. A trace too long for the response cap is cut from the end (traceTruncated); narrow the query or the input to see the rest.

Inputs: query (required), input, paths, source, +5 more.

rego_explain_undefinedExplain why a Rego query is undefinedA

Diagnose why a fully-qualified Rego query (e.g. "data.authz.allow") produces no value, or falls back to its default. Combines a plain eval, a full-trace eval, and per-condition AST analysis to identify the exact body expression blocking each rule. Handles both runtime failures (trace-based) and indexer elimination (standalone condition eval). A rule written with default allow := false always has a value, so queryResult reports default for it and the same per-rule breakdown follows: the question "why is allow false" is the question this answers. A multi-value rule such as deny contains msg if ... that holds no element reports empty with the same breakdown, which answers "why was this not denied". Returns a structured breakdown of which conditions blocked each rule plus a human-readable summary. paths may name directories, which are searched for the modules that declare the queried package.

Inputs: query (required), input, paths, source, +2 more.

rego_fixAuto-fix Rego violationsA
Destructive

Run regal fix to automatically apply mechanical fixes. Regal 0.42 fixes opa-fmt, use-rego-v1, use-assignment-operator, no-whitespace-comment, directory-package-mismatch, non-raw-regex-pattern, prefer-equals-comparison, redundant-existence-check and constant-condition; older releases fix a subset. Use dryRun: true to preview changes before modifying files. NOTE: directory-package-mismatch moves files to match their package path -- use disable: ["directory-package-mismatch"] to skip it. Regal before 0.41 refuses files with uncommitted git changes unless force: true. Requires regal.

Inputs: paths (required), force, dryRun, enable, +5 more.

rego_formatFormat RegoA
Read-onlyIdempotent

Format Rego source code using opa fmt. Returns the formatted source and a changed flag indicating whether the input was already canonical. When the source uses string interpolation ($"..." or $... syntax) and OPA v1.12.0 or v1.12.1 is detected, the tool warns about or blocks formatting due to a known OPA bug that corrupts { escape sequences (fixed in OPA v1.12.2).

Inputs: source (required), v0Compatible.

rego_format_writeFormat Rego files in placeA
DestructiveIdempotent

Run opa fmt --write to canonically format one or more Rego files or directories in place. Use dryRun: true to preview which files would change without modifying them. Returns a list of files that were (or would be) reformatted. Unlike rego_format which returns formatted source as a string, this tool writes directly to disk. Supports regoV1, v0Compatible, and v1Compatible flags for version-specific formatting. If any file cannot be parsed, the operation is aborted and no files are written.

Inputs: paths (required), dryRun, regoV1, v0Compatible, +1 more.

rego_generate_test_skeletonGenerate Rego test skeletonA
Read-onlyIdempotent

Generate a *_test.rego skeleton from a policy. Parses the AST, finds each non-test rule, and emits one stub test per rule. Existing test_* and todo_test_* rules are skipped automatically -- only production rules get stubs. Every stub is a todo_test_ rule, which opa test reports as skipped (and exits non-zero for) until a realistic input and the expected value are filled in and it is renamed test_: a placeholder input against a placeholder expectation would otherwise pass while testing nothing. The AST is walked to infer which input.* fields the policy accesses; the inferred shape is used as the placeholder with input as {...} in each stub, so the developer only needs to fill in realistic values rather than guess the structure. With tableStyle: true, each stub uses an every tc in cases { ... } loop so you can add multiple input/expected pairs without duplicating assertion code. The inferredInputShape field in the response shows the detected shape for reference.

Inputs: source (required), tableStyle, v0Compatible.

rego_infer_input_schemaInfer input schemaA
Read-onlyIdempotent

Statically analyse one or more Rego policies and return a JSON Schema (draft-07) object describing every input.* field the policies read. It follows the ways a policy reads input: loop variables (some c in input.containers then c.image), rules whose value is an input path (pod_spec := input.request.object.spec), function parameters (trusted(c)) and object.get keys. A key the policy computes is left open, and a scalar type is set only where a comparison with a literal, an is_* check or a string built-in shows it. Uses opa parse -- no running OPA server required. A starting point for test inputs and for a schema to give rego_check_schema; review it first, since types the policy never shows are left out. Accepts inline source, individual files, or directories (walked recursively for *.rego files).

Inputs: paths, source, v0Compatible.

rego_inspectInspect bundle or policyA
Read-onlyIdempotent

Inspect an OPA bundle, policy directory, or single Rego file with opa inspect. Returns manifest data, namespaces, rule annotations, and (if signed) signature metadata.

Inputs: target (required), v0Compatible.

rego_lintLint RegoA

Lint Rego source with the Regal linter. Returns categorized violations (style, bugs, idiomatic, performance) with file/line locations. Requires regal on PATH or REGAL_BINARY set; returns REGAL_NOT_FOUND otherwise. When called with inline source, location-bound rules whose verdict depends on the on-disk path (directory-package-mismatch) are auto-disabled to avoid temp-file false positives, and location.file is reported as <inline> instead of the randomized temp path. Re-enable those rules via enable if your workflow actually needs them.

Inputs: paths, enable, source, disable, +5 more.

rego_migrate_v1Migrate Rego to v1 syntaxA

Migrate Rego v0 source to Rego v1. First renames what v1 reserves (a rule called contains, every, if or in, and every reference to it in the module) and replaces built-ins v1 removed: re_match and net.cidr_overlap by their v1 names, and all, any, set_diff and the cast_* family by a helper function appended to the module that returns exactly what the built-in did, so behaviour does not change. re_match and net.cidr_overlap get such a helper too where the rename would change behaviour: the module mocks one spelling with with while calling both, or binds regex or net itself. Then opa fmt --rego-v1 converts the syntax (if, contains, import rego.v1) and opa check validates the result. rewrites lists each change by line and notes says why; a renamed rule must also be renamed in any other module that uses it. Pass inputs to evaluate the original as v0 and the result as v1 against each and compare every rule of the package, and queries to compare expressions too, such as calls to its functions; equivalence reports any difference. Evaluating runs the policy, http.send included. Returns the migrated source even when check finds remaining errors. A source that parses only as Rego v1 is returned unchanged; one that parses as both, such as v1 that imports rego.v1, is reformatted like any other. If the source parses as neither, returns INVALID_REGO with opa's own message.

Inputs: source (required), inputs, queries, dataPaths.

rego_parse_astParse Rego to ASTA
Read-onlyIdempotent

Parse Rego source to a JSON AST using opa parse. Returns the AST as a tree of nodes (package, imports, rules, expressions, terms). Use this when you need to introspect policy structure programmatically.

Inputs: source (required), v0Compatible.

rego_playground_shareShare Rego policy as a GitHub GistA

Share a Rego policy with teammates or create a reproducible example by publishing it as a GitHub Gist, secret unless public: true is passed, so only people holding the link can read it. Returns { gistUrl, rawPolicyUrl, id, public }: the gistUrl renders the policy with syntax highlighting on github.com; the rawPolicyUrl can be passed directly to OPA (opa eval -d <rawPolicyUrl> <query>) or used as a data source in Conftest. When query, input, or data are supplied, a metadata.json file is bundled into the Gist so recipients have the full evaluation context to reproduce results. Each call creates a new Gist -- use the returned id to reference it later. Requires GITHUB_TOKEN in the environment (GitHub personal access token with the "gist" scope); returns GITHUB_TOKEN_MISSING with setup instructions if unset.

Inputs: policy (required), data, input, query, +2 more.

rego_policy_diffDiff two Rego policiesA

Evaluate the same query against two policies (or two versions of the same policy) and compare the results. Both evaluations run in parallel. Returns equal: true/false, the raw result from each side, and changedPaths -- the dot/bracket paths that differ. Useful for verifying that a refactor preserves behavior, or understanding exactly where two policies diverge. Each side takes either inline source (sourceA/sourceB) or a file/directory path (pathA/pathB). The same input and query are used for both evaluations; pass inputs to compare on many input documents in one call, and dataPaths to load the data the policies read.

Inputs: query (required), input, pathA, pathB, +7 more.

rego_security_auditRego security auditA

Run regal lint restricted to its bugs category plus any custom rules placed in a security category, across one or more policy directories, and return the findings grouped by severity (high/medium) with remediation guidance and a link to each rule. Use it for a periodic fleet-wide sweep rather than per-file style review. Modules that do not parse are listed in unparseable and the rest are audited. These are lint findings: a policy with none can still let a request through, so probe it with rego_eval inputs (absent, null and wrong-typed fields) for that. Requires regal.

Inputs: paths (required), configFile, ignoreFiles.

rego_suggest_fixSuggest fix for Rego diagnosticsA
Read-onlyIdempotent

Map common Rego compile errors and Regal lint findings to fix suggestions. Pass errors from rego_check or violations from rego_lint unchanged. Returns one suggestion per diagnostic; confidence is high for well-known patterns, medium where the answer is the rule's documentation (docs), low for everything else.

Inputs: diagnostics (required).

rego_testRun Rego testsA

Run Rego unit tests with opa test. Returns aggregate pass/fail/skip/error counts plus per-test records. errored counts tests OPA could not evaluate (a rule conflict, a raising built-in); such a test is neither a pass nor a failure, and a suite with any is not passing. Tests live in *_test.rego files; rule names beginning with test_ are picked up automatically. Use runPattern to filter by name regex; when no tests match, the error hint includes the pattern you supplied. Use threshold to gate on minimum coverage (returns COVERAGE_BELOW_THRESHOLD on failure). Use varValues: true with verbose: true to include local variable bindings in the trace -- essential for debugging table-driven tests written with every tc in cases { ... } to identify which case caused a failure. When tests use the test_x[case] parameterized form, OPA reports the rule as a single test whatever the number of cases; parameterizedGroups maps the rule name to a record per case and caseCounts totals them, so a failing rule says which case failed. Use ignorePatterns to exclude generated or fixture files. Use bundle: true when testing bundle-structured policy directories. Use timeout to raise the per-test limit beyond OPA's default 5s. With coverage or threshold the suite runs a second time for the coverage report, which is added to the results; it is left out when a test fails or errors, since opa reports no coverage for such a run.

Inputs: paths (required), count, bundle, explain, +9 more.

rego_test_multirootRun Rego tests across multiple rootsA

Run opa test once per root and aggregate results. Solves the package-conflict problem that occurs when opa test . is run on a repo with multiple independent package namespaces (OPA issue #4724). Two modes: explicit (supply root list with optional per-root include paths for shared libraries) and scan (auto-discover leaf test roots using the leaf rule -- a directory is a root only if it directly contains *_test.rego files and none of its eligible subdirectories do, preventing OPA's automatic recursion from double-running tests). Use sharedPaths in scan mode to add shared library directories to every root's invocation without including them in discovery. Coverage and threshold work per-root; overallCoveragePct is the mean across roots that have coverage data.

Inputs: roots, scanDir, verbose, coverage, +8 more.

rego_verifyFormally verify a Rego policy ruleA
Read-onlyIdempotent

Formally verify a property about a Rego rule using SMT solving (Microsoft Z3). Unlike testing, this checks ALL possible inputs and either proves the property holds or returns a concrete counterexample input that falsifies it. Supports equality, comparison, startswith, endswith, contains, and simple regex.match patterns (prefix: ^lit.*, suffix: .lit$, exact: ^lit$, contains: .lit., wildcard: .). Complex regex patterns (character classes, quantifiers, alternation) return INCONCLUSIVE. Also reports INCONCLUSIVE for negation-as-failure (not), comprehensions, partial set and object rules (deny contains msg), functions, else chains, and any operand it cannot encode. A body that reads an absent field is undefined rather than true, so always_true holds only if the rule is also true for an empty input: a rule requiring input.x will be answered with the counterexample {}.

Inputs: kind (required), rule (required), source (required), v0Compatible.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 52 tools

    Disambiguation2/5

    Several clusters have blurred boundaries: rego_eval, rego_eval_with_profile, rego_eval_with_explain, rego_eval_with_coverage, rego_explain_decision, rego_explain_undefined, opa_exec, and opa_query_decision all answer overlapping evaluation/debug questions. opa_status and opa_config explicitly return the same underlying document, and rego_test/rego_test_multiroot/conftest_verify plus rego_format/rego_format_write are easy to misselect. Descriptions are detailed, but with 52 tools the risk of choosing the wrong variant remains high.

    Naming Consistency4/5

    Names are consistently snake_case and use clear domain prefixes (rego_, opa_, conftest_), with bundle operations grouped under opa_bundle_. The pattern is not a uniform verb_noun scheme—some names are action+object and some use long variant suffixes—but it is predictable overall.

    Tool Count2/5

    At 52 tools, the set is far beyond a well-scoped 3-15 tool surface and creates substantial context and selection burden. Many specialized variants, such as the eval-with-* tools and status/config duplication, could be consolidated into fewer tools with flags.

    Completeness5/5

    The surface covers the Rego policy lifecycle unusually thoroughly: authoring, evaluation, tracing, testing, coverage, linting, formatting, schema inference/checking, migration, verification, bundles, policy/data CRUD, and Conftest workflows. There are essentially no dead ends for the stated domain.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      A Model Context Protocol (MCP) server that provides safe, read-only access to Kubernetes resources for debugging and inspection. Built with security in mind, it offers comprehensive cluster visibility without modification capabilities.
      45
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      An MCP server that enforces authorization by evaluating Rego policies or querying an OpenID AuthZEN-compliant PDP, providing accurate access decisions for AI agents. It includes tools for policy evaluation, AuthZEN evaluation, batch evaluation, and PDP discovery.
      1
      MIT