// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Diagnostics.CodeAnalysis;
using A2A;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Hosting;
using Microsoft.Agents.AI.Hosting.A2A;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Shared.DiagnosticIds;
namespace Microsoft.Extensions.DependencyInjection;
///
/// Provides extension methods for registering A2A server instances in the dependency injection container.
///
[Experimental(DiagnosticIds.Experiments.AIResponseContinuations)]
public static class A2AServerServiceCollectionExtensions
{
///
/// Registers an in the dependency injection container, keyed by the agent name
/// specified in the . This method only registers the server; to expose it
/// as an HTTP endpoint, call one of the MapA2AHttpJson or MapA2AJsonRpc endpoint mapping
/// methods during application startup.
///
/// The agent builder whose name identifies the agent.
/// An optional callback to configure .
/// The for chaining.
public static IHostedAgentBuilder AddA2AServer(this IHostedAgentBuilder agentBuilder, Action? configureOptions = null)
{
ArgumentNullException.ThrowIfNull(agentBuilder);
agentBuilder.ServiceCollection.AddA2AServer(agentBuilder.Name, configureOptions);
return agentBuilder;
}
///
/// Registers an in the dependency injection container, keyed by the specified
/// agent name. This method only registers the server; to expose it as an HTTP endpoint, call one of the
/// MapA2AHttpJson or MapA2AJsonRpc endpoint mapping methods during application startup.
///
/// The host application builder to configure.
/// The name of the agent to create an A2A server for.
/// An optional callback to configure .
/// The for chaining.
public static IHostApplicationBuilder AddA2AServer(this IHostApplicationBuilder builder, string agentName, Action? configureOptions = null)
{
ArgumentNullException.ThrowIfNull(builder);
builder.Services.AddA2AServer(agentName, configureOptions);
return builder;
}
///
/// Registers an in the dependency injection container for the specified
/// instance, keyed by the agent's . This method only
/// registers the server; to expose it as an HTTP endpoint, call one of the MapA2AHttpJson or
/// MapA2AJsonRpc endpoint mapping methods during application startup.
///
/// The host application builder to configure.
/// The agent instance to create an A2A server for.
/// An optional callback to configure .
/// The for chaining.
public static IHostApplicationBuilder AddA2AServer(this IHostApplicationBuilder builder, AIAgent agent, Action? configureOptions = null)
{
ArgumentNullException.ThrowIfNull(builder);
builder.Services.AddA2AServer(agent, configureOptions);
return builder;
}
///
/// Registers an in the dependency injection container, keyed by the specified
/// agent name. This method only registers the server; to expose it as an HTTP endpoint, call one of the
/// MapA2AHttpJson or MapA2AJsonRpc endpoint mapping methods during application startup.
///
/// The service collection to add the A2A server to.
/// The name of the agent to create an A2A server for.
/// An optional callback to configure .
/// The for chaining.
public static IServiceCollection AddA2AServer(this IServiceCollection services, string agentName, Action? configureOptions = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentException.ThrowIfNullOrWhiteSpace(agentName);
A2AServerRegistrationOptions? options = null;
if (configureOptions is not null)
{
options = new A2AServerRegistrationOptions();
configureOptions(options);
}
services.AddKeyedSingleton(agentName, (sp, _) =>
{
var agent = sp.GetRequiredKeyedService(agentName);
return CreateA2AServer(sp, agent, options);
});
return services;
}
///
/// Registers an in the dependency injection container for the specified
/// instance, keyed by the agent's . This method only
/// registers the server; to expose it as an HTTP endpoint, call one of the MapA2AHttpJson or
/// MapA2AJsonRpc endpoint mapping methods during application startup.
///
/// The service collection to add the A2A server to.
/// The agent instance to create an A2A server for.
/// An optional callback to configure .
/// The for chaining.
public static IServiceCollection AddA2AServer(this IServiceCollection services, AIAgent agent, Action? configureOptions = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(agent);
ArgumentException.ThrowIfNullOrWhiteSpace(agent.Name, nameof(agent) + "." + nameof(agent.Name));
A2AServerRegistrationOptions? options = null;
if (configureOptions is not null)
{
options = new A2AServerRegistrationOptions();
configureOptions(options);
}
services.AddKeyedSingleton(agent.Name, (sp, _) => CreateA2AServer(sp, agent, options));
return services;
}
private static A2AServer CreateA2AServer(IServiceProvider serviceProvider, AIAgent agent, A2AServerRegistrationOptions? options)
{
var agentHandler = serviceProvider.GetKeyedService(agent.Name);
if (agentHandler is null)
{
var agentSessionStore = serviceProvider.GetKeyedService(agent.Name);
var runMode = options?.AgentRunMode ?? AgentRunMode.DisallowBackground;
// Ensure that we have an IsolationKeyScopedAgentSessionStore registered.
var isolationKeyProvider = serviceProvider.GetService();
if (agentSessionStore?.GetService() is null)
{
agentSessionStore ??= new InMemoryAgentSessionStore();
agentSessionStore = new IsolationKeyScopedAgentSessionStore(agentSessionStore, isolationKeyProvider, new());
}
var hostAgent = new AIHostAgent(
innerAgent: agent,
sessionStore: agentSessionStore);
agentHandler = new A2AAgentHandler(hostAgent, runMode);
}
var loggerFactory = serviceProvider.GetService() ?? NullLoggerFactory.Instance;
var taskStore = serviceProvider.GetKeyedService(agent.Name) ?? new InMemoryTaskStore();
return new A2AServer(
agentHandler,
taskStore,
new ChannelEventNotifier(),
loggerFactory.CreateLogger(),
options?.ServerOptions);
}
}