feat: initial MCP server scaffold (authzen_evaluate)#1
Conversation
Provides the `authzen_evaluate` tool: POST a subject/resource/action/context bundle to the configured PDP and return the AuthZEN decision JSON. - PDP endpoint configurable via AUTHZEN_PDP_URL env or per-call pdp_url arg - Unit tests cover allow/deny, missing PDP, malformed JSON, 5xx upstream - goreleaser config with SBOM (syft) and keyless cosign signing - CI workflow (vet, test -race, golangci-lint) + release workflow
|
Warning Review limit reached
More reviews will be available in 28 minutes and 27 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
📝 WalkthroughWalkthroughA new MCP server ( ChangesMCP AuthZEN Server Implementation
Sequence DiagramsequenceDiagram
participant Client as MCP Client
participant Server as mcp-authzen<br/>(MCP Server)
participant PDP as AuthZEN PDP<br/>(HTTP endpoint)
Client->>Server: CallToolRequest<br/>(authzen_evaluate)
Server->>Server: parseJSONArg<br/>(subject, resource, action)
Server->>Server: resolve PDP URL<br/>(pdp_url or env)
Server->>PDP: POST authzenRequest<br/>(subject, resource, action)
PDP-->>Server: JSON response<br/>(decision, context)
Server->>Server: unmarshal & format<br/>response
Server-->>Client: CallToolResult<br/>(decision + context)
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces mcp-authzen, a Model Context Protocol (MCP) server that acts as a gateway to an OpenID AuthZEN 1.0 compliant Policy Decision Point (PDP). It exposes the authzen_evaluate tool to allow LLM agents to query authorization decisions. The feedback identifies several key improvements: downgrading the unreleased Go version (1.26.3) in go.mod to a stable release, adding Windows compilation support in GoReleaser, securing PDP requests with an authorization token, preventing potential memory exhaustion by limiting the response reader size, and optimizing JSON validation using json.Valid.
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (3)
main_test.go (1)
42-49: ⚡ Quick winAssert outbound HTTP contract in
fakePDP.Add method/content-type checks so tests fail if evaluate() stops sending proper AuthZEN POST JSON requests.
Suggested test hardening diff
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "expected POST", http.StatusMethodNotAllowed) + return + } + if ct := r.Header.Get("Content-Type"); !strings.Contains(ct, "application/json") { + http.Error(w, "expected application/json", http.StatusUnsupportedMediaType) + return + } body, _ := io.ReadAll(r.Body) if capture != nil { _ = json.Unmarshal(body, capture) } w.Header().Set("Content-Type", "application/json")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@main_test.go` around lines 42 - 49, The fakePDP test server should assert the outbound HTTP contract: inside the httptest handler created by fakePDP (the anonymous handler passed to httptest.NewServer), validate r.Method == "POST" and that r.Header.Get("Content-Type") contains "application/json", returning a 400/appropriate error if not, then read and json.Unmarshal the body into capture as before; this ensures calls from evaluate() must be POST with JSON content-type and will cause the test to fail if evaluate() stops sending proper AuthZEN POST JSON requests..github/workflows/release.yml (1)
35-35: ⚡ Quick winPin GoReleaser version instead of
latest.Line 35 uses
version: latest; pinning a specific version makes releases reproducible and avoids surprise breakage.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/release.yml at line 35, The workflow step currently sets the GoReleaser action with the key "version: latest"; update that to pin a specific released tag (or full SHA) of the GoReleaser action instead of "latest" so releases are reproducible—locate the workflow step containing the "version" key and change the value from "latest" to a concrete tag like a vX.Y.Z release (or immutable commit SHA)..github/workflows/ci.yml (1)
31-31: ⚡ Quick winAvoid floating lint tool versions in CI.
Line 31 uses
version: latest, which can break pipelines unpredictably; pin a specific golangci-lint version for reproducible CI.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/ci.yml at line 31, The CI workflow currently pins the linter to "version: latest" which causes non-reproducible runs; replace the "version: latest" value used for the golangci-lint step with a specific released tag (e.g., a stable semver like v1.59.0) so the workflow always uses a known tool version, and update any related references to the same tag (the "version: latest" literal in the golangci-lint job/step) to ensure reproducible CI.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 15-16: CI workflow uses floating action tags and leaves checkout
credentials enabled; replace the three action references actions/checkout@v6,
actions/setup-go@v6, and golangci/golangci-lint-action@v6 with their
corresponding full commit SHAs to pin to immutable versions, and add
persist-credentials: false to each actions/checkout step (the occurrences of
actions/checkout in the file) so the runner does not expose repo credentials to
subsequent steps.
In @.github/workflows/release.yml:
- Around line 17-19: The checkout step using actions/checkout@v6 currently sets
fetch-depth: 0 but leaves credentials persisted; update the checkout step (the
actions/checkout@v6 block) to include persist-credentials: false under the with:
section so the runner does not retain a writable token in local git config (keep
the existing fetch-depth: 0 and add persist-credentials: false alongside it).
- Line 17: Replace the tag-based action references with immutable commit SHAs
for each `uses:` entry (specifically the occurrences of actions/checkout@v6,
actions/setup-go@v6, anchore/sbom-action/download-syft@v0,
sigstore/cosign-installer@v3, and goreleaser/goreleaser-action@v7): look up the
corresponding GitHub repository for each action, find the commit SHA that
matches the tag currently used, and replace the tag (e.g., actions/checkout@v6)
with the full 40-character SHA form (e.g., actions/checkout@<full-sha>) in the
release workflow; ensure you use the exact repo/name@<sha> syntax and verify the
workflow runs successfully after updating.
In @.goreleaser.yml:
- Line 7: Replace the mutating before.hooks command "go mod tidy" with a
non-mutating check such as "go mod verify" in the .goreleaser.yml before.hooks
entry: locate the before.hooks key that currently runs "go mod tidy" and change
it to run "go mod verify" (and keep any "go mod tidy" usage confined to
CI/development enforcement scripts instead of the release hook).
In `@main.go`:
- Around line 114-121: Validate the pdp_url value before using it: parse pdpURL
with net/url.Parse, ensure url.Scheme is exactly "http" or "https", and ensure
url.Hostname() is non-empty (reject empty host or malformed URLs), and return
the existing mcp.NewToolResultError when validation fails. Apply the same checks
to both uses of the pdpURL variable (the initial block that reads
pdp_url/AUTHZEN_PDP_URL and the other occurrence around line 147) so no outbound
requests are made to invalid or non-http(s) targets.
- Around line 179-191: parseJSONArg currently accepts any JSON value; change it
to enforce that the parsed JSON is an object by unmarshaling into a generic
value (v) as it does now and then verifying v is a map[string]any (i.e. JSON
object); if not, return an error like "arg %q must be a JSON object". Apply this
validation in parseJSONArg for the documented AuthZEN args (subject, resource,
action, and optional context) so arrays/scalars are rejected early, and still
return json.RawMessage(s) on success (or nil for missing optional args).
- Around line 160-163: The code uses io.ReadAll(res.Body) in the PDP response
handling (variable raw) which is unbounded and can exhaust memory; replace the
direct io.ReadAll call with a bounded read using an io.LimitReader (or io.CopyN)
capped to a safe maximum (e.g. const maxPdpResponseBytes) before reading, and
handle the case where the body exceeds the limit by returning a proper error via
mcp.NewToolResultError; update the read error path to include both read errors
and the "response too large" condition so mcp.NewToolResultError is used
consistently.
---
Nitpick comments:
In @.github/workflows/ci.yml:
- Line 31: The CI workflow currently pins the linter to "version: latest" which
causes non-reproducible runs; replace the "version: latest" value used for the
golangci-lint step with a specific released tag (e.g., a stable semver like
v1.59.0) so the workflow always uses a known tool version, and update any
related references to the same tag (the "version: latest" literal in the
golangci-lint job/step) to ensure reproducible CI.
In @.github/workflows/release.yml:
- Line 35: The workflow step currently sets the GoReleaser action with the key
"version: latest"; update that to pin a specific released tag (or full SHA) of
the GoReleaser action instead of "latest" so releases are reproducible—locate
the workflow step containing the "version" key and change the value from
"latest" to a concrete tag like a vX.Y.Z release (or immutable commit SHA).
In `@main_test.go`:
- Around line 42-49: The fakePDP test server should assert the outbound HTTP
contract: inside the httptest handler created by fakePDP (the anonymous handler
passed to httptest.NewServer), validate r.Method == "POST" and that
r.Header.Get("Content-Type") contains "application/json", returning a
400/appropriate error if not, then read and json.Unmarshal the body into capture
as before; this ensures calls from evaluate() must be POST with JSON
content-type and will cause the test to fail if evaluate() stops sending proper
AuthZEN POST JSON requests.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4b9cb31f-e938-4318-8ac0-c404550214fa
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (6)
.github/workflows/ci.yml.github/workflows/release.yml.goreleaser.ymlgo.modmain.gomain_test.go
- scripts/smoke.sh spins a Python fake PDP, runs mcp-authzen pointed at it, drives an MCP initialize -> tools/call(authzen_evaluate) sequence, and asserts the PDP's decision was forwarded back. - Makefile targets: build, test, vet, lint, smoke, fmt, tidy, clean - .github/dependabot.yml for gomod + github-actions (weekly, grouped) - README rewritten: shorter; cites AuthZEN spec sections 6 / 5.5 it conforms to; documents tested-against-opa-authzen-plugin path. - main.go: silence errcheck on response Body.Close()
….24, can't read go1.26 modules
- main.go: optional AUTHZEN_PDP_TOKEN env. Bare value auto-prefixed with
"Bearer "; "Bearer " or "Basic " prefixes pass through verbatim
- main.go: io.LimitReader(1 MiB) on PDP response body to bound memory
- main.go: validate JSON args with json.Valid instead of a throwaway
Unmarshal into any (idiomatic, fewer allocations)
- .goreleaser.yml: add windows/{amd64,arm64} build target; zip on windows
- workflows: pin actions to full commit SHAs and set
persist-credentials: false on checkout (zizmor / CodeRabbit hardening)
- README: document AUTHZEN_PDP_TOKEN
- tests: cover bearer-token auto-prefixing and verbatim-prefix passthrough
(Gemini suggested downgrading go.mod to 1.23 because "Go 1.26 is not yet
released" — that was based on stale training data; Go 1.26.3 is the
current toolchain. Test step on Linux CI uses it without issue.)
- main.go: validate pdp_url before issuing the request — require absolute http(s) URL with a host. Closes the SSRF / arbitrary-target footgun when a model supplies pdp_url at call time. - main.go: enforce JSON object for subject/resource/action/context args. Arrays and scalars previously slipped through and only failed at the PDP; AuthZEN §5 entities are objects, so reject earlier. - .goreleaser.yml: switch `before.hooks` from `go mod tidy` to `go mod verify`. tidy can mutate go.mod / go.sum during release, breaking reproducibility. verify is read-only. - tests: cover non-http scheme, relative URL, and array-as-subject rejection paths.
- smoke: build and run `make smoke` (in-process Python fake PDP, full MCP handshake, decision propagation assertion). - vuln: govulncheck (Go reachable-CVE scan) + osv-scanner (OSV.dev cross- ecosystem scan). - actionlint: lint the workflow files themselves. - test: emit coverage with -covermode=atomic and print summary. All actions pinned to SHAs with version comments for Dependabot tracking.
…ner-action/action.yml, not repo root)
Summary
authzen_evaluateMCP tool that POSTs to an OpenID AuthZEN 1.0 PDP and returns the decisionAUTHZEN_PDP_URLenv or per-callpdp_urlarggo vet,go test -race,golangci-lint; release workflow triggers onv*tagsTest plan
go vet ./...passes locallygo test -race ./...passes (5/5)v0.1.0after merge; verify cosign-signed checksums verify with the documentedcosign verify-blobcommandSummary by CodeRabbit
New Features
Chores
Tests