agentgateway + AxonFlow Integration
Prerequisites: AxonFlow Enterprise ≥ 9.7.0 (Getting Started), agentgateway v1.3.1+, the axonflow-gateway-adapters binary (built from the AxonFlow Enterprise source tree — see below).
- Interception point: agentgateway's three native external-policy seams —
mcpGuardrails/ExtMcp (MCP method layer),extAuthz(Envoy ext_authz, HTTP headers), andextProc(Envoy ext_proc, HTTP bodies) — each calling the AxonFlowaxonflow-gateway-adaptersgRPC service, which translates to Decision Mode (/api/v1/decide+ the engine's check-input/check-output endpoints). - Governs (can block): MCP
tools/call(and other MCP methods) on both request and response; LLM/HTTP requests and responses via ext_proc; HTTP requests via ext_authz. Prompt and tool content is policy-checked and can be denied or engine-redacted. - Fail-closed on both sides: agentgateway's seams default to
failClosed/deny, and the adapters block on an unreachable PDP (request plane configurable; the response plane is unconditionally fail-closed).
Why pair agentgateway with AxonFlow
agentgateway (a Linux Foundation project) is a high-performance data plane for agentic traffic: MCP multiplexing, A2A, LLM provider routing, JWT validation, rate limiting — and its own inline guardrails. It is a Policy Enforcement Point.
What a data plane structurally does not carry is the regulated-market decision layer, and that is the layer AxonFlow provides as the Policy Decision Point its callouts consult:
- Regulated-market PII decisioning — Indonesia NIK (checksum-validated, not regex-guessed), Singapore NRIC, Aadhaar/PAN, and per-category, per-tenant detection posture — decided centrally, not per-gateway-config.
- Durable, tamper-evident audit lineage — every gateway callout produces a canonical decision record with
decision_id,trace_id/correlation_id, and plane attribution, chained across the request and response planes and exportable as compliance evidence. A gateway log line disappears with the pod; the decision trail does not. - Compliance frameworks as policy — OJK / UU PDP (Indonesia), RBI & SEBI (India), MAS (Singapore), EU AI Act lineage — with cross-border transfer bases recorded on the decision.
- Human-in-the-loop verdicts —
needs_approvalis a first-class PDP verdict, not a config branch. - One policy plane for every enforcement point — the same policies govern your SDKs, plugins (Claude Code, Cursor, Codex, Claude Desktop), and gateways; changing a policy changes them everywhere, with one audit trail.
The division of labor: agentgateway owns the data path; AxonFlow owns the decision and the evidence. AxonFlow never sits inline as a proxy — the gateway consults it at each governance touchpoint below and keeps forwarding traffic itself.
Architecture
┌────────────────────────── data plane ──────────────────────────┐
MCP / LLM │ agentgateway │ MCP servers /
clients ──▶│ mcpGuardrails (ExtMcp) extAuthz extProc │──▶ LLM providers /
│ │ gRPC │ gRPC │ gRPC │ HTTP upstreams
└────────┼───────────────────┼───────────────┼──────────────────┘
▼ ▼ ▼
┌─────────────────────────────────────────────────┐
│ axonflow-gateway-adapters (gRPC) │ ← thin protocol translator,
│ ExtMcp · ext_authz · ext_proc (one binary) │ zero policy/redaction logic
└────────────────────────┬────────────────────────┘
▼ HTTPS
┌─────────────────────────────────────────────────┐
│ AxonFlow PDP — /api/v1/decide, │ ← verdicts, engine redaction,
│ /api/v1/mcp/check-input, …/check-output │ canonical audit lineage
└─────────────────────────────────────────────────┘
The adapters are deliberately thin: they translate wire formats and never make a policy or redaction decision themselves. A redacted payload is always the engine's bytes (the obligation two-touch flow); an unfulfillable obligation blocks rather than degrades.
The three seams
| agentgateway seam | Layer | Blocks | Mutates content | AxonFlow contract |
|---|---|---|---|---|
mcpGuardrails (ExtMcp gRPC) | MCP methods (tools/call, …) | ✅ | ✅ params (request) + result (response) | decide (stage tool) + engine check-input / check-output |
extAuthz (Envoy ext_authz) | HTTP request (headers + optional bounded body) | ✅ | headers only | decide |
extProc (Envoy ext_proc) | HTTP request + response bodies | ✅ | ✅ both bodies | decide + engine check-input / check-output |
Choosing a seam: govern MCP traffic with ExtMcp; govern LLM/HTTP legs with ext_proc when content may need redaction; use ext_authz for pure allow/deny gating. Because ext_authz is headers-only, a verdict that carries a redaction obligation is denied there (with a pointer at ext_proc) — the adapter will not forward content it cannot redact through the engine.
Run the adapters
The axonflow-gateway-adapters binary serves all three seams on one gRPC listener (plus standard gRPC health). It is an Enterprise component: build it from the Enterprise source tree (available to Enterprise customers with source access):
# from the axonflow-enterprise checkout
cd ee && go build ./platform/agent/gateway_adapters/cmd/axonflow-gateway-adapters
AXONFLOW_ENDPOINT=https://your-axonflow-agent:8080 \
AXONFLOW_ORG_ID=your-org \
AXONFLOW_LICENSE_KEY=your-license-key \
AXONFLOW_TENANT_ID=your-tenant \
AXONFLOW_GATEWAY_ID=agentgateway-prod \
GATEWAY_ADAPTERS_LISTEN=:9090 \
axonflow-gateway-adapters
| Variable | Default | Purpose |
|---|---|---|
AXONFLOW_ENDPOINT | — (required) | PDP base URL |
AXONFLOW_ORG_ID / AXONFLOW_LICENSE_KEY | — | HTTP Basic PDP credentials (together) |
AXONFLOW_TENANT_ID | — | tenant scope on every decision |
AXONFLOW_GATEWAY_ID | agentgateway | caller_identity.gateway_id on the audit row |
AXONFLOW_DEFAULT_STAGE | llm | decide stage for the HTTP seams (llm|tool|agent); ExtMcp always uses tool |
AXONFLOW_FAIL_MODE | closed | request-plane posture on PDP-unreachable (closed|open) |
AXONFLOW_REQUEST_TIMEOUT | 10s | engine-call bound (circuit posture) |
AXONFLOW_CONNECTOR_TAG | agentgateway | connector_type origin tag on fulfillment audit rows |
AXONFLOW_MAX_BODY_BYTES | 8388608 (8 MB) | scannable-payload bound; larger payloads fail closed |
AXONFLOW_BREAKER_THRESHOLD / AXONFLOW_BREAKER_COOLDOWN | 5 / 30s | consecutive PDP transport failures that open the circuit, and how long it stays open |
AXONFLOW_TRUST_IDENTITY_HEADERS | false | forward client X-User-Email/X-Session-Id on the response-governance call (a reserved channel the engine currently ignores — attribution flows via user_token); enable ONLY when a hop upstream of agentgateway re-sets them from a validated source (see the trust boundary below) |
GATEWAY_ADAPTERS_LISTEN | :9090 | gRPC listen address |
Configure agentgateway
MCP governance (ExtMcp)
binds:
- port: 3000
listeners:
- routes:
- backends:
- mcp:
targets:
- name: your-mcp-server
mcp: { host: http://mcp-backend:3001/mcp }
policies:
mcpGuardrails:
processors:
- kind: remote
methods:
"tools/call": full # request + response phases
host: axonflow-adapters:9090
failureMode: failClosed # the default — keep it
A denied tools/call surfaces to the MCP client as JSON-RPC error -32001 whose data payload carries the AxonFlow decision_id, verdict, and reasons — quotable evidence, straight from the wire.
LLM leg (ext_proc)
- port: 3001
listeners:
- routes:
- backends:
- ai:
name: openai
provider:
openAI: { model: gpt-4o-mini }
policies:
backendAuth:
key: $OPENAI_API_KEY # gateway-held provider credential
extProc:
host: axonflow-adapters:9090
failureMode: failClosed
processingOptions:
requestHeaderMode: send
responseHeaderMode: send
requestBodyMode: buffered # REQUIRED — the adapter fails closed on streamed bodies
responseBodyMode: buffered
backendAuth matters twice: agentgateway's openAI provider does not inject a key by itself, and holding the provider credential at the gateway frees the client's own Authorization header to carry the end-user JWT the adapter forwards for audit attribution.
The adapter buffers each body, decides on the extracted prompt (OpenAI-shaped bodies get model + last user message; anything else is gated whole), fulfills redaction obligations through the engine against the full body, and governs the response body through the engine's response-governance endpoint before the client sees it. To override the decide stage per route, use the gateway-config metadataContext (clients cannot influence it):
extProc:
# …
metadataContext:
axonflow: { stage: '"tool"' } # CEL string literal
ext_authz routes use the axonflow-stage context extension shown below instead. Stage overrides come only from these gateway-controlled channels — the adapter deliberately ignores any client-sent stage header, so the governed party can never pick its own policy layer.
HTTP allow/deny (ext_authz)
- port: 3002
listeners:
- routes:
- backends:
- host: your-upstream:8080
policies:
extAuthz:
host: axonflow-adapters:9090
failureMode: deny # the default — keep it
includeRequestBody: { maxRequestBytes: 65536 }
protocol:
grpc:
context:
axonflow-stage: llm # optional per-route stage override
Denials return a direct 403 JSON response with decision_id, trace_id, verdict, and reasons; allows stamp x-axonflow-decision-id / x-axonflow-trace-id onto the upstream request and expose the decision to later CEL filters as extauthz.* dynamic metadata.
End-user identity and audit attribution
agentgateway forwards the inbound request headers with each callout. The adapters propagate:
Authorization: Bearer <jwt>→ the decision'suser_token— this is the attribution channel: the PDP resolves and attributes it (an invalid token is denied), on both the decide and response-governance calls;traceparent→ trace stitching across gateway layers (trace correlation).
So the audit row names the actual end user behind the gateway, not just the gateway service account — the attribution regulators ask for.
Trust boundary details:
X-User-Email/X-Session-Idare a reserved channel, ignored by default. The engine's response-governance endpoint currently derives audit identity from the validateduser_tokenand ignores these headers. The adapter still refuses to forward them unlessAXONFLOW_TRUST_IDENTITY_HEADERS=true, because they are client-assertable and agentgateway applies route header modifiers after the ext_proc callout — no gateway config could strip a forged value if the engine starts honoring them. Opt in only when a hop upstream of agentgateway strips the inbound headers and re-sets them from a validated source (e.g. a jwtAuth claim).- ExtMcp response-plane rows attribute to the gateway. agentgateway's ExtMcp response callout structurally carries no headers, so MCP response-plane governance rows attribute to the gateway credentials; the request-plane row for the same call still names the end user. The reference config additionally suppresses the identity headers at the ExtMcp seam (
requestHeaders: {disallowed: [x-user-email, x-session-id]}) as defense-in-depth.
Failure posture
| Failure | Request plane | Response plane |
|---|---|---|
| PDP unreachable / 5xx / circuit open | block (default) or forward with AXONFLOW_FAIL_MODE=open | always block |
| PDP rejects the call (4xx) | always block | always block |
| Redaction obligation unfulfillable (engine error, redactor didn't run, unforwardable output) | always block | always block |
| Payload exceeds the scannable bound (8 MB default) | always block | always block |
Both layers default fail-closed independently — agentgateway's failClosed/deny covers a dead adapter; the adapter's posture covers a dead PDP. Response-plane governance additionally requires the engine's redaction_evaluated attestation (platform ≥ 9.7.0), so "the redactor wasn't running" is indistinguishable from a block, never from an allow.
Coexistence with agentgateway's native guardrails
agentgateway ships inline promptGuard regex/webhook guardrails; keep using them for cheap structural checks if you like. Route regulated decisions — PII with legal definitions (NIK, NRIC, Aadhaar), approval workflows, anything you must later evidence to OJK/RBI/SEBI/EU-AI-Act auditors — through the AxonFlow seams, where every decision lands in the compliance-grade trail. The two compose: the seams run as ordered processors alongside native policies.
Verification
The integration ships with a runtime E2E harness (runtime-e2e/2886_agentgateway_pep_adapters/ in the platform repo) that drives a real agentgateway binary, a real MCP server, and a real LLM provider through all three seams against a live PDP — allow, deny, engine-redaction, and fail-closed on every seam. Its config template is the canonical reference config.
See also
- Decision Mode architecture — the PDP contract these adapters speak
- Detection posture — per-org, per-category PII actions the decisions honor
- Governance Architecture & Coverage — how gateway seams fit the runtime modes
- Community vs Enterprise — the adapters are an Enterprise feature
