Skip to content
cccskills
FreePublished on GitHub

teams-connect

Set up Microsoft Teams as a notification channel for your Egregore — registers an Azure bot, uploads the app, and binds a channel. Use for /teams-connect, 'connect teams', or 'set up teams notifications'.

The English summary is being prepared. Showing the original description.

egregore-labs/egregore293 stars(GitHub 293 + cccskills 0)

Run score Not tested

See how to install

Files included (1)

  • SKILL.md10.2 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Set up Microsoft Teams as a notification channel for your Egregore.

Arguments: $ARGUMENTS (none expected)

When to invoke

Trigger phrases:

  • "set up teams", "connect teams", "teams-connect", "microsoft teams"
  • "add teams notifications", "teams channel", "notify us on teams"

Disambiguation:

  • "set up telegram" → /telegram-connect
  • "send a message to teams" → NOT this — use the separate notification consent protocol in .claude/context/notification-consent.md

What this does

Registers a bot in the org's own Microsoft tenant, installs it in their Teams, and binds one channel to Egregore notifications. After this, everything that plans a group notification may also resolve Teams as a receiving channel; all resolved channels are shown before separate dispatch approval.

You (the agent) run all the Azure work via az after one browser login. The human does exactly three things: one login, two portal clicks, one paste.

OSS tier — visible, gated, honest

This skill ships to every Egregore, but Teams notifications run on the Connected tier. Before anything else, detect the tier:

bash bin/config-get.sh mode

Use the printed value as {mode}; quote it in single quotes when you use it in a command, and write any single quote inside the value as '\''. If it prints local, the instance is on the local (OSS) tier; if it prints connected, continue with setup. The command uses exactly the canonical _detect_mode truth table (bin/lib/config.sh): explicit local mode or an empty API URL means local. (A hand-set mode: "connected" without an api_url is still local — never treat it as an unlock.)

On the local tier, deliver exactly this message, then the question — do not paraphrase the message:

You can expand your knowledge base with connections to Notion, Google Drive, Docs, Sheets, and many more. Upgrade to Connected Tier to accelerate.

Ask with a structured question when available and permitted in this session; otherwise ask in plain text:

  • Upgrade to Connected Tier — tell them the one sanctioned path: run egregore connect in a terminal (the launcher walks the whole upgrade: registers your org with the platform, provisions the key, replays your graph). Teams notifications unlock once Connect is active.
  • Not now — respect it and stop. Do not improvise config edits, api_url values, or partial flows.

Only continue past this section on a connected instance.

Step 0: Prerequisites (check before touching anything)

Ask, or verify where possible. All three must be true:

  1. A Microsoft 365 tenant with Teams (work tenant, not personal Teams).
  2. The person here is admin of that tenant (Entra + Teams admin center).
  3. An Azure subscription in that tenant — the bot resource is free (F0) but must live in a subscription.

az CLI must be installed: which az — if missing, brew install azure-cli (macOS) or point at https://aka.ms/azure-cli for their OS.

Step 1: Login (human, ~1 min)

Ask them to run in the prompt:

! az login

Then verify tenant + user:

az account show --query "{tenant:tenantId, user:user.name, state:state}" -o json

Verify Teams is actually licensed (the #1 silent dead-end — an Azure-only tenant has no Teams and the Teams admin center errors with a cryptic "can't find the tenant region for …" / PLS message):

az rest --method get --url "https://graph.microsoft.com/v1.0/subscribedSkus" \
  --query "value[].skuPartNumber" -o json
  • [] or no Teams-bearing SKU → they need a license first. Guide: M365 admin center → Marketplace → Microsoft 365 Business Basic (Trial). EEA trap: every SKU marked "(no Teams)" literally excludes Teams (EU unbundling) — pick the plain variant, or add "Microsoft Teams EEA" separately. After purchase, assign the license (set usageLocation on the user first — assignLicense fails without it, and racing the two calls fails too: PATCH usageLocation, then assign in a separate call).
  • Graph 403 AADSTS530035 (security defaults) → az logout && az login fresh.

Step 2: Azure infrastructure (agent, ~2 min)

Use the app ID printed here as {app_id}, the tenant ID from Step 1 as {tenant_id}, and the chosen Azure region (for example westeurope) as {region}; single-quote substituted values as shown and write any embedded single quote as '\''.

  1. App registration (single-tenant — deliberate; Microsoft is deprecating new multi-tenant bots):
az ad app create --display-name "Egregore Notifications" --sign-in-audience AzureADMyOrg \
  --query "{appId:appId}" -o json
  1. Client secret (2 years):
az ad app credential reset --id '{app_id}' --display-name "teams-notify" --years 2 -o json
  1. Service principal — az ad app create does NOT make one; skipping this yields token 401 AADSTS7000229 later:
az ad sp create --id '{app_id}'
  1. Resource group + free Azure Bot + Teams channel, one command at a time:
az group create -n egregore-bots -l '{region}'
az bot create --resource-group egregore-bots --name egregore-notifications \
  --app-type SingleTenant --appid '{app_id}' --tenant-id '{tenant_id}' --sku F0
az bot msteams create --name egregore-notifications --resource-group egregore-bots

Save to the instance .env (gitignored — never commit):

TEAMS_APP_ID=…
TEAMS_APP_SECRET=…
TEAMS_TENANT_ID=…

Smoke the token before proceeding (catches AADSTS7000229 immediately):

curl -sS -X POST "https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token" \
  -d grant_type=client_credentials -d "client_id=<APP_ID>" \
  -d "client_secret=<SECRET>" -d "scope=https://api.botframework.com/.default"

Step 3: App package + catalog upload (human, 2 clicks)

Build and reveal the zip:

bash teams-app/build-package.sh '{app_id}'

After the build succeeds:

open teams-app/build/

(If this instance doesn't carry teams-app/, fetch it from the framework repo.)

Guide them: https://admin.teams.microsoft.com/policies/manage-apps → Actions → Upload new app → pick egregore-teams-app.zip.

Traps:

  • Fresh tenants show "setting up your new app management experience… up to 30 minutes" with the upload button greyed out. It's provisioning — wait, refresh.
  • Re-uploading the same app id via "Upload new app" is rejected — updates go through the app's detail page → Upload file to update.

Step 4: Add the bot to the team (human, 1 click)

In Teams — the work account (trap: the desktop app may silently be the consumer client — "Build your community" UI, no org catalog; use teams.microsoft.com if in doubt): Apps → Built for your org → Egregore → ▾ → Add to a team → pick the channel → Set up a bot.

Without this, sends fail 403 BotNotInConversationRoster.

Step 5: Channel binding (human pastes, agent decodes)

Ask for the channel link (channel → ⋯ → Get link to channel), then decode:

python3 -c "import sys,urllib.parse,re; m=re.search(r'(19%3a[^/?]+|19:[^/?]+)', sys.argv[1]); print(urllib.parse.unquote(m.group(1)))" "<PASTED_LINK>"

Result must look like 19:…@thread.tacv2.

Step 6: Persist

mkdir -p tmp

Only read the response with jq after the helper succeeds. If it fails, show its error and stop; the helper leaves no response file after a failure.

Write the file tmp/teams-connect-bind.json with {"conversation_id":"<CONV_ID>","app_id":"<APP_ID>","app_secret":"<SECRET>","tenant_id":"<TENANT_ID>"}, substituting the channel and Azure registration values. Non-default region: the bot's service URL may differ from the EMEA default — include "service_url" too (Azure portal → the bot → Channels → Teams shows it). The helper reads EGREGORE_API_KEY internally.

bash bin/api-call.sh POST /api/org/teams --auth egregore --json-file tmp/teams-connect-bind.json --out tmp/teams-connect-bind-response.json
jq '.' tmp/teams-connect-bind-response.json

Expect {"status":"connected", …}. The per-org creds matter: a single-tenant bot only works in its home tenant, so each org brings its own registration.

Step 7: Verify with separate exact notification consent

Follow .claude/context/notification-consent.md. Connecting Teams is not consent to send a test message. Prepare after configuration is final:

mkdir -p tmp
bash bin/notify.sh plan group "Teams channel connected — this message reached you through Egregore." > tmp/teams-connect-plan.json
jq -r '.plan_id, .digest' tmp/teams-connect-plan.json

Use the printed values as {plan_id} and {digest}, respectively, for the notification consent flow. Read the exact preview fields:

jq -r '.org, .recipient, .channels, .deliveries, .message' tmp/teams-connect-plan.json

Show every resolved receiving channel—potentially both Telegram and Teams—and the exact message in a dedicated Send / Edit / Cancel checkpoint. Only dispatch after Send is selected for that preview. Card avatar may show a generic icon for a few hours (Teams caches app icons) — cosmetic, self-heals.

Troubleshooting quick table

SymptomCause → fix
"can't find the tenant region" (PLS) in Teams admin centerNo M365/Teams license on the tenant → Step 1 licensing
Token 401 AADSTS7000229Missing service principal → az ad sp create --id '{app_id}'
Send 403 BotNotInConversationRosterBot not added to the team → Step 4
Send 404Wrong conversation id or wrong service_url region
"already an app with the same app ID" on uploadUse the app detail page → Upload file to update
No Apps rail / no org catalog in TeamsConsumer Teams client → work account at teams.microsoft.com
Upload button greyed outTenant still provisioning app management (≤30 min)

After

Emit telemetry (fire-and-forget):

bash bin/telemetry.sh emit "teams-connect" '{"command":"teams-connect"}' 2>/dev/null &

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Explicit Activity card only. Use when the user names /activity or directly asks to show, open, or render the Activity dashboard/card. Never use it to answer or synthesize what the team is focused on, busy with, prioritizing, or doing; analytical organizational questions use Search/Runtime QMD.

The English summary is being prepared. Showing the original description.

egregore-labs/egregore293Updated Oct 17, 2026

Draft, preview, and explicitly send an Egregore group announcement. Use for /announce, telling the team something, or notifying everyone—not a direct message or structured handoff.

The English summary is being prepared. Showing the original description.

egregore-labs/egregore293Updated Oct 17, 2026

Capture an effective prompting or steering technique as a reusable canonical pattern. Use when a prompt chain worked especially well; use /reflect for insight about the work and /note for a private thought.

The English summary is being prepared. Showing the original description.

egregore-labs/egregore293Updated Oct 17, 2026

branch

Free

Create a working branch from what you are about to work on. Use for /branch, or any request to start a new branch before making changes.

The English summary is being prepared. Showing the original description.

egregore-labs/egregore293Updated Oct 17, 2026

Run diagnostics on an Egregore environment and render a health report, repairing safe local Runtime problems when possible. Use for /checkup, a health check, or when something is broken and the root cause is unclear.

The English summary is being prepared. Showing the original description.

egregore-labs/egregore293Updated Oct 17, 2026

commit

Free

Stage changes and create a commit with a properly formatted message. Use for /commit, or saving work locally — not sharing it (/push) or opening a pull request (/pr).

The English summary is being prepared. Showing the original description.

egregore-labs/egregore293Updated Oct 17, 2026

See all skills by egregore-labs

Report a problem with this skill