Skip to content

.NET: Split A2A endpoint mapping into protocol-specific methods#5413

Merged
SergeyMenshykh merged 2 commits intomicrosoft:a2a-agent-migrationfrom
SergeyMenshykh:a2a-hosting-update-v2
Apr 22, 2026
Merged

.NET: Split A2A endpoint mapping into protocol-specific methods#5413
SergeyMenshykh merged 2 commits intomicrosoft:a2a-agent-migrationfrom
SergeyMenshykh:a2a-hosting-update-v2

Conversation

@SergeyMenshykh
Copy link
Copy Markdown
Member

Summary

Split MapA2A into protocol-specific methods (MapA2AHttpJson, MapA2AJsonRpc) that are more extensible than extending the A2AProtocolBinding flags enum. This allows exposing the same A2A server with different protocols under different paths if necessary, which was impossible with the previous solution.

Changes

  • Rename A2AHostingOptionsA2AServerRegistrationOptions
  • Add A2AServerServiceCollectionExtensions to separate DI registration from endpoint mapping
  • Remove A2AProtocolBinding enum and AIAgentExtensions (consolidated into the new extensions)

…onExtensions

- 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>
@moonbox3 moonbox3 added the .NET label Apr 21, 2026
@SergeyMenshykh SergeyMenshykh self-assigned this Apr 21, 2026
@SergeyMenshykh SergeyMenshykh added the a2a Issue relates to A2A label Apr 21, 2026
@SergeyMenshykh SergeyMenshykh moved this to In Progress in Agent Framework Apr 21, 2026
@SergeyMenshykh SergeyMenshykh moved this from In Progress to In Review in Agent Framework Apr 21, 2026
@SergeyMenshykh SergeyMenshykh marked this pull request as ready for review April 22, 2026 09:38
@SergeyMenshykh SergeyMenshykh merged commit c54483f into microsoft:a2a-agent-migration Apr 22, 2026
3 checks passed
@github-project-automation github-project-automation Bot moved this from In Review to Done in Agent Framework Apr 22, 2026
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

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: 91%

✓ Security Reliability

This PR cleanly refactors A2A server lifecycle from inline creation during endpoint mapping into proper DI registration via AddA2AServer, with separate MapA2AHttpJson/MapA2AJsonRpc endpoint methods. All public entry points have comprehensive input validation (endpoints, agentName, path). The CreateA2AServer factory is properly scoped as a keyed singleton. No injection risks, secrets, resource leaks, or unhandled failure modes were found. The error message when AddA2AServer is forgotten is clear and actionable. External A2A SDK types (A2AServer, ChannelEventNotifier, InMemoryTaskStore) could not be verified for IDisposable, but since they are registered as DI singletons, any disposable semantics would be handled by the container at shutdown.

✓ Test Coverage

This PR cleanly separates A2A server registration (AddA2AServer) from endpoint mapping (MapA2AHttpJson/MapA2AJsonRpc). The existing A2AgentHandler tests were properly refactored to use the new CreateHandler helper. However, there are several test coverage gaps: the AddA2AServer(IServiceCollection, AIAgent, ...) overload has zero test coverage despite having distinct validation logic, the MapA2AJsonRpc overloads lack argument-validation tests that exist for their MapA2AHttpJson counterparts, the AIAgent-parameter overloads of both MapA2AHttpJson and MapA2AJsonRpc are untested, and the old test verifying the configureOptions callback is actually invoked was removed without replacement.

✗ Design Approach

The refactor improves separation between server registration and transport mapping, but it over-corrects by collapsing all A2A hosting into a keyed-DI A2AServer lookup model. That changes the semantics of the public AIAgent-based API in a way that is broader than a cleanup: callers that already have an AIAgent instance, or that need endpoint-specific behavior, lose direct composition and are now forced through prior DI registration keyed only by agent name. I found one blocking design issue around that regression; otherwise the rest of the change is internally consistent.

Suggestions

  • Add argument-validation tests for MapA2AJsonRpc that mirror the existing MapA2AHttpJson tests. Currently only null-endpoints is tested for JsonRpc; missing coverage for null agentName, empty agentName, null path, whitespace path, and null agentBuilder.
  • If the goal is to reuse a single A2AServer across transports, keep the AddA2AServer + name/builder-based mapping as an opt-in DI path, but retain AIAgent overloads that build from the supplied instance so the public API still supports non-DI and already-materialized agents.

Automated review by SergeyMenshykh's agents

moonbox3 pushed a commit to moonbox3/agent-framework that referenced this pull request Apr 23, 2026
…ft#5423)

* update a2a agent to the latest a2a sdk (microsoft#5257)

* Move A2A samples from 04-hosting to 02-agents (microsoft#5267)

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>

* .NET: Fix stream reconnection for A2AAgent (microsoft#5275)

* 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>

* .NET: Use IA2AClientFactory to create A2AClient (microsoft#5277)

* 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 (microsoft#5363)

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

* .NET: Split A2A endpoint mapping into protocol-specific methods (microsoft#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>

* Remove unnecessary using directive in AgentWebChat.AgentHost

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

* restore AsyncEnumerable package version

* address copilot initial feedback

* address automated code review and formatting issues

* fix formatting issues

* Add DI wiring verification tests for AddA2AServer

Add three tests to A2AServerServiceCollectionExtensionsTests that verify
custom keyed services are actually wired through to the A2AServer, not
just that the server resolves non-null:

- Custom IAgentHandler: verifies the keyed handler is invoked when
  processing a SendMessageRequest instead of the default A2AAgentHandler.
- Custom AgentSessionStore (no handler): verifies the keyed session
  store's GetSessionAsync is called during request processing when no
  custom handler is registered.
- Default stores end-to-end: verifies the InMemoryAgentSessionStore and
  InMemoryTaskStore defaults successfully process a request. Uses a new
  CreateAgentMockForRequests helper that includes SerializeSessionCoreAsync
  setup needed by InMemoryAgentSessionStore.

All tests call A2AServer.SendMessageAsync directly (no HTTP layer needed)
and use CancellationToken timeouts to guard against hangs.

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
pull Bot pushed a commit to TheTechOddBug/agent-framework that referenced this pull request Apr 23, 2026
* update a2a agent to the latest a2a sdk (microsoft#5257)

* Move A2A samples from 04-hosting to 02-agents (microsoft#5267)

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>

* .NET: Fix stream reconnection for A2AAgent (microsoft#5275)

* 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>

* .NET: Use IA2AClientFactory to create A2AClient (microsoft#5277)

* 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 (microsoft#5363)

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

* .NET: Split A2A endpoint mapping into protocol-specific methods (microsoft#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>

* Remove unnecessary using directive in AgentWebChat.AgentHost

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

* restore AsyncEnumerable package version

* address copilot initial feedback

* address automated code review and formatting issues

* fix formatting issues

* Add streaming support to A2A agent handler

Add HandleNewMessageStreamingAsync to A2AAgentHandler that routes
StreamingResponse requests through RunStreamingAsync, enqueuing an A2A
Message for each AgentResponseUpdate.

Add MessageConverter.ToParts(AgentResponseUpdate) extension to convert
streaming update contents to A2A Parts with unsupported-content filtering.

Add CreateMessageFromUpdate to map AgentResponseUpdate to A2A Message.

Add 16 new tests covering the streaming path and converter.

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

* Add streaming edge-case tests for A2AAgentHandler

Add two tests covering gaps in the streaming path:

- ExecuteAsync_Streaming_WhenNoUpdates_EnqueuesNoMessagesAndSavesSessionAsync:
  Verifies that when RunStreamingAsync yields an empty async enumerable,
  no messages are enqueued and only SaveSessionAsync runs.

- ExecuteAsync_Streaming_CancellationTokenIsPropagatedToRunStreamingAsyncAsync:
  Verifies that the CancellationToken from ExecuteAsync is propagated
  through to the inner agent's RunCoreStreamingAsync call.

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

* address copilot comments

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a2a Issue relates to A2A .NET

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants