Microsoft Teams
Agent Kernel supports integration with Microsoft Teams via the Azure Bot Framework. This allows you to deploy agents that can communicate in 1:1 chats, group chats, and Teams channels, supporting text, images, and file attachments.
How It Works
- Azure Bot Service: Acts as the bridge between the Teams client and your Agent Kernel server.
- Webhook: Your Agent Kernel server exposes a
/teams/messagesendpoint that receives activities from Azure. - Agent Processing: Incoming messages are routed to your configured Agent.
- Response: The Agent's reply is sent back to the conversation via the Bot Framework Connector.
Setup Guide
Setting up a Teams bot involves three main parts: Azure, your Agent Kernel Server, and the Teams Developer Portal.
1. Azure App Registration
- Log in to the Azure Portal.
- Go to App registrations > New registration.
- Name: Enter your bot's name (e.g., "Agent Bot").
- Supported account types: Select "Accounts in any organizational directory (Any Microsoft Entra ID tenant - Multitenant)".
- Click Register.
- Copy the Application (client) ID. You'll use this as
AK_TEAMS__APP_ID. - Go to Certificates & secrets > New client secret. Create one and copy the Value. You'll use this as
AK_TEAMS__APP_PASSWORD.
2. Create Azure Bot Resource
- Search for Azure Bot in the Azure Portal and click Create.
- Handle: Pick a unique handle.
- Type of App: Select Multi Tenant.
- Creation type: Use existing app registration. Paste the App ID from Step 1.
- Click Review + create.
3. Configure Webhook
- In your new Azure Bot resource, go to Configuration.
- Messaging endpoint: Enter your server's public URL +
/teams/messages.- Example:
https://your-server.com/teams/messages
- Example:
- Click Apply.
- Go to Channels and add Microsoft Teams. accepting the terms.
4. Teams Developer Portal
- Open the Developer Portal app inside Microsoft Teams.
- Create a New app.
- In App features, select Bot.
- Select an existing bot and paste your Client ID (from Step 1).
- Select scopes (Personal, Team, Group Chat) and Save.
- Go to Publish to org to submit for admin approval, or Preview in Teams to test immediately.
Configuration
Pick the agent in config.yaml:
teams:
agent: "general" # Default agent for Teams messages
agent_acknowledgement: "I'm looking into that..." # Optional: sent as soon as a message arrives
tenant_id: "" # Optional: the bot's own tenant; see "Tenant ID" below
Supply the bot credentials as environment variables, so secrets stay out of the config file:
# Required
export AK_TEAMS__APP_ID="your-azure-client-id"
export AK_TEAMS__APP_PASSWORD="your-azure-client-secret"
# Optional
export AK_TEAMS__TENANT_ID="your-tenant-id"
Every key is settable either way: teams.agent is also AK_TEAMS__AGENT, and so on.
Features
- Text Messaging: Full support for 1:1, group chat, and channel conversations.
- File Attachments: Uploaded files are downloaded and passed to the agent. Audio and video are
rejected, and anything over
api.max_file_sizeis refused while streaming rather than buffered. - Images: Images pasted or dragged into the chat are fetched from the Bot Connector using the bot's own credentials and passed to the agent.
- Mentions: The bot's own
@BotNamemention is stripped from the prompt. Mentions of other people keep their display name, and text that merely looks like a handle — an email address, a@decorator— is left untouched. - Long replies: Replies larger than a single Teams message are split across several messages.
Tenant ID
teams.tenant_id is the Entra ID tenant that owns the bot's own app registration, matching the
Bot Framework SDK's MicrosoftAppTenantId:
- Multi-tenant registration (
signInAudience: AzureADMultipleOrgs, and the Azure Bot's app type set to MultiTenant): leave it empty. Channel tokens are then issued by the Bot Framework tenant. - Single-tenant registration (
AzureADMyOrg): set it to that tenant, whose authority is the only one that can issue the tokens. Leaving it empty fails withAADSTS700016.
The two must agree — check the app with az ad app show --id <app-id> --query signInAudience and the
bot with az bot show -n <bot> -g <group> --query "properties.msaAppType".
It is a different tenant from the one an app-only attachment download needs — that one belongs to the customer whose Teams the message came from, and is read off the incoming activity, falling back to this value only when the activity carries none.
Attachment downloads
Most files Teams delivers carry a pre-authenticated downloadUrl, which the handler fetches with no
extra credentials — that is the common path and it needs no Azure permissions at all.
When a download URL is not pre-authenticated, the handler falls back to an app-only (client credentials) token for the host serving the file. That fallback needs:
- A tenant to mint the token in. The client credentials grant is not valid against the
/commonauthority, so a specific tenant is required. The handler prefers the tenant on the incoming activity and usesteams.tenant_idas the fallback; if neither is available the download is refused with a clear message rather than being retried unauthenticated. - A SharePoint application permission —
Sites.Read.Allunder Office 365 SharePoint Online, with admin consent — because the token requested is for the SharePoint resource serving the file, not for Microsoft Graph.
A bearer token is only ever sent to a host it was minted for. An unrecognised download host is
fetched without an Authorization header rather than being handed a token.
Troubleshooting
401 Unauthorized Downloads
If the bot fails to download files:
- Check the logs for
Cannot authorize the download of .... That means the app-only fallback was needed and could not be used — setteams.tenant_idand grant the SharePoint permission above. Direct download failed with status 401means the URL itself was rejected. Confirm the bot still has access to the file and that thedownloadUrlhas not expired.
Bot Not Responding
- Check Azure Bot Configuration to ensure the Messaging endpoint is correct and accessible.
- Verify your App ID and Password in environment variables match the Azure App Registration. A credential mismatch is answered with HTTP 401, which appears in your access logs.
The webhook returns 200 but no reply arrives
Inbound and outbound use different credentials: an incoming activity is validated against Bot Framework public keys and only has to match the App ID, while the reply needs a token minted with the App Password. So a broken outbound credential looks like silence, not an error.
Check the logs for Error sending reply to Teams: Failed to get access token:
AADSTS7000229— "missing service principal in the tenant". The app registration exists in that tenant but has no service principal, which is the state an app created withaz ad app createor the Graph API is left in. Create it:az ad sp create --id <app-id>.AADSTS700016— "application ... not found in the directory". The token was requested from the wrong tenant. If the directory in the message isd6d49420-f39b-4df7-a1dc-d59a935871db, that is the Bot Framework tenant, meaningteams.tenant_idwas left empty for a single-tenant registration. See "Tenant ID" below.
Duplicate replies
The agent runs outside the webhook turn (via a proactive continue_conversation follow-up), so a
slow agent cannot exceed the Bot Framework delivery timeout and make Azure redeliver the activity.
If you still see duplicates, check that only one instance is registered on the messaging endpoint.
