Model Context Protocol · v1.0

Run post mate from any AI assistant.

Hook up Claude, Cursor, ChatGPT, or anything else that speaks MCP. Then schedule, publish, and audit your posts in plain language — fifteen tools, one bearer token, sub-minute setup.

  • Compose, schedule, publish across every network
  • Pull media by URL — no chat attachments
  • Drop into your queue slots automatically
  • Per-network captions and platform overrides
01

What you get

A Streamable-HTTP MCP server that exposes the same surface as the web app — with every call scoped to the bearer-token owner.

Plain-language control

Ask your assistant to do anything the web composer can do. It maps your intent to the right tool calls — no SDK, no glue code.

Server-side media

Pass a URL, post mate fetches it for you. Works around the assistant's attachment limits and chat-token budget.

Queue & calendar aware

Drop posts into your next free queue slot, or query a date window without scrolling through a UI.

Single-target retries

When one network hiccups, re-run just that target — the others keep their published state.

02

Get an MCP key

Three clicks. Keep it private — anyone with the key can read and post on your behalf.

  1. 1

    Open Settings

    Head to /app/settings and scroll to Connect to Claude (MCP).
  2. 2

    Generate a key

    Click Generate new key, give it a memorable name (e.g. "Claude Desktop — laptop"), and copy the value. The plaintext is shown once; the database stores only a SHA-256 hash.
  3. 3

    Keep the URL handy

    The server URL is the same for every account:
    https://post-mate.com/api/mcp/mcp

Lost the key? You can't recover it. Generate a new one and revoke the old one — clients break immediately on the old token.

03

Hook up a client

Four flavours covered — every other MCP client speaks the same Streamable-HTTP transport.

Claude Desktop

Easiest

Settings → Developer → Edit Config, paste, fully quit, reopen.

claude_desktop_config.jsonsnippet
{
  "mcpServers": {
    "post-mate": {
      "url": "https://post-mate.com/api/mcp/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_KEY_HERE"
      }
    }
  }
}

Claude Code

CLI

One command and it's wired into every session.

terminalsnippet
claude mcp add --transport http post-mate \
  https://post-mate.com/api/mcp/mcp \
  --header "Authorization: Bearer YOUR_KEY_HERE"

Verify with claude mcp list.

Cursor

Editor

Merge into ~/.cursor/mcp.json or use the in-app MCP picker.

~/.cursor/mcp.jsonsnippet
{
  "mcpServers": {
    "post-mate": {
      "url": "https://post-mate.com/api/mcp/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_KEY_HERE"
      }
    }
  }
}

ChatGPT

Plus / Team

Settings → Connectors → Add custom MCP.

URL
https://post-mate.com/api/mcp/mcp
Transport
Streamable HTTP
Auth
Bearer · paste your key from step 2

Any other MCP client

Generic

If it speaks HTTP MCP — it works. SSE transport is exposed too.

HTTP
https://post-mate.com/api/mcp/mcp
SSE
https://post-mate.com/api/mcp/sse
Header
Authorization: Bearer YOUR_KEY_HERE
04

Tools reference

Fifteen tools cover the whole post lifecycle. Run tools/list in your client for the full Zod schemas.

ToolWhat it doesInputs
list_accountsread
Every social account connected to your workspace — id, platform, display name.
list_account_groupsread
Account groups you set up in Settings (e.g. Work, Personal). Pair with list_accounts.groupId.
next_queue_slotread
The next free slot from Settings → Queue. Drop into create_post.scheduledAt.
list_postsread
Paginate your posts (newest first). Filter by status: draft, scheduled, publishing, posted, partial, failed.status?, limit?
list_calendarread
Posts whose scheduledAt falls in [from, to). Window capped at 366 days.from, to
get_postread
Hydrate a single post: caption, per-network targets, statuses, and media.postId
upload_mediamedia
Fetch a public URL server-side, store it in post mate, return {key, mimeType, sizeBytes} for create_post.media[].url
create_postwrite
Build a post and (by default) publish or schedule it. type ∈ text/image/video/story. overrides[] sets per-account caption + platformConfig.caption, socialAccountIds[], type?, media[]?, overrides[]?, scheduledAt?, asDraft?
update_postwrite
Patch caption or scheduledAt on a draft or scheduled post.postId, caption?, scheduledAt?
reschedule_postwrite
Shortcut for moving a scheduled post to a new time.postId, scheduledAt
publish_postwrite
Publish a draft or scheduled post right now, ignoring its scheduledAt.postId
duplicate_postwrite
Clone an existing post into a fresh draft (caption, media, targets, overrides).postId
retry_targetwrite
Re-run a single network target after a failure — same as the Retry button in the web app.targetId
delete_postwrite
Hard-delete a draft or scheduled post. Posts that already went live stay where they are.postId
get_analyticsanalytics
Whatever metrics each platform exposes for one post — impressions, likes, reach.postId
05

Example prompts

Anything natural-sounding works — your assistant figures out the tool chain. A few we use every day.

Show every post I have scheduled this week, grouped by day.
Schedule "We just shipped v2 🎉" to my LinkedIn and Bluesky for tomorrow at 9 AM Kyiv time.
Take this image URL, attach it to my Instagram + Threads, post now.
How did my last Instagram reel perform?
Move my 3 PM Friday post to Monday morning.
Duplicate yesterday's LinkedIn post but swap the caption to "Day 2 recap" and save as a draft.
Cancel every scheduled post that mentions the word "draft".
Drop this caption into my next free queue slot for all my work accounts.
06

Workflow recipes

Three tool chains the assistant figures out on its own — useful to know the shape if you're debugging.

Schedule an image to the next queue slot

  1. 1list_accounts → pick the account ids you want.
  2. 2upload_media with the image URL → keep the returned {key, mimeType, sizeBytes}.
  3. 3next_queue_slot → grab the timestamp.
  4. 4create_post with type: image, the media ref, account ids, and the queue timestamp.

Compose once, tweak per platform

  1. 1Write the main caption.
  2. 2Pass overrides[] with { socialAccountId, captionOverride, platformConfig }.
  3. 3Use platformConfig for platform-specific knobs (YouTube title, TikTok privacy, etc.).

Re-run only the network that failed

  1. 1list_posts with status: "partial".
  2. 2get_post → find the target whose status is failed.
  3. 3retry_target with that target id. Other targets stay put.
07

Limits & types

Hard-coded caps; the server returns a typed error well before any underlying network's limit.

Max media size per file
100 MB
Image types
jpeg, png, webp, gif
Video types
mp4, mov, webm
Max accounts per post
10
Max media items per post
20
Calendar window per query
366 days
Caption length per call
10 000 chars
08

Security model

Defence in depth — the assumption is that a key may eventually leak, so individual blast radius stays small.

Hashed at rest

Keys carry 256 bits of entropy. We persist only the SHA-256 hash, so a database dump cannot replay a key.

Tenant-scoped

Every tool resolves the user from the bearer token. No call can read or mutate another tenant's data.

SSRF-hardened

upload_media resolves the hostname, rejects private IPv4/IPv6 and metadata aliases, and re-validates each redirect hop.

Instant revoke

Revoking a key from Settings takes effect on the very next request. No propagation delay, no logout.

09

Troubleshooting

The handful of things that bite people in their first hour.

"Unauthorized" / 401
Wrong key, the key was revoked, or the Authorization header is missing. Generate a fresh one and re-paste.
Tools don't appear in Claude Desktop
Fully quit the app (Cmd-Q on macOS — not just closing the window) and reopen. The config file is read at startup only.
"One or more account ids are not connected"
Account ids change when you disconnect and reconnect a platform. Call list_accounts again to refresh.
"Host X resolves to a private IPv4"
The URL you passed to upload_mediaresolves to a local or private address. That's blocked on purpose — host the media on a public CDN or any normal HTTPS URL.
A scheduled post didn't fire
Run get_post. If a target's status is failed, the error message is on the post itself. retry_target re-runs just that network.

Stuck on something, or hitting an MCP edge case?

Email support@post-mate.com — we read every message.