Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,38 @@ substack-mcp-setup
If Substack sent a sign-in email, paste the email link into the same setup
browser window before closing it.

### Magic-link email never arrives

Substack's anti-bot layer sometimes silently drops the magic-link email when
it is requested from the automated setup browser — no error is shown and the
wizard waits forever. The fallback is manual session-cookie auth, which the
server already supports through the `SUBSTACK_SESSION_TOKEN` environment
variable:

1. In your normal browser, already signed in to Substack, open
`https://substack.com`.
2. Open DevTools → Application tab (Chrome) or Storage (Firefox/Safari) →
Cookies → `https://substack.com`.
3. Copy the value of the `substack.sid` cookie. Either the decoded `s:...`
form or the URL-encoded `s%3A...` form works — the token is passed
directly as the cookie value.
4. Set it as `SUBSTACK_SESSION_TOKEN` in your MCP client config alongside
`SUBSTACK_PUBLICATION_URL`. For Claude Code:

```bash
claude mcp add substack --scope user \
--env SUBSTACK_PUBLICATION_URL=https://YOUR_PUBLICATION.substack.com \
--env SUBSTACK_SESSION_TOKEN=PASTED_COOKIE_VALUE \
-- substack-mcp
```

For Claude Desktop and other clients, add both variables to the server's
`env` block instead.

This cookie is password-equivalent — treat it like a secret. To revoke it,
sign out of all sessions in your Substack account settings, then capture a
fresh cookie.

### MCP shows connected but tools do not appear

This is usually a client session cache issue.
Expand Down
14 changes: 14 additions & 0 deletions docs/QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ Upload image from https://picsum.photos/800/400 optimized for web
| "No authentication found" | Run `substack-mcp-setup` |
| CAPTCHA appears | Solve it in the browser window |
| Email link opens elsewhere | Copy the link and paste it into the setup browser |
| Magic-link email never arrives | Substack sometimes silently drops it for automated browsers — use the [session-token fallback](#session-token-if-you-have-one) |
| Session expired | Run `substack-mcp-setup` again |
| Import errors | Reinstall with `npm install -g github:IgnazioDS/Substak-MCP` |

Expand All @@ -141,11 +142,24 @@ export SUBSTACK_PUBLICATION_URL="https://YOUR_PUBLICATION.substack.com"
```

### Session Token (If you have one)

This is also the reliable fallback when the wizard's magic-link email never
arrives (Substack sometimes silently drops it for automated browsers). To get
the token: in your normal signed-in browser open `https://substack.com`, then
DevTools → Application (Chrome) / Storage (Firefox/Safari) → Cookies →
`https://substack.com`, and copy the `substack.sid` cookie value (the `s:...`
and `s%3A...` forms both work).

```bash
export SUBSTACK_SESSION_TOKEN="YOUR_SUBSTACK_SESSION_TOKEN"
export SUBSTACK_PUBLICATION_URL="https://YOUR_PUBLICATION.substack.com"
```

This cookie is password-equivalent — treat it like a secret. Revoke it by
signing out of all sessions in your Substack settings.

Full walkthrough: [Authentication Guide](authentication.md#method-2-session-token-advanced).

## 📚 More Resources

- [Full Documentation](docs/)
Expand Down
26 changes: 24 additions & 2 deletions docs/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,27 @@ export SUBSTACK_PUBLICATION_URL="https://YOUR_PUBLICATION.substack.com"

### Method 2: Session Token (Advanced)

If you already have a session token:
Use this when the setup wizard cannot complete — most commonly when
Substack's anti-bot layer silently drops the magic-link email requested from
the automated browser (no error is shown; the wizard just waits).

Grab the session cookie from a browser where you are already signed in:

1. Open `https://substack.com` in your normal browser
2. Open DevTools → **Application** tab (Chrome) or **Storage** (Firefox/Safari) → Cookies → `https://substack.com`
3. Copy the value of the `substack.sid` cookie — the decoded `s:...` form and the URL-encoded `s%3A...` form both work, since the token is passed directly as the cookie value

Then set it:

```env
SUBSTACK_SESSION_TOKEN=YOUR_SUBSTACK_SESSION_TOKEN
SUBSTACK_SESSION_TOKEN=PASTED_COOKIE_VALUE
SUBSTACK_PUBLICATION_URL=https://YOUR_PUBLICATION.substack.com
```

**Security note**: this cookie is password-equivalent — treat it like a
secret. To revoke it, sign out of all sessions in your Substack account
settings, then capture a fresh cookie.

## 🚨 Troubleshooting

### "No authentication found" Error
Expand All @@ -144,6 +158,14 @@ The setup wizard handles CAPTCHA automatically. If you still have issues:
`substack-mcp-setup`. The setup must see the final signed-in browser state
before it can store the session.

### Magic-Link Email Never Arrives
Substack's anti-bot layer sometimes silently drops magic-link emails
requested from the automated setup browser — no error appears and the wizard
waits forever.

**Solution**: Use the manual session-cookie fallback described in
[Method 2: Session Token](#method-2-session-token-advanced).

### Session Expired
**Solution**: Run `substack-mcp-setup` to refresh

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ dependencies = [
# python-substack wraps reverse-engineered/private behaviors, so we keep an
# upper bound until each newer release is contract-tested against our fixtures.
"python-substack>=0.1.20,<0.1.23",
"mcp>=1.10.0",
"mcp>=1.10.0,<2", # mcp 2.0 removed the low-level Server.list_tools() decorator API
"markdown2>=2.4.10",
"pydantic>=2.5.0",
"python-dotenv>=0.21.0,<1.0.0",
Expand Down
2 changes: 1 addition & 1 deletion setup_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ async def _authenticate_with_browser(self) -> Optional[Dict[str, str]]:
# Navigate to Substack login
logger.info("Navigating to Substack login...")
await page.goto(
"https://substack.com/sign-in", wait_until="networkidle"
"https://substack.com/sign-in", wait_until="domcontentloaded"
)

# Use a manual-first flow because Substack's auth UI changes frequently,
Expand Down