About Substack MCP
Substack MCP is an MCP server published by arthurcolle in the AI category: model Context Protocol server for Substack integration with Claude Code.
The repository has 9 stars and 13 forks, with the last commit 8 months ago. Six months or more without a commit doesn't mean the server is broken, but check the open issues before depending on it in production.
Install
git clone https://github.com/arthurcolle/substack-mcpThis server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.
Ask AI
Ask AI about Substack MCP
Powered by Claude · Grounded in docs
Security checks
Static checks derived from the repository yesterday. Heuristics on the source, not an audit.
- ✓README presentThe repository ships documentation.
- !License declaredNo license file. Legally you have no right to use it in a product.
- ✓Tests presentA test suite exists in the repository.
- !Dependencies pinnedNo lockfile. Installs can pull different code over time.
- ✓No dynamic code executionSource scan found no eval, dynamic Function, or shell execution.
1 check has not been run yet.
README
Substack MCP Server
A Model Context Protocol (MCP) server for Substack integration with Claude Code and other MCP-compatible AI tools.
Features
- Create and manage drafts - Create, update, and publish Substack posts programmatically
- Image upload - Upload images to Substack's CDN with proper metadata
- Live blogging - Real-time post updates with timestamps
- Notes - Post short-form content to Substack Notes
- Full ProseMirror support - Proper document structure for Substack's editor
Installation
# Clone the repo
git clone https://github.com/acolle/substack-mcp.git
cd substack-mcp
# Install dependencies
pip install -r requirements.txt
Testing
python -m unittest discover -s tests
Setup
1. Get your Substack credentials
- Go to your Substack dashboard in Chrome/Safari
- Open DevTools (Cmd+Option+I or F12)
- Go to Application tab → Cookies → your-substack.substack.com
- Find
substack.sidand copy its value
2. Create credentials file
# Create ~/.substackrc
cat > ~/.substackrc << 'EOF'
export SUBSTACK_PUBLICATION="your-publication.substack.com"
export SUBSTACK_SID="your-cookie-value-here"
EOF
3. Add to Claude Code
# Add the MCP server to Claude Code
claude mcp add substack \
--command "bash" \
--args "-c" "source ~/.substackrc && python3 $(pwd)/substack_mcp/server.py"
Or manually add to ~/.claude.json:
{
"mcpServers": {
"substack": {
"command": "bash",
"args": ["-c", "source ~/.substackrc && python3 /path/to/substack-mcp/substack_mcp/server.py"]
}
}
}
Available Tools
| Tool | Description |
|---|---|
substack_create_draft |
Create a new draft post |
substack_update_draft |
Update an existing draft |
substack_append_to_draft |
Append content (for live blogging) |
substack_add_code_block |
Add a code block to a draft |
substack_add_image |
Add an image to a draft |
substack_publish |
Publish a draft |
substack_post_note |
Post a short note |
substack_get_drafts |
List all drafts |
substack_get_posts |
List published posts |
substack_live_blog_start |
Start a live blogging session |
substack_live_blog_end |
End live blogging session |
Usage Examples
Create a post with Claude Code
You: Create a new Substack post about AI tools
Claude: I'll create a draft for you...
[Uses substack_create_draft tool]
Created draft 12345. Edit at: https://your-pub.substack.com/publish/post/12345
Upload and embed images
You: Add this diagram to my post [image path]
Claude: I'll upload the image and add it to your draft...
[Uses substack_add_image tool]
Image added successfully.
Live blogging
You: Start a live blog for the product launch
Claude: Starting live blog session...
[Uses substack_live_blog_start tool]
Live blog started. I'll append updates as they happen.
API Reference
SubstackClient
The core client for interacting with Substack's API.
from substack_client import SubstackClient, SubstackDocument
# rate_limit is seconds between requests; timeout is per-request timeout.
client = SubstackClient(token="your-sid", publication="your-pub.substack.com", rate_limit=0.5, timeout=30.0)
# Upload an image
img = client.upload_image("/path/to/image.png")
# Returns: {"url": "https://...", "width": 800, "height": 600, "bytes": 12345, "contentType": "image/png"}
# Create a document
doc = SubstackDocument()
doc.heading("My Post", level=2)
doc.paragraph("Hello world!")
doc.image(src=img['url'], width=img['width'], height=img['height'],
bytes_size=img['bytes'], content_type=img['contentType'])
# Create and publish
draft = client.create_draft(title="My Post", body=doc)
client.publish_draft(draft.id, send_email=False)
Key Technical Details
Image Node Structure
Substack uses ProseMirror and requires specific attributes for images:
{
"type": "captionedImage",
"content": [{
"type": "image2",
"attrs": {
"src": "https://substack-post-media.s3.amazonaws.com/...",
"width": 800,
"height": 600,
"bytes": 12345,
"type": "image/png",
"internalRedirect": "https://pub.substack.com/i/{draft_id}?img={encoded_url}",
"belowTheFold": false,
"topImage": false,
"isProcessing": false
}
}]
}
The internalRedirect field is required - without it, Substack's editor fails to render the document.
Image Handling Notes
create_draftandupdate_draftautomatically add missinginternalRedirectvalues for any image nodes.- Image captions are emitted as separate italic paragraphs (not
imageCaptionnodes) to avoid editor rendering issues. - For best rendering results, upload images through
upload_image()and use the returned metadata.
Credits
License
MIT
Disclaimer
This uses Substack's unofficial/internal API which may change without notice. Use at your own risk.
README fetched from the repository yesterday. The original is authoritative.