user-invocable: false description: Generate and insert an Adaptive Card into a Copilot Studio topic using AdaptiveCardPrompt. Use when the user asks to add an adaptive card, rich card, form card, info card, confirmation card, or interactive card to a topic. argument-hint: <card-type> in <topic-name> allowed-tools: Bash(node *schema-lookup.bundle.js *), Read, Write, Edit, Glob context: fork agent: copilot-studio-author
Add Adaptive Card
Add an AdaptiveCardPrompt node to an existing Copilot Studio topic. Use this for all Adaptive Card scenarios — display-only cards, input forms, and confirmation flows.
Instructions
-
Auto-discover the agent directory:
Glob: agents/**/agent.mcs.ymlNEVER hardcode an agent name.
-
Clarify requirements from the user if not specified:
- Which topic should the card be added to?
- Which card type (form, info, confirmation)?
- What fields, labels, and placeholders are needed?
- Which topic variables should receive the submitted values?
-
Read the target topic file to understand its structure and find the correct insertion point.
-
Verify the schema if needed:
# Copilot Studio node schema node ${CLAUDE_SKILL_DIR}/../../scripts/schema-lookup.bundle.js summary AdaptiveCardPrompt # Adaptive Cards element schema (v1.6 — the version supported by Copilot Studio) node ${CLAUDE_SKILL_DIR}/../../scripts/schema-lookup.bundle.js ac-summary TextBlock node ${CLAUDE_SKILL_DIR}/../../scripts/schema-lookup.bundle.js ac-summary Input.Text node ${CLAUDE_SKILL_DIR}/../../scripts/schema-lookup.bundle.js ac-search Action -
Select and adapt the template from card-templates.md matching the requested type.
-
Generate unique IDs for all new nodes (format:
<nodeType>_<6-8 random alphanumeric>). -
Insert the node(s) at the correct position in the
actionsarray using Edit. -
Validate the updated topic file:
node ${CLAUDE_SKILL_DIR}/../../scripts/schema-lookup.bundle.js validate <topic-file.yml> -
Inform the user that they must push (VS Code Extension) and publish (Copilot Studio UI) before testing with
/chat-with-agentor/run-tests.
AdaptiveCardPrompt Structure
AdaptiveCardPrompt is the correct node kind for all Adaptive Cards in Copilot Studio. The card JSON is embedded as a multiline YAML literal string under card: |.
- kind: AdaptiveCardPrompt
id: adaptiveCardPrompt_m9Kp2x
card: |
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.5",
"body": [...],
"actions": [...]
}
output:
binding:
fieldId: Topic.MyVariable
outputType:
properties:
fieldId:
type: String
Rules:
card: |— the literal block scalar is mandatory. Do not usecard: >or inline the JSON without it.$schemaandversionare required inside the card JSON.- The
output.bindingmaps each card inputidto a topic variable (Topic.VarName, no=prefix). outputType.propertiesmust declare a type for every bound field.- Use plain
binding:— nokind:property inside it. - Every
AdaptiveCardPromptmust haveoutput,outputType, and anAction.Submitbutton — including display-only info cards. Omitting any of these causes VS Code extension errors (MissingRequiredProperty,AdaptiveCardMissingActionSubmit). - For info cards with no meaningful inputs, add an
Action.Submit(e.g. "OK") and bind a dummy acknowledgement variable (Topic.CardAcknowledged, typeString).
Node Comparison
| Node | Use When |
|---|---|
AdaptiveCardPrompt | Any Adaptive Card — with or without input fields |
SendActivity | Plain text messages with optional {} variable interpolation |
Do NOT use SendActivity with an attachments array for Adaptive Cards.
Field Validation
Do NOT use style: "Email" or style: "Tel" for validation — these only change the mobile keyboard and do not validate on submit. Always use regex:
{
"type": "Input.Text",
"id": "email",
"label": "Email address",
"placeholder": "Enter your email address",
"isRequired": true,
"regex": "^[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}$",
"errorMessage": "Enter a valid email address"
}
{
"type": "Input.Text",
"id": "mobile",
"label": "Mobile number",
"placeholder": "Enter your mobile number",
"isRequired": true,
"regex": "^[+]?[0-9][\\s\\-\\(\\)0-9]{6,14}$",
"errorMessage": "Enter a valid mobile number"
}
| Approach | Validates on submit? | Notes |
|---|---|---|
regex | Yes — blocks submit if pattern fails | Use for email, phone, any format |
isRequired: true | Yes — blocks submit if empty | Mandatory fields |
style: "Email" | No | Keyboard hint only — never use for validation |
style: "Tel" | No | Keyboard hint only — never use for validation |
Rule: Every
Input.ChoiceSet(and any required input) must have alabelproperty, or the VS Code extension raisesAdaptiveCardInputIsRequiredMissingLabel.
Output Binding
- Card input
idvalues must exactly match the keys inoutput.binding. - Topic variable references:
Topic.VariableName(no=prefix). - Declare every bound field in
outputType.propertieswith its type.
output:
binding:
userName: Topic.UserName
userEmail: Topic.UserEmail
category: Topic.Category
outputType:
properties:
userName:
type: String
userEmail:
type: String
category:
type: String
Dynamic Text Around Cards
Card JSON is static — Power Fx expressions and {} interpolation do not work inside the card body. Use SendActivity before or after AdaptiveCardPrompt for dynamic content:
- kind: SendActivity
id: sendMessage_Rz4Wq1
activity: "Hello {Topic.UserName}, please complete the form below."
- kind: AdaptiveCardPrompt
id: adaptiveCardPrompt_m9Kp2x
card: |
{ ... }
Card Types Quick Reference
| Type | Has inputs? | Has output? | Template |
|---|---|---|---|
form | Yes | Yes | Form Card |
info | No (dummy acknowledgement) | Yes (always required) | Info Card |
confirmation | Yes (ChoiceSet) | Yes | Confirmation Card |
For full YAML examples of each type, see card-templates.md.