|
Add this skill
npx mdskills install sickn33/m365-agents-pyComprehensive SDK integration guide with routing, streaming, and auth examples but over-scoped permissions
1---2name: m365-agents-py3description: |4 Microsoft 365 Agents SDK for Python. Build multichannel agents for Teams/M365/Copilot Studio with aiohttp hosting, AgentApplication routing, streaming responses, and MSAL-based auth. Triggers: "Microsoft 365 Agents SDK", "microsoft_agents", "AgentApplication", "start_agent_process", "TurnContext", "Copilot Studio client", "CloudAdapter".5package: microsoft-agents-hosting-core, microsoft-agents-hosting-aiohttp, microsoft-agents-activity, microsoft-agents-authentication-msal, microsoft-agents-copilotstudio-client6---78# Microsoft 365 Agents SDK (Python)910Build 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.1112## Before implementation13- Use the microsoft-docs MCP to verify the latest API signatures for AgentApplication, start_agent_process, and authentication options.14- Confirm package versions on PyPI for the microsoft-agents-* packages you plan to use.1516## Important Notice - Import Changes1718> **⚠️ Breaking Change**: Recent updates have changed the Python import structure from `microsoft.agents` to `microsoft_agents` (using underscores instead of dots).1920## Installation2122```bash23pip install microsoft-agents-hosting-core24pip install microsoft-agents-hosting-aiohttp25pip install microsoft-agents-activity26pip install microsoft-agents-authentication-msal27pip install microsoft-agents-copilotstudio-client28pip install python-dotenv aiohttp29```3031## Environment Variables (.env)3233```bash34CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=<client-id>35CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=<client-secret>36CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=<tenant-id>3738# Optional: OAuth handlers for auto sign-in39AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=<connection-name>4041# Optional: Azure OpenAI for streaming42AZURE_OPENAI_ENDPOINT=<endpoint>43AZURE_OPENAI_API_VERSION=<version>44AZURE_OPENAI_API_KEY=<key>4546# Optional: Copilot Studio client47COPILOTSTUDIOAGENT__ENVIRONMENTID=<environment-id>48COPILOTSTUDIOAGENT__SCHEMANAME=<schema-name>49COPILOTSTUDIOAGENT__TENANTID=<tenant-id>50COPILOTSTUDIOAGENT__AGENTAPPID=<app-id>51```5253## Core Workflow: aiohttp-hosted AgentApplication5455```python56import logging57from os import environ5859from dotenv import load_dotenv60from aiohttp.web import Request, Response, Application, run_app6162from microsoft_agents.activity import load_configuration_from_env63from microsoft_agents.hosting.core import (64 Authorization,65 AgentApplication,66 TurnState,67 TurnContext,68 MemoryStorage,69)70from microsoft_agents.hosting.aiohttp import (71 CloudAdapter,72 start_agent_process,73 jwt_authorization_middleware,74)75from microsoft_agents.authentication.msal import MsalConnectionManager7677# Enable logging78ms_agents_logger = logging.getLogger("microsoft_agents")79ms_agents_logger.addHandler(logging.StreamHandler())80ms_agents_logger.setLevel(logging.INFO)8182# Load configuration83load_dotenv()84agents_sdk_config = load_configuration_from_env(environ)8586# Create storage and connection manager87STORAGE = MemoryStorage()88CONNECTION_MANAGER = MsalConnectionManager(**agents_sdk_config)89ADAPTER = CloudAdapter(connection_manager=CONNECTION_MANAGER)90AUTHORIZATION = Authorization(STORAGE, CONNECTION_MANAGER, **agents_sdk_config)9192# Create AgentApplication93AGENT_APP = AgentApplication[TurnState](94 storage=STORAGE, adapter=ADAPTER, authorization=AUTHORIZATION, **agents_sdk_config95)969798@AGENT_APP.conversation_update("membersAdded")99async def on_members_added(context: TurnContext, _state: TurnState):100 await context.send_activity("Welcome to the agent!")101102103@AGENT_APP.activity("message")104async def on_message(context: TurnContext, _state: TurnState):105 await context.send_activity(f"You said: {context.activity.text}")106107108@AGENT_APP.error109async def on_error(context: TurnContext, error: Exception):110 await context.send_activity("The agent encountered an error.")111112113# Server setup114async def entry_point(req: Request) -> Response:115 agent: AgentApplication = req.app["agent_app"]116 adapter: CloudAdapter = req.app["adapter"]117 return await start_agent_process(req, agent, adapter)118119120APP = Application(middlewares=[jwt_authorization_middleware])121APP.router.add_post("/api/messages", entry_point)122APP["agent_configuration"] = CONNECTION_MANAGER.get_default_connection_configuration()123APP["agent_app"] = AGENT_APP124APP["adapter"] = AGENT_APP.adapter125126if __name__ == "__main__":127 run_app(APP, host="localhost", port=environ.get("PORT", 3978))128```129130## AgentApplication Routing131132```python133import re134from microsoft_agents.hosting.core import (135 AgentApplication, TurnState, TurnContext, MessageFactory136)137from microsoft_agents.activity import ActivityTypes138139AGENT_APP = AgentApplication[TurnState](140 storage=STORAGE, adapter=ADAPTER, authorization=AUTHORIZATION, **agents_sdk_config141)142143# Welcome handler144@AGENT_APP.conversation_update("membersAdded")145async def on_members_added(context: TurnContext, _state: TurnState):146 await context.send_activity("Welcome!")147148# Regex-based message handler149@AGENT_APP.message(re.compile(r"^hello$", re.IGNORECASE))150async def on_hello(context: TurnContext, _state: TurnState):151 await context.send_activity("Hello!")152153# Simple string message handler154@AGENT_APP.message("/status")155async def on_status(context: TurnContext, _state: TurnState):156 await context.send_activity("Status: OK")157158# Auth-protected message handler159@AGENT_APP.message("/me", auth_handlers=["GRAPH"])160async def on_profile(context: TurnContext, state: TurnState):161 token_response = await AGENT_APP.auth.get_token(context, "GRAPH")162 if token_response and token_response.token:163 # Use token to call Graph API164 await context.send_activity("Profile retrieved")165166# Invoke activity handler167@AGENT_APP.activity(ActivityTypes.invoke)168async def on_invoke(context: TurnContext, _state: TurnState):169 invoke_response = Activity(170 type=ActivityTypes.invoke_response, value={"status": 200}171 )172 await context.send_activity(invoke_response)173174# Fallback message handler175@AGENT_APP.activity("message")176async def on_message(context: TurnContext, _state: TurnState):177 await context.send_activity(f"Echo: {context.activity.text}")178179# Error handler180@AGENT_APP.error181async def on_error(context: TurnContext, error: Exception):182 await context.send_activity("An error occurred.")183```184185## Streaming Responses with Azure OpenAI186187```python188from openai import AsyncAzureOpenAI189from microsoft_agents.activity import SensitivityUsageInfo190191CLIENT = AsyncAzureOpenAI(192 api_version=environ["AZURE_OPENAI_API_VERSION"],193 azure_endpoint=environ["AZURE_OPENAI_ENDPOINT"],194 api_key=environ["AZURE_OPENAI_API_KEY"]195)196197@AGENT_APP.message("poem")198async def on_poem_message(context: TurnContext, _state: TurnState):199 # Configure streaming response200 context.streaming_response.set_feedback_loop(True)201 context.streaming_response.set_generated_by_ai_label(True)202 context.streaming_response.set_sensitivity_label(203 SensitivityUsageInfo(204 type="https://schema.org/Message",205 schema_type="CreativeWork",206 name="Internal",207 )208 )209 context.streaming_response.queue_informative_update("Starting a poem...\n")210211 # Stream from Azure OpenAI212 streamed_response = await CLIENT.chat.completions.create(213 model="gpt-4o",214 messages=[215 {"role": "system", "content": "You are a creative assistant."},216 {"role": "user", "content": "Write a poem about Python."}217 ],218 stream=True,219 )220221 try:222 async for chunk in streamed_response:223 if chunk.choices and chunk.choices[0].delta.content:224 context.streaming_response.queue_text_chunk(225 chunk.choices[0].delta.content226 )227 finally:228 await context.streaming_response.end_stream()229```230231## OAuth / Auto Sign-In232233```python234@AGENT_APP.message("/logout")235async def logout(context: TurnContext, state: TurnState):236 await AGENT_APP.auth.sign_out(context, "GRAPH")237 await context.send_activity(MessageFactory.text("You have been logged out."))238239240@AGENT_APP.message("/me", auth_handlers=["GRAPH"])241async def profile_request(context: TurnContext, state: TurnState):242 user_token_response = await AGENT_APP.auth.get_token(context, "GRAPH")243 if user_token_response and user_token_response.token:244 # Use token to call Microsoft Graph245 async with aiohttp.ClientSession() as session:246 headers = {247 "Authorization": f"Bearer {user_token_response.token}",248 "Content-Type": "application/json",249 }250 async with session.get(251 "https://graph.microsoft.com/v1.0/me", headers=headers252 ) as response:253 if response.status == 200:254 user_info = await response.json()255 await context.send_activity(f"Hello, {user_info['displayName']}!")256```257258## Copilot Studio Client (Direct to Engine)259260```python261import asyncio262from msal import PublicClientApplication263from microsoft_agents.activity import ActivityTypes, load_configuration_from_env264from microsoft_agents.copilotstudio.client import (265 ConnectionSettings,266 CopilotClient,267)268269# Token cache (local file for interactive flows)270class LocalTokenCache:271 # See samples for full implementation272 pass273274def acquire_token(settings, app_client_id, tenant_id):275 pca = PublicClientApplication(276 client_id=app_client_id,277 authority=f"https://login.microsoftonline.com/{tenant_id}",278 )279280 token_request = {"scopes": ["https://api.powerplatform.com/.default"]}281 accounts = pca.get_accounts()282283 if accounts:284 response = pca.acquire_token_silent(token_request["scopes"], account=accounts[0])285 return response.get("access_token")286 else:287 response = pca.acquire_token_interactive(**token_request)288 return response.get("access_token")289290291async def main():292 settings = ConnectionSettings(293 environment_id=environ.get("COPILOTSTUDIOAGENT__ENVIRONMENTID"),294 agent_identifier=environ.get("COPILOTSTUDIOAGENT__SCHEMANAME"),295 )296297 token = acquire_token(298 settings,299 app_client_id=environ.get("COPILOTSTUDIOAGENT__AGENTAPPID"),300 tenant_id=environ.get("COPILOTSTUDIOAGENT__TENANTID"),301 )302303 copilot_client = CopilotClient(settings, token)304305 # Start conversation306 act = copilot_client.start_conversation(True)307 async for action in act:308 if action.text:309 print(action.text)310311 # Ask question312 replies = copilot_client.ask_question("Hello!", action.conversation.id)313 async for reply in replies:314 if reply.type == ActivityTypes.message:315 print(reply.text)316317318asyncio.run(main())319```320321## Best Practices3223231. Use `microsoft_agents` import prefix (underscores, not dots).3242. Use `MemoryStorage` only for development; use BlobStorage or CosmosDB in production.3253. Always use `load_configuration_from_env(environ)` to load SDK configuration.3264. Include `jwt_authorization_middleware` in aiohttp Application middlewares.3275. Use `MsalConnectionManager` for MSAL-based authentication.3286. Call `end_stream()` in finally blocks when using streaming responses.3297. Use `auth_handlers` parameter on message decorators for OAuth-protected routes.3308. Keep secrets in environment variables, not in source code.331332## Reference Files333334| File | Contents |335| --- | --- |336| [references/acceptance-criteria.md](references/acceptance-criteria.md) | Import paths, hosting pipeline, streaming, OAuth, and Copilot Studio patterns |337338## Reference Links339340| Resource | URL |341| --- | --- |342| Microsoft 365 Agents SDK | https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/ |343| GitHub samples (Python) | https://github.com/microsoft/Agents-for-python |344| PyPI packages | https://pypi.org/search/?q=microsoft-agents |345| Integrate with Copilot Studio | https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/integrate-with-mcs |346
Full transparency — inspect the skill content before installing.