# MCP for AI agents

> Connect Cursor, Claude, or another MCP client to Postless. Create drafts, generate images, and schedule posts without opening the dashboard.

- HTML: https://postless.app/docs/mcp
- Published: 2026-09-12
- Author: Postless
- Category: Product
- Tags: founders, creators, agencies, teams, api, mcp

Postless exposes a remote [Model Context Protocol](https://modelcontextprotocol.io) 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](https://postless.app/docs/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

1. Open **Settings → API keys**.
2. Name the key (for example `Cursor` or `Claude`).
3. Turn on the permissions the agent should have. For the demos below, use:

   | Permission    | Why                                                    |
   | ------------- | ------------------------------------------------------ |
   | `brands:read` | List brands and read voice, audience, pillars          |
   | `inbox:read`  | Browse notes and uploads already in the inbox          |
   | `posts:read`  | List posts and poll media generation                   |
   | `posts:write` | Create drafts, generate image/video/carousel, schedule |
   | `plans:read`  | Read 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.

4. Optionally limit the key to specific brands.
5. 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

1. `list_brands` / `get_brand` (or read the voice resource) before writing.
2. `get_brand_social_accounts` when you need `publishConnectionIds`.
3. `list_inbox_items` when you want to reuse notes or uploads (`inboxItemIds` on `create_post`).
4. `create_post` with finished copy. Omit `scheduledAt` to keep a draft while media generates.
5. `generate_post_media` if the post needs a still, video, or carousel. Poll
   `get_media_generation_status`, then `get_post` to confirm `media[]`.
6. `schedule_post` only after the draft is ready.
7. `publish_post` only when the user explicitly asks to publish that draft. Never create and publish
   in one step.

## Connect Cursor

1. Create an API key as above.
2. Open Cursor **Settings → MCP** (or add a project file `.cursor/mcp.json`).
3. Add a Streamable HTTP server:

```json
{
  "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:

```json
{
  "mcpServers": {
    "postless": {
      "url": "https://dashboard.postless.app/mcp",
      "headers": {
        "Authorization": "Bearer ${env:POSTLESS_API_KEY}"
      }
    }
  }
}
```

4. Restart MCP or reload Cursor. You should see tools such as `list_brands` and `create_post`.
5. 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

```bash
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`:

```json
{
  "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:

```json
{
  "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.

1. In the host, add a remote MCP server URL: `https://dashboard.postless.app/mcp`.
2. The client reads `/.well-known/oauth-protected-resource` and starts the authorization code + PKCE
   flow.
3. Sign in to Postless and approve scopes on **/oauth/consent**. Leave **Publish posts** off unless
   you want that client to publish existing drafts.
4. 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:

1. Read the diff or changelog locally
2. `list_brands` / `get_brand` (or `postless://brands/{id}/voice`)
3. `create_post` with a LinkedIn caption
4. `generate_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:

1. `get_content_calendar`
2. `get_brand`
3. `create_post` for each gap (no `scheduledAt` until 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_post` never publishes. `publish_post` needs `posts:publish` and
  an existing draft the user asked to publish.
- **`schedule_post` only accepts drafts.** Queued or published posts return `409`.
- **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 |

## Related

- [Public API](https://postless.app/docs/public-api)
- [Scheduling](https://postless.app/docs/scheduling)
- [Set up your brand profile](https://postless.app/docs/brand-setup)
- [Getting started](https://postless.app/docs/getting-started)
