Skip to content

.NET: Migrate A2A agent and hosting to A2A SDK v1 - #5422

Closed
SergeyMenshykh wants to merge 7 commits into
microsoft:mainfrom
SergeyMenshykh:a2a-agent-migration
Closed

.NET: Migrate A2A agent and hosting to A2A SDK v1#5422
SergeyMenshykh wants to merge 7 commits into
microsoft:mainfrom
SergeyMenshykh:a2a-agent-migration

Conversation

@SergeyMenshykh

@SergeyMenshykhSergeyMenshykh commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Migrate the .NET A2A agent and hosting packages from A2A SDK 0.3.4-preview to 1.0.0-preview2. This is a comprehensive update that reworks the agent, hosting, and extension APIs to align with the v1 SDK, adds SSE stream reconnection support, and reorganizes samples.

What Changed

Core Agent (Microsoft.Agents.AI.A2A)

  • Refactored A2AAgent for v1 SDK APIs — updated message/task handling and streaming logic.
  • Added SSE stream reconnection support using continuation tokens, allowing recovery from stream interruptions without losing progress.
  • Updated A2AAgentCardExtensions and A2ACardResolverExtensions to use A2AClientFactory.Create() for client creation and accept an optional A2AClientOptions for protocol binding preference.
  • Updated A2AClientExtensions to operate on IA2AClient interface instead of concrete A2AClient.

Hosting (Microsoft.Agents.AI.Hosting.A2A / .AspNetCore)

  • Added: A2AAgentHandler — new handler aligned with v1 SDK hosting model.
  • Added: A2AServerRegistrationOptions — configuration for server registration.
  • Added: A2AServerServiceCollectionExtensions — consolidated DI registration for A2A servers.
  • Added: A2AEndpointRouteBuilderExtensions — protocol-specific endpoint mapping methods.
  • Removed: EndpointRouteBuilderExtensions (replaced by A2AEndpointRouteBuilderExtensions).
  • Removed: AIAgentExtensions (logic consolidated into A2AServerServiceCollectionExtensions).

Samples

  • Moved A2A samples from samples/04-hosting/A2A/ to samples/02-agents/A2A/.
  • New: A2AAgent_ProtocolSelection — demonstrates protocol selection.
  • New: A2AAgent_StreamReconnection — demonstrates SSE stream reconnection.
  • Updated A2AClientServer, AgentWebChat, and A2AAgent_PollingForTaskCompletion to use the new APIs.

SergeyMenshykhand others added 6 commits April 15, 2026 11:08
Move the A2A sample projects (A2AAgent_AsFunctionTools and
A2AAgent_PollingForTaskCompletion) from samples/04-hosting/A2A/ to
samples/02-agents/A2A/ to better align with the sample directory
structure. Update solution file and samples README accordingly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add SSE stream reconnection support to A2AAgent
Implement automatic reconnection for SSE streams that disconnect mid-task,
using the Last-Event-ID header to resume from where the stream left off.
Changes:
- Add InvokeStreamingWithReconnectAsync method to A2AAgent with configurable
max retries and delay between attempts
- Add new log messages for reconnection events
- Add A2AAgent_StreamReconnection sample demonstrating the feature
- Update existing polling sample to use simplified SendMessageAsync API
- Add unit tests for stream reconnection logic
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* address comments
* Address PR review feedback
- Dispose SSE enumerator before GetTaskAsync fallback to release HTTP connection
- Wrap StreamWriter in using blocks with leaveOpen:true and explicit UTF-8 encoding
- Print update.Text instead of update object in stream reconnection sample
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Refactor A2A extensions to use IA2AClientFactory and add ProtocolSelection sample
- Update A2AAgentCardExtensions to accept IA2AClientFactory instead of A2AClientOptions
- Update A2ACardResolverExtensions to accept IA2AClientFactory
- Update A2AClientExtensions to accept IA2AClientFactory
- Update A2AAgent to use IA2AClientFactory for client creation
- Add A2AAgent_ProtocolSelection sample demonstrating protocol selection
- Add comprehensive unit tests for all changes
- Update README files with new sample reference
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Reorder params: options before loggerFactory in A2A extensions
Move A2AClientOptions parameter before ILoggerFactory in AsAIAgent
and GetAIAgentAsync extension methods to follow the repo convention
of keeping LoggerFactory and CancellationToken as the last parameters.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* .NET: Migrate A2A hosting to A2A SDK v1
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* remove unused agent card
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…osoft#5413)
* .NET: Refactor A2A hosting registration into A2AServerServiceCollectionExtensions
- Rename A2AHostingOptions to A2AServerRegistrationOptions
- Move server registration logic from A2AEndpointRouteBuilderExtensions
and AIAgentExtensions into new A2AServerServiceCollectionExtensions
- Remove A2AProtocolBinding and AIAgentExtensions (consolidated)
- Update samples and tests to use the new registration API
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* address copilot comments
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings April 22, 2026 09:57
@moonbox3Evan Mattson (moonbox3) added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs .NET Usage: [Issues, PRs], Target: .Net labels Apr 22, 2026
@SergeyMenshykhSergeyMenshykh self-assigned this Apr 22, 2026
@SergeyMenshykhSergeyMenshykh moved this to In Progress in Agent FrameworkApr 22, 2026
@SergeyMenshykhSergeyMenshykh moved this from In Progress to In Review in Agent FrameworkApr 22, 2026
@SergeyMenshykh

Copy link
Copy Markdown
ContributorAuthor

Copilot resolve the merge conflicts in this pull request

CopilotAI 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.

Pull request overview

Migrates the .NET A2A agent and hosting layers to A2A SDK v1 (1.0.0-preview2), updating agent/task/streaming models, introducing a new DI-first hosting registration + endpoint mapping approach, and reorganizing/expanding samples and tests to match the new SDK surface.

Changes:

  • Updated A2A agent/client integration to v1 SDK types (IA2AClient, SendMessageRequest/Response, StreamResponse) and added stream reconnection fallback behavior.
  • Reworked A2A hosting to a server-registration model (AddA2AServer) with protocol-specific endpoint mapping (MapA2AHttpJson, MapA2AJsonRpc) and added a new A2AAgentHandler.
  • Moved/added A2A samples under samples/02-agents/A2A/ and refreshed/expanded unit tests; bumped A2A + related dependency versions.

Reviewed changes

Copilot reviewed 50 out of 54 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.csMigrates agent runtime + streaming to v1 SDK request/response models and adds Subscribe-to-task fallback logic.
dotnet/src/Microsoft.Agents.AI.A2A/A2AAgentLogMessages.csAdds a warning log for subscribe→get-task fallback on UnsupportedOperation.
dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AClientExtensions.csSwitches extension surface to IA2AClient.
dotnet/src/Microsoft.Agents.AI.A2A/Extensions/ChatMessageExtensions.csUpdates A2A message conversion to v1 Message/Role types.
dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AAgentCardExtensions.csUses A2AClientFactory.Create(...) and adds optional A2AClientOptions for binding selection.
dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2ACardResolverExtensions.csAdds optional A2AClientOptions passthrough when resolving agents from cards.
dotnet/src/Microsoft.Agents.AI.A2A/Microsoft.Agents.AI.A2A.csprojTargets $(TargetFrameworksCore) for the A2A library.
dotnet/src/Microsoft.Agents.AI.Hosting.A2A/A2AAgentHandler.csIntroduces a v1-aligned agent handler emitting A2A events for messages/tasks.
dotnet/src/Microsoft.Agents.AI.Hosting.A2A/A2ARunDecisionContext.csUpdates run-decision context to wrap v1 RequestContext.
dotnet/src/Microsoft.Agents.AI.Hosting.A2A/AgentRunMode.csFixes background decision default behavior and updates docs/behavior.
dotnet/src/Microsoft.Agents.AI.Hosting.A2A/Converters/MessageConverter.csUpdates converter to accept v1 SendMessageRequest.
dotnet/src/Microsoft.Agents.AI.Hosting.A2A/A2AServerRegistrationOptions.csAdds options container for keyed server registration.
dotnet/src/Microsoft.Agents.AI.Hosting.A2A/A2AServerServiceCollectionExtensions.csAdds DI registration extensions for keyed A2AServer creation.
dotnet/src/Microsoft.Agents.AI.Hosting.A2A.AspNetCore/A2AEndpointRouteBuilderExtensions.csAdds protocol-specific endpoint mapping methods (HTTP+JSON, JSON-RPC).
dotnet/src/Microsoft.Agents.AI.Hosting.A2A.AspNetCore/EndpointRouteBuilderExtensions.csRemoves legacy endpoint-mapping extensions.
dotnet/src/Microsoft.Agents.AI.Hosting.A2A.AspNetCore/Microsoft.Agents.AI.Hosting.A2A.AspNetCore.csprojAdds RT0002 suppression and normalizes formatting/props.
dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Microsoft.Agents.AI.A2A.UnitTests.csprojAligns test TFMs with $(TargetFrameworksCore).
dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AClientExtensionsTests.csAdds interface-based client extension coverage.
dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/ChatMessageExtensionsTests.csUpdates assertions for v1 parts/content cases.
dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2ACardResolverExtensionsTests.csUpdates card URL model and verifies protocol binding option passthrough.
dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAgentCardExtensionsTests.csAdds protocol selection test cases and updates JSON-RPC response shaping.
dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AArtifactExtensionsTests.csUpdates artifact parts creation to v1 Part helpers.
dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAgentTaskExtensionsTests.csUpdates task/status types and artifact part creation for v1.
dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAIContentExtensionsTests.csUpdates content-to-part mapping assertions for v1.
dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/A2AAgentHandlerTests.csAdds extensive handler tests for message/task flows and run modes.
dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/AgentRunModeTests.csAdds equality/to-string/validation coverage for AgentRunMode.
dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/Converters/MessageConverterTests.csUpdates converter test inputs to v1 request/message types.
dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/A2AEndpointRouteBuilderExtensionsTests.csUpdates tests to new endpoint mapping + DI server registration model.
dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/AIAgentExtensionsTests.csRemoves tests for removed legacy AIAgentExtensions surface.
dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/A2AIntegrationTests.csRemoves legacy integration test tied to old mapping/card model.
dotnet/samples/README.mdUpdates hosting samples description after moving A2A samples.
dotnet/samples/02-agents/README.mdAdds entry for new A2A samples folder.
dotnet/samples/02-agents/A2A/README.mdFixes relative link and lists new A2A samples.
dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTools/*Adds new A2A agent-as-tools sample under 02-agents.
dotnet/samples/02-agents/A2A/A2AAgent_PollingForTaskCompletion/*Updates polling sample for v1 APIs and background responses.
dotnet/samples/02-agents/A2A/A2AAgent_StreamReconnection/*Adds stream reconnection sample using continuation tokens.
dotnet/samples/02-agents/A2A/A2AAgent_ProtocolSelection/*Adds protocol binding selection sample via A2AClientOptions.
dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/A2AAgentClient.csUpdates client code to v1 response union (PayloadCase).
dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/Program.csUpdates hosting setup to AddA2AServer + MapA2AHttpJson.
dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/Program.csMigrates sample server to new registration/mapping model and updated agent card interfaces.
dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/HostAgentFactory.csUpdates generated agent cards to use SupportedInterfaces and configurable URLs.
dotnet/samples/05-end-to-end/A2AClientServer/A2AClient/Program.csSimplifies output printing to use AgentResponse.Text.
dotnet/agent-framework-dotnet.slnxMoves A2A samples folder location and adds new sample projects.
dotnet/Directory.Packages.propsBumps A2A/A2A.AspNetCore + related dependency versions for v1.

RawRepresentation = statusUpdateEvent,
Role = ChatRole.Assistant,
AdditionalProperties = taskUpdateEvent.Metadata?.ToAdditionalProperties() ?? [],
FinishReason = MapTaskStateToFinishReason(statusUpdateEvent.Status.State),

CopilotAIApr 22, 2026

Copy link

Choose a reason for hiding this comment

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

TaskStatusUpdateEvent streaming updates currently don’t populate AgentResponseUpdate.ContinuationToken. That makes it impossible for callers to capture a continuation token if the stream begins with status updates (common for long-running tasks), which conflicts with the stream-reconnection behavior described in this PR. Consider setting ContinuationToken from the task id when Status.State is Submitted/Working (same logic used for AgentTask updates).

Suggested change
FinishReason=MapTaskStateToFinishReason(statusUpdateEvent.Status.State),
FinishReason=MapTaskStateToFinishReason(statusUpdateEvent.Status.State),
ContinuationToken=CreateContinuationToken(statusUpdateEvent.TaskId,statusUpdateEvent.Status.State),

Copilot uses AI. Check for mistakes.
Comment on lines +161 to +168
ProtocolBinding = "JSONRPC",
ProtocolVersion = "1.0",
}));

agentInterfaces.AddRange(agentUrls.Select(url => new AgentInterface
{
Url = url,
ProtocolBinding = "HTTP+JSON",

CopilotAIApr 22, 2026

Copy link

Choose a reason for hiding this comment

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

In CreateAgentInterfaces, protocol binding names are hard-coded as "JSONRPC" and "HTTP+JSON". Since the repo already uses ProtocolBindingNames.HttpJson/JsonRpc elsewhere, using those constants here would avoid casing/typo bugs and keep samples consistent with the rest of the codebase.

Suggested change
ProtocolBinding="JSONRPC",
ProtocolVersion="1.0",
}));
agentInterfaces.AddRange(agentUrls.Select(url =>newAgentInterface
{
Url=url,
ProtocolBinding="HTTP+JSON",
ProtocolBinding=ProtocolBindingNames.JsonRpc,
ProtocolVersion="1.0",
}));
agentInterfaces.AddRange(agentUrls.Select(url =>newAgentInterface
{
Url=url,
ProtocolBinding=ProtocolBindingNames.HttpJson,

Copilot uses AI. Check for mistakes.

@github-actionsgithub-actionsBot 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.

Automated Code Review

Reviewers: 3 | Confidence: 85%

✓ Security Reliability

No actionable issues found in this dimension.

✗ Test Coverage

No test additions or updates are visible for any of these. The existing EndpointRouteA2ABuilderExtensionsTests still reference the deleted MapA2A overloads from the removed EndpointRouteBuilderExtensions.cs, so those tests will fail against the new MapA2AHttpJson/MapA2AJsonRpc API. The existing A2AAgentCardExtensionsTests do not cover the new A2AClientOptions parameter added to AsAIAgent. The SubscribeToTaskWithFallbackAsync method contains nuanced async-enumerator disposal and A2AException fallback logic that especially warants dedicated unit tests. The AgentRunMode bug fix (DisallowBackground returning wrong value) is covered by tests in other files in this PR. The new tests have meaningful assertions and appropriate mocking. Minor suggestions: two binding-related tests use overly broad exception assertions, and the handler stub has a minor inconsistency in JSON serialization options. The new A2AgentHandlerTests and AgentRunModeTests provide good baseline coverage for the new handler architecture. However, there are notable coverage regressions compared to the deleted AIAgentExtensionsTests: (1) no test for the positive case where input metadata with values is forwarded as AgentRunOptions.AdditionalProperties, (2) weaker assertions on response metadata that only check key existence rather than values, (3) the deletion of A2AIntegrationTests removes the only HTTP-level test verifying the agent card endpoint, and (4) no tests exercise the streaming path (all tests hardcode StreamingResponse=false). The AgentRunModeTests and endpoint builder tests are thorough for their scope.

✗ Design Approach

The A2A 1.0 migration is mostly coherent, but the new hosting split introduces a discovery mismatch in the AgentWebChat sample. The host now exposes only the HTTP+JSON transport endpoints, while the in-repo web client still resolves agent cards from /v1/card/; that leaves the sample unable to retrieve cards unless discovery is mapped explicitly or the client is changed to use the new endpoint shape. I found two design-level issues. First, the new streaming-continuation behavior appears to treat a task ID as a resumable stream cursor, but the current continuation token shape and the existing A2A agent comments both indicate the protocol does not define replay/resume semantics here, so this can surface duplicate or missing updates. Second, the new card-selection tests lock the client onto SupportedInterfaces and exceptions when that collection is absent, which turns an SDK-shape migration into a client compatibility break for existing AgentCard.Url producers that this repo still uses and tests today. That bakes a misleading abstraction into the contract. I also think the test reshuffle weakens coverage of the HTTP-facing agent-card contract by deleting the only end-to-end assertion around URL population and replacing it with route-mapping smoke tests.

Flagged Issues

  • A2AEndpointRouteBuilderExtensions replaces the deleted EndpointRouteBuilderExtensions.cs, introducing MapA2AHttpJson and MapA2AJsonRpc. The existing EndpointRouteA2ABuilderExtensionsTests still call the now-deleted MapA2A methods (lines 32, 54, 74, 94, 120) and will fail. No tests for the new methods were found.
  • AgentRunMode equality for callback-backed dynamic modes compares only the string _value and ignores the delegate (AgentRunMode.cs:86–93), so behaviorally different policies collapse to the same key/hash. This is a poor contract for a configuration object whose purpose is to carry execution policy.

Suggestions

  • Add tests for the extracted ConvertToAgentResponse(Message) and ConvertToAgentResponse(AgentTask) methods. While indirectly tested through RunAsync, dedicated tests would catch regressions in response mapping, especially ContinuationToken creation for tasks.
  • Prefer SupportedInterfaces when present but keep AgentCard.Url as a compatibility fallback until all card producers have moved to the new shape.
  • Do not expose streaming continuation as supported until the token can encode replay/resume state explicitly, or limit the API to non-streaming GetTask continuation for now.

Automated review by SergeyMenshykh's agents

Assert.Equal(2, message.Metadata.Count);
Assert.True(message.Metadata.ContainsKey("responseKey1"));
Assert.True(message.Metadata.ContainsKey("responseKey2"));
}

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.

This test asserts metadata keys exist (ContainsKey) but doesn't verify the values. The deleted AIAgentExtensionsTests had stronger assertions like Assert.Equal("responseValue1", ...). Consider verifying that metadata values round-trip correctly, not just that keys are present.

@@ -154,17 +158,9 @@ Once the user has deduced what type (knight or knave) both Alice and Bob are, te
// Configure the HTTP request pipeline.
app.UseExceptionHandler();

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.

MapA2AHttpJson only wires up the REST transport routes, but this sample's client still resolves agent cards via A2ACardResolver(..., agentCardPath: "/v1/card/") in AgentWebChat.Web/A2AgentClient.cs:148. The host no longer publishes the discovery route the client expects, so agent-card retrieval will fail. Either preserve discovery mapping here or update host and client together.

@SergeyMenshykh

Copy link
Copy Markdown
ContributorAuthor

Closing to recreate from microsoft:a2a-agent-migration

@github-project-automationgithub-project-automationBot moved this from In Review to Done in Agent FrameworkApr 22, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationUsage: [Issues, PRs], Target: documentation in the code base and learn docs.NETUsage: [Issues, PRs], Target: .Net

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants

@SergeyMenshykh@moonbox3