MCP for AI agents
Connect Cursor, Claude, or another MCP client to Postless. Create drafts, generate images, and schedule posts without opening the dashboard.
PostlessSeptember 12, 202612 min read
Postless exposes a remote Model Context Protocol server so an AI client can create drafts, generate images, and schedule posts on your brands.
The dashboard stays the place you review work. MCP is the agent-facing adapter around the same
public API. Create a draft first. Publish later only if you turn on
posts:publish and ask the agent to publish that draft.
Typical flow:
create_post (draft)
↓
generate_post_media
↓
poll get_media_generation_status
↓
schedule_post
What you need
- An active Postless subscription and a verified email
- At least one finished brand (incomplete and archived brands are unreachable)
- An API key from Settings → API keys
- An MCP client that can call a remote Streamable HTTP server with a Bearer token
Create a key for MCP
-
Open Settings → API keys.
-
Name the key (for example
CursororClaude). -
Turn on the permissions the agent should have. For the demos below, use:
Permission Why brands:readList brands and read voice profile brands:writeUpdate voice, audience, pillars, and related profile inbox:readBrowse notes and uploads already in the inbox posts:readList posts and poll media generation posts:writeCreate & update drafts, generate media, schedule plans:readRead the content calendar Leave Publish posts off unless you want the agent to publish a draft you already created. Create and publish stay two steps. There is no create-and-publish-now tool.
-
Optionally limit the key to specific brands.
-
Click Create key and copy the secret. It is shown once.
The key is pl_…. Treat it like a password. Do not commit it to git.
Same subscription, media quotas, and 60 requests per 60 seconds as the public API apply. When
the API key limit is hit, tool calls fail with 429 rate_limit_exceeded, a Retry-After header
(seconds), and retryAfterSec in the error body — wait that long before retrying.
Endpoint
https://dashboard.postless.app/mcp
Transport: Streamable HTTP. Auth:
Authorization: Bearer pl_YOUR_KEY
x-api-key: pl_YOUR_KEY also works. On your own host, replace the domain with that host.
Tools
| Tool | Permission | What it does |
|---|---|---|
list_brands | brands:read | List onboarded brands the key can access |
get_brand | brands:read | Voice profile: goal, audience, voice, pillars, about, beliefs, avoidTopics, samplePosts, styleGuide |
update_brand | brands:write | Partially update those voice profile fields |
get_brand_social_accounts | brands:read | ACTIVE accounts; providerAccountId for targets. Pinterest includes boards / defaults |
list_inbox_items | inbox:read | Brand inbox notes and uploads. Use ids as inboxItemIds on create_post |
get_inbox_item | inbox:read | One inbox item |
add_inbox_item | inbox:write | Add a text/link/idea/feedback note. Images and video stay on HTTP upload |
delete_inbox_item | inbox:write | Permanently delete one inbox item. Only when the user explicitly asks |
bulk_delete_inbox_items | inbox:write | Permanently delete up to 100 inbox items by id. Only when the user explicitly asks |
list_posts | posts:read | Filter by status / date range. Ordered by scheduledAt, publishedAt, createdAt (newest first) |
get_post | posts:read | One post: body, platformVariants, status, schedule, media |
create_post | posts:write | Create a draft (or schedule). Optional platformVariants per connection |
update_post | posts:write | Partially update topic/body/angle/schedule/targets/platformVariants by postId |
generate_post_media | posts:write | Start image, video, or carousel generation from a prompt. Async |
get_media_generation_status | posts:read | Poll until succeeded or failed |
get_content_calendar | plans:read | Current plan slots (day, topic, format, time, linked postId) |
add_plan_slot | plans:write | Add one idea to the current plan. Does not create a post |
schedule_post | posts:write | Set scheduledAt by postId. Convenience wrapper around update_post |
publish_post | posts:publish | Publish an existing draft now. Only when the user explicitly asks |
create_post is not AI generation. The client writes the caption. Use get_brand or the voice
resource so the copy matches the brand.
update_post requires a postId (UUID). Take it from the user (shown under the title on the
draft page), or from a prior create_post / list_posts / get_post result. Do not guess or
search by topic text — that is ambiguous and can overwrite the wrong post.
platformVariants on create/update is keyed by providerAccountId from
get_brand_social_accounts. Use it for different captions per connection (for example longer
Threads, shorter X). Shared body remains the fallback.
Pinterest / YouTube extras on each variant:
| Field | Notes |
|---|---|
title | Discovery title (Pinterest and YouTube; not TikTok) |
link | Pinterest destination URL (optional) |
boardIds | Pinterest boards for this pin (overrides connection defaultBoardIds) |
get_brand_social_accounts returns pinterest.boards, defaultBoardIds, and defaultLink for
Pinterest connections (set in the dashboard). Sending boardIds on create/update also adds unknown
board ids to that catalog (name = id); it does not change defaults. Pins need exactly one image
or video on the Pinterest account and at least one board. Instagram/Pinterest may omit caption when
media is attached; other platforms still need body. Non-empty captions must still respect each
platform’s character limit on schedule/publish.
generate_post_media does not invent a prompt. Pass type (image, video, or carousel) and a
prompt (10–3000 characters). aspectRatio is optional (1:1, 4:5, 9:16, 16:9). Image and
carousel default to 4:5; video defaults to 9:16. Then poll. Video and carousel can take minutes.
Brand aiImagesEnabled / aiVideosEnabled and weekly media quota still apply. Prefer a single
still or video for Pinterest (not a multi-image carousel).
schedule_post is the step that puts something on the public calendar. The post must be ready to
schedule (connected accounts, platform media rules, character limits, caption when required). It
does not publish now.
Resources
Read-only context:
postless://brands/{brandId}/voice
Tone, audience, pillars, and related voice-profile fields for that brand. An agent can write in the
brand’s style without a custom integration per model. Use update_brand when the user asks to
change the profile.
How agents should work
list_brands/get_brand(or read the voice resource) before writing.get_brand_social_accountswhen you needpublishConnectionIds.list_inbox_itemswhen you want to reuse notes or uploads (inboxItemIdsoncreate_post).create_postwith finished copy. OmitscheduledAtto keep a draft while media generates.update_postwith an explicitpostIdwhen the user wants copy or targets changed.generate_post_mediaif the post needs a still, video, or carousel. Pollget_media_generation_status, thenget_postto confirmmedia[].schedule_post/update_postwithscheduledAtonly when the post is ready to schedule.publish_postonly when the user explicitly asks to publish that draft. Never create and publish in one step.
Connect Cursor
- Create an API key as above.
- Open Cursor Settings → MCP (or add a project file
.cursor/mcp.json). - Add a Streamable HTTP server:
{
"mcpServers": {
"postless": {
"url": "https://dashboard.postless.app/mcp",
"headers": {
"Authorization": "Bearer pl_YOUR_KEY"
}
}
}
}
If your Cursor build supports env interpolation, keep the secret out of the file:
{
"mcpServers": {
"postless": {
"url": "https://dashboard.postless.app/mcp",
"headers": {
"Authorization": "Bearer ${env:POSTLESS_API_KEY}"
}
}
}
}
- Restart MCP or reload Cursor. You should see tools such as
list_brandsandcreate_post. - Try: “List my Postless brands, then draft a LinkedIn post for the first one about this week’s shipping notes. Save it as a draft — don’t schedule yet.”
Do not put .cursor/mcp.json with a raw pl_ secret in a shared repo.
Connect Claude Code
claude mcp add --transport http postless https://dashboard.postless.app/mcp \
--header "Authorization: Bearer pl_YOUR_KEY"
Then in a Claude Code session:
Turn the changes in this working tree into a LinkedIn announcement for my Postless brand. Save it as a draft, generate a 4:5 image from a prompt that matches the caption, and wait until the image is attached.
Claude Code can inspect git locally and call Postless for distribution.
Connect Claude Desktop
Claude Desktop often speaks stdio, not remote HTTP. Bridge with mcp-remote:
{
"mcpServers": {
"postless": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://dashboard.postless.app/mcp",
"--header",
"Authorization: Bearer pl_YOUR_KEY"
]
}
}
}
Config path is usually Claude → Settings → Developer → Edit Config
(claude_desktop_config.json). Restart Claude Desktop after saving.
If a later Claude Desktop build supports Streamable HTTP URLs natively, use the same URL and Bearer header as Cursor.
Connect VS Code (Copilot / agent)
Add .vscode/mcp.json (workspace) or a user MCP config:
{
"servers": {
"postless": {
"type": "http",
"url": "https://dashboard.postless.app/mcp",
"headers": {
"Authorization": "Bearer pl_YOUR_KEY"
}
}
}
}
Reload the window. In agent chat, ask it to list Postless brands or create a draft.
ChatGPT, Claude.ai, and other hosted connectors
Hosted MCP clients that speak OAuth (not a pasted API key) can connect to the same /mcp
endpoint.
- In the host, add a remote MCP server URL:
https://dashboard.postless.app/mcp. - The client reads
/.well-known/oauth-protected-resourceand starts the authorization code + PKCE flow. - Sign in to Postless and approve scopes on /oauth/consent. Leave Publish posts off unless you want that client to publish existing drafts.
- The host stores the access token. You do not paste a
pl_key. - To cut access later, open Settings → Connected apps in Postless and disconnect the client. Also remove it from the host’s apps/connectors list if you want it gone on that side too.
If the host says the connection already exists, do not create a second app. Remove Postless from that host’s Apps/connectors list, or reuse the existing one and refresh tools. Start a new chat after reconnecting — an old thread can keep an empty tool cache.
Cursor, Claude Code, Claude Desktop (mcp-remote), and VS Code can still use an API key. Both auth
methods are v1.
initialize and tools/list do not require a token so hosts can scan tools before OAuth. Calling a
tool still needs an API key or access token.
Dynamic client registration is on so older hosts can register themselves. Newer hosts that send a Client ID Metadata Document URL are accepted too.
Sample use cases
1. Cursor: ship notes → LinkedIn draft
You just merged a release. In Cursor:
Turn the changes in this release into a LinkedIn announcement for Postless. Match our brand voice. Save it as a draft and generate a simple product still. Do not schedule.
The agent should:
- Read the diff or changelog locally
list_brands/get_brand(orpostless://brands/{id}/voice)create_postwith a LinkedIn captiongenerate_post_media(type: image) and poll until attached
You open Posts in Postless to edit, then schedule or publish.
2. Fill gaps in next week’s calendar
Look at next week’s Postless calendar. For empty days, draft LinkedIn posts in our voice and leave them as drafts.
The agent should:
get_content_calendarget_brandcreate_postfor each gap (noscheduledAtuntil you say so)
If you want them on the calendar: “Schedule those drafts for 10:00 on each empty day.” That is
schedule_post — confirm the times before it runs.
3. Repurpose what you already posted
List my LinkedIn posts from this month. Draft three new posts that continue the same themes. Save drafts only.
Uses list_posts (filter by date / status). There is no analytics tool yet, so the agent cannot
rank “top performing” posts. It can read recent copy and write variations.
4. Founder agent with GitHub
An agent with an external GitHub MCP (or pasted release notes) and Postless MCP:
We shipped 2.4 today. Read the GitHub release, write LinkedIn and X drafts in our voice, attach a generated image to LinkedIn, and schedule LinkedIn for tomorrow 9:00.
GitHub MCP (or the dashboard GitHub shipping connection) supplies facts. Postless is distribution: voice, draft, image, schedule. You still own publish. Postless MCP does not install or query GitHub repos.
5. Image for an existing draft
Generate a 4:5 image for post POST_ID: a quiet laptop on a desk, no text on the image. Poll until it’s attached.
Needs posts:write and posts:read. If generation is disabled on the brand or the weekly image
quota is used, the tool returns an error — fix that in brand settings or wait for the week to reset.
Limits and safeguards
- No create-and-publish.
create_postnever publishes.publish_postneedsposts:publishand an existing draft the user asked to publish. update_post/schedule_postrequirepostId. Published posts return409post_not_editable.- Media generation uses the same providers, brand toggles, and weekly caps as the dashboard.
- Tenant isolation is the API key or OAuth token: one Postless user (and optional brand allowlist), not every account on the server.
- Idempotency and validation are the same as
/api/v1.
Troubleshooting
| What you see | What to do |
|---|---|
401 Unauthorized | Missing/wrong key or expired OAuth token. API-key clients: Authorization: Bearer pl_…. Hosted connectors: reconnect so the host refreshes the token |
401 insufficient_scope | OAuth token is missing a tool scope (often posts:publish). Reconnect, check the box on the Postless consent screen, then start a new chat |
403 missing_permission | Turn on the permission on the API key or consent screen (inbox:read, plans:read, posts:publish) |
403 brand_not_allowed | The key is limited to other brands |
402 subscription_required | No active subscription |
404 | Unknown brand/post, or the brand is unfinished or archived |
409 media_generation_in_progress | Poll; don’t start a second generate on the same post |
409 media_generation_disabled | Enable AI images or videos on the brand |
409 post_not_editable | Published posts cannot be updated. Create a new draft or edit before publish |
429 media_quota_exceeded / rate_limit_exceeded | Weekly media cap, API key 60 req/60s (retryAfterSec + Retry-After), OAuth MCP 60 req/min per user, or anonymous /mcp 60 req/min per IP (Redis) |
503 media_provider_not_configured | Image/video provider isn’t configured on the server |
| Client never lists tools | Confirm Streamable HTTP (not stdio), Accept headers, and a reload/restart |
| Hosted connector: no callable tools | Deploy current /mcp, then refresh tools on the existing connector. Do not create a second app for the same URL. Start a new chat. |
| Hosted connector: connection already exists | ChatGPT keys apps by MCP URL. Delete Postless under Settings → Apps, wait a minute, then add it once — or keep the existing app and refresh tools |