Broadcast
•3 min read
MCP server — AI assistants without a raw post tool
@sizl/broadcast-mcp is the only sanctioned path for an AI assistant to interact with the broadcast runtime. It exposes preview, queue, kill, list_pending, and verify — but never a raw post.
Install
The MCP server runs over stdio. Wire it into Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json on macOS) or any MCP-compatible client:
{
"mcpServers": {
"broadcast": {
"command": "npx",
"args": ["@sizl/broadcast-mcp"],
"env": {
"BROADCAST_MCP_ALLOWLIST": "bluesky,mastodon",
"BROADCAST_MCP_OPERATOR": "jason",
"BROADCAST_MCP_NOTIFY_WEBHOOK": "https://hooks.slack.com/services/..."
}
}
}
}
BROADCAST_MCP_ALLOWLIST is mandatory and explicit. If unset, no platform is allowlisted and every preview + queue rejects. Default-deny is the correct posture for an AI-facing surface — the AI doesn't get to invent destinations.
Tools
| Tool | What it does |
|---|---|
broadcast.preview | Runs the credential + voice gate on a draft. Returns rendered preview or a structured rejection. Does NOT publish. |
broadcast.queue | Same gate as preview, then enqueues the draft for Tier 2 approval. The MCP server never publishes — the operator approves out-of-band. |
broadcast.kill | Kills a queued draft. Ownership-guarded: only the operator who queued the draft can kill it unless BROADCAST_MCP_ALLOW_CROSS_OPERATOR_KILL=1. |
broadcast.list_pending | Read-only view of the Tier 2 queue. Each entry includes queuedBy so the assistant knows who owns what. |
broadcast.verify | Run the credential + voice scan on arbitrary text without enqueueing. Useful for self-linting mid-thought. |
What is NOT exposed
There is no broadcast.post. There is no broadcast.publish, broadcast.send, broadcast.dispatch, or any synonym — all are explicitly denylisted at the dispatcher with a structured tool-disabled-by-contract rejection. An LLM retrying variations gets the same clear refusal each time.
This is enforced at the schema layer, not via runtime check. The contract is the API surface.
Credential scanner
The shared sanitizeDraft() handles internal-voice tokens (review-round identifiers, phase tags, severity IDs). The MCP server adds a credential-shape scan on top — AWS access keys, Stripe live/test, GitHub PATs + classic + OAuth, OpenAI (including sk-proj- and sk-org-), Anthropic, Slack bot tokens + incoming webhook URLs, JWT (3-segment base64url, tightened to 16+ char middle), PEM private key blocks, Google API keys, Twilio SIDs, GCP service-account JSON markers.
Both scans run on every preview + queue. Rejections are distinguishable by reason code so the assistant can self-correct without guessing.
Notify webhook
A stdio MCP server's stderr is captured to a log file the user rarely reads — wiring BROADCAST_MCP_NOTIFY_WEBHOOK is the right way to wake a human when an AI session queues a draft. The Worker fires a JSON POST to your Slack incoming webhook (or any compatible endpoint) with draftId, platform, queuedBy, queuedAt, operator, and text. Stderr stays the fallback if the webhook fails.
Ownership
Every queued draft records queuedBy: session.operator. Kill is ownership-guarded: a session running as alice cannot kill a draft queued by bob. For ops takeovers, set BROADCAST_MCP_ALLOW_CROSS_OPERATOR_KILL=1 in the server env. The rejection message names the original queuer so the operator running the kill knows who to coordinate with.

