mcp-create-adaptive-cards
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chineseprompt
---
mode: 'agent'
tools: ['changes', 'search/codebase', 'edit/editFiles', 'problems']
description: 'Add Adaptive Card response templates to MCP-based API plugins for visual data presentation in Microsoft 365 Copilot'
model: 'gpt-4.1'
tags: [mcp, adaptive-cards, m365-copilot, api-plugin, response-templates]
---prompt
---
mode: 'agent'
tools: ['changes', 'search/codebase', 'edit/editFiles', 'problems']
description: '为基于MCP的API插件添加Adaptive Card响应模板,以在Microsoft 365 Copilot中实现数据的可视化展示'
model: 'gpt-4.1'
tags: [mcp, adaptive-cards, m365-copilot, api-plugin, response-templates]
---Create Adaptive Cards for MCP Plugins
为MCP插件创建Adaptive Card
Add Adaptive Card response templates to MCP-based API plugins to enhance how data is presented visually in Microsoft 365 Copilot.
为基于MCP的API插件添加Adaptive Card响应模板,以增强Microsoft 365 Copilot中的数据可视化展示效果。
Adaptive Card Types
Adaptive Card 类型
Static Response Templates
静态响应模板
Use when API always returns items of the same type and format doesn't change often.
Define in in ai-plugin.json:
response_semantics.static_templatejson
{
"functions": [
{
"name": "GetBudgets",
"description": "Returns budget details including name and available funds",
"capabilities": {
"response_semantics": {
"data_path": "$",
"properties": {
"title": "$.name",
"subtitle": "$.availableFunds"
},
"static_template": {
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.5",
"body": [
{
"type": "Container",
"$data": "${$root}",
"items": [
{
"type": "TextBlock",
"text": "Name: ${if(name, name, 'N/A')}",
"wrap": true
},
{
"type": "TextBlock",
"text": "Available funds: ${if(availableFunds, formatNumber(availableFunds, 2), 'N/A')}",
"wrap": true
}
]
}
]
}
}
}
}
]
}当API始终返回相同类型的项目且格式不经常变化时使用。
在ai-plugin.json的中定义:
response_semantics.static_templatejson
{
"functions": [
{
"name": "GetBudgets",
"description": "Returns budget details including name and available funds",
"capabilities": {
"response_semantics": {
"data_path": "$",
"properties": {
"title": "$.name",
"subtitle": "$.availableFunds"
},
"static_template": {
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.5",
"body": [
{
"type": "Container",
"$data": "${$root}",
"items": [
{
"type": "TextBlock",
"text": "Name: ${if(name, name, 'N/A')}",
"wrap": true
},
{
"type": "TextBlock",
"text": "Available funds: ${if(availableFunds, formatNumber(availableFunds, 2), 'N/A')}",
"wrap": true
}
]
}
]
}
}
}
}
]
}Dynamic Response Templates
动态响应模板
Use when API returns multiple types and each item needs a different template.
ai-plugin.json configuration:
json
{
"name": "GetTransactions",
"description": "Returns transaction details with dynamic templates",
"capabilities": {
"response_semantics": {
"data_path": "$.transactions",
"properties": {
"template_selector": "$.displayTemplate"
}
}
}
}API Response with Embedded Templates:
json
{
"transactions": [
{
"budgetName": "Fourth Coffee lobby renovation",
"amount": -2000,
"description": "Property survey for permit application",
"expenseCategory": "permits",
"displayTemplate": "$.templates.debit"
},
{
"budgetName": "Fourth Coffee lobby renovation",
"amount": 5000,
"description": "Additional funds to cover cost overruns",
"expenseCategory": null,
"displayTemplate": "$.templates.credit"
}
],
"templates": {
"debit": {
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"size": "medium",
"weight": "bolder",
"color": "attention",
"text": "Debit"
},
{
"type": "FactSet",
"facts": [
{
"title": "Budget",
"value": "${budgetName}"
},
{
"title": "Amount",
"value": "${formatNumber(amount, 2)}"
},
{
"title": "Category",
"value": "${if(expenseCategory, expenseCategory, 'N/A')}"
},
{
"title": "Description",
"value": "${if(description, description, 'N/A')}"
}
]
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
},
"credit": {
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"size": "medium",
"weight": "bolder",
"color": "good",
"text": "Credit"
},
{
"type": "FactSet",
"facts": [
{
"title": "Budget",
"value": "${budgetName}"
},
{
"title": "Amount",
"value": "${formatNumber(amount, 2)}"
},
{
"title": "Description",
"value": "${if(description, description, 'N/A')}"
}
]
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
}
}当API返回多种类型的项目且每个项目需要不同模板时使用。
ai-plugin.json配置:
json
{
"name": "GetTransactions",
"description": "Returns transaction details with dynamic templates",
"capabilities": {
"response_semantics": {
"data_path": "$.transactions",
"properties": {
"template_selector": "$.displayTemplate"
}
}
}
}包含嵌入式模板的API响应:
json
{
"transactions": [
{
"budgetName": "Fourth Coffee lobby renovation",
"amount": -2000,
"description": "Property survey for permit application",
"expenseCategory": "permits",
"displayTemplate": "$.templates.debit"
},
{
"budgetName": "Fourth Coffee lobby renovation",
"amount": 5000,
"description": "Additional funds to cover cost overruns",
"expenseCategory": null,
"displayTemplate": "$.templates.credit"
}
],
"templates": {
"debit": {
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"size": "medium",
"weight": "bolder",
"color": "attention",
"text": "Debit"
},
{
"type": "FactSet",
"facts": [
{
"title": "Budget",
"value": "${budgetName}"
},
{
"title": "Amount",
"value": "${formatNumber(amount, 2)}"
},
{
"title": "Category",
"value": "${if(expenseCategory, expenseCategory, 'N/A')}"
},
{
"title": "Description",
"value": "${if(description, description, 'N/A')}"
}
]
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
},
"credit": {
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"size": "medium",
"weight": "bolder",
"color": "good",
"text": "Credit"
},
{
"type": "FactSet",
"facts": [
{
"title": "Budget",
"value": "${budgetName}"
},
{
"title": "Amount",
"value": "${formatNumber(amount, 2)}"
},
{
"title": "Description",
"value": "${if(description, description, 'N/A')}"
}
]
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
}
}Combined Static and Dynamic Templates
静态与动态模板结合
Use static template as default when item doesn't have template_selector or when value doesn't resolve.
json
{
"capabilities": {
"response_semantics": {
"data_path": "$.items",
"properties": {
"title": "$.name",
"template_selector": "$.templateId"
},
"static_template": {
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"text": "Default: ${name}",
"wrap": true
}
]
}
}
}
}当项目没有template_selector或值无法解析时,将静态模板作为默认模板使用。
json
{
"capabilities": {
"response_semantics": {
"data_path": "$.items",
"properties": {
"title": "$.name",
"template_selector": "$.templateId"
},
"static_template": {
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"text": "Default: ${name}",
"wrap": true
}
]
}
}
}
}Response Semantics Properties
响应语义属性
data_path
data_path
JSONPath query indicating where data resides in API response:
json
"data_path": "$" // Root of response
"data_path": "$.results" // In results property
"data_path": "$.data.items"// Nested path用于指示API响应中数据所在位置的JSONPath查询:
json
"data_path": "$" // 响应根节点
"data_path": "$.results" // results属性中
"data_path": "$.data.items"// 嵌套路径properties
properties
Map response fields for Copilot citations:
json
"properties": {
"title": "$.name", // Citation title
"subtitle": "$.description", // Citation subtitle
"url": "$.link" // Citation link
}为Copilot引用映射响应字段:
json
"properties": {
"title": "$.name", // 引用标题
"subtitle": "$.description", // 引用副标题
"url": "$.link" // 引用链接
}template_selector
template_selector
Property on each item indicating which template to use:
json
"template_selector": "$.displayTemplate"每个项目上用于指示要使用哪个模板的属性:
json
"template_selector": "$.displayTemplate"Adaptive Card Template Language
Adaptive Card 模板语言
Conditional Rendering
条件渲染
json
{
"type": "TextBlock",
"text": "${if(field, field, 'N/A')}" // Show field or 'N/A'
}json
{
"type": "TextBlock",
"text": "${if(field, field, 'N/A')}" // 显示字段值或'N/A'
}Number Formatting
数字格式化
json
{
"type": "TextBlock",
"text": "${formatNumber(amount, 2)}" // Two decimal places
}json
{
"type": "TextBlock",
"text": "${formatNumber(amount, 2)}" // 保留两位小数
}Data Binding
数据绑定
json
{
"type": "Container",
"$data": "${$root}", // Break to root context
"items": [ ... ]
}json
{
"type": "Container",
"$data": "${$root}", // 切换到根上下文
"items": [ ... ]
}Conditional Display
条件显示
json
{
"type": "Image",
"url": "${imageUrl}",
"$when": "${imageUrl != null}" // Only show if imageUrl exists
}json
{
"type": "Image",
"url": "${imageUrl}",
"$when": "${imageUrl != null}" // 仅当imageUrl存在时显示
}Card Elements
卡片元素
TextBlock
TextBlock
json
{
"type": "TextBlock",
"text": "Text content",
"size": "medium", // small, default, medium, large, extraLarge
"weight": "bolder", // lighter, default, bolder
"color": "attention", // default, dark, light, accent, good, warning, attention
"wrap": true
}json
{
"type": "TextBlock",
"text": "Text content",
"size": "medium", // small, default, medium, large, extraLarge
"weight": "bolder", // lighter, default, bolder
"color": "attention", // default, dark, light, accent, good, warning, attention
"wrap": true
}FactSet
FactSet
json
{
"type": "FactSet",
"facts": [
{
"title": "Label",
"value": "Value"
}
]
}json
{
"type": "FactSet",
"facts": [
{
"title": "Label",
"value": "Value"
}
]
}Image
Image
json
{
"type": "Image",
"url": "https://example.com/image.png",
"size": "medium", // auto, stretch, small, medium, large
"style": "default" // default, person
}json
{
"type": "Image",
"url": "https://example.com/image.png",
"size": "medium", // auto, stretch, small, medium, large
"style": "default" // default, person
}Container
Container
json
{
"type": "Container",
"$data": "${items}", // Iterate over array
"items": [
{
"type": "TextBlock",
"text": "${name}"
}
]
}json
{
"type": "Container",
"$data": "${items}", // 遍历数组
"items": [
{
"type": "TextBlock",
"text": "${name}"
}
]
}ColumnSet
ColumnSet
json
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "auto",
"items": [ ... ]
},
{
"type": "Column",
"width": "stretch",
"items": [ ... ]
}
]
}json
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "auto",
"items": [ ... ]
},
{
"type": "Column",
"width": "stretch",
"items": [ ... ]
}
]
}Actions
Actions
json
{
"type": "Action.OpenUrl",
"title": "View Details",
"url": "https://example.com/item/${id}"
}json
{
"type": "Action.OpenUrl",
"title": "View Details",
"url": "https://example.com/item/${id}"
}Responsive Design Best Practices
响应式设计最佳实践
Single-Column Layouts
单列布局
- Use single columns for narrow viewports
- Avoid multi-column layouts when possible
- Ensure cards work at minimum viewport width
- 为窄视口使用单列布局
- 尽可能避免多列布局
- 确保卡片在最小视口宽度下正常显示
Flexible Widths
灵活宽度
- Don't assign fixed widths to elements
- Use "auto" or "stretch" for width properties
- Allow elements to resize with viewport
- Fixed widths OK for icons/avatars only
- 不要为元素分配固定宽度
- 为宽度属性使用"auto"或"stretch"
- 允许元素随视口调整大小
- 仅图标/头像可使用固定宽度
Text and Images
文本与图片
- Avoid placing text and images in same row
- Exception: Small icons or avatars
- Use "wrap": true for text content
- Test at various viewport widths
- 避免在同一行放置文本和图片
- 例外:小图标或头像
- 为文本内容设置"wrap": true
- 在不同视口宽度下进行测试
Test Across Hubs
跨中心测试
Validate cards in:
- Teams (desktop and mobile)
- Word
- PowerPoint
- Various viewport widths (contract/expand UI)
在以下环境中验证卡片:
- Teams(桌面端和移动端)
- Word
- PowerPoint
- 各种视口宽度(收缩/展开UI)
Complete Example
完整示例
ai-plugin.json:
json
{
"functions": [
{
"name": "SearchProjects",
"description": "Search for projects with status and details",
"capabilities": {
"response_semantics": {
"data_path": "$.projects",
"properties": {
"title": "$.name",
"subtitle": "$.status",
"url": "$.projectUrl"
},
"static_template": {
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.5",
"body": [
{
"type": "Container",
"$data": "${$root}",
"items": [
{
"type": "TextBlock",
"size": "medium",
"weight": "bolder",
"text": "${if(name, name, 'Untitled Project')}",
"wrap": true
},
{
"type": "FactSet",
"facts": [
{
"title": "Status",
"value": "${status}"
},
{
"title": "Owner",
"value": "${if(owner, owner, 'Unassigned')}"
},
{
"title": "Due Date",
"value": "${if(dueDate, dueDate, 'Not set')}"
},
{
"title": "Budget",
"value": "${if(budget, formatNumber(budget, 2), 'N/A')}"
}
]
},
{
"type": "TextBlock",
"text": "${if(description, description, 'No description')}",
"wrap": true,
"separator": true
}
]
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "View Project",
"url": "${projectUrl}"
}
]
}
}
}
}
]
}ai-plugin.json:
json
{
"functions": [
{
"name": "SearchProjects",
"description": "Search for projects with status and details",
"capabilities": {
"response_semantics": {
"data_path": "$.projects",
"properties": {
"title": "$.name",
"subtitle": "$.status",
"url": "$.projectUrl"
},
"static_template": {
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.5",
"body": [
{
"type": "Container",
"$data": "${$root}",
"items": [
{
"type": "TextBlock",
"size": "medium",
"weight": "bolder",
"text": "${if(name, name, 'Untitled Project')}",
"wrap": true
},
{
"type": "FactSet",
"facts": [
{
"title": "Status",
"value": "${status}"
},
{
"title": "Owner",
"value": "${if(owner, owner, 'Unassigned')}"
},
{
"title": "Due Date",
"value": "${if(dueDate, dueDate, 'Not set')}"
},
{
"title": "Budget",
"value": "${if(budget, formatNumber(budget, 2), 'N/A')}"
}
]
},
{
"type": "TextBlock",
"text": "${if(description, description, 'No description')}",
"wrap": true,
"separator": true
}
]
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "View Project",
"url": "${projectUrl}"
}
]
}
}
}
}
]
}Workflow
工作流程
Ask the user:
- What type of data does the API return?
- Are all items the same type (static) or different types (dynamic)?
- What fields should appear in the card?
- Should there be actions (e.g., "View Details")?
- Are there multiple states or categories requiring different templates?
Then generate:
- Appropriate response_semantics configuration
- Static template, dynamic templates, or both
- Proper data binding with conditional rendering
- Responsive single-column layout
- Test scenarios for validation
询问用户:
- API返回的数据类型是什么?
- 所有项目类型相同(静态)还是不同(动态)?
- 卡片中应显示哪些字段?
- 是否需要添加操作(如“查看详情”)?
- 是否存在需要不同模板的多种状态或类别?
然后生成:
- 合适的response_semantics配置
- 静态模板、动态模板或两者结合
- 带有条件渲染的正确数据绑定
- 响应式单列布局
- 用于验证的测试场景
Resources
资源
- Adaptive Card Designer - Visual design tool
- Adaptive Card Schema - Full schema reference
- Template Language - Binding syntax guide
- JSONPath - Path query syntax
- Adaptive Card Designer - 可视化设计工具
- Adaptive Card Schema - 完整架构参考
- Template Language - 绑定语法指南
- JSONPath - 路径查询语法
Common Patterns
常见模式
List with Images
带图片的列表
json
{
"type": "Container",
"$data": "${items}",
"items": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "Image",
"url": "${thumbnailUrl}",
"size": "small",
"$when": "${thumbnailUrl != null}"
}
]
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"text": "${title}",
"weight": "bolder",
"wrap": true
}
]
}
]
}
]
}json
{
"type": "Container",
"$data": "${items}",
"items": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "Image",
"url": "${thumbnailUrl}",
"size": "small",
"$when": "${thumbnailUrl != null}"
}
]
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"text": "${title}",
"weight": "bolder",
"wrap": true
}
]
}
]
}
]
}Status Indicators
状态指示器
json
{
"type": "TextBlock",
"text": "${status}",
"color": "${if(status == 'Completed', 'good', if(status == 'In Progress', 'attention', 'default'))}"
}json
{
"type": "TextBlock",
"text": "${status}",
"color": "${if(status == 'Completed', 'good', if(status == 'In Progress', 'attention', 'default'))}"
}Currency Formatting
货币格式化
json
{
"type": "TextBlock",
"text": "$${formatNumber(amount, 2)}"
}undefinedjson
{
"type": "TextBlock",
"text": "$${formatNumber(amount, 2)}"
}undefined