Teams Link Unfurling Message Extension Prompt
Build a message extension that auto-unfurls pasted incident, PR, dashboard, and runbook URLs into rich Adaptive Card previews directly in the Teams compose box.
- Target user
- Developers adding link previews for internal ops tools in Teams
- Difficulty
- Intermediate
- Tools
- Claude, ChatGPT
The prompt
You are a Teams app developer who has built link-unfurling message extensions that turn raw internal URLs into rich, glanceable cards so engineers stop pasting bare links into incident channels. I will provide: - The URL domains/patterns to unfurl (e.g., status.acme.io/incidents/*, gitlab.acme.io/*/merge_requests/*, grafana.acme.io/d/*) - The API or metadata source behind each - My app manifest and bot registration details - Auth model for the internal APIs Your job: 1. **Manifest config** — show the exact `composeExtensions` block with `messageHandlers` of type `link` and the `domains` array. Explain domain matching rules and the wildcard limitations. 2. **Invoke handling** — implement the `composeExtension/queryLink` invoke: parse the pasted URL, route by pattern, fetch metadata, return a `result` attachment with a `preview` and full card. 3. **Per-type cards** — design a distinct Adaptive Card per URL type: - Incident: status badge, severity, opened-at, current commander - Merge request: title, author, CI status, approvals, mergeable - Dashboard: panel thumbnail (if available), time range, key SLO value 4. **Auth** — handle the case where the unfurl needs the user's identity. Show silent SSO token exchange and the `auth`/`config` response when consent is required, plus a clean sign-in card fallback. 5. **Caching & latency** — unfurls block the compose box; cache metadata briefly keyed by URL, set tight timeouts, and degrade to a minimal card on miss. 6. **Anti-patterns** — unfurling links the user can't access, leaking private titles to channel members, slow fetches that make typing feel laggy. 7. **Zero-install preview** — note how `previewCard` renders before the app is even opened and what fields must be present. Output as: (a) manifest `composeExtensions` snippet, (b) queryLink handler code, (c) one card JSON per URL type, (d) SSO/consent fallback flow, (e) a test plan covering authorized, unauthorized, unknown-domain, and API-timeout cases. Bias toward: fast and graceful over feature-rich, respecting per-user access on every unfurl.