showcase/shell-docs/src/content/docs/microsoft-teams.mdx
import { Accordions, Accordion } from "fumadocs-ui/components/accordion"; import { Callout } from "fumadocs-ui/components/callout"; import { Step, Steps } from "fumadocs-ui/components/steps"; import { Tab, Tabs } from "fumadocs-ui/components/tabs";
By the end of this guide, you will have a small Node app that can answer Microsoft Teams messages with a CopilotKit agent. You will test it locally first with Teams DevTools, then optionally expose the same bot through Azure Bot Service and sideload it into Microsoft Teams.
@copilotkit/bot-teams handles the Teams-specific work: receiving Teams activities, rendering
Adaptive Cards, streaming updates, and running the local DevTools bridge. Your app provides the
CopilotKit runtime, agent configuration, Microsoft credentials, tunnel, and Teams manifest.
| Process | Port | Purpose |
|---|---|---|
| CopilotKit runtime | 8200 | Runs a BuiltInAgent and exposes /api/copilotkit |
| Teams bot | 3978 | Receives Teams activities at POST /api/messages |
| Teams DevTools | 3979 | Local browser UI for testing Teams messages without a Teams account |
For local development:
For sideloading into Microsoft Teams:
devtunnel CLIStart with a new Node project and install the CopilotKit runtime, core package, and Teams bridge:
<Tabs items={["npm", "pnpm", "yarn"]}> <Tab value="npm">
```bash
mkdir copilotkit-teams-bot
cd copilotkit-teams-bot
npm init -y
npm pkg set type=module
npm install @copilotkit/bot-teams @copilotkit/core @copilotkit/runtime
npm install -D tsx typescript @types/node
```
```bash
mkdir copilotkit-teams-bot
cd copilotkit-teams-bot
pnpm init
pnpm pkg set type=module
pnpm add @copilotkit/bot-teams @copilotkit/core @copilotkit/runtime
pnpm add -D tsx typescript @types/node
```
```bash
mkdir copilotkit-teams-bot
cd copilotkit-teams-bot
yarn init -y
yarn config set nodeLinker node-modules
npm pkg set type=module
yarn add @copilotkit/bot-teams @copilotkit/core @copilotkit/runtime
yarn add -D tsx typescript @types/node
```
Create tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}
Create bot.ts. This single file starts both the CopilotKit runtime and the Teams-facing bot:
import { createServer } from "node:http";
import { CopilotKitCore, ProxiedCopilotRuntimeAgent } from "@copilotkit/core";
import { createTeamsAgentBot } from "@copilotkit/bot-teams/bot";
import { BuiltInAgent, CopilotSseRuntime } from "@copilotkit/runtime/v2";
import { createCopilotNodeListener } from "@copilotkit/runtime/v2/node";
const agentId = "assistant";
const runtimePort = Number(process.env.RUNTIME_PORT ?? 8200);
const botPort = Number(process.env.PORT ?? 3978);
const runtimeUrl = `http://localhost:${runtimePort}/api/copilotkit`;
const runtime = new CopilotSseRuntime({
agents: {
[agentId]: new BuiltInAgent({
model: process.env.OPENAI_MODEL ?? "openai/gpt-4o-mini",
prompt:
"You are a helpful Microsoft Teams assistant. Keep replies concise and useful.",
}),
},
});
createServer(
createCopilotNodeListener({
runtime,
basePath: "/api/copilotkit",
}),
).listen(runtimePort, () => {
console.log(`CopilotKit runtime listening at ${runtimeUrl}`);
});
const core = new CopilotKitCore({ runtimeUrl });
core.setDefaultThrottleMs(1000);
core.addAgent__unsafe_dev_only({
id: agentId,
agent: new ProxiedCopilotRuntimeAgent({
agentId,
runtimeAgentId: agentId,
runtimeUrl,
}),
});
const bot = createTeamsAgentBot({
core,
agentId,
approvalTimeoutMs: 5 * 60 * 1000,
reviewerName: "Reviewer",
});
await bot.start(botPort);
console.log(`Teams bot listening at http://localhost:${botPort}/api/messages`);
console.log(`Teams DevTools available at http://localhost:${botPort + 1}/devtools`);
Your project should now look like this:
copilotkit-teams-bot/
package.json
tsconfig.json
bot.ts
Set your OpenAI key and start the bot:
<Tabs items={["macOS/Linux", "Windows PowerShell"]}> <Tab value="macOS/Linux">
```bash
export OPENAI_API_KEY=sk-...
npx tsx bot.ts
```
```powershell
$env:OPENAI_API_KEY="sk-..."
npx tsx bot.ts
```
Open http://localhost:3979/devtools and send:
Hello from Teams
The local DevTools path does not require Microsoft credentials. The real Teams client does. Keep the
same bot.ts; add a tunnel, a Microsoft app registration, an Azure Bot resource, and a Teams app
manifest.
In a separate terminal, expose the bot port:
devtunnel create copilotkit-teams-local -a
devtunnel port create copilotkit-teams-local -p 3978
devtunnel host copilotkit-teams-local
Copy the HTTPS tunnel URL. Your bot messaging endpoint will be:
https://<your-tunnel-host>/api/messages
In Microsoft Entra ID:
In Azure Bot Service:
https://<your-tunnel-host>/api/messages.Run the bot with those credentials:
<Tabs items={["macOS/Linux", "Windows PowerShell"]}> <Tab value="macOS/Linux">
```bash
export OPENAI_API_KEY=sk-...
export CLIENT_ID=<application-client-id>
export CLIENT_SECRET=<client-secret-value>
export TENANT_ID=<directory-tenant-id>
npx tsx bot.ts
```
```powershell
$env:OPENAI_API_KEY="sk-..."
$env:CLIENT_ID="<application-client-id>"
$env:CLIENT_SECRET="<client-secret-value>"
$env:TENANT_ID="<directory-tenant-id>"
npx tsx bot.ts
```
Create appPackage/manifest.json:
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.19/MicrosoftTeams.schema.json",
"manifestVersion": "1.19",
"version": "1.0.0",
"id": "<new-teams-app-uuid>",
"developer": {
"name": "Your company",
"websiteUrl": "https://example.com",
"privacyUrl": "https://example.com/privacy",
"termsOfUseUrl": "https://example.com/terms"
},
"name": {
"short": "CopilotKit Bot",
"full": "CopilotKit Teams Bot"
},
"description": {
"short": "A CopilotKit assistant for Microsoft Teams.",
"full": "A Microsoft Teams bot powered by CopilotKit."
},
"icons": {
"outline": "outline.png",
"color": "color.png"
},
"accentColor": "#5B5FC7",
"bots": [
{
"botId": "<application-client-id>",
"scopes": ["personal"],
"supportsFiles": false,
"isNotificationOnly": false
}
],
"validDomains": ["<your-tunnel-host>"]
}
Replace:
<new-teams-app-uuid> with a new UUID for this Teams app package<application-client-id> with the Entra application client ID<your-tunnel-host> with the tunnel host only, without https://Add the required Teams icons:
appPackage/color.png: 192 x 192appPackage/outline.png: 32 x 32, transparent backgroundappPackage/
manifest.json
color.png
outline.png
Package the manifest:
cd appPackage
zip -r appPackage.local.zip manifest.json color.png outline.png
In Microsoft Teams:
appPackage/appPackage.local.zip.Hello.You should receive the same response you saw in DevTools.
</Step> </Steps>