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_postsandget_best_timesread this project's own results — use them to pick angles and times.