content-distribution-mcp
An MCP server that publishes one piece of content to eight developer-community channels with idempotent state, per-subreddit anti-spam rules, and dual YAML or Notion backends. No LLM calls in the server. Your agent writes the copy, the MCP does the I/O.
Eight channels, three publish tiers
The server ships eight channel adapters. Five publish through native APIs; three use Playwright to pre-fill the compose form when the platform's API is paid, locked-down, or rate-limited to uselessness.
| Channel | Tier | Notes |
|---|---|---|
devto |
auto | Forem API v1. Native canonical_url so your blog stays the SEO origin. |
hashnode |
auto | GraphQL. Native originalArticleURL field. Publication-scoped tokens. |
github_discussions |
auto | GraphQL per-repo. Footer link for canonical (no native field). |
bluesky |
auto | atproto SDK. Canonical link appended to post text. App-password auth. |
reddit |
auto-gated | Per-subreddit cooldown, 5/day global cap, self-promo ratio, flair resolution. Pre-flight refuses posts that would violate sub rules. |
medium_browser |
manual | Playwright pre-fill plus batched-tab UX. Operator clicks Publish; mark_live CLI records the URL. |
linkedin_browser |
manual | Personal feed and company-admin compose. Plain-text draft, copy-paste workflow, mark_live flip. |
twitter_browser |
manual | Free-tier API is unusable. Plain-text draft plus pre-filled compose URL, mark_live records the tweet. |
What it gives your agent
Eight MCP tools. The agent calls publish or schedule with one content envelope plus per-channel variants. The MCP handles state, idempotency, retries, and platform quirks.
| Tool | Purpose |
|---|---|
publish |
Immediate publish across the variants you supply. Idempotent on (content_id, channel): re-running with the same envelope is a no-op. |
schedule |
Queue variants for schedule_at timestamps. Stored in the state backend until drain picks them up. |
drain |
Fire any due scheduled posts. One-shot and cron-friendly. Skips items that are already live. |
status |
Per-variant state for one content piece: pending, live, failed, needs_browser, with URLs and error context. |
unpublish |
Best-effort delete on DEV.to and GitHub Discussions. Reddit is honor-system (no API delete). Browser channels are operator-driven. |
hints |
Static per-channel metadata: character limits, tag vocabulary, canonical-URL support, recommended posting times. The agent decides what to do with them. |
list_profiles |
Returns the Distribution Profiles configured in your backend (token sets, default tags, posting cadences). |
list_subreddits |
Returns the curated Subreddit Catalog: name, rules, flair vocabulary, cooldown, allowed self-promo ratio. |
What makes it different
Idempotent on (content_id, channel)
Every publish call claims a key in the state backend before hitting the API. Network blip on attempt 47? Re-run the same envelope, get the same URL back. No double-posts, no duplicate Reddit threads, no embarrassing retries.
Reddit anti-spam pre-flight
Per-subreddit cooldown, 5-posts-per-day global cap, 10-to-1 content-to-self-promo ratio, flair resolution against the live sub config. The MCP refuses the post before it offends the mods, not after.
Browser fallback where APIs are hostile
Medium's API is partner-only. LinkedIn's is enterprise-gated. Twitter's free tier is unusable. For those, the MCP writes a clean draft, opens the compose form with fields pre-filled via Playwright, and you click Publish. mark_live records the URL.
Zero LLM calls in the server
Grep src/ for "anthropic". You'll find nothing. The host process supplies copy, credentials, and decisions. The MCP supplies idempotent I/O. Works with any model, any host, any pipeline.
Dual backend: YAML local, Notion team
Solo developer? YAML files in ~/.distribution-mcp/, zero config. Agency or team? Three Notion databases for Distribution Profiles, Subreddit Catalog, and Post Log, with URL write-back to source tasks. Same Protocol, swap with one constructor argument.
Works in any MCP host
stdio or SSE transport. No Anthropic-specific code. Tested against Claude Code, n8n's MCP Client node, Cursor, and the plain mcp Python client. The skill that ships with the repo is optional; the protocol is universal.
Install in 3 steps
-
Install the PyPI package (requires Python 3.11+)
pip install content-distribution-mcpOptional extras for browser channels:pip install content-distribution-mcp[browser]thenplaywright install chromium. For Bluesky:pip install content-distribution-mcp[bluesky]. -
Add the server to your MCP host config (
.claude/mcp.jsonfor Claude Code, equivalent for Cursor, n8n MCP Client node, etc.){ "mcpServers": { "content-distribution": { "command": "content-distribution-mcp", "args": ["serve"], "env": { "DEVTO_API_KEY": "...", "HASHNODE_API_KEY": "...", "BLUESKY_HANDLE": "you.bsky.social", "BLUESKY_PASSWORD": "app-password", "REDDIT_CLIENT_ID": "...", "REDDIT_CLIENT_SECRET": "...", "REDDIT_REFRESH_TOKEN": "..." } } } }Only the credentials for the channels you actually use need to be set. Browser channels (Medium, LinkedIn, Twitter) need no env vars; they run via Playwright on demand. -
Restart your MCP host. The eight content-distribution tools appear. From your agent, call
publishwith one content envelope and a variants list.
Works with Claude Code, Claude Desktop, Cursor, n8n via the MCP Client node, plain Python via the mcp client library, and any custom integration that speaks MCP over stdio or SSE. Full reference: the GitHub readme and spec.
FAQ
What does it cost?
Why not just use Buffer or Hootsuite?
publish once, and the MCP handles every platform's quirks (Reddit cooldowns, Bluesky's 300-char limit, Medium's missing API) in one round trip.Do I need a Notion account?
YamlBackend stores everything in four YAML files in ~/.distribution-mcp/. Zero configuration. The NotionBackend is for agency or team use; it provisions three databases and writes published URLs back to source tasks. Both implement the same Protocol; you swap with one constructor argument.How does the Reddit anti-spam logic work?
What does it not do?
list_subreddits). It doesn't game algorithms. It doesn't run analytics. It doesn't auto-follow, auto-DM, or auto-reply. It does one job: take a finished piece of content plus per-channel variants and route them with idempotent state.Want it wired into your distribution pipeline?
We use content-distribution-mcp daily to publish our own posts and tools across the developer-community channels. If you want it set up for your stack, integrated with your CMS, or a full agent pipeline built end to end, we can do that.
Get in touch