Skip to content

feat(server): restore FastAPI /docs visibility for A2A routes#1024

Open
martimfasantos wants to merge 4 commits intoa2aproject:mainfrom
martimfasantos:feat/fastapi-routes-helpers
Open

feat(server): restore FastAPI /docs visibility for A2A routes#1024
martimfasantos wants to merge 4 commits intoa2aproject:mainfrom
martimfasantos:feat/fastapi-routes-helpers

Conversation

@martimfasantos
Copy link
Copy Markdown
Contributor

@martimfasantos martimfasantos commented Apr 27, 2026

Summary

  • Introduces src/a2a/server/routes/helpers/ — a new subpackage of transport-specific route helpers mirroring the layout of client/transports/
  • add_a2a_routes_to_fastapi() re-registers A2A Starlette routes as APIRoute instances so FastAPI's OpenAPI generator picks them up and they appear in /docs and /openapi.json
  • Each endpoint group is tagged (A2A: Agent Card, A2A: JSON-RPC, A2A: REST) and annotated with proto-derived request-body schemas so Swagger UI's Try it out panel renders a typed, editable payload
  • The required A2A-Version: 1.0 header is declared on every dispatcher route so Swagger pre-fills it and callers aren't blocked by the @validate_version guard

Why

In v0.3, A2AFastAPIApplication was a first-class FastAPI subclass, so all endpoints were automatically visible in /docs (see #280).

The v1.0 rewrite dropped that wrapper in favour of bare Starlette route factories (create_agent_card_routes, create_jsonrpc_routes, create_rest_routes). These return starlette.routing.Route objects; FastAPI's OpenAPI generator only enumerates fastapi.routing.APIRoute instances, so every A2A endpoint silently vanished from /docs and /openapi.json.

This PR restores that capability without requiring callers to switch frameworks or restructure their apps — a single helper function wraps and enriches the routes in place.

Additional improvements over the v0.3 approach:

  • Rich request-body schemas generated from proto descriptors (google.protobuf.descriptor) rather than Pydantic models, matching the proto-first v1.0 types
  • Proto oneof → JSON Schema oneOf so Part.content variants (text, raw, url, data) are correctly mutually exclusive in Swagger — prevents -32602 Invalid params errors from the auto-generated sample payload
  • JSON-RPC params as oneOf over all 11 method types — every method is individually inspectable from a single endpoint row
  • A2A-Version header pre-filled to 1.0 on all dispatcher routes — removes the -32009 version error that would otherwise block every "Try it out" call
  • Scalable layouthelpers/ mirrors client/transports/ with one file per integration target; adding a new framework is a single new file that imports from _proto_schema.py and jsonrpc.py

Changes

src/a2a/server/routes/helpers/
  __init__.py          re-exports add_a2a_routes_to_fastapi
  _proto_schema.py     proto → JSON Schema utilities (field_schema, message_schema, REST_BODY_TYPES)
  jsonrpc.py           JSON-RPC specifics (METHOD_TYPES, DESCRIPTION, envelope_schema)
  fastapi.py           add_a2a_routes_to_fastapi + _A2ARoute

src/a2a/server/routes/__init__.py   exports add_a2a_routes_to_fastapi
src/a2a/server/routes/agent_card_routes.py   docstring on _get_agent_card endpoint

tests/server/routes/helpers/
  test_proto_schema.py   field_schema, message_schema, oneof handling, REST_BODY_TYPES
  test_jsonrpc.py        envelope_schema, METHOD_TYPES, DESCRIPTION
  test_fastapi.py        OpenAPI tags, dispatch, tenant mount, schema injection, version header

Usage

from fastapi import FastAPI
from a2a.server.routes import (
    add_a2a_routes_to_fastapi,
    create_agent_card_routes,
    create_jsonrpc_routes,
    create_rest_routes,
)

app = FastAPI()
add_a2a_routes_to_fastapi(
    app,
    agent_card_routes=create_agent_card_routes(agent_card),
    jsonrpc_routes=create_jsonrpc_routes(request_handler, rpc_url='/'),
    rest_routes=create_rest_routes(request_handler),
)

Validation

  • uv run pytest tests/server/routes/helpers/ -v — 25 tests, all passing

Screenshots

Before
image

After
image
image

Continues #280 🦕

@martimfasantos martimfasantos requested a review from a team as a code owner April 27, 2026 17:32
@martimfasantos martimfasantos force-pushed the feat/fastapi-routes-helpers branch from 40cbe9c to 697db0d Compare April 27, 2026 17:33
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 27, 2026

🧪 Code Coverage (vs main)

⬇️ Download Full Report

Base PR Delta
src/a2a/server/routes/helpers/_proto_schema.py (new) 94.23%
src/a2a/server/routes/helpers/fastapi.py (new) 91.78%
src/a2a/server/routes/helpers/jsonrpc.py (new) 100.00%
Total 93.00% 93.02% 🟢 +0.01%

Generated by coverage-comment.yml

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the add_a2a_routes_to_fastapi utility, which enables A2A endpoints (Agent Card, JSON-RPC, and REST) to be correctly registered and documented within FastAPI's auto-generated OpenAPI schema. The implementation includes a new Protobuf-to-JSON-Schema conversion module to provide detailed request body documentation derived from protocol definitions. Feedback was provided regarding the scalability of the oneOf generation logic in _proto_schema.py, which currently creates a Cartesian product of variants for messages containing multiple oneofs, potentially leading to an exponential explosion of schema variants.

Comment thread src/a2a/server/routes/helpers/_proto_schema.py Outdated
…d inline component install

Replace O(∏fields) Cartesian product oneof generation with O(∑fields)
allOf+oneOf-per-group approach, and inline _install_components into
add_a2a_routes_to_fastapi to remove a single-use wrapper function.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant