From teams-cli-skills
Interactive Microsoft Teams assistant — read, reply, search messages, manage contacts and persona context using teams-cli.
How this skill is triggered — by the user, by Claude, or both
Slash command
/teams-cli-skills:teamsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use `teams-cli` (installed at /usr/local/bin/teams-cli) to interact with Microsoft Teams.
Use teams-cli (installed at /usr/local/bin/teams-cli) to interact with Microsoft Teams.
This skill lets you check messages, draft replies, search conversations, and manage
relationship context for persona-aware communication.
All outgoing messages MUST be shown to the user for confirmation before sending.
ls ~/.teams-agent/persona.md 2>/dev/null
If ~/.teams-agent/persona.md does NOT exist, start profile setup immediately before
doing anything else. Do not ask — just begin the setup process described in "Building Your
Profile" below. The profile is required for persona-aware replies.
If the profile exists, proceed to what the user asked for.
Authentication is automatic. The CLI auto-detects expired tokens and re-authenticates
via webview. No need to check teams-cli status or run teams-cli auth manually.
The CLI has a built-in file-based cache at ~/.config/teams-cli/cache/. This means you
do NOT need to maintain your own cache in ~/.teams-agent/ — the CLI handles it.
What is cached (and TTLs):
| Data | TTL | Notes |
|---|---|---|
| Conversations (chats list) | never | Always fetches fresh data |
| User info (users search) | 1 hour | Name, email, MRI, department |
| Own profile (me) | 1 hour | Very stable |
| Chat ID resolution (chats resolve) | 10 min | Email → chat ID mapping |
| Teams/channels | 30 min | Team structure |
What is NEVER cached:
messages list — always fetches fresh messageschats list --unread — always bypasses cache for fresh unread statemessages search — always searches livemessages send — invalidates conversations cache after sendingCache control flags:
teams-cli chats list --no-cache --format json # Bypass cache this time
teams-cli chats list --refresh --format json # Ignore cache, write fresh
teams-cli cache clear # Delete all cached data
Why this matters for the skill: Since the CLI caches user lookups, chat resolution,
and conversation lists, the skill doesn't need to cache these. Just call the commands
normally — repeated calls within the TTL window are instant from cache. Use --no-cache
when you need guaranteed-fresh data (e.g., checking if a new message arrived).
People also chat via Teams UI: If someone sends a message through the desktop/web app,
the conversations cache (5 min TTL) will pick it up on the next call. For --unread
queries, the cache is always bypassed to avoid showing stale read/unread state.
This runs automatically when /teams is invoked and no profile exists. The profile
enables persona-aware messaging — without it, replies will be generic.
mkdir -p ~/.teams-agent/{contacts,groups,patterns}
teams-cli me --format json
teams-cli contacts discover --days 30 --format json
The contacts discover command fetches messages from active chats, groups them by
contact, tags your messages as "me", and includes message counts. This single command
replaces manual chat-by-chat fetching.
Use --contact "name" to discover for a specific person (supports nicknames).
Use --chats N and --msgs N to control scan depth.
From the discover output, filter to messages where from is "me" across all contacts.
Analyze your messages for:
Write to ~/.teams-agent/persona.md
persona.md format:
# [Name]'s Communication Style
## General Tone
- [Overall style: casual/formal/mixed]
- [Response length: brief/moderate/detailed]
- [Formality: low/medium/high]
## Common Phrases
- "[phrase 1]"
- "[phrase 2]"
- "[phrase 3]"
## Greeting Style
- [How they typically open messages]
## Things I Avoid
- [Patterns NOT seen in their messages]
## Bangla Communication
- Default pronoun: tumi
- Use "apni" for: [seniors, managers, formal contacts]
- Use "tui" for: [very close friends only, if explicitly allowed]
- Bangla script usage: [yes/no, when]
## Work Context
- Role: [from user profile]
- Timezone: [if determinable]
From the same discover output, build profiles for contacts with the most messages.
Focus on contacts with my_messages > 0 (you've actually interacted with them).
For each contact:
teams-cli contacts list "name" --format json — this returns email,
MRI, nicknames, and persona data. Always use this first, never guess emails.~/.teams-agent/contacts/<email>.md (or <sanitized-name>.md if email unknown)To discover for a single contact later:
teams-cli contacts discover --contact "nabil" --format json # By name
teams-cli contacts discover --contact "tusher" --format json # By nickname
Name disambiguation: Multiple people can have similar names (e.g., "Kamrul Hassan" vs "Kamrul Hasan"). Email is the unique identifier when available. When email is unknown, MRI is the unique identifier. Use nicknames for non-obvious aliases (e.g., "tusher" for "Kamrul Hasan").
Contact profile format:
# [Contact Name]
email: [email]
nicknames: [comma-separated aliases not derivable from the display name]
## Bangla Pronoun
- pronoun: [tui | tumi | apni]
- Note: default to "tumi" if unsure. "apni" for seniors/managers, "tui" only if very close
## Chat IDs
- [1:1 chat ID, if exists]
- [group chat IDs where this contact appears]
## Relationship
- Type: [teammate | manager | report | cross-team | external]
- Closeness: [close | friendly | professional | formal | new]
- Team: [team name if known]
## Cultural Context
- Humor: [none | light-work-only | casual]
- Formality: [low | medium | high]
- Note: [any relevant context, default to "neutral — use professional tone"]
## Communication Pattern
- They send: [message style description]
- I reply with: [my typical reply style to this person]
- Common topics: [what we discuss]
## Mention Behavior (in groups)
- How to @mention: [first name]
## My Phrases With Them
- "[phrase 1]"
- "[phrase 2]"
## Sample Exchanges
Them: "[example message]"
Me: "[example reply]"
## Preferences
- Puns allowed: no
- Auto-reply: no
For each group chat with recent activity:
from fields or teams-cli users search and create a meaningful title
like "Alice, Bob, Carol" for the group profile.~/.teams-agent/groups/<group-name>.mdGroup profile format:
# [Group Name]
chat_id: [chat ID]
type: [team-channel | group-chat]
human_title: [resolved readable title if original was MRI strings]
## Members & Relationships
- [Name] — [relationship to user], [tone] (email: [email])
- [Name] — [relationship to user], [tone] (email: [email])
## Relationship Diagram
[Describe how members relate to each other and to the user]
[Who reports to whom, who works closely, who is new]
## Group Dynamics
- Overall tone: [description]
- Tone setter: [who sets the formality floor]
- Cultural mix: [if relevant, note to stay neutral/inclusive]
## Mention Rules
- @[Name]: [when to mention]
- Default: don't over-mention, only @ when specifically addressing someone
## My Behavior in This Group
- [How user adjusts tone vs 1:1]
- [What to avoid in this group]
Look across all conversations for recurring topics and how the user responds:
~/.teams-agent/patterns/<topic>.mdPattern format:
# [Topic Name]
## When This Applies
- [Trigger descriptions]
## Response by Relationship
- Close teammate: "[casual response]"
- Manager: "[professional response]"
- New contact: "[neutral response]"
Write the hardcoded safety rules to ~/.teams-agent/safety-rules.md:
# Safety Rules — ABSOLUTE, NON-NEGOTIABLE
## Communication Safety
- NEVER make jokes about race, religion, ethnicity, gender, politics, sexuality, disability
- NEVER use sarcasm that could be misread across cultures
- NEVER make puns unless EXPLICITLY enabled for a specific contact
- NEVER assume cultural norms — default to professional-neutral
- NEVER discuss politics, religion, or controversial topics
- NEVER use slang or idioms that may not translate across cultures
- NEVER use passive-aggressive language
- If humor is allowed for a contact, keep it strictly work-related and light
## Operational Safety
- NEVER send a message without user confirmation
- NEVER reply to unknown contacts without asking the user
- NEVER forward or quote messages between different people
- NEVER share personal info about one contact with another
- NEVER escalate tone — if someone is upset, stay calm and neutral
- If unsure about ANYTHING, ask the user
## Group Chat Safety
- NEVER use inside jokes from 1:1 chats in group contexts
- NEVER over-mention (@) people
- In mixed groups, use the most neutral/formal tone appropriate
# ~/.teams-agent/config.yaml
safe_mode: true
confirm_before_send: true
default_tone: "professional"
default_formality: "medium"
max_history_messages: 30
puns_allowed: false
humor_default: "none"
cultural_default: "neutral"
teams-cli auth # Launch OAuth login
teams-cli auth --force # Force re-auth even if tokens valid
teams-cli status --format json # Check token health
teams-cli me --format json # Current user profile
teams-cli config show --format json # Show all config
teams-cli config signature on # Enable "sent via claude" signature
teams-cli config signature off # Disable signature
teams-cli config signature set "custom" # Set custom signature text
Message signature: By default, all messages sent via the CLI are appended with
— sent via claude 🤖. This lets recipients know the message was composed with AI
assistance. The user can ask to turn it on/off:
teams-cli config signature offteams-cli config signature onteams-cli config signature set "new text"teams-cli chats list --format json # All chats
teams-cli chats list --unread --format json # Unread only
teams-cli chats list --type dm --format json # True DMs only (not meeting chats)
teams-cli chats list --type "1:1" --format json # 1:1 chats (meeting-based)
teams-cli chats list --type group --format json # Group chats only
teams-cli chats list --type dm --include-bots --format json # DMs including bot members
teams-cli chats list --limit 10 --format json # Limit results
teams-cli chats list --offset 10 --limit 10 --format json # Pagination
teams-cli chats list --compact --format json # Omit members array
teams-cli chats list --with "alice" --format json # Find chats with a person
teams-cli chats list --active-since 2024-01-01 --format json # Filter by activity date
teams-cli chats resolve [email protected] # Get DM chat ID from email
teams-cli chats create --with [email protected] --format json # Create new 1:1 chat
Chat types:
dm — True 1:1 direct messages (personal chats at @unq.gbl.spaces)1:1 — Meeting-based 1:1 chats (scheduled meetings with 2 people)group — Group chats and channelsBy default, bot members (Jira, Copilot, etc.) are hidden from DM listings. Use --include-bots to show them.
teams-cli messages list <chat-id> --format json # Read messages (default 50)
teams-cli messages list --to [email protected] --format json # Read by email
teams-cli messages list <chat-id> -n 20 --format json # Last N messages
teams-cli messages list <chat-id> --mine --format json # Only my messages
teams-cli messages list <chat-id> --since 2024-01-01 # Since date
teams-cli messages list <chat-id> --from "alice" --format json # Filter by sender
teams-cli messages list <chat-id> --plain --format text # Clean text output
teams-cli messages list <chat-id> --before <time> --after <time> # Cursor pagination
teams-cli messages send <chat-id> "message text" # Send by chat ID
teams-cli messages send --to [email protected] "text" # Send by email (creates 1:1 if needed)
teams-cli messages send --group "Monad Standup" "Hello team" # Send to group by name
teams-cli messages send <chat-id> "**bold** text" --msg-format markdown # Markdown → HTML
teams-cli messages send <chat-id> "Hey @alice" --mention [email protected] # @mention
teams-cli messages send <chat-id> "text" --reply-to <msg-id> # Reply to specific message
teams-cli messages reply <chat-id> <msg-id> "reply text" # Threaded reply
teams-cli messages reply <chat-id> <msg-id> "text" --quote <quote-msg-id> # Reply quoting another message
teams-cli messages send <chat-id> "text" --quote <msg-id> # Send with embedded quote
teams-cli messages edit <chat-id> <msg-id> "updated text" # Edit sent message
teams-cli messages edit <chat-id> <msg-id> "text" --quote <quote-msg-id> # Edit with embedded quote
teams-cli messages delete <chat-id> <msg-id> # Delete sent message
teams-cli messages search "query" --format json # Search messages
teams-cli messages search "query" --chat <id> --format json # Search in specific chat
teams-cli messages mine --format json # My messages across chats
teams-cli messages stats <chat-id> --format json # Message count by sender
teams-cli messages export <chat-id> -o chat.json # Export chat history
teams-cli messages react <chat-id> <message-id> like # React with emoji
teams-cli messages react <chat-id> <message-id> heart # React with heart
Markdown support (--msg-format markdown): Converts markdown to Teams HTML:
**bold** → bold, *italic* → italic, `code` → inline code```code blocks```, ~~strikethrough~~, # headers, - lists@mentions (--mention): Resolves email to Teams MRI and wraps in <at> tag.
Multiple mentions: --mention [email protected] --mention [email protected]
Emoji reactions: Valid reactions are: like (👍), heart (❤️), laugh (😂),
surprised (😮), sad (😢), angry (😡). You can also use the emoji characters directly.
Quoting messages (--quote <msg-id>): Embeds another user's message as a blockquote
in your reply/send/edit. The quoted message appears as a visual quote block in Teams with
the original sender's name and a preview of their message. Use this when responding to a
specific message in a thread — it makes clear which message you're addressing.
When the skill targets a specific message (e.g., replying to what someone said in a thread),
always use --quote to embed the referenced message for context.
teams-cli teams list --format json # List joined teams
teams-cli channels list <team-id> --format json # List channels in a team
teams-cli notifications --format json # All notifications
teams-cli notifications --type mentions --format json # Only @mentions
teams-cli notifications --type replies --format json # Only replies to your messages
teams-cli notifications --type reactions --format json # Only emoji reactions
teams-cli notifications --since 2024-04-07 --format json # Since a specific date
teams-cli notifications --since 2024-04-07 --type mentions --format json # Combined filters
Notification types: mention (channel @mention), mentionInChat (DM @mention),
replyToReply (thread reply), reactionInChat (emoji reaction), follow (channel activity).
Use cases:
notifications --since today's-date --format jsonnotifications --type mentions --format jsonnotifications --type mentions --since <date> --format jsonteams-cli users search [email protected] --format json # Look up by email
teams-cli users search "Alice Smith" --format json # Look up by display name
teams-cli users resolve "mri1,mri2" --format json # Batch resolve MRIs
teams-cli contacts list --format json # All contacts, sorted by most contacted
teams-cli contacts list "nabil" --format json # Search by name, email, or nickname (includes persona)
teams-cli contacts list "tusher" --format json # Nickname search (prioritized over name match)
teams-cli contacts sync # Force refresh contacts cache
Contact search returns persona/interaction data inline when searching a specific person.
Search priority: nickname match > name match > email match.
Contact profiles stored at ~/.teams-agent/contacts/<email>.md.
All commands support --format with values: json (default), table, text.
Use --pretty for pretty-printed JSON.
The chat list can return 100KB+ of JSON that floods the context window. ALWAYS pipe through python3 or jq to extract only the fields you need:
# Bad — dumps everything into context
teams-cli chats list --format json
# Good — extract only what you need
teams-cli chats list --format json 2>&1 | python3 -c "
import json,sys
data=json.load(sys.stdin)
for c in data[:20]:
print(json.dumps({
'id': c['id'],
'title': c.get('title',''),
'type': c['type'],
'is_read': c['is_read'],
'last_msg': c.get('last_message',{}).get('content','')[:80] if c.get('last_message') else ''
}))"
# Bad — raw message dump
teams-cli messages list <id> -n 50 --format json
# Good — compact summary
teams-cli messages list <id> -n 50 --format json 2>&1 | python3 -c "
import json,sys
data=json.load(sys.stdin)
for m in data:
print(json.dumps({'from':m['from'],'content':m['content'][:200],'time':m['time']}))"
When fetching from multiple chats, always use the compact form. The raw JSON from even a single chat can be several KB.
When the user invokes /teams:
~/.teams-agent/persona.md exists — if not, run Building Your Profile automaticallyteams-cli chats list --unread --format json 2>&1 | python3 -c "
import json,sys
data=json.load(sys.stdin)
for c in data:
print(json.dumps({'id':c['id'],'title':c.get('title',''),'type':c['type']}))"
teams-cli messages list <chat-id> -n 10 --format json 2>&1 | python3 -c "
import json,sys
data=json.load(sys.stdin)
for m in data:
print(json.dumps({'from':m['from'],'content':m['content'][:200],'time':m['time']}))"
To: [recipient] | Message: "[exact text]" and ask for confirmationteams-cli messages search "query" --format json~/.teams-agent/When drafting a reply, load context in this order. This is a mandatory checklist — do not skip any step. If a file is missing, note it and proceed with defaults.
~/.teams-agent/safety-rules.md (always apply)~/.teams-agent/persona.md (base tone)teams-cli contacts list "<name>" --format json (returns persona inline)~/.teams-agent/groups/<name>.md (group dynamics)~/.teams-agent/patterns/*.md (topic-specific templates)teams-cli messages list (recent context)Then draft the reply following these rules:
<at id="[mri]">[Name]</at> formatAfter helping with messages, offer to update context if new patterns emerged:
Use contacts discover with a shorter time window to get recent data:
teams-cli contacts discover --days 7 --format json # Recent activity
teams-cli contacts discover --days 7 --contact "nabil" --format json # Single contact
MERGE new information — don't replace the existing profile. Add new phrases, update patterns, note tone shifts. Always show the user what changed before writing.
The user can ask to:
contacts discover --days 7, re-analyze "me" messagescontacts discover --contact "name", create profile from outputcontacts discover --contact "alice", merge new datateams-cli contacts list "alice" --format json (includes persona inline)If a message is from someone not in contacts/:
<at id="[mri]">[Name]</at>If a message touches on politics, religion, or controversial topics:
| Task | Command |
|---|---|
| Notifications | teams-cli notifications --format json |
| Mentions | teams-cli notifications --type mentions --format json |
| Catch up | teams-cli notifications --since YYYY-MM-DD --format json |
| List DMs | teams-cli chats list --type dm --format json |
| Check unreads | teams-cli chats list --unread --format json |
| Read messages | teams-cli messages list <chat-id> -n N --format json |
| Read by email | teams-cli messages list --to email --format json |
| My messages | teams-cli messages list <chat-id> --mine --format json |
| Send message | teams-cli messages send <chat-id> "text" |
| Send by email | teams-cli messages send --to email "text" |
| Send to group | teams-cli messages send --group "name" "text" |
| Send markdown | teams-cli messages send <chat-id> "**bold**" --msg-format markdown |
| @mention | teams-cli messages send <chat-id> "Hey @alice" --mention alice=email |
| Reply thread | teams-cli messages reply <chat-id> <msg-id> "text" |
| Reply + quote | teams-cli messages reply <chat-id> <msg-id> "text" --quote <quote-id> |
| Edit message | teams-cli messages edit <chat-id> <msg-id> "new text" |
| Delete message | teams-cli messages delete <chat-id> <msg-id> |
| Search | teams-cli messages search "query" --format json |
| Resolve email | teams-cli chats resolve email |
| Create DM | teams-cli chats create --with email --format json |
| Find chats with | teams-cli chats list --with "name" --format json |
| Compact chats | teams-cli chats list --compact --format json |
| Chat stats | teams-cli messages stats <chat-id> --format json |
| Export chat | teams-cli messages export <chat-id> -o file.json |
| All contacts | teams-cli contacts list --format json (sorted by most contacted) |
| Find contact | teams-cli contacts list "name" --format json (includes persona) |
| Sync contacts | teams-cli contacts sync |
| Discover convos | teams-cli contacts discover --format json (persona building) |
| Discover one | teams-cli contacts discover --contact "name" --format json |
| List teams | teams-cli teams list --format json |
| List channels | teams-cli channels list <team-id> --format json |
| Look up user | teams-cli users search email-or-name --format json |
| Resolve MRIs | teams-cli users resolve "mri1,mri2" --format json |
| My profile | teams-cli me --format json |
| Auth status | teams-cli status --format json |
| Re-auth | teams-cli auth |
| React emoji | teams-cli messages react <chat-id> <msg-id> like |
| Clear cache | teams-cli cache clear |
| Show config | teams-cli config show --format json |
| Signature on | teams-cli config signature on |
| Signature off | teams-cli config signature off |
| Set signature | teams-cli config signature set "text" |
! teams-cli auth. Note that
the chats API (chatsvcagg token) and messages API (skype token) use different tokens — one
can expire while the other is still valid.~/.teams-agent/ doesn't exist: Start profile setup automatically.--from filter returns empty: Use python3 post-processing filter instead (see "Known issue" in Messages section).teams-cli messages list <chat-id> -n 5 --format json
If the sent message appears in the results, it was delivered despite the error.teams-cli contacts list "name" --format json.
This searches by name, email, and nickname, returns persona data, and is sorted by most contacted.
If not found there, try:
teams-cli users search "<email>" --format json (exact email lookup)teams-cli chats list --with "name" --format json (find chats with someone)ALWAYS validate before sending a message to a chat ID:
Distinguish true 1:1 from meeting chats: Meeting chat IDs often contain meeting_ in
the ID or have titles like "Understand Task..." that don't match a person's name. True 1:1
chats have IDs like 19:[email protected] without meeting_.
meeting_ in ID)"This is a meeting chat, not a true 1:1. Other participants may have been in this chat. Want me to send here or use
--to emailto create a proper 1:1?"
teams-cli messages send --to email "text" which guarantees a proper
direct chatCheck for stale/deleted accounts: Before sending to a chat, verify members are active:
teams-cli users search <email> returns no
result or an empty display name, that account may be deleted/deactivated"This chat has a member ([MRI]) whose account appears deleted. Messages here may not reach the intended recipient. Want me to create a fresh 1:1 instead?"
Verify chat is active: For any chat you haven't used recently:
Prefer --to email for 1:1 messages: Using --to [email protected] is safer than
sending to a raw chat ID because it resolves to the correct 1:1 chat or creates one.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub kamrul1157024/teams-cli --plugin teams-cli-skills