v0.3.3·MIT·Python 3.12+·MCP-compatible·sibling: medium-ops ↗

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.

~/projects · zsh
no API keys/48 commands/26 MCP tools/SQLite dedup/propose / confirm/audit.jsonl/$0 AI bill/dry-run default/no API keys/48 commands/26 MCP tools/SQLite dedup/propose / confirm/audit.jsonl/$0 AI bill/dry-run default/
§ 01·Surface

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.

┌─ CLI ─┐
48commands

auth · posts · notes · comments · search · daemon

└────────┘
┌─ MCP ─┐
26tools

host-LLM drafts. you confirm. zero AI key.

└────────┘
┌─ AUDIT ─┐
2guards

SQLite dedup · JSONL append-only log

└────────┘
┌─ AUTO ─┐
1daemon + TUI

scheduled tasks · TUI · pid file safety

└────────┘
one line:uvx substack-ops mcp install cursor
§ 02·Read surface

Every 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.
before · manual workflow
# 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, repeat
after · substack-ops
from 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, audited
26
MCP tools
48
CLI commands
10
command groups
0
AI tokens spent
§ 03·Reply engine

Token-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.

paid LLM, retyping context~4,820 tok
propose / confirm0 AI tok
every reply pays your subscription, never a per-token bill
before · copy / paste with paid LLM
// 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 $$$
after · propose_reply / confirm_reply
// 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.
5m
token TTL
dedup hash
JSONL
audit log
$0
AI bill
§ 04·Safety stack

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.

SQLite dedup

Hash of (kind, target, body) deduped at write. Re-runs return the original audit entry.

JSONL audit log

Append-only, grep-friendly. Every dry-run and every real call lands in one file.

ancestor_path guard

Replies are pinned to the right thread root. No accidental top-level posts under the wrong comment.

rate limiter

Per-target spacing, configurable seconds. Gentle to Substack, gentle to your reputation.

dry-run default

Every write is a preview unless you flip --dry-run=false. Newcomers can't fire blanks.

dedup_status tool

Inspect the dedup DB live from your IDE. Never wonder if a draft already shipped.

§ 05·The 26-tool MCP server

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.

L01Read
10 tools

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
L02Write
9 tools

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
L03Safety
4 tools

Replays are no-ops. Mistakes leave a trail.

  • audit_search
  • dedup_status
  • get_unanswered_comments
  • bulk_draft_replies
L04Draft loop
3 tools

Token-gated. Host LLM drafts. You confirm. Substack ships.

  • propose_reply
  • confirm_reply
  • list_proposals
§ 06·Why this matters

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.

your Substack inbox
posts · notes · comments
your IDE
Cursor · Claude Desktop · Codex
your scripts
cron · GitHub Actions · TUI
substack-ops
one binary. four surfaces.
pip install substack-ops · uvx substack-ops · pipx install substack-ops
L01 · CLI
48 commands, every endpoint
L02 · MCP server
26 tools — host LLM drafts
L03 · Safety
dedup db · audit log · dry-run
L04 · Auto
daemon + TUI · pid-file lock
Substack API ←→ your subscribers
§ 07·Architecture

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.
featuresubstack-opsconorbronsdon/substack-mcpmanual / browser
CLI commands48
MCP server (tools)2612
Host-LLM drafting (no AI key)
SQLite dedup
JSONL audit log
Auto-install into Cursor / Claude
Daemon + TUI
Dry-run by defaultn/a
LicenseMITMIT