Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Added

- Agent skills for AI agents under `skills/`: `teams-cli-setup`, `teams-cli-messaging`, `teams-cli-admin` and `teams-cli-automation`. Install them with `npx skills add osodevops/ms-teams-cli`.

## v0.10.0 - 2026-10-15

### Added
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,22 @@ scoop install teams
cargo install --git https://github.com/osodevops/ms-teams-cli
```

## Agent Skills

Install the agent skills so Claude, Codex and other AI agents know how to drive `teams`: sign-in and output contract, messaging, team administration, and automation.

```bash
npx skills add osodevops/ms-teams-cli
npx skills add osodevops/ms-teams-cli --skill teams-cli-messaging
```

| Skill | Use it for |
|-------|------------|
| `teams-cli-setup` | Install, sign in, profiles, CI auth, JSON envelope and exit codes |
| `teams-cli-messaging` | Read, send and reply in chats and channels, attachments, files |
| `teams-cli-admin` | Teams, channels, members, tags, apps and tabs |
| `teams-cli-automation` | Presence, meetings, notifications, webhooks and polling |

## Documentation

- [Project website](http://msteamscli.com/)
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Project website: [msteamscli.com](http://msteamscli.com/)

## Start here

- [Agent skills](../skills/): installable SKILL.md files for AI agents that drive the CLI.
- [Quickstarts](quickstarts/README.md): short paths for local setup, agents, agencies, enterprises, and Windows.
- [Authentication guide](auth.md): commercial auth direction, OSO app defaults, BYO app setup, consent, and app-only limits.
- [Command reference](command-reference.md): practical syntax for every command group.
Expand Down
4 changes: 2 additions & 2 deletions docs/attachments-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,8 @@ Ordered, each step compiles and passes tests independently.
403 from `/shares`, the error message must say: add `Files.Read.All` to the profile
scopes and run `teams auth refresh`.
8. **Docs + skill**: update `docs/command-reference.md`, `docs/examples.md`, and the
`teams-cli` agent skill so agents learn the pattern: `message get` → see
`attachment_items` → `attachments download --dir`.
`skills/teams-cli-messaging/SKILL.md` agent skill so agents learn the pattern:
`message get` → see `attachment_items` → `attachments download --dir`.
9. **Tests**: unit tests per step; integration tests (feature `integration`) that list
and download hosted contents from a known message; a wiremock-style test for the 403
scope-error message if the client tests already use a mock server (they do —
Expand Down
99 changes: 99 additions & 0 deletions skills/teams-cli-admin/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
name: teams-cli-admin
description: "Manage Microsoft Teams structure with the teams CLI: create, update, clone, archive, unarchive or delete a team; add or remove team and channel members; create, rename or delete channels; manage tags; list, install or uninstall team apps; list, add or remove channel tabs. Use when asked to create a team or channel, add someone to a team, archive a team, clone a team, tag a group, install a Teams app or add a tab."
---

# Teams CLI Admin

Create and maintain teams, channels, tags, apps and tabs. These commands change shared resources: read first, write second.

## Before you start

```bash
teams auth status --output json
```

Exit 1 or 3 means you are not signed in: use `teams-cli-setup`. Always pass `--output json` when parsing. Exit 4 usually means you are not an owner of the team or lack admin consent. Report it. Do not retry.

## Teams

```bash
teams team list --output json
teams team get <team-id> --output json
teams team create --name "Project Atlas" --description "Atlas delivery"
teams team update <team-id> --name "Project Atlas 2" --description "Updated"
teams team clone <team-id> --name "Atlas Template" --parts channels,tabs,settings --visibility private
teams team archive <team-id>
teams team unarchive <team-id>
teams team delete <team-id>
```

- `team create` and `team clone` may finish asynchronously. `team list` afterwards confirms it exists.
- `clone --parts` takes a comma-separated subset of `apps,tabs,settings,channels,members`. The default is all five, so say what you drop.
- `archive` makes the team read-only. `unarchive` reverses it. `delete` removes the team and its Microsoft 365 group; only an admin can restore it, and only for a limited time.

## Team members

```bash
teams team members list <team-id> --output json
teams team members add <team-id> --user-id <user-id> --role member
teams team members remove <team-id> <membership-id>
```

`remove` takes the membership id from `members list`, not the user id. Find a user id with `teams user resolve "<user-email>"`. Check the roster before and after.

## Channels

```bash
teams channel list <team-id> --output json
teams channel create <team-id> --name "Releases" --description "Release notes" --membership-type standard
teams channel update <team-id> <channel-id> --name "Releases 2026"
teams channel delete <team-id> <channel-id>
teams channel members list <team-id> <channel-id> --output json
teams channel members add <team-id> <channel-id> --user-id <user-id> --role member
teams channel members remove <team-id> <channel-id> <membership-id>
```

`--membership-type` is `standard`, `private` or `shared`. Channel member commands apply to private and shared channels.

## Tags

```bash
teams tag list <team-id> --output json
teams tag create <team-id> --name "oncall" --members <user-id>,<user-id>
teams tag update <team-id> <tag-id> --name "oncall-eu"
teams tag add-member <team-id> <tag-id> --user <user-id>
teams tag remove-member <team-id> <tag-id> --user <user-id>
teams tag delete <team-id> <tag-id>
```

## Apps and tabs

```bash
teams app list <team-id> --output json
teams app install <team-id> --app-id <catalog-app-id>
teams app uninstall <team-id> --app-id <installation-id>
teams tab list <team-id> <channel-id> --output json
teams tab create <team-id> <channel-id> --app-id <catalog-app-id> --name "Board" --content-url <https-url>
teams tab delete <team-id> <channel-id> --tab-id <tab-id>
```

- `app install` takes the catalog app id. `app uninstall --app-id` takes the installation id from `app list`, a different value.
- There is no tab update command. Delete and re-create the tab.

## Destructive commands

These cannot be undone, or are disruptive: `team delete`, `team archive`, `team unarchive`, `channel delete`, `app uninstall`, `tag delete`, `tab delete`, and any `members remove`.

1. Read the target first (`team get`, `channel get <team-id> <channel-id>`, `tag get`, `app list`) and show the user its name and id.
2. Ask for explicit confirmation naming that exact target, for example "Delete channel Releases (<channel-id>) in team Project Atlas (<team-id>)?".
3. Run the single command only after a clear yes. Never loop destructive commands over a list without confirming the full list.
4. Read back (`team list`, `channel list`) to confirm the result.

None of the admin commands above has a `--yes` flag (only `message delete` and `message attachments delete` do; see `teams-cli-messaging`), so the user's confirmation is the only guard.

## Related skills

- `teams-cli-setup`: install, sign in, exit codes, output contract.
- `teams-cli-messaging`: read, send and reply in chats and channels.
- `teams-cli-automation`: presence, meetings, notifications, webhooks.
4 changes: 4 additions & 0 deletions skills/teams-cli-admin/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
interface:
display_name: "Teams CLI Admin"
short_description: "Manage Teams teams, channels, tags, apps and tabs"
default_prompt: "Use $teams-cli-admin to create or manage Microsoft Teams teams, channels, tags, apps and tabs safely."
100 changes: 100 additions & 0 deletions skills/teams-cli-automation/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
name: teams-cli-automation
description: "Automate Microsoft Teams with the teams CLI: read or set presence and status messages, create and manage online meetings and attendance reports, send activity notifications, subscribe to Graph change notifications, run the local webhook listener, or poll for new messages. Use when asked to set my Teams status, check if someone is available, schedule a Teams meeting, get a meeting join link, notify a user, watch a channel for new messages, or create a webhook subscription."
---

# Teams CLI Automation

Presence, meetings, notifications and event watching.

## Before you start

```bash
teams auth status --output json
```

Exit 1 or 3 means you are not signed in: use `teams-cli-setup`. Always pass `--output json` when parsing. Exit 4 means a missing scope or consent. Exit 6 means back off.

## Presence

```bash
teams presence get --output json
teams presence get --user <user-id> --output json
teams presence get-batch --user-ids <user-id>,<user-id> --output json
teams presence set --availability Busy --activity InACall --expiration PT1H
teams presence set-preferred --availability DoNotDisturb --expiration PT2H
teams presence status --message "Back at 14:00" --expiry 2026-01-01T14:00:00Z
teams presence clear
teams presence clear-preferred
```

- `set` needs a valid pair: `--availability` and `--activity` (for example `Available` + `Available`, `Busy` + `InACall`). Expiration is `PT5M` to `PT4H`.
- `set-preferred` overrides every session until it expires. Always set an expiration and tell the user to `clear-preferred` afterwards.
- Read presence for other people only when the user asked for it.

## Meetings

```bash
teams meeting list --output json
teams meeting get <meeting-id> --output json
teams meeting create --subject "Sprint review" --start 2026-01-01T10:00:00Z --end 2026-01-01T10:30:00Z
teams meeting update <meeting-id> --subject "Sprint review (moved)" --start 2026-01-01T11:00:00Z --end 2026-01-01T11:30:00Z
teams meeting join-url <meeting-id> --output json
teams meeting attendance <meeting-id> --output json
teams meeting attendance <meeting-id> --report-id <report-id> --output json
teams meeting delete <meeting-id>
```

- Times are ISO 8601 with an explicit offset or `Z`. Ask for the time zone if the user gave none.
- `meeting list` only shows meetings you created.
- `attendance` lists reports. Pass `--report-id` for the individual records.
- `meeting delete` removes the meeting and its join link for every participant: confirm the subject and id with the user first.

## Activity notifications

```bash
teams notify send --user <user-id> --topic "Deploy" --activity-type <activity-type> --preview "Deploy finished"
teams notify send-to-team <team-id> --topic "Deploy" --activity-type <activity-type> --preview "Deploy finished"
teams notify send-to-chat <chat-id> --topic "Deploy" --activity-type <activity-type> --preview "Deploy finished" --recipient-user <user-id>
```

`--activity-type` must match an activity declared in the manifest of a Teams app installed for the recipient. Without one the call fails. A notification is not a chat message: use `teams-cli-messaging` to post text.

## Change notifications

Graph pushes events to a public HTTPS URL. `teams listen` is plain HTTP on localhost, so put an HTTPS reverse proxy or tunnel in front of it.

```bash
teams listen --port 8080
teams subscribe create --resource <resource-path> --change-type created --webhook-url <https-url> --expiration 2026-01-01T12:00:00Z --client-state <random-secret>
teams subscribe list --output json
teams subscribe renew <subscription-id> --expiration 2026-01-02T12:00:00Z
teams subscribe delete <subscription-id>
```

- `listen` prints each notification as one JSON object per line (NDJSON). Read it line by line, and keep other output off that stream.
- Graph validates the webhook URL when you create the subscription, so start `listen` and the tunnel first.
- Use a random `--client-state` and compare it on every notification.
- Subscriptions expire quickly: Teams message, chat and presence resources last at most one hour, which is also the default when `--expiration` is omitted. Renew before `expirationDateTime`, or re-create.
- Message resources need extra permissions, and some need admin consent. See `teams subscribe --help` and the Graph docs.
- Delete subscriptions you created when done. `subscribe delete` stops delivery: confirm the id and its `resource` from `subscribe list` with the user first. There is no `subscribe get`.

## Polling instead

When no HTTPS endpoint is possible, poll and diff by message id.

```bash
teams message list --chat <chat-id> --output json
teams message list --team <team-id> --channel <channel-id> --page-size 20 --output json
```

1. Store the ids already seen (a file or state store you own).
2. Poll every 30 to 60 seconds or more. On exit 6, double the wait.
3. Process ids not seen. Follow `metadata.has_more` or add `--all-pages` for the first full pass only.
4. Do not poll many chats in parallel.

## Related skills

- `teams-cli-setup`: install, sign in, exit codes, output contract.
- `teams-cli-messaging`: read, send and reply in chats and channels.
- `teams-cli-admin`: teams, channels, tags, apps and tabs.
4 changes: 4 additions & 0 deletions skills/teams-cli-automation/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
interface:
display_name: "Teams CLI Automation"
short_description: "Presence, meetings, notifications and Teams webhooks"
default_prompt: "Use $teams-cli-automation to set presence, manage meetings, send notifications or watch Teams for new messages."
Loading
Loading