Skip to main content

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).

Governance coverage
  • Interception point: agentgateway's three native external-policy seams — mcpGuardrails/ExtMcp (MCP method layer), extAuthz (Envoy ext_authz, HTTP headers), and extProc (Envoy ext_proc, HTTP bodies) — each calling the AxonFlow axonflow-gateway-adapters gRPC 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 verdictsneeds_approval is 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 seamLayerBlocksMutates contentAxonFlow 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 onlydecide
extProc (Envoy ext_proc)HTTP request + response bodies✅ both bodiesdecide + 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
VariableDefaultPurpose
AXONFLOW_ENDPOINT— (required)PDP base URL
AXONFLOW_ORG_ID / AXONFLOW_LICENSE_KEYHTTP Basic PDP credentials (together)
AXONFLOW_TENANT_IDtenant scope on every decision
AXONFLOW_GATEWAY_IDagentgatewaycaller_identity.gateway_id on the audit row
AXONFLOW_DEFAULT_STAGEllmdecide stage for the HTTP seams (llm|tool|agent); ExtMcp always uses tool
AXONFLOW_FAIL_MODEclosedrequest-plane posture on PDP-unreachable (closed|open)
AXONFLOW_REQUEST_TIMEOUT10sengine-call bound (circuit posture)
AXONFLOW_CONNECTOR_TAGagentgatewayconnector_type origin tag on fulfillment audit rows
AXONFLOW_MAX_BODY_BYTES8388608 (8 MB)scannable-payload bound; larger payloads fail closed
AXONFLOW_BREAKER_THRESHOLD / AXONFLOW_BREAKER_COOLDOWN5 / 30sconsecutive PDP transport failures that open the circuit, and how long it stays open
AXONFLOW_TRUST_IDENTITY_HEADERSfalseforward 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:9090gRPC 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's user_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-Id are a reserved channel, ignored by default. The engine's response-governance endpoint currently derives audit identity from the validated user_token and ignores these headers. The adapter still refuses to forward them unless AXONFLOW_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

FailureRequest planeResponse plane
PDP unreachable / 5xx / circuit openblock (default) or forward with AXONFLOW_FAIL_MODE=openalways block
PDP rejects the call (4xx)always blockalways block
Redaction obligation unfulfillable (engine error, redactor didn't run, unforwardable output)always blockalways block
Payload exceeds the scannable bound (8 MB default)always blockalways 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