mcp-create-adaptive-cards by github/awesome-copilot
npx skills add https://github.com/github/awesome-copilot --skill mcp-create-adaptive-cardsmode: '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] ---
# 为 MCP 插件创建 Adaptive Cards
为基于 MCP 的 API 插件添加 Adaptive Card 响应模板,以增强在 Microsoft 365 Copilot 中数据的可视化呈现方式。
## Adaptive Card 类型
### 静态响应模板
当 API 始终返回相同类型的项目且格式不经常更改时使用。
在 ai-plugin.json 的 `response_semantics.static_template` 中定义:
```json
{
"functions": [
{
"name": "GetBudgets",
"description": "返回预算详情,包括名称和可用资金",
"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": "名称: ${if(name, name, 'N/A')}",
"wrap": true
},
{
"type": "TextBlock",
"text": "可用资金: ${if(availableFunds, formatNumber(availableFunds, 2), 'N/A')}",
"wrap": true
}
]
}
]
}
}
}
}
]
}
```
### 动态响应模板
当 API 返回多种类型且每个项目需要不同的模板时使用。
**ai-plugin.json 配置:**
```json
{
"name": "GetTransactions",
"description": "返回带有动态模板的交易详情",
"capabilities": {
"response_semantics": {
"data_path": "$.transactions",
"properties": {
"template_selector": "$.displayTemplate"
}
}
}
}
```
**包含嵌入式模板的 API 响应:**
```json
{
"transactions": [
{
"budgetName": "Fourth Coffee 大厅翻新",
"amount": -2000,
"description": "许可证申请的物业勘测",
"expenseCategory": "permits",
"displayTemplate": "$.templates.debit"
},
{
"budgetName": "Fourth Coffee 大厅翻新",
"amount": 5000,
"description": "用于支付成本超支的额外资金",
"expenseCategory": null,
"displayTemplate": "$.templates.credit"
}
],
"templates": {
"debit": {
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"size": "medium",
"weight": "bolder",
"color": "attention",
"text": "借记"
},
{
"type": "FactSet",
"facts": [
{
"title": "预算",
"value": "${budgetName}"
},
{
"title": "金额",
"value": "${formatNumber(amount, 2)}"
},
{
"title": "类别",
"value": "${if(expenseCategory, expenseCategory, 'N/A')}"
},
{
"title": "描述",
"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": "贷记"
},
{
"type": "FactSet",
"facts": [
{
"title": "预算",
"value": "${budgetName}"
},
{
"title": "金额",
"value": "${formatNumber(amount, 2)}"
},
{
"title": "描述",
"value": "${if(description, description, 'N/A')}"
}
]
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
}
}
```
### 静态与动态模板结合
当项目没有 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": "默认: ${name}",
"wrap": true
}
]
}
}
}
}
```
## 响应语义属性
### data_path
JSONPath 查询,指示数据在 API 响应中的位置:
```json
"data_path": "$" // 响应根目录
"data_path": "$.results" // 在 results 属性中
"data_path": "$.data.items"// 嵌套路径
```
### properties
映射响应字段以供 Copilot 引用:
```json
"properties": {
"title": "$.name", // 引用标题
"subtitle": "$.description", // 引用副标题
"url": "$.link" // 引用链接
}
```
### template_selector
每个项目上指示使用哪个模板的属性:
```json
"template_selector": "$.displayTemplate"
```
## Adaptive Card 模板语言
### 条件渲染
```json
{
"type": "TextBlock",
"text": "${if(field, field, 'N/A')}" // 显示字段或 'N/A'
}
```
### 数字格式化
```json
{
"type": "TextBlock",
"text": "${formatNumber(amount, 2)}" // 两位小数
}
```
### 数据绑定
```json
{
"type": "Container",
"$data": "${$root}", // 切换到根上下文
"items": [ ... ]
}
```
### 条件显示
```json
{
"type": "Image",
"url": "${imageUrl}",
"$when": "${imageUrl != null}" // 仅当 imageUrl 存在时显示
}
```
## 卡片元素
### TextBlock
```json
{
"type": "TextBlock",
"text": "文本内容",
"size": "medium", // small, default, medium, large, extraLarge
"weight": "bolder", // lighter, default, bolder
"color": "attention", // default, dark, light, accent, good, warning, attention
"wrap": true
}
```
### FactSet
```json
{
"type": "FactSet",
"facts": [
{
"title": "标签",
"value": "值"
}
]
}
```
### Image
```json
{
"type": "Image",
"url": "https://example.com/image.png",
"size": "medium", // auto, stretch, small, medium, large
"style": "default" // default, person
}
```
### Container
```json
{
"type": "Container",
"$data": "${items}", // 遍历数组
"items": [
{
"type": "TextBlock",
"text": "${name}"
}
]
}
```
### ColumnSet
```json
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "auto",
"items": [ ... ]
},
{
"type": "Column",
"width": "stretch",
"items": [ ... ]
}
]
}
```
### Actions
```json
{
"type": "Action.OpenUrl",
"title": "查看详情",
"url": "https://example.com/item/${id}"
}
```
## 响应式设计最佳实践
### 单列布局
- 在狭窄的视口中使用单列
- 尽可能避免多列布局
- 确保卡片在最小视口宽度下正常工作
### 灵活宽度
- 不要为元素分配固定宽度
- 对 width 属性使用 "auto" 或 "stretch"
- 允许元素随视口调整大小
- 仅图标/头像可以使用固定宽度
### 文本和图像
- 避免将文本和图像放在同一行
- 例外:小图标或头像
- 对文本内容使用 "wrap": true
- 在不同视口宽度下进行测试
### 跨中心测试
在以下环境中验证卡片:
- Teams(桌面端和移动端)
- Word
- PowerPoint
- 各种视口宽度(收缩/展开 UI)
## 完整示例
**ai-plugin.json:**
```json
{
"functions": [
{
"name": "SearchProjects",
"description": "搜索带有状态和详情的项目",
"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, '未命名项目')}",
"wrap": true
},
{
"type": "FactSet",
"facts": [
{
"title": "状态",
"value": "${status}"
},
{
"title": "负责人",
"value": "${if(owner, owner, '未分配')}"
},
{
"title": "截止日期",
"value": "${if(dueDate, dueDate, '未设置')}"
},
{
"title": "预算",
"value": "${if(budget, formatNumber(budget, 2), 'N/A')}"
}
]
},
{
"type": "TextBlock",
"text": "${if(description, description, '无描述')}",
"wrap": true,
"separator": true
}
]
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "查看项目",
"url": "${projectUrl}"
}
]
}
}
}
}
]
}
```
## 工作流程
询问用户:
1. API 返回什么类型的数据?
2. 所有项目都是相同类型(静态)还是不同类型(动态)?
3. 卡片中应显示哪些字段?
4. 是否需要操作(例如“查看详情”)?
5. 是否存在需要不同模板的多种状态或类别?
然后生成:
- 适当的 response_semantics 配置
- 静态模板、动态模板或两者
- 带有条件渲染的适当数据绑定
- 响应式单列布局
- 用于验证的测试场景
## 资源
- [Adaptive Card Designer](https://adaptivecards.microsoft.com/designer) - 可视化设计工具
- [Adaptive Card Schema](https://adaptivecards.io/schemas/adaptive-card.json) - 完整模式参考
- [模板语言](https://learn.microsoft.com/en-us/adaptive-cards/templating/language) - 绑定语法指南
- [JSONPath](https://www.rfc-editor.org/rfc/rfc9535) - 路径查询语法
## 常见模式
### 带图像的列表
```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": "TextBlock",
"text": "${status}",
"color": "${if(status == 'Completed', 'good', if(status == 'In Progress', 'attention', 'default'))}"
}
```
### 货币格式化
```json
{
"type": "TextBlock",
"text": "$${formatNumber(amount, 2)}"
}
```
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
每周安装量
7.3K
代码仓库
GitHub 星标数
26.7K
首次出现
2026年2月25日
安全审计
安装于
codex7.2K
gemini-cli7.2K
opencode7.2K
cursor7.2K
github-copilot7.2K
amp7.2K
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]
---
# Create Adaptive Cards for MCP Plugins
Add Adaptive Card response templates to MCP-based API plugins to enhance how data is presented visually in Microsoft 365 Copilot.
## Adaptive Card Types
### Static Response Templates
Use when API always returns items of the same type and format doesn't change often.
Define in `response_semantics.static_template` in ai-plugin.json:
```json
{
"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"
}
}
}
```
### 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
}
]
}
}
}
}
```
## Response Semantics Properties
### 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
```
### properties
Map response fields for Copilot citations:
```json
"properties": {
"title": "$.name", // Citation title
"subtitle": "$.description", // Citation subtitle
"url": "$.link" // Citation link
}
```
### template_selector
Property on each item indicating which template to use:
```json
"template_selector": "$.displayTemplate"
```
## Adaptive Card Template Language
### Conditional Rendering
```json
{
"type": "TextBlock",
"text": "${if(field, field, 'N/A')}" // Show field or 'N/A'
}
```
### Number Formatting
```json
{
"type": "TextBlock",
"text": "${formatNumber(amount, 2)}" // Two decimal places
}
```
### Data Binding
```json
{
"type": "Container",
"$data": "${$root}", // Break to root context
"items": [ ... ]
}
```
### Conditional Display
```json
{
"type": "Image",
"url": "${imageUrl}",
"$when": "${imageUrl != null}" // Only show if imageUrl exists
}
```
## Card Elements
### 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
}
```
### FactSet
```json
{
"type": "FactSet",
"facts": [
{
"title": "Label",
"value": "Value"
}
]
}
```
### Image
```json
{
"type": "Image",
"url": "https://example.com/image.png",
"size": "medium", // auto, stretch, small, medium, large
"style": "default" // default, person
}
```
### Container
```json
{
"type": "Container",
"$data": "${items}", // Iterate over array
"items": [
{
"type": "TextBlock",
"text": "${name}"
}
]
}
```
### ColumnSet
```json
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "auto",
"items": [ ... ]
},
{
"type": "Column",
"width": "stretch",
"items": [ ... ]
}
]
}
```
### Actions
```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
### 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
### Test Across Hubs
Validate cards in:
- Teams (desktop and mobile)
- Word
- PowerPoint
- Various viewport widths (contract/expand 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}"
}
]
}
}
}
}
]
}
```
## Workflow
Ask the user:
1. What type of data does the API return?
2. Are all items the same type (static) or different types (dynamic)?
3. What fields should appear in the card?
4. Should there be actions (e.g., "View Details")?
5. 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
## Resources
- [Adaptive Card Designer](https://adaptivecards.microsoft.com/designer) - Visual design tool
- [Adaptive Card Schema](https://adaptivecards.io/schemas/adaptive-card.json) - Full schema reference
- [Template Language](https://learn.microsoft.com/en-us/adaptive-cards/templating/language) - Binding syntax guide
- [JSONPath](https://www.rfc-editor.org/rfc/rfc9535) - Path query syntax
## 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
}
]
}
]
}
]
}
```
### Status Indicators
```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)}"
}
```
Weekly Installs
7.3K
Repository
GitHub Stars
26.7K
First Seen
Feb 25, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex7.2K
gemini-cli7.2K
opencode7.2K
cursor7.2K
github-copilot7.2K
amp7.2K
97,600 周安装