Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This article describes the required changes to migrate from the Bot Framework SDK for Python to the Microsoft 365 Agents SDK for Python.
Tip
Treat this work as a modernization effort, not only a package swap. The Agents SDK favors simpler, unopinionated patterns (async/await, dependency injection, standard logging) and removes legacy features. Keep only what your bot actually needs and avoid carrying forward old template complexity.
Prerequisites
- Python version 3.10 or higher (3.11+ recommended, supports up to 3.14)
- Existing Bot Framework SDK project
- Azure Bot Service resource
Azure resources
Your existing Azure resources remain unchanged during this migration. Continue using your current Azure Bot registration (App ID and secret). You reference those values in the new configuration structure described later. Many solutions also include legacy app settings such as APP_ID, APP_PASSWORD, and APP_TENANT_ID. These entries are harmless during migration but the Microsoft 365 Agents SDK no longer uses them. You can remove these settings after you validate the new configuration.
First steps
Start by updating package dependencies. The following changes don't cover every namespace you might need to touch, but they handle most package substitutions.
Update package dependencies
| Bot Framework SDK | Agents SDK |
|---|---|
botbuilder-core |
microsoft-agents-hosting-core |
botbuilder-schema |
microsoft-agents-activity |
botbuilder-azure |
microsoft-agents-storage-blob and microsoft-agents-storage-cosmos |
botbuilder-integration-aiohttp |
microsoft-agents-hosting-aiohttp |
If your bot integrates with Microsoft Teams, include the Teams Extension package microsoft-agents-hosting-teams for core Teams features.
For authentication, add microsoft-agents-authentication-msal to handle MSAL-based authentication.
Update your requirements.txt or equivalent dependency file to replace Bot Framework packages with their Agents SDK equivalents. Use pip install -r requirements.txt or your preferred package manager to install the new dependencies.
The Agents SDK enforces code quality standards by using black for formatting and flake8 for linting. Consider adding these tools to your development dependencies.
Update imports
Important
The Agents SDK uses underscores in import paths (microsoft_agents) instead of dots (microsoft.agents). This change affects all imports.
Next, update imports. The easiest approach is a project-wide find-and-replace of the exact text.
| Bot Framework Import | Agents SDK Import |
|---|---|
from botbuilder.core import ... |
from microsoft_agents.hosting.core import ... |
from botbuilder.schema import ... |
from microsoft_agents.activity import ... |
from botbuilder.integration.aiohttp import ... |
from microsoft_agents.hosting.aiohttp import ... |
from botbuilder.core.teams import ... |
from microsoft_agents.hosting.teams import ... |
Type and class renames
Some types and classes have different names in the Agents SDK. Use the following mappings to guide your changes.
| Bot Framework | Agents SDK |
|---|---|
BotState |
AgentState |
BotFrameworkAdapter |
CloudAdapter |
BotFrameworkHttpClient |
AgentHttpClient |
OAuthPromptSettings.connection_name |
OAuthPromptSettings.azure_bot_oauth_connection_name |
Activity class changes
The Activity class in the Agents SDK includes built-in validation by using Pydantic. You can parse and validate activities from JSON or dictionaries by using Activity.model_validate().
Additionally, the Activity class centralizes operations related to activity payloads. Methods that were previously static methods on TurnContext are now instance methods on Activity:
| Bot Framework Static Method | Agents SDK Instance Method |
|---|---|
TurnContext.apply_conversation_reference() |
activity.apply_conversation_reference() |
TurnContext.get_conversation_reference() |
activity.get_conversation_reference() |
TurnContext.get_reply_conversation_reference() |
activity.get_reply_conversation_reference() |
TurnContext.remove_recipient_mention() |
activity.remove_recipient_mention() |
TurnContext.get_mentions() |
activity.get_mentions() |
TurnContext.remove_mention_text() |
activity.remove_mention_text() |
Update common turn_state references. Replace the following usages accordingly.
| Bot Framework | Agents SDK |
|---|---|
turn_context.turn_state.get(ConnectorClient) |
turn_context.services.get(ConnectorClient) |
turn_context.turn_state.get(UserTokenClient) |
turn_context.services.get(UserTokenClient) |
turn_context.turn_state |
turn_context.services |
Configuration
The Agents SDK uses environment variables with a hierarchical naming convention for configuration.
Environment variables
Create or update your .env file with the following structure:
# Required for Azure Bot Service
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=your-app-id
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=your-app-secret
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=your-tenant-id
# Optional - for local debugging
PORT=3978
Migration Note: Update your environment variable names as shown in the following table:
| Bot Framework SDK | Agents SDK |
|---|---|
APP_ID or MICROSOFT_APP_ID |
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID |
APP_PASSWORD or MICROSOFT_APP_PASSWORD |
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET |
APP_TENANT_ID or MICROSOFT_APP_TENANT_ID |
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID |
The Agents SDK configuration uses double underscores (__) to represent nested configuration hierarchies. This pattern allows for flexible configuration management across different deployment environments.
Startup and initialization
Initialization differs in the Agents SDK. The Bot Framework SDK typically used aiohttp with manual adapter configuration. By using the Agents SDK, you can use the built-in server utilities for simplified setup.
Server setup
Bot Framework SDK approach
from aiohttp import web
from botbuilder.core import BotFrameworkAdapter, BotFrameworkAdapterSettings
from botbuilder.schema import Activity
# Configure adapter
SETTINGS = BotFrameworkAdapterSettings(
app_id=os.environ.get("APP_ID", ""),
app_password=os.environ.get("APP_PASSWORD", "")
)
ADAPTER = BotFrameworkAdapter(SETTINGS)
# Bot instance
BOT = MyBot()
async def messages(req: web.Request) -> web.Response:
if "application/json" in req.headers["Content-Type"]:
body = await req.json()
else:
return web.Response(status=415)
activity = Activity().deserialize(body)
auth_header = req.headers["Authorization"] if "Authorization" in req.headers else ""
response = await ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
if response:
return web.json_response(data=response.body, status=response.status)
return web.Response(status=201)
APP = web.Application()
APP.router.add_post("/api/messages", messages)
if __name__ == "__main__":
web.run_app(APP, host="localhost", port=3978)
Agents SDK approach (decorator pattern)
The recommended approach uses the AgentApplication with decorators for a more modern, declarative style:
import re
from os import environ
from dotenv import load_dotenv
from microsoft_agents.hosting.aiohttp import CloudAdapter
from microsoft_agents.hosting.core import (
Authorization,
AgentApplication,
TurnState,
TurnContext,
MemoryStorage,
)
from microsoft_agents.authentication.msal import MsalConnectionManager
from microsoft_agents.activity import load_configuration_from_env
# Load environment variables
load_dotenv()
agents_sdk_config = load_configuration_from_env(environ)
# Initialize core components
STORAGE = MemoryStorage()
CONNECTION_MANAGER = MsalConnectionManager(**agents_sdk_config)
ADAPTER = CloudAdapter(connection_manager=CONNECTION_MANAGER)
AUTHORIZATION = Authorization(STORAGE, CONNECTION_MANAGER, **agents_sdk_config)
# Create agent application
AGENT_APP = AgentApplication[TurnState](
storage=STORAGE,
adapter=ADAPTER,
authorization=AUTHORIZATION,
**agents_sdk_config
)
# Register handlers using decorators
@AGENT_APP.conversation_update("membersAdded")
async def on_members_added(context: TurnContext, _state: TurnState):
await context.send_activity("Welcome!")
return True
@AGENT_APP.activity("message")
async def on_message(context: TurnContext, _state: TurnState):
await context.send_activity(f"You said: {context.activity.text}")
The Agents SDK provides load_configuration_from_env() to automatically load configuration from environment variables, eliminating manual configuration parsing. The decorator pattern allows you to register handlers declaratively without explicit class-based inheritance.
Authentication and security
The Bot Framework SDK included JSON Web Token (JWT) validation in the adapter. The Agents SDK separates authentication concerns, so you can configure authentication middleware explicitly.
For production environments, ensure JWT validation is enabled through the CloudAdapter configuration. The adapter automatically validates incoming requests by using the MSAL authentication configuration.
For local development with the Bot Framework Emulator, you can use empty credentials in your .env file:
# Only for local development with Emulator
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__ANONYMOUS_ALLOWED=True
Activity handling
Most bots created with the Bot Framework SDK are based on the ActivityHandler base class.
The Agents SDK provides a compatible ActivityHandler that maintains a similar API surface for easier migration.
Key differences
Handler method signature changes
Bot Framework SDK handlers typically use positional parameters, while Agents SDK handlers maintain the same pattern with TurnContext as the primary parameter.
Additional methods in Agents SDK
| Method | Description |
|---|---|
on_message_delete() |
Handles message deletion activities |
on_message_update() |
Handles message update activities |
on_sign_in_invoke() |
Handles sign-in invoke activities |
Missing methods in Agents SDK
| Method | Reason |
|---|---|
on_command_activity() |
Command activities aren't supported |
on_command_result_activity() |
Command result activities aren't supported |
Migration example
Bot Framework SDK
from botbuilder.core import ActivityHandler, TurnContext
from botbuilder.schema import ChannelAccount
class MyBot(ActivityHandler):
async def on_message_activity(self, turn_context: TurnContext):
await turn_context.send_activity(f"You said: {turn_context.activity.text}")
async def on_members_added_activity(
self,
members_added: list[ChannelAccount],
turn_context: TurnContext
):
for member in members_added:
if member.id != turn_context.activity.recipient.id:
await turn_context.send_activity("Welcome!")
Agents SDK
from microsoft_agents.hosting.core import AgentApplication, TurnState, TurnContext
# AGENT_APP is initialized as shown in the Startup and initialization section
@AGENT_APP.activity("message")
async def on_message(context: TurnContext, _state: TurnState):
await context.send_activity(f"You said: {context.activity.text}")
@AGENT_APP.conversation_update("membersAdded")
async def on_members_added(context: TurnContext, _state: TurnState):
for member in context.activity.members_added:
if member.id != context.activity.recipient.id:
await context.send_activity("Welcome!")
return True
The migration is mostly straightforward, with the main changes being the import statements. Most existing ActivityHandler-based bots work with minimal modifications.
State management
ConversationState, UserState, and PrivateConversationState are compatible with a few differences. The core state management patterns are similar to the Bot Framework SDK.
You must register state objects and explicitly save them after modifications.
from microsoft_agents.hosting.core import (
AgentApplication,
TurnState,
TurnContext,
ConversationState,
UserState,
MemoryStorage,
)
# Initialize storage and state
STORAGE = MemoryStorage()
conversation_state = ConversationState(STORAGE)
user_state = UserState(STORAGE)
count_property = conversation_state.create_property("conversation_data")
# AGENT_APP is initialized as shown in the Startup and initialization section
@AGENT_APP.activity("message")
async def on_message(context: TurnContext, _state: TurnState):
# Access state
conversation_data = await count_property.get(context, {})
# Modify state
conversation_data["count"] = conversation_data.get("count", 0) + 1
await count_property.set(context, conversation_data)
await context.send_activity(f"Message count: {conversation_data['count']}")
# Save state changes
await conversation_state.save_changes(context)
await user_state.save_changes(context)
Storage options
The Agents SDK provides storage implementations for different backends:
# Memory storage (development only)
from microsoft_agents.hosting.core import MemoryStorage
storage = MemoryStorage()
# Azure Blob Storage
from microsoft_agents.storage.blob import BlobStorage
storage = BlobStorage(
connection_string="your-connection-string",
container_name="bot-state"
)
# Azure Cosmos DB
from microsoft_agents.storage.cosmos import CosmosDbPartitionedStorage
storage = CosmosDbPartitionedStorage(
cosmos_client_options={
"url": "your-cosmos-url",
"key": "your-cosmos-key"
},
database_id="bot-db",
container_id="bot-state"
)
Logging
The Agents SDK uses Python's standard logging module. Configure logging in your main application file:
import logging
# Configure logging for Agents SDK
ms_agents_logger = logging.getLogger("microsoft_agents")
console_handler = logging.StreamHandler()
console_handler.setFormatter(
logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
)
ms_agents_logger.addHandler(console_handler)
ms_agents_logger.setLevel(logging.INFO)
Log levels
- DEBUG: Verbose local state tracking
- INFO: Activity handling, API calls, and requests to or from external entities
- WARNING: Unexpected events that cause potential problems
- ERROR: Observed errors, whether caught or uncaught
Common migration patterns
Simple echo bot
Simple echo bot in Bot Framework SDK
from botbuilder.core import ActivityHandler, TurnContext
class EchoBot(ActivityHandler):
async def on_message_activity(self, turn_context: TurnContext):
await turn_context.send_activity(f"Echo: {turn_context.activity.text}")
Simple echo bot in Agents SDK
from microsoft_agents.hosting.core import AgentApplication, TurnState, TurnContext
# AGENT_APP is initialized as shown in the Startup and initialization section
@AGENT_APP.activity("message")
async def on_message(context: TurnContext, _state: TurnState):
await context.send_activity(f"Echo: {context.activity.text}")
State management with counter
from microsoft_agents.hosting.core import (
AgentApplication,
TurnState,
TurnContext,
ConversationState,
MemoryStorage,
)
# Initialize storage and state
STORAGE = MemoryStorage()
conversation_state = ConversationState(STORAGE)
count_property = conversation_state.create_property("count")
# AGENT_APP is initialized as shown in the Startup and initialization section
@AGENT_APP.activity("message")
async def on_message(context: TurnContext, _state: TurnState):
count = await count_property.get(context, 0)
count += 1
await count_property.set(context, count)
await context.send_activity(f"Message count: {count}")
await conversation_state.save_changes(context)
Teams bot
from microsoft_agents.hosting.core import AgentApplication, TurnState, TurnContext
# AGENT_APP is initialized as shown in the Startup and initialization section
# Include microsoft-agents-hosting-teams in your dependencies for Teams support
@AGENT_APP.conversation_update("membersAdded")
async def on_members_added(context: TurnContext, _state: TurnState):
for member in context.activity.members_added:
await context.send_activity(f"Welcome to the team, {member.name}!")
return True
@AGENT_APP.activity("message")
async def on_message(context: TurnContext, _state: TurnState):
# Remove mentions
text = context.activity.remove_mention_text(context.activity.recipient.id)
await context.send_activity(f"You said: {text}")
Troubleshooting and tips
The following tips might help you be more successful.
Import errors
If you encounter import errors, ensure you're using underscores (microsoft_agents) instead of dots (microsoft.agents) in all import statements. This error is the most common migration problem.
Async/await patterns
All bot methods must be async and use await for asynchronous operations. Ensure all calls to send_activity, state operations, and dialog operations use await.
Type hints
The Agents SDK uses Python type hints extensively. Consider adding type hints to your bot code for better IDE support and error detection:
from microsoft_agents.hosting.core import TurnContext
async def on_message_activity(self, turn_context: TurnContext) -> None:
await turn_context.send_activity("Hello")
Configuration validation
If your bot fails to start due to configuration errors, verify your environment variable names use the correct double-underscore (__) notation and that you set all required values.
Local 401 errors during debugging
If you see 401 errors during local debugging by using the Bot Framework Emulator, verify your credentials are correctly configured in the .env file or use the Emulator's authentication settings.
Python version compatibility
Ensure you're using Python 3.10 or higher. The Agents SDK uses modern Python features that aren't available in earlier versions.
Migration checklist
✓ Analyze and plan: Identify any deprecated features and decide migration vs. rebuild scope.
✓ Upgrade Python version: Move to Python 3.10 or higher (3.11+ recommended).
✓ Replace packages: Remove botbuilder-*; add microsoft-agents-* (hosting-core, activity, hosting-aiohttp, authentication-msal, storage providers, hosting-teams).
✓ Update imports: Apply find/replace per the tables above; change all microsoft.agents to microsoft_agents.
✓ Update types and classes: Fix renamed APIs and move TurnContext static methods to Activity instance methods.
✓ Update configuration: Create .env file with hierarchical naming (double underscores).
✓ Update initialization: Use CloudAdapter with MsalAuth.from_environment() and AgentsHttpMiddleware.
✓ Configure logging: Set up Python logging for microsoft_agents logger.
✓ Build and test locally: Use the Bot Framework Emulator with credentials; validate core scenarios.
✓ Deploy and monitor: Update environment variables in Azure to match the new configuration and watch logs after rollout.