# Microsoft Teams

> Read and send Microsoft Teams chat messages via Microsoft Graph v1.0. Use when the user mentions Microsoft Teams chats, a 1:1 or group chat, reading recent Teams messages, or sending a Teams chat message. (Channel messages are not covered — they need admin-consented scopes.)

- Skill: `acedatacloud/microsoft-teams` (Agent Skill)
- Install (CLI): `npx skillmds@latest add acedatacloud/microsoft-teams`
- Raw SKILL.md: https://api.skillmd.com/api/skills/acedatacloud/microsoft-teams/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: Apache-2.0
- Author: acedatacloud (https://skillmd.com/u/acedatacloud)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/acedatacloud/microsoft-teams

---


Drive **Microsoft Teams chats** via Microsoft Graph with `curl + jq`. The user's
OAuth bearer token is in `$MICROSOFT_TEAMS_TOKEN`; every call needs
`Authorization: Bearer $MICROSOFT_TEAMS_TOKEN`. Base URL:
`https://graph.microsoft.com/v1.0`.

Failures are `{"error":{"code","message"}}` — show `message` verbatim. `401` =
re-install. `403`/`Forbidden` on send = the user granted read-only
(`Chat.Read`) → re-connect with `Chat.ReadWrite` + `ChatMessage.Send`.

**`403 "No authorization information present on the request"` on `/me/chats` =
the connected account is a *personal* Microsoft account.** Teams chat is a
work/school (organization) feature — Microsoft Graph does not expose `/me/chats`
or grant the `Chat.*` scopes for personal MSA accounts (outlook.com/hotmail/etc).
Tell the user plainly: "Microsoft Teams chat needs a work or school account;
your connected account is a personal Microsoft account. Reconnect Teams and
choose a work/school account." Do not retry — it cannot succeed for that account.

```bash
G="https://graph.microsoft.com/v1.0"; AUTH="Authorization: Bearer $MICROSOFT_TEAMS_TOKEN"
# My chats (1:1 + group), most recent first; expand members for names
curl -sS -H "$AUTH" "$G/me/chats?\$top=20&\$expand=members" \
  | jq '.value[] | {id, chatType, topic, members: [.members[].displayName]}'
```

## Read & send chat messages

```bash
CHAT="CHAT_ID"
# Recent messages
curl -sS -H "$AUTH" "$G/chats/$CHAT/messages?\$top=20" \
  | jq '.value[] | {from: .from.user.displayName, created: .createdDateTime, text: .body.content}'

# Send a message (confirm content with the user first). contentType html|text.
curl -sS -X POST -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"body":{"contentType":"html","content":"Hi from the assistant 👋"}}' \
  "$G/chats/$CHAT/messages" | jq '{id, created: .createdDateTime}'
```

## Gotchas

- **Chats only.** Reading Teams **channel** messages needs
  `ChannelMessage.Read.All` — a Microsoft "protected API" requiring tenant-admin
  consent — which this connector deliberately does not request. Don't try
  `/teams/{id}/channels/.../messages`; it will 403.
- `body.content` for HTML messages contains markup — strip tags when
  summarizing.
- OData `$top`/`$expand` need the `$` escaped in the shell; quote the URL.

