| name | m365-agents-py |
| description | ALWAYS use this when the request matches M365 Agents PY: Microsoft 365 Agents SDK for Python. |
Microsoft 365 Agents SDK (Python)
Selective Reading Rule
Start with:
references/senior-master-standard.md
references/usage-routing.md
references/quality-checklist.md
Then load only the inherited docs, scripts, assets, or examples that match the user's actual task.
Build enterprise agents for Microsoft 365, Teams, and Copilot Studio using the Microsoft Agents SDK with aiohttp hosting, AgentApplication routing, streaming responses, and MSAL-based authentication.
Before implementation
- Use the microsoft-docs MCP to verify the latest API signatures for AgentApplication, start_agent_process, and authentication options.
- Confirm package versions on PyPI for the microsoft-agents-* packages you plan to use.
Important Notice - Import Changes
⚠️ Breaking Change: Recent updates have changed the Python import structure from microsoft.agents to microsoft_agents (using underscores instead of dots).
Installation
pip install microsoft-agents-hosting-core
pip install microsoft-agents-hosting-aiohttp
pip install microsoft-agents-activity
pip install microsoft-agents-authentication-msal
pip install microsoft-agents-copilotstudio-client
pip install python-dotenv aiohttp
Environment Variables (.env)
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=<client-id>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=<client-secret>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=<tenant-id>
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=<connection-name>
AZURE_OPENAI_ENDPOINT=<endpoint>
AZURE_OPENAI_API_VERSION=<version>
AZURE_OPENAI_API_KEY=<key>
COPILOTSTUDIOAGENT__ENVIRONMENTID=<environment-id>
COPILOTSTUDIOAGENT__SCHEMANAME=<schema-name>
COPILOTSTUDIOAGENT__TENANTID=<tenant-id>
COPILOTSTUDIOAGENT__AGENTAPPID=<app-id>
Core Workflow: aiohttp-hosted AgentApplication
import logging
from os import environ
from dotenv import load_dotenv
from aiohttp.web import Request, Response, Application, run_app
from microsoft_agents.activity import load_configuration_from_env
from microsoft_agents.hosting.core import (
Authorization,
AgentApplication,
TurnState,
TurnContext,
MemoryStorage,
)
from microsoft_agents.hosting.aiohttp import (
CloudAdapter,
start_agent_process,
jwt_authorization_middleware,
)
from microsoft_agents.authentication.msal import MsalConnectionManager
ms_agents_logger = logging.getLogger("microsoft_agents")
ms_agents_logger.addHandler(logging.StreamHandler())
ms_agents_logger.setLevel(logging.INFO)
load_dotenv()
agents_sdk_config = load_configuration_from_env(environ)
STORAGE = MemoryStorage()
CONNECTION_MANAGER = MsalConnectionManager(**agents_sdk_config)
ADAPTER = CloudAdapter(connection_manager=CONNECTION_MANAGER)
AUTHORIZATION = Authorization(STORAGE, CONNECTION_MANAGER, **agents_sdk_config)
AGENT_APP = AgentApplicationTurnState
@AGENT_APP.conversation_update("membersAdded")
async def on_members_added(context: TurnContext, _state: TurnState):
await context.send_activity("Welcome to the agent!")
@AGENT_APP.activity("message")
async def on_message(context: TurnContext, _state: TurnState):
await context.send_activity()
():
context.send_activity()
() -> Response:
agent: AgentApplication = req.app[]
adapter: CloudAdapter = req.app[]
start_agent_process(req, agent, adapter)
APP = Application(middlewares=[jwt_authorization_middleware])
APP.router.add_post(, entry_point)
APP[] = CONNECTION_MANAGER.get_default_connection_configuration()
APP[] = AGENT_APP
APP[] = AGENT_APP.adapter
__name__ == :
run_app(APP, host=, port=environ.get(, ))
AgentApplication Routing
import re
from microsoft_agents.hosting.core import (
AgentApplication, TurnState, TurnContext, MessageFactory
)
from microsoft_agents.activity import ActivityTypes
AGENT_APP = AgentApplicationTurnState
@AGENT_APP.conversation_update("membersAdded")
async def on_members_added(context: TurnContext, _state: TurnState):
await context.send_activity("Welcome!")
@AGENT_APP.message(re.compile(r"^hello$", re.IGNORECASE))
async def on_hello(context: TurnContext, _state: TurnState):
await context.send_activity("Hello!")
@AGENT_APP.message("/status")
async def on_status(context: TurnContext, _state: TurnState):
await context.send_activity("Status: OK")
@AGENT_APP.message("/me", auth_handlers=["GRAPH"])
async def on_profile(context: TurnContext, state: TurnState):
token_response = await AGENT_APP.auth.get_token(context, )
token_response token_response.token:
context.send_activity()
():
invoke_response = Activity(
=ActivityTypes.invoke_response, value={: }
)
context.send_activity(invoke_response)
():
context.send_activity()
():
context.send_activity()
Streaming Responses with Azure OpenAI
from openai import AsyncAzureOpenAI
from microsoft_agents.activity import SensitivityUsageInfo
CLIENT = AsyncAzureOpenAI(
api_version=environ["AZURE_OPENAI_API_VERSION"],
azure_endpoint=environ["AZURE_OPENAI_ENDPOINT"],
api_key=environ["AZURE_OPENAI_API_KEY"]
)
@AGENT_APP.message("poem")
async def on_poem_message(context: TurnContext, _state: TurnState):
context.streaming_response.set_feedback_loop(True)
context.streaming_response.set_generated_by_ai_label(True)
context.streaming_response.set_sensitivity_label(
SensitivityUsageInfo(
type="https://schema.org/Message",
schema_type="CreativeWork",
name="Internal",
)
)
context.streaming_response.queue_informative_update("Starting a poem...\n")
streamed_response = await CLIENT.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "You are a creative assistant."},
{"role": "user", "content": "Write a poem about Python."}
],
stream=True,
)
try:
async for chunk in streamed_response:
if chunk.choices and chunk.choices[].delta.content:
context.streaming_response.queue_text_chunk(
chunk.choices[].delta.content
)
:
context.streaming_response.end_stream()
OAuth / Auto Sign-In
@AGENT_APP.message("/logout")
async def logout(context: TurnContext, state: TurnState):
await AGENT_APP.auth.sign_out(context, "GRAPH")
await context.send_activity(MessageFactory.text("You have been logged out."))
@AGENT_APP.message("/me", auth_handlers=["GRAPH"])
async def profile_request(context: TurnContext, state: TurnState):
user_token_response = await AGENT_APP.auth.get_token(context, "GRAPH")
if user_token_response and user_token_response.token:
async with aiohttp.ClientSession() as session:
headers = {
"Authorization": f"Bearer {user_token_response.token}",
"Content-Type": "application/json",
}
async with session.get(
"https://graph.microsoft.com/v1.0/me", headers=headers
) as response:
if response.status == 200:
user_info = await response.json()
await context.send_activity(f"Hello, {user_info['displayName']}!")
Copilot Studio Client (Direct to Engine)
import asyncio
from msal import PublicClientApplication
from microsoft_agents.activity import ActivityTypes, load_configuration_from_env
from microsoft_agents.copilotstudio.client import (
ConnectionSettings,
CopilotClient,
)
class LocalTokenCache:
pass
def acquire_token(settings, app_client_id, tenant_id):
pca = PublicClientApplication(
client_id=app_client_id,
authority=f"https://login.microsoftonline.com/{tenant_id}",
)
token_request = {"scopes": ["https://api.powerplatform.com/.default"]}
accounts = pca.get_accounts()
if accounts:
response = pca.acquire_token_silent(token_request["scopes"], account=accounts[0])
return response.get("access_token")
else:
response = pca.acquire_token_interactive(**token_request)
return response.get("access_token")
async def main():
settings = ConnectionSettings(
environment_id=environ.get("COPILOTSTUDIOAGENT__ENVIRONMENTID"),
agent_identifier=environ.get("COPILOTSTUDIOAGENT__SCHEMANAME"),
)
token = acquire_token(
settings,
app_client_id=environ.get("COPILOTSTUDIOAGENT__AGENTAPPID"),
tenant_id=environ.get("COPILOTSTUDIOAGENT__TENANTID"),
)
copilot_client = CopilotClient(settings, token)
act = copilot_client.start_conversation()
action act:
action.text:
(action.text)
replies = copilot_client.ask_question(, action.conversation.)
reply replies:
reply. == ActivityTypes.message:
(reply.text)
asyncio.run(main())
Best Practices
- Use
microsoft_agents import prefix (underscores, not dots).
- Use
MemoryStorage only for development; use BlobStorage or CosmosDB in production.
- Always use
load_configuration_from_env(environ) to load SDK configuration.
- Include
jwt_authorization_middleware in aiohttp Application middlewares.
- Use
MsalConnectionManager for MSAL-based authentication.
- Call
end_stream() in finally blocks when using streaming responses.
- Use
auth_handlers parameter on message decorators for OAuth-protected routes.
- Keep secrets in environment variables, not in source code.
Reference Files
| File | Contents |
|---|
| references/acceptance-criteria.md | Import paths, hosting pipeline, streaming, OAuth, and Copilot Studio patterns |
Reference Links
When to Use
This skill is applicable to execute the workflow or actions described in the overview.
Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.