Repository navigation
Description
📋 Prerequisites
- I have searched the existing issues to avoid creating a duplicate
- By submitting this issue, you agree to follow our Code of Conduct
📝 Feature Summary
When a kagent Agent CR has spec.declarative.a2aConfig populated, set appProtocol: kgateway.dev/a2a on the agent's Service port so agentgateway automatically switches to A2A protocol handling — same way it already does for MCP backends marked with appProtocol: kgateway.dev/mcp.
❓ Problem Statement / Motivation
agentgateway's documented Kubernetes integration for A2A (docs) relies on the Service appProtocol marker:
"Notice that the Service uses the
appProtocol: kgateway.dev/a2asetting. This way, agentgateway configures the agentgateway proxy to use the A2A protocol."
Without this marker, the proxy treats traffic as opaque HTTP and skips A2A-specific behaviors like agent-card url rewriting. That breaks the round-trip: a card fetched through the gateway points back at the in-cluster Service hostname, which external A2A clients can't reach.
The kagent operator (go/core/internal/controller/translator/agent/conversion.go) already reads appProtocol to find the MCP port on referenced Services, but does not write any appProtocol value on the agent's own generated Service. So the obvious fix — patch the Service in-place — gets reverted on the next reconcile loop within ~10s (verified empirically on v0.8.0).
Today the only workaround is a sibling "wrapper" Service per agent in a separate manifest, with a duplicated pod selector. Six agents in our deployment ⇒ six wrapper Services. Two boilerplate Services per A2A agent is a lot of Kubernetes for what should be a one-liner in the operator's translator.
💡 Proposed Solution
In internal/controller/translator/agent/conversion.go (or wherever the agent Service is generated), set the appProtocol field on the agent port when the source Agent CR has spec.declarative.a2aConfig non-nil:
agentPort := corev1.ServicePort{
Name: "http",
Port: 8080,
TargetPort: intstr.FromInt(8080),
Protocol: corev1.ProtocolTCP,
AppProtocol: ptr.To("kgateway.dev/a2a"), // ← new, when a2aConfig is set
}A bare http Agent (no A2A) would keep the field unset, exactly as today.
🔄 Alternatives Considered
- In-place
oc patchof the operator-managed Service — reverted by the controller within seconds. - Wrapper Services in user manifests — works but doubles Service count for every A2A agent.
- A separate
Servicefield on the Agent CR allowing arbitrary spec overrides — more flexible but heavier; the appProtocol case is mechanical and doesn't need configurability. - Mutating webhook — fights the operator on every reconcile; brittle.
Additional Context
- Reproduced on kagent v0.8.0, agentgateway v1.1.0, OpenShift 4.x.
oc explain agentgatewaybackends.agentgateway.dev.specconfirms agentgateway has no dedicateda2abackend type yet —appProtocolon the Service is the only documented integration mechanism for A2A in K8s.- Same pattern is already in production for MCP (
appProtocol: kgateway.dev/mcponargocd-mcp-server,db2-luw-mcp-server, etc.). - Code reference:
go/core/internal/controller/translator/agent/conversion.goalready has the surrounding shape — it readssvc.Spec.Ports[].AppProtocolfor MCP detection but never writes it back on agent services.
Happy to send a PR if there's interest.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsBacklog
Activity
davidkarlsen commentedon May 7, 2026
Update from downstream client implementation
Implementing an A2A client downstream (Spring AI tool callbacks in our Slack bot) hit a related discovery gap that's adjacent to this issue. Posting here so it stays linked.
Empirical probe (kagent v0.8.0 + agentgateway v1.1.0)
With the wrapper-Service workaround from this issue's "Alternatives Considered #2" in place, per-agent discovery already works via the spec path:
But there is no gateway-level enumeration. Probing the routed gateway root:
The MCP federation backend catches them via the default HTTPRoute rule.
oc explain agentgatewaybackends.agentgateway.dev.specconfirms agentgateway v1.1.0 has no nativea2abackend kind, so there's nothing on the gateway side to expose a routed-agent catalog either.Net effect: an A2A client today has to keep a static list of agent names even though kagent already knows the full set from its
AgentCRs (and the operator's controller-runtime cache).A small runtime quirk worth noting — Go-ADK agents (e.g.
snow-agent) serve only/.well-known/agent-card.json(the legacy/.well-known/agent.jsonreturns 503), while Python-ADK agents (e.g.promql-agent) serve both. Per A2A's discovery doc, the new path is canonical, but downstream clients that follow the older docs get bitten.Spec / framing context
The A2A spec on discovery explicitly leaves the registry API undefined: "the A2A specification does not prescribe a standard API for curated registries." Solo's own write-up positions the gateway as the natural resolver layer here: "Agent discovery, naming and resolution — the missing pieces to A2A".
Suggested follow-up (separate from the appProtocol fix this issue tracks)
A sibling to kagent's existing
:8083/mcplisting (which enumerates MCP tool servers) but for A2A agents would close the gap. The appProtocol fix in this issue + a listing endpoint would let downstream A2A clients drop the staticapp.a2a.agentslist entirely.Happy to file as a separate kagent issue if preferred — keeping this one focused on the appProtocol writeback.
feat(controller): set appProtocol on agent Service when a2aConfig is …