---
name: teams-message
description: Send Microsoft Teams messages to people, chats, or channels.
metadata:
  activation:
    keywords: [teams message, teams chat, teams channel, send message to, 发消息给]
    patterns:
      - "(?i)\\b(send|post|write|reply|tell|forward)\\b.{0,48}\\bteams\\b"
      - "(?i)\\bteams\\s+(message|msg|chat|channel)\\b"
      - "(?i)\\b(message|msg|chat)\\b.{0,32}\\b(on|in|to|via)\\s+teams\\b"
      - "(?i)(teams上(发|写|说|回|聊)|发到teams)"
      - "(?i)(发消息给|发.*teams.*消息|给.{0,10}发.{0,5}teams)"
      - "(?i)(给.{1,32}发(个|条)?消息|发(个|条)消息给)"
      - "(?i)\\b(send|post)\\s+(a\\s+)?(message|msg)\\s+to\\b"
  requires:
    tools: [MCP_teams, MCP_user]
---

# Teams Message

## Routing

- Other target (user/chat/channel/link): resolve target → send.
- Self (发给我自己/save to Teams/记一下): self path → send.
- Edit own message (edit/update/correct my message): find `chatId` + `messageId`, verify sender is current user → update.
- Notify current user (通知我/notify me/Teams 上提醒我): use `notify(channel='cloud', content)`, not this skill.
- Scheduled reminder: use `cron(message, notify_channels=['cloud'])`, not this skill.
- Ambiguous/missing target/content/multiple matches: read-only lookup; if still unclear, use `clarify` once with `options` + draft.

## Resolve target

If tools are hidden, run `tools(name='MCP_teams')`.

### Self chat

Use `MCP_teams_SendMessageToSelf`. Fallback: `MCP_teams_SendMessageToChat(chatId="48:notes", content)`.

### Teams link

- Chat: `.../l/chat/19:...@.../conversations` → chatId
- Channel: `.../l/channel/19:...@...?groupId=...` → channelId + teamId=`groupId`
- Verify with `MCP_teams_GetChat` / `MCP_teams_GetChannel` only if link provenance is uncertain.

### UPN or email

- UPN/GUID known: `MCP_teams_SendMessageToUser(userIdOrUpn, content)`.
- chatId known: `MCP_teams_SendMessageToChat(chatId, content)`.
- Need chatId: `MCP_teams_CreateChat(chatType="oneOnOne", members_upns=[myUPN, targetUPN])`.
- UPN must be `alias@domain`; otherwise use GUID.

### Person's name (resolve → UPN)

Use `MCP_user_GetMultipleUsersDetails`; never construct UPNs. Search is substring-only on `displayName`; even one hit may be wrong. Cross-check `MCP_teams_ListChats` + signals (department/title/known UPN). Proceed only on convergence; otherwise §Safety.

### Past chat content (KQL / NL search)

For content-identified chats: KQL first, NL only exploratory.
1. `MCP_teams_SearchTeamMessagesQueryParameters(queryString="from:alice@contoso.com AND budget")`; supports `from:`, `sent:`, booleans, `"phrase"`; max 25/page via `from`/`size`.
2. `MCP_teams_SearchTeamsMessages(message)` when sender/date/keywords are unknown; returns `chatIds`.
ChatId types: `19:{guid}_{guid}@unq.gbl.spaces`=1:1, `19:{hex}@thread.v2`=group, `19:meeting_*@thread.v2`=meeting. Verify with `MCP_teams_GetChat` + `MCP_teams_ListChatMembers`; multiple → confirm target + draft once.

### Team/channel name

`MCP_teams_ListTeams` → teamId; `MCP_teams_ListChannels` → channelId. If no channel: prefer `General`, then team-name match, else ask.

### Message to edit

Use a message link, search result, or recent messages to get `chatId` + `messageId`. Before `MCP_teams_UpdateChatMessage`, verify the message sender is the current user.

## Send

### Methods

- User: `MCP_teams_SendMessageToUser(userIdOrUpn, content)` or `MCP_teams_SendFileToUser`
- Chat/group: `MCP_teams_SendMessageToChat(chatId, content)` or `MCP_teams_SendFileToChat`
- Edit message: `MCP_teams_UpdateChatMessage(chatId, messageId, content)`
- Channel: `MCP_teams_SendMessageToChannel(teamId, channelId, content, subject)` or `MCP_teams_SendFileToChannel`
- Reply in a channel thread: `MCP_teams_ReplyToChannelMessage(teamId, channelId, messageId, content)`
- Self: `MCP_teams_SendMessageToSelf(content)`

Success requires response `id` + `createdDateTime`; missing `id` = silent failure.

### Content

Prepend AI marker unless disabled: html `<b>[🤖]</b> ...`; text `[🤖] ...`; channel subject `[🤖] title`. Human-readable, max 32KB.

### ContentType

- `text` only for short and plain messages.
- `html` for long/formatted messages, @mentions, or Adaptive Cards.

HTML: no `<style>`/`<script>`/`<iframe>`. Supported: `<b>`, `<i>`, `<u>`, `<s>`, `<a>`, lists, table, `<br>`, `<p>`, `<pre>`, `<code>`, font sizes, inline `color`/`background-color`. Markdown does not render; convert to HTML.

### Mentions

For @mentions, importance, or Adaptive Cards, call `MCP_teams_GetRichMessageFormats` first for `<at>`/`mentions`, card templates, importance levels.

## Pitfalls

- KQL chatMessage supports only `from:`, `sent:`, keywords, booleans; no `subject:`/`to:`/`hasAttachment:`. Unsupported filters fail silently.
- Search caps at 25/page; KQL paginates via `from`/`size`.
- `MCP_teams_SearchTeamsMessages` (NL) has Substrate indexing lag; prefer KQL for "in the last few minutes".
- `MCP_user_GetMultipleUsersDetails`: substring match, weak ranking; common names need full name or wider `top` + tiebreakers.

## Safety

- **Cross-org**: confirm before sending to external orgs.
- **Inactive**: `MCP_teams_ListChatMessages` or `MCP_teams_ListChannelMessages` (top=1), check `createdDateTime`. If older than 7 days, confirm.
- **Ambiguous target**: investigate with read-only APIs first, then confirm target + draft in one shot.

