Cette page est affichée en anglais. Une traduction française est en cours.
SkillsMay 8, 2026·4 min de lecture

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.

Slack
Slack · Community
Prêt pour agents

Staging sûr pour cet actif

Cet actif est d'abord staged. Le prompt copié demande à l'agent d'inspecter les fichiers staged avant d'activer scripts, config MCP ou config globale.

Stage only · 29/100Policy : staging
Surface agent
Tout agent MCP/CLI
Type
Skill
Installation
Stage only
Confiance
Confiance : Community
Point d'entrée
Asset
Commande de staging sûr
npx -y tokrepo@latest install e50d6b32-9054-4bb8-a230-02546da7f852 --target codex

Stage les fichiers d'abord; l'activation exige la revue du README et du plan staged.

Introduction

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

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

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_, <url|label> 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.


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

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

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_, <url|label> 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 + Bolt SDK.

Bolt MIT-licensed, helper Apache-2.0.

🙏

Source et remerciements

Pattern compiled from Slack Block Kit + Bolt SDK.

Bolt MIT-licensed, helper Apache-2.0.

Fil de discussion

Connectez-vous pour rejoindre la discussion.
Aucun commentaire pour l'instant. Soyez le premier à partager votre avis.

Actifs similaires