{"id":49426657,"url":"https://github.com/osodevops/ms-teams-cli","last_synced_at":"2026-07-03T07:01:34.856Z","repository":{"id":352274047,"uuid":"1179573200","full_name":"osodevops/ms-teams-cli","owner":"osodevops","description":"A fast, single-binary Microsoft Teams CLI built in Rust — agent-first design, structured JSON output, real-time webhooks, and full Graph API coverage","archived":false,"fork":false,"pushed_at":"2026-06-21T07:00:56.000Z","size":236,"stargazers_count":20,"open_issues_count":4,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-21T08:23:35.103Z","etag":null,"topics":["agent-first","automation","chatops","cli","command-line-tool","graph-api","json","microsoft-graph","microsoft-teams","rust","teams","webhooks"],"latest_commit_sha":null,"homepage":"http://msteamscli.com/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/osodevops.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-12T06:47:47.000Z","updated_at":"2026-06-21T06:56:41.000Z","dependencies_parsed_at":null,"dependency_job_id":"13c877b9-f20e-4618-b4ae-43f7646ce8c6","html_url":"https://github.com/osodevops/ms-teams-cli","commit_stats":null,"previous_names":["osodevops/ms-teams-cli"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/osodevops/ms-teams-cli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osodevops%2Fms-teams-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osodevops%2Fms-teams-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osodevops%2Fms-teams-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osodevops%2Fms-teams-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/osodevops","download_url":"https://codeload.github.com/osodevops/ms-teams-cli/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osodevops%2Fms-teams-cli/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35075805,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-03T02:00:05.635Z","response_time":110,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-first","automation","chatops","cli","command-line-tool","graph-api","json","microsoft-graph","microsoft-teams","rust","teams","webhooks"],"created_at":"2026-04-29T09:30:55.506Z","updated_at":"2026-07-03T07:01:34.838Z","avatar_url":"https://github.com/osodevops.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# teams — Microsoft Teams CLI for AI Agents and Developers (Rust)\n\nA fast, single-binary CLI that gives AI agents and automation full access to [Microsoft Teams](https://teams.microsoft.com) via the Microsoft Graph API.\n\nEvery command returns structured JSON with deterministic exit codes — designed from the ground up for autonomous agents (Claude, GPT, custom LLM agents), CI/CD pipelines, and developer scripts. Not a chatbot framework. A tool that agents wield.\n\n[![CI](https://github.com/osodevops/ms-teams-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/osodevops/ms-teams-cli/actions/workflows/ci.yml)\n[![Release](https://github.com/osodevops/ms-teams-cli/actions/workflows/release.yml/badge.svg)](https://github.com/osodevops/ms-teams-cli/releases)\n[![Latest Release](https://img.shields.io/github/v/release/osodevops/ms-teams-cli)](https://github.com/osodevops/ms-teams-cli/releases)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\nWebsite: [msteamscli.com](http://msteamscli.com/)\n\n## Why This Exists\n\nAI agents need to operate in Microsoft Teams — reading messages, posting updates, managing channels, reacting to events — but there is no comprehensive CLI for Teams. Existing MCP servers cover only a fraction of the Graph API. Bot Framework SDKs assume a conversational UI inside Teams, not an external agent calling in.\n\n**teams-cli** fills this gap: a complete, headless, machine-readable interface to Teams that any agent can call as a subprocess.\n\n## Agent Integration Contract\n\nEvery command, when piped or called programmatically, returns a **JSON envelope**:\n\n```json\n{\n  \"success\": true,\n  \"data\": { \"id\": \"...\", \"displayName\": \"Engineering\", \"...\" : \"...\" },\n  \"metadata\": {\n    \"request_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n    \"timestamp\": \"2026-03-15T00:00:00Z\",\n    \"duration_ms\": 123\n  }\n}\n```\n\nOn failure:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"AUTH_TOKEN_EXPIRED\",\n    \"message\": \"Access token has expired. Run `teams auth login` to re-authenticate.\"\n  }\n}\n```\n\n**Exit codes** map directly to error categories — agents can branch on `$?` without parsing:\n\n| Code | Meaning | Agent Action |\n|------|---------|--------------|\n| 0 | Success | Parse `.data` from stdout |\n| 1 | General error | Log and investigate |\n| 2 | Invalid arguments | Fix command syntax |\n| 3 | Authentication error | Re-authenticate |\n| 4 | Permission denied (403) | Escalate or skip |\n| 5 | Resource not found (404) | Handle missing resource |\n| 6 | Rate limited (429) | Back off and retry |\n| 7 | Network error / timeout | Retry with backoff |\n| 8 | Server error (5xx) | Retry with backoff |\n| 10 | Configuration error | Check config/env vars |\n\n**Output auto-detection**: When stdout is a TTY, output defaults to human-readable tables. When piped (which is how agents call it), output defaults to JSON. Override with `--output json|human|plain`.\n\n## Install\n\n```bash\n# Homebrew (macOS/Linux)\nbrew install osodevops/tap/teams-cli\n\n# Scoop (Windows)\nscoop bucket add osodevops https://github.com/osodevops/scoop-bucket\nscoop install teams\n\n# Pre-built binaries — download from GitHub Releases\n# https://github.com/osodevops/ms-teams-cli/releases\n\n# From source\ncargo install --git https://github.com/osodevops/ms-teams-cli\n```\n\n## Documentation\n\n- [Project website](http://msteamscli.com/)\n- [Docs index](docs/README.md)\n- [Quickstarts](docs/quickstarts/README.md)\n- [Authentication guide](docs/auth.md)\n- [Command reference](docs/command-reference.md)\n- [Examples](docs/examples.md)\n- [Use cases](docs/use-cases.md)\n- [FAQ](docs/faq.md)\n- [Troubleshooting](docs/troubleshooting.md)\n- [Release readiness](docs/release-readiness.md)\n- Man pages: `docs/man/teams.1`, `docs/man/teams-config.5`, `docs/man/teams-auth.7`, `docs/man/teams-agent-contract.7`, `docs/man/teams-examples.7`\n\n## Quickstart\n\n```bash\n# 1. Sign in with the built-in OSO public client app\nteams auth login --device-code\n\n# 2. Check auth, tenant, token type, and consent URL\nteams auth doctor --output json\n\n# 3. Read Teams context\nteams user me --output json\nteams chat list --page-size 10 --output json\nteams team list --output json\n\n# 4. Send only to a known safe test chat or channel\nteams message send --chat \u003cchat-id\u003e --body \"teams-cli smoke test\" --output json\n```\n\nUse a dedicated test target for first writes. Do not test against client or production chats.\n\n## Commercial Microsoft Setup\n\nThe CLI has OSO's multi-tenant delegated public client app baked in:\n\n```text\nClient ID: fba1b5d0-fdd0-4fe2-9729-9ccdc38f9595\nDefault authority: organizations\nLoopback redirect URI: http://localhost:8400/callback\n```\n\nCustomer admin consent:\n\n```bash\nteams auth consent-url --tenant-id \u003ccustomer-tenant-id-or-domain\u003e --output json\n```\n\nThis prints a Microsoft identity platform v2 admin-consent URL with an explicit\n`scope` parameter for the CLI's default delegated Graph scopes.\n\nFor enterprises that require their own app registration, configure BYO mode:\n\n```toml\n[profiles.customer]\nauth_app = \"byo\"\nclient_id = \"11111111-1111-1111-1111-111111111111\"\ntenant_id = \"22222222-2222-2222-2222-222222222222\"\nauth_flow = \"device-code\"\n```\n\nThen sign in:\n\n```bash\nteams --profile customer auth login --device-code\nteams --profile customer auth doctor --output json\n```\n\nOfficial Microsoft trust checklist before broad external rollout:\n\n- Complete Microsoft Entra publisher verification for the OSO app.\n- Set clear Entra branding: display name, logo, publisher domain, homepage, privacy policy, and terms URLs.\n- Document every delegated Graph scope requested.\n- Use tenant-specific admin consent URLs for customer onboarding.\n- Teams Store submission is not required for this CLI-only Graph app. It becomes relevant only if OSO ships a Teams app/bot package.\n\n## Agent Authentication\n\nFor Teams message posting and most user-facing actions, use **delegated auth**.\nMessages sent through Microsoft Graph appear as the signed-in user, and Microsoft\nGraph does not generally support app-only tokens for normal live Teams\nchat/channel message posting.\n\nDelegated login defaults to the OSO multi-tenant public client app. Customers\ncan still bring their own Entra app by passing `--client-id` and `--tenant-id`\nor configuring a profile; see\n[`docs/auth-implementation-plan.md`](docs/auth-implementation-plan.md).\n\nThis CLI calls Microsoft Graph. Every token used by Graph commands must be a\nMicrosoft Graph access token. Tokens captured from the Microsoft Teams web or\ndesktop client, including `fossteams/teams-token` files such as\n`~/.config/fossteams/token-teams.jwt`, are issued for Teams-specific audiences\nand will fail against Graph with `InvalidAuthenticationToken: Invalid audience`.\n\n```bash\n# Browser-based login with OSO's public client app\nteams auth login\n\n# Device code flow with OSO's public client app\nteams auth login --device-code\n\n# Channel message reads require a broader Graph scope that often needs admin approval\nteams auth login --device-code --scopes \"User.Read ChannelMessage.Read.All offline_access\"\n\n# Browser-based login with a customer-owned app\nteams auth login --client-id \u003cclient-id\u003e --tenant-id \u003ctenant-id\u003e\n\n# Device code flow with a customer-owned app\nteams auth login --device-code --client-id \u003cclient-id\u003e --tenant-id \u003ctenant-id\u003e\n```\n\nUse **client credentials** only for commands backed by Graph application\npermissions, such as supported read/admin automation. Do not use this as the\nprimary model for sending normal Teams messages:\n\n```bash\n# Environment variables for app-only Graph operations\nexport TEAMS_CLI_CLIENT_ID=\u003cclient-id\u003e\nexport TEAMS_CLI_CLIENT_SECRET=\u003cclient-secret\u003e\nexport TEAMS_CLI_TENANT_ID=\u003ctenant-id\u003e\nteams auth login --client-credentials\n\n# Pass a pre-obtained Microsoft Graph token directly\nexport TEAMS_CLI_ACCESS_TOKEN=\u003caccess-token\u003e\nteams team list  # no login step needed\n\n# Explicit flags\nteams auth login --client-credentials \\\n  --client-id \u003cclient-id\u003e --client-secret \u003cclient-secret\u003e --tenant-id \u003ctenant-id\u003e\n```\n\nTokens are cached in the OS keyring — subsequent commands reuse the session without re-authentication.\n\nDefault delegated login asks for chat, channel-send, discovery, user lookup,\nand presence scopes. It intentionally does not request\n`ChannelMessage.Read.All`, because Microsoft marks that delegated scope as\nadmin-consent required. Use `--scopes` or a customer-owned app when a workflow\nneeds channel message reads.\n\n**Credential resolution order**: CLI flags \u003e environment variables \u003e config file profiles.\n\n### Why not import Teams client tokens?\n\nTools such as `fossteams/teams-token` are attractive because they avoid Entra\napp registration and consent setup, especially in tenants where users cannot\napprove third-party apps themselves. That is a real usability signal: `teams\nauth login` should be easy to diagnose, should support browser and device-code\nflows clearly, and should not make users reverse-engineer token audiences.\n\nThe supported fix is better Graph-native auth, not reusing Teams client tokens.\nA Teams, Skype, ChatSvcAgg, or ID token cannot be converted into a Graph token\nby this CLI. Use delegated login, device-code login, client credentials for\nsupported app-only Graph operations, or `TEAMS_CLI_ACCESS_TOKEN` only when the\ntoken was explicitly acquired for Microsoft Graph.\n\n## Agent Workflow Patterns\n\n### Pattern 1: Read-Act-Respond\n\n```bash\n# Agent reads recent messages, decides how to respond\nMESSAGES=$(teams message list --team $TEAM --channel $CHANNEL --output json)\n# Parse with jq, pass to LLM, then act\necho \"$MESSAGES\" | jq -r '.data[].body.content' | my-agent-process\nteams message send --team $TEAM --channel $CHANNEL --body \"$RESPONSE\"\n```\n\n### Pattern 2: Fan-Out Notifications\n\n```bash\n# Send a message to every channel in a team\nteams channel list $TEAM_ID --output json | \\\n  jq -r '.data[].id' | \\\n  xargs -I{} teams message send --team $TEAM_ID --channel {} --body \"Deploy v2.3.1 complete\"\n```\n\n### Pattern 3: Real-Time Event Loop\n\n```bash\n# Listen for new messages via webhook and pipe to agent processing\nteams listen --port 8080 | \\\n  jq --unbuffered 'select(.changeType == \"created\")' | \\\n  while IFS= read -r event; do\n    my-agent-handler \"$event\"\n  done\n```\n\n### Pattern 4: Stdin Composition\n\n```bash\n# Pipe any command output as a Teams message\nkubectl get pods --namespace prod | \\\n  teams message send --team $TEAM --channel $CHANNEL --stdin\n\n# Pipe a file as message body\ncat report.md | teams message send --team $TEAM --channel $CHANNEL --stdin --content-type html\n```\n\n### Pattern 5: Conditional Error Handling\n\n```bash\nteams message send --team $TEAM --channel $CHANNEL --body \"Hello\"\ncase $? in\n  0) echo \"Sent\" ;;\n  3) teams auth login --device-code \u0026\u0026 retry ;;\n  6) sleep 30 \u0026\u0026 retry ;;  # Rate limited\n  5) echo \"Channel not found\" ;;\n  *) echo \"Unexpected error\" ;;\nesac\n```\n\n## Capabilities\n\n### Authentication\n\n```bash\nteams auth login             # Interactive login (browser)\nteams auth login --device-code  # Device code flow\nteams auth login --client-credentials  # App-only Graph operations where supported\nteams auth status            # Check if session is valid (exit code 0/1)\nteams auth consent-url       # Print admin consent URL for the active auth app\nteams auth doctor            # Diagnose config and token state\nteams auth list              # List authenticated profiles\nteams auth switch \u003cprofile\u003e  # Switch active profile\nteams auth logout            # Clear stored credentials\nteams auth token             # Export access token to stdout\n```\n\n### Teams\n\n```bash\nteams team list              # List joined teams\nteams team get \u003cteam-id\u003e     # Get team details\nteams team create --name \"Engineering\" [--description \"...\"]\nteams team update \u003cteam-id\u003e --name \"New Name\"\nteams team delete \u003cteam-id\u003e\nteams team clone \u003cteam-id\u003e --name \"Cloned Team\" [--parts apps,tabs,settings,channels,members]\nteams team archive \u003cteam-id\u003e\nteams team unarchive \u003cteam-id\u003e\nteams team members list \u003cteam-id\u003e\nteams team members add \u003cteam-id\u003e --user-id \u003cuser-id\u003e [--role owner]\nteams team members remove \u003cteam-id\u003e \u003cmember-id\u003e\n```\n\n### Channels\n\n```bash\nteams channel list \u003cteam-id\u003e\nteams channel get \u003cteam-id\u003e \u003cchannel-id\u003e\nteams channel create \u003cteam-id\u003e --name \"releases\" [--description \"...\"] [--type private]\nteams channel update \u003cteam-id\u003e \u003cchannel-id\u003e [--name \"...\"] [--description \"...\"]\nteams channel delete \u003cteam-id\u003e \u003cchannel-id\u003e\nteams channel members list \u003cteam-id\u003e \u003cchannel-id\u003e\nteams channel members add \u003cteam-id\u003e \u003cchannel-id\u003e --user-id \u003cuser-id\u003e\nteams channel members remove \u003cteam-id\u003e \u003cchannel-id\u003e \u003cmember-id\u003e\n```\n\n### Messages\n\n```bash\nteams message send --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e --body \"Hello\"\nteams message send --chat \u003cchat-id\u003e --body \"Hello\"\nteams message send --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e --body \"\u003ch1\u003eRich\u003c/h1\u003e\" --content-type html\necho \"Build passed\" | teams message send --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e --stdin\nteams message list --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e\nteams message list --chat \u003cchat-id\u003e\nteams message get --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e --message \u003cmsg-id\u003e\nteams message reply --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e --message \u003cmsg-id\u003e --body \"Thanks!\"\nteams message delete --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e --message \u003cmsg-id\u003e\nteams message react --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e --message \u003cmsg-id\u003e --reaction like\nteams message unreact --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e --message \u003cmsg-id\u003e --reaction like\nteams message pin --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e --message \u003cmsg-id\u003e\nteams message unpin --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e --pinned-message-id \u003cid\u003e\n```\n\n### Chats\n\n```bash\nteams chat list\nteams chat get \u003cchat-id\u003e\nteams chat create --type oneOnOne --members \u003cuser-id-1\u003e,\u003cuser-id-2\u003e\nteams chat create --type oneOnOne --members \u003cyour-user-id\u003e,\u003cguest-user-id\u003e:guest   # chat with a guest user\nteams chat hide \u003cchat-id\u003e\nteams chat unhide \u003cchat-id\u003e\nteams chat members list \u003cchat-id\u003e\nteams chat members add \u003cchat-id\u003e --user-id \u003cuser-id\u003e\nteams chat members remove \u003cchat-id\u003e \u003cmember-id\u003e\n```\n\n### Presence\n\n```bash\nteams presence get                      # Your own presence\nteams presence get --user-id \u003cuser-id\u003e  # Another user's presence\nteams presence get-batch --user-ids \u003cid1\u003e,\u003cid2\u003e\nteams presence set --availability Available --activity Available\nteams presence clear\nteams presence status --message \"In deep focus\" [--expiry \u003cdatetime\u003e]\n```\n\n### Search\n\n```bash\nteams search messages --query \"quarterly review\"\nteams search users --query \"John\"\nteams search teams --query \"engineering\"\n```\n\n### Tags\n\n```bash\nteams tag list \u003cteam-id\u003e\nteams tag get \u003cteam-id\u003e \u003ctag-id\u003e\nteams tag create \u003cteam-id\u003e --name \"Frontend\" --members \u003cuser-id-1\u003e,\u003cuser-id-2\u003e\nteams tag delete \u003cteam-id\u003e \u003ctag-id\u003e\nteams tag add-member \u003cteam-id\u003e \u003ctag-id\u003e --user \u003cuser-id\u003e\nteams tag remove-member \u003cteam-id\u003e \u003ctag-id\u003e --user \u003cuser-id\u003e\n```\n\n### Meetings\n\n```bash\nteams meeting list\nteams meeting get \u003cmeeting-id\u003e\nteams meeting create --subject \"Standup\" --start \"2026-03-15T10:00:00Z\" --end \"2026-03-15T10:30:00Z\"\nteams meeting delete \u003cmeeting-id\u003e\nteams meeting join-url \u003cmeeting-id\u003e\nteams meeting attendance \u003cmeeting-id\u003e\n```\n\n### Notifications\n\n```bash\nteams notify send --user \u003cuser-id\u003e --topic \"New Assignment\" \\\n  --activity-type taskCreated --preview \"You have a new task\"\nteams notify send-to-team \u003cteam-id\u003e --topic \"Deploy\" \\\n  --activity-type deploymentComplete --preview \"v2.3.1 deployed\"\nteams notify send-to-chat \u003cchat-id\u003e --topic \"Update\" \\\n  --activity-type statusUpdate --preview \"Status changed\"\n```\n\n### Apps \u0026 Tabs\n\n```bash\nteams app list \u003cteam-id\u003e\nteams app install \u003cteam-id\u003e --app-id \u003ccatalog-app-id\u003e\nteams app uninstall \u003cteam-id\u003e --app-id \u003cinstallation-id\u003e\nteams tab list \u003cteam-id\u003e \u003cchannel-id\u003e\nteams tab create \u003cteam-id\u003e \u003cchannel-id\u003e --app-id \u003capp-id\u003e --name \"Wiki\" --content-url \u003curl\u003e\nteams tab delete \u003cteam-id\u003e \u003cchannel-id\u003e --tab-id \u003ctab-id\u003e\n```\n\n### Files\n\n```bash\nteams file list --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e\nteams file get --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e --file-id \u003cid\u003e\nteams file upload --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e --file ./report.pdf\nteams file download --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e --file-id \u003cid\u003e --path ./local.pdf\nteams file delete --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e --file-id \u003cid\u003e\nteams file share --team \u003cteam-id\u003e --channel \u003cchannel-id\u003e --file-id \u003cid\u003e --scope organization\n```\n\n### Subscriptions \u0026 Webhooks\n\n```bash\n# Create a subscription for real-time change notifications\nteams subscribe create \\\n  --resource \"/teams/{team-id}/channels/{channel-id}/messages\" \\\n  --change-type created,updated \\\n  --webhook-url https://your-endpoint/webhook\nteams subscribe list\nteams subscribe renew \u003csubscription-id\u003e [--expiration \u003cdatetime\u003e]\nteams subscribe delete \u003csubscription-id\u003e\n\n# Start a webhook listener (outputs NDJSON to stdout)\nteams listen --port 8080\nteams listen --port 8080 | jq '.changeType, .resource'\n```\n\n**Note:** Microsoft Graph requires HTTPS for webhook URLs. Use [ngrok](https://ngrok.com) or similar:\n\n```bash\nngrok http 8080\nteams subscribe create --resource \"/teams/all/messages\" \\\n  --change-type created --webhook-url https://xxxx.ngrok.io/webhook\nteams listen --port 8080\n```\n\n### User Lookup\n\n```bash\nteams user me\nteams user get \u003cuser-id-or-upn\u003e\nteams user list\n```\n\n### Configuration\n\n```bash\nteams config path            # Show config file path\nteams config show            # Show current config\nteams config set \u003ckey\u003e \u003cvalue\u003e\nteams config get \u003ckey\u003e\nteams config init            # Create default config file\n```\n\n## Resilience \u0026 Rate Limiting\n\nThe Graph API client handles transient failures automatically:\n\n- **Retry with exponential backoff** on 429 (rate limited) and 5xx errors\n- **Respects `Retry-After` header** from Microsoft Graph\n- **Configurable**: `--retry \u003cmax\u003e` flag, or `network.max_retries` in config\n- **Pagination**: `--all-pages` fetches all results; `--page-size` controls batch size\n- **Idempotent operations**: safe to retry on failure\n\nAgents should treat exit code 6 (rate limited) as \"retry later\" — the CLI has already retried internally.\n\n## Multi-Profile Support\n\nManage multiple tenants, delegated users, or service principals:\n\n```bash\nteams --profile prod auth login --device-code\nteams --profile staging auth login --device-code --client-id ... --tenant-id ...\n\nteams --profile prod team list\nteams --profile staging message send --team \u003cid\u003e --channel \u003cid\u003e --body \"Deployed\"\n```\n\nConfig file (`~/.config/teams-cli/config.toml` on Linux, `~/Library/Application Support/teams-cli/config.toml` on macOS, `%APPDATA%\\teams-cli\\config.toml` on Windows):\n\n```toml\n[default]\nprofile = \"prod\"\n\n[output]\nformat = \"auto\"    # auto, json, human, plain\ncolor = true\npage_size = 50\n\n[network]\ntimeout = 30\nmax_retries = 3\nretry_backoff_base = 2\n\n[profiles.prod]\ntenant_id = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\nauth_flow = \"device-code\"\n\n[profiles.staging]\nauth_app = \"byo\"\nclient_id = \"yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy\"\ntenant_id = \"yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy\"\nauth_flow = \"device-code\"\n```\n\n## Environment Variables\n\n| Variable | Description |\n|----------|-------------|\n| `TEAMS_CLI_CLIENT_ID` | Azure AD application (client) ID |\n| `TEAMS_CLI_CLIENT_SECRET` | Azure AD client secret |\n| `TEAMS_CLI_TENANT_ID` | Azure AD tenant ID |\n| `TEAMS_CLI_ACCESS_TOKEN` | Pre-obtained Microsoft Graph access token (skips login entirely) |\n| `RUST_LOG` | Tracing filter (e.g., `debug`, `teams=trace`) |\n\n## Verbosity\n\n```bash\nteams team list              # Warnings only (stderr)\nteams -v team list           # Info level\nteams -vv team list          # Debug level\nteams -vvv team list         # Trace level\n```\n\nTrace output goes to stderr, never polluting stdout JSON.\n\n## Shell Completions\n\n```bash\nteams completions bash \u003e\u003e ~/.bashrc\nteams completions zsh \u003e\u003e ~/.zshrc\nteams completions fish \u003e ~/.config/fish/completions/teams.fish\nteams completions powershell \u003e teams.ps1\n```\n\n## Man Pages\n\nMan pages are maintained under `docs/man/` and included in release archives:\n\n```bash\nman ./docs/man/teams.1\nman ./docs/man/teams-config.5\nman ./docs/man/teams-auth.7\nman ./docs/man/teams-agent-contract.7\nman ./docs/man/teams-examples.7\n```\n\n## Development\n\n```bash\ncargo build                          # Debug build\ncargo build --release                # Release build\ncargo test --all-targets             # All tests (unit + integration)\ncargo test --lib --bins              # Unit tests only\ncargo fmt -- --check                 # Check formatting\ncargo clippy --all-targets -- -D warnings  # Lint\n```\n\n## Contributing\n\nWe welcome issues and PRs. Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n## Security\n\nSee [SECURITY.md](SECURITY.md) for our security policy and how to report vulnerabilities.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosodevops%2Fms-teams-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fosodevops%2Fms-teams-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosodevops%2Fms-teams-cli/lists"}