The Tool Gateway agentgateway Operator is a Kubernetes operator that manages ToolGateway instances based on agentgateway. It provides centralized gateway management for tool workloads within the Agentic Layer ecosystem.
- Prerequisites
- Getting Started
- Development
- Configuration
- End-to-End (E2E) Testing
- Testing Tools and Configuration
- Sample Data
- Contribution
Before working with this project, ensure you have the following tools installed on your system:
- Go: version 1.26.0 or higher
- Docker: version 20.10+ (or a compatible alternative like Podman)
- kubectl: The Kubernetes command-line tool
- kind: For running Kubernetes locally in Docker
- helm: Helm 3+ for installing agentgateway
- make: The build automation tool
Quick Start:
# Create local cluster
kind create cluster
# Install required dependencies (cert-manager, Gateway API CRDs, agentgateway, agent-runtime)
make install-deps
# Install the Tool Gateway operator
kubectl apply -f https://github.com/agentic-layer/tool-gateway-agentgateway/releases/latest/download/install.yamlTo remove all dependencies from the cluster again:
kubectl delete -f https://github.com/agentic-layer/tool-gateway-agentgateway/releases/latest/download/install.yaml
make uninstall-depsThe Tool Gateway agentgateway Operator creates and manages Gateway API resources based on ToolGateway, ToolRoute, and ToolServer custom resources:
-
Gateway Creation: When a ToolGateway is created, the operator creates a dedicated Gateway with the same name in the same namespace as the ToolGateway with HTTP listener on port 80. Each ToolGateway has its own Gateway instance.
-
ToolRoute Integration: For each ToolRoute resource, the operator creates:
- AgentgatewayBackend: Configures the MCP backend connection to the route's upstream (a cluster
ToolServerservice or an external URL). - HTTPRoute: Routes traffic from the referenced
ToolGatewayto theAgentgatewayBackendat the path/<toolroute-namespace>/<toolroute-name>/mcp. The reachable URL is surfaced asToolRoute.status.url. - AgentgatewayPolicy (optional): When
spec.toolFilteris set, anAgentgatewayPolicyis attached to the route'sHTTPRoutecarrying MCP authorization CEL rules translated from the glob allow/deny patterns.
- AgentgatewayBackend: Configures the MCP backend connection to the route's upstream (a cluster
-
ToolServer: Describes how an MCP server is deployed. It is consumed by
ToolRoute.spec.upstream.toolServerRef; exposure through a gateway is always driven by aToolRoute. -
Automatic Updates: The operator watches for changes to ToolGateway and ToolRoute resources and updates the corresponding Gateway API resources automatically.
ToolGateway (CRD)
|
Gateway (same name and namespace)
ToolRoute (CRD)
|
HTTPRoute (Gateway API) --> AgentgatewayBackend --> upstream (ToolServer or external URL)
|
AgentgatewayPolicy (optional, when toolFilter is set)
Follow the prerequisites above to set up your local environment. Then follow these steps to build and deploy the operator locally:
# Install CRDs into the cluster
make install
# Build docker image
make docker-build
# Load image into kind cluster (not needed if using local registry)
make kind-load
# Deploy the operator to the cluster
make deployAfter a successful start, you should see the controller manager pod running in the tool-gateway-agentgateway-system namespace.
kubectl get pods -n tool-gateway-agentgateway-systemBefore creating a ToolGateway, ensure you have:
- Gateway API CRDs installed in your cluster
- agentgateway support installed (see Getting Started)
To create a agentgateway-based gateway for your tools, define a ToolGateway resource:
apiVersion: runtime.agentic-layer.ai/v1alpha1
kind: ToolGateway
metadata:
name: my-tool-gateway
namespace: my-namespace
spec:
toolGatewayClassName: agentgateway # Optional: uses default if not specifiedThis will create a my-tool-gateway Gateway in the my-namespace namespace.
The operator automatically translates standard OpenTelemetry environment variables to agentgateway's telemetry configuration. This provides seamless observability integration without requiring manual configuration of agentgateway's config file.
Supported OTEL Environment Variables:
OTEL_EXPORTER_OTLP_ENDPOINT- The OTLP exporter endpoint URLOTEL_EXPORTER_OTLP_PROTOCOL- The protocol to use (grpc or http)OTEL_EXPORTER_OTLP_HEADERS- Headers to send with telemetry data- Signal-specific overrides:
OTEL_EXPORTER_OTLP_TRACES_ENDPOINTOTEL_EXPORTER_OTLP_TRACES_PROTOCOLOTEL_EXPORTER_OTLP_TRACES_HEADERS- (Similar for METRICS and LOGS)
Example:
apiVersion: runtime.agentic-layer.ai/v1alpha1
kind: ToolGateway
metadata:
name: my-tool-gateway
namespace: my-namespace
spec:
toolGatewayClassName: agentgateway
env:
# OTEL configuration - automatically translated to agentgateway config
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "http://otel-collector:4318"
- name: OTEL_EXPORTER_OTLP_PROTOCOL
value: "http/protobuf"
- name: OTEL_EXPORTER_OTLP_HEADERS
value: "api-key=secret123"
# Other environment variables are passed through as-is
- name: LOG_LEVEL
value: "info"The OTEL environment variables are translated to agentgateway's rawConfig.telemetry section and are not passed as environment variables to the agentgateway container. Other environment variables (like LOG_LEVEL above) are passed through unchanged.
See config/samples/toolgateway_v1alpha1_otel_example.yaml for complete examples.
Define ToolServer resources that represent your MCP server deployments. A ToolServer
only describes what runs; to actually expose it through a ToolGateway you must
create a ToolRoute.
apiVersion: runtime.agentic-layer.ai/v1alpha1
kind: ToolServer
metadata:
name: my-tool-server
namespace: my-namespace
spec:
protocol: mcp
transportType: http
image: my-tool-server:latest
port: 8000
path: /mcp
replicas: 1Create one ToolRoute per exposure of a tool server (cluster ToolServer or
external URL) through a ToolGateway. Optionally restrict which tools are
visible with toolFilter:
apiVersion: runtime.agentic-layer.ai/v1alpha1
kind: ToolRoute
metadata:
name: my-tool-route
namespace: my-namespace
spec:
toolGatewayRef: # Optional; see "Default ToolGateway resolution" below
name: my-tool-gateway
upstream:
# Exactly one of toolServerRef or external must be set
toolServerRef:
name: my-tool-server
# OR: external:
# url: https://github-mcp.example.com/mcp
toolFilter:
# Glob allow/deny. Deny wins on conflict. If toolFilter is nil, all
# tools pass through unfiltered.
allow: ["get_*", "list_*"]
deny: ["*delete*"]For each ToolRoute, the operator creates:
- An AgentgatewayBackend configured with the resolved upstream
- An HTTPRoute matching
/<route-namespace>/<route-name>/mcpon the referencedToolGateway - An AgentgatewayPolicy with MCP
authorizationCEL rules (only whentoolFilteris set)
The reachable URL is written to ToolRoute.status.url and is what consumers read.
spec.toolGatewayRef is optional. The operator resolves the target ToolGateway as follows:
- If
toolGatewayRefis set, the explicit reference is used. Thenamespacefield defaults to the ToolRoute's own namespace when omitted. - If
toolGatewayRefis omitted, the operator selects aToolGatewayfrom the well-knowntool-gatewaynamespace. If multiple exist there, the first one listed wins (a warning is logged). If none exists, the route's status is set toNotReadywith reasonToolRouteGatewayNotFound; reconciliation retriggers automatically once a defaultToolGatewayis created in that namespace.
The operator supports adding guardrails to your ToolGateway to filter sensitive
content (PII, harmful content) from tool traffic. For each Guard you create,
the operator automatically deploys a dedicated guardrail-adapter instance
pre-configured from the Guard and its GuardrailProvider — no manual adapter
deployment required.
-
The operator must be started with
--guardrail-adapter-image=<image:tag>. The shippedinstall.yamlalready pins a known-compatible adapter version. If you build your own kustomize overlay, set the image via:patches: - target: { kind: Deployment, name: controller-manager, namespace: tool-gateway-agentgateway-system } patch: |- apiVersion: apps/v1 kind: Deployment metadata: name: controller-manager spec: template: spec: containers: - name: manager args: - --leader-elect - --health-probe-bind-address=:8081 - --guardrail-adapter-image=ghcr.io/agentic-layer/guardrail-adapter:0.2.1
-
A
GuardrailProviderbacking service (e.g. Presidio) must be reachable from the cluster.
Step 1: Deploy the GuardrailProvider
apiVersion: runtime.agentic-layer.ai/v1alpha1
kind: GuardrailProvider
metadata:
name: presidio-analyzer
namespace: default
spec:
type: presidio-api
presidio:
baseUrl: http://presidio-analyzer.default.svc:8080Step 2: Create a Guard
apiVersion: runtime.agentic-layer.ai/v1alpha1
kind: Guard
metadata:
name: pii-guard
namespace: default
spec:
mode:
- pre_call # Filter requests
- post_call # Filter responses
description: "Detects and masks PII in tool traffic"
providerRef:
name: presidio-analyzer
presidio:
language: "en"
scoreThresholds:
ALL: "0.5" # Default threshold
PERSON: "0.8" # Higher threshold for person names
EMAIL_ADDRESS: "0.7"
entityActions:
PERSON: "MASK" # Replace with <PERSON>
EMAIL_ADDRESS: "MASK" # Replace with <EMAIL_ADDRESS>
CREDIT_CARD: "BLOCK" # Reject requests with credit cards
PHONE_NUMBER: "MASK"Step 3: Reference the Guard in Your ToolGateway
apiVersion: runtime.agentic-layer.ai/v1alpha1
kind: ToolGateway
metadata:
name: my-tool-gateway
namespace: default
spec:
toolGatewayClassName: agentgateway
guardrails:
- name: pii-guardThe operator creates pii-guard-adapter (Deployment + ConfigMap + Service)
in the Guard's namespace and wires the ToolGateway's AgentgatewayPolicy
extProc.backendRef at it automatically.
- Single Guard per ToolGateway: Agentgateway currently supports only one ext_proc slot per target. If you specify multiple guards, the operator will set a
GuardrailsUnsupportedstatus condition. - Presidio only: Only
presidio-apiproviders are supported today. Guards withopenai-moderation-apiorbedrock-apiproviders becomeReady=False/UnsupportedProviderType.
| Resource | Condition | Reason | Meaning |
|---|---|---|---|
Guard |
Ready=False |
AdapterNotConfigured |
Operator started without --guardrail-adapter-image |
Guard |
Ready=False |
ProviderNotFound |
providerRef does not resolve |
Guard |
Ready=False |
UnsupportedProviderType |
Only presidio-api is supported today |
Guard |
Ready=False |
InvalidConfig |
Validation failed (missing endpoint, empty modes, etc.) |
ToolGateway |
Ready=False |
GuardNotFound |
The referenced Guard does not exist |
ToolGateway |
Ready=False |
GuardNotReady |
The Guard exists but its Ready condition is False |
See config/samples/guardrails/ for a complete example. Apply it with kubectl apply -k config/samples/guardrails to deploy Presidio and a configured ToolGateway in one step.
Once deployed, tools are accessible via the ToolGateway's Gateway:
# Get the Gateway service endpoint (example for 'my-tool-gateway')
kubectl get svc -n my-namespace | grep my-tool-gateway
# Access your tool via its ToolRoute path (see ToolRoute.status.url)
curl http://<gateway-endpoint>/<route-namespace>/<route-name>/mcp- kind must be installed and available in PATH
- Docker running and accessible
- kubectl configured and working
The E2E tests automatically create an isolated Kind cluster, deploy the operator, run comprehensive tests, and clean up afterwards.
# Run complete E2E test suite
make test-e2eIf you need to run E2E tests manually or inspect the test environment:
# Set up test cluster
make setup-test-e2e
# Install required dependencies
make install-deps
# Run E2E tests against the existing cluster
KIND_CLUSTER=tool-gateway-agentgateway-test-e2e go test ./test/e2e/ -v -ginkgo.v
# Clean up test cluster when done
make cleanup-test-e2eThe project includes comprehensive test coverage:
- Unit Tests: Test suite for the controller
- E2E Tests: End-to-end tests in Kind cluster
- Ginkgo/Gomega: BDD-style testing framework
- EnvTest: Kubernetes API server testing environment
Run tests with:
# Run unit and integration tests
make test
# Run E2E tests in kind cluster
make test-e2eThe project includes sample manifests to help you get started.
-
Where to find sample data? Sample manifests are located in the
config/samples/directory. -
How to deploy sample resources? You can deploy sample ToolGateway resources with:
kubectl apply -k config/samples/
See Contribution Guide for details on contribution, and the process for submitting pull requests.