SKILL.md
只读
名称
mcp-create-adaptive-cards
描述
从 mcp-create-adaptive-cards.prompt.md 转换而来的 Skill
---
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]
---
# 为 MCP 插件创建 Adaptive Card 卡片
为基于 MCP 的 API 插件添加 Adaptive Card(自适应卡片)响应模板,优化数据在 Microsoft 365 Copilot 中的视觉呈现效果。
## Adaptive Card 类型
### 静态响应模板
适用于 API 返回数据类型固定且格式不常变化的场景。
在 `ai-plugin.json` 的 `response_semantics.static_template` 中定义:
```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
}
]
}
]
}
}
}
}
]
}
```
### 动态响应模板
适用于 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"
}
}
}
```
### 静态与动态结合模板
当数据项没有 `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
指定 API 响应中数据所在位置的 JSONPath 查询:
```json
"data_path": "$" // 响应根节点
"data_path": "$.results" // 位于 results 属性中
"data_path": "$.data.items"// 嵌套路径
```
### properties
映射响应字段用于 Copilot 引用(citation):
```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": "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}", // 遍历数组
"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}"
}
```
## 响应式设计最佳实践
### 单列布局
- 窄视口优先使用单列布局
- 尽量避免多列布局
- 确保卡片在最小视口宽度下仍可正常显示
### 弹性宽度
- 不要为元素指定固定宽度
- 宽度属性建议使用 "auto" 或 "stretch"
- 允许元素随视口自动缩放
- 仅图标/头像允许指定固定宽度
### 文本与图片
- 避免将文本和图片放在同行
- 特例:小型图标或头像
- 文本内容必须设置 `"wrap": true`
- 在各种视口宽度下进行测试
### 多端与多应用测试
在以下环境中验证卡片效果:
- Teams(桌面端与移动端)
- Word
- PowerPoint
- 各种视口宽度(收缩/拉伸界面)
## 完整示例
**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}"
}
]
}
}
}
}
]
}
```
## 工作流
询问用户以下问题:
1. API 返回什么类型的数据?
2. 所有数据项类型一致(静态),还是包含多种不同类型(动态)?
3. 卡片中需要展示哪些字段?
4. 是否需要添加交互操作(例如“查看详情”)?
5. 是否存在需要不同模板的多种状态或分类?
然后生成:
- 对应的 response_semantics 配置
- 静态模板、动态模板或两者组合
- 带有条件渲染的正确数据绑定
- 响应式单列布局
- 用于验证的测试用例
## 资源
- [Adaptive Card 设计器](https://adaptivecards.microsoft.com/designer) - 可视化设计工具
- [Adaptive Card Schema](https://adaptivecards.io/schemas/adaptive-card.json) - 完整 Schema 参考
- [模板语言](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",
"w






