All skills
microsoft avatar

/teams-app-developer

@0bef15b
by microsoftmicrosoft/skills3.1k stars
351

Builds, tests, and deploys Microsoft 365 apps and agents for Teams and Copilot. Includes sub-skills for project creation, local testing, cloud deployment, troubleshooting, and Slack-to-Teams migration. USE FOR: Teams agent, bot, tab, message extension, Declarative Agents, Custom Engine Agents, local testing, Agents Playground, Azure resource provision, remote deployment, Slack to Teams migration, cross-platform bot development, Block Kit to Adaptive Cards conversion. DO NOT USE FOR: general web development, non-bot/non-Teams projects.

Use this Skill: https://skilld.dev/gh/microsoft/skills/teams-app-developer

This session only. Nothing lands on disk.

docsui-components.md

≈1.8k tokens on demand. Your agent reads this file only when SKILL.md points to it.

UI Components

Block Kit vs Adaptive Cards

Slack Block Kit Adaptive Card Element Notes
section (text) TextBlock Set wrap: true; convert *bold* mrkdwn → **bold** Markdown
section (fields) FactSet Each field becomes { title, value }
section (text + accessory) ColumnSet with 2 Columns Col 1 = text, Col 2 = accessory
header TextBlock size Large, weight Bolder
actions ActionSet Max 6 actions in Teams (vs 25 in Slack)
divider TextBlock with separator: true
image Image alt_text (underscore) → altText (camelCase)
context TextBlock size Small, isSubtle: true
input (plain_text) Input.Text
input (static_select) Input.ChoiceSet style: "compact"
input (multi_select) Input.ChoiceSet isMultiSelect: true
input (datepicker) Input.Date
input (timepicker) Input.Time
input (checkboxes) Input.ChoiceSet style: "expanded", isMultiSelect: true
input (radio_buttons) Input.ChoiceSet style: "expanded"
rich_text RichTextBlock Schema 1.5+
overflow menu No equivalent Redesign as ActionSet or Input.ChoiceSet dropdown

Rating: GREEN for most elements, RED for overflow menus.

Markdown Differences

Formatting Slack mrkdwn Adaptive Card Markdown
Bold *bold* **bold**
Italic _italic_ _italic_
Strikethrough ~strike~ ~~strike~~
Code `code` `code`
Emoji :emoji_shortcode: Unicode characters only
User mention <@U12345> Display name (no mention syntax)

Impact: Failing to convert *bold* to **bold** produces literal asterisks in Teams. Slack emoji shortcodes render as plain text in Adaptive Cards.

Mitigation: Apply a text transform function when converting between formats:

// Slack mrkdwn → Adaptive Card Markdown
text.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, "**$1**")
    .replace(/~([^~]+)~/g, "~~$1~~");

// Adaptive Card Markdown → Slack mrkdwn
text.replace(/\*\*([^*]+)\*\*/g, "*$1*")
    .replace(/~~([^~]+)~~/g, "~$1~");

Button / Action Styles

Slack Adaptive Card Visual
"primary" "positive" Green
"danger" "destructive" Red
(default) "default" Neutral

Interaction Model Difference

This is the biggest behavioral shift between platforms:

Aspect Slack Teams
Model Event-per-interaction — every select/button fires immediately Form-then-submit — inputs collect, submit sends all at once
Select behavior static_select fires block_actions on selection Input.ChoiceSet does nothing until Action.Submit clicked
Live updates dispatch_action: true for real-time block_actions No equivalent — use Action.Execute with refresh for critical cases
Form data Per-element: action.selected_option.value Batched: activity.value contains all input IDs

Impact: Bots that rely on instant-fire selects to update UI dynamically will feel different in Teams. The Teams UX has fewer round trips but less reactivity.

Mitigation: Accept the batch-submit model for Teams. Group related inputs and submit together. For cases requiring per-interaction updates, use Action.Execute with the refresh property (schema 1.4+).

Block / Action Limits

Limit Slack Teams
Blocks per message 50 No formal block limit, but 28 KB payload max
Blocks per modal/view 100 28 KB payload max
Actions per block 25 per actions block 6 per ActionSet
Card payload size No formal limit 28 KB after JSON serialization

Mitigation: Paginate dense action rows into multiple cards. Consolidate overflow menus into Input.ChoiceSet dropdowns.


Modals vs Dialogs (Task Modules)

Aspect Slack Modal Teams Dialog (Task Module)
Open views.open(trigger_id, view) Return card from dialog.open handler
Submit handler app.view("callback_id") app.on("dialog.submit")
Form data location view.state.values[block_id][action_id] activity.value.data (flat object keyed by input id)
Stacking views.push() — up to 3 levels Not supported
Cancel notification notify_on_close: true → view_closed Not supported
Mid-form updates dispatch_action + block_actions + views.update Not supported
Field validation ack({ response_action: "errors", errors }) Client-side only (isRequired, regex)
Private metadata private_metadata (3000 char limit) Hidden fields in Action.Submit.data
View hash (race protection) view_hash parameter in views.update Not supported — manual _version field needed

Rating: YELLOW for basic modal-to-dialog, RED for stacking, cancel notifications, and mid-form updates.

Mitigation Strategies

Gap Strategy Effort
No stacking Flatten into single dialog with step routing in submit handler. Include a "Back" button that re-renders the previous step. 8–16 hrs
No cancel notification Add explicit "Cancel" button inside the dialog. Implement 5-min timeout-based cleanup for stale locks. 4–8 hrs
No mid-form updates Multi-step dialogs for dependent fields. Action.ToggleVisibility for simple show/hide. 8–16 hrs
No server-side validation Re-open dialog with pre-populated data + error messages in field labels. Use client-side isRequired/regex where possible. 4–8 hrs
No view hash Inject _version counter into Action.Submit.data. Reject stale updates server-side. 2–4 hrs

Reverse Direction (Teams → Slack)

Dialogs map to modals via views.open(). Teams' batch-submit model decomposes into Slack's per-element handlers. Client-side validation (isRequired, regex) becomes server-side validation in view_submission.


App Home vs Personal Tab

Aspect Slack App Home Teams Personal Tab
Render views.publish() with Block Kit tab.fetch handler returns Adaptive Card, or staticTabs in manifest for web
Trigger app_home_opened event tab.fetch fires on every tab open
Dynamic updates views.publish() any time tab.submit for interactions within tab
Race protection view_hash parameter Not supported

Rating: YELLOW — functional equivalent exists, different event model.

Mitigation Strategies

Strategy How Effort
tab.fetch handler (Recommended) Return Adaptive Card on every tab open. Closest to app_home_opened. 4–8 hrs
Welcome message only Send card to 1:1 chat on install.add. Simple but fires once. 1–2 hrs
Static web tab Full web page in iframe. Richer UI but needs hosting. 8–16 hrs

Source: SKILL.md on GitHub

1 alert3mo3 checks · Risk SAFE
  • Gen Agent Trust Hub3mo

    This skill provides a comprehensive developer guide for building Microsoft 365 agents and Teams applications. It includes several security considerations such as handling untrusted user input, using dynamic execution in examples, and reading sensitive local files for protocol requirements. These patterns are presented with appropriate security warnings and architectural mitigations. See detailed analysis for more context.

  • Socket3mo

    No alerts

  • Snyk3mo

    Risk: HIGH · 1 issue

Signed by skilld at 0bef15b. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 20 hours ago.

Activeupdated 3 months ago

README badge

README badge for microsoft/skills/teams-app-developer