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
/mcpThe 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_listList platforms and their capabilities.
GET /api/platforms · scope posts:read
posts_listList posts.
GET /api/posts · scope posts:read
posts_createCreate, schedule or publish a post.
POST /api/posts · scope posts:write
posts_import_csvImport posts from CSV.
POST /api/posts/bulk · scope posts:write
posts_getGet a post.
GET /api/posts/{postId} · scope posts:read
posts_updateUpdate a post.
PATCH /api/posts/{postId} · scope posts:write
posts_deleteDelete a post.
DELETE /api/posts/{postId} · scope posts:write
posts_publishPublish a post now.
POST /api/posts/{postId}/publish · scope posts:write
posts_unpublishUnpublish a post.
POST /api/posts/{postId}/unpublish · scope posts:write
posts_edit_publishedEdit a published post's text.
POST /api/posts/{postId}/edit-published · scope posts:write
posts_retryRetry failed accounts.
POST /api/posts/{postId}/retry · scope posts:write
media_upload_createStart a media upload.
POST /api/media/uploads · scope media:write
media_upload_statusGet a media upload.
GET /api/media/uploads/{uploadId} · scope media:write
accounts_listList connected accounts.
GET /api/accounts · scope accounts:read
accounts_getGet a connected account.
GET /api/accounts/{accountId} · scope accounts:read
accounts_disconnectDisconnect an account.
DELETE /api/accounts/{accountId} · scope accounts:write
accounts_set_analyticsTurn X analytics on or off.
PATCH /api/accounts/{accountId}/analytics · scope accounts:write
accounts_list_boardsList a Pinterest account's boards.
GET /api/accounts/{accountId}/boards · scope accounts:read
accounts_set_boardPick a Pinterest account's default board.
PATCH /api/accounts/{accountId}/board · scope accounts:write
accounts_connectStart connecting an account.
POST /api/accounts/connect · scope accounts:write
accounts_connect_statusGet a connection request.
GET /api/accounts/connect/{connectRequestId} · scope accounts:read
platform_sets_listList platform sets.
GET /api/platform-sets · scope accounts:read
platform_sets_createCreate a platform set.
POST /api/platform-sets · scope accounts:write
platform_sets_getGet a platform set.
GET /api/platform-sets/{platformSetId} · scope accounts:read
platform_sets_updateRename a platform set.
PATCH /api/platform-sets/{platformSetId} · scope accounts:write
platform_sets_deleteDelete a platform set.
DELETE /api/platform-sets/{platformSetId} · scope accounts:write
queue_getGet a posting queue.
GET /api/platform-sets/{platformSetId}/queue · scope posts:read
queue_updateUpdate a posting queue.
PATCH /api/platform-sets/{platformSetId}/queue · scope posts:write
queue_slotsPreview queue slots.
GET /api/platform-sets/{platformSetId}/queue/slots · scope posts:read
analytics_postsPost analytics.
GET /api/analytics/posts · scope analytics:read
analytics_overviewAnalytics overview.
GET /api/analytics/overview · scope analytics:read
analytics_followersFollower history.
GET /api/analytics/followers · scope analytics:read
webhooks_listList webhooks.
GET /api/webhooks/endpoints · scope webhooks:write
webhooks_createCreate a webhook.
POST /api/webhooks/endpoints · scope webhooks:write
webhooks_getGet a webhook.
GET /api/webhooks/endpoints/{webhookId} · scope webhooks:write
webhooks_updateUpdate a webhook.
PATCH /api/webhooks/endpoints/{webhookId} · scope webhooks:write
webhooks_deleteDelete a webhook.
DELETE /api/webhooks/endpoints/{webhookId} · scope webhooks:write
webhooks_testSend a test event.
POST /api/webhooks/endpoints/{webhookId}/test · scope webhooks:write
webhooks_deliveries_listList webhook deliveries.
GET /api/webhooks/endpoints/{webhookId}/deliveries · scope webhooks:write
webhooks_redeliverRedeliver an event.
POST /api/webhooks/endpoints/{webhookId}/deliveries/{deliveryId}/redeliver · scope webhooks:write
docs_searchSearch the docs.
GET /api/docs/search · scope none (any key)
feedback_submitReport 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:
{
"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.
mediaitems are publichttpsURLs (no localhost, private or internal hosts) with a requiredtype(imageorvideo). TikTok needswidth,heightand, for video,durationSeconds; every platform's needs are inmedia.requiredFields.- Without
scheduledForthe post is a draft: publish it withposts_publish, or passpublishNow: true. A scheduled post's media is copied to Ampost storage right away. - Pass a new
idempotencyKey(a UUID) toposts_create,posts_import_csv,posts_publish,posts_retry,accounts_connect,platform_sets_create,webhooks_createandfeedback_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 getsIDEMPOTENCY_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 withdryRun: truefirst, fix the rows it reports, then import with anidempotencyKey. - TikTok publishes only with privacy levels the creator allows (see
platformMetadatafromaccounts_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.
firstCommentis 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_retryposts only the comment again.
{
"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
}
}
}{
"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:
{
"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.
{
"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
}
}
}{
"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:
{
"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.
{
"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.
{
"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:
{
"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.
Docs Search
docs_search answers "how do I…" questions inside the client: pass a query and an optional limit and get the best matching docs sections, each with its title, an excerpt and its URL. It needs no scope, so every key and connected app can call it.
The public docs MCP server needs no key at all. Its tools search, list_pages and get_page read the same sections:
claude mcp add --transport http ampost-docs https://api.ampost.io/mcp/docsFeedback
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.
{
"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.