substack-mcp
by nanameru
README.md
# substack-mcp
A Model Context Protocol (MCP) server for Substack. Lets Claude Code create
drafts, upload images, set cover thumbnails, schedule, and publish posts on
your Substack publication.
> Built on top of [`python-substack`](https://github.com/ma2za/python-substack).
> Uses Substack's internal API (no public posting API exists). Not affiliated
> with Substack Inc.
## Tools
**Required**
- `create_draft(title, content_markdown, subtitle?, audience?)` — Create a new draft from Markdown.
- `update_draft(post_id, title?, subtitle?, content_markdown?, audience?)` — Edit an existing draft.
- `upload_image(image_path)` — Upload a local file or remote URL to Substack's CDN, returning the URL.
- `publish_draft(post_id, send_email?, share_automatically?)` — Publish immediately. `send_email` toggles email delivery.
**Recommended**
- `schedule_draft(post_id, iso_datetime)` — Schedule a publish for a future date/time (ISO 8601).
- `unschedule_draft(post_id)` — Cancel a scheduled publish.
- `set_cover_image(post_id, image_url)` — Set the cover thumbnail (from `upload_image` URL).
**Utility**
- `list_drafts(limit?)` — List recent drafts.
- `get_draft(post_id)` — Get a draft's full body.
- `delete_draft(post_id)` — Permanent deletion.
## Setup
```bash
# 1. Install dependencies
uv pip install -e .
# 2. Make sure you're logged in to Substack in Chrome (or Brave/Edge) — that's it.
# 3. Save credentials — auto-detects your existing browser session
substack-mcp-setup
# 4. Register with Claude Code
claude mcp add substack-mcp --scope user -- /Users/$USER/substack/.venv/bin/substack-mcp
```
Restart Claude Code, then `/mcp` should show `substack-mcp` as `connected`.
### (Optional) Install the `substack-article` skill
This repo also ships a [Vercel Skills](https://skills.sh/)-compatible **agent skill**
that uses the MCP above to plan, draft, generate a thumbnail, and publish a
Substack article in one flow.
```bash
# Globally for all projects
npx skills add nanameru/substack-mcp -g -a claude-code -y
# Or scoped to the current project
npx skills add nanameru/substack-mcp -a claude-code
```
Restart Claude Code. The skill is automatically invoked when you say things like
「Substack 記事を書いて」 / 「Substack に投稿して」 / 「短文ポストして」.
`npx skills` works with Claude Code, Cursor, Codex, OpenCode, GitHub Copilot,
and 50+ other agents. See [skills.sh](https://skills.sh/) for details.
The skill expects:
- `substack-mcp` registered as an MCP (above)
- `codex` MCP registered (for thumbnail generation; optional if you skip thumbnails)
- macOS with Hiragino fonts installed (default on macOS for Japanese text overlay)
See `skills/substack-article/SKILL.md` for the full flow and guardrails.
### How auth works
By default `substack-mcp-setup` reads the `substack.sid` cookie directly from
your existing Chrome session via [pycookiecheat](https://pypi.org/project/pycookiecheat/).
Substack can't tell anything was automated because **nothing was**: it's the
same session you're already using.
macOS will prompt once for Keychain access ("Chrome Safe Storage"). Click
"Always Allow" so it doesn't ask again next time.
Supports: Chrome, Brave, Edge, Chromium, Vivaldi, Opera.
### Fallback modes
```bash
# Specific browser
substack-mcp-setup --from-browser brave
# Playwright-based (often blocked by Substack — use --chrome instead)
substack-mcp-setup --browser
# Manual paste from DevTools
substack-mcp-setup --manual
```
Tokens are stored at `~/Library/Application Support/substack-mcp/config.json`
with `0600` permissions.
## Security
The `substack.sid` cookie is **equivalent to a password** — anyone with it has
full account access (publish posts, edit billing, etc.). Treat it as such.
### Where the token lives
- macOS: `~/Library/Application Support/substack-mcp/config.json` (mode `0600`)
- Linux: `~/.config/substack-mcp/config.json` (mode `0600`)
- Or via env vars: `SUBSTACK_PUBLICATION_URL` + `SUBSTACK_SESSION_TOKEN` (env
vars are inherited by child processes — be aware when spawning subprocesses)
The `.gitignore` excludes `config.json`; never commit it. The MCP also writes
a temporary cookie file via `tempfile.mkstemp` (mode `0600`) and deletes it in
a `finally` block — see `auth.py:write_cookie_file`.
### If a token leaks
1. **Sign out of all sessions**: Substack → Settings → Security → "Sign out of
all sessions". This invalidates every existing `substack.sid` immediately.
2. Log back in to Substack in your browser.
3. Re-run `substack-mcp-setup` to capture the new cookie.
### Image upload safety
`upload_image` only accepts:
- HTTP(S) URLs, or
- Local files with image extensions (`.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`,
`.heic`, `.heif`) that are **not** under sensitive system paths
(`/etc`, `/System`, `~/.ssh`, `~/.aws`, `~/Library/Keychains`, etc.)
This guards against an assistant being tricked (via prompt injection in
fetched content) into uploading e.g. an SSH private key to Substack's CDN.
**Known limitation**: Markdown image syntax `` inside `create_draft`
is processed by `python-substack` and bypasses this validation. If you pass
untrusted Markdown, sanitize image paths first.
### Dependencies
Versions are pinned with `~=` (compatible release, no major bumps). Bumping
`python-substack` in particular should be reviewed — it talks to Substack's
private API and lives outside Substack's official surface.
## Notes
- `audience` accepts: `everyone` (default), `only_paid`, `founding`, `only_free`.
- Markdown image syntax `` auto-uploads local files when you call `create_draft`.
- The cover image (set via `set_cover_image`) is what appears on your publication
homepage and in social shares. If you don't set one explicitly, Substack
typically uses the first image in the body.
TDQS
A4/5.0
Scored across 11 tools
Disambiguation5/5
Each tool has a clearly distinct purpose: draft CRUD, publishing, scheduling, notes, and image operations. No two tools overlap in functionality.
Naming Consistency5/5
All tools follow a consistent verb_noun pattern with underscores (e.g., create_draft, publish_draft, upload_image), making the API predictable.
Tool Count5/5
11 tools is well-scoped for a Substack server, covering drafting, publishing, scheduling, notes, and images without being excessive.
Completeness4/5
The tool set covers core drafting and publishing workflows comprehensively, but lacks tools for managing published posts, subscribers, or analytics, which are minor gaps for a content creation MCP.
Maintenance
ActivityInactive
ResponsivenessResponsive