showcase/shell-docs/src/content/docs/channels/platforms/teams.mdx
Connect Teams yourself with the Teams adapter when you want to hold the platform credentials. If you would rather CopilotKit hold them, use a managed channel instead. Either way the runtime runs the channel, so a CopilotKit Intelligence key is required (free tier available).
npm install @copilotkit/channels @copilotkit/runtime @tanstack/ai @tanstack/ai-openai
The Teams adapter authenticates with an Entra (Azure AD) app. It reads
clientId, clientSecret, and tenantId from the environment, or you can
pass them directly.
clientId=...
clientSecret=...
tenantId=...
# The runtime runs every channel, so a CopilotKit Intelligence key is required
# even for a direct adapter (free tier available).
COPILOTKIT_API_KEY=cpk-...
The adapter serves the Bot Framework messaging endpoint at POST /api/messages.
import { createChannel } from "@copilotkit/channels";
import { teams } from "@copilotkit/channels/teams";
import { CopilotRuntime, CopilotKitIntelligence } from "@copilotkit/runtime/v2";
import { createCopilotNodeListener } from "@copilotkit/runtime/v2/node";
import { agent } from "./agent"; // your TanStack AI agent, see the Quickstart
const channel = createChannel({
name: "support-bot",
agent,
adapters: [
teams({
port: 3978, // Bot Framework endpoint at /api/messages
// clientId, clientSecret, tenantId fall back to env
}),
],
});
channel.onMessage(async ({ thread, message }) => {
await thread.runAgent({ prompt: message.text });
});
// The runtime drives the channel — it starts the Teams adapter, you don't call
// channel.start() yourself. `ready()` activates it.
const runtime = new CopilotRuntime({
agents: {},
intelligence: new CopilotKitIntelligence({
// apiUrl and wsUrl default to the managed Intelligence platform. Override
// both together only for a self-hosted deployment.
apiKey: process.env.COPILOTKIT_API_KEY!,
}),
identifyUser: () => ({ id: "demo-user", name: "Demo User" }),
channels: [channel],
});
const listener = createCopilotNodeListener({ runtime });
await listener.channels.ready();
You need an Entra app, an Azure Bot resource pointing at your public
/api/messages URL with the Teams channel enabled, and a sideloaded Teams app
package. For a tunnel during local development, point the Azure Bot messaging
endpoint at your public tunnel URL.
Teams accepts image attachments. thread.postFile with an image mime type
posts an inline image. See files and
multimodality.