docs

MCP connector

xpost speaks Model Context Protocol natively, on every plan. One hosted URL gives any MCP client 11 tools — posting, media, guardrail dry-runs, delivery receipts, and the engagement insights that let an agent learn what works. Everything goes through the same API as the dashboard, so guardrails, copilot approval, key scopes, and rate limits all apply. An agent cannot bypass the approval queue through MCP, by design.

https://dev.xpost.to/api/mcp · Streamable HTTP · Bearer auth

Create the key under Dashboard → Agent (agent key). That tab generates all of the snippets below with your key filled in.

Claude Code

claude mcp add --transport http xpost https://dev.xpost.to/api/mcp \
  --header "Authorization: Bearer xp_live_YOUR_KEY"

Claude Desktop & claude.ai

Settings → Connectors → Add custom connector and paste your personal URL (the connector UI has no header field, so the key rides in the URL):

https://dev.xpost.to/api/mcp/xp_live_YOUR_KEY

Treat that URL as a secret — anyone holding it can act as your agent key. Revoking the key on the Keys page kills the URL instantly.

Cursor

Add to .cursor/mcp.json (project) or ~/.cursor/mcp.json (global):

{
  "mcpServers": {
    "xpost": {
      "url": "https://dev.xpost.to/api/mcp",
      "headers": { "Authorization": "Bearer xp_live_YOUR_KEY" }
    }
  }
}

Any other MCP client

The connector is standard Streamable HTTP at https://dev.xpost.to/api/mcp with Authorization: Bearer <key>. Clients that can't send headers can use the /api/mcp/<key> form.

Local stdio server (self-hosting)

Running xpost on localhost, or want the agent to upload files straight from the machine it runs on? Use the stdio server that ships in the repo (mcp/server.mjs):

claude mcp add xpost \
  -e XPOST_API_KEY=xp_live_YOUR_KEY \
  -e XPOST_URL=http://localhost:3001 \
  -- node /path/to/xpost/mcp/server.mjs

Same tools as the hosted connector, plus upload_media can read local file paths (the hosted connector accepts URLs and base64 only).

The 11 tools

list_accounts

List the connected social accounts (id, platform, username, status). Use the returned ids as social_account_ids when creating a post.

upload_media

Attach an image, video or PDF for a post. Supply a public https url, or base64 data with mime_type (local file paths are not available over the hosted connector). Returns a media_id for create_post. Required for Instagram, TikTok, YouTube, Pinterest (no text-only posts). A PDF becomes a LinkedIn document post — one PDF, on its own, LinkedIn only, and the account must be one that supports documents (create_post says so if not).

create_post

Create a social media post targeting one or more connected accounts. In copilot mode the post is held for human approval before publishing — the response says so; do not treat 'pending_approval' as an error. Omit scheduled_at to post as soon as it's approved/created. Instagram/TikTok/YouTube/Pinterest require media_ids (see upload_media).

list_posts

List recent posts with their status (pending_approval, scheduled, processing, posted, partial, failed, rejected).

get_delivery_receipt

Per-platform delivery receipt for a post: status, live post URL on success, error detail on failure. A successful row may also carry note — the post went out, but published less than was attached (X takes 4 images, Facebook won't let an app post photos and video together, a reel is one clip). Report it: a bare 'success' would overstate what was published. Check this after creating a post.

check_guardrails

Dry-run a caption against this workspace's brand guardrails (banned words/topics, link policy, posting cap) WITHOUT creating a post. Use this to fix a draft before create_post instead of burning a rejection. Returns { allowed, violations[] }.

bulk_post

Create up to 100 posts in one call. Each row is independent and runs the full single-post path — guardrails, plan limits, and copilot approval routing all apply, so in copilot mode the batch lands in the human approval queue. accounts entries may be account ids, platform:username, or a bare username when unambiguous. Returns a per-row report; check it — some rows can fail while others succeed.

get_post_metrics

Per-platform engagement for ONE published post: views, likes, comments, shares, reach, saves, engagement, plus the live URL. A null metric means the platform doesn't report it (not zero); empty data means metrics haven't synced yet (they refresh roughly every 6h).

get_insights

Workspace engagement roll-up over a window: post/delivery counts, metric totals, and a per-platform breakdown. Use this for 'how are we doing' questions and to compare platforms.

get_top_posts

Best-performing deliveries ranked by a metric — study these before drafting new content to learn what works for THIS audience. Returns caption, platform, username, metrics, and live URL per entry.

get_best_times

The workspace's best posting slots (weekday × hour, UTC) ranked by average engagement from its OWN past results. Use when choosing scheduled_at. Few samples = weak signal; the response includes sample counts per slot.

What your agent should expect

  • · pending_approval is not an error. In copilot mode every agent post waits for a human. The right behavior is to tell the user and stop — not retry.
  • · 422 = a guardrail spoke. The violations name the rule. Rewrite to comply and try once; never attempt to evade a guardrail.
  • · Check receipts. Delivery is per platform — report partial failures honestly.
  • · Learn before drafting. get_top_posts and get_best_timesread this project's own results — use them to pick angles and times.