{"slug":"m365-agents-py","title":"m365-agents-py","summary":"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_proces","platform":"GitHub Copilot","tags":[],"authorName":"Ciza","authorSlug":"ciza","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-08-12T21:05:10.79573Z","repo":{"url":"https://github.com/microsoft/skills","stars":3052,"forks":351,"license":"MIT","updatedAt":"2026-09-24T16:38:17Z"},"bodyHtml":"<hr>\n<h2>name: m365-agents-py\ndescription: |\nMicrosoft 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\".\nlicense: MIT\nmetadata:\nauthor: Microsoft\nversion: \"1.0.0\"\npackage: microsoft-agents-hosting-core, microsoft-agents-hosting-aiohttp, microsoft-agents-activity, microsoft-agents-authentication-msal, microsoft-agents-copilotstudio-client</h2>\n<h1>Microsoft 365 Agents SDK (Python)</h1>\n<p>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.</p>\n<h2>Before implementation</h2>\n<ul>\n<li>Use the microsoft-docs MCP to verify the latest API signatures for AgentApplication, start_agent_process, and authentication options.</li>\n<li>Confirm package versions on PyPI for the microsoft-agents-* packages you plan to use.</li>\n</ul>\n<h2>Important Notice - Import Changes</h2>\n<blockquote>\n<p><strong>⚠️ Breaking Change</strong>: Recent updates have changed the Python import structure from <code>microsoft.agents</code> to <code>microsoft_agents</code> (using underscores instead of dots).</p>\n</blockquote>\n<h2>Installation</h2>\n<pre><code>pip install microsoft-agents-hosting-core\npip install microsoft-agents-hosting-aiohttp\npip install microsoft-agents-activity\npip install microsoft-agents-authentication-msal\npip install microsoft-agents-copilotstudio-client\npip install python-dotenv aiohttp\n</code></pre>\n<h2>Environment Variables (.env)</h2>\n<pre><code>CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=&lt;client-id&gt;\nCONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=&lt;client-secret&gt;\nCONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=&lt;tenant-id&gt;\n\n# Optional: OAuth handlers for auto sign-in\nAGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=&lt;connection-name&gt;\n\n# Optional: Azure OpenAI for streaming (AAD auth via DefaultAzureCredential)\nAZURE_OPENAI_ENDPOINT=&lt;endpoint&gt;\nAZURE_OPENAI_API_VERSION=&lt;version&gt;\n\n# Optional: Copilot Studio client\nCOPILOTSTUDIOAGENT__ENVIRONMENTID=&lt;environment-id&gt;\nCOPILOTSTUDIOAGENT__SCHEMANAME=&lt;schema-name&gt;\nCOPILOTSTUDIOAGENT__TENANTID=&lt;tenant-id&gt;\nCOPILOTSTUDIOAGENT__AGENTAPPID=&lt;app-id&gt;\n</code></pre>\n<h2>Authentication &amp; Lifecycle</h2>\n<blockquote>\n<p><strong>\uD83D\uDD11 Two rules apply to every code sample below:</strong></p>\n<ol>\n<li><strong>This SDK is async-first — use <code>async def</code> handlers and <code>async with</code> throughout.</strong></li>\n<li><strong>Use explicit auth managers and context-managed network resources.</strong> Use <code>MsalConnectionManager</code> for agent auth, and wrap per-request HTTP resources in <code>async with</code> (for example, <code>aiohttp.ClientSession</code>).</li>\n</ol>\n<p>Snippets may abbreviate this setup, but production code should always follow both rules.</p>\n</blockquote>\n<h2>Core Workflow: aiohttp-hosted AgentApplication</h2>\n<pre><code>import logging\nfrom os import environ\n\nfrom dotenv import load_dotenv\nfrom aiohttp.web import Request, Response, Application, run_app\n\nfrom microsoft_agents.activity import load_configuration_from_env\nfrom microsoft_agents.hosting.core import (\n    Authorization,\n    AgentApplication,\n    TurnState,\n    TurnContext,\n    MemoryStorage,\n)\nfrom microsoft_agents.hosting.aiohttp import (\n    CloudAdapter,\n    start_agent_process,\n    jwt_authorization_middleware,\n)\nfrom microsoft_agents.authentication.msal import MsalConnectionManager\n\n# Enable logging\nms_agents_logger = logging.getLogger(\"microsoft_agents\")\nms_agents_logger.addHandler(logging.StreamHandler())\nms_agents_logger.setLevel(logging.INFO)\n\n# Load configuration\nload_dotenv()\nagents_sdk_config = load_configuration_from_env(environ)\n\n# Create storage and connection manager\nSTORAGE = MemoryStorage()\nCONNECTION_MANAGER = MsalConnectionManager(**agents_sdk_config)\nADAPTER = CloudAdapter(connection_manager=CONNECTION_MANAGER)\nAUTHORIZATION = Authorization(STORAGE, CONNECTION_MANAGER, **agents_sdk_config)\n\n# Create AgentApplication\nAGENT_APP = AgentApplication[TurnState](\n    storage=STORAGE, adapter=ADAPTER, authorization=AUTHORIZATION, **agents_sdk_config\n)\n\n\n@AGENT_APP.conversation_update(\"membersAdded\")\nasync def on_members_added(context: TurnContext, _state: TurnState):\n    await context.send_activity(\"Welcome to the agent!\")\n\n\n@AGENT_APP.activity(\"message\")\nasync def on_message(context: TurnContext, _state: TurnState):\n    await context.send_activity(f\"You said: {context.activity.text}\")\n\n\n@AGENT_APP.error\nasync def on_error(context: TurnContext, error: Exception):\n    await context.send_activity(\"The agent encountered an error.\")\n\n\n# Server setup\nasync def entry_point(req: Request) -&gt; Response:\n    agent: AgentApplication = req.app[\"agent_app\"]\n    adapter: CloudAdapter = req.app[\"adapter\"]\n    return await start_agent_process(req, agent, adapter)\n\n\nAPP = Application(middlewares=[jwt_authorization_middleware])\nAPP.router.add_post(\"/api/messages\", entry_point)\nAPP[\"agent_configuration\"] = CONNECTION_MANAGER.get_default_connection_configuration()\nAPP[\"agent_app\"] = AGENT_APP\nAPP[\"adapter\"] = AGENT_APP.adapter\n\nif __name__ == \"__main__\":\n    run_app(APP, host=\"localhost\", port=environ.get(\"PORT\", 3978))\n</code></pre>\n<h2>AgentApplication Routing</h2>\n<pre><code>import re\nfrom microsoft_agents.hosting.core import (\n    AgentApplication, TurnState, TurnContext, MessageFactory\n)\nfrom microsoft_agents.activity import ActivityTypes\n\nAGENT_APP = AgentApplication[TurnState](\n    storage=STORAGE, adapter=ADAPTER, authorization=AUTHORIZATION, **agents_sdk_config\n)\n\n# Welcome handler\n@AGENT_APP.conversation_update(\"membersAdded\")\nasync def on_members_added(context: TurnContext, _state: TurnState):\n    await context.send_activity(\"Welcome!\")\n\n# Regex-based message handler\n@AGENT_APP.message(re.compile(r\"^hello$\", re.IGNORECASE))\nasync def on_hello(context: TurnContext, _state: TurnState):\n    await context.send_activity(\"Hello!\")\n\n# Simple string message handler\n@AGENT_APP.message(\"/status\")\nasync def on_status(context: TurnContext, _state: TurnState):\n    await context.send_activity(\"Status: OK\")\n\n# Auth-protected message handler\n@AGENT_APP.message(\"/me\", auth_handlers=[\"GRAPH\"])\nasync def on_profile(context: TurnContext, state: TurnState):\n    token_response = await AGENT_APP.auth.get_token(context, \"GRAPH\")\n    if token_response and token_response.token:\n        # Use token to call Graph API\n        await context.send_activity(\"Profile retrieved\")\n\n# Invoke activity handler\n@AGENT_APP.activity(ActivityTypes.invoke)\nasync def on_invoke(context: TurnContext, _state: TurnState):\n    invoke_response = Activity(\n        type=ActivityTypes.invoke_response, value={\"status\": 200}\n    )\n    await context.send_activity(invoke_response)\n\n# Fallback message handler\n@AGENT_APP.activity(\"message\")\nasync def on_message(context: TurnContext, _state: TurnState):\n    await context.send_activity(f\"Echo: {context.activity.text}\")\n\n# Error handler\n@AGENT_APP.error\nasync def on_error(context: TurnContext, error: Exception):\n    await context.send_activity(\"An error occurred.\")\n</code></pre>\n<h2>Streaming Responses with Azure OpenAI</h2>\n<pre><code>from openai import AsyncAzureOpenAI\nfrom azure.identity import DefaultAzureCredential, get_bearer_token_provider\nfrom microsoft_agents.activity import SensitivityUsageInfo\n\n# AAD token provider (preferred over AZURE_OPENAI_API_KEY)\ntoken_provider = get_bearer_token_provider(\n    DefaultAzureCredential(),\n    \"https://cognitiveservices.azure.com/.default\",\n)\n\n# Module-level singleton: client lives for the agent app lifetime.\nCLIENT = AsyncAzureOpenAI(\n    api_version=environ[\"AZURE_OPENAI_API_VERSION\"],\n    azure_endpoint=environ[\"AZURE_OPENAI_ENDPOINT\"],\n    azure_ad_token_provider=token_provider,\n)\n\n@AGENT_APP.message(\"poem\")\nasync def on_poem_message(context: TurnContext, _state: TurnState):\n    # Configure streaming response\n    context.streaming_response.set_feedback_loop(True)\n    context.streaming_response.set_generated_by_ai_label(True)\n    context.streaming_response.set_sensitivity_label(\n        SensitivityUsageInfo(\n            type=\"https://schema.org/Message\",\n            schema_type=\"CreativeWork\",\n            name=\"Internal\",\n        )\n    )\n    context.streaming_response.queue_informative_update(\"Starting a poem...\\n\")\n\n    # Stream from Azure OpenAI\n    streamed_response = await CLIENT.chat.completions.create(\n        model=\"gpt-4o\",\n        messages=[\n            {\"role\": \"system\", \"content\": \"You are a creative assistant.\"},\n            {\"role\": \"user\", \"content\": \"Write a poem about Python.\"}\n        ],\n        stream=True,\n    )\n\n    try:\n        async for chunk in streamed_response:\n            if chunk.choices and chunk.choices[0].delta.content:\n                context.streaming_response.queue_text_chunk(\n                    chunk.choices[0].delta.content\n                )\n    finally:\n        await context.streaming_response.end_stream()\n</code></pre>\n<h2>OAuth / Auto Sign-In</h2>\n<pre><code>@AGENT_APP.message(\"/logout\")\nasync def logout(context: TurnContext, state: TurnState):\n    await AGENT_APP.auth.sign_out(context, \"GRAPH\")\n    await context.send_activity(MessageFactory.text(\"You have been logged out.\"))\n\n\n@AGENT_APP.message(\"/me\", auth_handlers=[\"GRAPH\"])\nasync def profile_request(context: TurnContext, state: TurnState):\n    user_token_response = await AGENT_APP.auth.get_token(context, \"GRAPH\")\n    if user_token_response and user_token_response.token:\n        # Use token to call Microsoft Graph\n        async with aiohttp.ClientSession() as session:\n            headers = {\n                \"Authorization\": f\"Bearer {user_token_response.token}\",\n                \"Content-Type\": \"application/json\",\n            }\n            async with session.get(\n                \"https://graph.microsoft.com/v1.0/me\", headers=headers\n            ) as response:\n                if response.status == 200:\n                    user_info = await response.json()\n                    await context.send_activity(f\"Hello, {user_info['displayName']}!\")\n</code></pre>\n<h2>Copilot Studio Client (Direct to Engine)</h2>\n<pre><code>import asyncio\nfrom msal import PublicClientApplication\nfrom microsoft_agents.activity import ActivityTypes, load_configuration_from_env\nfrom microsoft_agents.copilotstudio.client import (\n    ConnectionSettings,\n    CopilotClient,\n)\n\n# Token cache (local file for interactive flows)\nclass LocalTokenCache:\n    # See samples for full implementation\n    pass\n\ndef acquire_token(settings, app_client_id, tenant_id):\n    pca = PublicClientApplication(\n        client_id=app_client_id,\n        authority=f\"https://login.microsoftonline.com/{tenant_id}\",\n    )\n\n    token_request = {\"scopes\": [\"https://api.powerplatform.com/.default\"]}\n    accounts = pca.get_accounts()\n\n    if accounts:\n        response = pca.acquire_token_silent(token_request[\"scopes\"], account=accounts[0])\n        return response.get(\"access_token\")\n    else:\n        response = pca.acquire_token_interactive(**token_request)\n        return response.get(\"access_token\")\n\n\nasync def main():\n    settings = ConnectionSettings(\n        environment_id=environ.get(\"COPILOTSTUDIOAGENT__ENVIRONMENTID\"),\n        agent_identifier=environ.get(\"COPILOTSTUDIOAGENT__SCHEMANAME\"),\n    )\n\n    token = acquire_token(\n        settings,\n        app_client_id=environ.get(\"COPILOTSTUDIOAGENT__AGENTAPPID\"),\n        tenant_id=environ.get(\"COPILOTSTUDIOAGENT__TENANTID\"),\n    )\n\n    # CopilotClient does not implement the context manager protocol.\n    copilot_client = CopilotClient(settings, token)\n\n    # Start conversation\n    act = copilot_client.start_conversation(True)\n    async for action in act:\n        if action.text:\n            print(action.text)\n\n    # Ask question\n    replies = copilot_client.ask_question(\"Hello!\", action.conversation.id)\n    async for reply in replies:\n        if reply.type == ActivityTypes.message:\n            print(reply.text)\n\n\nasyncio.run(main())\n</code></pre>\n<h2>Best Practices</h2>\n<ol>\n<li><strong>This SDK is async-first — use <code>async def</code> handlers and <code>async with</code> throughout.</strong></li>\n<li><strong>Always use context managers for clients and async credentials.</strong> Wrap every client in <code>with Client(...) as client:</code> (sync) or <code>async with Client(...) as client:</code> (async). For async <code>DefaultAzureCredential</code> from <code>azure.identity.aio</code>, also use <code>async with credential:</code> so tokens and transports are cleaned up.</li>\n<li>Use <code>microsoft_agents</code> import prefix (underscores, not dots).</li>\n<li>Use <code>MemoryStorage</code> only for development; use BlobStorage or CosmosDB in production.</li>\n<li>Always use <code>load_configuration_from_env(environ)</code> to load SDK configuration.</li>\n<li>Include <code>jwt_authorization_middleware</code> in aiohttp Application middlewares.</li>\n<li>Use <code>MsalConnectionManager</code> for MSAL-based authentication.</li>\n<li>Call <code>end_stream()</code> in finally blocks when using streaming responses.</li>\n<li>Use <code>auth_handlers</code> parameter on message decorators for OAuth-protected routes.</li>\n<li>Keep secrets in environment variables, not in source code.</li>\n</ol>\n<h2>Reference Links</h2>\n<table>\n<thead>\n<tr>\n<th>Resource</th>\n<th>URL</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Microsoft 365 Agents SDK</td>\n<td><a href=\"https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/\">https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/</a></td>\n</tr>\n<tr>\n<td>GitHub samples (Python)</td>\n<td><a href=\"https://github.com/microsoft/Agents-for-python\">https://github.com/microsoft/Agents-for-python</a></td>\n</tr>\n<tr>\n<td>PyPI packages</td>\n<td><a href=\"https://pypi.org/search/?q=microsoft-agents\">https://pypi.org/search/?q=microsoft-agents</a></td>\n</tr>\n<tr>\n<td>Integrate with Copilot Studio</td>\n<td><a href=\"https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/integrate-with-mcs\">https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/integrate-with-mcs</a></td>\n</tr>\n</tbody>\n</table>\n<h2>Reference Files</h2>\n<table>\n<thead>\n<tr>\n<th>File</th>\n<th>Contents</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"references/capabilities.md\">references/capabilities.md</a></td>\n<td>Additional non-hero capabilities, operation-group coverage, and production checklists.</td>\n</tr>\n</tbody>\n</table>\n","files":[{"path":"references/capabilities.md","sizeBytes":2825,"isText":true},{"path":"SKILL.md","sizeBytes":13854,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-08-12T21:50:29.892443Z","sha256":"9DDC611F163D0A0A9EB9533B23A0EA5F1B3760C20F59666511CF9C61302A3597","sizeBytes":5981},"review":null,"source":{"repositoryUrl":"https://github.com/microsoft/skills","path":".github/plugins/azure-sdk-python/skills/m365-agents-py","license":"MIT","commit":"23d0dac5f83f268166a17f0bc7dc6c73dc348a33","subtreeSha":"5CDAE657EEF9F79C260AFBE95D52AE8CBEE761DC363CD6518DC76BF359B554CE","lastSyncedAt":"2026-09-25T06:48:53.330584Z"},"reviewedAt":"2026-08-12T21:54:01.784169Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-python/skills/m365-agents-py"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install microsoft-skills@llmmart"},{"target":"git","command":"git clone https://github.com/microsoft/skills.git"}]}