Use when migrating a Bot Framework .NET SDK bot to Microsoft 365 Agents SDK. Triggered by projects that depend on packages: Microsoft.Bot.Builder or Microsoft.Bot.Builder.Integration.AspNet.Core that want to migrate to Agents SDK.
Use when migrating a Bot Framework .NET SDK bot to Microsoft 365 Agents SDK. Triggered by projects that depend on packages: Microsoft.Bot.Builder or Microsoft.Bot.Builder.Integration.AspNet.Core that want to migrate to Agents SDK.
Bot Framework to Agents SDK Migration (.NET)
Overview
Migrates a Bot Framework SDK bot to Microsoft 365 Agents SDK using the ActivityHandler/TeamsActivityHandler compat layer — minimal code changes, original class names preserved.
This skill stops at the compat layer. After completing this migration, ask the user whether to also convert to AgentApplication (see final step).
The following Bot Framework packages have no equivalent in the Agents SDK and are not supported. There is no recommended replacement. Remove them from the .csproj, but do not attempt to migrate the code that uses them — it will cause build errors that cannot be resolved within the Agents SDK migration. Flag this to the customer.
Deprecated Package
Microsoft.Bot.Builder.AI.Luis
Microsoft.Bot.Builder.AI.Orchestrator
Microsoft.Bot.Builder.AI.QnA
Microsoft.Bot.Builder.Azure.Queues
Microsoft.Bot.Builder.Dialogs.Adaptive
Microsoft.Bot.Builder.Dialogs.Adaptive.Runtime
Microsoft.Bot.Builder.Dialogs.Adaptive.Testing
Microsoft.Bot.Builder.Dialogs.Debugging
Microsoft.Bot.Builder.LanguageGeneration
Microsoft.Bot.Builder.TemplateManager
Microsoft.Bot.Configuration
Microsoft.Bot.Connector.Streaming
Microsoft.Bot.Streaming
Microsoft.Bot.Builder.Parsers.LU
AdaptiveExpressions
Migration still proceeds — complete all other migration steps. The build errors from unmigrated code must be communicated to the customer as out-of-scope blockers.
(remove) — TraceActivityAsync is built into ITurnContext
Migration Steps
Step 1: Update packages and namespaces (see tables above)
Step 2: Keep bot class — retain original class name, only update using statements
ActivityHandler and TeamsActivityHandler exist unchanged in the Compat namespace. All override methods keep the same signatures. Do not rename the class.
// Beforeusing Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;
publicclassEchoBot : ActivityHandler { /* unchanged */ }
// After — only using directives change; class name stays EchoBotusing Microsoft.Agents.Builder;
using Microsoft.Agents.Builder.Compat; // ActivityHandler lives hereusing Microsoft.Agents.Core.Models; // IMessageActivity, ChannelAccount, etc.publicclassEchoBot : ActivityHandler { /* unchanged */ }
For Teams:
// Beforeusing Microsoft.Bot.Builder.Teams;
using Microsoft.Bot.Schema.Teams;
publicclassMyBot : TeamsActivityHandler { /* unchanged */ }
// After — class name stays MyBotusing Microsoft.Agents.Extensions.Teams.Compat; // TeamsActivityHandler lives hereusing Microsoft.Agents.Extensions.Teams.Models; // TeamsChannelAccount, TeamInfo, etc.publicclassMyBot : TeamsActivityHandler { /* unchanged */ }
Step 3: State management for dialog bots
If the bot overrides OnTurnAsync to call SaveChangesAsync, both patterns work — keep whichever the bot already uses:
Delete BotController.cs and AdapterWithErrorHandler.cs. Create Program.cs:
using Microsoft.Agents.Hosting.AspNetCore;
using Microsoft.Agents.Storage;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient();
builder.Services.AddControllers();
// Register bot — AddAgent works because ActivityHandler implements IAgent// DO NOT call AddAgentApplicationOptions() — that is only for AgentApplication
builder.AddAgent<MyBot>();
// Storage — always required by the Agents SDK
builder.Services.AddSingleton<IStorage, MemoryStorage>();
// Preserve all customer-specific DI registrations:// builder.Services.AddSingleton<ConversationState>();// builder.Services.AddSingleton<UserState>();// builder.Services.AddSingleton<MyDialog>();// builder.Services.AddSingleton<IMyService, MyService>();// AddAgentAspNetAuthentication is defined in AspNetExtensions.cs — copy from any Agents SDK sample
builder.Services.AddAgentAspNetAuthentication(builder.Configuration);
WebApplication app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapAgentRootEndpoint();
// ActivityHandler bots use MapAgentEndpoints — NOT MapAgentApplicationEndpoints// MapAgentApplicationEndpoints only works for AgentApplication subclasses
app.MapAgentEndpoints(requireAuth: !app.Environment.IsDevelopment());
if (app.Environment.IsDevelopment())
{
app.Urls.Add("http://localhost:3978");
}
app.Run();
If customer had custom logic in BotController, preserve it using the process delegate:
Step 5 or at the very end of the migration: Ask user if they would like to write the learnings from this migration to a markdown file they could submit to use to help us improve the skill.
appsettings.json Changes
Remove Bot Framework auth config and add Agents SDK config. The shape of Connections:ServiceConnection depends on the value of MicrosoftAppType in the existing appsettings.
In all cases: remove MicrosoftAppId, MicrosoftAppPassword, MicrosoftAppType, and MicrosoftAppTenantId — their values are carried forward into the new structure.
If the source bot implements Microsoft.Bot.Builder.IMiddleware, retain the class and update its namespaces:
Old
New
Microsoft.Bot.Builder.IMiddleware
Microsoft.Agents.Builder.IMiddleware
Microsoft.Bot.Builder.ITurnContext
Microsoft.Agents.Builder.ITurnContext
Microsoft.Bot.Builder.NextDelegate
Microsoft.Agents.Builder.NextDelegate
The OnTurnAsync(ITurnContext, NextDelegate, CancellationToken) signature is unchanged.
Register custom middleware via a CloudAdapter subclass. Delete AdapterWithErrorHandler.cs only if it has no custom middleware — if it does, rename it or keep it as a custom adapter:
AdapterWithErrorHandler.cs — delete if it only has OnTurnError logic (Agents SDK handles errors internally); retain and rename if it contains custom Use() middleware registrations (see Custom IMiddleware section above)
BotController.cs (unless it has custom logic — see preservation note above)
Any Startup.cs (merge into Program.cs)
Common Mistakes
Mistake
Fix
Renamed the bot class or created a new class
Restore the original class name — renaming breaks git history
Changed more than needed (refactored, restructured)
Revert — only change what is required to compile and run
Called AddAgentApplicationOptions() for ActivityHandler bot
Remove — only for AgentApplication
Used MapAgentApplicationEndpoints() for ActivityHandler bot
Use MapAgentEndpoints() — former only works for AgentApplication
Left services.AddTransient<IBot, MyBot>()
Replace with builder.AddAgent<MyBot>()
Left services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>()
Delete — handled by AddAgent<>()
Left ConfigurationBotFrameworkAuthentication
Delete — use AddAgentAspNetAuthentication()
Missing IStorage registration
Add builder.Services.AddSingleton<IStorage, MemoryStorage>(); — always required, not just for dialog bots
Left old Bot Framework appsettings (MicrosoftAppId etc.)
Replace with Connections + TokenValidation — shape depends on MicrosoftAppType (see appsettings.json Changes section)
Used the wrong Connections:ServiceConnection shape
Check MicrosoftAppType: SingleTenant→ClientSecret+tenant authority; UserAssignedMSI→UserManagedIdentity (no secret); MultiTenant/missing→ClientSecret+botframework.com authority
Left BotAdapter as base class of custom adapter
Change to ChannelAdapter or CloudAdapter; update Activity[] → IActivity[], BotCallbackHandler → AgentCallbackHandler
Deleted AdapterWithErrorHandler.cs that contained custom Use() calls
Retain it (renamed if needed) as a CloudAdapter subclass; register via builder.AddAgent<TBot, TAdapter>()
Left Microsoft.Bot.Builder.IMiddleware namespace in custom middleware
Update to Microsoft.Agents.Builder.IMiddleware
Attempted to migrate code using a deprecated package (Luis, QnA, Adaptive, etc.)
Stop — no Agents SDK equivalent exists. Remove the package reference, leave the code in place, and flag build errors to the customer
Forgot to add AspNetExtensions.cs
Copy from any Agents SDK sample — AddAgentAspNetAuthentication is not in any NuGet package
Left conversationState.DeleteAsync(...) in adapter
Rename to conversationState.DeleteStateAsync(...) — method was renamed in Agents SDK
New Program.cs fails to compile (WebApplication, AddSingleton, IsDevelopment not found)
BF projects don't have <ImplicitUsings>enable</ImplicitUsings> — add explicit using for Microsoft.AspNetCore.Builder, Microsoft.Extensions.DependencyInjection, Microsoft.Extensions.Hosting
After Migration: Optional AgentApplication Upgrade
Once the compat-layer migration is complete and the build is clean, ask the user:
"The bot is now running on Agents SDK using the ActivityHandler compat layer. Would you like to also migrate to the modern AgentApplication routing pattern? This involves converting the bot class to subclass AgentApplication and updating Program.cs — it is handled by the agents-sdk-dotnet-activityhandler-migration skill."
If yes, invoke agents-sdk-dotnet-activityhandler-migration.
If no, migration is complete.