MCP server giving LLM agents real authorization answers: evaluate Rego in-process via OPA, or ask an OpenID AuthZEN 1.0 PDP
- ✓Open-source license (MIT)
- ✓Actively maintained (<30d)
- ✓Clear description
- ✓Topics declared
- ✓Documented (README)
git clone https://github.com/kanywst/mcp-opa-authz{
"mcpServers": {
"mcp-opa-authz": {
"command": "mcp-opa-authz",
"env": {
"AUTHZEN_PDP_URL": "<authzen_pdp_url>"
}
}
}
}AUTHZEN_PDP_URLMCP Servers overview
# mcp-opa-authz
[](https://github.com/kanywst/mcp-opa-authz/actions/workflows/ci.yml)
[](https://pkg.go.dev/github.com/kanywst/mcp-opa-authz)
[](https://goreportcard.com/report/github.com/kanywst/mcp-opa-authz)
[](./LICENSE)
**Stop letting your agent guess at authorization.** This is an [MCP](https://modelcontextprotocol.io) server that answers "is this allowed?" from real policy code — either Rego you hand it, or the [OpenID AuthZEN 1.0](https://openid.net/specs/authorization-api-1_0.html) PDP that actually governs your system.
Ask a model whether Alice may delete that document and it will produce a confident, plausible, unfalsifiable answer. Give it these tools and the answer comes from the policy.
```bash
brew install kanywst/tap/mcp-opa-authz
# or, without Homebrew:
go install github.com/kanywst/mcp-opa-authz@latest
claude mcp add opa-authz -- mcp-opa-authz
```
That is enough for `evaluate_policy`. Point it at a PDP to get the rest:
```bash
claude mcp add opa-authz \
--env AUTHZEN_PDP_URL=http://localhost:8181/access/v1/evaluation \
-- mcp-opa-authz
```
## Two layers, same question
| Tool | Answers | Needs |
| --- | --- | --- |
| `evaluate_policy` | "What does this Rego say?" Evaluated in-process by [OPA](https://www.openpolicyagent.org/). | Nothing external |
| `authzen_evaluate` | "What does the PDP that governs this system say?" | A reachable PDP |
| `authzen_evaluate_batch` | The same, over a list — which of these may the subject touch? | A reachable PDP |
| `authzen_search` | "Who may…", "which resources may…", "what may…" — the PDP lists what is permitted. | A PDP with the Search APIs |
| `authzen_discover` | "Which endpoints does this PDP offer?" | A reachable PDP |
Use `evaluate_policy` while authoring or debugging a policy you have the source of. Use `authzen_evaluate` when the decision has to come from production, not from a policy pasted into the chat. The server's MCP instructions tell the model the same thing, so it usually picks correctly on its own.
## What makes this different from `opa eval` in a shell tool
An agent given shell access can already run `opa eval`. What it cannot do is get an answer it is allowed to trust:
- **Undefined is not false.** A Rego query with no matching rule and no default returns `[]`. Every model reads that as a deny. `evaluate_policy` returns `defined` alongside `value`, so "the policy denied" and "the policy has no opinion" stop being the same answer.
- **A PDP that did not answer is not a deny.** AuthZEN makes `decision` a required member. A response missing it decodes into a Go `bool` as `false` — a broken PDP would look like a strict one. This server treats a missing `decision` as a failure, never as a deny. Same for a 401: that means *this server* failed to authenticate, not that the subject was denied, and the error says so.
- **The policy runs in a sandbox.** Rego handed to an MCP server was written by a model, from text that may have come from a web page. OPA's `http.send` would let that policy make arbitrary HTTP requests from your laptop, and `opa.runtime()` would hand it your environment. Both are compiled out. See [Security](#security).
- **Batch decisions carry their index.** `permit_on_first_permit` legitimately returns fewer decisions than you sent. Zipping the arrays would attach a decision to the wrong resource.
- **An empty search is not a failed search.** `results: []` means nothing is permitted; a response with no `results` member, or a result of the wrong entity type, is an error.
## Demo
Debugging a policy that is denying when it should not:
```text
> Why is bob getting denied on doc-1? Here's the policy and the input.
evaluate_policy(rego=…, query="data.rbac.allow", input_json=…, trace=true)
{
"defined": true,
"value": false,
"printed": ["checking roles for", "bob"],
"trace": [
"Enter data.rbac.allow",
"| Eval some role in roles[input.user]",
"| Fail roles[\"bob\"]",
…
]
}
Bob has no entry in `roles` at all — the rule never reaches the permission
check. Adding "bob": {"viewer"} fixes it.
```
Then confirming against the PDP that actually runs:
```text
> Does production agree?
authzen_evaluate(subject={"type":"user","id":"bob"}, …)
{ "decision": false, "context": { "reason": "no role binding" },
"request_id": "3f9c…", "pdp_url": "https://pdp.internal/access/v1/evaluation" }
```
## Tools
### `evaluate_policy`
| Param | Required | Description |
| --- | --- | --- |
| `rego` | yes | Rego source with a `package` declaration. |
| `query` | yes | Rego query, e.g. `data.example.allow`. |
| `input_json` | no | JSON-encoded `input` document. |
| `data_json` | no | JSON-encoded base document for the `data` namespace. |
| `rego_version` | no | `v1` (default) or `v0` for pre-OPA-1.0 syntax. |
| `trace` | no | Return a pretty-printed evaluation trace. Verbose; bounded at 4000 events, 200 lines, 1 KiB per line. |
Returns `defined`, `value`, the raw OPA `result_set` (omitted with `result_set_omitted` past 256 KiB encoded), any `print()` output (200 lines of 1 KiB), and the trace when asked for.
### `authzen_evaluate`
| Param | Required | Description |
| --- | --- | --- |
| `subject` | yes | JSON object. AuthZEN requires `type` and `id`. |
| `action` | yes | JSON object. AuthZEN requires `name`. |
| `resource` | yes | JSON object. AuthZEN requires `type` and `id`. |
| `context` | no | JSON object with runtime context (IP, time, MFA strength). |
| `pdp_url` | no | Override `AUTHZEN_PDP_URL` for this call. |
Returns `decision`, the PDP's `context` if any, the `pdp_url` that answered, and the `request_id` sent as `X-Request-ID` — so a decision in a transcript can be found in the PDP's logs.
### `authzen_evaluate_batch`
Same arguments, plus `evaluations` (a JSON array whose entries override the top-level defaults) and `evaluations_semantic` (`execute_all`, `deny_on_first_deny`, `permit_on_first_permit`). Capped at 100 entries per call. Without `pdp_url`, the endpoint is the `access_evaluations_endpoint` the PDP advertises in its metadata, or `/access/v1/evaluations` under the root of `AUTHZEN_PDP_URL` when it advertises none — the same resolution, cache and same-origin rule as `authzen_search`. `endpoint_source` in the result says which was used.
```json
{
"subject": "{\"type\":\"user\",\"id\":\"alice\"}",
"action": "{\"name\":\"read\"}",
"evaluations": "[{\"resource\":{\"type\":\"doc\",\"id\":\"1\"}},{\"resource\":{\"type\":\"doc\",\"id\":\"2\"}}]"
}
```
### `authzen_search`
The AuthZEN Search APIs. `search` picks which entity is being listed; that entity carries only its `type`.
| `search` | Asks | `subject` | `action` | `resource` |
| --- | --- | --- | --- | --- |
| `subject` | Who may do this to that resource? | `type` only | required | `type` and `id` |
| `resource` | Which resources of a type may the subject act on? | `type` and `id` | required | `type` only |
| `action` | What may the subject do to the resource? | `type` and `id` | must be omitted | `type` and `id` |
`context`, `pdp_url`, `page_limit` and `page_token` are optional. Without `pdp_url`, the endpoint is the one the PDP advertises in its metadata (`search_subject_endpoint` etc.), as the specification requires, falling back to the default path (`/access/v1/search/{subject,resource,action}`) under the root of `AUTHZEN_PDP_URL` when it has no usable metadata. The metadata is cached per PDP for five minutes, and `endpoint_source` in the result says which one was used. An advertised endpoint has to be on the PDP's own origin, because the PDP token goes with the request; a cross-origin one is refused unless passed explicitly as `pdp_url`. Returns `results`, `has_more` and `next_page_token`; pass the token back as `page_token`, with every other argument unchanged, for the next page. A response whose `page` object has no `next_token` is an error even though the specification's own example has one: without it nothing says whether the list is complete.
```json
{
"search": "resource",
"subject": "{\"type\":\"user\",\"id\":\"alice\"}",
"action": "{\"name\":\"read\"}",
"resource": "{\"type\":\"document\"}"
}
```
### `authzen_discover`
Fetches `/.well-known/authzen-configuration` from a PDP root. `pdp_url` may be a root or an evaluation endpoint — the known AuthZEN path suffix is stripped. For a PDP mounted under a prefix, the well-known string goes between the host and the prefix, as RFC 8615 and the specification place it (`https://gw.example.com/.well-known/authzen-configuration/pdp`); the older appended form (`…/pdp/.well-known/authzen-configuration`) is tried if that 404s. A document whose `policy_decision_point` is not the root it was fetched from is rejected, as the specification requires.
## Standards conformance
Implements [Authorization API 1.0](https://openid.net/specs/authorization-api-1_0.html), approved as an OpenID **Final Specification** in January 2026:
| Section | Status |
| --- | --- |
| Access Evaluation (`POST /access/v1/evaluation`) | `authzen_evaluate` |
| Access Evaluations, batch (`POST /access/v1/evaluations`) | `authzen_evaluate_batch` |
| PDP Metadata (`GET /.well-known/authzen-configuration`) | `authzen_discover` |
| Subject / Action / Resource information model | Required members validated before the request is sent |
| `X-Request-ID` correlation | Sent on every call, returned in the result |
| Search APIs (subject / resource / action, with pagination) | `authzen_search` |
Related work worth knowing about: the AuthZEN working group's [COAZ profile](https://github.com/openid/authzen/blob/main/profiles/authzen-coaz-mcp-binding-1_0.md) binds AuthZEN to MCP What people ask about mcp-opa-authz
What is kanywst/mcp-opa-authz?
+
kanywst/mcp-opa-authz is mcp servers for the Claude AI ecosystem. MCP server giving LLM agents real authorization answers: evaluate Rego in-process via OPA, or ask an OpenID AuthZEN 1.0 PDP It has 0 GitHub stars and its last recorded update is dated 2026-10-03.
How do I install mcp-opa-authz?
+
You can install mcp-opa-authz by cloning the repository (https://github.com/kanywst/mcp-opa-authz) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.
Is kanywst/mcp-opa-authz safe to use?
+
Our security agent has analyzed kanywst/mcp-opa-authz and assigned a Trust Score of 95/100 (tier: Verified). See the full breakdown of passed checks and flags on this page.
Who maintains kanywst/mcp-opa-authz?
+
kanywst/mcp-opa-authz is maintained by kanywst. The last recorded GitHub activity is dated 2026-10-03, with 0 open issues.
Are there alternatives to mcp-opa-authz?
+
Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.
Deploy mcp-opa-authz to your cloud
Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.
Maintain this repo? Add a badge to your README
Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.
[](https://claudewave.com/repo/kanywst-mcp-opa-authz)<a href="https://claudewave.com/repo/kanywst-mcp-opa-authz"><img src="https://claudewave.com/api/badge/kanywst-mcp-opa-authz" alt="Featured on ClaudeWave: kanywst/mcp-opa-authz" width="320" height="64" /></a>More MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ and follow here for daily tips and tricks: https://x.com/Scrapling_dev
The fastest path to AI-powered full stack observability, even for lean teams.