opa-mcp-server
opa-mcp-server turns any MCP-compatible client into an OPA/Rego authoring, testing, analysis, and operations environment.
Format, check, parse, inspect, lint, schema-check, dependency-analyze, and migrate Rego policies.
Evaluate policies/queries with inputs, batch inputs, traces, profiling, coverage, benchmarks, partial evaluation, and CI-style decision gates.
Generate test skeletons, run/aggregate Rego tests, find coverage gaps, suggest or auto-apply fixes, diff policy behavior, infer input schemas, and formally verify some rule properties.
Build, sign, and verify OPA bundles.
Manage a running OPA server: list/get/put/delete policies, read/write/patch/delete data, query decisions, compile queries, and check health/status/config.
Test Kubernetes/Terraform/other configs with conftest, verify conftest policies, and pull/push policy bundles.
Use higher-level helpers for decision debugging, undefined explanations, security audits, policy descriptions, and GitHub Gist sharing.
Use bundled prompts/resources for policy authoring, review, debugging, OPA built-ins, style guidance, and Rego patterns.
OPA 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_REGOin every tool that runs opa, where several returnedEVAL_ERROR.rego_explain_decisionreturns the trace as readable lines, not OPA's raw events.conftest_testreports a run that evaluated no rule aspassed: false, andrego_generate_test_skeletonwritestodo_test_stubs, whichopa testskips until they are filled in. Responses are compact JSON.
Upgrading to 0.8.0:
opa_execloadsdataPathsasopa eval --datadoes, so a directory that also holds test fixtures, or JSON and YAML that is not data, can now fail to load; pass it asbundleto load it as before. A policy that does not load isINVALID_REGOinopa_execand the conftest tools, where it wasEVAL_ERRORandUNKNOWN_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,onandoffin data files are strings now, not booleans. If you supply your own binary viaOPA_BINARYorPATH, only the Node requirement applies.
Upgrading to 0.6.0:
rego_benchreportsiterations,nsPerOp,allocsPerOpandbytesPerOp. The fields opa prints (N,T,Bytes,MemAllocs,MemBytes,Extra) were top-level and now sit underrawfor a single run, so anything that read them from the top level has to look there. Withcountabove one,rawis omitted: every document is inruns, andfastestindexes 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().envwill no longer see it; name the variable inOPA_MCP_PASSTHROUGH_ENVif 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 (
ifis required before a rule body,containsbefore a partial set). Runrego_migrate_v1to convert them, or passv0Compatible: trueto load one as it is. If you supply your own binary viaOPA_BINARYorPATH, 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 fmtandopa parseso output is byte-identical to what you'd get on the command line, andregal(optional) surfaces idiomatic suggestions.Evaluate against data. Run a query against a policy and an input document. Optional
--explain,--profile, and--coverageflags surface execution traces, hot rules, and per-line coverage.Debug a deny.
rego_explain_decisionwalks 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 --serveror 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.gzthe agent can hand to your delivery system.Lint.
rego_lintruns 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_evalgets 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, andrego_suggest_fixcompose 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 substituteC:\\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-mcpThis 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.exesyntax.
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 thehooksblock into.claude/settings.json. Runsopa checkafter Claude writes or edits a.regofile 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 --versionthen 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-mcpThe 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 / LinuxGet-Command opa, regal | Select-Object Source # WindowsThis 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 |
|
| Base URL of an OPA REST endpoint, used by |
| (unset) | Bearer token for OPA, if your instance requires auth. Treated as a secret. Never echoed in logs or tool responses. |
|
| Path to the |
|
| Path to the |
|
| Path to the |
| (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. |
|
| Path the server appends logs to. The server never writes to stdout; that channel is reserved for the MCP protocol. |
|
| One of |
|
| Hard cap on a single tool response. Larger payloads are truncated with a |
|
| Hard timeout for any spawned subprocess ( |
|
| Timeout for each request to the OPA REST API, from the connection attempt to the last byte of the response; reported as |
| (unset) | Set to |
|
| 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 |
| (unset) | Comma-separated variable names to pass through to |
| (unset) | Comma-separated variable names to withhold from |
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 |
| Format Rego source. Wraps |
| Type-check and validate Rego. Wraps |
| Run Regal across a file or directory. Returns each violation with its category, level and location. Requires |
| Parse Rego to AST JSON. Wraps |
| Inspect a bundle or directory: packages, rules, annotations. Wraps |
| List the built-ins and features the resolved |
| Static dependency analysis: rule-level data references and cross-package calls. |
| Migrate Rego v0 source to v1. Renames a rule v1 reserves the name of ( |
| Check Rego against a JSON Schema. Validates that every |
Featured: rego_format
// 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
}
}Featured: rego_check
// 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 |
| Evaluate a query against a policy and input. The bread-and-butter tool. |
| Evaluate with |
| Evaluate with |
| Evaluate with |
| Run Rego unit tests with |
| Run |
| Partially evaluate a query against a policy. |
| Batch-evaluate a decision against multiple input files. Returns per-file results with |
| Run |
Featured: rego_eval
// 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 |
| Build a |
| 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 |
| Verify a signed bundle with a public key through |
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 |
| List the policy IDs registered on the server, with a count. |
| Get a single policy by ID. Returns the Rego source; |
| Upload or replace a policy; |
| Delete a policy by ID. |
| Read a path from the data hierarchy. |
| Write to a path in the data hierarchy. |
| Apply a JSON Patch to the data hierarchy. |
| Delete a document from the data hierarchy. |
| POST to a |
| Partially evaluate a query against the running server. |
| Liveness / readiness check. A server that answers reports |
| The same |
| Server configuration from |
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 |
| Evaluate with full tracing and return the rules entered and fired, plus the trace as readable lines. The |
| Given a policy, generate a |
| Summarize a policy's package, imports and per-rule structure from its AST. For the input references a policy reads, use |
| Pass |
| Run |
| Run regal lint restricted to its |
| Statically analyse a policy (or directory of policies) with |
| Run |
| Run |
| Evaluate the same query against two policies in parallel and compare the results. Returns |
| 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 |
| Explain why a Rego query is undefined, fell back to its default, or (for a set such as |
| Publish a policy (and optional input) as a secret GitHub Gist (pass |
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 |
| Evaluate config files or an inline document against Rego policies with |
| Run the |
| Pull a policy bundle from an OCI registry or Git repo into a local directory with |
| Package a local policy directory as an OCI artifact and push to a registry with |
Featured: conftest_test with inline config
// 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 |
| Return server name, version, resolved |
Prompts
Three MCP prompts ship with the server. Clients surface them as slash commands or workflow templates.
Prompt | Purpose |
| Walks the agent through writing a new policy: ask about the decision surface, draft, review, format, lint, test. |
| Review checklist for an existing policy: completeness, edge cases, performance, security pitfalls. |
| 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 |
| Categorized OPA built-in function reference, derived at read time from |
| Condensed Rego style guide, formatted for LLM consumption. |
| 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:
stdout is the protocol channel. The server logs to a file via
lib/logger.tsand never writes to stdout. If you see stray stdout bytes, the client disconnects; the MCP transport layer is strict.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 withisError: truewhose text beginsMCP 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 atry/catchthat could not see it.Subprocesses are bounded in time, size, and environment.
lib/subprocess.tsruns the binaries withshell: false, a hard timeout withSIGTERM-then-SIGKILLescalation, 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:opabuffers 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.Children do not inherit the server's environment.
lib/child-env.tsbuilds an explicit allow-list instead. Rego can read its interpreter's environment throughopa.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 returnPATH_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
opaprocess throughopa.runtime().env, so a child that inheritedprocess.envwould handOPA_TOKEN,GITHUB_TOKEN, and every other variable to any policy it evaluated. Sincerego_evalaccepts 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_PROXYand 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 inOPA_MCP_BLOCK_ENVto withhold them anyway.OPA_MCP_PASSTHROUGH_ENVopts individual variables back in.OPA_TOKENis 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_evaland its variants,rego_test,rego_test_multiroot,rego_bench,rego_compile_query,opa_exec,rego_migrate_v1(when giveninputs), 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'shttp.sendlets a policy reach, and write to, any network address. A client that gates on the hints will ask before running one.opa_query_decisionandopa_compile_queryare 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, sohttp.sendcannot be restricted for evaluation;rego_checkandopa_bundle_buildaccept 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 ofopaandregalchecked 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 devCommon 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
@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 toolsconftest_pullConftest pullADestructiveIdempotent
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 pushADestructive
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 infoARead-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 bundleADestructiveIdempotent
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 bundleADestructiveIdempotent
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 signatureARead-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 OPAARead-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 configurationARead-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 OPAADestructive
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 policyADestructive
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 OPAARead-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 IDARead-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 checkARead-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 policiesARead-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 OPAADestructive
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 OPAADestructiveIdempotent
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 policyADestructiveIdempotent
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 decisionARead-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 statusARead-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 capabilitiesARead-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 RegoARead-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 SchemaARead-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 analysisARead-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 policyARead-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 violationsADestructive
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 RegoARead-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 placeADestructiveIdempotent
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 skeletonARead-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 schemaARead-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 policyARead-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 ASTARead-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_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 diagnosticsARead-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 ruleARead-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
Scored across 52 tools
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.
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.
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.
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
Related MCP Connectors
MCP server for mandates, delegation, policy-gated execution, credential grants, and audit.
MCP server for progressive tool usage at any scale (see https://klavis.ai)
Authenticated MCP server for ClearPolicy policy and compliance workflows.
MCP Server for Slima - AI Writing IDE for Novel Authors with AI Beta Reader.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceAn MCP Server that enables natural language interaction with the Open Policy Agent REST API, allowing users to manage policies, decisions, and data through conversational interfaces.1-
- AlicenseNot gradedqualityDmaintenanceA 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.45MIT
- AlicenseNot gradedqualityCmaintenanceMCP server to lint and validate Kubernetes-related manifests(Helm, FluxCD, ArgoCD, Kustomize, etc.)46 PyPIMIT
- AlicenseNot gradedqualityBmaintenanceAn 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.1MIT