| name | propose-cards |
| description | Surface typed UI cards in the console chat via the propose_card tool. Use when the user needs to take a configuration action (set up a channel, connect an integration, enable a trigger, configure a webhook, install a skill) or when you want to post a standout notice. Covers when to use a card vs. prose, which kind to pick, and how the tool blocks until the user decides. |
| requiresTools | ["propose_card"] |
Propose Cards
The console chat renders typed cards inline when you call propose_card. A card is a small UI that either shows an informational update or presents a CTA button that, when the user approves, commits a specific change against the console API.
You don't render cards in prose — you call propose_card with a discriminated kind, and the UI takes care of the rest. Interactive kinds block your turn until the user clicks Approve or Dismiss, so the tool's return value is the outcome (applied / dismissed / failed) you should plan the next turn around.
Choosing a Kind
| Situation | kind |
|---|
| User wants to set up Slack / Telegram / Discord / AgentMail / AgentPhone / API channel | channel_setup |
| User wants to connect a Composio toolkit (Notion, Linear, GitHub, etc.) | integration_connect |
| User wants to create a Composio trigger for an already-connected account | trigger_create |
| User needs to point Telegram or Composio at the edge's public webhook URL | webhook_configure |
User wants to install a skill — you have the skill body and a skills/... storage key | skill_enable |
| You need a standout status line (success / warning / note) that isn't a decision | info |
Use prose, not a card, when:
- You're answering a question, explaining a concept, or summarizing.
- The user already applied the change and you're confirming the outcome.
- The needed action is outside the console's jurisdiction (e.g. rotating credentials — tell them to go to
/console).
Blocking Semantics
Interactive kinds (everything except info) block your turn until the user clicks Approve or Dismiss. You can only have one card pending at a time, and the user cannot type new prompts while it's pending. Plan accordingly:
- Don't stack cards. If two decisions are needed, emit the first, wait for the result, then emit the second — not both at once.
- Honor the outcome. If the return is
status: "dismissed", do not re-propose the same card unprompted. Ask the user what they want to change.
- If it failed, surface the detail. The
detail field explains why the commit failed. Show it to the user so they can fix credentials, config, etc.
kind: "info" does not block. It resolves immediately; use it to post status updates that stand out from prose.
Picking Fields Per Kind
channel_setup — pass channel and any non-secret values under prefill (display names, webhook paths). Never put tokens or secrets in prefill. The user types secrets into the real setup form, which opens when they click the CTA.
integration_connect — pass toolkit (Composio slug, e.g. slack, notion, github). The UI resolves the display name and logo from the toolkits catalog.
trigger_create — pass toolkit (Composio slug) and triggerSlug. The UI resolves the connected account from the toolkit. Include triggerConfig when the trigger type has a schema; pass {} if not.
webhook_configure — pass target (telegram or composio). The card fetches the expected and current URLs from the edge and shows the diff.
skill_enable — skillKey must start with skills/ and include a filename. The user sees the full skill body as a code diff before approving.
info — only title, body, and optional severity (info | warn | success). No CTA.
Out of Jurisdiction
Do not try to use cards for these:
- Credentials — tokens, API keys, signing secrets. Human-only; point the user at
/console.
- Workflows and crons — those land via file writes in
.agent/workflows/ and .agent/crons/, not cards.
- Self-config changes — use
get_setup + patch_setup (see the self-config skill). Cards are for operator-facing actions, not for agent self-modification.
Examples
User asks to connect Slack:
{
"kind": "channel_setup",
"channel": "slack",
"title": "Connect Slack",
"summary": "Set up Slack so the agent can receive messages from your workspace."
}
On applied, confirm and offer the next step. On dismissed, ask what they want to change rather than retrying.
User asks to enable Notion:
{
"kind": "integration_connect",
"toolkit": "notion",
"title": "Connect Notion",
"summary": "Opens Notion's OAuth flow in a new tab."
}
When the user approves, the card opens Composio's OAuth URL in a new tab. The tool returns applied noting the tab was opened; the actual OAuth completion is out-of-band. Once the user says they finished, follow up with kind: "trigger_create" for any needed triggers.
Standout confirmation that isn't a decision:
{
"kind": "info",
"severity": "success",
"title": "Slack connected",
"body": "Incoming messages will now route through the main agent."
}
Return Shape
Every propose_card call returns:
{ "cardId": "channel-abc-123", "status": "applied", "detail": "Slack bot verified" }
status: "applied" — the user clicked the CTA and the commit succeeded.
status: "dismissed" — the user dismissed the card, or closed the setup dialog without verifying.
status: "failed" — the CTA was clicked but the commit threw; detail explains why.
Read this and plan your next step from it; do not ask the user to repeat what the outcome was.