AI agents

MCP server

PostLake runs a hosted MCP server: the open standard that lets AI agents use external tools. Connect once and your agent can publish, schedule and pull analytics across every network, just by you asking in plain language.

Server URL

https://api.postlake.dev/mcp

Connect in three steps

  1. Open your agent's connector settings. In Claude: Settings → Connectors → Add custom connector. ChatGPT, Cursor and others have an equivalent "add MCP server" option.
  2. Paste the URL above.
  3. Approve on the PostLake screen. You're sent to a PostLake Authorize screen. Sign in and click Approve. OAuth means you do not need to paste an existing API key into the client. The agent then appears under Agents in your dashboard.

OAuth compatibility

PostLake supports both registration methods used by MCP clients. Newer clients can identify themselves with a Client ID Metadata Document (CIMD). Clients that still use Dynamic Client Registration (DCR), including current Cursor versions, continue to work. The client chooses automatically from PostLake's OAuth metadata, so you do not need different URLs or setup for each service.

Both paths require PKCE and an exact registered callback. The approval screen shows where the result will be returned before you continue.

Access tokens last one hour and clients refresh them automatically. Signed channel-connection links created by get_connect_link expire after 30 minutes.

Streamable HTTP clients (Cursor and others that send Accept: application/json, text/event-stream) receive HTTP 401 with a WWW-Authenticate challenge on first connect. That is what opens the host's Connect / login prompt. Directories may still probe initialize and tools/list as plain JSON without a token. A green "connected" status with no login means the client skipped that 401; reconnect so it can complete OAuth.

Per-agent setup

Pick your host for the exact steps. Each connects to the URL above over OAuth, so you do not need to paste an existing API key:

Claude Code

One command, then approve the browser page that opens:

claude mcp add --transport http postlake https://api.postlake.dev/mcp

Start a fresh session and run /mcp to confirm postlake is connected.

Claude Desktop

Open Settings → Connectors → Add custom connector, paste the server URL, and approve the browser sign-in. PostLake's tools then appear in any chat.

Cursor

Open Settings → Tools & MCP → New MCP server (this opens mcp.json), merge this in, and save:

{
  "mcpServers": { "postlake": { "url": "https://api.postlake.dev/mcp" } }
}

Then open Customize → MCPs (or Ctrl+Shift+POpen MCPs) and click Connect / Needs login. The first handshake returns 401 on purpose so that button appears. Sign in and click Approve once. Cursor Desktop may use its native cursor://anysphere.cursor-mcp/oauth/callback callback or a local loopback callback. PostLake supports both.

For something running unattended, send an API key instead and skip the browser step entirely. The same URL takes either credential:

{
  "mcpServers": {
    "postlake": {
      "url": "https://api.postlake.dev/mcp",
      "headers": { "Authorization": "Bearer sk_live_..." }
    }
  }
}

An API key does not expire, so nothing has to refresh it and nobody has to approve anything after the key exists. It still appears as a named identity in Agent Control, where its publishing limits are enforced. An OAuth token lasts an hour and is tied to one client, which is what you want when a person is present.

ChatGPT

In ChatGPT's developer mode / custom connectors, add a new MCP server pointing at the URL above, then approve the sign-in.

Gemini CLI
gemini mcp add postlake https://api.postlake.dev/mcp

Then approve the browser page to authorize.

VS Code · GitHub Copilot

In Copilot Agent mode → MCP servers → Add, point it at the URL above and sign in.

Any other MCP host

Point its “add MCP server” action at https://api.postlake.dev/mcp with no Authorization header. OAuth signs you in through your browser. The hosted /mcp endpoint accepts OAuth access tokens only; a normal sk_live_ REST API key does not unlock it.

Clients that cannot complete browser OAuth send Authorization: Bearer sk_live_... to the same https://api.postlake.dev/mcp URL. One endpoint takes either credential. A key has account-level access, so OAuth agent attribution and per-agent publishing limits do not apply to it. (POST /v1/mcp is the same server under an older path and still works, so existing configs need no change.)

The toolset

Once connected, the agent gets tools for the full social workflow. Responses use a consistent, model-readable format throughout:

GroupWhat the agent can do
Identity & limitsCheck the connected account, agent publishing limits and credit balance before acting. whoami, get_credits
Profiles & channelsList and manage profiles, inspect channel destinations and shoppable products, connect supported credential-based channels, create a human approval link for OAuth channels, or disconnect a channel. list_profiles, create_profile, rename_profile, delete_profile, list_social_accounts, get_social_account, list_account_targets, list_products, check_allowance, list_tagged_posts, list_own_posts, list_branded_partners, list_ad_accounts, list_events, create_event, connect_account, get_connect_link, disconnect_account
Publish & scheduleUpload media from a public URL or directly from the agent's local disk, dry-run a post, publish now or later, confirm an asynchronous publish, save a draft for a person to approve and publish it once they have, inspect history, edit or cancel queued posts, and retract something already live. upload_media, upload_media_batch, validate_post, create_post, get_post, confirm_post, list_posts, edit_post, publish_draft, cancel_post, delete_post
Read & respondRead notifications and comments, mark notifications seen, reply to comments, hide a reply you do not want on your post, delete a comment, engage with posts or people, and update supported social profiles. list_notifications, mark_notifications_seen, read_comments, reply_to_comment, hide_comment, delete_comment, engage, update_profile
DiscoverLook at the network itself, not just your own corner of it: search public posts by keyword or topic tag, look someone up before replying to them, read what an account has been publishing, and find a place to tag on a post. search_posts, look_up_profile, read_profile_posts, search_places, find_creators
Direct messagesRead threads, explicitly mark a handled thread as read, reply, or start a supported conversation from a handle. list_conversations, read_conversation, mark_conversation_read, send_message
Platform rulesRead each platform's limits, media rules, options and live creator-level publishing constraints. get_platform_capabilities, get_publish_info
AnalyticsPer-post and cross-platform performance, to decide what to post next. get_analytics, get_post_analytics
Automation handoffAccount-level API key callers can create another full-access key for a trusted service. OAuth agents cannot see or call this tool because it would bypass their owner-set limits. create_api_key
Safe by defaultIdempotent retries mean a retrying agent never double-posts, even at 3am. Reads say which networks they could not reach, so an empty result never quietly means "we did not look".

Upload a local file

upload_media supports both remote and local assets. Pass url for an existing public asset. For a file on the agent's machine, omit url and pass contentType plus the file's sizeBytes. The tool returns a preallocated med_… id, a five-minute HTTPS PUT target and the exact headers to send.

The agent uploads the bytes with its local HTTP client, then passes that media id to create_post. For example, after upload_media({ "contentType": "video/mp4", "sizeBytes": 123456 }), it runs:

curl --fail-with-body --request PUT "$UPLOAD_URL" \
  --header "Authorization: Bearer $UPLOAD_TOKEN" \
  --header "Content-Type: video/mp4" \
  --upload-file "/absolute/path/video.mp4"

No temporary public bucket or base64 in the model context is needed. The signed target is account-bound, expires after five minutes, accepts only the prepared type and size, and cannot be reused after a successful upload. The local path stays on the agent's machine; only the bytes are sent to PostLake.

For a carousel, call upload_media_batch with one item per slide instead of upload_media once per file. Each local item still returns its own five-minute PUT target. Then call validate_post before create_post. TikTok photo posts need JPEG or WebP (max 1080×1920); a PNG is converted to JPEG with a warning. Photo posts also need a title via platformOptions.tiktok.title.

To schedule "7am in London", pass timezone: "Europe/London" with a naive scheduledAt such as 2026-08-13T07:00:00, or set an account default timezone. The stored fire time is always UTC. A naive datetime without timezone (and without an account default) is refused. edit_post can change text, media, platformOptions and the fire time. list_posts accepts account and profile filters. If delete_post cannot retract a live post, the reason includes the live URL.

What comes back

List and action tools usually answer in compact markdown that your assistant can render as a real table. Complete records such as get_post and detailed capability lookups return structured JSON when every field matters. Every result carries the ids needed for the next call.

list_social_accounts (ids shortened here to fit the page):

| id             | profile  | platform | handle                  | status |
| ---            | ---      | ---      | ---                     | ---    |
| acc_29fd5066…  | postlake | bluesky  | crumblelake.bsky.social | active |
| acc_6fb037b0…  | postlake | x        | yourbrand               | active |
| acc_1f756183…  | postlake | youtube  | @postlake               | active |

3 rows

list_posts gives per-network status on one line, so a partial failure is visible at a glance:

| id             | state     | when             | profile  | targets                                        | text             |
| ---            | ---       | ---              | ---      | ---                                            | ---              |
| post_bf85a34c… | published | 2026-08-02 20:26 | postlake | bluesky/crumblelake.bsky.social:published      | Release update   |
| post_9f8e7d6c… | partial   | 2026-08-01 14:12 | postlake | bluesky/crumblelake:published x/yourbrand:failed | Product launch   |

2 rows

create_post tells you what happened per network, and spells out anything that did not go:

post_9f8e7d6c: partial

| platform | state     | url                                     |
| ---      | ---       | ---                                     |
| bluesky  | published | https://bsky.app/profile/you/post/3ms4t |
| x        | failed    |                                         |

x did not publish: Not enough credits for an X post with a link (75 needed).

When a tool fails you get the sentence first, then the facts your agent can act on, so it can choose differently instead of retrying the same call:

This agent may post to bluesky, linkedin. Not x.

type: agent_not_permitted
retryable: no, trying again will fail the same way
refused by: the platforms limit your owner set
you may use instead: bluesky, linkedin

What it looks like in use

Once connected you can just say:

"Post this photo to Instagram and LinkedIn, schedule a follow-up
for Friday at 9am, and tell me which of last week's posts did best."

Local media files

An MCP server runs remotely and cannot read a path on your computer directly. For a local image or video, call upload_media with its MIME type and no url. PostLake returns a short-lived authenticated uploadUrl, the required PUT method and headers, and a ready-to-run upload instruction. Your agent can upload the local bytes without making the file public or obtaining your API key. The PUT response contains the med_… id to pass to create_post. For a carousel, use upload_media_batch.

For media already on the web, pass its public url to upload_media as before. PostLake fetches and validates it server-side.

Security & revoking access

Agents authorise over OAuth, so you do not paste an existing API key into the client or a chat. Every connection you authorise is listed under Agents in your dashboard. Click Revoke next to any one to cut off its access immediately; it would need to re-authorise to return.

An approved OAuth connection can use the social workflow tools listed above, but it cannot create an unrestricted account API key. This prevents an agent from escaping the limits its owner set. Only approve clients you trust, and revoke any connection you no longer recognise or need.

Per-agent limits

OAuth connections appear under Agents in the dashboard. You can restrict which profiles and networks each connection may publish to, set a daily post limit, control whether it may spend purchased credit packs, and require your approval before anything publishes. When approval is required, create_post saves a draft even if the agent omits draft: true, and that OAuth agent cannot publish the draft itself. The owner can review and publish it from PostLake or with an account-level API key. Call whoami to see the active policy before planning work. Existing connections keep their current direct-publishing behavior until the owner enables approval.