Kagenti with two MCP servers behind Envoy AI Gateway

This example deploys the Rosso weather and restaurant-reservation MCP servers, aggregates them behind one Envoy Gateway data plane, and connects a Kagenti generic agent to the single /mcp endpoint.

The manifests are in docs/en/ai_applications/components/kagenti/assets/envoy_ai_gateway_mcp. They target the kagenti-mcp-demo namespace. Before applying them, change the namespace and GatewayClass name if your environment uses different values.

Validated versions

This example was validated on 2026-07-28 with:

ComponentVersion
Alauda Build of Envoy Gateway1.7.0-build.20260625161000
Alauda Build of Envoy AI Gateway0.6.0-1
MCPRoute APIaigateway.envoyproxy.io/v1beta1

Envoy AI Gateway 0.6 supports MCP aggregation, authentication, tool authorization, and upstream credentials through MCPRoute.

Kuadrant mcp-gateway is not interchangeable with this API. Its controller creates an Istio EnvoyFilter and requires that resource before MCPGatewayExtension becomes ready. Envoy Gateway's native EnvoyExtensionPolicy does not expose the mutation_rules.allow_all_routing setting that Kuadrant MCP Gateway currently needs to rewrite :authority. Use the native Envoy AI Gateway MCPRoute shown here with Envoy Gateway, or use Kuadrant MCP Gateway with its supported Istio provider.

Topology and the HTTPRoute count

Kagenti generic agent
        |
        | one authenticated MCP URL
        v
Envoy Gateway :80 /mcp
        |
        | Envoy AI Gateway MCP proxy
        +-----------------------+
        |                       |
        v                       v
weather:8000/mcp       reservation:8000/mcp

Author one MCPRoute with two backendRefs; do not author the backend HTTPRoutes by hand. The controller creates exactly two backend routes and one required client-facing route:

  • ai-eg-mcp-br-kagenti-tools-weather
  • ai-eg-mcp-br-kagenti-tools-reservation
  • ai-eg-mcp-main-kagenti-tools

The first two are the requested per-server HTTPRoutes. The third exposes the unified /mcp endpoint. All three are owned by the MCPRoute and must not be edited directly.

Source and images

The workloads come from rossoctl/examples commit ec17c73235c8ef291a6d4aaa346b50a4fba35e72:

  • mcp/weather_tool
  • mcp/reservation_tool
  • a2a/generic_agent

The YAML pins immutable multi-architecture image digests published at ghcr.io/rossoctl/examples.

Deploy the gateway and two MCP servers

Create the example namespace if it does not exist:

kubectl create namespace kagenti-mcp-demo --dry-run=client -o yaml | kubectl apply -f -

Replace replace-this-demo-key in 03-mcp-route-auth-rate-limit.yaml before using this outside a disposable dev environment. Then apply the server, gateway, and policy manifests:

EXAMPLE_DIR=docs/en/ai_applications/components/kagenti/assets/envoy_ai_gateway_mcp

kubectl apply -f "$EXAMPLE_DIR/01-mcp-servers.yaml"
kubectl apply -f "$EXAMPLE_DIR/02-gateway.yaml"
kubectl apply -f "$EXAMPLE_DIR/03-mcp-route-auth-rate-limit.yaml"

The example uses API-key authentication and a local rate limit of 30 requests per minute for each authenticated client ID. sanitize: true removes the API key before the request is sent upstream. The generated client ID is forwarded as x-mcp-client-id, which is the rate-limit key.

Verify the result:

kubectl -n kagenti-mcp-demo rollout status deploy/weather-mcp
kubectl -n kagenti-mcp-demo rollout status deploy/reservation-mcp
kubectl -n kagenti-mcp-demo get gateway kagenti-mcp
kubectl -n kagenti-mcp-demo get mcproute kagenti-tools
kubectl -n kagenti-mcp-demo get httproute

Expected conditions are Gateway Programmed=True, MCPRoute Accepted, and Accepted=True on all three generated HTTPRoutes.

Test the unified MCP endpoint

Forward the stable Envoy Service created by 02-gateway.yaml:

kubectl -n envoy-gateway-system \
  port-forward service/kagenti-mcp-envoy 8080:80

In another terminal, initialize a session. Omitting the API key must return 401; providing it must return an MCP response and an mcp-session-id header.

MCP_URL='http://127.0.0.1:8080/mcp?api-key=replace-this-demo-key'

curl -i "$MCP_URL" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  --data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'

Use the returned session ID to list tools:

SESSION_ID='<mcp-session-id>'

curl -sS "$MCP_URL" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H "mcp-session-id: $SESSION_ID" \
  --data '{"jsonrpc":"2.0","method":"notifications/initialized"}'

curl -sS "$MCP_URL" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H "mcp-session-id: $SESSION_ID" \
  --data '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
  | sed -n 's/^data: //p' | jq

Tool names are prefixed with their backend, for example weather__get_weather and reservation__search_restaurants.

Deploy the Kagenti agent

Create the LLM configuration Secret with an OpenAI-compatible endpoint:

kubectl -n kagenti-mcp-demo create secret generic generic-agent-llm \
  --from-literal=api-base='https://your-openai-compatible-endpoint/v1' \
  --from-literal=api-key='replace-me' \
  --from-literal=model='replace-me'

kubectl apply -f docs/en/ai_applications/components/kagenti/assets/envoy_ai_gateway_mcp/04-agent.yaml

kubectl -n kagenti-mcp-demo get agentruntime generic-agent
kubectl -n kagenti-mcp-demo rollout status deploy/generic-agent

MCP_URLS contains only the unified Envoy endpoint, so the agent discovers both tool sets through one MCP connection. The generic agent currently exposes only URL configuration, not per-server request headers; the example therefore uses an API key query parameter. The agent logs MCP_URLS during startup, so use this only for development. For production, add header-based MCP client configuration to the agent or use Kagenti AuthBridge with OAuth/JWT and switch the MCPRoute to securityPolicy.oauth.

Try prompts such as:

  • What is the weather in Taipei?
  • Find Italian restaurants in Boston.

Cleanup

The generated HTTPRoutes are deleted with the MCPRoute:

EXAMPLE_DIR=docs/en/ai_applications/components/kagenti/assets/envoy_ai_gateway_mcp

kubectl delete -f "$EXAMPLE_DIR/04-agent.yaml" --ignore-not-found
kubectl delete -f "$EXAMPLE_DIR/03-mcp-route-auth-rate-limit.yaml"
kubectl delete -f "$EXAMPLE_DIR/02-gateway.yaml"
kubectl delete -f "$EXAMPLE_DIR/01-mcp-servers.yaml"
kubectl -n kagenti-mcp-demo delete secret generic-agent-llm --ignore-not-found