feat: support v0.3 agent card parsing in A2ACardResolver#417
feat: support v0.3 agent card parsing in A2ACardResolver#417darrelmiller wants to merge 1 commit into
Conversation
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>
There was a problem hiding this comment.
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.
| 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}"); | ||
| } |
There was a problem hiding this comment.
Relying on ex.Message.Contains(...) to detect missing properties is fragile and error-prone for two main reasons:
- 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.
- Runtime Stability: The exact wording of
JsonExceptionmessages 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;
}| 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", |
There was a problem hiding this comment.
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",
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:
The upcast:
Testing