Skip to content

About

Cross-Substack Q&A as a hosted MCP server for Claude Desktop. Cloudflare Workers + D1 + FTS5. Single-tenant v0.1.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

substack-mcp

A single-tenant hosted MCP server that lets you ask cross-publication questions about your Substack subscriptions inside Claude Desktop.

"What are my subs saying about X this week?" — the question generic ChatGPT can't answer because it doesn't have your subs. This is the missing connector.

What it is (v0.1)

  • Single-tenant. Deployed to your Cloudflare account. You're the only user. No accounts, no signups, no UI.
  • RSS-only. Polls the public /feed endpoint of every Substack you list. Works for free posts; paid-only posts return only public teasers in v0.1 (paid content support → v2 via email forwarding).
  • MCP-native. Exposes a JSON-RPC 2.0 endpoint at /mcp. Drop the URL + bearer token into Claude Desktop's config; ask questions in chat.
  • Cheap. Cloudflare Workers + D1. Free tier comfortably covers a single user with ~50 subs.

MCP tools exposed

Tool Purpose
search_subscriptions FTS5 keyword search across all your indexed articles. The main one.
list_publications List indexed pubs with article counts and latest post date.
get_recent_posts N most recent posts from one pub (by slug).
get_article Full extracted text of one article by URL.

Admin endpoint

Route Purpose
POST /admin/poll Manually trigger the RSS poller now. Same Authorization: Bearer as /mcp. Useful after editing SUBSTACK_FEEDS and redeploying — saves 5 minutes.

Setup (one-time, ~15 minutes)

1. Install + create local config

npm install
cp wrangler.toml.example wrangler.toml

wrangler.toml is gitignored — that's where your real database_id and SUBSTACK_FEEDS list will live. The .example template is the version that ships in the repo.

2. Cloudflare account + Wrangler

You need a Cloudflare account (free tier is fine). Then:

npx wrangler login

3. Create the D1 database

npx wrangler d1 create substack-mcp

Copy the database_id from the output and paste it into wrangler.toml (replacing REPLACE_WITH_YOUR_D1_DATABASE_ID).

4. Apply the schema migration

npm run migrate:remote

(For local dev you can use npm run migrate:local against Miniflare's local D1.)

5. Set the bearer token secret

Generate a random token (any string >32 chars) and set it as a Worker secret:

# Generate one if you don't have a preferred method:
node -e "console.log(crypto.randomBytes(32).toString('base64url'))"

# Then set it:
npx wrangler secret put MCP_BEARER
# Paste the token when prompted

Save the token — you'll paste it into Claude Desktop's config below.

6. Configure your subscription list

Edit wrangler.toml. Set SUBSTACK_FEEDS to a comma-separated list:

[vars]
SUBSTACK_FEEDS = "https://www.astralcodexten.com/feed,https://stratechery.com/feed,https://acme.substack.com"

You can paste either bare hosts (stratechery.com) or full feed URLs (https://stratechery.com/feed). The poller normalizes both.

7. Deploy

npm run deploy

Wrangler prints the deployed URL, e.g. https://substack-mcp.your-subdomain.workers.dev.

8. Wait for the first cron tick

The cron runs every 5 minutes. The first run will populate publications from your SUBSTACK_FEEDS list and start fetching articles. Within ~10 minutes you should have content.

You can verify by curling:

curl https://substack-mcp.your-subdomain.workers.dev/health
# → {"status":"ok"}

curl -X POST https://substack-mcp.your-subdomain.workers.dev/mcp \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_publications","arguments":{}}}'

9. Connect Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS):

{
  "mcpServers": {
    "substack-mcp": {
      "url": "https://substack-mcp.your-subdomain.workers.dev/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_TOKEN"
      }
    }
  }
}

Note: Claude Desktop's remote-MCP config keys may vary by version. If your version doesn't support url + headers directly, use mcp-remote as a stdio adapter:

{
  "mcpServers": {
    "substack-mcp": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://substack-mcp.your-subdomain.workers.dev/mcp", "--header", "Authorization: Bearer YOUR_TOKEN"]
    }
  }
}

Restart Claude Desktop. You should see substack-mcp in the MCP servers list.

10. Try it

Open Claude Desktop and ask:

"What are my subs saying about [topic] this week?"

Claude will call search_subscriptions with your query and synthesize an answer with citations.

Development

npm run dev          # local Wrangler dev server with Miniflare
npm test             # run unit tests
npm run typecheck    # tsc --noEmit

Architecture (v0.1)

┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│   Cron (every 5 min) ──► poller.ts ──► fetches up to 25         │
│                                         eligible /feed URLs     │
│                                                ▼                │
│                                        rss.ts (parse XML)       │
│                                                ▼                │
│                                        extract.ts (clean HTML)  │
│                                                ▼                │
│                                        articles.ts (upsert)     │
│                                                ▼                │
│                                          ┌──────────┐           │
│                                          │ D1 SQLite│           │
│                                          │ + FTS5   │           │
│                                          └──────────┘           │
│                                                ▲                │
│                                                │                │
│   POST /mcp ──► index.ts (auth) ──► mcp.ts ────┘                │
│      ▲                                                          │
│      │                                                          │
│   Claude Desktop                                                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

What's intentionally missing in v0.1

These are deferred to v0.2+ (gated on you actually using v0.1 daily for 2+ weeks):

  • Multi-tenancy (users, signups, magic-link, JWT, multi-user MCP tokens)
  • Web UI (landing page, settings page)
  • Email-forwarding ingestion (paid Substack content)
  • ChatGPT support (needs OAuth 2.1 PKCE)
  • Vector search

If you want to ship v0.2 for "Brian" or anyone else, that's a separate ~7-day sprint. v0.1 first; signal first.

Known limitations (v0.1)

  • Free Substack content only — paid posts return public teasers only via RSS.
  • No historical backfill — only articles published after a sub is added are indexed.
  • Claude Desktop only — ChatGPT integration is v1.1 work.
  • No UI for managing subs — edit wrangler.toml and redeploy.

License

MIT.

About

Cross-Substack Q&A as a hosted MCP server for Claude Desktop. Cloudflare Workers + D1 + FTS5. Single-tenant v0.1.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages