BulkPublish

Publish, schedule, and manage social media across 11 platforms with media uploads and analytics tracking.

Documentation

BulkPublish MCP Server

A Model Context Protocol server that lets Claude and other AI assistants interact with the BulkPublish social media publishing API.

What it does

This MCP server exposes BulkPublish API operations as tools that AI assistants can call directly. You can ask Claude to schedule posts, check analytics, upload media, manage labels, and more — all through natural conversation.

Installation

Option 1: npx (recommended)

npx @bulkpublish/mcp-server

Option 2: Global install

npm install -g @bulkpublish/mcp-server
bulkpublish-mcp

Option 3: From source

git clone https://github.com/azeemkafridi/bulkpublish-api.git
cd bulkpublish-api/mcp-server
npm install
npm run build
node dist/index.js

Requires Node ≥ 20.19. npm run build bundles the composer UI with Vite (build:ui), then compiles the server with tsc.

Configuration

Set your API key as an environment variable:

export BULKPUBLISH_API_KEY=bp_your_api_key_here

Get your API key at app.bulkpublish.com/developer.

Claude Desktop

Add this to your Claude Desktop configuration file:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "bulkpublish": {
      "command": "npx",
      "args": ["-y", "@bulkpublish/mcp-server"],
      "env": {
        "BULKPUBLISH_API_KEY": "bp_your_api_key_here"
      }
    }
  }
}

If you installed from source:

{
  "mcpServers": {
    "bulkpublish": {
      "command": "node",
      "args": ["/absolute/path/to/bulkpublish-api/mcp-server/dist/index.js"],
      "env": {
        "BULKPUBLISH_API_KEY": "bp_your_api_key_here"
      }
    }
  }
}

Claude Code

Add the MCP server to your Claude Code configuration:

claude mcp add bulkpublish -- npx -y @bulkpublish/mcp-server

Set the environment variable in your shell profile or .env file:

export BULKPUBLISH_API_KEY=bp_your_api_key_here

Remote / hosted server (HTTP)

The npx/stdio setup above runs the server locally. Web clients that cannot spawn a local process — claude.ai custom connectors and Smithery's gateway — connect to a hosted HTTP endpoint instead. Alongside the stdio bin, the server ships a Streamable HTTP transport (dist/http.js, run with npm run start:http):

EndpointPurpose
POST /mcpMCP Streamable HTTP (stateless)
GET /.well-known/mcp/server-card.jsonStatic metadata so registries can skip scanning
GET /healthLiveness check

It is multi-tenant — the process holds no API key. Each caller supplies their own bp_… key per request via the connection URL (?key=bp_…), a Smithery ?config= blob, or an Authorization: Bearer bp_… / X-BulkPublish-Key header. initialize, tools/list, and resources/* need no key (so scans and tool discovery work); a tools/call without one returns 401.

A Dockerfile is included:

docker build -t bulkpublish-mcp . && docker run -p 8080:8080 bulkpublish-mcp

Once deployed, add it to claude.ai → Settings → Connectors → Add custom connector as https://<host>/mcp?key=bp_…, or publish the https://<host>/mcp URL to Smithery.

Tool profiles

The server ships three tool profiles, chosen with BULKPUBLISH_TOOL_PROFILE:

ProfileToolsDefault for
core20 — channels, posts, media, analytics, and the interactive panelsThe hosted HTTP server (dist/http.js), which is what the Claude and ChatGPT directories list
inbox27 — everything in core, plus the Inbox and comment tools: list_conversations, get_conversation, reply_to_conversation, update_conversation, list_post_comments, reply_to_comment, moderate_commentOpt-in on a hosted deployment (BULKPUBLISH_TOOL_PROFILE=inbox). The core tools in it are identical to the core profile's
fullEvery tool (including the Inbox and comment tools; labels, hashtag groups, templates, calendar notes, review links, client-connect links, recurring schedules, channel sets, RSS feeds, chunked uploads, quota)The local stdio server (npx @bulkpublish/mcp-server)

Set BULKPUBLISH_TOOL_PROFILE=full on a hosted deployment to expose everything, or core locally to keep a small tool list. The panels' buttons call back into create_post, publish_post, retry_post, update_post, delete_post, list_channels, list_media, get_post, create_media_upload and finalize_media_upload, so all of those are in core.

Every tool carries a title and explicit readOnlyHint, destructiveHint, idempotentHint and openWorldHint booleans; npm run check:annotations verifies that for every profile (add --table to print the per-tool justification table used in directory submissions).

Available Tools

Tools marked ★ are in the core profile (see Tool profiles); tools marked ✉ are in inbox (with every ★ tool); the rest need full.

ToolDescription
Posts
create_post ★Create and optionally schedule a post (supports reels, stories, carousels, threads via postTypeOverrides)
compose_post ★Open an interactive composer UI (MCP Apps) to draft/schedule a post; submits via create_post
update_post ★Update a draft or scheduled post
get_post ★Get a single post with full details
list_posts ★List posts with filters (status, search, date range)
delete_post ★Delete a post
publish_post ★Publish a draft post immediately
retry_post ★Retry a failed post
approve_postApprove a post awaiting team approval (roles with post:approve)
reject_postReject a pending post back to draft, with an optional reason
get_post_metrics ★Get engagement metrics (impressions, likes, comments, shares). Each platform entry carries supportedMetrics — a key not in that list is a stored 0, not a measurement
publish_storyPublish as a story to Facebook or Instagram
bulk_postsBulk delete or retry multiple posts
get_queue_slot ★Get the next optimal time slot for a channel
Channels
list_channels ★List all connected social media channels
get_channel_healthCheck channel token health
get_channel_optionsGet platform-specific options (boards, playlists)
search_mentionsSearch users for @mention (X, Bluesky, Mastodon, LinkedIn Pages — LinkedIn results carry the vanityName slug they matched on)
Channel Sets
list_channel_setsList saved channel groups for one-click multi-channel targeting
create_channel_setSave a named group of channels (max 50 per org, names unique per org)
update_channel_setRename a set or change its channels
delete_channel_setDelete a channel set
RSS Autopost
list_rss_feedsList RSS/Atom feeds polled every 15 minutes (new items become posts)
create_rss_feedAdd a feed (max 20 per org); mode = draft (default, items become drafts) or publish (auto-published); optional fieldMapping controls how items render (caption template, media pick, truncation, per-channel overrides)
update_rss_feedChange, pause, or re-point a feed (changing feedUrl re-baselines it — the backlog is not flooded)
delete_rss_feedStop and remove a feed
Media
upload_media ★Upload a media file from a URL (or local path on the stdio server)
get_mediaGet a media file by ID
list_media ★List uploaded media files
delete_mediaDelete a media file
create_media_upload ★Reserve a presigned R2 URL for a direct browser upload (used by the composer)
finalize_media_upload ★Record an uploaded object as a media file after the browser PUT (used by the composer)
create_multipart_uploadStart a chunked upload for large media (videos up to 1GB) — presigned URLs for fixed 10MB parts
complete_multipart_uploadAssemble the uploaded parts (partNumber + ETag each) and record the media file
abort_multipart_uploadCancel an in-progress chunked upload and free its stored parts
Labels
create_labelCreate a new label
list_labelsList all labels
update_labelUpdate a label name or color
delete_labelDelete a label
Analytics
get_analytics ★Get analytics summary for a date range
Schedules
list_schedulesList recurring schedules
create_scheduleCreate a recurring schedule
update_scheduleUpdate a schedule
delete_scheduleDelete a schedule
Inbox and comments
list_conversations ✉List direct messages, reviews and comment threads, newest activity first, with filters and paging (Pro and Business plans)
get_conversation ✉Read one conversation's messages, oldest first; does not mark it read
reply_to_conversation ✉Send a reply to the person on the platform right away; it cannot be unsent
update_conversation ✉Mark read or unread, archive or reopen, snooze, or assign a conversation; nothing is sent
list_post_comments ✉List the comments and reactions on a published post, per channel, with the ids the reply and moderate tools take
reply_to_comment ✉Post a public reply to a comment right away, as the channel the post went out on
moderate_comment ✉Like, unlike, hide, unhide or delete a comment on the platform (delete is permanent)
Account
get_quota_usageCheck current account usage (hidden when BULKPUBLISH_HIDE_BILLING=1)
Interactive UI (MCP Apps)
compose_post ★Open the interactive post composer (also listed above)
view_analytics ★Open an interactive analytics dashboard
view_posts ★Open an interactive posts list
view_channels ★Open an interactive channels view
view_media ★Open an interactive media gallery
view_quotaOpen an interactive account-usage view (hidden when BULKPUBLISH_HIDE_BILLING=1)

Interactive UI (MCP Apps)

In hosts that support MCP Apps — Claude, ChatGPT, VS Code, Goose, Postman, and the Smithery playground — the compose_post tool opens an interactive composer rendered inline in the conversation: pick channels, write content, optionally schedule, then submit. On submit it calls create_post through the host bridge, so the sandboxed iframe never holds your API key. Hosts without MCP Apps support simply receive a plain-text summary, so nothing breaks.

Several tools render UIs — compose_post (composer), view_analytics, view_posts, view_channels, view_media, and view_quota. Each is a self-contained widget under src/ui/<name>/, bundled into a single HTML file with Vite (one single-input pass per widget — see scripts/build-ui.mjs) and inlined into the server via src/ui/widgets.generated.ts. They share the BulkPublish web app's design tokens (src/ui/tokens.css), so the look matches. A View receives its data from the triggering tool's structuredContent and can call other tools back through the host bridge — so the sandboxed iframe never holds your API key. Hosts without MCP Apps support simply receive a plain-text summary.

Example Conversation

Here is an example of using the BulkPublish tools in Claude:

You: Schedule a post for tomorrow at 9am EST saying "Excited to announce our new feature! Check it out at example.com" to all my connected channels.

Claude: Let me check your connected channels first.

[Calls list_channels]

You have 3 active channels:

  • X/Twitter: @yourcompany (ID: 1)
  • LinkedIn: Your Company Page (ID: 2)
  • Instagram: @yourcompany (ID: 3)

Now I'll schedule the post for tomorrow at 9:00 AM Eastern.

[Calls create_post with content, channels, scheduledAt: "2025-01-16T14:00:00Z", timezone: "America/New_York", status: "scheduled"]

Done! Your post has been scheduled:

  • Post ID: 42
  • Status: scheduled
  • Scheduled for: January 16, 2025 at 9:00 AM EST
  • Channels: X/Twitter, LinkedIn, Instagram

You: How did my posts perform this month?

Claude: [Calls get_analytics with from: "2025-01-01", to: "2025-01-31"]

Here is your January analytics summary:

  • Total posts: 28
  • Published: 25
  • Failed: 1
  • Scheduled (pending): 2
  • By platform: X/Twitter (25 posts, 24 published), LinkedIn (20 posts, 19 published), Instagram (15 posts, all published)
  • Best day: January 12 with 5 posts

You: Upload this image and create a draft post with it: https://example.com/product-launch.png

Claude: [Calls upload_media with url]

Image uploaded successfully (ID: 15, 1200x630px, 245KB).

[Calls create_post with content and mediaFileIds]

Draft post created (ID: 43) with the product launch image attached. You can review and publish it when ready.

Environment Variables

VariableRequiredDefaultDescription
BULKPUBLISH_API_KEYYes—Your API key (starts with bp_)
BULKPUBLISH_BASE_URLNohttps://app.bulkpublish.comAPI base URL (for self-hosted instances)
BULKPUBLISH_TOOL_PROFILENofull (stdio) / core (hosted HTTP)core, inbox or full — see Tool profiles
BULKPUBLISH_HIDE_BILLINGNo—1 drops get_quota_usage and view_quota (only relevant with full; core never includes them)

License

MIT