mcp-create-adaptive-cards

mcp-create-adaptive-cards

热门

从 mcp-create-adaptive-cards.prompt.md 转换而来的 Skill

3.7万Star
4569Fork
更新于 2026/7/14
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