MCP Server

Schedule posts from Claude, ChatGPT, Cursor, or any MCP-compatible client, over stdio (npm) or HTTP.

Last updated Oct 24, 2026Updated this week

posterly speaks the Model Context Protocol so any MCP-compatible AI client can list connected accounts, schedule posts, and pull analytics on the user's behalf. Two connection options are available, pick the one that fits your client.

New accounts that start from /mcp get a plan plus API & MCP in one Stripe subscription (Starter + API is $10/month, or $96/year from the pricing page) with a 7-day free trial, then billed on the same monthly or yearly cycle as the plan. An account that already had a trial is charged immediately. Trial limits apply: 20 write and 100 read requests per hour, 25 posts created through the API and MCP, and batch posting, AI video, webhooks and OAuth clients unlock when the paid plan starts. After checkout, create a key at Dashboard → API & MCP.

Two connection options

1. Stdio (npm package), desktop AI clients

For Claude Code, Cursor, Windsurf, Cline, Claude Desktop local developer configuration, and other clients that run a local MCP subprocess. Claude chat uses the hosted option below without npm. ChatGPT uses the plugin listing. Developer mode is the advanced alternative on the same hosted URL.

You do not need to install anything globally. Add this to your client's MCP config and let npx run the current server:

{
  "mcpServers": {
    "posterly": {
      "command": "npx",
      "args": ["-y", "posterly-mcp-server@latest"],
      "env": {
        "POSTERLY_API_KEY": "pst_live_your_key_here"
      }
    }
  }
}

Quick setup, copy for your AI: Use Copy setup for your AI at Dashboard → API & MCP. It explains both hosted MCP and local npm setup, then helps your AI choose the option that fits your client. Local npm setup needs an API key; hosted OAuth connectors use posterly sign-in.

If the user has not signed up yet, install the same server without POSTERLY_API_KEY. The public tools get_agent_signup_info, start_signup, and get_signup_session work before a key exists, so the AI can start the 7-day free trial (a card is required, nothing is charged today, and the card is charged on day 8 unless the paid plan starts sooner), send the posterly checkout handoff URL, or complete Checkout only with a reusable card, then poll progress while the user sets a password in the browser. API and MCP stay limited during the trial, and the API add-on is still required. See Pay for posterly from an agent.

2. HTTP, browser-based AI clients

For Claude.ai, Claude Desktop chat connectors, ChatGPT, Cursor in browser, Grok Bot, or any web-based or cloud agent that can't spawn a local process. Add posterly in ChatGPT from the plugin listing. Developer mode is the advanced alternative. Dedicated connect pages: Claude, Add posterly in ChatGPT, Gemini, Cursor, Grok Bot, Poke, Hermes, OpenClaw, Muse, Manus, and Cue. Gemini Spark uses the same URL. OAuth sign-in from gemini.google.com is still being validated. Manus and Cue are Custom MCP by URL. A verified live connect for those clients is still being validated. First-schedule walkthroughs: ChatGPT, Claude, Cursor, Poke, Hermes, OpenClaw, Grok Bot Marketplace. Media bodies: Solving the 4MB MCP limit.

In Claude, open Settings → Connectors → Add custom connector. Name it posterly, paste https://www.poster.ly/api/mcp, then choose Connect, sign in to posterly and press Allow. Leave the advanced OAuth client ID and secret fields empty. No npm install or API key is needed for this OAuth connection. Teammates use the workspace API access their owner enabled.

Use the exact HTTPS URL with www. https://poster.ly/api/mcp returns a 307 redirect to it; some connector clients do not handle that redirect. If consent says the connection request expired, return to the AI client's connector settings and start Connect again. Press Allow once and wait while the connection completes.

  • Endpoint: POST https://www.poster.ly/api/mcp
  • Wire format: JSON-RPC 2.0 (single request, no SSE or streamable HTTP in v1)
  • Authentication: OAuth sign-in for hosted custom connectors, or Authorization: Bearer pst_live_your_key_here in clients that support custom headers
  • Capability hint: GET /api/mcp returns server info without auth.
  • Muse Custom Connector: Connect posterly to Muse. OpenAPI plus Bearer is the primary path. Hosted MCP is optional. If Muse requires streamable HTTP, stay on OpenAPI and treat hosted MCP as a known gap.
  • Manus and Cue: Manus and Cue. Custom MCP by URL at https://www.poster.ly/api/mcp (www required). OAuth is preferred, with Authorization Bearer pst_live_… as the fallback. This is not a directory listing. A verified live connect is still being validated because v1 is a JSON-RPC 2.0 single POST, with no SSE or streamable HTTP.

Quick smoke test from any terminal:

curl -s https://www.poster.ly/api/mcp \
  -H "Authorization: Bearer pst_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Both options use the same API access and route-aware API limits. Hosted OAuth creates a token after browser consent; local npm and custom-header connections use an API key. They expose the same authenticated toolset, but media upload ergonomics differ by transport because hosted HTTP MCP tool calls are JSON only.

Create-post calls are limited to 100 requests per hour per key. This does not mean you can only schedule 100 posts per hour: use create_posts_batch to create up to 25 posts in one confirmed request. Media writes and read-only calls have separate higher limits.

User-created API keys are tier-based: Starter 1, Pro 2, Power 3, Agency 4. Keys can also be scoped to one workspace; connection handoff and webhook tools use the scoped workspace automatically and reject a different workspace_id.

Managed connection through Smithery

Smithery provides a managed connection to the same hosted HTTP MCP endpoint. It uses posterly OAuth, so users authorize their own account without pasting a posterly API key into Smithery.

  1. Open the posterly server on Smithery.
  2. Add or connect it to your Smithery toolbox.
  3. Sign in to posterly, or create an account if needed.
  4. Review the requested account, post, media, and analytics scopes.
  5. Select Allow, then return to Smithery and test with whoami or list_accounts.

Each OAuth connection belongs to the posterly user who approved it. Other Smithery users cannot access that user's accounts, posts, media, or analytics. An active posterly plan plus the API/MCP add-on is still required; Smithery does not provide free posterly API access.

For permission explanations, example prompts, and troubleshooting, read the Smithery social media MCP setup guide.

Media upload: where the 4MB limit exists, and how to get past it

Vercel caps request bodies to posterly API routes at about 4MB. Hosted MCP (POST /api/mcp) is JSON on that same path, so a base64 file inside upload_media counts against it. Base64 also inflates the file, so hosted upload_media is only for small images. Our relay cap is 5MB decoded; treat anything larger as signed upload.

This is not an MCP-protocol file limit, and it is not posterly's plan storage cap. It is the function body on Vercel.

PathHits the ~4MB Vercel body?What to use it for
Hosted MCP upload_media (base64 JSON to /api/mcp)YesSmall images only
REST POST /api/v1/media/upload (base64)YesSmall images only
Legacy PUT /api/v1/media/signed-upload/uploadYesDo not use. Returns FUNCTION_PAYLOAD_TOO_LARGE on large videos
upload_media with url / create_post with a third-party media_urlThe JSON call is small. posterly then fetches the URL on the serverSmall public files. About 5MB on the relay path, 60MB remote-fetch hard cap. Large R2/S3 URLs fail with remote_media_too_large
MCP create_signed_upload, then PUT bytes to upload_urlNo. The PUT goes to object storage (R2/Supabase), not VercelVideos and large images. Plan caps: Starter 500MB, Pro 750MB, Power 1GB, Agency 4GB
MCP create_media_drop, human opens /drop/<token>No. The browser PUTs to object storageChatGPT web, Claude.ai, and Gemini web laptop files. Agent then calls list_media
Stdio upload_media with file_pathNo. The npm server reads the disk and uses signed upload for large filesCursor, Claude Code, Claude Desktop, Codex, Hermes: give a real local path
CLI media:signed-upload then PUTNoScripts and coding agents that can HTTP PUT
Dashboard composer and Media LibraryNo. They already use signed / direct storageHumans in the app

create_signed_upload is the bypass when the agent can PUT the file itself (know filename, content_type, and exact size, then PUT raw bytes to upload_url with the returned headers, then pass public_url to create_post). It handles 1 to 20 files per call: pass files and each file gets its own slot or its own error, and slots expire in 15 minutes. Pass pin_content_type: true to lock each slot to the content_type you declare: the PUT must then send exactly the returned Content-Type header or storage answers 403 (the slot says content_type_pinned: true; posterly may turn pinning on by default in the future, so always send the returned Content-Type). The npm stdio upload_media with file_path already sends the returned headers as is. It also takes a PDF (content_type application/pdf, filename ending in .pdf, up to 50MB): to publish it, call create_post with post_type document, media_url set to the public_url, and a document_title in settings. Only LinkedIn publishes documents. Cursor, Claude Code, and Codex can do that.

ChatGPT web, Claude.ai, and Gemini web cannot PUT a laptop file, and chat paperclips never reach MCP. For those clients call create_media_drop, send the user drop_url (https://www.poster.ly/drop/<token>), wait, then call list_media. One drop link takes up to 20 files. Accepted: JPEG, PNG, GIF, WebP, MP4, MOV, WebM, and PDF up to 50MB (a PDF is for LinkedIn document posts). HEIC is not accepted: export or convert it to JPG or PNG first. Plan video caps still apply: Starter 500MB, Pro 750MB, Power 1GB, Agency 4GB.

Why the 4MB number exists, and which tool to call on each runtime: Solving the 4MB MCP limit.

Local stdio MCP can read a local file path and automatically use signed upload for larger media. Use this when the agent is on the same machine as the file.

Hosted HTTP MCP has no binary channel. For larger media on a client that can PUT:

  • Call create_signed_upload with filename, content_type, and size, or with files (up to 20) to get one slot per file in a single call.
  • PUT the raw file bytes to the returned upload_url (object storage). Do not PUT to a poster.ly API route.
  • Pass public_url into validate_post / create_post.

Do not pass a 500MB to 2GB Cloudflare R2, S3, or other remote URL straight to create_post. That fetch fails with remote_media_too_large. Download it (or PUT it) via signed upload instead.

Uploaded media is analyzed automatically in the background. If a video can never reach a selected platform (for example, it is longer than the platform allows, or too large for the platform's file size limit), create_post rejects that combination with an actionable message such as "This video is too large for Telegram (50 MB multipart limit). Export it under 50 MB to include Telegram." Relay the message to the user. Colour, geometry, and scan-mode conversions (and container remuxes) are handled automatically at publish, so media that only needs one of those is never rejected for that reason; duration and file size limits still apply.

Incomplete or non-standard colour tags do not reject create_post or validate_post. validate_post / dry_run: true may include a video_requires_color_safe_pipeline warning. Missing or non-square pixel geometry also does not reject create. validate_post / dry_run: true may include a video_requires_geometry_normalization warning when the file is known to use non-square pixels. An unverified scan tag (progressive versus interlaced) also does not reject create or publish. At publish time, posterly converts the video when the analysis allows, or publishes the original file. A converted file that is otherwise usable is not rejected only because its measured frame rate differs slightly from the planned output. You do not need to re-export Rec.709, a square-pixel file, or a tagged progressive MP4 before including Facebook or Instagram. Compatible originals, remuxes, and video-copy repairs still pass. See Video colour compatibility, Video pixel geometry, and Video scan mode and converted-file checks.

create_post and validate_post still reject media combinations that no platform call could satisfy: a video that is too long or below a platform minimum, a PDF sent anywhere other than LinkedIn, an .avi or .mkv video sent to Instagram, an Instagram feed photo (or still carousel slide) taller than 9:16 or wider than 1.91:1 when posterly knows the image's dimensions (publish-time preparation is the final check), a YouTube still photo (JPEG, PNG, WebP, GIF, and other image files; message: "YouTube needs a video file, not a photo. Upload a video and try again.", code youtube_still_image_unsupported), and an Instagram account that is missing its business account id (which needs reconnecting in posterly). Relay those messages rather than retrying. Stories, Reels, and Story Series stills are not held to those feed bounds.

The .mov, .webm and .m4v containers remain accepted at creation. Whether a repair is needed depends on the inspected input and destination; compatible originals can pass through unchanged.

Agent response style

MCP signup and connect tools return user-facing guidance by default. Agents should narrate progress in plain language, send the secure browser links, and avoid showing raw curl commands, HTTP payloads, or JSON unless the user asks for debugging.

Use debug: true only when you deliberately need the raw signup or connect response for troubleshooting. The tools that support this are start_signup, get_signup_session, create_connect_session, list_platforms with view: "connect_link", and list_accounts with view: "connect_session".

Post tools also return posterly dashboard links. After creating, listing, reading, or deleting posts, share the returned View in posterly link so the user lands in the right dashboard view. Current-month scheduled posts open in Calendar with the post selected; general/future post lists fall back to Table. Reading one post names its platform and account, and when a publish failed, the reason. The missing view lists each gap and how to fix it. A post that belongs to a group shows that group id, the same id a group update or delete already accepts. Listing posts also includes a link back to posterly, and the platform when the post has one. A team list includes each member's user id when the workspace API sends it, so it can be matched to the account that created a post.

Auto-discovery for agents

posterly publishes the standard .well-known discovery endpoints so agents can find everything programmatically:

URLPurpose
/.well-known/mcp/server-card.jsonMCP server card, describes both connection options + tools
/.well-known/api-catalogRFC 9727 linkset describing the REST API surface
/.well-known/oauth-authorization-serverRFC 8414, authorize/token/register endpoints, refresh-token grant
/.well-known/oauth-protected-resourceRFC 9728, bearer methods, scopes, where to mint tokens
/.well-known/agent-skills/index.jsonCloudflare Agent Skills index, links to SKILL.md files
/api/openapiFull OpenAPI 3.1 spec for the REST API

Public marketing pages also emit RFC 8288 Link headers pointing at all of the above, so agents that crawl https://www.poster.ly discover the API surface without any prior configuration.

ChatGPT Apps and the OpenAI plugin directory verify control of poster.ly by fetching /.well-known/openai-apps-challenge. That URL is a domain-verification check for review, not a public listing.

Auth & billing handoff (RFC 6750)

When an agent calls a tool with a missing/invalid token or no API add-on, posterly returns a WWW-Authenticate header that tells the agent exactly what to do, surface this to the user and they can subscribe inside the agent conversation.

  • 401 invalid_token, WWW-Authenticate: Bearer ... token_uri="https://www.poster.ly/dashboard/api"
  • 403 insufficient_subscription, WWW-Authenticate: Bearer ... error_uri="https://www.poster.ly/dashboard/api?upgrade=api_addon"
  • 403 trial_feature_locked, the workspace owner is on the free trial and the tool needs batch posting, AI video generation, webhooks or OAuth clients. Starting the paid plan unlocks it; retrying does not help.
  • 403 trial_post_cap_reached, the free trial's 25 posts created through the API and MCP are used up. Starting the paid plan lifts the cap.
  • 403 api_access_not_granted, the token is bound to a workspace where the owner has not turned on API access for this teammate (or the owner's add-on lapsed). Ask the workspace owner to turn it on in Team settings.
  • 400 workspace_id_required, the connection can act in more than one workspace and the call did not say which. Pass workspace_id (from whoami). generate_image, generate_video, upload_media, create_signed_upload and create_media_drop take an optional workspace_id for this.
  • 403 insufficient_scope, WWW-Authenticate: Bearer ... scope="posts:write"
  • 403 origin_not_allowed, browser caller from an Origin not on the trusted MCP host list (Claude, ChatGPT, Gemini, posterly, localhost, plus MCP_TRUSTED_ORIGINS).
  • 429, body includes retry_after, limit, remaining, and reset, plus a Retry-After header. On the free trial the hourly limits are 20 write and 100 read requests.

Hosted MCP clients (Claude.ai, ChatGPT, and similar)

Browser-hosted MCP clients can complete a full OAuth connect flow without a human allowlisting the app first. Two paths are supported:

  • Client ID Metadata Documents, for hosts that send an HTTPS client_id pointing at a published metadata document. Claude.ai / Claude Connectors uses this (https://claude.ai/oauth/mcp-oauth-client-metadata, callback https://claude.ai/api/mcp/auth_callback, token auth none). ChatGPT custom connectors publish a per-connector document (https://chatgpt.com/oauth/.../client.json, callback https://chatgpt.com/connector/oauth/...) with a private_key_jwt preference, token_endpoint_auth_methods_supported of none and private_key_jwt, and a jwks_uri on chatgpt.com. posterly fetches the document from a small allowlist of known connector hosts (Claude, ChatGPT, Cursor, Smithery, and accountlinking.google.com for Gemini Spark) and verifies a signed token request when present, or accepts PKCE-only when the document also lists none. This is not Dynamic Client Registration.
  • RFC 7591 Dynamic Client Registration, for hosts that register at POST /api/oauth/register. Send a JSON metadata document (client_name, redirect_uris, scope) and receive a client_id immediately. DCR clients stay public PKCE (none); they do not use private_key_jwt.

Gemini web sign-in and tool discovery are still being validated. The experimental setup uses the hosted URL https://www.poster.ly/api/mcp. Gemini may present a Client ID Metadata Document whose client_id is an HTTPS URL on accountlinking.google.com (for example https://accountlinking.google.com/clientidmetadata/...). The redirect URI in that document can sit on another Google host, including googleusercontent.com, and is matched exactly. The same apps can also register with Dynamic Client Registration, which already accepts those googleusercontent.com redirect URIs. The exact browser origins https://gemini.google.com and https://www.gemini.google.com are trusted. These allowlists do not establish a successful end-to-end connection. google.com is not opened as a suffix: when the document uses private_key_jwt, its jwks_uri must stay on accountlinking.google.com or a subdomain of it, the same host-family rule ChatGPT uses for chatgpt.com.

Both paths then use the standard authorization-code + PKCE flow at https://www.poster.ly/oauth/authorize.

  • If you are new and still need a plan, posterly sends you to choose a plan and then returns you to the consent screen. The API add-on is still required before the access token activates.
  • The consent screen flags self-registered (DCR) clients so the user can decide whether to trust the source.
  • Access tokens issued via DCR or a CIMD client that authenticates with private_key_jwt live 1 hour, with a rotating refresh token (30 days; reuse revokes the chain). Claude CIMD (none), and ChatGPT when it uses none, keep a long-lived access token and no refresh grant.
  • grant_type=refresh_token at /api/oauth/token to renew, using the method the client actually presented (signed assertion when present, otherwise none if the document supports it).

The discovery document at /.well-known/oauth-authorization-server advertises client_id_metadata_document_supported, registration_endpoint, token_endpoint, token_endpoint_auth_methods_supported (none and private_key_jwt), and the supported grants so MCP hosts can wire this up automatically. API keys still work for stdio MCP and scripts.

Available tools

The local stdio server exposes 73 tools covering 115 actions. The public setup tools work without POSTERLY_API_KEY; everything else needs a posterly API key. The HTTP MCP connection is authenticated and exposes 70 tools covering 112 actions (the same catalog minus the three signup tools).

Older tool names were merged into the tools below and keep working for now. See MCP tool changes for the full old-to-new table, the date the old names may stop working, and what to update in scripts and permission settings.

How merged tools work

Many tools pick what to do with one parameter:

  • view on read tools, for example list_brands with view: "profile".
  • action on write tools, for example manage_comment with action: "reply".
  • type on list_jobs (image or video).

A few tools pick their mode from which parameters you pass: update_post changes the status when you pass status, delete_post deletes a group when you pass group_id, upload_media copies a public file when you pass url and deletes one library file when you pass asset_id (preview first), and list_comments / list_conversations return one item when you pass its ID. Every tool description names all of its views and actions, so the AI knows what is inside.

Preview then confirm

Actions that post publicly or delete things work in two steps. The first call, without confirm, changes nothing: it returns a plain summary of what will happen and a preview_id. Show it to the user. Only after they approve, call again with the same fields plus confirm: true and that preview_id.

  • A preview expires after 10 minutes and only works for exactly the same fields and the same user.
  • Public posts (a comment reply, a DM, a Google review reply, new Google profile media) can be confirmed once, so a retry never posts twice.
  • Other writes keep a single confirm: true after explicit user confirmation.

Tool sets

Every tool belongs to one set: core, creative, inbox, google_business, admin, and billing, plus signup on the local npm package (always on). Every set is listed by default, so you do not need to do anything. To load fewer tools:

  • Stdio (npm): set POSTERLY_TOOLSETS=core,inbox in the server's env (or pass --toolsets=core,inbox).
  • HTTP with an API key in the Authorization header: use https://www.poster.ly/api/mcp?toolsets=core,inbox.
  • OAuth connectors (Claude.ai, ChatGPT, Gemini Spark) always get every set.

Sets only change which tools are listed. Any tool can still be called by name, and whoami mentions the active sets when a filter is on.

Tool reference

Every tool, grouped by set, with its views or actions.

Core (core)

Accounts, posting, media, schedule, analytics, and support. What most assistants need.

ToolWhat it does
disconnect_accountDisconnect an account
connect_accountConnect with credentials
create_connect_sessionStart a connect session
trigger_platform_helperRun a platform helper
ask_supportAsk support
find_available_slotFind a free slot
submit_agent_feedbackAgent feedback
submit_product_feedbackProduct feedback
validate_postValidate a post
create_postCreate a post
create_posts_batchCreate posts in bulk
get_x_posting_quotaX posting quota
create_signed_uploadSigned upload link
create_media_dropMedia drop link
list_mediaMedia library
generate_captionsGenerate captions
list_activityActivity feed
get_creditsAI credits
get_updatesProduct updates
posterly_statusPublishing status

whoami: Who am I

ViewWhat it does
identity (default)You, your scopes, and your workspaces
statusMCP server and connection status

list_accounts: Connected accounts

ViewWhat it does
accounts (default)Connected accounts
learned_voiceLearned voice of an account
performance_profilePerformance profile of an account
connect_sessionCheck a connect session

list_platforms: Platforms

ViewWhat it does
platforms (default)Supported platforms
schemaPlatform or account schema
connect_linkConnection links

list_brands: Brands

ViewWhat it does
brands (default)All brands
brandOne brand
accountsBrand accounts
profileBrand profile

manage_brand: Create, update, or archive a brand

create and update need an admin, and confirm: true only after the user agrees. archive needs an admin: call it once without confirm, show the preview, then call again with confirm: true and that preview_id. Archiving takes the brand off the active list. Connected accounts stay connected, and scheduled posts are not cancelled: they still publish.

list_posts: Posts

ViewWhat it does
list (default)Posts, one page at a time
countsPost counts by day, status, account, and brand

get_post: One post

ViewWhat it does
post (default)One post, including platform, account, a link back to posterly, and the failure reason when a publish failed
missingEach missing field and how to fix it

update_post: Edit a post

Mode (picked by params)What it does
edit (default)Edit caption, media, schedule, or settings. If the post's status or pending publish changes while the update runs (for example during a video check), it answers post_changed; reload the post with get_post and retry
status (pass status)Pause, resume, schedule, or move to draft
release_id (pass release_id)Set a release or group ID
approval (pass approval_action)Request, approve, request changes, or reject client review (owner or admin only)

delete_post: Delete posts

Mode (picked by params)What it does
post (default)Delete one post. Choose delete_mode: posterly keeps live content; platform also requires confirm_published
group (pass group_id)Delete a post group (preview then confirm). Never deletes published posts

upload_media: Upload media

Mode (picked by params)What it does
file (default)Upload a file
url (pass url)Upload from a URL

list_analytics: Analytics

ViewWhat it does
accountsAccount analytics
postsPost analytics
insightsPost insights

Creative (creative)

AI image and video generation, jobs, and post suggestions.

ToolWhat it does
list_post_suggestionsPost suggestions
dismiss_suggestionDismiss a suggestion
get_video_optionsVideo options
run_video_functionVideo helper
generate_videoGenerate a video
generate_imageGenerate an image

list_jobs: AI jobs

TypeWhat it does
imageImage jobs
videoVideo jobs

Inbox (inbox)

Social inbox comments and DMs.

list_conversations: Conversations

Mode (picked by params)What it does
list (default)List conversations
conversation (pass conversation_id)One conversation with its messages

manage_conversation: Manage conversations

ActionWhat it does
sendSend a DM (preview then confirm)
syncSync the inbox

list_comments: Comments

Mode (picked by params)What it does
list (default)List comments
comment (pass comment_id)One comment with its replies

manage_comment: Manage comments

ActionWhat it does
replyReply to a comment (preview then confirm)
updateHide or mark read
deleteDelete a comment (preview then confirm)

Google Business (google_business)

Google Business Profile reviews, media, and audits. Limits, example prompts, and connection notes are on the Google Business Profile MCP connector.

ToolWhat it does
audit_google_business_profileAudit a Google profile
list_google_business_mediaGoogle profile media
list_google_business_service_areasGoogle service areas
manage_google_business_service_areaUpdate Google service areas

list_google_business_service_areas: Google service areas

ViewWhat it does
areas (default)Towns and postcodes already set on one location
placesSearch for a town or postcode to add

manage_google_business_service_area: Update Google service areas

ActionWhat it does
updateReplace the service area list (preview then confirm)

list_google_business_reviews: Google reviews

ViewWhat it does
reviews (default)Google reviews, each with review_name for a reply
review_linkGoogle review link

manage_google_business_review: Manage Google reviews

ActionWhat it does
suggest_replySuggest a reply (never posts)
replyReply to a review (preview then confirm)
delete_replyDelete a review reply (preview then confirm)

Each review from list_google_business_reviews includes review_name (accounts/{account}/locations/{location}/reviews/{review}) plus account_id and location_id when known. Pass that review_name with account_id (or location_id) to manage_google_business_review reply or delete_reply. Do not pass the Place ID from view: "review_link".

manage_google_business_media: Manage Google profile media

ActionWhat it does
addAdd a photo or video (preview then confirm)
deleteDelete a photo or video (preview then confirm)

list_google_business_locations: Google location hours

Reads regular opening hours and attributes for one location. Stays off until location edits are switched on.

manage_google_business_location: Manage a Google location

ActionWhat it does
update_hoursUpdate regular hours (preview then confirm)
update_attributesUpdate attributes (preview then confirm)

Admin (admin)

API keys, OAuth clients, webhooks, workspace members, and the publishing pause.

ToolWhat it does
create_api_keyCreate an API key
delete_api_keyRevoke an API key
list_workspace_membersWorkspace members
list_publishing_pausesPublishing pause: whether the workspace or any brand is paused, and what you may do
list_oauth_clientsOAuth clients
list_webhooksWebhooks
create_webhookCreate a webhook

list_webhooks: Webhooks

ViewWhat it does
webhooks (default)Webhook subscriptions
deliveriesRecent delivery attempts for one webhook. Stays off until delivery history is switched on

manage_workspace_member: Manage a workspace member

ActionWhat it does
inviteInvite a colleague (preview then confirm)
updateChange role or brands, or turn a teammate's API and AI access on or off (preview then confirm)
removeRemove a member or invite (preview then confirm)

manage_publishing_pause: Pause or resume publishing

ActionWhat it does
pausePause publishing for the workspace or one brand: call without confirm for a preview of what it holds, then confirm: true after the user agrees (publisher or higher)
resumeResume publishing: future posts are scheduled again, posts that missed their time stay paused and are listed (admin or higher)
publish_missedPublish missed posts now, with confirm: true after the user agrees (admin or higher)

manage_oauth_client: Manage an OAuth client

ActionWhat it does
createCreate an OAuth client
updateUpdate an OAuth client
deleteDelete an OAuth client (preview then confirm)

manage_webhook: Manage a webhook

ActionWhat it does
updateUpdate a webhook
deleteDelete a webhook (preview then confirm)
testSend a test delivery

Billing (billing)

Subscription changes and Agency extra accounts.

ToolWhat it does
get_subscriptionSubscription, plans above yours with prices, extra accounts (Agency owners) and the exact next steps
cancel_subscriptionCancel subscription
pause_subscriptionPause subscription
resume_subscriptionResume subscription
downgrade_subscriptionDowngrade subscription

upgrade_subscription: Upgrade subscription

ActionWhat it does
upgradeMove to a higher plan: quote the exact charge, then upgrade after confirm: true with the preview_id (preview then confirm)

manage_extra_account: Buy or remove extra accounts

ActionWhat it does
setSet extra accounts: quote the exact charge, then buy, raise, lower, or remove them after confirm: true with the preview_id (preview then confirm)

manage_x_posting_addon: Buy an X posting add-on

ActionWhat it does
buyQuote the monthly price, then open Stripe Checkout after the user agrees. Nothing is charged until that page is paid. An add-on that is already on opens the billing page for the owner instead.

Signup (signup)

Pre-auth signup for brand-new users. Local npm package only; always on.

ToolWhat it does
get_agent_signup_infoSignup info for agents
start_signupStart a free trial
get_signup_sessionCheck a signup session

Tool notes

Setup and status

  • whoami (default view) shows the user, workspaces, and scopes your key can access. view: "status" shows the installed server version, latest npm version, MCP endpoint health, API auth health, and update guidance, and works before POSTERLY_API_KEY is set. It does not report posterly's publishing health; use posterly_status for that.
  • get_agent_signup_info explains the safe agent-led signup flow. start_signup starts the card-backed 7-day free trial and returns a poll URL, a browser Checkout handoff, and a payment object (trial length, whether anything is charged today, and agent-wallet instructions). Agent-card pay is not available during a trial. get_signup_session polls checkout, card save, password, and agent-access status.

Accounts, connections, and keys

  • list_accounts shows connected social accounts (pass account_id for one). Account and post ids are plain numbers, with no thousands separators. A number copied with commas, such as 1,961, is still accepted. view: "learned_voice" reads the voice posterly learned from one account's real published captions (the "learned captions" on the dashboard): a narrative summary, voice traits, style guidelines, and observed posting habits; read-only, it never overwrites the saved brand profile. view: "performance_profile" reads the profile posterly derives from the last 90 days of per-post analytics (top formats, timing, caption-length patterns, an engagement-rate trend, and a narrative summary; Pro plan or higher). view: "connect_session" polls connection progress while the user logs in, approves OAuth, or enters credentials.
  • list_accounts shows connected social accounts (pass account_id for one). view: "learned_voice" reads the voice posterly learned from one account's real published captions (the "learned captions" on the dashboard): a narrative summary, voice traits, style guidelines, and observed posting habits; read-only, it never overwrites the saved brand profile. view: "performance_profile" reads the profile posterly derives from the last 90 days of per-post analytics (top formats, timing, caption-length patterns, an engagement-rate trend, and a narrative summary; Pro plan or higher). view: "connect_session" polls connection progress while the user logs in, approves OAuth, or enters credentials. Each account row also says whether the connection is fine or needs a reconnect, with a short reason and a date, and never a token. Pass connection: "needs_reconnect" to see only those accounts. The connect page in the dashboard already shows the same reconnect prompt.
  • disconnect_account disconnects a connected social account after explicit confirmation. It preserves reconnect metadata, archives Instagram analytics, and emits account.disconnected webhooks. Scheduled posts for that account are paused rather than left scheduled to fail at publish time.
  • list_platforms inspects supported integrations, post types, media limits, analytics support, and planned providers. view: "schema" returns the settings fields for one connected account_id (preferred, with connection-specific capabilities) or one platform. view: "connect_link" inspects dashboard connection links, provider readiness, required scopes, and connected account counts; pass target for one connection target.
  • connect_account connects a credential-based account (telegram, bluesky, discord, wordpress, devto, hashnode, lemmy) directly, no browser session needed. create_connect_session creates a guided browser handoff for connecting a social account.
  • trigger_platform_helper runs helper discovery such as pinterest.boards, youtube.playlists, tiktok.creator_info, linkedin.recent_mentions, or x.quota.
  • create_api_key creates a new scoped API key after explicit confirmation. The calling key must be a dashboard-created key, and new scopes cannot exceed the calling key. delete_api_key revokes a user-created API key after explicit confirmation; it refuses to revoke the key currently authenticating the request. whoami with include: "api_keys" lists the keys on this account: name, the start of the key, what it can do, when it was created, and when it was last used. The secret is never returned. Managed assistant connections and OAuth connections are left off that list. A key that is locked to one workspace only sees keys for that workspace. The API keys page shows the same permissions, and asks you to confirm before a key is revoked.
  • trigger_platform_helper runs helper discovery such as pinterest.boards, youtube.playlists, tiktok.creator_info, linkedin.recent_mentions, linkedin.company_lookup, or x.quota. YouTube playlists and LinkedIn company lookup stay off until they are switched on. Company lookup uses the organization name search the composer already uses.
  • create_api_key creates a new scoped API key after explicit confirmation. The calling key must be a dashboard-created key, and new scopes cannot exceed the calling key. delete_api_key revokes a user-created API key after explicit confirmation; it refuses to revoke the key currently authenticating the request.
  • list_oauth_clients and manage_oauth_client manage self-serve public OAuth 2.1 + PKCE developer clients (create and update after confirmation; delete with preview then confirm).
  • list_workspace_members lists the people in a workspace: members with their role, status, brand access and API access, pending invites, and seats in use (owners and admins also see each member's key count, connected AI apps and last use). manage_workspace_member invites, updates, or removes a colleague (action: invite | update | remove); action: update with member_id and api_access (on its own) turns a teammate's API and AI access on or off. The first call always returns a preview and changes nothing. Both need the "Manage team" permission; see Managing your team.

Brands

  • list_brands shows brands/clients in the current workspace context. view: "brand" inspects one brand, view: "accounts" shows the social accounts assigned to it, and view: "profile" reads its saved brand voice/profile, including learned_voice from each connected account's real published captions. manage_brand creates or updates a brand (admin only, after confirmation) and archives one (admin only, preview then confirm). Archiving does not disconnect accounts or cancel scheduled posts.

Posts

  • list_posts and get_post browse scheduled content; get_post with view: "missing" inspects missing content/settings before publish. To check a month, call list_posts with scheduled_from and scheduled_to (plain dates like 2026-10-01 and 2026-10-31; the end date includes its whole day) and timezone (for example Europe/London); with a range it lists oldest first, up to limit posts (default 20, max 200) per call. Every answer says whether all matching posts are shown. If it says more are available, call again with the offset it gives until it says complete, before concluding a client is light or a day is empty. For "how many" questions, view: "counts" returns exact totals per day, status, account, and brand for the same filters (range required, up to 366 days). list_activity, list_media, list_post_suggestions, list_comments, list_conversations, and get_updates also say when there is more and take offset. list_posts status accepts every stored status, including publishing, paused, paused_due_to_token, and paused_due_to_billing. A post that missed its time while publishing was paused stays paused; missed is not its own status. Filter list_posts with approval_status or approval_waiting_on (client or team). Each post includes approval.pending_client_approval, true only while the overall status is pending_client. Each post also shows when it was created (an ISO 8601 time in UTC) and who created it: the teammate's name and the channel, such as Dashboard, REST API, MCP, or Bulk Upload. Older posts made before the channel was recorded show it as unknown. These return posts in every workspace you belong to, teammates' posts included, per your role there (a brand-locked seat only sees its assigned brands); pass workspace_id to scope to one workspace. A draft any editor skipped in the Content Engine review queue never appears in list_posts, and get_post returns a not-found error for one; if it is ever scheduled by some other path, it is reverted to a draft at publish time rather than published. They do not return a recovery-action field. For status=failed or paused-for-reconnect posts, inspect the post and list_activity, then open it in posterly. The fail/paused banner leads with What to do. Do not republish reconnect, re-upload, or edit-and-retry failures (expired token, missing media, Google Business location not found, invalid YouTube keywords, X URL-cost skip) until the user has done that action. Temporary wait cases, such as Facebook Story/Reel/video ingest, may already be retrying.
  • list_posts and get_post browse scheduled content; get_post with view: "missing" inspects missing content/settings before publish. To check a month, call list_posts with scheduled_from and scheduled_to (plain dates like 2026-10-01 and 2026-10-31; the end date includes its whole day) and timezone (for example Europe/London); with a range it lists oldest first, up to limit posts (default 20, max 200) per call. Every answer says whether all matching posts are shown. If it says more are available, call again with the offset it gives until it says complete, before concluding a client is light or a day is empty. For "how many" questions, view: "counts" returns exact totals per day, status, account, and brand for the same filters (range required, up to 366 days). list_activity, list_media, list_post_suggestions, list_comments, list_conversations, and get_updates also say when there is more and take offset. Filter list_posts with approval_status or approval_waiting_on (client or team). Pass group_id for one caller group, or release_id for one release. status=missed lists posts that failed, posts still scheduled after their time, and posts a publishing pause held past their time (the ones publish_missed can post). The calendar, list, and table have the same Missed filter. Each post includes approval.pending_client_approval, true only while the overall status is pending_client. Each post also shows when it was created (an ISO 8601 time in UTC) and who created it: the teammate's name and the channel, such as Dashboard, REST API, MCP, or Bulk Upload. Older posts made before the channel was recorded show it as unknown. These return posts in every workspace you belong to, teammates' posts included, per your role there (a brand-locked seat only sees its assigned brands); pass workspace_id to scope to one workspace. A draft any editor skipped in the Content Engine review queue never appears in list_posts, and get_post returns a not-found error for one; if it is ever scheduled by some other path, it is reverted to a draft at publish time rather than published. They do not return a recovery-action field. For status=failed or paused-for-reconnect posts, inspect the post and list_activity, then open it in posterly. The fail/paused banner leads with What to do. Do not republish reconnect, re-upload, or edit-and-retry failures (expired token, missing media, Google Business location not found, invalid YouTube keywords, X URL-cost skip) until the user has done that action. Temporary wait cases, such as Facebook Story/Reel/video ingest, may already be retrying.
  • ask_support asks authenticated docs-backed support questions with read-only account/post diagnostics. If the user asks for a human, agent, real person, or an escalation, Support AI stops the docs answer and files one ticket for that conversation. A person replies by email to the signed-in account address, not an address in the message. Asking again in that conversation does not open another ticket, and at most three conversations a day are passed on. The same docs answer is not sent again in that conversation, and after two replies that do not settle the topic Support offers a person. Agent-initiated handoff still uses request_human plus confirm_escalation.
  • create_post schedules or immediately publishes one post. Pass thread_posts: string[] (2+ entries) for X or Threads chains; when account_id is set, platform is optional because the account already names the network. Pass platform_settings for platform-specific composer controls. For media, pass URLs returned by the media upload tools. A successful create can still come back with a non-blocking warnings entry; the post was created either way. Today the only one is TikTok's pending-inbox cap: a post sent to the TikTok inbox (no direct-post privacy_level) warns when the account already has around 5 pending shares due to land around the same time, since TikTok only holds about 5 at once. Relay the warning rather than treating the create as failed.
  • validate_post validates the complete payload without creating a post, copying remote media, consuming post-item/storage quota, reserving X quota, emitting webhooks, or enqueueing publication. Resolve warnings, show the final preview, and ask for explicit confirmation before calling create_post.
  • create_posts_batch creates up to 25 confirmed posts in one API request. Each item accepts the same fields as create_post, and partial failures return the item index. Third-party media URLs are copied into posterly storage before each post is saved. Any item-level warnings (same shape as create_post) come back alongside that item.
  • update_post edits a scheduled or draft post. It does not move the post onto another account, and it does not edit an X or Threads thread. With status it pauses, resumes, schedules, or drafts it, or with release_id repairs release/group metadata, always after confirmation. approval_action (request, approve, request_changes, or reject) changes client approval. Only a workspace owner or admin may do that, and the actor is the API key user, never a brand guest. request emails the brand reviewers. approve, request_changes, and reject are team-side decisions that reuse the same approval state as the client portal. request_changes needs approval_note. Optional approval_axis is asset or caption; omit it for both. Pass confirm=true only after the user explicitly confirms, including for request. delete_post deletes one post (post_id, after confirmation) or every unpublished post in a group (group_id, preview then confirm). A post that is currently publishing is never deleted. For a published post, explicitly choose delete_mode: "posterly" to keep live content, or delete_mode: "platform" with confirm_published: true after confirming live removal. Eligible connected platforms need saved post IDs and deletion permissions; a refusal keeps the record. Threads requires threads_delete, which the current connection flow does not request. YouTube upload permission alone is insufficient. Instagram live deletion is disabled: Meta requires instagram_manage_contents through Facebook Login and a Facebook user token, which our current deletion integration does not provide. Publishing permissions are insufficient. A group or bulk delete never requests live platform deletion. A published post without a saved platform target can only be removed with the explicit local-only choice; requesting platform deletion keeps the record and returns an error. A group delete never removes published posts. These act on a teammate's post in a shared workspace too, per your role there: editing a draft or deleting one needs Editor, anything already scheduled/paused/failed needs Publisher, and moving the scheduled time always needs Publisher. Published delete still follows that delete role check. A group delete only ever removes posts you created yourself. Updating a client-approved post reopens the changed review axis (caption edits reopen caption approval, media changes reopen asset approval) so it goes back to pending client review.
  • update_post edits a scheduled or draft post, or with status pauses, resumes, schedules, or drafts it, or with release_id repairs release/group metadata, always after confirmation. account_id moves an unpublished post onto another account you can use in the same workspace and on the same brand, and the platform follows that account. thread_posts replaces an X or Threads reply chain (at least two non-empty parts, and the first becomes the caption). A published post, or a post that is currently publishing, cannot be moved or rewritten. approval_action (request, approve, request_changes, or reject) changes client approval. Only a workspace owner or admin may do that, and the actor is the API key user, never a brand guest. request emails the brand reviewers. approve, request_changes, and reject are team-side decisions that reuse the same approval state as the client portal. request_changes needs approval_note. Optional approval_axis is asset or caption; omit it for both. Pass confirm=true only after the user explicitly confirms, including for request. delete_post deletes one post (post_id, after confirmation) or every unpublished post in a group (group_id, preview then confirm). A post that is currently publishing is never deleted. For a published post, explicitly choose delete_mode: "posterly" to keep live content, or delete_mode: "platform" with confirm_published: true after confirming live removal. Eligible connected platforms need saved post IDs and deletion permissions; a refusal keeps the record. Threads requires threads_delete, which the current connection flow does not request. YouTube upload permission alone is insufficient. Instagram live deletion is disabled: Meta requires instagram_manage_contents through Facebook Login and a Facebook user token, which our current deletion integration does not provide. Publishing permissions are insufficient. A group or bulk delete never requests live platform deletion. A published post without a saved platform target can only be removed with the explicit local-only choice; requesting platform deletion keeps the record and returns an error. A group delete never removes published posts. These act on a teammate's post in a shared workspace too, per your role there: editing a draft or deleting one needs Editor, anything already scheduled/paused/failed needs Publisher, and moving the scheduled time always needs Publisher. Published delete still follows that delete role check. A group delete only ever removes posts you created yourself. Updating a client-approved post reopens the changed review axis (caption edits reopen caption approval, media changes reopen asset approval) so it goes back to pending client review.
  • list_publishing_pauses shows whether publishing is paused for the workspace or any brand you can see, and whether you may pause, resume, or publish missed posts. manage_publishing_pause pauses or resumes a whole workspace or one brand_id, the same switches as the dashboard's Pause publishing cards. Only act when the user asks; never pause on your own initiative. action: "pause" (publisher or higher; a seat limited to some brands can only pause those brands) first returns a preview with the scope and how many scheduled posts it would hold; show it, then call again with confirm: true once the user agrees. action: "resume" (admin or higher) returns how many posts are back on the schedule and the IDs of posts that missed their time while paused. Missed posts never publish on their own: ask the user, then either action: "publish_missed" with those post_ids and confirm: true, or leave them paused to reschedule. See Pause all publishing for a workspace or a brand.
  • submit_agent_feedback submits bounded private operational telemetry after a concrete workflow outcome. Never include secrets, prompts, captions, media URLs, personal data, or fabricated outcomes. submit_product_feedback files a product bug, idea, or feedback on the public posterly feedback board after the user explicitly approves the title and category (confirm=true); do not use it for tool failures.

Media

  • upload_media takes small base64 files on hosted HTTP (Vercel ~4MB body), a local file_path on stdio (large files go through signed upload automatically), or a public url that posterly fetches into storage with SSRF protections (small files; large R2/S3 URLs fail). Pass asset_id from list_media to delete that library file. The first call shows the file and deletes nothing. Call again with confirm and the preview_id only after the person agrees. A file a post still uses is kept.
  • create_signed_upload mints object-storage PUT URLs (1 to 20 files per call) so large files never enter the Vercel body. The client must PUT the bytes.
  • create_media_drop mints a no-login https://www.poster.ly/drop/<token> page so a human can upload laptop files (up to 20 per link). Chat paperclips still never reach MCP.
  • list_media lists newest media assets, or files from one drop session, then pass public_url to validate_post / create_post. Each file includes its filename and the time it was added. There is no delete, and the list follows the key's workspace rather than a workspace filter.
  • find_available_slot finds the next open posting slot honoring the 1h gap. Each slot includes the UTC instant to pass as scheduled_at. The default zone stays America/New_York when timezone is omitted, so pass the workspace zone. Pass strategy: "best_engagement" with account_ids to get the best times in the next 7 days instead, ranked by when those accounts get the most engagement, each with a Rating column (1 to 5 stars, confidence-capped by basis) and a short reason. See Suggested times.

AI generation

  • generate_captions generates or adapts brand-aware caption suggestions without creating a post. Captions are checked against the brief and the workspace's business facts; unverifiable prices, dates, times, links, phone numbers and handles are removed, as is any never-say phrase set on a fact, so fewer variants than requested may come back.
  • generate_image queues an AI image job (Nano Banana or Grok). Gemini quality is model, resolution (1K, 2K, or 4K; 512 is no longer available), and thinking_level (quality=high enables thinking; thinking does not change the credit price). Optional reference_image_urls for logo lock. Poll list_jobs with type: "image" until urls are ready.
  • get_video_options and run_video_function inspect video models, durations, input modes, and credit estimates, and validate a request, without spending credits. generate_video queues a cost-guarded video job after user confirmation; poll list_jobs with type: "video" until video_url is ready.
  • get_credits reads the AI credit balance for the caller's workspace wallet. Read-only, spends nothing.

Analytics

  • list_analytics with view: "accounts" returns follower growth, reach, views, engagement rate, and API-provided native display_metrics such as Google Business Profile's Profile Views, Search Views, Maps Views, Customer Actions, Calls, Directions, Website clicks, and Posts. view: "posts" returns per-post likes, comments, reach, impressions, saves, shares, and plays. Google Business has location totals on view: "accounts" and no per-post rows, and the posts view says so. Both accept presentation: "compact" | "table" | "json" so Telegram can stay bullet-based while Markdown clients or custom renderers can choose tables/structured data. Supported: Instagram, X, Facebook Pages, LinkedIn, Google Business Profile, Pinterest, YouTube, and Threads.
  • list_analytics with view: "accounts" returns follower growth, reach, views, engagement rate, and API-provided native display_metrics such as Google Business Profile's Profile Views, Search Views, Maps Views, Customer Actions, Calls, Directions, Website clicks, and Posts. view: "posts" returns per-post likes, comments, reach, impressions, saves, shares, and plays. Both accept presentation: "compact" | "table" | "json" so Telegram can stay bullet-based while Markdown clients or custom renderers can choose tables/structured data. Supported: Instagram, X, Facebook Pages, LinkedIn, Google Business Profile, Pinterest, YouTube, and Threads. include_extras: true on accounts also asks for saved audience breakdowns, the last analytics sync, and entity snapshots. Those stay off until snapshot extras are switched on, and the call does not start a new sync.
  • list_analytics with view: "insights" lists per-post feedback-loop insights for recently published posts: the performance tier (great/good/mixed/poor), a diagnosis, a suggested next action, metrics, and the baseline each was compared against. Filter by account_id, post_id, and checkpoint (1h, 6h, 24h, 72h, 7d). Requires a Pro plan or higher.
  • list_post_suggestions lists evidence-based weekly post drafts written in each account's learned voice, each with a rationale tying it back to what has performed well. The caption and rationale come back in full, with the account id. Filter by account_id and status (pending, scheduled, dismissed). Requires a Pro plan or higher. dismiss_suggestion dismisses one after user confirmation and never touches one already turned into a scheduled post; in a team workspace, dismissing requires the Editor role or above. There is no accept action that turns a suggestion into a draft.
  • list_post_suggestions lists evidence-based weekly post drafts written in each account's learned voice, each with a rationale tying it back to what has performed well. Filter by account_id and status (pending, scheduled, dismissed). Requires a Pro plan or higher. dismiss_suggestion dismisses one after user confirmation and never touches one already turned into a scheduled post. Pass save_as_draft: true to keep that suggestion as an unpublished draft instead: nothing is scheduled and nothing is published. In a team workspace, dismissing or saving a draft requires the Editor role or above.

Social inbox (Pro plan or higher)

  • list_conversations lists Instagram and Facebook Page inbox DMs (Threads has no DMs, so platform is only instagram or facebook); pass conversation_id for one conversation and its messages. Add since and/or until (an ISO date or datetime, inclusive) to list only conversations whose last message falls in that range. Each row shows when the last message arrived (in UTC on the hosted endpoint, in your local time in the npm package), the account and workspace, you: when you sent the last DM, and [media] or [unsupported] when the last message had no text. manage_conversation with action: "send" sends a DM reply with preview then confirm and honors Meta 24-hour messaging windows; action: "sync" pulls the latest inbox data for one Instagram, Facebook, Threads, or LinkedIn account, with a 30-second cooldown (60 seconds for LinkedIn). Threads and LinkedIn are comments-only.
  • list_comments lists Instagram, Facebook Page, Threads, and LinkedIn Page inbox comments; pass comment_id for one comment and its replies. When the inbox stored them, the row includes the account id and the parent post caption and link. A comment has no posterly post id. Add since and/or until (an ISO date or datetime, inclusive) to list only comments posted in that range. Each row shows when it was posted (in UTC on the hosted endpoint, in your local time in the npm package), the account and workspace, and you for the business's own comments. manage_comment with action: "reply" replies with preview then confirm (Facebook has no private comment replies; Threads and LinkedIn are public-only). action: "update" hides/unhides a comment or marks it read; hide is reversible and does not delete, and LinkedIn comments cannot be hidden. action: "delete" deletes an Instagram or Facebook Page comment with preview then confirm; it is irreversible and admin only. Threads replies cannot be deleted (hide them instead), and LinkedIn comments cannot be deleted.
  • list_conversations lists Instagram and Facebook Page inbox DMs (Threads has no DMs); pass conversation_id for one conversation and its messages. Add since and/or until (an ISO date or datetime, inclusive) to list only conversations whose last message falls in that range. Each row shows when the last message arrived (ISO 8601 UTC, such as 2026-10-21T15:09:05Z, on both the hosted endpoint and the npm package), the account and workspace, you: when you sent the last DM, and [media] or [unsupported] when the last message had no text. manage_conversation with action: "send" sends a DM reply with preview then confirm and honors Meta 24-hour messaging windows; action: "sync" pulls the latest inbox data for one Instagram, Facebook, Threads, or LinkedIn account, with a 30-second cooldown (60 seconds for LinkedIn). Threads and LinkedIn are comments-only.
  • A single post, a review, a comment, and a conversation show their time in UTC, as 2026-10-16T06:00:00Z. A post list still uses the timezone you pass, and a free slot's local label stays in that zone.
  • list_comments lists Instagram, Facebook Page, Threads, and LinkedIn Page inbox comments; pass comment_id for one comment and its replies. Add since and/or until (an ISO date or datetime, inclusive) to list only comments posted in that range. Each row shows when it was posted (ISO 8601 UTC, such as 2026-10-21T15:09:05Z, on both the hosted endpoint and the npm package), the account and workspace, and you for the business's own comments. manage_comment with action: "reply" replies with preview then confirm (Facebook has no private comment replies; Threads and LinkedIn are public-only). action: "update" hides/unhides a comment or marks it read; hide is reversible and does not delete, and LinkedIn comments cannot be hidden. action: "delete" deletes an Instagram or Facebook Page comment with preview then confirm; it is irreversible and admin only. Threads replies cannot be deleted (hide them instead), and LinkedIn comments cannot be deleted.
  • list_conversations lists Instagram and Facebook Page inbox DMs (Threads has no DMs); pass conversation_id for one conversation and its messages. Add since and/or until (an ISO date or datetime, inclusive) to list only conversations whose last message falls in that range. Each row shows when the last message arrived (in UTC on the hosted endpoint, in your local time in the npm package), the account and workspace, you: when you sent the last DM, and [media] or [unsupported] when the last message had no text. Once an inbox sync has stored it, the row also says whether the 24-hour reply window is still open, and until when, in UTC. That time comes from the customer's last message. A reply does not extend it, and it is not guessed from the messages loaded in the thread. manage_conversation with action: "send" sends a DM reply with preview then confirm and refuses a send after that window has closed; action: "sync" pulls the latest inbox data for one Instagram, Facebook, Threads, or LinkedIn account, with a 30-second cooldown (60 seconds for LinkedIn). Threads and LinkedIn are comments-only.
  • list_comments lists Instagram, Facebook Page, Threads, and LinkedIn Page inbox comments; pass comment_id for one comment and its replies. Add since and/or until (an ISO date or datetime, inclusive) to list only comments posted in that range. Each row shows when it was posted (in UTC on the hosted endpoint, in your local time in the npm package), the account and workspace, and you for the business's own comments. manage_comment with action: "reply" replies with preview then confirm (Facebook has no private comment replies; Threads and LinkedIn are public-only). action: "update" hides/unhides a comment or marks it read; hide is reversible and does not delete, and LinkedIn comments cannot be hidden. action: "delete" deletes an Instagram or Facebook Page comment with preview then confirm; it is irreversible and admin only. Threads replies cannot be deleted (hide them instead), and LinkedIn comments cannot be deleted.

Every inbox tool takes an optional workspace_id. Pass one from whoami (or use a key/token bound to a workspace) to scope a call to exactly that workspace, gated on that workspace's owner's plan, matching the dashboard. Omit it on list_conversations, list_comments, manage_comment, or manage_conversation with action: "send" and the tool searches every workspace you belong to, returning matches from the ones where your role and the workspace owner's plan both qualify; the list calls note any skipped workspaces (and why) instead of returning a silent empty result. manage_conversation with action: "sync" is different: it always acts on the connected account's own workspace, so an omitted workspace_id there does not search anything, it just skips requiring a specific workspace.

Google Business Profile

  • list_google_business_reviews lists reviews for one location or every accessible location. It returns every review Google holds (it pages past Google's own 50-per-page cap), with the full untruncated review text and its date. Filter by rating and unanswered, and page with limit (default 200, max 1000) and offset; has_more tells you when more remain. Each location also reports its total_review_count and average_rating from Google, plus an error field, so a location with no reviews is distinguishable from one that failed to sync. Every review includes review_name (accounts/{account}/locations/{location}/reviews/{review}) plus account_id and location_id when they are known. Pass that review_name with account_id (or location_id) to manage_google_business_review reply or delete_reply. Do not invent a review name, and do not pass the Place ID from view: "review_link". view: "review_link" returns the public review request link.
  • audit_google_business_profile runs a local-profile audit. The text result includes the 0 to 100 score, the letter grade (A from 90, B from 80, C from 70, D from 60, otherwise F), one line per section, then the top recommendations. A section short of full marks includes at least one recommendation. Sections and maximum points: Profile basics 20, Hours 15, Categories 15, Description 10, Photos 15, Attributes 5, Review health 20. Those points are the score. Recent posts are not scored. Name, address, and phone sit inside Profile basics. The audit does not compare them with the website. The dashboard auditor can show a different number for the same location.
  • audit_google_business_profile runs a local-profile audit. The text result includes the 0 to 100 score, the letter grade (A from 90, B from 80, C from 70, D from 60, otherwise F), one line per section, then the top recommendations. Sections and maximum points: Profile basics 20, Hours 15, Categories 15, Description 10, Photos 15, Attributes 5, Review health 20. Those points are the score. Service areas are a penalty on that 100, not extra points: a shop with no service area is unchanged, and a service listing that is otherwise perfect drops to 85 (a B) when areas are missing or any area is more than 80 miles from the business in a straight line. Recent posts are not scored. Name, address, and phone sit inside Profile basics. The audit does not compare them with the website. The dashboard auditor can show a different number for the same location.
  • list_google_business_service_areas reads the towns and postcodes on a location (view: "areas") or searches for one to add (view: "places", q required). manage_google_business_service_area with action: "update" replaces the list (1 to 20 places) with preview then confirm; the preview shows the current areas next to the new list. An editor or above is required. Customer-only listings that still show a street address need clear_storefront_address: true, which deletes the address from the listing rather than hiding it.
  • manage_google_business_review with action: "suggest_reply" generates brand-aware reply options and never posts. action: "reply" and action: "delete_reply" manage the public owner reply with preview then confirm.
  • list_google_business_media lists the photos and videos on a profile gallery (the media shown on Maps and Search). manage_google_business_media adds one from a public URL (action: "add") or removes one (action: "delete"), both with preview then confirm. Photos must be JPG or PNG between 10 KB and 5 MB.
  • list_google_business_locations reads regular opening hours and attributes for one location. manage_google_business_location replaces regular hours (update_hours) or a named set of attributes (update_attributes), both with preview then confirm. Both stay off until location edits are switched on (GBP_LOCATION_WRITES_ENABLED). A confirm while that is off changes nothing and does not call Google. This uses the same business management connection as posting and does not change the business name, address, phone, or website.

Google Business tip: pass account_id (the integer id from list_accounts) to every Google Business tool. list_accounts prints location_id on Google Business rows, and account_type when the account has one (for example a LinkedIn profile or organisation page). If you use location_id instead of account_id, it is that numeric location id, not the ChIJ... Place ID that list_google_business_reviews with view: "review_link" returns. Passing the Place ID resolves to no account and returns "Google Business Profile account not found". Google Business tip: pass account_id (the integer id from list_accounts) to every Google Business tool. If you use location_id instead, it is the numeric location_id from list_accounts (the GBP location id), not the place_id (ChIJ...) that the review link view returns as review_link and place_id. Passing the Place ID resolves to no account and returns "Google Business Profile account not found". A review list includes the owner reply text when one exists. A reply or delete-reply preview repeats the reviewer, the star rating, the review text, and the current reply. Profile photos include the image URL, and the gallery pages in sets of 50.

Activity, updates, and webhooks

  • list_activity reads the agent notifications feed of post activity, publish attempts, failures, and retries.
  • get_updates gets the latest posterly product updates and news from the updates feed. Requires an active posterly subscription.
  • posterly_status checks posterly's own publishing health: the overall state from the last 4 hours of publishing success, any active incident (for example X being unavailable), and per-platform state with 7-day success rate and publish counts. Call it before a time-critical post, or when someone asks whether posterly or a platform is down. It is read-only, takes no arguments and returns aggregates only (no post or user data). When fewer than 10 posts were counted in the last 4 hours and no incident is active, the state is unknown (not enough recent activity to judge), which is not an outage and not a green light. Failures caused by content or account problems, such as an expired connection or a content limit, are excluded from the rates and shown separately as customer-side failures. It is not whoami with view: "status", which only reports the MCP server and npm package version. Needs an API key with the accounts:read scope; the same data is behind GET /api/v1/status (API reference).
  • list_webhooks inspects webhook subscriptions and, with deliveries, recent delivery attempts. Delivery history stays off unless ENABLE_API_WEBHOOK_DELIVERY_HISTORY is true, and it never returns the signing secret or the request body. create_webhook creates a signed subscription after explicit confirmation and returns the signing secret once. manage_webhook updates one (action: "update") or sends a signed test delivery (action: "test") after explicit confirmation, or deletes one with preview then confirm (action: "delete").
  • list_webhooks inspects webhook subscriptions and delivery status. create_webhook creates a signed subscription after explicit confirmation and returns the signing secret once. manage_webhook updates one (action: "update") or sends a signed test delivery (action: "test") after explicit confirmation, or deletes one with preview then confirm (action: "delete"). Subscriptions can include account.needs_reconnect and account.reconnected: one delivery when a connected account starts needing a reconnect, and one when it is healthy again. The payload matches connection_status from list_accounts (account id, platform, brand, reason, time) and does not include tokens or the provider's error text.
  • get_x_posting_quota inspects workspace X posting allowance and URL-block status.

Billing

  • get_subscription reads the user's posterly subscription status, tier, cancel/pause state, period end, and connected accounts in use against the limit. It is also the first call for "upgrade my plan", "change plan", "add accounts" and "how many accounts": it lists the plans above the user's with real prices, account limits and included credits, and the next steps for the user's status. For an owner on an active or trialing plan it names upgrade_subscription as the way to move up; it names resume_subscription for a paused plan and the billing page for a past due, canceled or cancel-pending plan. For an Agency workspace owner it also shows the extra connected-account state (included, paid, in use, the limit, the price of $0.50 per extra account per month or $4.70 per year, the 20-account minimum, and any payment waiting on the card) and the exact manage_extra_account call to make. A teammate sees the numbers and is told only the owner can change them. Requires the billing:read scope. Claude, ChatGPT, Cursor, Slack, the posterly CLI, and the other connectors posterly lists include that permission on the next consent screen. A connection you already approved keeps its old permissions until you reconnect. Nothing signs you out on its own. Reconnect from the assistant's connector settings when you want it to read your plan. An OAuth app that registered itself cannot get this permission; use an API key with Read billing instead.

  • cancel_subscription cancels after explicit confirmation. The agent must ask the user for a cancellation reason first (a fixed catalog value) and pass confirm: true; immediate defaults to cancel-at-period-end. Requires the billing:write scope.

  • pause_subscription pauses for 30 days (one pause per 90-day cooldown) after confirm: true. resume_subscription resumes a paused subscription. downgrade_subscription moves down one tier (or to an explicit lower tier) after confirm: true: the lower limits and credits apply right away, the lower price starts at the next renewal, and the rest of the current period is not refunded. All require the billing:write scope.

  • upgrade_subscription with a tier moves the plan to a strictly higher one (never down; use downgrade_subscription for that), for the workspace owner. Call it without confirm to get the exact quote (what is charged today, the new recurring cost and interval, and the new account limit and credits) and a preview_id; if the change cannot be made you get the reason and no preview_id. The saved card is charged the quoted amount only after the user agrees and you call again with confirm: true, that preview_id, and the expected_current_tier and expected_interval values the preview lists, so a quote that went stale is refused rather than charged. If the card needs 3D Secure or is declined, nothing changes and you get a payment link for the owner; the plan changes automatically once it is paid. A trial keeps its trial: the price changes and nothing is charged today. The plan keeps its billing interval; Agency is monthly or yearly only, so from a weekly plan pass interval as month or year (any other interval change is a billing page change). If the subscription includes the API & MCP add-on, carries a discount other than a permanent percent-off below 100, or there is no card subscription on posterly, the tool says so and points to the billing page. If a standalone add-on would stop working on the new plan (for example Agency needs the Agency add-on), it points to the API page instead. Requires the billing:write scope.

  • manage_extra_account with action: "set" and extra_accounts (the desired total, read the current total from get_subscription) buys, raises, lowers, or removes extra accounts for the workspace owner, on the same rules as Billing in the dashboard: every increase adds at least 20, there is no maximum, and you cannot go below the accounts connected in any workspace you own. Call it without confirm to get the exact quote (charge today and new recurring cost) and a preview_id; if the change cannot be made you get the reason and no preview_id. The saved card is charged only after the user agrees and you call again with confirm: true, that preview_id, and the expected_current_extra_accounts and expected_interval values the preview lists, so a quote that went stale is refused rather than charged. If the card needs 3D Secure or is declined, nothing is added and you get a payment link for the owner; the accounts apply automatically once it is paid. Requires the billing:write scope.

Subscription management runs on the same API key but does not require the paid API add-on, so a paused or cancel-pending user can still resume or manage their plan. The destructive actions (cancel_subscription, pause_subscription, downgrade_subscription) require confirm: true after explicit user confirmation, and cancel_subscription also requires a reason. Reactivating a pending cancellation, restarting a lapsed plan, and retention offers stay in the dashboard.

Managing your team

With a key that has the Manage team permission (tick it when you create the key in /dashboard/api), list_workspace_members shows who is in a workspace and manage_workspace_member invites, updates, or removes a colleague. You must be an owner or admin of the workspace to make changes. In ChatGPT, Claude, and other clients connected with OAuth, both tools work without a key, but only when you are an owner or admin of the workspace (listing included); existing connections do not need to reconnect.

Owners and admins can also turn on API and AI access for a teammate (manage_workspace_member, action: update, member_id, api_access: true). That teammate can then connect their own ChatGPT, Claude or other AI app, or use their own keys, in your workspace with their usual role and brand limits, billed to your workspace plan. Viewers get read-only access: AI generation and media upload tools answer with a permissions error for them. It is off by default, nobody can change their own, and turning it off stops their connections in your workspace on the next call. A teammate whose only API access comes from one workspace gets a connection bound to it; whoami shows api_access and api_access_source for each workspace. Details: Teammates and API access.

Every change is previewed first. The agent's first call changes nothing and returns a summary to show you; it only acts when you approve and it calls again with confirm: true and the preview_id. In this version admin seats are dashboard-only, invites are always sent by email (never an instant add), the email names the person who sent it by name and email address, and the workspace owner is emailed about every change, whether it came from a key or an OAuth connection. Full rules and limits: Team management.

Usage examples

In Claude, ChatGPT, or any MCP client:

  • "Schedule a LinkedIn post about our Q1 results for tomorrow at 10am"
  • "What accounts do I have connected to posterly?"
  • "Disconnect the old Instagram account after I confirm the exact account ID"
  • "Pause post 971 after I confirm it is the right scheduled post"
  • "Upload this image and create an Instagram post with it"
  • "Schedule this as an Instagram Reel, add @partner as a collaborator, and use this image as the Reel cover"
  • "Schedule this YouTube video as unlisted, add these tags, and put it in our launch playlist"
  • "Post this TikTok with comments on, duet off, stitch off, and public privacy"
  • "Turn this Google Business update into an event post with a Learn more CTA"
  • "How did my Instagram perform over the last 14 days?"
  • "Which of my recent LinkedIn posts got the most engagement?"
  • "Post this 5-tweet thread on my X account at 3pm tomorrow" (uses thread_posts for X / Threads chains)

platform_settings covers the same scheduling controls exposed by the composer: Instagram feed/story/reel/carousel options plus parent-container is_ai_generated, licensed Reel audio_id, and caption add-ons (a poll or a comment prompt; needs a Meta (Facebook Login) connection for that Instagram account in this workspace, so Instagram Login-only accounts cannot use them; not Stories), Facebook stories/reels/backgrounds, YouTube thumbnails/privacy plus notify_subscribers, add_shorts_hashtag, and optional brand_partner, LinkedIn document, mention, poll, and content_call_to_action_label settings (including BUY_NOW and SHOP_NOW), TikTok video and photo slideshow controls (including video_cover_timestamp_ms to pick the cover frame on direct-post videos), Pinterest board/link/title/product_tags plus a required stored JPEG/PNG cover_image_url for video Pins (20 MiB maximum; registered upload, not a public video_url fetch), Google Business event/offer/CTA/recurrence fields, X polls, reply settings (incl. subscribers/verified), paid_partnership, and Articles (article: { title, cover }, with the caption as the Markdown body; needs an X Premium account), Threads reply controls, polls, topic tags, text attachments, ghost posts, spoilers, reply approvals, GIPHY, and share_to_instagram, Telegram polls, inline CTA buttons, video cover, and live photo, Slack markdown/blocks, Mastodon visibility/quote/poll (media plus poll allowed on 4.6+; text, media, and alt-text limits follow the instance; ambiguous final creates stop automatic retries), Dev.to title, tags, canonical URL, and images (the cover and any extra photos are uploaded to Dev.to, up to 25 MB each, with extra photos placed in the article using their descriptions; Dev.to allows 10 image uploads an hour by default, and a cover or extra photo that cannot be uploaded keeps its original image address, with a warning shown on the post), and Bluesky language, alt-text, content warnings, hidden tags, quote posts, and disable-quotes settings. Pass ai_generated: true to label a post as AI-generated on TikTok, X, YouTube, Pinterest, and Instagram.

An Instagram Story or Story Series can include an optional caption, with the same 2,200 character limit and up to 30 hashtags as a feed post. A Story Series sends the caption with the first slide only. Leave it empty for a media-only Story. A Facebook Story does not take a caption. Polls and comment prompts are still not available on Stories.

For media descriptions, prefer media_accessibility: [{ index, asset_id?, source_url?, alt_text }]. The legacy media_alt_texts URL map remains accepted. The ordered format keeps alt text with the original media item through carousel reordering and platform-specific image preparation. Updates merge entries by index and preserve omitted descriptions. An empty string clears that occurrence, while an empty array (or empty legacy map) clears all descriptions. Stable entries take precedence when both forms are supplied.

For TikTok you do not need to set a post type for images. posterly auto-detects image media and schedules it as a photo slideshow of 1 to 35 images (sending every image), so "Schedule these 5 photos as a TikTok" just works. A single video schedules as a standard TikTok video. On that video, the post text is the TikTok caption. A title in platform settings is placed on the first line when it and the caption both fit in 2,200 characters. Photo slideshows keep that title separate from the caption.

See the MCP marketing page for client-specific setup screenshots.

Claude Code plugin alternative

If you use Claude Code, you can install the official posterly plugin instead of editing MCP config by hand. The plugin bundles this MCP server plus the agent skill and a guided /posterly:setup flow.

/plugin marketplace add awpthorp/posterly-agent
/plugin install posterly@posterly-agent

Full steps: Claude Code plugin.