feat(a2a): sign served AgentCards and verify consumed ones - #3111
Open
vvlrff wants to merge 5 commits into
Open
Conversation
vvlrff
marked this pull request as draft
July 28, 2026 19:59
…ds and enhance signing capabilities
vvlrff
marked this pull request as ready for review
July 28, 2026 20:13
Remove `CardSigner` and `CardVerifier` from the top-level `ag2.a2a` package exports to clean up the public API. Update test imports to reference these classes from their correct submodules.
Add comprehensive user guides for JWS-based card signing and verification. This includes: - A new `card_signing.mdx` guide explaining the "why" and "how" of securing AgentCards. - Updates to the A2A overview, client, and server documentation to reflect new signing/verification capabilities. - Documentation for the new `A2ACardSignatureError` exception. - Updates to the documentation sidebar and navigation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why are these changes needed?
A2A v1.0 defines JWS signatures on the
AgentCard(thesignaturesfield), and thea2a-sdkships the primitives for it (create_agent_card_signer/create_signature_verifier). ag2 had no way to reach them: a server could not sign thecards it served, and a client had no way to check the signature on a card it consumed, so
card discovery was trust-on-first-fetch. This PR wires both sides through, opt-in and
off by default.
Server — signing what we serve.
A2AServer(card_signer=...)signs the public cardin all three transports (JSON-RPC, REST, gRPC) and signs the extended card when one is
configured. The subtlety is card modifiers: they mutate the card per request, and
mutating a signed card invalidates the JWS, so
wrap_card_modifier/wrap_extended_card_modifierre-sign the modifier's output instead of letting a stalesignature ship. Signing happens after
clone_card_with_capabilities, so thecapability flags the server derives are inside the signed payload.
Client — verifying what we consume.
A2AConfig(card_signature_verifier=...)(alsoaccepted directly by
A2AClient) verifies every card the client takes in: the fetchedcard, a
preset_cardsupplied by the caller, and the extended card. A failure raisesthe new
A2ACardSignatureError, which carries the card URL plus asourcefield(
"fetched agent card","preset agent card","extended agent card") so the failingcard is identifiable when several are in play. The SDK's
SignatureVerificationErroriswrapped so callers only need to catch ag2's error type.
Both knobs default to
None, which is a no-op — existing setups are unaffected. Notethe asymmetry once a verifier is set: the SDK verifier rejects unsigned cards, so
configuring verification makes signatures mandatory rather than best-effort. That is
deliberate (an attacker must not be able to downgrade to "no signature"), and it is
documented on
A2AConfig.The two type aliases are
ag2.a2a.transports._common.CardSignerandag2.a2a.client.CardVerifier. Neither is re-exported fromag2.a2a, so callers pass theSDK's callables positionally and annotate against the plain shapes
(
Callable[[AgentCard], AgentCard]andCallable[[AgentCard], None]) — worth a reviewercall on whether
CardSignershould become public rather than living in a private module.Dependency note. Signing is implemented in the SDK on top of PyJWT, which
a2a-sdkships behind its own
signingextra — ag2'sa2aextra (a2a-sdk>=1.1.0,<2) does notpull it in. Using the feature therefore needs
pip install "a2a-sdk[signing]"alongsideag2[a2a]. It passes in CI only becausemcpdepends onpyjwt[crypto]unconditionallyand is present in the dev environment.
Tests.
test/a2a/test_card_signing.pycovers the feature end to end with realEC P-256 keypairs rather than fakes: served card is signed / stays unsigned without a
signer, a verifying client completes a real conversation, both modifier flavours re-sign
their output, and four rejection paths — unsigned card against a verifier, wrong key,
tampered
preset_card, unsigned extended card.Not included in this diff yet, called out so it is not mistaken for done: the A2A
user-guide pages under
website/docs/user-guide/a2a/. Acard_signing.mdxpage pluscross-references is written locally and will be pushed as a follow-up commit on this
branch; until then the behaviour is documented in the
A2AConfigandA2AServerdocstrings only.
Related issue number
None — no existing issue tracks A2A card signing.
Checks
AI assistance