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, 20269 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, audience, pillars inbox:readBrowse notes and uploads already in the inbox posts:readList posts and poll media generation posts:writeCreate drafts, generate image/video/carousel, 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 minute as the public API apply.
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 | Name, voice, audience, pillars |
get_brand_social_accounts | brands:read | ACTIVE accounts; use providerAccountId as publishConnectionIds |
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 |
list_posts | posts:read | Filter by status (draft, queued, published, failed) and date range |
get_post | posts:read | One post: caption, status, schedule, attached media |
create_post | posts:write | Create a draft (or schedule if you pass scheduledAt). Finished copy |
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 on a draft. Highest-impact write besides create |
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.
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.
schedule_post is the step that puts something on the public calendar. The post must be a draft
with a body that is ready to schedule (connected accounts, caption, platform media rules). It does
not publish now.
Resources
Read-only context:
postless://brands/{brandId}/voice
Tone, audience, and pillars for that brand. An agent can write in the brand’s style without a custom integration per model.
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.generate_post_mediaif the post needs a still, video, or carousel. Pollget_media_generation_status, thenget_postto confirmmedia[].schedule_postonly after the draft is ready.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.
Cursor, Claude Code, Claude Desktop (mcp-remote), and VS Code can still use an API key. Both auth
methods are v1.
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 GitHub MCP 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 supplies facts. Postless is distribution: voice, draft, image, schedule. You still own publish.
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. schedule_postonly accepts drafts. Queued or published posts return409.- 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 |
403 missing_permission | Turn on the permission on the 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_draft | Only drafts can be scheduled or published from MCP |
429 media_quota_exceeded / rate_limit_exceeded | Weekly media cap or 60 req/min |
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 |