# Result cards

URL: https://docs.assistantcore.com/en/tools-mcp/result-cards

Let an MCP server or local connector tool return a card with a title, a few details, an image and up to two buttons, shown right in the conversation.



A tool on an [MCP server](/en/tools-mcp/mcp-server) or a [local connector](/en/tools-mcp/local-connector) can attach one card to its result. The assistant still reads the result's text to answer, and the user sees the card under the tool step: an order with its status, total and a tracking button, for example.

## What the user sees [#what-the-user-sees]

| Where              | Shown as                                                                          |
| ------------------ | --------------------------------------------------------------------------------- |
| Web App            | The full card: icon, title, subtitle, text, detail rows, image and buttons        |
| Web widget         | The full card, as in the Web App                                                  |
| Messaging channels | The card's fallback text, with link buttons as `Label: URL`, sent after the reply |
| Voice              | The assistant speaks the result; the card still appears in the chat history       |
| Developer API      | The card is passed as-is in the `tool_end` event for your app to render           |

A **link** button opens the page in a new tab. A **message** button sends a prepared sentence as the user's next message, such as "Cancel order 1042", for the assistant to handle. When a card is shown, the assistant is told not to repeat its contents in the reply.

## Declare a card [#declare-a-card]

Add `_meta.assistant_platform.card` to the `tools/call` result:

```json
{
  "content": [{ "type": "text", "text": "Order 1042 has been handed to the carrier and should arrive Friday." }],
  "_meta": {
    "assistant_platform": {
      "card": {
        "v": 1,
        "title": "Order #1042",
        "subtitle": "Shipped",
        "text": "Arrives Friday.",
        "icon_url": "https://shop.example.com/icon.png",
        "image_url": "https://shop.example.com/o/1042.jpg",
        "fields": [
          { "label": "Total", "value": "1,250,000 ₫" },
          { "label": "Carrier", "value": "GHN" }
        ],
        "actions": [
          { "kind": "url", "label": "Track", "url": "https://shop.example.com/o/1042" },
          { "kind": "message", "label": "Cancel", "text": "Cancel order 1042" }
        ],
        "fallback_text": "Order #1042 shipped, arrives Friday. Total 1,250,000 ₫."
      }
    }
  }
}
```

| Field                   | Required | Limit                                                                                                             |
| ----------------------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `v`                     | No       | Card version, currently `1`                                                                                       |
| `title`                 | Yes      | 80 characters                                                                                                     |
| `subtitle`              | No       | 160 characters                                                                                                    |
| `text`                  | No       | 300 characters, line breaks kept                                                                                  |
| `icon_url`, `image_url` | No       | `https` URLs                                                                                                      |
| `fields`                | No       | Up to 4 rows; `label` 40 characters, `value` 120 characters                                                       |
| `actions`               | No       | Up to 2 buttons; `label` 30 characters. `kind` is `url` with a `url`, or `message` with a `text` (200 characters) |
| `fallback_text`         | Yes      | 500 characters, line breaks kept. Shown where the card cannot be drawn                                            |

Every field is plain text, with no Markdown or HTML. The first button is the primary one. Unknown fields are ignored, so an older card still shows once newer versions exist.

## When a card is invalid [#when-a-card-is-invalid]

A card over a limit, without `fallback_text` or with a URL that is not `https` is dropped entirely, while the result's text still reaches the assistant, so the reply is unaffected. A card needs no re-approval when it changes: it is part of the result, not of the tool definition.

<Callout type="info">
  Keep one card to one thing: an order, an appointment, a result. For several items, let the assistant answer in text and return a card only for the item the user asked about.
</Callout>
