openclaw-adaptive-cards

March 23, 2026 · View on GitHub

License: MIT TypeScript Adaptive Cards CI Tests OpenClaw Plugin npm GitHub stars

Adaptive Cards plugin for OpenClaw — gives the AI an adaptive_card tool to respond with native Adaptive Cards (v1.6) instead of plain text. Powered by adaptive-cards-mcp (v2.3.0) — 9 tools, 924 tests, 21 layout patterns, schema validation, host compatibility, and accessibility scoring.

Blog: Building GenUI for AI Agents: Native Adaptive Cards Without Changing the Gateway

Cards render natively on:

PlatformRendererTechnology
iOSAdaptiveCards-MobileSwiftUI
AndroidAdaptiveCards-MobileJetpack Compose
TeamsBot Framework attachmentNative AC rendering
Webadaptivecards.ioJavaScript SDK
Telegram / Slack / IRCAuto-generated fallback textPlain text extraction

Topics

Quick Start | Ecosystem | Architecture | Usage | Supported Elements | Exports | What's New | Development | Related

Documentation: AGENTS.md (AI agent guidance) | CONTRIBUTING.md (developer guide) | BLOG.md (narrative deep-dive) | CHANGELOG.md (version history)

Quick Start

openclaw plugins install @vikrantsingh01/openclaw-adaptive-cards

That's it — no configuration needed. The plugin registers the adaptive_card tool automatically.

What happens next

  1. The agent's system prompt is augmented with 21 card layout patterns and channel-aware guidance
  2. When the agent decides structured content benefits from visual layout, it calls the adaptive_card tool
  3. The card is validated against the official v1.6 JSON Schema, scored for accessibility, and adapted for the target host
  4. Clients regex-extract the card JSON from HTML comment markers and render natively

Ecosystem

This plugin is part of a larger Adaptive Cards AI toolchain:

PackagePurposeRepo
openclaw-adaptive-cardsOpenClaw plugin — card delivery, transport, fallback, action routingThis repo
adaptive-cards-mcpMCP server + npm library — 7 AI tools for card generation, validation, optimizationadaptive-cards-mcp
AdaptiveCards-MobileNative renderers — SwiftUI (iOS) + Jetpack Compose (Android)AdaptiveCards-Mobile

The plugin consumes adaptive-cards-mcp as its core engine for validation, host adaptation, accessibility checking, and layout patterns. The plugin itself is a thin delivery layer focused on OpenClaw tool registration, marker-based transport, fallback text generation, prompt injection, and action routing.

openclaw-adaptive-cards (this plugin — thin delivery layer)
  └── adaptive-cards-mcp (shared core — validation, host compat, accessibility, 21 patterns)
        ├── ajv (official v1.6 JSON Schema validation)
        └── zod (runtime type validation)

Architecture

High-Level Flow

Agent LLM
  │  decides structured content is appropriate

adaptive_card tool executes (this plugin)
  │  assembles AdaptiveCard v1.6 JSON
  │  validates against official v1.6 schema (via adaptive-cards-mcp)
  │  checks accessibility (WCAG score 0-100)
  │  adapts for host compatibility (Teams, Outlook, Webex, etc.)
  │  generates fallback text
  │  embeds card + fallback in HTML comment markers

Gateway passes text through unmodified
  │  no schema changes, no hooks, no middleware
  │  cards piggyback on existing ReplyPayload.text

Client receives tool result text

  ├─► iOS / Android ── regex-extract JSON ── AdaptiveCards-Mobile SDK ── native SwiftUI / Compose view
  ├─► MS Teams ─────── regex-extract JSON ── Bot Framework Attachment ──────── native Teams card
  ├─► Web UI ──────── regex-extract JSON ── adaptivecards.io JS SDK ────────── rendered in browser
  └─► Telegram / Slack / IRC ── markers invisible (HTML comments) ──────────── shows fallback text only

Marker Wire Format

The tool result text contains three sections:

Project Status: Deploy API (done), Write tests (in progress).     ← FALLBACK TEXT (always visible)

<!--adaptive-card-->{"type":"AdaptiveCard","version":"1.6",...}<!--/adaptive-card-->     ← CARD JSON
<!--adaptive-card-data-->{"projectId":"abc123"}<!--/adaptive-card-data-->                ← TEMPLATE DATA (optional)
SectionPurpose
Fallback textAuto-generated or agent-provided. Visible on all channels. Non-card channels only see this.
Card JSONFull AdaptiveCard v1.6 envelope. Clients regex between <!--adaptive-card--> markers.
Template dataOptional. Enables client-side ${expression} expansion. Separated from card body for clean data binding.

Plugin Layers

src/index.ts          ← Plugin registration: tool, hook, command, gateway method, cardId + preview
src/mcp-bridge.ts     ← Bridge: 25+ functions from adaptive-cards-mcp (validation, patterns, persistence, tools)
src/prompt.ts         ← System prompt injection with 21 patterns, host matrix, accessibility guidance
src/fallback.ts       ← Recursive card body → plain text extraction (30+ v1.6 element types)
src/actions.ts        ← Action.Execute/Submit routing from client taps
src/constants.ts      ← Marker tags, AC version, defaults

What the MCP Core Provides

The plugin delegates to adaptive-cards-mcp for:

CapabilityDescription
Schema validationAJV-based validation against the official 3,297-line v1.6 JSON Schema
Host compatibilityChecks cards against 7 hosts (Generic, Teams, Outlook, Web Chat, Windows, Viva, Webex)
Card adaptationReplaces unsupported elements (Table→ColumnSet, Carousel→Container, Charts→FactSet, Action.Execute→Submit)
AccessibilityWCAG scoring (0-100) checking altText, labels, wrap, speak property
Layout patterns21 production-ready card templates (approval, incident, calendar, PR review, etc.)
Card analysisElement counts, nesting depth, templating detection, duplicate ID detection
Card persistenceSession-scoped storage (30-min TTL) with cardId for subsequent optimize/validate calls
Preview generationHTML preview file for Adaptive Cards Designer
9 tool handlersgenerateCard, validateCardFull, dataToCard, optimizeCard, templateCard, transformCard, suggestLayout, generateAndValidate, cardWorkflow

Design Decisions

DecisionWhy
HTML comment markersTravel inside existing tool result text. No gateway schema changes. Gateway sanitization only truncates — never strips comments.
Stateless pluginNo config, no database, no state. Zero operational overhead.
MCP as shared coreOne source of truth for validation, host compat, and patterns. Plugin stays thin (~1,000 lines).
Client-side parsingEach client independently decides whether to parse markers. Clients adopt at their own pace.
AC v1.6Latest version with Carousel, CodeBlock, Charts, and full Teams support. Auto-adapted for older hosts.
Action.Execute preferredUniversal Actions enable server-side card refresh, role-based views, and Copilot compatibility.
Auto-fallback generationRecursive extractor handles all v1.6 elements so agent focuses on card authoring.

Usage

AI Tool

The agent calls the adaptive_card tool automatically when structured content benefits from visual layout:

{
  "body": [
    { "type": "TextBlock", "text": "Expense Approval", "weight": "Bolder", "size": "Medium" },
    { "type": "FactSet", "facts": [
      { "title": "Amount", "value": "\$2,450.00" },
      { "title": "Category", "value": "Travel" }
    ]}
  ],
  "actions": [
    { "type": "Action.Execute", "title": "Approve", "verb": "expense_approve", "data": { "decision": "approved" } },
    { "type": "Action.Execute", "title": "Reject", "verb": "expense_reject", "data": { "decision": "rejected" } }
  ]
}

Tool Parameters

ParameterTypeRequiredDescription
bodyunknown[]YesArray of AC v1.6 body elements (TextBlock, FactSet, Table, Carousel, Chart., Input., etc.)
actionsunknown[]NoArray of card actions (Action.Execute, Action.Submit, Action.OpenUrl, Action.ShowCard, Action.ToggleVisibility)
fallback_textstringNoPlain text for non-card channels. Auto-generated if omitted.
template_dataunknownNoData context for client-side ${expression} expansion.

Commands

/acard test                   — Send a test card to verify rendering
/acard validate {...}         — Validate card JSON + accessibility score
/acard compat teams {...}     — Check card compatibility with a host
/acard {...}                  — Send custom card JSON

Agent Intelligence

The plugin injects channel-aware guidance into the agent's system prompt via a before_prompt_build hook:

ChannelGuidance
Native (iOS, Android, Web, Teams)"Prefer cards over plain text when content is structured"
Translated (Telegram, Slack, Discord)"Keep cards simple: TextBlock, FactSet, simple actions"
Fallback (Signal, IRC, SMS)"Only fallback text shown — prefer plain text"

The prompt includes all 21 layout patterns from adaptive-cards-mcp so the agent knows what card designs are available.

Supported Elements

Body Elements (v1.6)

ElementFallback TextNotes
TextBlock.textCore text element
RichTextBlock.inlines[] concatenationTextRun and string inlines
FactSettitle: value per factKey-value pairs
ColumnSetRecurse into .columns[].itemsFlattened in fallback
ContainerRecurse into .itemsFlattened in fallback
Image[Image: altText]Only if altText provided
ImageSet[Image: altText] per imageGallery
TableRecurse cells, join with |Row-by-row extraction
CodeBlockFenced code blockcodeSnippet as ``` block
CarouselRecurse .pages[].itemsPage-by-page extraction
Accordion.title + recurse card bodySection titles + content
TabSet[tabTitle] + recurse card bodyTab labels + content
Chart.*title + label: value per data pointBar, Line, Pie, Donut
Rating★★★☆☆ starsVisual star rating
ProgressBarLabel or percentageProgress indicator
CompoundButton.title + .descriptionRich button
Badge[text]Status badge
Input.*.label or [placeholder]All input types

Actions

ActionDescription
Action.ExecuteServer-side processing with automatic card refresh (preferred)
Action.SubmitClient-side data submission (legacy, widely supported)
Action.OpenUrlOpens URL in browser
Action.ShowCardReveals nested card inline
Action.ToggleVisibilityShows/hides elements by targetElementId

Exports

The plugin re-exports key functions from adaptive-cards-mcp for advanced consumers:

import {
  // Plugin-specific
  generateFallbackText,
  buildCardPromptGuidance,
  formatActionAsMessage,
  CARD_OPEN_TAG, CARD_CLOSE_TAG,

  // MCP-powered core (via bridge)
  validateCard,
  checkHostCompatibility,
  adaptCardForHost,
  checkCardAccessibility,
  analyzeCard,
  findDuplicateIds,
  getValidElementTypes,
  getValidActionTypes,

  // Patterns & hosts
  getAllPatterns,
  scorePatterns,
  findPatternByName,
  findPatternByIntent,
  getAllHostSupport,
  getHostSupport,

  // Card persistence & preview
  storeCard,
  getCard,
  listCards,
  writePreviewFile,

  // High-level MCP tool handlers
  generateCard,
  validateCardFull,
  optimizeCard,
  dataToCard,
  suggestLayout,
} from "@vikrantsingh01/openclaw-adaptive-cards";

Configuration

No configuration needed — the plugin is stateless.

What's New in v4.1.0

  • Card persistencestoreCard() / getCard() / listCards() with session-scoped 30-min TTL
  • Preview generationwritePreviewFile() generates HTML preview for Adaptive Cards Designer
  • Duplicate ID detectionfindDuplicateIds() catches broken Action.ToggleVisibility targets
  • Pattern scoringscorePatterns() ranks all 21 patterns against natural language descriptions
  • High-level tool handlersgenerateCard(), validateCardFull(), optimizeCard(), dataToCard(), suggestLayout()
  • Full sync with adaptive-cards-mcp v2.3.0 (9 tools, 924 tests, 36 examples)

See CHANGELOG.md for full version history.

Development

# Clone and install
git clone https://github.com/VikrantSingh01/openclaw-adaptive-cards.git
cd openclaw-adaptive-cards && npm install

# Run tests
npm test

# Type check
npm run typecheck

# Full check (typecheck + test)
npm run check

See CONTRIBUTING.md for architecture details, testing guidelines, and PR checklist.

Origin

This plugin was extracted from openclaw/openclaw#33486 (Extensions: add adaptive-cards extension for native GenUI) at the maintainers' request to be published as a standalone third-party plugin.

Community plugins listing PR: openclaw/openclaw#41735

License

MIT