Skip to content

feat: support v0.3 agent card parsing in A2ACardResolver#417

Open
darrelmiller wants to merge 1 commit into
mainfrom
v03-card-upcast
Open

feat: support v0.3 agent card parsing in A2ACardResolver#417
darrelmiller wants to merge 1 commit into
mainfrom
v03-card-upcast

Conversation

@darrelmiller

Copy link
Copy Markdown
Collaborator

Summary

Adds backward-compatible parsing of v0.3 agent cards in A2ACardResolver.

Problem

v0.3 agents expose agent cards without supportedInterfaces (a required field in v1.0), causing deserialization failures when a v1.0 client resolves a v0.3 agent's card.

Solution

A2ACardResolver now performs two-pass deserialization:

  1. First attempts standard v1.0 parsing
  2. On JsonException (missing required properties), falls back to UpcastV03AgentCard() which maps v0.3 properties (url, preferredTransport, protocolVersion, additionalInterfaces) into a valid v1.0 AgentCard

The upcast:

  • Synthesizes supportedInterfaces from url + preferredTransport
  • Preserves additionalInterfaces entries
  • Adds sensible defaults for defaultInputModes/defaultOutputModes
  • Logs a warning when upcast is triggered

Testing

  • All existing tests continue to pass
  • ITK interoperability tests pass (4/5 scenarios)

When fetching an agent card, if deserialization fails due to missing v1.0
required properties (supportedInterfaces, skills, etc.), attempt to parse
it as a v0.3 card and upcast to a valid v1.0 AgentCard in memory.

v0.3 cards have a top-level 'url' and optional 'preferredTransport' instead
of 'supportedInterfaces'. The upcast maps these to a single AgentInterface
entry with the appropriate protocol binding (defaulting to JSONRPC).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

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

Copy link
Copy Markdown
Contributor

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 a fallback mechanism in A2ACardResolver to support parsing older v0.3 agent cards and upcasting them to v1.0 when v1.0 deserialization fails. It reads the response as a byte array, attempts standard deserialization, and falls back to manual parsing via UpcastV03AgentCard upon encountering specific JsonException errors. Feedback highlights two key areas for improvement: first, relying on ex.Message.Contains(...) for exception filtering is fragile due to localization and runtime changes, and catching any JsonException to attempt the upcast is recommended instead; second, calling GetString() on non-string JSON properties during manual parsing can throw an exception, so verifying ValueKind beforehand is advised.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +79 to +88
catch (JsonException ex) when (ex.Message.Contains("supportedInterfaces") ||
ex.Message.Contains("skills") ||
ex.Message.Contains("defaultInputModes") ||
ex.Message.Contains("defaultOutputModes"))
{
// The card is missing v1.0 required properties — attempt v0.3 upcast
_logger.AttemptingV03AgentCardUpcast(ex);
return UpcastV03AgentCard(bytes)
?? throw new A2AException($"Failed to parse JSON: {ex.Message}");
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Relying on ex.Message.Contains(...) to detect missing properties is fragile and error-prone for two main reasons:

  1. Localization: Exception messages in .NET can be localized based on the system's UI culture. On non-English systems, the message will not contain these English strings, causing the fallback to be skipped entirely.
  2. Runtime Stability: The exact wording of JsonException messages is an implementation detail of the .NET runtime and can change between versions or patches.

Instead, catch any JsonException, attempt the upcast, and if the upcast succeeds, return the upcast card. If it fails (returns null), rethrow the original exception (throw;) to let the outer catch block handle it.

            catch (JsonException ex)
            {
                // The card is missing v1.0 required properties — attempt v0.3 upcast
                var upcastCard = UpcastV03AgentCard(bytes);
                if (upcastCard is not null)
                {
                    _logger.AttemptingV03AgentCardUpcast(ex);
                    return upcastCard;
                }

                throw;
            }

Comment on lines +178 to +180
Name = root.TryGetProperty("name", out var name) ? name.GetString() ?? "" : "",
Description = root.TryGetProperty("description", out var desc) ? desc.GetString() ?? "" : "",
Version = root.TryGetProperty("version", out var ver) ? ver.GetString() ?? "0.3" : "0.3",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

If any of these properties exist in the JSON but are not strings (e.g., they are numbers, booleans, or objects), calling GetString() will throw an InvalidOperationException. It is safer to verify that the property value kind is JsonValueKind.String before calling GetString().

            Name = root.TryGetProperty("name", out var name) && name.ValueKind == JsonValueKind.String ? name.GetString() ?? "" : "",
            Description = root.TryGetProperty("description", out var desc) && desc.ValueKind == JsonValueKind.String ? desc.GetString() ?? "" : "",
            Version = root.TryGetProperty("version", out var ver) && ver.ValueKind == JsonValueKind.String ? ver.GetString() ?? "0.3" : "0.3",

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