Install
$ agentstack add skill-lageev-feishu-msg-card-skill-feishu-msg-card-skill ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Feishu Card JSON 2.0
Create production-oriented Feishu Card JSON 2.0. Select the delivery surface first, build only supported interactions, keep the visual hierarchy compact, and validate the exact final payload.
Select the delivery surface
Classify the request before choosing components:
| Surface | Output shape | Supported interaction | |---|---|---| | Custom bot webhook | {"msg_type":"interactive","card":{...}} | Static display and URL navigation only | | Application bot / OpenAPI | Raw card object, or the enclosing API request requested by the user | URL navigation, callbacks, forms, updates | | Callback response | {"toast":...,"card":{"type":"raw","data":{...}}} | Immediate toast and optional card replacement | | CardKit/template | Template ID, version, and variables | Follow the specific CardKit API; do not pretend raw JSON supports template variables |
Never add callback behavior, form submission, input collection, or server-side interaction to a custom-bot webhook card. Custom bots are one-way senders. Use an application bot when the user needs callbacks or data collection.
Read [delivery-and-interaction.md](references/delivery-and-interaction.md) for webhook signing, callbacks, updates, streaming, and payload envelopes.
Gather the minimum brief
Infer safe defaults when possible. Identify:
- one primary purpose and audience;
- delivery surface;
- title, summary, content sections, severity/status;
- primary and secondary actions;
- dynamic fields, dates, people IDs, image/file keys, and URLs;
- localization and minimum client-version needs;
- whether the user wants raw card JSON, a webhook envelope, callback response, or code that returns the payload.
Use placeholder strings such as ${TITLE} only when real values are unavailable. Never invent webhook URLs, secrets, user IDs, image keys, file keys, message IDs, or business metrics.
Build the card
- Start with
"schema": "2.0". - Put global behavior in
config, optional whole-card navigation incard_link, title information inheader, and ordered components inbody.elements. - Keep
config.update_multiabsent ortrue; JSON 2.0 does not supportfalse. - Choose
config.width_modedeliberately:
- omit for the default 600 px maximum;
compactfor short notices or narrow forms;fillfor tables, charts, and dashboards.
- Use body padding around
12px, vertical spacing around8pxor12px, and explicit responsive behavior forcolumn_set. - Assign stable, globally unique
element_idvalues only when later component operations need them. Match^[A-Za-z][A-Za-z0-9_]{0,19}$. - Keep strict JSON: no comments, trailing commas, JavaScript expressions, or unescaped newlines.
Read [core-schema-and-style.md](references/core-schema-and-style.md) for root fields, layout, colors, Markdown, localization, and global limits.
Choose components
Prefer the smallest component set that expresses the information:
- Use
markdownfor structured narrative, links, mentions, lists, code, and compact key-value content. - Use
divfor plain text that needs precise text/icon styling. - Use
column_setfor responsive key metrics, paired labels/values, and button rows. - Use
tablefor typed, multi-row data; use Markdown tables only for small simple tables. - Use
chartonly when the data relationship benefits from visualization. - Use
imgorimg_combinationwhen a realimg_keyexists. - Use
personorperson_listfor identity display; use Markdown `` only when an actual mention notification is intended. - Use
collapsible_panelfor secondary detail. - Use
buttonwithopen_urlfor webhook actions. - Use
form, inputs, selectors, pickers,checker, or callback behaviors only for application-delivered cards. - Use
audioonly with an OPUSfile_key, client fallback, andconfig.enable_forward: false.
Read [component-catalog.md](references/component-catalog.md) before using an unfamiliar, nested, interactive, chart, table, image, or audio component.
Compose for visual quality
Apply these defaults unless the content suggests otherwise:
- Lead with outcome or status, then details, then actions.
- Use one header theme that reflects semantics: red/carmine for failure, orange/yellow for warning, green for success, blue/indigo for neutral information.
- Keep one primary filled button. Render other actions as default buttons or links.
- Limit header suffix tags to three.
- Use separators sparingly; whitespace and grouping should carry most of the hierarchy.
- Prefer 2–4 metric columns and configure
flex_modeso narrow clients do not crush content. - Truncate or summarize long material, then link to the full source.
- Define both light and dark values for custom colors.
- Preserve meaningful fallbacks for version-gated components.
Read [design-recipes.md](references/design-recipes.md) for notification, alert, report, form, approval, and AI-streaming patterns. Reuse the closest file in assets/templates/ rather than starting from an empty object.
Add interaction correctly
For URL navigation, use:
"behaviors": [
{
"type": "open_url",
"default_url": "https://example.com",
"pc_url": "https://example.com",
"ios_url": "https://example.com",
"android_url": "https://example.com"
}
]
Use lark://msgcard/unsupported_action to disable a specific client. Ensure at least one usable URL exists.
For application callbacks, use:
"behaviors": [
{
"type": "callback",
"value": {
"action": "acknowledge",
"record_id": "${RECORD_ID}"
}
}
]
Keep callback value small and non-sensitive. Treat it as untrusted input on the server. For forms, give every interactive child a globally unique name and include a button with "form_action_type": "submit".
Localize
Use component-level i18n_* fields in JSON 2.0, such as i18n_content, i18n_img_key, i18n_file_key, and i18n_text_tag_list. JSON 2.0 does not support root-level i18n_elements.
If config.locales is present, include only supported locale identifiers and ensure fallback content still exists. Use local_datetime in Markdown for viewer-local time when appropriate.
Validate and deliver
Run:
python3 scripts/validate_card.py path/to/payload.json
Use --mode custom-bot, --mode raw, or --mode callback-response when automatic envelope detection is ambiguous.
To wrap a raw card for a custom-bot webhook without sending it:
python3 scripts/wrap_webhook.py card.json
Add --secret-env FEISHU_BOT_SECRET only when the secret is already available through that environment variable. Never print or embed the secret or webhook URL.
Before returning output, verify:
- the delivery envelope matches the requested surface;
- the payload parses as strict JSON;
schemais exactly"2.0";- all text, URLs, IDs, and metrics come from the user or are explicit placeholders;
- no custom-bot card contains callback-only controls;
element_idand formnamevalues are unique;- component count is at most 200 and container depth is at most five;
- the card remains legible on narrow screens and in dark mode;
- no secret appears in the card, example command, logs, or prose.
Return the complete payload first. Add only the integration note or caveat needed for the chosen surface. Do not send the webhook or mutate a live card unless the user explicitly asks.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: lageev
- Source: lageev/feishumsgcard_skill
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.