Microsoft announced native SDK support for Teams agents in July 2026. You can now build agents in TypeScript, C#, or Python that live directly inside Teams — not as bots bolted on from the outside, but as first-class participants in channels and chats, capable of acting on conversations, calling APIs, and handing work off to other agents.
This post covers the architecture, the build workflow, how authentication actually works under the hood, and — critically for anyone managing an enterprise fleet — what IT admins need to audit before any of this goes live.
What a Teams agent actually is
There are two types of agents that can run in Microsoft Teams. Understanding the difference matters before you write a single line of code or approve a single Teams app policy.
| Type | How it works | Governance scope |
|---|---|---|
| Declarative agent | Configured in JSON — no code. Defines scope, instructions, and data connectors. Runs on Copilot's inference engine. | Copilot Studio / M365 admin centre |
| SDK agent (custom) | Built with Teams SDK in TypeScript, C#, or Python. Full control over logic, auth, tool calls, and agent-to-agent routing. | Teams Admin Centre — app policies, bot permissions |
SDK agents have two interaction modes: private (the agent talks directly with one user in a 1:1 chat — like a DM with a bot) and public (the agent is installed into a team or channel and can read messages it is mentioned in). The distinction matters for data access — a channel-installed agent can potentially see far more conversation history than a 1:1 agent.
Scaffolding your first agent
Microsoft ships a CLI to generate the project structure. Install it globally and scaffold a new project:
npm install -g @microsoft/teams.cli
teams project new typescript my-agent
The scaffold creates the directory structure, a TypeScript config, and a starter manifest.json for Teams app registration. You get a working echo agent out of the box — it replies to messages it is mentioned in.
The SDK handles three things you would otherwise build yourself:
| SDK responsibility | What it replaces |
|---|---|
| Activity routing | Manual webhook parsing and activity type dispatch |
| Token management | Bot Framework credential refresh and JWT validation |
| Adaptive Card rendering | Raw JSON card authoring and action handling |
The app manifest — what IT admins actually care about
Every Teams agent ships with an appPackage/manifest.json. This file declares what the agent is allowed to do. Here is a minimal example with the fields your governance review should focus on:
{
"manifestVersion": "1.17",
"id": "your-app-guid",
"name": { "short": "My Agent" },
"bots": [
{
"botId": "your-bot-app-registration-id",
"scopes": ["personal", "team"],
"isNotificationOnly": false
}
],
"permissions": ["identity", "messageTeamMembers"],
"validDomains": ["your-backend.azurewebsites.net"]
}
scopes field controls where the agent can be installed. "team" scope means it can be added to any channel in the tenant — not just the team it was built for. Lock this down in Teams Admin Centre app policies before approving the app for org-wide distribution.
Authentication — two layers, not one
Teams agent authentication has two distinct layers. Most developers focus on the second one and get confused when the first one blocks them.
Layer 1 — Bot Channel Service (JWT): Every inbound activity from Teams is signed with a JWT from the Bot Framework. The SDK validates this automatically. If you are self-hosting the agent backend (not on Azure Bot Service), you must validate the JWT yourself or use the SDK's built-in middleware. Skip this and any caller can POST fake activities to your endpoint.
Layer 2 — Entra ID app registration: This is the Entra app that backs the bot registration. It governs what the agent can call via Microsoft Graph. The permissions granted here are what allow the agent to read calendar events, send emails, query users, or access SharePoint. This is the layer IT admins must review and approve in the Entra admin centre.
Mail.Read as an application permission means the agent can read every mailbox in the tenant. Never approve application permissions without understanding their full blast radius.
Registering MCP tools
The Teams SDK supports MCP (Model Context Protocol) tool registration. This is how you give the agent the ability to call external APIs — your internal ITSM system, a product database, a ticketing tool — without hard-coding the calls into the agent logic.
Here is how tool registration looks in TypeScript:
import { app, McpServer } from '@microsoft/teams.apps';
const mcpServer = new McpServer({ name: 'my-tools', version: '1.0.0' });
mcpServer.tool(
'get-ticket',
'Retrieve a support ticket by ID from the ITSM system',
{ ticketId: { type: 'string', description: 'The ITSM ticket ID' } },
async ({ ticketId }) => {
const ticket = await itsmClient.getTicket(ticketId);
return { content: [{ type: 'text', text: JSON.stringify(ticket) }] };
}
);
app.use(mcpServer);
From the agent's perspective, registered tools are callable capabilities the model can decide to invoke. The SDK handles the tool-call loop — the model responds with a tool call, the SDK executes the registered handler, and the result goes back into the model context.
Agent-to-agent communication — and why IT admins need to care
The Teams SDK supports routing conversations from one agent to another. A triage agent can hand off to a specialist agent. This sounds useful — and it is — but it creates a governance gap that is easy to miss.
IT admin governance checklist
Before any Teams SDK agent goes live in production, run through these four checks:
team scope or is personal sufficient? Restrict to the minimum scope in Teams Admin Centre app setup policies.Finding Teams bot app registrations with PowerShell
If you want an inventory of all Teams bot app registrations in your tenant before agents start proliferating, this Graph query will give you a starting point:
Connect-MgGraph -Scopes "Application.Read.All"
# Find all app registrations that have bot tags
$apps = Get-MgApplication -All | Where-Object {
$_.Tags -contains "HideApp" -or
$_.RequiredResourceAccess.Count -gt 0
}
# Filter for those with Bot Framework service access
$botApps = $apps | Where-Object {
$_.RequiredResourceAccess | Where-Object {
# Bot Framework service resource ID
$_.ResourceAppId -eq "bf4a9c9e-3592-4f13-8428-a5c7a3a59e6b"
}
}
$botApps | Select-Object DisplayName, AppId, CreatedDateTime |
Export-Csv -Path ".\TeamsBotApps.csv" -NoTypeInformation
Write-Host "$($botApps.Count) Teams bot app registrations found"
Cross-reference the output against your approved app list in Teams Admin Centre. Any app registration that appears here but is not in your approved app policy is a potential governance gap.