Skip to content

[EPIC][A2A]: A2A Protocol v0.3.0 Full Compliance Implementation#3150

Draft
crivetimihai wants to merge 9 commits intomainfrom
a2a-spec
Draft

[EPIC][A2A]: A2A Protocol v0.3.0 Full Compliance Implementation#3150
crivetimihai wants to merge 9 commits intomainfrom
a2a-spec

Conversation

@crivetimihai
Copy link
Member

Note: This PR was re-created from #2978 due to repository maintenance. Your code and branch are intact. @crivetimihai please verify everything looks good.

Summary

This PR delivers full A2A Protocol v0.3.0 compliance in the gateway’s A2A stack, including:

  • canonical transport typing (a2a-jsonrpc, a2a-rest, a2a-grpc, rest-passthrough, custom),
  • spec-aligned routing and payload handling,
  • gRPC transport support,
  • task lifecycle/push-notification operations,
  • Agent Card discovery and retrieval,
  • persisted upstream task snapshots,
  • admin UI/admin API updates,
  • and expanded unit/JS coverage.

Closes #2547

What Was Implemented

1) A2A v0.3.0 operation coverage

Implemented canonical A2A operations end-to-end across router -> service -> transport:

  • message/send
  • message/stream
  • tasks/list
  • tasks/get
  • tasks/cancel
  • tasks/subscribe
  • tasks/pushNotificationConfig/set
  • tasks/pushNotificationConfig/get
  • tasks/pushNotificationConfig/list
  • tasks/pushNotificationConfig/delete
  • agent/getCard

New/updated API routes in mcpgateway/main.py:

  • POST /a2a/{agent_name}/message/send
  • POST /a2a/{agent_name}/message/stream
  • GET /a2a/{agent_name}/tasks
  • GET /a2a/{agent_name}/tasks/{task_id}
  • POST /a2a/{agent_name}/tasks/{task_id}/cancel
  • POST /a2a/{agent_name}/tasks/{task_id}/subscribe
  • POST /a2a/{agent_name}/tasks/{task_id}/pushNotificationConfigs
  • GET /a2a/{agent_name}/tasks/{task_id}/pushNotificationConfigs
  • GET /a2a/{agent_name}/tasks/{task_id}/pushNotificationConfigs/{config_id}
  • DELETE /a2a/{agent_name}/tasks/{task_id}/pushNotificationConfigs/{config_id}
  • GET /a2a/{agent_name}/card

Also introduced _get_a2a_invoke_context(...) to consistently derive user_id, user_email, and scoped token_teams for A2A invocation routes.

2) Transport model normalization and strict dispatch

Added canonical transport normalization and alias handling in schemas and admin/UI:

  • canonical values:
    • a2a-jsonrpc
    • a2a-rest
    • a2a-grpc
    • rest-passthrough
    • custom
  • aliases mapped from legacy values (generic, jsonrpc, openai, anthropic, a2a, etc.)

Key behavior change:

  • transport selection is now determined by normalized agent_type, not by endpoint trailing slash heuristics.

This is implemented in:

  • mcpgateway/schemas.py (normalize_a2a_agent_type, validators in create/update/read models)
  • mcpgateway/admin.py and mcpgateway/static/admin.js (legacy alias normalization + display labels)
  • mcpgateway/services/a2a_service.py and mcpgateway/services/tool_service.py (runtime dispatch)

3) A2A service enhancements (mcpgateway/services/a2a_service.py)

Major service improvements:

  • Added explicit A2A v0.3 constants and JSON-RPC error mapping (A2A_VERSION_HEADER = "0.3", code mapping for task/operation errors).
  • Added message part normalization to v0.3 shape (parts[].kind, with compatibility from legacy type).
  • Added JSON-RPC error extraction/parsing helper for uniform error handling.
  • Added REST request builder that maps A2A methods to /v1/... REST paths.
  • Added gRPC method resolver and gRPC invocation path:
    • method mapping from A2A method strings to stub methods,
    • protobuf request construction,
    • metadata/auth propagation,
    • TLS/insecure channel handling (grpcs:// vs grpc://),
    • stream collection for streaming APIs,
    • consistent A2AAgentError wrapping for grpc failures.
  • Added SDK-aware gRPC stub loading fallback to avoid descriptor collisions when both SDK and local generated stubs are present.
  • Added Agent Card discovery:
    • candidate URL generation,
    • base card + extended card merge when supportsAuthenticatedExtendedCard is enabled,
    • transport inference from card fields (preferredTransport, additionalInterfaces).
  • Added wrapper methods for task/push-notification/card operations:
    • send_message, stream_message, list_tasks, get_task, cancel_task, subscribe_task,
    • set_task_push_notification_config, get_task_push_notification_config, list_task_push_notification_configs, delete_task_push_notification_config,
    • get_agent_card.
  • Added persisted task snapshot upsert logic (_extract_task_payloads, _upsert_a2a_task) integrated into invoke flow.
  • Added OAuth auth support and query-param auth handling in invocation path with sanitized logging.

4) Tool integration behavior (mcpgateway/services/tool_service.py)

A2A tool invocation now aligns with same transport logic:

  • canonical agent-type normalization before dispatch,
  • support for JSON-RPC / REST / gRPC / passthrough / custom flows,
  • query-param auth decryption/application,
  • OAuth token acquisition and bearer header injection,
  • A2A REST header injection (A2A-Version) when transport is a2a-rest,
  • improved JSON-RPC error extraction and response handling,
  • passthrough header merge and correlation-id propagation retained.

5) DB model + migrations

Added persisted A2A task storage:

  • new ORM model A2ATask in mcpgateway/db.py
  • relationship from A2AAgent -> tasks
  • task table fields include agent/task identifiers, state, payload snapshots, latest message, error, timestamps
  • indexes and uniqueness constraints for efficient lookup and dedup

New migration 1:

  • mcpgateway/alembic/versions/x7h8i9j0k1l2_add_a2a_tasks_table.py
  • creates a2a_tasks table and indexes
  • idempotent checks before create/index operations

New migration 2:

  • mcpgateway/alembic/versions/y8i9j0k1l2m3_normalize_a2a_agent_type_values.py
  • data migration canonicalizing legacy a2a_agents.agent_type values
  • idempotent checks for table/column presence
  • linear migration chain (down_revision points to previous new revision)

6) gRPC protocol artifacts and generation pipeline

Added A2A protocol source and generated stubs:

  • mcpgateway/plugins/framework/external/grpc/proto/a2a.proto
  • mcpgateway/plugins/framework/external/grpc/proto/a2a_pb2.py
  • mcpgateway/plugins/framework/external/grpc/proto/a2a_pb2.pyi
  • mcpgateway/plugins/framework/external/grpc/proto/a2a_pb2_grpc.py

Updated plugin proto package loading:

  • mcpgateway/plugins/framework/external/grpc/proto/__init__.py
  • SDK-first load attempt for A2A stubs, then local fallback.

Updated generation target in Makefile:

  • grpc-proto now generates stubs for both plugin_service.proto and a2a.proto
  • uses explicit venv python/uv paths for reproducible generation
  • applies import/noqa headers to generated files

7) Admin UI and admin API improvements

mcpgateway/admin.py, mcpgateway/templates/admin.html, mcpgateway/static/admin.js:

  • canonical A2A type labels and alias normalization for display/create/edit/test flows,
  • explicit protocol choices in Add/Edit forms,
  • warning that endpoint trailing slash no longer controls transport,
  • request payload preview for selected transport,
  • improved A2A test modal payload construction by transport,
  • table rendering now formats legacy types safely and clearly.

8) Schema updates

mcpgateway/schemas.py:

  • added canonical transport constants + alias map,
  • added normalize_a2a_agent_type(...),
  • validators in A2AAgentCreate, A2AAgentUpdate, and A2AAgentRead now normalize types,
  • default create type changed to canonical a2a-jsonrpc.

Tests Added / Updated

A2A service tests (tests/unit/mcpgateway/services/test_a2a_service.py)

Added broad coverage for:

  • Agent Card discovery and extended card merge,
  • transport selection behavior (including "trailing slash does not switch transport"),
  • REST mapping behavior and A2A-Version header rules,
  • OAuth header behavior,
  • JSON-RPC alias behavior with parts.kind normalization,
  • gRPC dispatch and all main gRPC operations:
    • SendMessage
    • SendStreamingMessage
    • TaskSubscription
    • GetTask
    • CancelTask
    • GetAgentCard
    • Create/Get/List/Delete task push-notification config
    • secure/insecure channel behavior
    • gRPC error mapping.

Router/admin/schema/UI tests

  • tests/unit/mcpgateway/test_main.py: new tests for card and push-notification routes.
  • tests/unit/mcpgateway/test_admin.py: new tests for admin type normalization, payload builders, and legacy handling.
  • tests/unit/mcpgateway/test_schemas_validators_extra.py: alias normalization tests.
  • tests/js/admin-ui.test.js: protocol helper tests (normalization, labels, payload preview shape, trailing slash warning behavior).

Lint/Test/Validation Results

Executed and passing:

  • make doctest test
    • doctest: 1192 passed, 52 skipped
    • tests: 12267 passed, 456 skipped, 5 warnings
  • make pylint flake8
    • pylint: 10.00/10
    • flake8: pass

Additional Build/Tooling Changes

  • Makefile doctest targets now ignore generated protobuf modules during doctest collection.
  • .flake8 generated-protobuf per-file ignores were updated for regenerated stub formatting.
  • .flake8 includes targeted DAR* per-file ignores for large modules touched in this PR (main.py, admin.py, schemas.py, a2a_service.py) to keep CI lint stable while preserving functional changes scope.

Backward Compatibility / Behavior Notes

  • Legacy A2A agent_type values continue to work, now normalized to canonical transport names.
  • Transport is now explicit and deterministic via agent_type; endpoint trailing slash no longer toggles JSON-RPC behavior.
  • A2A REST transport sets A2A-Version: 0.3 header.
  • Task snapshots are now persisted for observed upstream task payloads.

Files of Interest

  • Core service: mcpgateway/services/a2a_service.py
  • Tool integration: mcpgateway/services/tool_service.py
  • Router: mcpgateway/main.py
  • Admin API/UI: mcpgateway/admin.py, mcpgateway/static/admin.js, mcpgateway/templates/admin.html
  • DB model: mcpgateway/db.py
  • Schemas: mcpgateway/schemas.py
  • Migrations:
    • mcpgateway/alembic/versions/x7h8i9j0k1l2_add_a2a_tasks_table.py
    • mcpgateway/alembic/versions/y8i9j0k1l2m3_normalize_a2a_agent_type_values.py
  • Proto:
    • mcpgateway/plugins/framework/external/grpc/proto/a2a.proto
    • mcpgateway/plugins/framework/external/grpc/proto/a2a_pb2.py
    • mcpgateway/plugins/framework/external/grpc/proto/a2a_pb2.pyi
    • mcpgateway/plugins/framework/external/grpc/proto/a2a_pb2_grpc.py

@crivetimihai crivetimihai added this to the Release 1.0.0-GA milestone Feb 24, 2026
@crivetimihai crivetimihai added enhancement New feature or request a2a Support for A2A protocol epic Large feature spanning multiple issues MUST P1: Non-negotiable, critical requirements without which the product is non-functional or unsafe labels Feb 24, 2026
jonpspri added 9 commits March 2, 2026 07:35
Signed-off-by: Jonathan Springer <jps@s390x.com>
Implement the foundation for A2A Protocol v1.0 RC1 migration:

Proto & schemas:
- Update a2a.proto with v1.0 Part restructuring (flat oneof), Message
  field rename (content -> parts), AgentCard with supported_interfaces,
  PascalCase RPC methods, tenant field, and blocking behavior
- Add Pydantic schemas for tenant, icon_url, and server A2A config
- Regenerate protobuf stubs

DB & migrations:
- Add A2AAgent.tenant, icon_url; Server A2A fields (a2a_enabled, etc.)
- Add ServerInterface, A2AAgentAuth, ServerTaskMapping models
- New migration 86d15e51b773 for A2A v1.0 tables and data migration
- Fix duplicate alembic revision y8i9j0k1l2m3 (two files shared same ID)
  by renaming backfill_oauth_config to y8j9k0l1m2n3 and linearizing chain
- Rebase a2a_v1 migration onto d9e0f1a2b3c4 for single head

Service refactoring:
- Extract shared A2A transport dispatch into a2a_dispatcher.py
  (resolve_a2a_auth, dispatch_a2a_transport, prepare_rpc_params,
  build_dispatch_headers) used by both a2a_service and tool_service
- Move _get_team_name, _batch_get_team_names to BaseService
- Unify _check_*_access into BaseService.check_item_access (static)
- Remove duplicate _apply_visibility_filter from A2AAgentService
- Update A2A_VERSION_HEADER from "0.3" to "1.0"
- Add proto compat shim for a2a-sdk 0.3.x descriptor pool collision

Bug fixes:
- Fix A2A response serialization using str() instead of orjson.dumps(),
  which produced Python repr (None/False/single quotes) instead of
  valid JSON (null/false/double quotes)

Architecture docs:
- ADR-0041: A2A v1.0 RC1 migration strategy
- ADR-0042: Virtual servers as multi-protocol endpoints
- ADR-0043: Tenant vs team separation
- A2A v1.0 architecture overview
- Update multitenancy docs for tenant/team distinction

Closes #2547

Signed-off-by: Jonathan Springer <jps@s390x.com>
- Add version-aware outbound dispatch: v0.3 agents receive v0.3
  method names (slash-style) and kind-based Part format automatically
  based on their protocol_version field
- Add MCPGATEWAY_A2A_V1_COMPAT_MODE config flag (default true) to
  gate acceptance of v0.3 inbound formats; when false, rejects
  legacy content field, kind discriminator, and slash-style methods
- Add compat telemetry: structured DEBUG logs when v0.3 inbound
  normalization or outbound conversion paths are exercised
- Migrate all internal service methods to v1.0 PascalCase
  (SendMessage, GetTask, ListTasks, etc.) — outbound conversion
  handles the translation for v0.3 agents
- Update admin test payload builder to v1.0 format (no kind on
  parts, SendMessage method)
- Extract A2A exception hierarchy into a2a_errors.py to break
  circular imports between a2a_service and a2a_dispatcher; re-export
  from a2a_service for backward compatibility
- Mock _discover_agent_card in register_agent tests to eliminate
  20-second network timeouts per test (~100s total savings)

Closes #2547

Signed-off-by: Jonathan Springer <jps@s390x.com>
…hierarchy

102 new tests covering:
- Backward compat: v0.3 ↔ v1.0 Part normalization roundtrip, method name
  mapping, compat mode gating (46 tests)
- Dispatcher: prepare_rpc_params, dispatch_a2a_transport, build_dispatch_headers,
  dataclass defaults (37 tests)
- Error hierarchy: exception inheritance, re-exports from a2a_service (19 tests)

Signed-off-by: Jonathan Springer <jps@s390x.com>
…ndpoints

Implements Phase 1a/1b of the A2A v1.0 migration plan:
- A2AServerService: generates AgentCards from server metadata, routes
  A2A requests to associated agents, manages server-level task mappings
  via ServerTaskMapping, supports tenant precedence resolution
- a2a_server_router: mounts at /servers/{id}/a2a/* with REST endpoints
  (message:send, message:stream, tasks, tasks/{id}, tasks/{id}:cancel),
  JSON-RPC dispatcher, and agent card discovery (v1/card,
  .well-known/agent-card.json)
- 35 unit tests covering agent card generation, request routing, task
  mapping, tenant resolution, skill-based selection, and discovery

Signed-off-by: Jonathan Springer <jps@s390x.com>
- admin.js: remove v0.3 `kind` fields from message/parts, update
  JSON-RPC method `message/send` to `SendMessage`, update gRPC method
- admin.html: show protocol version below agent type in dynamic table
- admin-ui.test.js: update test assertions for v1.0 format

Signed-off-by: Jonathan Springer <jps@s390x.com>
…ions

- Security: delegate _base_url to get_base_url_with_protocol (SSRF fix)
- Security: use server-derived team_id/owner_email in register_agent
- Security: add access control to get_agent_by_name
- Security: validate URL scheme for HTTP transports in dispatcher
- Security: add identifier validation (_validate_a2a_identifier)
- Correctness: replace get_for_update with direct SELECT in read paths
- Correctness: remove db.close() on injected sessions
- Correctness: add params dict validation in JSON-RPC dispatcher
- Correctness: add db.commit() to _batch_get_team_names
- Correctness: use agent_id instead of agent_name in ServerTaskMapping
- Correctness: remap task IDs in list_tasks response
- Correctness: filter by DbServer.enabled in server queries
- Performance: set explicit httpx timeout for streaming connections
- Robustness: add recursion depth limit to _extract_task_payloads
- Robustness: redact JSON-RPC error data from client responses
- Migration: use batch_alter_table for SQLite compat in downgrade
- Migration: use .mappings().all() for named column access
- Admin: remove server-side agentType formatting (JS handles it)
- Docstrings: resolve ~300 DAR violations across 8 files (~87 functions)
- Docstrings: remove 6 DAR per-file ignores from .flake8 (net reduction)
- Imports: move re import and error re-exports to proper sections (E402)
- Tests: add router tests (19), dispatcher auth tests (14),
  identifier validation tests (11), update ~35 existing test mocks

Signed-off-by: Jonathan Springer <jps@s390x.com>
Resolve all blocking (B1-B3), major (M1-M11), and suggestion (S1-S4)
issues identified in PR #3150 code review, spanning security, correctness,
migration safety, and test coverage.

Source fixes:
- Fix route path collision: register a2a_server_router before server_router
- Add JSON-RPC 2.0 version validation and gRPC config_name sanitization
- Fix SSE error sanitization with auth_ctx.query_params_decrypted
- Add agent_card_override allowlist to prevent unsafe card field injection
- Add thread-safe singleton for A2AServerService with double-checked locking
- Extract shared A2A error-to-HTTP mapping via global exception handlers
- Replace serial agent card discovery and list_tasks with asyncio.gather
- Remove _resolve_tenant dead code and fix circular import naming

Migration fixes:
- Add JSON serialization helper for raw SQL INSERT bind params
- Fix nullable/server_default for timestamp columns across 3 tables
- Add downgrade warning in no-op normalize migration

Tests (+80 new):
- Add security deny-path tests for a2a_server_router and main A2A routes
- Add comprehensive _build_rest_request tests covering all 15 method branches
- Add tests for stream_message, invoke_agent, team visibility, and
  agent lookup failure scenarios

Signed-off-by: Jon Spriggs <github@jonspriggs.co.uk>
Signed-off-by: Jonathan Springer <jps@s390x.com>
…est updates

Rename alembic migrations to fix ordering, update A2A protobuf generated
code, refine schemas and services for v1.0 protocol compliance, and
expand test coverage for server router and service.

Signed-off-by: Jonathan Springer <jps@s390x.com>
@jonpspri
Copy link
Collaborator

Converting to a2a 1.0 via Rust.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a2a Support for A2A protocol enhancement New feature or request epic Large feature spanning multiple issues MUST P1: Non-negotiable, critical requirements without which the product is non-functional or unsafe

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[EPIC][A2A]: A2A Protocol v0.3.0 Full Compliance Implementation

2 participants