MCP server for Substack — read posts, manage drafts. No publish or delete by design.
claude mcp add substack-mcp -- npx -y playwright{
"mcpServers": {
"substack-mcp": {
"command": "npx",
"args": ["-y", "playwright"]
}
}
}MCP Servers overview
<div align="center">
# substack-mcp
An MCP server for Substack. Read your publication data and manage drafts from your AI agent. Long-form posts are draft-only by design — no publish, no delete. Short-form Notes publish immediately.
[](LICENSE)
[](https://www.typescriptlang.org/)
[](https://www.npmjs.com/package/@conorbronsdon/substack-mcp)
[](https://modelcontextprotocol.io/)
[](https://chainofthought.show/?utm_source=github&utm_medium=referral&utm_campaign=repo-readme&utm_content=substack-mcp)
[](https://x.com/ConorBronsdon)
</div>
---

An MCP server for Substack that lets AI assistants read your publication data and manage drafts. The draft list shown in the demo above is sample data, not real account values.
**Safe by design — with one loud exception:** This server cannot publish or delete long-form posts. Post tools create and edit drafts only; you review and publish manually through Substack's editor. The exception is Substack **Notes**: `create_note` and `create_note_with_link` publish short-form Notes immediately, because Notes have no draft state on Substack. Treat the Note tools as public-publish actions — there is no preview step and no undo from this server.
<a href="https://glama.ai/mcp/servers/conorbronsdon/substack-mcp">
<img width="380" height="200" src="https://glama.ai/mcp/servers/conorbronsdon/substack-mcp/badge" alt="substack-mcp MCP server" />
</a>
## About
Built and maintained by [Conor Bronsdon](https://github.com/conorbronsdon) for the [Chain of Thought](https://chainofthought.show/?utm_source=github&utm_medium=referral&utm_campaign=repo-readme&utm_content=substack-mcp) podcast production workflow, where it drafts and reviews newsletter posts before a human hits publish. Conor hosts Chain of Thought, a show about AI infrastructure and how practitioners actually build with it. More tools for creators live in [ai-tools-for-creators](https://github.com/conorbronsdon/ai-tools-for-creators). Find Conor on X at [@ConorBronsdon](https://x.com/ConorBronsdon).
**Sibling MCP servers:**
- [Transistor-MCP](https://github.com/conorbronsdon/Transistor-MCP): manage podcast episodes, analytics, and transcripts on Transistor.fm
- [podcastindex-mcp](https://github.com/conorbronsdon/podcastindex-mcp): search the Podcast Index and track guest appearances
## Tools
Every tool declares MCP [tool annotations](https://modelcontextprotocol.io/docs/concepts/tools#tool-annotations), set **explicitly** rather than left to MCP's defaults (an omitted `destructiveHint` or `openWorldHint` defaults to `true`). Reads carry `readOnlyHint: true`. Every write is additive, so all writes carry `destructiveHint: false`. Draft writes are private (`openWorldHint: false`); `upload_image` carries `openWorldHint: true` because it returns a publicly-fetchable CDN URL; and the Note tools carry `openWorldHint: true` for immediate public publish. Annotations are untrusted hints, so the authoritative wording lives in each tool's description.
### Read
| Tool | Description |
|------|-------------|
| `get_subscriber_count` | Get your publication's current subscriber count |
| `list_published_posts` | List published posts with pagination |
| `list_drafts` | List draft posts |
| `get_post` | Get full content of a published post by ID |
| `get_draft` | Get full content of a draft by ID |
| `get_post_comments` | Get comments on a published post |
| `get_sections` | List your publication's sections (categories) with their IDs |
| `get_post_analytics` | Get a published post's stats (views, opens, signups, subscribes, reactions) by ID |
| `list_scheduled_posts` | List posts scheduled for future publication (read-only; scheduling stays in Substack's editor) |
### Write (private drafts; image upload returns a public URL)
| Tool | Description |
|------|-------------|
| `create_draft` | Create a new draft from markdown (private) |
| `update_draft` | Update an existing draft (unpublished only; private) |
| `upload_image` | Upload an image to Substack's CDN — returns a publicly-fetchable (unlisted) URL |
### Publish (Notes — public immediately)
| Tool | Description |
|------|-------------|
| `create_note` | Publish a Substack Note (short-form, **publishes immediately**) |
| `create_note_with_link` | Publish a Note with a link card attachment (**publishes immediately**) |
Notes have no draft state on Substack, so there is no draft-first option for these two tools.
### Intentionally excluded
- **Publish posts** — Publishing long-form posts should be a deliberate human action (Notes are the documented exception above)
- **Delete** — Too destructive for an AI tool
- **Schedule** — Use Substack's editor for scheduling. (`list_scheduled_posts` *reads* what you've queued there, but this server never creates, edits, or cancels a schedule.)
## Setup
You can supply credentials two ways: paste them as env vars (below), or run the
optional **browser login** which captures and stores them for you.
### Option A — Browser login (optional, no manual cookie copying)
Removes the DevTools cookie hunt and the ~90-day re-copy. Playwright is **not**
bundled (it's large), so install it once, then sign in:
```bash
npm i -g playwright && npx playwright install chromium
npx --package @conorbronsdon/substack-mcp substack-mcp-login https://yourblog.substack.com
```
A browser opens; sign in to Substack (CAPTCHA included). The tool captures your
session cookie, auto-resolves your user id, and writes them to
`~/.substack-mcp/session.json` (override the directory with `SUBSTACK_MCP_HOME`).
The MCP server reads that file automatically whenever the `SUBSTACK_*` env vars
are unset — so with browser login you can omit the `env` block entirely.
**Storage & security:** the file is written `0600` and encrypted with AES-256-GCM
under a key derived from this OS account + machine (never stored). A copied file
is useless elsewhere and casual disk/backup reads see only ciphertext. This is
machine-binding + obfuscation, **not** a secret vault — code running as you on
this machine can re-derive the key (the same caveat as the plaintext env-var
path). If you prefer, use Option B and let your MCP client handle the secret.
### Option B — Get your credentials manually
Open your Substack in a browser, then:
1. **Session token:** Navigate to your publication, open DevTools → Application → Cookies → copy the value of `connect.sid` (URL-encoded string starting with `s%3A`)
2. **User ID:** In DevTools Console, run: `fetch('/api/v1/archive?sort=new&limit=1').then(r=>r.json()).then(d=>console.log(d[0]?.publishedBylines?.[0]?.id))`
3. **Publication URL:** Your Substack URL, including custom domain if you have one (e.g., `https://newsletter.yourdomain.com` or `https://yourblog.substack.com`)
### 2. Configure your MCP client
#### Claude Desktop
Add to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"substack": {
"command": "npx",
"args": ["-y", "@conorbronsdon/substack-mcp"],
"env": {
"SUBSTACK_PUBLICATION_URL": "https://yourblog.substack.com",
"SUBSTACK_SESSION_TOKEN": "your-session-token",
"SUBSTACK_USER_ID": "your-user-id"
}
}
}
}
```
#### Claude Code
Add to your `.mcp.json`:
```json
{
"mcpServers": {
"substack": {
"command": "npx",
"args": ["-y", "@conorbronsdon/substack-mcp"],
"env": {
"SUBSTACK_PUBLICATION_URL": "https://yourblog.substack.com",
"SUBSTACK_SESSION_TOKEN": "your-session-token",
"SUBSTACK_USER_ID": "your-user-id"
}
}
}
}
```
### 3. Verify
Ask your AI assistant: "How many Substack subscribers do I have?"
## Token expiration
Substack session tokens expire periodically (typically ~90 days). If you get authentication errors, grab a fresh `connect.sid` cookie from your browser and update the env var (make sure ad blockers are disabled when copying the cookie) — or, if you used the browser login, just re-run `substack-mcp-login` to refresh the stored session.
## Custom domains & Cloudflare
Substack publications served on a custom domain (e.g. `blog.example.com`) sit behind Cloudflare, which can reject non-browser requests with `403 error code: 1010`. To avoid this, the server sends a browser `User-Agent` and a `Referer` by default, and addresses the publication by its canonical `*.substack.com` host.
- **Use the canonical host.** Set `SUBSTACK_PUBLICATION_URL` to the publication's `*.substack.com` address rather than the custom domain. Calls to the canonical host are served directly; custom-domain calls may 301-redirect and then 401.
- **Override the User-Agent** (optional) via `SUBSTACK_USER_AGENT` if you need a different browser signature:
```json
"env": {
"SUBSTACK_PUBLICATION_URL": "https://yourblog.substack.com",
"SUBSTACK_SESSION_TOKEN": "your-session-token",
"SUBSTACK_USER_ID": "your-user-id",
"SUBSTACK_USER_AGENT": "Mozilla/5.0 ..."
}
```
## Typed errors
API failures are mapped to a typed error hierarchy (`SubstackAPIError` base, with `AuthenticationError`, `RateLimitError`, `ValidationError`, `NotFoundError`, and `ServerError` subclasses keyed off HTTP status) in `src/utils/errors.ts`. Every tool call still surfaces the same error response shape on failure — the typed hierarchy just makes the message specific to what went wrong instead of a single generic "Substack API error" string.
| Class | StaWhat people ask about substack-mcp
What is conorbronsdon/substack-mcp?
+
conorbronsdon/substack-mcp is mcp servers for the Claude AI ecosystem. MCP server for Substack — read posts, manage drafts. No publish or delete by design. It has 16 GitHub stars and was last updated yesterday.
How do I install substack-mcp?
+
You can install substack-mcp by cloning the repository (https://github.com/conorbronsdon/substack-mcp) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.
Is conorbronsdon/substack-mcp safe to use?
+
conorbronsdon/substack-mcp has not been audited yet by our security agent. Review the original repository on GitHub before using it in production.
Who maintains conorbronsdon/substack-mcp?
+
conorbronsdon/substack-mcp is maintained by conorbronsdon. The last recorded GitHub activity is from yesterday, with 1 open issues.
Are there alternatives to substack-mcp?
+
Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.
Deploy substack-mcp to your cloud
Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.
Maintain this repo? Add a badge to your README
Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.
[](https://claudewave.com/repo/conorbronsdon-substack-mcp)<a href="https://claudewave.com/repo/conorbronsdon-substack-mcp"><img src="https://claudewave.com/api/badge/conorbronsdon-substack-mcp" alt="Featured on ClaudeWave: conorbronsdon/substack-mcp" width="320" height="64" /></a>More MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
The fastest path to AI-powered full stack observability, even for lean teams.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!