Back to Copilotkit

Microsoft Teams

showcase/shell-docs/src/content/docs/microsoft-teams.mdx

1.60.110.2 KB
Original Source

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";

What you will build

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.

ProcessPortPurpose
CopilotKit runtime8200Runs a BuiltInAgent and exposes /api/copilotkit
Teams bot3978Receives Teams activities at POST /api/messages
Teams DevTools3979Local browser UI for testing Teams messages without a Teams account

Prerequisites

For local development:

For sideloading into Microsoft Teams:

Build and verify locally

<Steps> <Step>

Create the Node project

Start 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
```
</Tab> <Tab value="pnpm">
```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
```
</Tab> <Tab value="yarn">
```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
```
</Tab> </Tabs> </Step> <Step>

Add TypeScript

Create tsconfig.json:

json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  }
}
</Step> <Step>

Add the CopilotKit runtime and Teams bot

Create bot.ts. This single file starts both the CopilotKit runtime and the Teams-facing bot:

ts
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:

txt
copilotkit-teams-bot/
  package.json
  tsconfig.json
  bot.ts
</Step> <Step>

Run the bot

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
```
</Tab> <Tab value="Windows PowerShell">
```powershell
$env:OPENAI_API_KEY="sk-..."
npx tsx bot.ts
```
</Tab> </Tabs>

Open http://localhost:3979/devtools and send:

txt
Hello from Teams
<Callout type="success" title="Local verification"> If you receive a response card in DevTools, the CopilotKit runtime and Teams bot are working locally. </Callout> </Step> </Steps>

Sideload into Microsoft 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.

<Steps> <Step>

Create a public tunnel

In a separate terminal, expose the bot port:

bash
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:

txt
https://<your-tunnel-host>/api/messages
</Step> <Step>

Create Microsoft credentials

In Microsoft Entra ID:

  1. Create an app registration.
  2. Copy the Application (client) ID. This is your Teams bot ID.
  3. Copy the Directory (tenant) ID.
  4. Create a client secret and copy its value.

In Azure Bot Service:

  1. Create a bot resource that uses the app registration from Entra ID.
  2. Set the messaging endpoint to https://<your-tunnel-host>/api/messages.
  3. Enable the Microsoft Teams channel.

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
```
</Tab> <Tab value="Windows PowerShell">
```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
```
</Tab> </Tabs> </Step> <Step>

Create the Teams app package

Create appPackage/manifest.json:

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 192
  • appPackage/outline.png: 32 x 32, transparent background
txt
appPackage/
  manifest.json
  color.png
  outline.png

Package the manifest:

bash
cd appPackage
zip -r appPackage.local.zip manifest.json color.png outline.png
</Step> <Step>

Upload and test in Teams

In Microsoft Teams:

  1. Go to Apps.
  2. Select Manage your apps.
  3. Select Upload a custom app.
  4. Choose appPackage/appPackage.local.zip.
  5. Open a personal chat with the bot and send Hello.

You should receive the same response you saw in DevTools.

</Step> </Steps>

Troubleshooting

<Accordions type="single" collapsible> <Accordion title="DevTools opens but messages do not answer"> Confirm `OPENAI_API_KEY` is set in the terminal running `npx tsx bot.ts`, and check that `http://localhost:8200/api/copilotkit` is not blocked by another process. </Accordion> <Accordion title="Teams says the bot cannot be reached"> Confirm the Azure Bot messaging endpoint is exactly `https://<your-tunnel-host>/api/messages`, and that `devtunnel host copilotkit-teams-local` is still running. </Accordion> <Accordion title="Teams returns auth errors"> Confirm `CLIENT_ID`, `CLIENT_SECRET`, and `TENANT_ID` match the Entra app registration used by the Azure Bot resource. </Accordion> <Accordion title="Upload a custom app is missing"> Your Microsoft 365 tenant does not allow sideloading for your account. Ask a tenant admin to enable custom app upload for Teams. </Accordion> <Accordion title="Port 3978 or 8200 is already in use"> Stop the process using that port, or set `PORT` and `RUNTIME_PORT` before running `npx tsx bot.ts`. </Accordion> </Accordions>