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.

An MCP server that gives Claude (and any other MCP client) access to your Substack account: your subscriptions, a combined feed, full posts including paid ones you have access to, search, publication chats, and direct messages. It can also subscribe you to free newsletters and unsubscribe you from them. You log in once in a browser window; no API keys or cookie exporting.

Node 20+ TypeScript MCP Tools License: MIT

Quick start · Other clients · Hermes digest · Tools · Logging in · Privacy and security · Troubleshooting

What it uses

What

Where

Needed?

Your Substack account

substack.com

Yes. Free and paid subscriptions both work

Node.js 20+

nodejs.org

Yes

MCP SDK, Zod, Turndown

npm (@modelcontextprotocol/sdk, zod, turndown)

Yes, installed by npm install

Chrome or Edge

Your existing install, driven by playwright-core

Optional. Only for the browser login; you can paste a cookie instead

Substack has no public API. This server calls the same endpoints Substack's website uses, with your own session, so it can only see what you can see when logged in.

Related MCP server: finary-mcp

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 (a session that's already running won't pick up new servers), then try:

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

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

  • "Search One Useful Thing for posts about agents."

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

  • "Unsubscribe me from newsletters I haven't opened in a while." (Claude asks before each change)

Example

> Search One Useful Thing for posts about agents

● substack-reader - search_posts (publication: "One Useful Thing", query: "agents", limit: 3)

  Three posts match:
  1. Agency and Agents (Aug 31, 2026): From the Hugging Face Incident to Twilight Factories
  2. Three Years from GPT-3 to Gemini 3 (Nov 18, 2025): From chatbots to agents
  3. The End of Search, The Beginning of Research (Feb 3, 2025): The first narrow agents are here

Other MCP clients

install covers Claude Code. For other clients, point them at dist/cli.js with an absolute path. Log in with node dist/cli.js login first either way.

Add to claude_desktop_config.json (Settings → Developer → Edit Config):

{
  "mcpServers": {
    "substack-reader": {
      "command": "node",
      "args": ["/absolute/path/to/substack-reader-mcp/dist/cli.js"]
    }
  }
}

Add to ~/.cursor/mcp.json:

{
  "mcpServers": {
    "substack-reader": {
      "command": "node",
      "args": ["/absolute/path/to/substack-reader-mcp/dist/cli.js"]
    }
  }
}

Add to .vscode/mcp.json in a workspace, or to your user MCP configuration:

{
  "servers": {
    "substack-reader": {
      "type": "stdio",
      "command": "node",
      "args": ["/absolute/path/to/substack-reader-mcp/dist/cli.js"]
    }
  }
}
claude mcp add --scope user substack-reader -- node /absolute/path/to/substack-reader-mcp/dist/cli.js

Daily digest with Hermes Agent

hermes/ contains a skill for Hermes Agent that turns this server into a scheduled Substack digest. Each morning it collects every post published since the last run in your subscriptions, plus unread chats and DMs. Subagents read every post in full, and it sends one message: the posts worth reading in full (with a two-line summary and why), everything else grouped by publication, and a summary of your chats that puts anything addressed to you first.

Setup

  1. Build the server on your machine (npm ci && npm run build). Copy dist/, package.json and package-lock.json to a directory the Hermes container can see (for example $HERMES_HOME/mcp/substack-reader-mcp, which is /opt/data/mcp/substack-reader-mcp inside the official image), and install the runtime dependencies there:

    npm ci --omit=dev --omit=optional

    Node 20 or later works, including the Node 26 in the Hermes image.

  2. Log in on a machine with a browser (node dist/cli.js login), then copy ~/.config/substack-reader/auth.json into a directory on the Hermes host, for example $HERMES_HOME/mcp/substack-reader-home/. Keep it at owner-only permissions.

  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
  4. Install the skill: copy hermes/SKILL.md to $HERMES_HOME/skills/productivity/substack-digest/SKILL.md, and hermes/substack_digest_start.sh to $HERMES_HOME/scripts/. Optionally, copy hermes/interests.example.md to STATE_DIR/interests.md and edit it (see below).

  5. Edit the Settings block at the top of SKILL.md: STATE_DIR, TIMEZONE, MAX_PARALLEL and the REAUTH message.

  6. Restart Hermes and schedule it. Cron times are in the Hermes host's local time:

    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>
    hermes cron run <job-id>   # try it once now

    Run hermes cron commands as the user the gateway runs as (docker exec -u hermes … in the official image), so the files it writes keep the right owner.

Customizing

  • What gets picked: STATE_DIR/interests.md is free text the skill reads on every run. Describe what you want more of. Name topics and writers to rank up, and kinds of posts (link roundups, podcast notes) to rank down.

  • Sizes: the numbers in the Procedure section (chunks of 5 posts per subagent, per_publication: 20 when collecting) are plain instructions, so edit them directly.

  • Output format: the message template targets Discord Markdown. For Telegram, Slack or email, edit the template in the "Send the digest" step and the formatting rules under it.

  • Schedule and delivery: use hermes cron edit <job-id> --schedule "…" or --deliver ….

Things to know

  • Tool results over about 50,000 characters don't reach the model. Hermes saves them to a file the model can't parse, and cron runs can't run scripts to help. That's why the skill asks for results in smaller pages. Keep that in mind if you raise the limits.

  • Reads everything: the skill reads every new post, which suits a few dozen posts a day. With many more subscriptions, have it shortlist first, the way the Medium digest does.

  • State: each run records what it reported in STATE_DIR/state.json, so posts never repeat. A run that fails doesn't save state, so the next run covers the same period.

  • Read-only: the skill never uses the tools that change your account.

  • Cost: a test run made 12 model calls and took about 5 minutes on Claude Sonnet. Cost grows with the number of new posts, because every post is read in full.

  • Your own account: this server uses Substack's undocumented web API with your session cookies. A daily digest is light, read-only use, but if Substack objects to automated access, it's your account at risk.

Tools

Publications can be named loosely: by name ("The Pragmatic Engineer"), part of a name ("pragmatic"), subdomain, or URL. Posts can be given as any Substack link (/p/slug, substack.com/home/post/p-123, open.substack.com/pub/…) or a post ID.

Reading

Tool

What it returns

auth_status

Whether you're logged in, as whom, and whether Substack still accepts the session

list_subscriptions

Every subscription, including ones hidden from your public profile, with URL and membership

get_feed

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

get_recent_posts

Recent posts from one publication, with offset for paging back

search_posts

Keyword search within one publication's archive

read_post

A full post as Markdown (or text / html). Long posts are paged with start. A paid post you can't access is flagged as a preview

Chat

Tool

What it returns

list_chats

Your chat inbox: publication chats you're in and direct messages, with unread state

get_chat_threads

Threads in a publication's chat, newest first, with reply counts. before pages back

read_chat_thread

A thread with its replies and replies to replies, as a readable transcript

read_dm

One direct-message conversation

Account changes

Tool

What it does

subscribe

Free-subscribes you to a publication. Never starts a paid plan; does nothing if you're already subscribed

unsubscribe

Removes a free subscription. Paid subscriptions are refused, and an ambiguous name lists the matches instead of guessing

Every tool except these two is marked read-only. These two are marked as changing your account, so MCP clients ask before running them, and unsubscribe is also marked destructive. After each change the server checks with Substack and reports what actually happened.

Logging in

Substack has no API keys or OAuth for readers, so the server uses your normal web session.

  • Browser login (default). login opens Chrome or Edge on Substack's sign-in page. Sign in however you normally do: email link, password, or Google. The session is captured, checked with Substack, and saved as soon as you're in. The login window keeps its own browser profile, so when the session expires months later, running login again usually finishes without typing anything.

  • Paste. login --paste is for machines without a display. Copy the substack.sid cookie from your browser's DevTools (Application → Cookies → substack.com). The bare value, a Cookie: header, a Cookie-Editor JSON export, and cookies.txt all work. login --stdin reads the same formats from a pipe.

  • No restart needed. The server re-reads the session on every call, so after login the next request just works.

TIP

If Substack emails you a sign-in link, paste it into the address bar of the windowlogin opened. Clicking it opens your normal browser instead.

Google sign-in sometimes refuses to run in an automated window. Use Substack's email link or password option instead, or --paste.

Command

What it does

node dist/cli.js login

Browser login. Add --paste or --stdin to paste the cookie instead

node dist/cli.js status

Shows the logged-in account, how many subscriptions it has, and whether the session still works

node dist/cli.js logout

Deletes the saved session. --all also deletes the login browser profile

node dist/cli.js install

Registers the server with Claude Code. --scope user|project|local (default user)

Variable

Purpose

SUBSTACK_SID

Session cookie value. Overrides the saved session, for containers or CI

SUBSTACK_COOKIE

A full Cookie: header to take substack.sid from

SUBSTACK_READER_HOME

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

SUBSTACK_BROWSER_PATH

A Chromium-based browser for login, if Chrome and Edge aren't installed

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

Privacy and security

  • Where the session goes. The substack.sid cookie is only ever sent to substack.com and *.substack.com. Publications on their own domain (such as www.lennysnewsletter.com) get a separate session for that domain through Substack's own sign-in handoff, the way your browser does it. Redirects are followed one hop at a time, so no cookie is carried to a different host.

  • What's stored. The session is saved to ~/.config/substack-reader/auth.json with owner-only permissions (600), next to the login browser profile. Nothing is stored in this repository, and .gitignore excludes session files in case you copy them in.

  • What leaves your machine. Requests go only to Substack and to the publications you ask about. There's no analytics or telemetry. What Claude does with the content it reads is governed by your MCP client.

  • Account changes. Only subscribe and unsubscribe change anything, and only free subscriptions. unsubscribe checks with the publication first and refuses anything with a payment attached (a paid plan, founding membership, gift, or bundle).

  • Dependencies. package-lock.json pins every dependency to an exact version and integrity hash. Install with npm ci for a reproducible install.

Troubleshooting

Symptom

Fix

"Not logged in" or "session was rejected"

Run node dist/cli.js login. Sessions last about three months

The server doesn't appear in /mcp

Restart Claude Code. New servers are only loaded when a session starts

A paid post shows "Only a preview was returned"

You don't have a paid subscription to that publication, or run status to check the session

login can't find a browser

Install Chrome, set SUBSTACK_BROWSER_PATH, run npx playwright install chromium, or use --paste

"doesn't look like a Substack publication"

The newsletter may have left Substack (Platformer, for example, moved to Ghost)

The server stopped starting after a Node upgrade

install records the Node binary it ran with. Run it again with your current Node

Development

npm ci
npm test            # vitest against a fake fetch; no network, no account needed
npm run typecheck
npm run build       # compiles src/ to dist/
src/
  cli.ts                 entry point: serve, login, status, logout, install
  server.ts              MCP tool definitions
  format.ts              HTML → Markdown, paywall-preview detection
  auth/credentials.ts    session storage; parses pasted cookies in any format
  auth/login.ts          browser and paste login
  substack/http.ts       fetch wrapper: cookie scoping, custom-domain sessions, redirects, retries
  substack/api.ts        subscriptions, posts, feed, subscribe/unsubscribe
  substack/chat.ts       chat inbox, threads, replies, direct messages
test/                    one file per module, plus an in-memory MCP client test

CLAUDE.md records how Substack's endpoints actually behave, including the ones that look right but aren't. Read it before changing anything under src/substack/.

Disclaimer

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

License

MIT © 2026 Bill Baran. Use, modify, and share it freely; keep the copyright notice.

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
    Not graded
    quality
    B
    maintenance
    Enables MCP clients like Codex and Claude Code to securely read and manage a personal Cronometer account, including food logs, nutrient summaries with missing-data awareness, food search, biometrics, and export analysis.
    MIT
  • 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