docs: add A2A protocol support design document - #1114
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughPR adds A2A design, E2E test, and implementation-plan documents for discovery, path-based routing, gateway task-ID mapping, streaming rewrite behavior, CRD/config/security details, and phased implementation tasks. ChangesA2A Protocol Design and Implementation Planning
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~5 minutes Possibly related issues
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Signed-off-by: Aman-Cool <aman017102007@gmail.com>
9a21ac3 to
399eb7a
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
docs/design/a2a/tasks/tasks.md (1)
401-401:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd newline at end of file.
File must end with a newline per coding guidelines.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/design/a2a/tasks/tasks.md` at line 401, Add a final newline character at the end of docs/design/a2a/tasks/tasks.md so the file ends with a newline per repository coding guidelines; simply edit the file and ensure the last line is terminated with a newline (save the file so the trailing newline is present).Source: Coding guidelines
docs/design/a2a/tasks/e2e_test_cases.md (1)
102-102:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd newline at end of file.
File must end with a newline per coding guidelines.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/design/a2a/tasks/e2e_test_cases.md` at line 102, The file e2e_test_cases.md is missing a trailing newline; add a single newline character at the end of the file so the file ends with a newline (ensure the editor saves the file with a newline at EOF).Source: Coding guidelines
🧹 Nitpick comments (1)
docs/design/a2a/a2a-design.md (1)
318-319: ⚖️ Poor tradeoffConsider cleanup strategy for terminal-state task routes.
TaskRoutes persist until session expiry (matching JWT TTL) even after tasks reach terminal states (completed, failed, canceled, rejected per line 198-201). For long-lived sessions with high task volume, this could accumulate unnecessary entries. Consider adding explicit cleanup on terminal state transitions, or document this as an acceptable tradeoff.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/design/a2a/a2a-design.md` around lines 318 - 319, TaskRoutes are currently kept until the JWT TTL via the Redis key pattern a2atask:{gatewayTaskID} using JWTManager.GetExpiresIn(gatewaySessionID); add explicit cleanup on task terminal-state transitions (completed, failed, canceled, rejected) by removing the Redis key or resetting its TTL to a short value in the task state transition handler (the function that processes task state changes / terminal-state events), or alternatively document this tradeoff; update the code paths that write/refresh a2atask:{gatewayTaskID} (and any task-route creation helpers) to perform the delete/TTL-update when the TaskRoutes reach a terminal state so entries don’t accumulate for long-lived sessions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/design/a2a/a2a-design.md`:
- Line 64: The document uses mixed British and American spelling—specifically
the word "initialises" (UK) appears while "initialize" (US) is used elsewhere;
standardize to a single variant by replacing all occurrences of "initialises"
with "initialize" across the document (search for the token "initialises" and
update it), and run a quick grep for other UK/US pairs to ensure consistency
(e.g., "initialise"/"initialize") so the term is consistent with the existing
"initialize" usage.
- Line 404: This file ends with the line "See
[`tasks/e2e_test_cases.md`](tasks/e2e_test_cases.md) for E2E test case
definitions." but is missing a trailing newline; open the corresponding markdown
file and add a single newline character after that final line so the file
terminates with a newline (configure your editor to trim/ensure final newline on
save if needed).
---
Outside diff comments:
In `@docs/design/a2a/tasks/e2e_test_cases.md`:
- Line 102: The file e2e_test_cases.md is missing a trailing newline; add a
single newline character at the end of the file so the file ends with a newline
(ensure the editor saves the file with a newline at EOF).
In `@docs/design/a2a/tasks/tasks.md`:
- Line 401: Add a final newline character at the end of
docs/design/a2a/tasks/tasks.md so the file ends with a newline per repository
coding guidelines; simply edit the file and ensure the last line is terminated
with a newline (save the file so the trailing newline is present).
---
Nitpick comments:
In `@docs/design/a2a/a2a-design.md`:
- Around line 318-319: TaskRoutes are currently kept until the JWT TTL via the
Redis key pattern a2atask:{gatewayTaskID} using
JWTManager.GetExpiresIn(gatewaySessionID); add explicit cleanup on task
terminal-state transitions (completed, failed, canceled, rejected) by removing
the Redis key or resetting its TTL to a short value in the task state transition
handler (the function that processes task state changes / terminal-state
events), or alternatively document this tradeoff; update the code paths that
write/refresh a2atask:{gatewayTaskID} (and any task-route creation helpers) to
perform the delete/TTL-update when the TaskRoutes reach a terminal state so
entries don’t accumulate for long-lived sessions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 0cf24eff-430d-4ff6-80af-26be8ffb65b3
📒 Files selected for processing (3)
docs/design/a2a/a2a-design.mddocs/design/a2a/tasks/e2e_test_cases.mddocs/design/a2a/tasks/tasks.md
… design Correct ImmediateResponse to BodyResponse in message/send sequence diagram, add missing non-streaming ResponseHeaders/ResponseBody phases with ModeOverride BUFFERED and StoreTaskRoute, expand tasks/get diagram to show full request and response body rewriting, fix StoreTaskRoute timing in state machine note, rewrite Router component responsibilities to split behaviour by ext_proc phase, add Auth field to A2AAgent config struct, fix COW note in TaskStore section, replace validating webhook claim with controller-level prefix conflict detection, and add a2a-task-routing-infra interface incompatibility note to Task 11. Signed-off-by: Aman-Cool <aman017102007@gmail.com>
c88f6da to
ddfacc1
Compare
Signed-off-by: Aman-Cool <aman017102007@gmail.com>
|
Hey @david-martin, @jasonmadigan and @maleck13, Did some digging on Q2 (which spec version to target) since this blocks the test server work; turns out the spec has moved more than I expected, so wanted to lay out what I found. There are effectively three versions in the wild right now:
My suggestion: target v0.3.0 for this term. Every method name in the design doc and the routing logic stays valid; the only real change is serving the card at One wrinkle worth flagging: the official Go SDK ( If this sounds reasonable I'll update the doc to reflect v0.3.0 + the dual card path and get moving on the test server. |
|
Design conformance issue found during Week 2 prep: (Cross-posting from the Slack group so the decision is tracked here.) While going through the A2A v0.3.0 spec line by line to build the test server, I found that the protocol has no This design doc currently assumes skill-based dispatch in three places:
Standard A2A clients will never send that field. It also explains why Options
Recommendation Option 2. It matches how the rest of the ecosystem solved this, works with unmodified clients, and removes complexity from the router rather than adding it. The skill-prefix federation idea survives in the discovery layer; just not in invocation routing. Impact on current work Not blocked: the test server's agent card, |
Replace skill-dispatch routing with path-per-agent: each registered A2A agent
is accessible at /a2a/{prefix} and its card at /a2a/{prefix}/.well-known/agent.json.
Multi-agent discovery moves to /.well-known/api-catalog (RFC 9264) instead of a
merged agent.json. Agent lookup moves to RequestHeaders phase (GetAgentByPrefix)
instead of RequestBody. Resolves open questions Q1 (mcp.kuadrant.io API group)
and Q2 (A2A v0.3.0). Updates flow diagrams, component responsibilities, CRD
example, security section, tasks, and e2e test cases throughout.
Signed-off-by: Aman-Cool <aman017102007@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/design/a2a/tasks/e2e_test_cases.md (1)
8-101: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winNormalize the case heading level.
These test cases jump from the file title (
#) straight to###, which triggers the markdownlint heading-increment warning. Use##for the case headings (and keep the same level for the rest of the cases). As per coding guidelines, this doc should stay aligned with the repo’s e2e test-case format; markdownlint also flags the h1→h3 jump here.Proposed fix
-### [Happy,A2A] API Catalog lists registered agents and per-agent card returns correct skills +## [Happy,A2A] API Catalog lists registered agents and per-agent card returns correct skills🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/design/a2a/tasks/e2e_test_cases.md` around lines 8 - 101, The heading levels jump from the file title ("#") to "###" which triggers markdownlint; update all test-case headings like "### [Happy,A2A] API Catalog lists registered agents and per-agent card returns correct skills" (and every other "### ..." entry) to use "##" instead, keeping the rest of the subheadings unchanged so the document matches the repo e2e test-case format and resolves the heading-increment warning.Sources: Coding guidelines, Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/design/a2a/a2a-design.md`:
- Around line 177-183: The final diagram line incorrectly shows the last SSE
chunk as "Upstream-->>Client" bypassing Envoy and Router; update the diagram so
the final hop follows the same path as earlier chunks (Upstream → Envoy → Router
→ Client) and ensure the narrative references the a2aSSEPassthrough.Process()
behavior and StoreTaskRoute(gatewayTaskID, TaskRoute{serverName,
"upstream-abc"}) semantics — i.e., replace the terminal "Upstream-->>Client"
with "Upstream-->>Envoy", then "Envoy->>Router: ProcessingRequest_ResponseBody",
and "Router-->>Client: data: {\"result\": {\"id\": \"gateway-123\", ...}}" so
the flow remains consistent with the replacement of "upstream-abc" →
"gateway-123".
- Line 239: Update the ExtProcServer router contract documentation to remove any
mention of the `message/stream` method and instead describe streaming as
negotiated via `Accept: text/event-stream` on `message/send`; specifically edit
the RequestHeaders section (where `message/send`/`message/stream` is referenced)
to only reference `message/send` and the agent-prefix lookup via
A2ABroker.GetAgentByPrefix(), keep gateway task ID generation and header sets
(`:authority`, `x-a2a-agent`, `x-a2a-task-id`) unchanged, adjust any
RequestBody/ResponseHeaders/ResponseBody text that mentions `message/stream` to
describe streaming behavior as `message/send` + SSE negotiation, and update the
a2aSSEPassthrough.Process() description to say it handles streaming for
`message/send` negotiated via Accept: text/event-stream while keeping references
to ResolveTaskRoute() and StoreTaskRoute().
---
Outside diff comments:
In `@docs/design/a2a/tasks/e2e_test_cases.md`:
- Around line 8-101: The heading levels jump from the file title ("#") to "###"
which triggers markdownlint; update all test-case headings like "### [Happy,A2A]
API Catalog lists registered agents and per-agent card returns correct skills"
(and every other "### ..." entry) to use "##" instead, keeping the rest of the
subheadings unchanged so the document matches the repo e2e test-case format and
resolves the heading-increment warning.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4defcd30-a0f3-4995-bc34-1a4f559c29e1
📒 Files selected for processing (3)
docs/design/a2a/a2a-design.mddocs/design/a2a/tasks/e2e_test_cases.mddocs/design/a2a/tasks/tasks.md
✅ Files skipped from review due to trivial changes (1)
- docs/design/a2a/tasks/tasks.md
|
Pushed an update to the design doc based on some spec research I did this week; wanted to share what I found and get your thoughts before moving forward. The main thing: while going through the A2A v0.3.0 spec more carefully, I noticed that Given that, I looked at a few options and updated the doc toward path-per-agent ( Also took a first pass at Q1 (CRD group -> Q4 (session handling) I've left open : planning to bring that to the Week 8 sync as originally discussed. @david-martin, @jasonmadigan and @maleck13, What do you think? Anything I should reconsider or approach differently? |
…ract SSE final chunk was shown going directly Upstream→Client, skipping Envoy and Router. All SSE chunks including the terminal one must flow through the full ext_proc path. Also removes message/stream from the router component description — streaming is negotiated via Accept: text/event-stream on message/send, not a separate method. Signed-off-by: Aman-Cool <aman017102007@gmail.com>
|
Thanks @Aman-Cool . This is on my list to review this week. Sorry at the moment just a bit swamped. Thank you for the effort put in so far |
Clarify that the broker rewrites each Agent Card's url field to the gateway
path (/a2a/{prefix}) so unmodified A2A clients route through the gateway by
following the card they already fetch. Note the agentgateway precedent and the
upstream multi-agent discovery work (a2aproject/A2A Kuadrant#641, Kuadrant#883, Kuadrant#166).
Add corresponding acceptance criteria to the test server, broker, and e2e
discovery tasks, and the discovery e2e test case. Add new technical terms to
the cspell dictionary.
Signed-off-by: Aman-Cool <aman017102007@gmail.com>
|
@maleck13, genuinely no rush on the review. I know it's a swamped stretch, and I'd much rather you get to this with real headspace than rush a skim. I'm posting this less to add to your queue and more so that when you (and @jasonmadigan) get a window, the reasoning behind the routing change is in one place instead of scattered across the earlier comments. One important caveat up front: this is where I've landed after going deep on the spec. @david-martin had an early look and the direction seemed reasonable to him, but it isn't a settled group decision.., you and @jasonmadigan haven't had a chance to weigh in yet, and I really don't want to lock it until you guys also have. So please read everything below as a proposal I'm fairly confident in, with the door wide open to push back; especially on the one piece I've flagged as still genuinely unresolved. How we got hereThe original draft routed A2A requests by reading a skill out of the That leaves two honest ways to tell the gateway which agent a request is for:
I'm proposing the path. Reasoning below, and if either of you sees a third option I've missed, I'd genuinely like to hear it. Why I'm leaning path-per-agentThe header approach has the problem that a stock A2A client has no concept of The path doesn't need the client to know anything. A2A clients already fetch an agent card and POST to whatever The part that almost caught me outI want to call this out specifically, because it's subtle and it changed how I wrote the discovery flow. The original draft had the broker simply proxying the upstream Agent Card through unchanged. That looks correct right up until you trace what a real client does with it: it reads the So rewriting the card's Prior artWorth noting this isn't us out on a limb. The skill-prefix federation idea from the original draft doesn't die, by the way. It just stops being the routing mechanism and becomes the discovery/index layer instead. The one piece still genuinely open; and where I'd most value your inputDiscovery. A2A assumes one card per agent at the well-known path, so "how does a client find all the agents behind a single gateway" is a convention we have to define ourselves. The doc currently uses an RFC 9264 API Catalog at
This is the part I'd treat as "track upstream and stay aligned" rather than lock down hard, and it's the spot where a second and third opinion would help most, whether we commit to the RFC 9264 catalog now or keep it deliberately loose while the upstream discussions settle. One framing I'd put on the table, since I think it's a useful north star: this is a fast-moving spec and any specific shape of it could shift under us. The durable value the gateway adds is the auth / rate-limit / observability layer, which holds no matter how A2A itself settles; so I'm comfortable holding the discovery convention loosely as long as the routing and policy story underneath is solid. What's already in the PRI've pushed the design doc and task plan updates so they reflect the path-per-agent approach and the So: no urgency at all, but whenever you both have a moment, I'd really value your read on (1) path-per-agent as the routing mechanism and (2) how firmly we want to commit to the RFC 9264 catalog for discovery. I'll hold off treating either as settled until you've had the chance. |
|
Hey @Aman-Cool, apologies for not flagging this sooner. We recently updated our contributing guidelines to ask contributors to limit themselves to one open PR at a time, so you may not have seen the change. The idea is that getting one change reviewed and merged is more valuable than having several open in parallel. You currently have four open PRs. Which one do you think is best to focus on first? We'll prioritize getting that one through. |
|
Thanks @david-martin, and no worries at all; the guideline makes complete sense, and it's a good forcing function. I'd rather get one thing landed well than spread thin across several. I'd like to focus on #1114 (the A2A design doc). It's the one the rest of my A2A work depends on.., until the routing approach and discovery convention are agreed, the CRD, controller, and router PRs would all be building on a design that isn't settled yet. CI's green and it's the furthest along, so it should be the cleanest to get over the line. To keep things to a single active PR, I'll move my other open PRs to draft and bring them back one at a time once #1114 is merged. The two small bug fixes: the Really appreciate you prioritizing it. |
…s-namespace prefix - streaming via message/stream (§7.2) + tasks/resubscribe, not message/send+Accept - agent card at /.well-known/agent-card.json (§5.3), agent.json as v0.2 alias - api-catalog cited as RFC 9727 served as an RFC 9264 Linkset - TaskState aligned to 9 kebab-case values incl auth-required/unknown (§6.3) - Q4: per-request OAuth auth + principal-scoped task ownership (SEP-2567), deferred to wk8 sync - TaskStore TTL decoupled from JWT (idmap safety-net), task-id work at RequestBody - multi-field SSE task-id rewrite; cached agent cards; JSON-RPC error codes (-32001/-32601) - cross-namespace prefix collision reframed; skillPrefix -> agentPrefix Signed-off-by: Aman-Cool <aman017102007@gmail.com>
46ee1e4 to
6b9988d
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
docs/design/a2a/a2a-design.md (1)
62-67: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winFix UK/US spelling inconsistency.
Line 64 uses "initialises" (UK) while line 100 uses "initialize" (US). This was flagged in a previous review and marked as addressed, but the current text still shows "initialises". Standardize to US spelling to match the existing "initialize" usage.
Suggested fix
-When a platform engineer has an upstream A2A agent running in their cluster, they want to register +When a platform engineer has an upstream A2A agent running in their cluster, they want to register -it with the gateway so that clients can discover it via the federated agent card and send tasks +it with the gateway so that clients can discover it via the federated agent card and send tasks -through the gateway, so that all inter-agent traffic is subject to the same AuthPolicy and +through the gateway, so that all inter-agent traffic is subject to the same AuthPolicy and -RateLimitPolicy as MCP traffic. +RateLimitPolicy as MCP traffic.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/design/a2a/a2a-design.md` around lines 62 - 67, The A2A design doc still mixes UK and US spelling in the same flow, so standardize the wording to US English. Update the occurrence in the section describing the platform engineer deploying a new A2A agent to match the existing “initialize” usage elsewhere in the document, and keep the terminology consistent in the surrounding A2A gateway registration text.
🧹 Nitpick comments (1)
docs/design/a2a/a2a-design.md (1)
270-281: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReduce repetitive "At the" sentence openings in the Router description.
The Router row (line 276) has four successive sentences beginning with "At the
...phase:". Restructure for readability, e.g., bullet the phases or vary the sentence openings.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/design/a2a/a2a-design.md` around lines 270 - 281, The Router (`ExtProcServer`) description repeats four sentences starting with “At the ... phase,” making the responsibilities hard to scan. Rework that row in the component responsibilities table by restructuring the phase descriptions into bullets or otherwise varying the sentence openings while keeping the same `RequestHeaders`, `RequestBody`, `ResponseHeaders`, and `ResponseBody` behavior details intact.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/design/a2a/a2a-design.md`:
- Around line 96-101: The job story in the “When a client sends a request
without valid auth” section does not follow the required template because it
states “the gateway’s AuthPolicy should return” instead of using an
actor-centered “[actor] wants [action/outcome] so that [benefit]” phrasing.
Update the wording in this section of the design doc to match the required job
story format by naming the actor, describing the desired outcome in
present-tense “wants” language, and keeping the existing auth/401 behavior and
upstream-forwarding intent intact.
---
Duplicate comments:
In `@docs/design/a2a/a2a-design.md`:
- Around line 62-67: The A2A design doc still mixes UK and US spelling in the
same flow, so standardize the wording to US English. Update the occurrence in
the section describing the platform engineer deploying a new A2A agent to match
the existing “initialize” usage elsewhere in the document, and keep the
terminology consistent in the surrounding A2A gateway registration text.
---
Nitpick comments:
In `@docs/design/a2a/a2a-design.md`:
- Around line 270-281: The Router (`ExtProcServer`) description repeats four
sentences starting with “At the ... phase,” making the responsibilities hard to
scan. Rework that row in the component responsibilities table by restructuring
the phase descriptions into bullets or otherwise varying the sentence openings
while keeping the same `RequestHeaders`, `RequestBody`, `ResponseHeaders`, and
`ResponseBody` behavior details intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 24da5f48-01f1-4ce0-8874-97b70ff93535
📒 Files selected for processing (3)
docs/design/a2a/a2a-design.mddocs/design/a2a/tasks/e2e_test_cases.mddocs/design/a2a/tasks/tasks.md
✅ Files skipped from review due to trivial changes (2)
- docs/design/a2a/tasks/tasks.md
- docs/design/a2a/tasks/e2e_test_cases.md
…ity, ratelimit, observability)
- per-agent authorization via AuthPolicy on router-set x-a2a-agent (mirrors x-mcp-toolname RBAC)
- x-a2a-authorized skill filtering as allowed-capabilities.skills, filtered by ServeAgentCard
- skill filtering is visibility/discovery only, NOT access control: message/send has no skill (§7.1.1),
so the enforceable authorization boundary is the agent (/a2a/{prefix}) via Kuadrant AuthPolicy
- RateLimitPolicy (x-a2a-method, principal) and OTel/Authorino/Limitador observability
- e2e cases: per-agent 403, skill-filter visibility
Signed-off-by: Aman-Cool <aman017102007@gmail.com>
…artifacts - task id lives at the event envelope (result.id/result.taskId), sibling to artifact/status/history, never inside parts (base64 FilePart.file.bytes); router parses only down to the identity fields - heavy subtrees (status/artifact/history/message/parts) kept as json.RawMessage, never decoded/re-encoded - cost is O(envelope), not O(artifact); large artifacts pass through as raw bytes - bounds stated: single-event buffering + no SSE size cap (chunk via append/lastChunk); re-marshal copies raw bytes once (byte-splice fallback if profiling warrants) - history[].taskId rewrite is a scoped replace within the history raw bytes, never a global replace Signed-off-by: Aman-Cool <aman017102007@gmail.com>
…ler status no-op guard) - A2A has no card-change notification (verified against the v0.3.0 method set); gateway must poll - broker A2AAgentManager mirrors only MCPManager's ticker (poll), no persistent conn / subscription - cheap poll: conditional GET (304) -> version compare -> SHA-256 of normalized card body - act only on change; card refresh is an in-memory cache swap under RWMutex, NOT a Secret write - real anti-thrash point is the controller discoveredSkills status Update: skip when unchanged (reuse MCPServer.ConfigChanged no-op discipline) - staleness bound = ticker interval (reuse managerTickerInterval, default 1 min); cache-buster rejected Signed-off-by: Aman-Cool <aman017102007@gmail.com>
…dentity/token-exchange for invocation) - two paths, two models (mirrors MCP, which keeps credentialRef off the client path) - card fetch (no client): Gateway-to-Agent via credentialRef; router has no credentialRef access - message/send/tasks (real client): client identity, NOT credentialRef (static cred = confused deputy) - default: forward client bearer; recommended: RFC 8693 token exchange (Authorino) -> agent audience (RFC 8707) - agent securitySchemes (apiKey/http/oauth2/openIdConnect/mutualTLS), card- and skill-level (§5) - per-skill security advisory at gateway (no skill named in message/send); agent enforces it - exception: apiKey/mTLS-only agents -> opt-in per-agent static credential, client authz enforced at gateway AuthPolicy Signed-off-by: Aman-Cool <aman017102007@gmail.com>
Signed-off-by: Aman-Cool <aman017102007@gmail.com>
Signed-off-by: Aman-Cool <aman017102007@gmail.com>
Signed-off-by: Aman-Cool <aman017102007@gmail.com>
… shapes Signed-off-by: Aman-Cool <aman017102007@gmail.com>
…tore TTL source Signed-off-by: Aman-Cool <aman017102007@gmail.com>
…pe, and non-goals Signed-off-by: Aman-Cool <aman017102007@gmail.com>
Task IDs are assigned by the upstream agent and pass through the gateway unchanged. Routing is by path, so the gateway no longer mints, virtualizes, or rewrites task IDs. Replace the gateway-owned task ID and TaskRoute mapping with an (agent, taskID) -> principal ownership record used only for ownership checks and trace correlation. Drop the buffered and SSE task-ID body rewrites: the non-streaming path forwards the response body byte-for-byte after reading result.id, and the SSE handler becomes a read-only observer that binds ownership on the first event and deletes the record on a terminal state. Update the implementation plan and e2e test cases to match, including a new security case covering per-principal task ownership. Signed-off-by: Aman-Cool <aman017102007@gmail.com>
The ext_proc filter's default response_body_mode is NONE, so the router receives a response body only when it sets a ModeOverride at ResponseHeaders. Binding task ownership requires reading the agent-assigned task ID from the SendMessage response, so non-streaming methods set a BUFFERED override for observation. Nothing is mutated, so the body length is unchanged and no content-length removal is needed; that part of the spike remains relevant only to a future body rewrite. Restore the ResponseHeaders step in the SendMessage and GetTask flows and align the router responsibilities and task plan. Describe discovery routing as implemented: card and catalog requests share the /a2a rule, traverse ext_proc, and are passed through untouched to the broker, which serves them. A separate card-suffix HTTPRoute rule could not bypass the listener-scoped filter, so none exists. Signed-off-by: Aman-Cool <aman017102007@gmail.com>
Ownership checks fail closed with -32001, cover SendMessage continuations, and records persist past terminal states (retention TTL, Redis for multi-replica, insert-only store). SendMessage reads result.task.id per the v1.0 oneof; GetTask/CancelTask return a bare Task. Card validation covers every advertised interface; deferred methods are rejected, never forwarded. Signed-off-by: Aman-Cool <aman017102007@gmail.com>
…GENT_PREFIX) - drop the message/send + Accept streaming branch; streaming is message/stream only (§7.2) - remove now-dead wantsSSE - rename SKILL_PREFIX env -> AGENT_PREFIX (matches the agentPrefix rename in Kuadrant#1114) Signed-off-by: Aman-Cool <aman017102007@gmail.com>
- 'large'/'image' trigger returns a FilePart with a deterministic, size-configurable base64 payload (ARTIFACT_BYTES, default 1 MiB) - message/send: single large FilePart artifact (buffered rewrite path) - message/stream: chunked artifact-update events (append/lastChunk), split mid-base64 on purpose so a decoder chokes but a passthrough doesn't (proves envelope-only, no decode) - deterministic content so e2e can regenerate and assert byte-for-byte forwarding - adds fileContent/artifactUpdateEvent types; exercises the Task 12 SSE design (Kuadrant#1114) Signed-off-by: Aman-Cool <aman017102007@gmail.com>
raw ': ping' comment after the initial task event, so the gateway's data:-only SSE rewriter must pass non-data: lines through untouched without JSON-parsing them (regression guard for the Task 12 passthrough, Kuadrant#1114) Signed-off-by: Aman-Cool <aman017102007@gmail.com>
streaming method: reconnect replays the current task state over SSE and, for a working task, streams working updates then a terminal final event (bounded so it can't hang); an already-terminal task still gets an SSE final event, not a buffered response; unknown id -> -32001. completes the v0.3.0 method matrix (Kuadrant#1114) Signed-off-by: Aman-Cool <aman017102007@gmail.com>
- agentCard gains security/securitySchemes; AUTH_MODE declares them - enforcement, not just declaration: apikey requires X-API-Key (API_KEY) on the card fetch (tests credentialRef discovery) and /a2a; bearer requires Authorization: Bearer on /a2a (tests forward/token-exchange invocation); none (default) stays open - 401 + WWW-Authenticate when the credential is missing - lets e2e validate the gateway's credentialRef + token-exchange auth brokering (Kuadrant#1114) Signed-off-by: Aman-Cool <aman017102007@gmail.com>
…ation The A2A project closed its RFC 9264-catalog discovery issue in favor of the Agent-Card AI Catalog specification, whose entries carry both A2A agent cards and MCP server cards; update the discovery [OPEN] marker with that state and note the catalog layer is thin enough to serve ai-catalog additively. Cite the reference a2a-go client's behavior where it verifies this design's contract: it binds to the card's supportedInterfaces, its default resolution matches the gateway card path with no configuration, and its transport selection would pick a stray non-JSONRPC interface. Note that the reference SDK does not verify card signatures today and that additive counter-signing is an open protocol question, and state that the task-record retention TTL is operator-configured because A2A defines no discoverable task-retention period. Signed-off-by: Aman-Cool <aman017102007@gmail.com>
…ownership Align the card-validation claims with what the broker enforces (http(s) scheme, gateway path and host, JSONRPC binding, v1 protocolVersion) and correct the fail-closed wording: the broker excludes an agent from the catalog, a broker-local catalog-ineligible state, not the CRD Ready condition, since the broker is a read-only config observer with no status channel. Catalog eligibility now means a validated card is cached. Add a parallel insert-only (agent, contextId) -> principal record so a principal that learns another's contextId cannot inject into or continue that conversation, and both send methods verify context ownership. State insert-only as LoadOrStore / SET NX with new / same-owner / different-owner / store-unavailable outcomes, binding before the response is released. Note that catalog traversal is a gateway convention, not stock A2A discovery (per-agent card resolution is stock), with the RFC 9727 HEAD and profile details; that signed cards are single-gateway and their securitySchemes are advisory under gateway auth; that the AuthPolicy must scope to POST so public card GET stays unauthenticated; that the router guards the A2A-Version before parsing ownership fields; and that the router consumes a sanitized routing view without discovery credentials. Sync the task plan and e2e cases. Signed-off-by: Aman-Cool <aman017102007@gmail.com>
Align the Data Storage section with the implemented task ownership store:
records are the owning principal keyed (agent, taskID) in the existing in-memory
sync.Map under a taskowner: prefix, not a separate taskRecords field holding a
TaskRecord struct; StoreTaskRecord takes the principal and a TTL and returns the
current owner plus whether it created the record (insert-only via LoadOrStore /
SET NX); the Redis key is taskowner:{agent}:{id}. Trace context on the record is
reframed as the additive span-link enhancement rather than a stored struct field.
Fix an internal inconsistency in header stripping: the Security section listed
only x-a2a-agent and x-a2a-task-id, while the API Changes and RateLimitPolicy
sections rely on x-a2a-method being router-derived and un-forgeable. All three
are stripped at the HTTPRoute and in internalOnlyHeaders.
Signed-off-by: Aman-Cool <aman017102007@gmail.com>
Land the design incrementally behind an experimental --enable-a2a router flag, default off, with any APIs introduced along the way alpha. Phase boundaries follow review: no new CRDs while the long-term home for A2A support is being settled, and no broker changes while the broker is mid-churn on the new MCP spec version. Phase 1 is router-only passthrough for auditing, auth and observability: strip inbound x-a2a-* headers, parse POST bodies envelope-only, set x-a2a-method and x-a2a-agent (agent identity is the first path segment after /a2a/, a documented convention since the body names no agent), fail closed on unparseable POSTs, pass GETs through. Users hand-author agent HTTPRoutes; the headers feed Istio Telemetry and AuthPolicy with no further gateway code, and the agent-side task isolation and card-URL trust boundaries are documented rather than enforced. Phase 2 is registration and discovery (CRD, card cache and validation, verbatim serving); phase 3 is task ownership records and read-only SSE lifecycle observation. Phases 2 and 3 are the direction as understood, not a commitment. The Summary gains a pointer to the phases so the full-direction description is read as the destination rather than the first landing. Signed-off-by: Aman-Cool <aman017102007@gmail.com>
The task plan predated the phased landing and collided with it: its "Phase 1: Foundation" contained the CRD and reconciler, while the design's phase 1 is the router-only passthrough cut that explicitly excludes them. Restructure the plan around the design's implementation phases: phase 1 is the passthrough cut (Kuadrant#1333) — the flag, header stripping, envelope-only parse and passthrough metadata, its docs, and e2e against a hand-authored HTTPRoute — with the test server moved up since the e2e needs it; registration/discovery and task ownership tasks move under gated phases 2 and 3, narrowed where phase 1 already ships a piece. Week-based scheduling is dropped in favour of the phase gates. Also reconcile stale details with the design and the tree: the task-ownership store criteria now match the design's store section (records in the existing in-memory map under a taskowner: prefix, insert-only store returning owner and created, taskowner:{agent}:{id} Redis key); file references follow the router refactor (ext_proc_adapter.go, internal/routing); and the session-validation row gives way to per-request principal extraction via ExtractSubClaim, matching the per-request OAuth model. Signed-off-by: Aman-Cool <aman017102007@gmail.com>
The e2e case catalog describes the full direction; point phase 1's passthrough cases at the plan's Task 5 so the two read consistently with the phased landing. Signed-off-by: Aman-Cool <aman017102007@gmail.com>
Reconcile the full-direction body with the implementation phases and drop stale citations found on a close read: - Non-Goals now qualifies the -32004 deferred-method rejection as landing with the ownership enforcement it protects, so it no longer reads as always-on against the phase 1 exclusion. - Phase 2 explicitly names registry-backed request routing (agent resolution + :authority) as part of the discovery half, and the AuthPolicy section notes x-a2a-agent carries the bare path segment in phase 1, the namespace-qualified identity once the registry exists. - Remove line-number citations that drift as code moves (manager.go, tracing.go, request_handlers.go) and the header-strip reference now points at InternalOnlyHeaders in internal/routing and the a2a rule's filter, matching the router refactor. All 11 internal anchors verified; no [OPEN markers remain. Signed-off-by: Aman-Cool <aman017102007@gmail.com>
david-martin
left a comment
There was a problem hiding this comment.
Approving, with phase 1 changes being implementable
The intent is to have a --enable-a2a flag on the router, and only then parse traffic as a2a if it's on a /a2a prefixed path. No CRDs in this phase. Enables auth, auditing and observability at this phase. No a2a aggregation of cards or a2a catalog endpoint at this time.
The design doc merged upstream (Kuadrant#1114) with an implementation-phases section, and the landing is now incremental behind an experimental --enable-a2a flag: a router-only passthrough cut first (Kuadrant#1333), with registration/discovery and task ownership as later phases. Bring the fork's copies of the design doc, tasks plan and e2e cases up to the merged upstream versions, and update the README to match: Kuadrant#1114 merged, the phased landing narrative, the first-cut scope agreed with David and Craig, the test server approved (Kuadrant#1200), and the router/SSE/e2e prototype now merged and live-verified in-fork. Signed-off-by: Aman-Cool <aman017102007@gmail.com>
Design proposal for A2A protocol support in the MCP Gateway, refs #766 ; this document describes the full direction; agent registration and discovery, path-per-agent routing through ext_proc, and per-principal task ownership.., and, following review, an implementation-phases section that lands it incrementally behind an experimental --enable-a2a router flag, default off, with any new APIs alpha.
Phase 1 is the slice we've agreed to build first (#1333): router-only passthrough for auditing, auth and observability. The router strips inbound x-a2a-* headers, parses /a2a POST bodies envelope-only, and sets x-a2a-method and x-a2a-agent (the agent identity being the first path segment after /a2a/, a documented convention since the A2A body names no agent) ; unparseable POSTs fail closed, GETs pass through, users hand-author their agent HTTPRoutes, and the headers feed Istio Telemetry and AuthPolicy with no further gateway code. Registration/discovery and task ownership follow as phases 2 and 3; kept as the direction we're cutting from rather than a commitment, gated on where A2A support lands long-term and on the broker settling after the MCP spec-version work.
A few design positions worth calling out for review context: signed v1.0 AgentCards are served verbatim (a URL rewrite breaks the JWS over the JCS-canonicalized card), with the catalog advertising gateway paths alongside rather than inside the card ; task IDs are agent-assigned and pass through unchanged; routing is by path, so the gateway never mints or rewrites them, and ownership is an insert-only (agent, taskID) -> principal record that fails closed on any miss or mismatch ; and A2A authenticates per-request via OAuth bearer with ownership scoped to the principal, so it is untouched by MCP's stateless changes. The doc's decisions have been exercised end-to-end in a working prototype; discovery, routing and streaming passthrough against a live gateway; so the phases start from validated ground rather than paper.
All review feedback to date is incorporated and the open markers are resolved ; the discovery convention (RFC 9727 catalog vs the emerging AI Catalog) is deliberately held loose until phase 2 ships a catalog at all. Happy to adjust anything that would make this easier to build against as phase 1 lands.