WriteStack

Connect Claude, ChatGPT, Notion, or any MCP client to a writer’s WriteStack account. Draft and schedule Substack Notes, manage articles, and read statistics.

Hosted MCP Server

npx add-mcp 'https://www.writestack.io/api/mcp/mcp'

Installs into Claude Code, Codex, Cursor and more

Documentation

WriteStack MCP server

Connect Claude, ChatGPT, Notion, or any MCP client to a writer’s WriteStack account. Draft and schedule Substack Notes, manage articles, and read statistics.

Endpoint: https://www.writestack.io/api/mcp/mcp

Quick start

  1. Add the endpoint. In Claude, ChatGPT, or Notion, add a custom MCP connector and paste the endpoint URL.
  2. Sign in to WriteStack. The client opens WriteStack. Sign in and approve access. No API key or config file needed.
  3. Ask in plain language. Try “Draft three Notes about my last post and queue them.” The client picks the tools.

Server

One remote MCP server, scoped to the writer who signed in.

A connected client can draft and update Substack Notes, place them on the writer’s queue, work with articles, read statistics and activity, and set Buffer cross-posts. Every call runs as the authenticated WriteStack user and never touches another account.

Authentication

Every request needs a bearer token. Use OAuth when the client supports it, and an API key when it does not.

For clients without OAuth, create an API key in WriteStack under Settings → MCP (https://www.writestack.io/settings) and send it as Authorization: Bearer <key>. Treat the key like a password. Revoke it in Settings if it leaks.

Plan access. MCP is included on eligible WriteStack plans. A call from an account without access returns an error that points the writer to Settings.

Connect a client

Claude, ChatGPT, and Notion connect with OAuth. Paste the endpoint, then sign in.

  • Claude: Claude Desktop and Claude on the web
  • ChatGPT: ChatGPT Desktop and the web app
  • Notion: Notion AI custom connectors

A client that reads a local config file and has no OAuth support can reach the server through mcp-remote with an API key header:

{
  "mcpServers": {
    "writestack": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://www.writestack.io/api/mcp/mcp",
        "--header",
        "Authorization: Bearer ws_your_api_key"
      ]
    }
  }
}

Notes

Draft, read, and update Substack Notes in the writer’s voice.

Note bodies are Markdown, and a line break is a blank line (two newlines). Before drafting new text, call get_writing_style unless the writer already pasted full style instructions. Call list_tags before assigning tag names.

Remote images go in imageUrl, up to five https URLs per note. WriteStack fetches and stores them.

Ask before adding link previews. If a body contains a link, ask the writer before attaching a preview card. Pass includePreviews: true only after they say yes.

  • list_notes (Read): List notes, optionally by status or tag. Status evergreen means notes set to repeat. Paginate with skip and read hasMore.
  • get_note (Read): Read one note, including auto-restack, Buffer channels, and any per-channel Buffer text or images.
  • get_writing_style (Read): Read the saved note style, topics, and onboarding context.
  • list_tags (Read): List category names. Tags also decide which reserved queue times a note can take.
  • create_note (Write): Create a draft or ready note. Optional auto-restack 1–168 hours after it publishes, plus optional Buffer channels and per-channel posts.
  • create_many_notes (Write): Create several notes in one call. Each note has its own status, tags, images, previews, auto-restack, and Buffer settings.
  • update_note (Irreversible): Replace the fields you pass: content, status, tags, auto-restack, Buffer channels, per-channel posts, or link preview. No undo, so read the note and confirm first. autoRestackAfterHours: null turns auto-restack off, and an empty bufferChannelIds clears channels.

Schedule

Queue Notes on the writer’s weekly template or at exact times.

The template is a set of weekly times. Each time stays open, reserves tags, or accepts only untagged notes. A note takes a reserved time when it shares any of that time’s tags. Untagged notes use open and untagged-only times. A time can also carry default Buffer channels, which replace the note’s channels when it lands there.

The writer syncs to Substack. Scheduling stores the queue in WriteStack. It does not publish to Substack. After a schedule call, the writer opens WriteStack and clicks the yellow Sync button so the Chrome extension syncs the queue. Ask them to confirm they will, then pass userConfirmedManualSync: true. Never set it without an explicit yes.

Check capacity first. Call get_schedule_capacity before scheduling. Schedule at most remaining notes unless unlimited is true. The rest stay as drafts or ready notes.

  • get_schedule (Read): Read the weekly template, including reserved tags, untagged-only times, and default Buffer channels.
  • get_queue (Read): List notes that already have a posting time. Paginate with skip.
  • get_next_available_slots (Read): Preview the next free times. Pass tagNames to preview one note’s lane, or omit them to list every free time in order.
  • get_schedule_capacity (Read): Read the plan limit, the current scheduled count, how many remain, and whether the account is unlimited.
  • schedule_notes (Irreversible): Place notes on the next template times, routed by each note’s tags. Use this unless the writer asks for exact times.
  • schedule_notes_with_custom_dates (Irreversible): Place each note at an explicit future ISO 8601 time. Use this when the writer names times or has no template.

Articles

Plan, write, and push long-form articles to Substack drafts.

Article tools only see newsletters the writer enabled in WriteStack Articles. Call list_article_newsletters and ask which one to use before creating, pushing, or scheduling. Never guess a publicationId.

Bodies are GitHub-flavored Markdown that Substack accepts: headings, bold, italic, strikethrough, inline code, links, images, lists, blockquotes, :::pullquote, :::callout, and a :::paywall::: line. Voice and video embeds are not supported.

create_article and update_article save in WriteStack only. Pushing and scheduling go through the writer’s Chrome extension. With WriteStack open, a push usually finishes in seconds. Otherwise it waits until WriteStack is next open.

Read before you write. Call get_article right before update_article and pass its contentVersion as baseVersion. A version conflict means the writer edited it, so read it again. Pushing an article that already has a Substack draft overwrites that draft.

  • list_article_newsletters (Read): List enabled newsletters with publicationId, title, URL, and which one is primary.
  • list_articles (Read): List articles from enabled newsletters, optionally by status or publicationId.
  • get_article (Read): Read one article: Markdown body, newsletter, content version, and Substack draft or schedule status.
  • create_article (Write): Create an article draft on the newsletter the writer chose, with an optional target date.
  • update_article (Irreversible): Replace title, subtitle, body, images, or Substack settings (audience, comments, SEO, slug, social title, publishAt) in WriteStack. No undo.
  • push_article_to_substack (Irreversible): Send or update the article as a Substack draft through the Chrome extension.
  • schedule_article_on_substack (Write): Schedule the article on Substack at publishAt. Pushes the draft first if it is not synced.
  • unschedule_article_on_substack (Irreversible): Remove the article from the Substack schedule. The draft stays on Substack.
  • get_article_substack_status (Read): Read the draft URL, scheduled time, and last sync error.

Statistics and activity

Read what worked, and what readers are saying.

Statistics tools need the matching WriteStack feature and a current extension. If a call fails, the error text says what to do next.

  • get_notes_statistics (Read): Published Note analytics: impressions, clicks, reactions, comments, restacks, profile visits, shares, subscribers, and an audience split when Substack stored one. Filter by date, sort, search, and paginate.
  • get_posts_statistics (Read): Post statistics for every newsletter on Posts Statistics: views, opens, open rate, delivery, likes, comments, restacks, shares, clicks, signups, subscriber split, and ARR. Use the returned openRate; email traffic share is a different number. Needs extension 1.5.25 or newer.
  • list_activities (Read): Substack comments and mentions, the same as the WriteStack activity feed, without the writer’s own posts. Paginate with skip and limit (at most 50).

Buffer cross-posting

Send a Note to LinkedIn, X, Threads, and more after it goes live on Substack.

Call list_buffer_channels before passing channel ids. Only returned ids are accepted, up to 20 per note. An empty list means the writer has not connected Buffer.

Instagram needs media. Instagram feed images must be between 4:5 and 1.91:1, or Buffer falls back to Notify Me. See https://support.buffer.com/article/622-instagrams-accepted-aspect-ratio-ranges

  • list_buffer_channels (Read): List connected Buffer channels with id, name, and service.

Feature ideas

When the writer asks for something the server cannot do.

  • get_feature_suggestion_link (Read): Return https://writestack.io/features, where the writer can post the idea for votes. It does not submit anything.

Limits and rules

What the server will not do, and what clients must not assume.

  • The server cannot publish Notes to Substack. The writer syncs with the yellow Sync button in WriteStack.
  • The server cannot unschedule Notes. The writer does that in WriteStack.
  • update_note and update_article overwrite the fields you send. Read first, confirm, then write.
  • Never schedule more Notes than get_schedule_capacity reports, unless unlimited is true.
  • Never invent publication ids, tag names, or Buffer channel ids. Read them from the matching list tool.
  • Auto-restack runs 1–168 hours after a Note publishes and needs WriteStack extension 1.4.78 or newer.
  • Personal data practices are in the privacy policy at https://www.writestack.io/privacy. Questions go to orel@writestack.io.

Next