Skip to main content
Glama
wkbaran

Substack Reader MCP

by wkbaran

Substack Reader MCP

Ask Claude what's new in your Substack subscriptions, and have it read the posts for you.

Node 20+ TypeScript MCP Tools Jev License: MIT

Quick start · Tools · Daily digest · Headline ranking with Jev · Logging in · Troubleshooting

An MCP server for your own Substack account:

  • Read: your subscriptions, a combined feed, full posts (including paid ones you have access to), and search.

  • Your reading activity: reading history with how far you read each post, plus saved and hearted posts.

  • Chats: publication chats and direct messages.

  • Act, when you ask: subscribe to and unsubscribe from free newsletters.

  • Daily digest: with Hermes Agent, a scheduled morning digest. Jev can rank posts against your interests before they're read.

  • No API keys for Substack: you log in once in a browser window.

What it uses

What

Needed?

Your Substack account

Yes. Free and paid subscriptions both work

Node.js 20+

Yes

Chrome or Edge

Only for the browser login (you can paste a cookie instead)

A Jev-style decision model

Optional. Ranks digest posts against your interests in a few seconds. Any provider with the same decisions API works, set by URL, key and model like an OpenAI-compatible client. The default is TypeSafe's Jev on OpenRouter, about $0.03 per 1,000 posts

An MCP client

Claude Code, Claude Desktop, Cursor, VS Code, Hermes Agent…

Substack has no public API. The server calls the endpoints substack.com uses, with your session, so it sees only what you see logged in.

Related MCP server: Fenn MCP server

Quick start

git clone https://github.com/wkbaran/substack-reader-mcp.git
cd substack-reader-mcp
npm install && npm run build

node dist/cli.js login     # a browser window opens; sign in to Substack as usual
node dist/cli.js install   # registers the server with Claude Code

Restart Claude Code, then try:

  • "What's new in my Substack feed this week?"

  • "Summarize the latest post from The Pragmatic Engineer."

  • "Which of my saved posts did I never finish?"

  • "What are people talking about in Nate's subscriber chat?"

  • "Propose an interests.md from my Substack activity."

Point the client at dist/cli.js with an absolute path, after running login:

{ "mcpServers": { "substack-reader": { "command": "node", "args": ["/absolute/path/to/substack-reader-mcp/dist/cli.js"] } } }
  • Claude Desktop: claude_desktop_config.json (Settings → Developer → Edit Config)

  • Cursor: ~/.cursor/mcp.json

  • VS Code: .vscode/mcp.json, using "servers" instead of "mcpServers" and adding "type": "stdio"

  • Claude Code, manually: claude mcp add --scope user substack-reader -- node /absolute/path/to/dist/cli.js

Tools

How to name things:

  • Publications: a name, part of a name ("pragmatic"), subdomain or URL.

  • Posts: any Substack link, or a post id.

Reading (all read-only)

Tool

What it returns

get_feed

Recent posts across all subscriptions, newest first. since takes "7d", "48h" or a date

read_post

A full post as Markdown. Paid posts you can't access are flagged as previews

get_recent_posts · search_posts

One publication's latest posts · keyword search within one publication

list_subscriptions

Every subscription, including hidden ones, with membership

get_reading_history

Posts you opened on the web or in the app, with how far you read each one, and whether you hearted or saved it

get_saved_posts · get_liked_posts

Posts you saved · posts you hearted

interests_evidence

What your activity says about your taste, for drafting an interests.md

auth_status

Who you're logged in as, and whether the session works

Chat (read-only; Substack's unread flags aren't reliable, so these work by activity time)

Tool

What it returns

list_chats

Publication chats you're in, and direct messages

get_chat_activity

Chats and DMs with activity since a time, optionally with the new messages

get_chat_threads · read_chat_thread · read_dm

A chat's threads · one thread with its replies · one DM conversation

Account changes (clients ask first; the server re-checks with Substack afterwards)

Tool

What it does

subscribe

Free-subscribes you. Never starts a paid plan

unsubscribe

Removes a free subscription. Anything with a payment attached is refused

Digest (only when SUBSTACK_DIGEST_DIR is set; plain-text output for agent harnesses)

Tool

What it does

digest_begin

Fetches every new post and chat activity, and returns a work list with refs (P1, P2, …)

digest_finish

Takes a verdict per post and chat, renders the message and saves state. Safe to repeat

digest_status · mark_reported

Inspect the state · repair it

save_interests_proposal

Saves a drafted interests.md as interests.proposed.md, never over the real one

A worked example of a whole run, with each tool's arguments and output: docs/digest-tools.md.

Daily digest with Hermes Agent

Hermes Agent runs skills on a schedule and delivers to Discord and other chats. hermes/SKILL.md turns this server into a morning digest:

  • What it covers: every new post in your subscriptions, plus chats and DMs with new activity.

  • How it reads: subagents read every post in full.

  • What you get: one message, with:

    • picks worth reading in full, each with a summary and a reason

    • everything else, grouped by publication

    • chats, with anything addressed to you first

  • What the model does: only the judging. Fetching, retries, state and layout are code, so the whole digest can run on a local 27B model.

Setup

  1. Build (npm ci && npm run build) and copy dist/, package.json and package-lock.json to where Hermes can see them, e.g. /opt/data/mcp/substack-reader-mcp. Then run npm ci --omit=dev --omit=optional there.

  2. Log in on a machine with a browser (node dist/cli.js login), then copy ~/.config/substack-reader/auth.json to the Hermes host, e.g. /opt/data/mcp/substack-reader-home/.

  3. Register the server in Hermes's config.yaml:

    mcp_servers:
      substack-reader:
        command: node
        args: ["/opt/data/mcp/substack-reader-mcp/dist/cli.js"]
        env:
          SUBSTACK_READER_HOME: /opt/data/mcp/substack-reader-home
          SUBSTACK_DIGEST_DIR: /opt/data/sandbox/substack_digest   # absolute; created if needed
          SUBSTACK_DIGEST_TZ: America/Denver
          # SUBSTACK_CLASSIFIER: jev                               # optional; see below
          # SUBSTACK_JEV_API_KEY: ${JEV_OPENROUTER_API_KEY}        # from Hermes's .env
          # SUBSTACK_JEV_URL: https://openrouter.ai/api/alpha/decisions   # the default; any decisions-API endpoint
  4. Install the skill: copy hermes/SKILL.md to skills/productivity/substack-digest/ and hermes/substack_digest_start.sh to scripts/. Then edit the skill's Settings block. Optionally add an interests.md to the digest directory (from hermes/interests.example.md, or drafted from your activity).

  5. Schedule it (run as the hermes user). The job needs only the delegation toolset and this server:

    hermes cron create "0 7 * * *" "Run the substack-digest skill and deliver the digest." \
      --name substack-digest --skill substack-digest --script substack_digest_start.sh --deliver discord:<channel-id>

More detail is in docs/digest.md: why the server has harness-specific tools, customizing, state, rate limits, chats and cost.

Headline ranking with Jev

Optional here, and off by default.

  • Your Substack feed is only what you subscribe to, so it's already curated, and the digest can afford to read every post.

  • This classifier was built for medium-reader-mcp, where the feed is effectively endless (100–200 new posts a day from writers you never chose), and was carried over here.

What it still adds on Substack:

  • Jev is a "decision model" from TypeSafe. It returns typed answers with probabilities, not text. Before anything is read, it gives every post:

    • a rank (how much you'd want it, judged against interests.md)

    • a skip probability (whether it matches your Skip list)

  • In the digest:

    • Posts are read best-first, and the rank informs the "Read in full" picks.

    • Confident skips (say, podcast show notes) aren't read at all.

    • With SUBSTACK_DIGEST_RANK_FLOOR, low-ranked posts become optional, which helps if your subscription list grows.

  • Backends (SUBSTACK_CLASSIFIER):

    • off (default).

    • jev: ranks and skips. The server calls the decisions API itself over HTTPS, not through sampling.

    • sampling: skips only. The server asks your MCP client's own model to rate each headline, which is what MCP sampling means: the server borrows the client's LLM rather than having its own. It doesn't rank, so ordering is left to the agent's model.

  • Any Jev-style provider: the jev backend speaks the decisions API (POST model, state, questions → answers), and you point it at a provider the way you'd point an OpenAI-compatible client at a local model:

    • SUBSTACK_JEV_URL: the endpoint. Default: OpenRouter's https://openrouter.ai/api/alpha/decisions. TypeSafe's System One API (…/v1/systemone) and compatible or self-hosted servers work too.

    • SUBSTACK_JEV_API_KEY: the bearer token (falls back to OPENROUTER_API_KEY). It can be empty for a server without auth.

    • SUBSTACK_JEV_MODEL: the model id. Default typesafe/jev-1.13; TypeSafe's own API uses jev-1.13.

  • Tune it to you:

    • Label your own posts with one keypress each, and the tools recommend thresholds and edits to interests.md.

    • The tools can also draft an interests.md from what you pay for, save, heart, finish and dismiss.

Everything about it is in docs/classifier.md.

Logging in

  • Browser (default): login opens Chrome or Edge on Substack's sign-in page. The session is saved as soon as you're in and lasts about three months. Re-running login usually needs no typing.

  • Paste: login --paste (or --stdin) takes the substack.sid cookie in any format: the bare value, a Cookie: header, a Cookie-Editor export, or cookies.txt.

  • No restart needed: the server re-reads the session on every call.

  • Tips:

    • If Substack emails you a sign-in link, paste it into the login window.

    • Google sign-in sometimes refuses an automated window; use email or password instead.

Command

What it does

node dist/cli.js login

Log in (--paste / --stdin to paste the cookie)

node dist/cli.js status

The logged-in account, subscription count, and whether the session works

node dist/cli.js logout

Delete the session (--all also deletes the login browser profile)

node dist/cli.js install

Register with Claude Code (--scope user|project|local)

Variable

Purpose

SUBSTACK_SID, SUBSTACK_COOKIE

Session cookie, overriding the saved session (containers, CI)

SUBSTACK_READER_HOME

Config directory (default ~/.config/substack-reader)

SUBSTACK_BROWSER_PATH

A Chromium-based browser for login

SUBSTACK_USERNAME

When logged out, list_subscriptions shows this user's public subscriptions

SUBSTACK_COOKIES_PATH

A Cookie-Editor export from the original Python version, read as a fallback

SUBSTACK_DIGEST_DIR

Absolute path of the digest directory; setting it turns on the digest tools

SUBSTACK_DIGEST_TZ

Time zone for digest and chat times (default UTC)

SUBSTACK_CLASSIFIER

off (default), jev or sampling

SUBSTACK_JEV_URL

Decisions-API endpoint for jev (default OpenRouter's)

SUBSTACK_JEV_API_KEY

Bearer token for that endpoint (default OPENROUTER_API_KEY)

SUBSTACK_JEV_MODEL

Model id (default typesafe/jev-1.13)

SUBSTACK_DIGEST_SKIP_THRESHOLD

Skip probability at which a post is set aside (default 0.7)

SUBSTACK_DIGEST_RANK_FLOOR

Rank below which reading a post is optional (default off)

Privacy and security

  • Your session goes only to substack.com and *.substack.com. Custom-domain publications get their own session through Substack's sign-in handoff, as in a browser. Redirects are followed one hop at a time, so no cookie reaches another host.

  • It's stored in ~/.config/substack-reader/auth.json, mode 600. Nothing is stored in the repo.

  • No telemetry. Requests go only to Substack, to the publications you ask about, and to your Jev provider if it's on (headlines and your interests only).

  • Only subscribe and unsubscribe change anything, and only free subscriptions.

  • Reading activity is read, never changed. The endpoints that mark posts as seen or saved are never called.

  • package-lock.json pins every dependency; install with npm ci.

Troubleshooting

Symptom

Fix

"Not logged in" / "session was rejected"

node dist/cli.js login (sessions last about three months)

"Only a preview was returned"

No paid subscription to that publication, or the session expired; status checks

The server doesn't appear in /mcp

Restart Claude Code

login can't find a browser

Install Chrome, set SUBSTACK_BROWSER_PATH, or use --paste

"doesn't look like a Substack publication"

The newsletter may have left Substack

The digest says CLASSIFIER: … unavailable

For jev, check SUBSTACK_JEV_URL and the key; for sampling, the client's sampling setup

Development

npm ci && npm test     # vitest against a fake fetch; no network needed
npm run typecheck && npm run build
  • src/substack/: the HTTP client, posts, subscriptions, reader shelves and chat.

  • src/digest/: the digest tools.

  • src/classifier/: the headline classifier, shared with medium-reader-mcp.

  • tools/classifier/: label, score, analyze, propose.

  • CLAUDE.md: how Substack's endpoints actually behave, including look-alikes that write. Read it before changing src/substack/.

Disclaimer

An independent project, not affiliated with or endorsed by Substack or TypeSafe. It uses Substack's undocumented web endpoints, which can change without notice. Use it with your own account, within Substack's Terms of Use.

License

MIT © 2026 Bill Baran.

Available Tools

12 tools
auth_statusSubstack auth statusA
Read-only

Check whether a Substack session is configured and still valid, and which account it belongs to.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, but the description adds meaningful context beyond those: it specifies that the check covers both configuration and ongoing validity, and that it returns account ownership. This is useful behavioral information for an agent deciding whether to rely on the session.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single, tight sentence that front-loads the core action and outcomes. Every word earns its place, with no filler or repetition of the title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter status check with no output schema, the description covers the essential return semantics (configured, valid, account). It does not specify the exact output format, but for this simple tool the conceptual description is likely sufficient for an agent to interpret the result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters and full schema coverage, so the baseline is 4. The description correctly avoids inventing parameter semantics. It adds no param-specific meaning because none are needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('check') with a clear resource ('Substack session') and states the outcome (configured, valid, account). It unambiguously distinguishes itself from the sibling tools, which are all content- or subscription-focused actions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives no guidance on when to use this tool versus alternatives or when it is appropriate to call it. It does not mention that it is a prerequisite for other actions, nor does it route the agent to a sibling for other purposes. This is a clear gap for a status-check tool that sits alongside many action-oriented siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_chat_threadsGet chat threadsA
Read-only

Threads in a publication's chat, newest first, with reply counts. Pass before from a previous response to page back.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
beforeNoPaging cursor: the nextBefore value from a previous call.
publicationYesPublication name, subdomain, URL, or numeric publication id (from list_chats).

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint, so the description need not restate safety. It adds value by disclosing sort order ('newest first'), that reply counts are included, and that paging is done via the `before` cursor. These are behavioral details beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two concise sentences that immediately state the purpose and the key paging mechanism. No fluff, and the most useful details are front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only listing tool with three parameters and no output schema, the description covers sorting, reply counts, and paging. It is adequate for an agent to call it correctly, though it does not describe the response shape beyond reply counts, which is acceptable given the lack of an output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 67%, moderate. The description mentions paging via `before`, which duplicates the schema's parameter description, and does not clarify `limit` beyond what schema constraints already convey. It adds minimal new meaning, so it does not fully compensate for the moderate coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a clear purpose: retrieving chat threads for a publication, newest first, with reply counts. It is specific about the resource and verb, but it does not explicitly differentiate from sibling tools like list_chats or read_chat_thread, so it falls short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage context ('in a publication's chat') but does not explicitly state when to use this tool vs. alternatives or when not to use it. There is no mention of list_chats for finding publications or read_chat_thread for reading a single thread, so guidance is only implicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_feedGet subscription feedA
Read-only

Recent posts across all subscriptions, newest first.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoTotal posts to return.
sinceNoOnly posts after this point: an ISO date ("2026-09-01") or relative ("7d", "48h").
per_publicationNoPosts to look at per publication.

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint, so the safety profile is covered. The description adds the useful behavioral details that results span all subscriptions and are sorted newest-first, but it does not mention freshness, pagination behavior, or other operational nuances.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single front-loaded sentence with no filler: it communicates scope and ordering in seven words. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only feed tool with fully documented optional parameters and safety annotations, the description plus schema is largely sufficient. It loses a point only because it does not clarify how it differs from sibling get_recent_posts or describe the expected response shape, which is more important given there is no output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

All three parameters already have descriptive schema entries, giving 100% schema coverage. The description does not add parameter-level detail, so the baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the resource ('subscription feed'), the scope ('across all subscriptions'), and the ordering ('newest first'), so an agent can tell what this tool returns. It stops short of a 5 because it does not explicitly contrast itself with sibling tools like get_recent_posts or search_posts.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The wording implies this is the tool for browsing an aggregated feed of recent posts from all subscriptions. However, it gives no explicit guidance on when to prefer it over get_recent_posts or search_posts, nor any when-not-to-use conditions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_recent_postsGet recent postsC
Read-only

Recent posts from one publication.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
offsetNoSkip this many posts, for paging back through the archive.
publicationYesPublication name, subdomain, or URL (e.g. "Platformer", "platformer", "https://www.platformer.news").

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, covering the safety profile. The description adds no behavioral detail beyond the annotations, such as sorting order, pagination behavior, or whether only published posts are included. It does not contradict the annotations but also contributes no extra transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that is front-loaded and contains no fluff. It is appropriately concise for a simple read operation, though it could benefit from a few more words to add usage context.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with only 3 parameters and no output schema, the description is mostly complete for a basic call, but it lacks guidance on how this tool differs from siblings like get_feed or search_posts. This omission makes it slightly incomplete for agent decision-making in a crowded toolset.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 67% (offset and publication have descriptions, limit does not). The description adds little beyond the schema—it repeats the 'one publication' idea but does not explain limit or offset semantics. With moderate coverage, a baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description 'Recent posts from one publication' clearly indicates the tool returns a list of recent posts for a single publication. It is not a tautology and distinguishes from sibling tools like search_posts (which searches) and get_feed (potentially multiple publications), though it doesn't explicitly name them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is given on when to use this tool versus alternatives such as get_feed or search_posts. The description only implies it is for a single publication, but it does not state conditions or exclusions, leaving the agent to infer usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_chatsList chatsA
Read-only

The user's Substack chat inbox: publication chats they belong to and direct messages, most recent first, with unread state. Use the returned id with get_chat_threads (publication_chat) or read_dm (direct_message).

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true. The description adds behavioral details beyond that: 'most recent first, with unread state' and the distinction between two kinds of chats. It does not mention pagination or result limits, but for a simple list tool with open-world semantics, this is adequate and adds real context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no filler. The first sentence defines the scope and ordering, the second gives actionable routing instructions. Every word earns its place; it is front-loaded with the core purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given there is no output schema, the description carries the burden of explaining what the tool returns. It covers the two chat types, ordering, unread state, and how to consume the returned id. It doesn't mention pagination or field details, but for a list tool this is sufficient for an agent to call and use it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so the schema covers everything trivial. The description doesn't need to explain parameters and adds no parameter-specific meaning. Baseline for 0 params is 4; the description correctly focuses on output and usage rather than params.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'The user's Substack chat inbox: publication chats they belong to and direct messages' – it clearly defines what the tool lists. It also distinguishes itself from siblings like get_chat_threads and read_dm by describing the overall inbox vs. per-thread operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly names alternatives and the condition for using them: 'Use the returned id with get_chat_threads (publication_chat) or read_dm (direct_message).' This tells the agent exactly how to route based on the item type, leaving no ambiguity about when to use this tool versus its siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_subscriptionsList subscriptionsA
Read-only

List every newsletter the logged-in user subscribes to (including subscriptions hidden from their public profile), with URL and membership state.

ParametersJSON Schema
NameRequiredDescriptionDefault
refreshNoBypass the 10 minute cache.
usernameNoOnly used when not logged in: read this user's *public* subscription list instead.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With readOnlyHint/openWorldHint annotations already covering safety and dynamic data, the description adds meaningful behavior: hidden subscriptions are included for the logged-in user, results include URL and membership state, and refresh can bypass a 10-minute cache. No annotation contradiction; the annotations and prose are consistent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One tightly worded sentence states the scope, edge case (hidden subscriptions), and output shape without repeating the title or schema. It is front-loaded and every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only list with fully documented optional parameters, the description covers the key return values and the main behavioral variants. It could mention the unspecified behavior when not logged in and no username is provided, but that is a minor edge case given the schema's explicit username note.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents refresh and username thoroughly. The description indirectly clarifies the username semantics by contrasting public and logged-in lists, but it adds little beyond what the schema states, matching the baseline for full coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb ('List'), resource ('every newsletter the logged-in user subscribes to'), and includes the hidden-from-public-profile scope plus the fields returned. This clearly differentiates the private logged-in list from the public one accessed via username, and from unrelated siblings like get_feed or list_chats.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description and username parameter make the main conditional explicit: when not logged in, use username to read a public list; when logged in, get the full private list. It does not explicitly name sibling alternatives or exclusion cases, but the tool's purpose is distinct enough that no competing sibling exists for listing subscriptions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

read_chat_threadRead chat threadB
Read-only

A chat thread with its replies (and replies to replies) as a readable transcript.

ParametersJSON Schema
NameRequiredDescriptionDefault
thread_idYesThread id from get_chat_threads.
max_repliesNoMost recent replies to include.
include_sub_repliesNoAlso load replies to replies.

TDQS

B3.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds useful context about what the tool returns—thread content with replies and nested replies—but does not disclose behavior like pagination, truncation, or how max_replies affects the output.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single concise sentence that conveys the core output format without filler. It is front-loaded with the resource and immediately clarifies scope with the parenthetical about nested replies.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read operation with complete schema documentation, the description is mostly adequate. However, it lacks explicit selection guidance against sibling tools and does not describe the return structure beyond 'readable transcript,' which could leave an agent uncertain about how the three parameters shape the output.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the parameters are already fully documented. The description adds the notion of 'replies to replies,' which aligns with include_sub_replies, but it does not add meaningful detail beyond what the schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The title and description together identify a clear resource: a chat thread, including nested replies, rendered as a readable transcript. This is specific enough to distinguish from the sibling list tool get_chat_threads, but the description does not explicitly call out that contrast or name any alternative.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool instead of siblings like read_dm, read_post, or get_chat_threads. The schema hints that thread_id comes from get_chat_threads, but the description itself leaves usage entirely to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

read_dmRead direct messagesA
Read-only

Messages in one of the user's direct-message conversations. Only use when the user asks about their DMs.

ParametersJSON Schema
NameRequiredDescriptionDefault
conversation_idYesA direct_message id from list_chats.

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile with readOnlyHint and openWorldHint, so the description does not need to re-establish that this is a read operation. The description adds minimal behavioral context beyond the resource itself, with no mention of return format, ordering, pagination, or ownership requirements, but this is acceptable for a simple read tool under annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two short sentences with no filler. The resource statement is front-loaded, and the usage condition is a separate, actionable sentence that earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter, read-only tool with a fully documented schema and clear usage condition, the description is mostly complete. It could briefly state what the agent should expect back, such as a list of messages, but the existing wording strongly implies the content, and the annotations handle safety.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%: conversation_id is described as 'A direct_message id from list_chats,' which gives the agent complete parameter meaning. The description adds no parameter-level detail, so it correctly relies on the schema; the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The title and description make clear this tool reads messages from a user's direct-message conversation, which is a specific resource. It does not explicitly name or contrast a sibling such as read_chat_thread, but the 'DMs' framing distinguishes it from the other read tools in the sibling list.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives an explicit trigger: 'Only use when the user asks about their DMs.' This is clear context for when to use the tool, though it does not state when not to use it or direct the agent to an alternative such as read_chat_thread.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

read_postRead a postA
Read-only

Read the full content of a Substack post as Markdown. Paid posts are returned in full when the logged-in account has access. Long posts are paged: pass start from the previous response to continue.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesPost URL (any Substack link format, including substack.com/home/post/p-123) or numeric post id.
startNoCharacter offset into the body, for paging.
formatNomarkdown
max_charsNo

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already carry the read-only and open-world hints; the description adds meaningful context by disclosing that paid posts are fully returned only when the account has access and that long posts are paged via a `start` cursor. It does not address failure modes, but it goes beyond the minimal annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, with the core action front-loaded and each sentence earning its place by adding paywall or pagination behavior. There is no redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only, single-resource fetch with a small parameter set, the description covers purpose, paywall access, and pagination. It omits explicit treatment of `format`/`max_chars` and error handling, but the enum and integer ranges are in the schema and the open-world annotation covers result volatility.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description clarifies the `start` parameter's role in pagination, reinforcing the schema's character-offset note. However, `format` and `max_chars` have no schema descriptions and are not explained in the description, leaving a coverage gap at 50% overall.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Read'), a resource ('full content of a Substack post'), and a concrete output format (Markdown). This clearly distinguishes it from sibling list/search tools like get_feed and search_posts, and adds scope beyond the generic title.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies when to use the tool ('full content' retrieval) but does not explicitly name alternatives or state when not to use it. The paywall note provides access conditions, but there is no direct routing guidance relative to siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_postsSearch a publicationB
Read-only

Search one publication's archive by keyword.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
queryYes
publicationYesPublication name, subdomain, or URL.

TDQS

B3.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, and the description does not contradict them. The phrase 'archive' adds scoping context, but the description does not disclose result ordering, pagination, matching semantics, or whether live content is excluded. Since the annotations carry the safety profile, the description adds only modest behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single front-loaded sentence with no filler, repetition, or irrelevant detail. It states the action, scope, and input type efficiently.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a straightforward read-only search, the schema plus description may be enough to construct a basic call. However, there is no guidance on choosing this tool among many similar siblings, no mention of expected output, and no output schema to fill that gap. Completeness is adequate but not strong.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is only 33%: publication is described, while query and limit are not. The description's 'by keyword' maps to the query parameter, but it does not clarify the limit parameter or otherwise compensate for the low coverage. This is below the baseline expected when the schema leaves most parameters undocumented.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('Search') and identifies the resource ('one publication's archive') and the method ('by keyword'), so an agent can recognize it as a scoped keyword search. However, it does not explicitly name or contrast sibling tools, so differentiation is implicit rather than stated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance on when to use search_posts versus get_feed, get_recent_posts, or read_post, and no exclusions or prerequisites are mentioned. The only usage signal is the verb and scope in the description, which is implied rather than explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

subscribeSubscribe (free)A
Idempotent

Free-subscribe the logged-in account to a publication. Never starts a paid plan. Does nothing if already subscribed. Only use when the user explicitly asks to subscribe.

ParametersJSON Schema
NameRequiredDescriptionDefault
publicationYesPublication URL, domain, or subdomain (e.g. "https://www.platformer.news", "platformer").

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations, the description discloses important behavior: it never starts a paid plan and is idempotent with 'Does nothing if already subscribed.' This adds meaningful context about side effects and state changes that annotations alone do not convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is four short sentences with no filler. Each sentence contributes a distinct piece of information: what the tool does, what it avoids, its idempotent behavior, and when to invoke it.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is simple with one fully documented parameter, and the description covers the key behavioral guarantees needed for correct invocation. The absence of an output schema is acceptable because the description focuses on action semantics rather than return values.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the sole parameter 'publication' is already fully documented with examples. The description does not add parameter-level detail, but it does not need to because the schema already carries the semantic weight.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Free-subscribe the logged-in account to a publication.' It also distinguishes the tool from paid subscriptions and clearly differentiates it from the sibling unsubscribe tool by focusing on the act of subscribing.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides an explicit trigger condition: 'Only use when the user explicitly asks to subscribe.' It also implies a boundary by noting that it never starts a paid plan, which helps disambiguate from paid subscription flows, though it does not explicitly name alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unsubscribeUnsubscribe (free only)A
DestructiveIdempotent

Remove one of the logged-in account's FREE subscriptions. Refuses paid subscriptions. Only use when the user explicitly asks to unsubscribe; if the name is ambiguous, ask them which one they mean.

ParametersJSON Schema
NameRequiredDescriptionDefault
publicationYesName, subdomain, or URL of a publication the user is subscribed to.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate destructive and non-read-only behavior, but the description adds critical context beyond them: it only affects free subscriptions, refuses paid ones, and requires explicit user consent. This explains what gets destroyed and under what conditions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three short sentences, action-first and free of filler. It front-loads the core operation, then adds restrictions and usage guidance efficiently.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With one well-documented parameter, no output schema, and annotations covering safety, the description covers the essential context: scope, eligibility, consent requirement, and ambiguity handling. Nothing critical is missing for an agent to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, and the parameter description already explains that 'publication' is a name, subdomain, or URL. The tool description does not add additional parameter-level semantics, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('Remove') with a clear resource ('one of the logged-in account's FREE subscriptions') and adds an explicit restriction ('Refuses paid subscriptions'). This clearly distinguishes it from sibling tools like subscribe and list_subscriptions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly states when to use the tool ('Only use when the user explicitly asks to unsubscribe') and excludes paid subscriptions. It also provides guidance for ambiguous names, telling the agent to ask the user which subscription they mean.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 12 tool updatesv0.2.0
    • First observedauth_status
    • First observedget_chat_threads
    • First observedget_feed
    • First observedget_recent_posts
    • First observedlist_chats
    • First observedlist_subscriptions
    • First observedread_chat_thread
    • First observedread_dm
    • First observedread_post
    • First observedsearch_posts
    • First observedsubscribe
    • First observedunsubscribe

TDQS

A3.8/5.0

Scored across 12 tools

Disambiguation4/5

Most tools map to distinct resources and actions: subscription management, feed reading, post retrieval, and chat/DMs. The only real confusion risk is get_feed versus get_recent_posts, though their descriptions clearly separate 'all subscriptions' from 'one publication.'

Naming Consistency4/5

The naming convention is largely verb_noun: list_subscriptions, get_feed, search_posts, read_post, subscribe, unsubscribe. Minor deviations such as auth_status instead of get_auth_status and the abbreviated read_dm prevent a perfect score.

Tool Count5/5

Twelve tools is well within the ideal range and each tool covers a distinct reader workflow: authentication, subscription management, post discovery/reading, and chat/DMs. No tool feels redundant or unnecessary.

Completeness5/5

The toolset covers the full Substack reader lifecycle: auth status, listing/subscribing/unsubscribing, feed and publication browsing, archive search, full post reading, and chat/DM threads. There are no obvious dead ends for the stated reader-focused purpose.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Enables read-only access to Finary portfolio data, including profiles, organizations, institution connections, portfolios, and transactions, through MCP.
    5
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables MCP clients like Claude to securely read your own Cronometer nutrition and biometric data using your account credentials, with read-only access and optional human-readable output.
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables MCP clients like Claude to access a user's Medium account, including Following and For you feeds, full member-only posts, search, reading lists, and managing follows, saves, and claps after a one-time browser login.
    14
    MIT