Connect a Claude Code session to Microsoft Teams via an MCP server running on Bun. Messages from Teams are delivered to your Claude session in real time; use the reply, react, and edit_message tools to respond.
- Bun runtime:
curl -fsSL https://bun.sh/install | bash - An Azure Bot registration (create one here)
- Copy the App ID, App Password, and Tenant ID
- See Bot permissions for required scopes
- A public HTTPS endpoint for the webhook (e.g., Tailscale Funnel, VPN, or Azure App Service) — see Security
git clone https://github.com/daocoding/claude-teams.git
cd claude-teamsCreate the state directory and save your Azure Bot credentials:
mkdir -p ~/.claude/channels/teamsThen launch Claude Code and run:
/teams:configure <APP_ID> <APP_PASSWORD> <TENANT_ID>
Or write ~/.claude/channels/teams/.env manually (see .env.example).
The server listens on port 3980 by default. Expose it via HTTPS and register the URL in your Azure Bot's Messaging endpoint:
https://your-domain.com/api/messages
Important: Do not use a raw public URL without network-level security. See Security: Webhook Authentication.
Development / pre-approval (now):
claude --load-development-channels /path/to/claude-teamsThis loads the plugin directly from your local clone.
Official plugin (future — once approved by Anthropic):
claude --channels plugin:teams-channel- Have the Teams user DM your bot — they'll receive a 6-character pairing code
- In your Claude Code terminal, run:
/teams:access pair <CODE> - Once all users are paired, lock it down:
/teams:access policy allowlist
| Tool | Description |
|---|---|
reply |
Send a message to a Teams conversation. Pass conversation_id from the inbound message. Optionally pass reply_to (activity_id) for threading. |
react |
React with an emoji (sent as a threaded reply — Teams Bot API has limited native reaction support). |
edit_message |
Update a previously sent bot message. Edits don't trigger push notifications. |
download_attachment |
Download an attachment by URL to the local inbox. Returns the file path. |
| Variable | Default | Description |
|---|---|---|
MICROSOFT_APP_ID |
— | Azure Bot App ID (required) |
MICROSOFT_APP_PASSWORD |
— | Azure Bot App Password (required) |
MICROSOFT_TENANT_ID |
— | Azure AD Tenant ID |
TEAMS_WEBHOOK_PORT |
3980 |
HTTP port for Bot Framework webhook |
TEAMS_WEBHOOK_PATH |
/api/messages |
Webhook URL path |
TEAMS_STATE_DIR |
~/.claude/channels/teams |
State directory |
See ACCESS.md for full details.
- DM policy:
pairing(default) →allowlist(production) - Group chats: must be explicitly added via
/teams:access group add <id> - Pending entries: auto-expire after 24 hours
Teams User ──Bot Framework──▶ Webhook (Bun HTTP) ──MCP notification──▶ Claude Code
│
Claude Code ──MCP tool call──▶ reply/react/edit ──Bot Framework API──▶ Teams User
The server runs as an MCP server connected to Claude Code via stdio. It simultaneously runs an HTTP server to receive Bot Framework webhook callbacks. Inbound messages are delivered as notifications/claude/channel MCP notifications; outbound messages use the Bot Framework REST API.
- Multi-user CLI sessions — route non-owner conversations to separate
claude --print --resumeprocesses, so multiple Teams users can interact with the bot concurrently - Owner DM routing — designate one conversation for live MCP delivery while others get independent CLI sessions
- Group workspace isolation — sandboxed working directories per group chat
- Outbound file attachments — send files from Claude to Teams
- Adaptive Cards — rich card formatting for structured responses
- JWT validation — verify Bot Framework tokens for production-grade webhook security
- No JWT validation: see Security: Webhook Authentication for mitigations.
- No message history: the Bot Framework webhook only delivers new messages. The assistant cannot retrieve earlier messages.
- Reactions: Teams Bot API has limited reaction support. The
reacttool sends the emoji as a threaded reply instead. - File attachments: images and files are downloaded to
~/.claude/channels/teams/inbox/. Images can be viewed with the Read tool. Sending files from Claude to Teams is not yet supported.
This plugin uses minimal permissions — only what's needed for messaging.
The bot authenticates with https://api.botframework.com/.default, which grants:
- Send and receive messages — reply to conversations, send typing indicators
- Edit messages — update previously sent bot messages
- No Graph API access — the bot cannot read email, calendar, files, or any other Microsoft 365 data
When registering the bot in Teams Admin Center or via app manifest:
| Permission | Why |
|---|---|
TeamMessagingSettings.Read |
Receive messages from chats |
ChatMessage.Send |
Send replies back |
The bot does not require or request:
User.Read.Allor any directory permissionsFiles.Read/Sites.Reador any SharePoint/OneDrive accessMail.Read/Calendars.Reador any Exchange access- Admin consent (single-tenant bots only need user-level consent)
This plugin is designed for single-tenant deployment (your own Azure AD tenant). Set MICROSOFT_TENANT_ID in your .env to restrict authentication to your tenant only. Do not leave it empty in production — an empty tenant ID falls back to botframework.com which allows any tenant.
This plugin does not validate Bot Framework JWT tokens on incoming webhook requests. This means anyone who discovers your webhook URL can send forged messages that will be delivered to your Claude Code session as if they came from a legitimate Teams user.
Risk: An attacker could impersonate an approved user or inject arbitrary prompts into your session.
Recommended mitigations (use at least one):
- Network-level security (strongly recommended) — expose the webhook only through Tailscale Funnel, a VPN, or Azure VNET. This ensures only trusted networks can reach the endpoint.
- Firewall rules — restrict inbound traffic to Microsoft's Bot Framework IP ranges.
- Reverse proxy with auth — place the webhook behind nginx/Caddy with mutual TLS or basic auth.
Do not expose the webhook on a public URL (e.g., raw ngrok) without one of the above. JWT validation is on the roadmap.
Apache-2.0