# Slack Block Kit Builder — Helper for AI-Generated Messages > Python helper that converts LLM markdown to valid Slack Block Kit JSON. Headers, sections, dividers, buttons. Auto-splits past the 3001-char limit. ## Install Save the content below to `.claude/skills/` or append to your `CLAUDE.md`: ## Quick Use 1. `pip install slack-bolt` 2. Drop `md_to_blocks()` into your bot module 3. Pipe LLM markdown straight into `chat.postMessage(blocks=md_to_blocks(text))` --- ## Intro This is a small Python helper that takes plain markdown from an LLM and produces valid Slack Block Kit JSON — header, section, divider, fields, buttons, image — automatically chunking text past Slack's 3001-char per-block limit. Slack messages without Block Kit look terrible; messages with Block Kit require fragile hand-built JSON. This bridges them. Best for: AI bots posting summaries, alerts, daily digests, and reports to Slack channels. Works with: Slack Bolt SDK (Python/JS), Slack chat.postMessage REST. Setup time: 5 minutes. --- ### Helper code ```python from typing import Iterable def md_to_blocks(text: str) -> list[dict]: blocks = [] for raw in text.split("\n\n"): chunk = raw.strip() if not chunk: continue if chunk.startswith("# "): blocks.append({"type": "header", "text": {"type": "plain_text", "text": chunk[2:].strip()[:150]}}) elif chunk == "---": blocks.append({"type": "divider"}) else: for piece in _split_3000(chunk): blocks.append({"type": "section", "text": {"type": "mrkdwn", "text": piece}}) return blocks def _split_3000(s: str, limit: int = 2900) -> Iterable[str]: while len(s) > limit: cut = s.rfind("\n", 0, limit) if cut == -1: cut = s.rfind(" ", 0, limit) or limit yield s[:cut] s = s[cut:].lstrip() if s: yield s def add_actions(blocks: list[dict], buttons: list[tuple[str, str, str]]) -> list[dict]: if not buttons: return blocks blocks.append({ "type": "actions", "elements": [ {"type": "button", "text": {"type": "plain_text", "text": label}, "action_id": action_id, "url": url} for label, action_id, url in buttons ], }) return blocks ``` ### Usage with Bolt ```python from slack_bolt import App from slack_bolt.adapter.socket_mode import SocketModeHandler import os app = App(token=os.environ["SLACK_BOT_TOKEN"]) llm_text = '''# Daily AI Digest *5 new MCP servers* shipped on TokRepo today. - Stripe MCP — payments - Datadog MCP — observability - Linear MCP — issue tracker --- Top reaction: stripe-mcp got 47 stars in 6 hours.''' blocks = md_to_blocks(llm_text) blocks = add_actions(blocks, [("Open TokRepo", "open_tokrepo", "https://tokrepo.com/explore")]) app.client.chat_postMessage(channel="#ai-digest", blocks=blocks, text="Daily AI Digest") ``` ### Slack Block Kit limits (cheat sheet) | Item | Limit | |---|---| | Blocks per message | 50 | | Section text | 3,000 chars | | Header text | 150 chars | | Action elements per actions block | 25 | | Total message size | 40 KB | --- ### FAQ **Q: Why mrkdwn instead of markdown?** A: Slack's mrkdwn is a subset — `*bold*` (one asterisk, not two), `_italic_`, `` for links, no headings, no tables. The helper outputs mrkdwn-flavored text. Hand-converting LLM markdown is the part this saves. **Q: Does the LLM need to know about Block Kit?** A: No — that's the point. Prompt it for normal markdown. The helper handles the conversion. Hand the LLM Block Kit JSON in the prompt and outputs become brittle and verbose. **Q: What if the message is over 50 blocks?** A: Slack rejects with `invalid_blocks` error. Send as a thread — first message gets the first 50 blocks, then `chat.postMessage` with `thread_ts` for follow-ups. Or split into multiple top-level messages with day/section headers. --- ## Source & Thanks > Pattern compiled from [Slack Block Kit](https://api.slack.com/block-kit) + [Bolt SDK](https://github.com/slackapi/bolt-python). > > Bolt MIT-licensed, helper Apache-2.0. --- ## 快速使用 1. `pip install slack-bolt` 2. 把 `md_to_blocks()` 丢进 bot 模块 3. LLM markdown 直灌 `chat.postMessage(blocks=md_to_blocks(text))` --- ## 简介 这是一个小 Python helper,把 LLM 输出的纯 markdown 转成合法的 Slack Block Kit JSON —— header / section / divider / fields / buttons / image —— 自动把超过 Slack 3001 字符/块限制的文本切块。Slack 消息不用 Block Kit 难看,用 Block Kit 又要写脆弱的手工 JSON。这个 helper 把两者打通。适合发摘要、告警、每日 digest、报告到 Slack 频道的 AI bot。兼容 Slack Bolt SDK(Python/JS)、Slack chat.postMessage REST。装机时间 5 分钟。 --- ### Helper 代码 ```python from typing import Iterable def md_to_blocks(text: str) -> list[dict]: blocks = [] for raw in text.split("\n\n"): chunk = raw.strip() if not chunk: continue if chunk.startswith("# "): blocks.append({"type": "header", "text": {"type": "plain_text", "text": chunk[2:].strip()[:150]}}) elif chunk == "---": blocks.append({"type": "divider"}) else: for piece in _split_3000(chunk): blocks.append({"type": "section", "text": {"type": "mrkdwn", "text": piece}}) return blocks def _split_3000(s: str, limit: int = 2900) -> Iterable[str]: while len(s) > limit: cut = s.rfind("\n", 0, limit) if cut == -1: cut = s.rfind(" ", 0, limit) or limit yield s[:cut] s = s[cut:].lstrip() if s: yield s def add_actions(blocks: list[dict], buttons: list[tuple[str, str, str]]) -> list[dict]: if not buttons: return blocks blocks.append({ "type": "actions", "elements": [ {"type": "button", "text": {"type": "plain_text", "text": label}, "action_id": action_id, "url": url} for label, action_id, url in buttons ], }) return blocks ``` ### 配 Bolt 用 ```python from slack_bolt import App from slack_bolt.adapter.socket_mode import SocketModeHandler import os app = App(token=os.environ["SLACK_BOT_TOKEN"]) llm_text = '''# Daily AI Digest 今天 TokRepo 上新了 *5 个 MCP server*。 - Stripe MCP —— 支付 - Datadog MCP —— 可观测 - Linear MCP —— issue tracker --- 最热反应:stripe-mcp 6 小时拿了 47 星。''' blocks = md_to_blocks(llm_text) blocks = add_actions(blocks, [("打开 TokRepo", "open_tokrepo", "https://tokrepo.com/explore")]) app.client.chat_postMessage(channel="#ai-digest", blocks=blocks, text="Daily AI Digest") ``` ### Slack Block Kit 限制(cheat sheet) | 项目 | 限制 | |---|---| | 每条消息块数 | 50 | | Section 文本 | 3,000 字符 | | Header 文本 | 150 字符 | | 每 actions 块元素数 | 25 | | 总消息大小 | 40 KB | --- ### FAQ **Q: 为啥用 mrkdwn 不用 markdown?** A: Slack 的 mrkdwn 是 markdown 的子集 —— `*粗体*`(一个星号不是两个)、`_斜体_`、`` 链接,没标题,没表格。Helper 输出 mrkdwn 风味文本。手动转换 LLM markdown 就是这个 helper 省的力气。 **Q: LLM 需要知道 Block Kit 吗?** A: 不需要 —— 这就是重点。让它输出普通 markdown,helper 负责转。把 Block Kit JSON 写进 prompt 反而让输出又脆又啰嗦。 **Q: 超过 50 块怎么办?** A: Slack 报 `invalid_blocks` 错。当成 thread 发 —— 第一条用前 50 块,再用 `chat.postMessage` 带 `thread_ts` 续发。或者拆成多条顶层消息,按天/分节加标题。 --- ## 来源与感谢 > Pattern compiled from [Slack Block Kit](https://api.slack.com/block-kit) + [Bolt SDK](https://github.com/slackapi/bolt-python). > > Bolt MIT-licensed, helper Apache-2.0. --- Source: https://tokrepo.com/en/workflows/slack-block-kit-builder-helper-for-ai-generated-messages Author: Slack