MCP Authorization Gateway
The MCP Authorization Gateway is EnforceAuth's pattern for governing Model Context Protocol (MCP) tool calls — identity-aware, policy-checked, auditable, and optionally escalated to a human before high-impact actions run.
It sits between AI agents and enterprise MCP servers so connected tools do not inherit the user's full access — the autonomous overreach problem.
EnforceAuth's baseline starting point for this gateway is PortcullisMCP from PAC.Labs, our strategic partner. The sections below describe the EnforceAuth workflow; component names (Gate, Keep, Guard) and operational detail follow the Portcullis reference build unless you substitute your own PEP.
Why it matters
| Pain point | Gateway response |
|---|---|
| All-or-nothing tool access | Resource-level Policy as Code (which tool, which args, which paths) |
| High-stakes actions without review | escalate decisions + human-in-the-loop approval |
| Opaque agent audit trail | Central decision logging with trace IDs |
| Credential sprawl | Identity injection; secrets via Vault / env — not in agent prompts |
The gateway is not a PDP and not a control plane. It is a PEP for MCP that delegates decisions to OPA (or another PDP). EnforceAuth manages the Rego lifecycle — Git, test, bundle, deploy — that OPA loads behind the gateway.
AI Agent ──MCP──► Gate ──► Keep ──► OPA (PDP)
│ │ │
│ │ ▼ allow / deny / escalate
│ │ EnforceAuth bundle poll
└──► Guard (approve escalated calls)
│
▼
MCP backends (APIs, DB, fetch, …)
Architecture (Portcullis baseline)
Three components in the reference implementation — see Portcullis ARCHITECTURE.md for full flows:
| Component | Role |
|---|---|
| Gate | Edge MCP interface — single entry for agents (stdio on desktop, or streamable HTTP in multi-tenant mode) |
| Keep | Central gateway proxy — validates identity, builds PDP input, enforces allow/deny/escalate, forwards to MCP backends |
| Guard | Human-in-the-loop — approval UI for escalate decisions; issues signed escalation JWTs bound to exact tool arguments |
OPA's job: Evaluate each intercepted MCP request. Return allow, deny, or escalate. Emit decision logs for every evaluation.
EnforceAuth's job: Author, test, and publish the bundle OPA polls — same Rego repo as microservice sidecars. Ingest decision logs and fleet status for console visibility.
The gateway layer is engine-agnostic; the Portcullis reference uses OPA + Rego, which aligns with EnforceAuth today.
When to use the MCP Authorization Gateway
| Scenario | Use this pattern? |
|---|---|
| Agents call enterprise MCP tools (GitHub, DB, internal APIs) | Yes |
| Need human approval before destructive agent actions | Yes — escalate + Guard |
| HTTP microservice authz only (no MCP) | No — use Application integration + sidecar OPA |
Inventory embedded if checks in legacy apps | No first — use Zift, then deploy via EnforceAuth |
| Manage EnforceAuth tenant from Cursor/Claude | No — use Connect EnforceAuth MCP (different product surface) |
Connect EnforceAuth MCP lets an IDE agent operate your EnforceAuth tenant. The MCP Authorization Gateway governs what agents do to your enterprise tools. They solve different problems and can coexist.
Deployment modes
| Mode | Where Gate runs | Typical use |
|---|---|---|
| Single-tenant | User desktop alongside the agent | Developer workstations, local filesystem sandbox |
| Multi-tenant | Central gateway (Kubernetes or VM fleet) | Enterprise AI consoles, shared MCP proxy |
For Kubernetes fleets of Keep, Guard, and OPA — bundle polling, API keys, and PDP Monitoring — see Kubernetes Control Center.
Quick start (Portcullis reference)
Minimal path (no OPA — noop PDP allows all traffic; learn the flow first):
git clone https://github.com/paclabsnet/PortcullisMCP.git
cd PortcullisMCP
make build && make install
make run-keep
Configure your MCP client to launch Gate — example claude_desktop_config.json:
"mcpServers": {
"portcullis": {
"command": "portcullis-gate",
"args": ["-config", "~/.portcullis/gate.yaml"]
}
}
Full demo stack (OPA + sample Rego + Guard + mock backends):
make demo-start
Try prompts that trigger escalation (e.g. update order status); approve via Guard, then retry. See Portcullis README § Quick Start.
EnforceAuth workflow
- Author policy — Rego in Git (tool names, resource paths, group claims,
escalaterules). Start from Portcullis sample policies and opa-examples.md. - Test —
opa testlocally; optional Raygun exercises bundles against known MCP inputs. - Connect EnforceAuth — policy source + bundle destination on your system entity — Quickstart Step 2–3.
- Deploy bundle — manual, webhook, or GitHub Actions; confirm Success on
/deployments. - Configure OPA behind Keep — bundle polling +
decision_logs+statuspointing at EnforceAuth — PDP integration. - API key —
decisions:writeandstatus:writefor audit and fleet health — API keys. - Operate — PDP Monitoring (
/monitoring) for OPA health; Decisions for allow/deny/escalate audit; Guard logs cross-reference escalation JWT JTIs.
EnforceAuth multi-environment promotion (dev → staging → prod) applies the same way as microservice sidecars — test policies in a lower environment before promoting the bundle Keep's OPA polls in production.
OPA config.yaml sketch (Keep's PDP)
Align with your EnforceAuth entity API key View Config, then add bundle service for your destination:
services:
enforceauth:
url: ${ENFORCEAUTH_URL}/v1
headers:
X-API-Key: ${ENFORCEAUTH_API_KEY}
enforceauth-bundle:
url: https://YOUR_BUCKET.s3.YOUR_REGION.amazonaws.com
credentials:
s3_signing:
environment_credentials: {}
bundles:
mcp-gateway:
service: enforceauth-bundle
resource: policies/bundle.tar.gz
polling:
min_delay_seconds: 60
max_delay_seconds: 120
decision_logs:
service: enforceauth
status:
service: enforceauth
labels:
instance_name: mcp-gateway-keep-prod
environment: production
Keep calls OPA over HTTP for each MCP tool evaluation; OPA does not need to sit in the same pod as Keep, but colocating or sidecar deployment in Kubernetes is common for latency.
Policy model highlights
The gateway sends structured input to OPA: user principal (from OIDC claims), tool name, arguments, escalation tokens on retry, etc. Policies can:
- Allow routine reads (e.g. fetch public headlines)
- Deny protected paths (
.git, admin APIs) - Escalate sensitive writes — Keep pauses, Guard collects human approval, retry carries a JWT bound to exact arguments
Escalation tokens are short-lived and argument-specific — approving DROP TABLE temp_logs does not authorize DROP TABLE users. See Portcullis README § Explain escalation.
Tracking in EnforceAuth
| Console | What you see for MCP gateway fleets |
|---|---|
| PDP Monitoring | OPA instances behind Keep — bundle health, plugin errors |
| Decisions | Every allow / deny / escalate evaluation from decision_logs |
| Deployments | Which bundle revision is active |
| Policies | Coverage — which rules fired |
Correlate Guard approval JTIs with decision log entries for compliance evidence.
Security and operations notes
- Fail closed — if OPA is unavailable, Keep does not forward MCP requests.
- mTLS — supported Gate ↔ Keep; production hardening in keep-config.example.yaml.
- Guard behind SSO — place Guard behind corporate SSO (Cloudflare Access, oauth2-proxy, etc.) so agents cannot self-approve — guard-sso-proxy.md.
- OpenTelemetry — embedded in Gate, Keep, and Guard; denial responses include
trace_idfor support. - PAC.Labs support — implementation and 24/7 helpdesk tiers available for enterprise rollouts.
Upstream PortcullisMCP repository
Day-to-day operations for the reference implementation:
| Topic | Location |
|---|---|
| Install, Make targets, demo stack | Portcullis README |
| Architecture & flows | ARCHITECTURE.md |
| Rego examples & testing | docs/policy/opa-examples.md |
| Vault secret URIs | docs/vault-integration.md |
| Windows quickstart | docs/quickstart-windows.md |
| Roadmap | docs/roadmap.md |
Next
- Kubernetes Control Center — OPA fleet ops + MCP gateway pattern
- AI workload guardrails
- PDP integration — bundles, decision logs, status
- Decision logs
- Zift policy discovery — extract auth from apps before MCP gateway rollout
- 15-minute quickstart