MCP Server

Ampost provides a Model Context Protocol (MCP) server that lets AI agents and LLMs publish to social platforms directly. Its tools are the Ampost REST API, one tool per endpoint, so an agent and a developer work from the same contract: discover platform rules with platforms_list, create and publish posts, and manage accounts and platform sets.

Endpoint

POST/mcp

The MCP endpoint is available at the root /mcp path, not under /api. Use your API key, or an OAuth access token from signing in with Ampost, as a Bearer token.

Ampost uses the Streamable HTTP transport in stateless mode on this single endpoint. Every JSON-RPC message is a POST /mcp with that Bearer token. No Mcp-Session-Id is issued, so long polling never fails with "Session not found". GET and DELETE return 405.

Available Tools

Each tool is one REST endpoint: same arguments (path parameters and body fields in one object), same result as structuredContent, same error codes and rate limits. Every tool publishes its input and output JSON Schema in tools/list; the REST shapes are in /openapi.json.

platforms_list

List platforms and their capabilities.

GET /api/platforms · scope posts:read

posts_list

List posts.

GET /api/posts · scope posts:read

posts_create

Create, schedule or publish a post.

POST /api/posts · scope posts:write

posts_import_csv

Import posts from CSV.

POST /api/posts/bulk · scope posts:write

posts_get

Get a post.

GET /api/posts/{postId} · scope posts:read

posts_update

Update a post.

PATCH /api/posts/{postId} · scope posts:write

posts_delete

Delete a post.

DELETE /api/posts/{postId} · scope posts:write

posts_publish

Publish a post now.

POST /api/posts/{postId}/publish · scope posts:write

posts_unpublish

Unpublish a post.

POST /api/posts/{postId}/unpublish · scope posts:write

posts_edit_published

Edit a published post's text.

POST /api/posts/{postId}/edit-published · scope posts:write

posts_retry

Retry failed accounts.

POST /api/posts/{postId}/retry · scope posts:write

media_upload_create

Start a media upload.

POST /api/media/uploads · scope media:write

media_upload_status

Get a media upload.

GET /api/media/uploads/{uploadId} · scope media:write

accounts_list

List connected accounts.

GET /api/accounts · scope accounts:read

accounts_get

Get a connected account.

GET /api/accounts/{accountId} · scope accounts:read

accounts_disconnect

Disconnect an account.

DELETE /api/accounts/{accountId} · scope accounts:write

accounts_set_analytics

Turn X analytics on or off.

PATCH /api/accounts/{accountId}/analytics · scope accounts:write

accounts_list_boards

List a Pinterest account's boards.

GET /api/accounts/{accountId}/boards · scope accounts:read

accounts_set_board

Pick a Pinterest account's default board.

PATCH /api/accounts/{accountId}/board · scope accounts:write

accounts_connect

Start connecting an account.

POST /api/accounts/connect · scope accounts:write

accounts_connect_status

Get a connection request.

GET /api/accounts/connect/{connectRequestId} · scope accounts:read

platform_sets_list

List platform sets.

GET /api/platform-sets · scope accounts:read

platform_sets_create

Create a platform set.

POST /api/platform-sets · scope accounts:write

platform_sets_get

Get a platform set.

GET /api/platform-sets/{platformSetId} · scope accounts:read

platform_sets_update

Rename a platform set.

PATCH /api/platform-sets/{platformSetId} · scope accounts:write

platform_sets_delete

Delete a platform set.

DELETE /api/platform-sets/{platformSetId} · scope accounts:write

queue_get

Get a posting queue.

GET /api/platform-sets/{platformSetId}/queue · scope posts:read

queue_update

Update a posting queue.

PATCH /api/platform-sets/{platformSetId}/queue · scope posts:write

queue_slots

Preview queue slots.

GET /api/platform-sets/{platformSetId}/queue/slots · scope posts:read

analytics_posts

Post analytics.

GET /api/analytics/posts · scope analytics:read

analytics_overview

Analytics overview.

GET /api/analytics/overview · scope analytics:read

analytics_followers

Follower history.

GET /api/analytics/followers · scope analytics:read

webhooks_list

List webhooks.

GET /api/webhooks/endpoints · scope webhooks:write

webhooks_create

Create a webhook.

POST /api/webhooks/endpoints · scope webhooks:write

webhooks_get

Get a webhook.

GET /api/webhooks/endpoints/{webhookId} · scope webhooks:write

webhooks_update

Update a webhook.

PATCH /api/webhooks/endpoints/{webhookId} · scope webhooks:write

webhooks_delete

Delete a webhook.

DELETE /api/webhooks/endpoints/{webhookId} · scope webhooks:write

webhooks_test

Send a test event.

POST /api/webhooks/endpoints/{webhookId}/test · scope webhooks:write

webhooks_deliveries_list

List webhook deliveries.

GET /api/webhooks/endpoints/{webhookId}/deliveries · scope webhooks:write

webhooks_redeliver

Redeliver an event.

POST /api/webhooks/endpoints/{webhookId}/deliveries/{deliveryId}/redeliver · scope webhooks:write

docs_search

Search the docs.

GET /api/docs/search · scope none (any key)

feedback_submit

Report a bug or suggest a change.

POST /api/feedback · scope none (any key)

tools/list shows only the tools your key's or app's scopes allow; calling another answers INSUFFICIENT_SCOPE. A failed call returns isError: true with the REST error body as text: { "error": { "code", "message", "details" } }.

Authentication

The MCP endpoint uses the same Bearer token authentication as the REST API. Pass your API key in the Authorization header. A key's scopes decide which tools it may call: tools/list shows only those, and calling another tool returns INSUFFICIENT_SCOPE. Each tool's scope is listed under Tools; scopes and platform-set restrictions are described in the API authentication docs. API keys and MCP work on every plan, Free included.

Claude (claude.ai, the desktop and mobile apps), ChatGPT and other clients that support MCP OAuth can connect with the URL alone: they open Ampost's sign-in page, where you choose the scopes and, optionally, the platform sets the app may use. Its access tokens work only at /mcp, and you disconnect it under Settings, Connected apps.

The exact command or config for Claude Code, Cursor, VS Code, Codex, the Claude app, ChatGPT and Devin Desktop, with one-click install for Cursor and VS Code, is in Connect Ampost to your AI agent. The generic config:

mcp.json
{
  "mcpServers": {
    "ampost": {
      "type": "http",
      "url": "https://api.ampost.io/mcp",
      "headers": {
        "Authorization": "Bearer amp_live_YOUR_KEY"
      }
    }
  }
}

Use the hosted API domain configuration above. Clients with Streamable HTTP and custom headers need no local wrapper. Dashboard flows remain on https://ampost.io.

Post Creation

Start with platforms_list: it returns each platform's text limit, media rules, required media metadata, placements and the JSON Schema of its options. Then call posts_create with targets (platform sets, each with platform names: instagram, facebook, linkedin, tiktok, threads, twitter, discord, telegram, bluesky, youtube, pinterest), text, optional linkUrl and media, and per-platform options. Ampost picks each platform's account from its platform set, so you never pass account ids. To post to several accounts of the same platform, add a target for each account's set.

  • media items are public https URLs (no localhost, private or internal hosts) with a required type (image or video). TikTok needs width, height and, for video, durationSeconds; every platform's needs are in media.requiredFields.
  • Without scheduledFor the post is a draft: publish it with posts_publish, or pass publishNow: true. A scheduled post's media is copied to Ampost storage right away.
  • Pass a new idempotencyKey (a UUID) to posts_create, posts_import_csv, posts_publish, posts_retry, accounts_connect, platform_sets_create, webhooks_create and feedback_submit, and the same key when you retry a call that timed out: Ampost returns the first result instead of posting twice. Another call with that key gets IDEMPOTENCY_KEY_REUSED.
  • To create many posts, pass CSV text to posts_import_csv: one post per row, in the columns of the dashboard's CSV export. Call it with dryRun: true first, fix the rows it reports, then import with an idempotencyKey.
  • TikTok publishes only with privacy levels the creator allows (see platformMetadata from accounts_get); private-only is normal while a TikTok app is in review. TikTok posts need media.
  • LinkedIn posts publish as the member: no organization pages or analytics.
  • firstComment is posted on each new post after it publishes (a reply on X, Threads and Bluesky; not on TikTok, Telegram, Discord or Pinterest). A failed comment leaves the post published; posts_retry posts only the comment again.
posts_create
{
  "targets": [
    {
      "platforms": [
        "tiktok"
      ]
    }
  ],
  "text": "Behind the scenes #buildinpublic",
  "media": [
    {
      "url": "https://cdn.example.com/launch.mp4",
      "type": "video",
      "mimeType": "video/mp4",
      "sizeBytes": 18200000,
      "width": 1080,
      "height": 1920,
      "durationSeconds": 21
    }
  ],
  "options": {
    "tiktok": {
      "privacyLevel": "SELF_ONLY",
      "disableDuet": true,
      "isAigc": false
    }
  }
}
posts_create: two LinkedIn accounts
{
  "targets": [
    {
      "platformSetId": "0b6e4d2a-9c3f-4e1b-8a7d-5f2c1e9b3d40",
      "platforms": [
        "linkedin"
      ]
    },
    {
      "platformSetId": "9a4f2c7e-1d3b-4a6e-8c5f-2b7d9e1a3c50",
      "platforms": [
        "linkedin"
      ]
    }
  ],
  "text": "We are hiring two engineers. Apply by Friday.",
  "linkUrl": "https://example.com/jobs",
  "publishNow": true
}

The result is the Post: check platformPosts[].status and error for each account, and call posts_retry for failures (all failed accounts, or the accountIds you pass). To take a published post down, call posts_unpublish: each account turns deleted, or keeps an error saying why (Instagram and TikTok have no delete API). To fix the text of a published post, call posts_edit_published with the new text (Facebook, LinkedIn, Telegram and Discord; each edited account's text holds it).

Uploading local files

media in posts_create takes public URLs. When the user has the file on their device, call media_upload_create and show the returned uploadPageUrl: an Ampost page where they upload up to 35 files in a browser. A client that supports URL elicitation (MCP revision 2026-07-28) opens that page for the user after asking them, and leaves uploadPageUrl out of the result. An agent that has the file and can make HTTP requests can instead PUT it to uploadUrl with the returned headers plus x-content-type. Both expire after 60 minutes.

Then call media_upload_status with the uploadId every 3–5 seconds. When it is completed, each item in files is a ready media item:

media_upload_status
{
  "id": "3c7e9a1b-5d2f-4e8a-9b6c-1f3d5a7e9c20",
  "status": "completed",
  "files": [
    {
      "url": "https://abc123.public.blob.vercel-storage.com/uploads/3c7e9a1b-5d2f-4e8a-9b6c-1f3d5a7e9c20/launch.mp4",
      "type": "video",
      "mimeType": "video/mp4",
      "sizeBytes": 18350112,
      "width": 1080,
      "height": 1920,
      "durationSeconds": 28
    }
  ],
  "createdAt": "2026-10-06T12:00:00.000Z",
  "expiresAt": "2026-10-06T13:00:00.000Z",
  "completedAt": "2026-10-06T12:03:41.000Z",
  "error": null
}

Pass those items to posts_create unchanged. Ampost checks each file's bytes and reads width, height and duration from them; images are re-saved without metadata (EXIF, GPS). On failed (a direct upload whose content is not the type it was sent as; the reason is in error) or expired, offer the user a new link.

The part of uploadPageUrl after # is a secret for this upload only. Files that no post uses are deleted 24 hours after the upload was created, by the daily cleanup.

LinkedIn Publishing

LinkedIn posts can be text-only, images, an article (a linkUrl) or one video. options.linkedin takes text, visibility, isReshareDisabledByAuthor, articleTitle, articleDescription and videoTitle. Put alt text on each image as media[].altText. A first comment works like on other platforms. Organization posting is not available, and analytics wait for Ampost's LinkedIn Community Management API access.

posts_create: images
{
  "targets": [
    {
      "platforms": [
        "linkedin"
      ]
    }
  ],
  "text": "Three screens from the new dashboard.",
  "media": [
    {
      "url": "https://cdn.example.com/compose.png",
      "type": "image",
      "altText": "Compose screen"
    },
    {
      "url": "https://cdn.example.com/posts.png",
      "type": "image",
      "altText": "Posts list"
    },
    {
      "url": "https://cdn.example.com/sets.png",
      "type": "image",
      "altText": "Platform sets"
    }
  ],
  "options": {
    "linkedin": {
      "isReshareDisabledByAuthor": false
    }
  }
}
posts_create: article
{
  "targets": [
    {
      "platforms": [
        "linkedin"
      ]
    }
  ],
  "text": "Our API reference is live.",
  "linkUrl": "https://ampost.io/docs",
  "options": {
    "linkedin": {
      "articleTitle": "Ampost API reference",
      "articleDescription": "Endpoints, options and limits for every platform.",
      "visibility": "PUBLIC"
    }
  }
}

Placement

Choose where an Instagram or Facebook post appears with options.instagram.placement or options.facebook.placement:

posts_create
{
  "targets": [
    {
      "platforms": [
        "instagram",
        "facebook"
      ]
    }
  ],
  "text": "New drop today",
  "media": [
    {
      "url": "https://cdn.example.com/drop.jpg",
      "type": "image"
    }
  ],
  "options": {
    "instagram": {
      "placement": "story"
    },
    "facebook": {
      "placement": "feed"
    }
  }
}

Instagram: feed, story, reel, carousel. Facebook: feed, story, reel. Which ones fit depends on the media (a carousel needs 2+ images, a reel one video). placements.byMediaShape in platforms_list lists them, and a mismatch returns INVALID_PLACEMENT with the allowed values in details.available.

Browser-assisted account connection

Call accounts_connect with one of instagram, facebook, linkedin, tiktok, threads, twitter, discord, telegram, bluesky, youtube, pinterest and an optional platformSetId. The tool uses the same service, ownership checks, plan feature, quota checks, expiry semantics, and connectPlatform rate limit as REST.

accounts_connect
{
  "platform": "instagram",
  "platformSetId": "0b6e4d2a-9c3f-4e1b-8a7d-5f2c1e9b3d40"
}

To reconnect an account, revoked or with health.needsReconnect, pass its id; the platform comes from the account. An active account gets refreshed tokens and permissions, a revoked one is reactivated. For a new connection, expectedUsername or expectedPlatformAccountId pins the account the user must sign in to. Signing in to another account fails with ACCOUNT_MISMATCH and changes nothing. On Free, an account connected in another Ampost workspace (or disconnected from one less than 30 days ago) fails with SOCIAL_ACCOUNT_IN_USE.

accounts_connect
{
  "accountId": "c41d8e2f-6a3b-4f9c-b1e5-7d2a9c4e6f80"
}

Show the returned connectUrl to the user and tell them to open it in a browser. Social consent cannot be completed by the agent or inside the conversation. A client on MCP revision 2026-07-28 that supports URL elicitation opens the page for the user instead (after asking them), and the result then has no connectUrl, so the link never enters the conversation. After the user finishes the Ampost-hosted confirmation and provider flow, poll every 2–5 seconds:

accounts_connect_status
{
  "connectRequestId": "11111111-1111-4111-8111-111111111111"
}

Status is exactly one of pending, awaiting_selection, connected, failed, or expired. A connected result includes accountId, which can be passed to accounts_get; a reconnected account keeps its id. On failed or expired, error.message gives the reason and the next step for the user, the same text the hosted page shows. error.code SUPERSEDED means a newer request for the same platform replaced this one, so poll the newer one. A new request also replaces one the user opened but did not finish, so call accounts_connect again when the user lost or closed the page; USER_CANCELLED means the user cancelled or declined consent. Status uses the apiRead rate limit.

Never ask the user to paste an access token, OAuth authorization code, password, state value, URL fragment, or Facebook Page token into chat. The create URL is returned once; status output contains only safe Ampost error codes and never provider credentials or raw provider errors.

accounts_disconnect disconnects an account and revokes Ampost's access at the platform where it can. accounts_set_analytics turns metered X analytics on or off for an X account; ask the user first, since every read counts against the plan's X units. accounts_list_boards and accounts_set_board pick the default board of a Pinterest account (or create one); the connect picks it by itself only when the account has exactly one board. There are no separate profiles (platform sets group accounts), customer callbacks, custom redirects or headless authorization. Facebook Page selection stays in the Ampost-hosted browser UI.

Platform Sets

A platform set groups connected accounts, at most one per platform. Use platform_sets_list, platform_sets_get, platform_sets_create (up to 20 sets), platform_sets_update to rename and platform_sets_delete (revokes the set's accounts; your last set cannot be deleted).

Each of posts_create's targets uses your default set unless it has a platformSetId. List several sets in one post to reach several accounts of the same platform. posts_list and accounts_list filter by set.

Each set has a posting queue: weekly slots in a time zone. Read it with queue_get, set its slots with queue_update (Pro and above) and see the next slots with queue_slots. Pass useQueue: true instead of scheduledFor to posts_create to schedule a post in the set's next free slot.

accounts_list and accounts_get include scopes, token health and public platformMetadata. For TikTok it holds the creator's allowed privacy levels and maximum video duration, so check it before choosing options.tiktok.privacyLevel. accounts_list returns active accounts unless you pass status (revoked or all), so its total for a set matches the set's accountCount.

Analytics

With the analytics:read scope, analytics_posts returns each published post's metrics per account, analytics_overview the totals per account for a date range, and analytics_followers the daily follower history. They cover Facebook Pages, Instagram, Threads, TikTok, Bluesky, YouTube and Pinterest accounts (LinkedIn once its API access is approved), X accounts turned on with accounts_set_analytics (metered in the plan's X units), and Telegram followers; Discord has none (ANALYTICS_NOT_AVAILABLE_ON_PLATFORM); a metric a platform does not report is null.

The numbers are read in the background (accounts about hourly, new posts hourly for 48 hours, then less often) and carry syncedAt. When no matching account has analytics enabled, the tool fails with ANALYTICS_NOT_ENABLED; tell the user why from details.accounts.

Webhooks

With the webhooks:write scope, webhooks_create sends post, account and analytics events to an https URL, signed with the webhook's secret. webhooks_test sends a test event and returns the URL's response, webhooks_deliveries_list shows the delivery log, and webhooks_redeliver sends a past event again.

Give the user the secret to verify signatures in their receiver; only the webhooks_create response (and a rotation with webhooks_update) contains it. The signing steps and code are in the Webhooks section of the API reference. Disconnecting the app turns off the webhooks it created. A webhook with disabledReason: "failing" was turned off after its deliveries kept failing; turn it on with webhooks_update once the receiver works.

Feedback

feedback_submit sends the Ampost team a bug report (kind: "bug") or a suggestion (kind: "suggestion") with a message. It needs no scope, so every key and connected app can call it, up to 10 reports an hour.

When a tool call fails in a way the agent can't explain or fix, report it with request (the tool name as path, method: "MCP" and the arguments as body) and error (code, message and request id). Ampost adds the user's plan and connected accounts, removes fields named like secrets, and returns the report's id.

Post Update

posts_update changes a draft or scheduled post. Pass postId and only the fields to change; media and targets replace the whole list, and options is merged per platform. New targets re-resolve the accounts from their sets (accounts the post keeps keep their results), and scheduledFor: null turns a scheduled post back into a draft.

posts_update (plus postId)
{
  "text": "Launch day (updated): Ampost publishes everywhere from one API.",
  "scheduledFor": "2026-10-09T08:00:00Z"
}

Continue browsing

Related docs and product pages

These pages connect the MCP tool surface back to the REST API, product capabilities, and the original launch context.