Reply to your Substack
from Cursor.
Without an API bill.
substack-ops is a standalone Substack CLI and a 26-tool MCP server. Your IDE's LLM does the drafting through propose_reply / confirm_reply. You never pay an Anthropic or OpenAI bill for a reply you'd write yourself anyway.
$ uvx substack-ops mcp install cursor
Writes ~/.cursor/mcp.json. Restart Cursor.
One binary. Four surfaces.
The same Python package you pip install shows up as a CLI, an MCP server, an audit layer, and a daemon. Pick whichever surface fits the moment.
auth · posts · notes · comments · search · daemon
host-LLM drafts. you confirm. zero AI key.
SQLite dedup · JSONL append-only log
scheduled tasks · TUI · pid file safety
uvx substack-ops mcp install cursorEvery Substack endpoint, one import.
Posts, notes, comments, search, feed, profiles. Read-only by design, scriptable by default, idempotent by convention.
- ·RSS-equivalent reads with paging, sort, search.
- ·Markdown conversion built-in via
as_markdown. - ·Search hits Substack's full-text endpoint, no scraping.
- ·Profile + feed for For-You / Subscribed tabs.
# 3 browser tabs · 17 clicks · ~6 min
1. open dashboard.substack.com
2. switch to Notes tab
3. scroll, copy/paste threads…
4. tab to Cursor, paste, ask LLM
5. copy reply back, paste in browser
6. submit, wait, repeatfrom substack_ops import SubstackOps
so = SubstackOps()
posts = so.posts.list(limit=20)
for p in posts:
threads = so.comments.unanswered(p.id)
for t in threads:
... # one shot, scriptable, auditedToken-gated propose → confirm.
The host LLM drafts. You see the preview. You confirm. The token expires in 5 minutes. SQLite dedup means a stuck loop replays as a no-op.
// you, in chat
"draft replies to my unanswered comments"
// LLM:
"sure, can you paste the comments here?"
... 12 more turns of copy/paste ...
$$$ tokens spent retyping context $$$// host LLM, via MCP
1. tool: get_unanswered_comments(post_id=…)
2. tool: propose_reply(token=A, body="…")
3. you: ✓ ship it
4. tool: confirm_reply(token=A)
→ posted, deduped, audited.
→ token TTL 5 min, single-use.Replays are no-ops. Mistakes leave a trail.
Six guards stack on every write. None of them are opt-in — they ship in the box, dry-run by default.
Hash of (kind, target, body) deduped at write. Re-runs return the original audit entry.
Append-only, grep-friendly. Every dry-run and every real call lands in one file.
Replies are pinned to the right thread root. No accidental top-level posts under the wrong comment.
Per-target spacing, configurable seconds. Gentle to Substack, gentle to your reputation.
Every write is a preview unless you flip --dry-run=false. Newcomers can't fire blanks.
Inspect the dedup DB live from your IDE. Never wonder if a draft already shipped.
flagship
Four layers. All shipping.
Tool definitions are written for TDQS — every entry tags side effects, parameter shape, and the sibling tool you should reach for instead.
Layers stack: Read never writes. Write always dry-runs first. Safety dedups + audits every call. Draft loop hands the keyboard back to you.
Pull anything from Substack. Zero side effects.
- test_connection
- get_own_profile
- get_profile
- list_posts
- get_post
- get_post_by_id
- get_post_content
- search_posts
- list_notes
- list_comments
- get_feed
Every mutation is dry-run first. Nothing ships unless you flip the flag.
- publish_note
- reply_to_note
- comment_on_post
- react_to_post
- react_to_comment
- restack_post
- restack_note
- delete_comment
- send_approved_drafts
Replays are no-ops. Mistakes leave a trail.
- audit_search
- dedup_status
- get_unanswered_comments
- bulk_draft_replies
Token-gated. Host LLM drafts. You confirm. Substack ships.
- propose_reply
- confirm_reply
- list_proposals
Stop double-paying
for tokens.
Most “AI” CLIs make you bring an ANTHROPIC_API_KEY. You then pay twice — once for your Cursor / Claude subscription, once for tokens your IDE could have generated for free.
MCP fixes that. The IDE talks to substack-ops over stdio, calls get_unanswered_comments, drafts the reply locally, hands it to propose_reply — you confirm — Substack ships it. The CLI is just hands.
How we compare to the field.
One toolkit, two real alternatives, one row per capability. No asterisks.
- Substack-side parity: every endpoint we found, exposed.
- Operational parity: dedup + audit + dry-run, baked in.
- Distribution parity: PyPI, official MCP Registry, Glama.
| feature | substack-ops | conorbronsdon/substack-mcp | manual / browser |
|---|---|---|---|
| CLI commands | 48 | — | — |
| MCP server (tools) | 26 | 12 | — |
| Host-LLM drafting (no AI key) | ✓ | ✗ | ✗ |
| SQLite dedup | ✓ | ✗ | ✗ |
| JSONL audit log | ✓ | ✗ | ✗ |
| Auto-install into Cursor / Claude | ✓ | ✗ | ✗ |
| Daemon + TUI | ✓ | ✗ | ✗ |
| Dry-run by default | ✓ | ✗ | n/a |
| License | MIT | MIT | — |
Public package, public registry, public PRs.
Six surfaces of receipts. Click any card to verify.
Open an issue. Open a PR. Or just install it and tell me what broke.