AI-native Microsoft Teams integration — read chats, send messages, search people
AI-native Microsoft Teams integration — read conversations, send messages, and manage members via the Teams Chat Service REST API.
Designed for autonomous AI agents that need to interact with Teams: read messages, reply to people, monitor conversations, and participate in team workflows.
<img src="https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white" alt="Install in VS Code">
<img src="https://img.shields.io/badge/VS_Code_Insiders-Install_Server-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white" alt="Install in VS Code Insiders">
[!NOTE] This project was AI-generated using Claude Opus 4.6 with human guidance and review.
teams-api can be used in three ways:
The quickest way to get started is to click one of the install badges above, or follow the instructions for your editor below.
Option 1 — One-click install:
Click the badge at the top of this README, or press Cmd+Shift+X / Ctrl+Shift+X, type @mcp in the search field, and look for Microsoft Teams API.
Option 2 — CLI:
# VS Code
code --add-mcp '{"name":"teams","command":"npx","args":["-y","-p","teams-api@latest","teams-api-mcp"],"env":{"TEAMS_LOGIN":"true"}}'
# VS Code Insiders
code-insiders --add-mcp '{"name":"teams","command":"npx","args":["-y","-p","teams-api@latest","teams-api-mcp"],"env":{"TEAMS_LOGIN":"true"}}'Option 3 — Manual config:
Add to your VS Code MCP config (.vscode/mcp.json or User Settings):
{
"mcpServers": {
"teams": {
"command": "npx",
"args": ["-y", "-p", "teams-api@latest", "teams-api-mcp"],
"env": {
"TEAMS_LOGIN": "true"
}
}
}
}Click the Install in Cursor badge at the top, or add to ~/.cursor/mcp.json:
{
"mcpServers": {
"teams": {
"command": "npx",
"args": ["-y", "-p", "teams-api@latest", "teams-api-mcp"],
"env": {
"TEAMS_LOGIN": "true"
}
}
}
}Add to claude_desktop_config.json (how to find it):
{
"mcpServers": {
"teams": {
"command": "npx",
"args": ["-y", "-p", "teams-api@latest", "teams-api-mcp"],
"env": {
"TEAMS_LOGIN": "true"
}
}
}
}claude mcp add teams -- npx -y -p teams-api@latest teams-api-mcpThen set the environment variable TEAMS_LOGIN=true in your shell before starting Claude Code. The server will ask for your email interactively on first use.
Add to ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"teams": {
"command": "npx",
"args": ["-y", "-p", "teams-api@latest", "teams-api-mcp"],
"env": {
"TEAMS_LOGIN": "true"
}
}
}
}[!TIP] On macOS with a FIDO2 passkey, replace
TEAMS_LOGINwithTEAMS_AUTOfor fully unattended auth. See Authentication for details.
You can also use the CLI directly without installing anything:
npx -y -p teams-api@latest teams-api auth --login
npx -y -p teams-api@latest teams-api list-conversations --login --limit 20If you use the CLI often, a global install is optional:
npm install -g teams-api
teams-api auth --loginManual token usage, debug-session auth, and programmatic Node.js usage are covered later in this README.
| Feature | macOS | Windows / Linux |
|---|---|---|
| Interactive login | Full support | Full support |
| Auto-login (FIDO2) | Full support | Not supported |
| Debug session | Full support | Full support |
| Direct token | Full support | Full support |
| Token caching | macOS Keychain | Not available |
| CLI & MCP server | Full support | Full support |
| Programmatic API | Full support | Full support |
Most users do not need to manage tokens manually.
If you use interactive login, auto-login, or a Chrome debug session, teams-api captures the full token bundle automatically from Teams web traffic. That includes the base skypeToken plus the extra bearer tokens used for profile resolution and reliable people/chat/channel search.
Those flows also detect the Teams chat region automatically from the intercepted request URLs, so most users do not need to set --region or TEAMS_REGION.
On macOS, prefer auto-login when you have a platform authenticator / FIDO2 passkey set up. On other platforms, use interactive login.
Direct token usage is the advanced/manual path.
| Method | Description | Automation | Platform |
|---|---|---|---|
| Auto-login | Playwright launches system Chrome and captures the full token bundle | Fully unattended | macOS |
| Interactive login | Opens a browser window and captures skype, middle-tier, and Substrate tokens | One-time manual | All |
| Debug session | Connects to a running Chrome instance and captures the full token bundle | Semi-manual | All |
| Direct token | Provide a previously captured token or token bundle explicitly | Manual | All |
Requires macOS with a platform authenticator (e.g. Intune Company Portal) and a FIDO2 passkey enrolled. Fully unattended — no browser window appears.
The easiest cross-platform option. A browser window opens, you log in with any method your organization supports (password, MFA, passkey, etc.), and the token is captured automatically:
teams-api auth --loginOptionally pre-fill your email:
teams-api auth --login --email you@example.com[!NOTE] Interactive login uses Playwright's bundled Chromium. No system Chrome installation is required.
Debug session — start Chrome with --remote-debugging-port=9222, navigate to Teams and log in, then run:
teams-api auth --debug-port 9222Direct token — advanced/manual only. Extract x-skypetoken from browser DevTools (Network tab) and pass it directly:
teams-api list-conversations --token "<paste-token-here>" --region emeaIf you want reliable people/chat/channel lookup and profile resolution on the direct-token path, also pass the extra bearer tokens captured from Teams requests:
teams-api find-people \
--token "<paste-skype-token-here>" \
--bearer-token "<paste-api-spaces-skype-bearer-token-here>" \
--substrate-token "<paste-substrate-bearer-token-here>" \
--region emea \
--query "Jane Doe"[!TIP] Skip this section if you are using
--login,--auto,TEAMS_LOGIN, orTEAMS_AUTO. Those modes capture the full token bundle automatically.
[!TIP] Direct-token mode still needs an explicit region. See API regions below.
Preferred without install:
npx -y -p teams-api@latest teams-api <command> [options]Optional global install for frequent use:
npm install -g teams-api
teams-api <command> [options]The examples below use teams-api for readability. If you are not installing globally, replace it with npx -y -p teams-api@latest teams-api.
| Flag | Description |
|---|---|
--login | Interactive browser login (all platforms) |
--auto | Auto-acquire token via FIDO2 passkey (macOS) |
--email <email> | Corporate email (required with --auto, optional otherwise) |
--token <token> | Use an existing skype token (advanced/manual) |
--bearer-token <token> | Optional middle-tier bearer token (advanced/manual) |
--substrate-token <token> | Optional Substrate bearer token (advanced/manual) |
--debug-port <port> | Chrome debug port (default: 9222) |
--region <region> | API region override. Auto-detected for login/debug auth; required with --token |
--format <format> | Output format: json, text, md, toon |
--output <file> | Export output to file (default format: md) |
# Acquire a token (interactive — all platforms)
teams-api auth --login
# Acquire a token (auto — macOS with FIDO2)
teams-api auth --auto --email you@example.com
# List conversations
teams-api list-conversations --login --limit 20 --format json
# Find a conversation by topic
teams-api find-conversation --auto --email you@example.com --query "Design Review"
# Find a 1:1 chat by person name
teams-api find-one-on-one --auto --email you@example.com --person-name "Jane Doe"
# Read messages (by topic name, person name, or direct ID)
teams-api get-messages --auto --email you@example.com --chat "Design Review"
teams-api get-messages --auto --email you@example.com --to "Jane Doe" --max-pages 5
teams-api get-messages --auto --email you@example.com --conversation-id "19:abc@thread.v2" --format json
# Newest-first order (API returns newest-first; default is oldest-first/chronological)
teams-api get-messages --auto --email you@example.com --chat "General" --order newest-first
# Send a message
teams-api send-message --auto --email you@example.com --to "Jane Doe" --content "Hello!"
teams-api send-message --auto --email you@example.com --chat "Design Review" --content "Status update"
# List members
teams-api get-members --auto --email you@example.com --chat "Design Review" --format md
# Get current user info
teams-api whoami --auto --email you@example.com
# Export messages to a file (default format: md)
teams-api get-messages --auto --email you@example.com --chat "General" --output exports/general.md
# Export as JSON to a file
teams-api get-messages --auto --email you@example.com --chat "General" --format json --output exports/general.json
# Toon format (fun ASCII art output)
teams-api list-conversations --auto --email you@example.com --format toonThe MCP server exposes Teams operations as tools for AI agents via stdio transport. See Getting Started for editor-specific setup.
Use this only if you already have tokens from another flow or need to avoid browser-based auth entirely:
{
"mcpServers": {
"teams": {
"command": "npx",
"args": ["-y", "-p", "teams-api@latest", "teams-api-mcp"],
"env": {
"TEAMS_TOKEN": "<paste-skype-token-here>",
"TEAMS_BEARER_TOKEN": "<optional-api-spaces-skype-bearer-token>",
"TEAMS_SUBSTRATE_TOKEN": "<optional-substrate-bearer-token>",
"TEAMS_REGION": "emea"
}
}
}
}[!TIP] If you do use direct tokens,
teams-api auth --loginprints the full token object as JSON. For basic chat operations,skypeTokenis enough. For reliable people/chat/channel search and profile resolution, also passbearerTokenandsubstrateToken.
| Variable | Description |
|---|---|
TEAMS_TOKEN | Pre-existing skype token |
TEAMS_BEARER_TOKEN | Optional middle-tier bearer token |
TEAMS_SUBSTRATE_TOKEN | Optional Substrate bearer token |
TEAMS_REGION | API region override. Required with TEAMS_TOKEN; optional otherwise |
TEAMS_EMAIL | Corporate email. Optional — the server prompts the AI agent if needed |
TEAMS_AUTO | Set to true to enable auto-login (macOS + FIDO2) |
TEAMS_LOGIN | Set to true to enable interactive browser login |
TEAMS_DEBUG_PORT | Chrome debug port (default: 9222) |
All MCP tools accept an optional format parameter (json, text, md, or toon). Default format is toon.
| Tool | Description |
|---|---|
teams_list_conversations | List available conversations |
teams_find_conversation | Find a conversation by topic or member name |
teams_find_one_on_one | Find a 1:1 chat with a person |
teams_find_people | Search the organization directory |
teams_find_chats | Search chats by name or member |
teams_get_messages | Get messages from a conversation |
teams_send_message | Send a message to a conversation |
teams_get_members | List members of a conversation |
teams_get_transcript | Get a meeting transcript from a recorded conversation |
teams_whoami | Get the authenticated user's display name |
Workflow guidance, tips, and important notes are served automatically via the MCP server's instructions field — no separate skill file needed. For the same content on the CLI, run teams-api guide.
The Teams Chat Service URL varies by region. Login-based and debug-session auth detect it automatically. You only need to set --region or TEAMS_REGION when you are supplying tokens directly or want to force an override:
| Region | Base URL |
|---|---|
apac | https://apac.ng.msg.teams.microsoft.com/v1 |
emea | https://emea.ng.msg.teams.microsoft.com/v1 |
amer | https://amer.ng.msg.teams.microsoft.com/v1 |
--login) instead.findOneOnOneConversation() to resolve names from message history.emotions field in message payloads. Parsing handles both JSON-string and array formats.This is the advanced integration path. Most users should start with MCP or CLI instead.
Install the package in your project:
npm install teams-apiExample:
import { TeamsClient } from "teams-api";
// Interactive login — opens a browser, you log in manually (all platforms)
const client = await TeamsClient.fromInteractiveLogin();
// Or auto-login via platform authenticator (macOS + FIDO2 passkey)
const autoClient = await TeamsClient.fromAutoLogin({
email: "you@example.com",
});
// Advanced/manual: create a client from previously captured tokens
const manualClient = TeamsClient.fromToken("skype-token-here", "emea");
const conversations = await client.listConversations();
const messages = await client.getMessages(conversations[0].id, {
maxPages: 5,
onProgress: (count) => console.log(`Fetched ${count} messages`),
});
await client.sendMessage(conversations[0].id, "Hello from the API!");
const oneOnOne = await client.findOneOnOneConversation("Jane Doe");
const members = await client.getMembers(conversations[0].id);See CONTRIBUTING.md for development setup, architecture, and implementation notes.
MIT