Substack Reader MCP
Read your Substack account (subscriptions, feed, posts, chats, DMs) and manage free subscriptions.
Check whether a Substack session is valid and which account it belongs to.
List all subscriptions, including hidden ones, or view a user's public subscriptions when logged out.
Get a combined recent feed across all subscriptions, with time or limit filters.
Get recent posts from one publication.
Search a publication's archive by keyword.
Read full posts as Markdown, text, or HTML, with paging for long posts and paid access if available.
List chats and direct messages in the chat inbox.
Read chat threads with replies and sub-replies.
Read direct-message conversations.
Free-subscribe to a publication.
Unsubscribe from a free subscription (refuses paid ones).
Provides access to a Substack account, including subscriptions, combined feed, full posts (including paid ones the user has access to), publication search, chats, direct messages, and the ability to subscribe to or unsubscribe from free newsletters.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Substack Reader MCPWhat's new in my Substack feed this week?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Substack Reader MCP
Ask Claude what's new in your Substack subscriptions, and have it read the posts for you.
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 CodeRestart 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.jsonVS 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 |
| Recent posts across all subscriptions, newest first. |
| A full post as Markdown. Paid posts you can't access are flagged as previews |
| One publication's latest posts · keyword search within one publication |
| Every subscription, including hidden ones, with membership |
| Posts you opened on the web or in the app, with how far you read each one, and whether you hearted or saved it |
| Posts you saved · posts you hearted |
| What your activity says about your taste, for drafting an interests.md |
| 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 |
| Publication chats you're in, and direct messages |
| Chats and DMs with activity since a time, optionally with the new messages |
| 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 |
| Free-subscribes you. Never starts a paid plan |
| 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 |
| Fetches every new post and chat activity, and returns a work list with refs ( |
| Takes a verdict per post and chat, renders the message and saves state. Safe to repeat |
| Inspect the state · repair it |
| Saves a drafted |
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
Build (
npm ci && npm run build) and copydist/,package.jsonandpackage-lock.jsonto where Hermes can see them, e.g./opt/data/mcp/substack-reader-mcp. Then runnpm ci --omit=dev --omit=optionalthere.Log in on a machine with a browser (
node dist/cli.js login), then copy~/.config/substack-reader/auth.jsonto the Hermes host, e.g./opt/data/mcp/substack-reader-home/.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 endpointInstall the skill: copy
hermes/SKILL.mdtoskills/productivity/substack-digest/andhermes/substack_digest_start.shtoscripts/. Then edit the skill's Settings block. Optionally add aninterests.mdto the digest directory (fromhermes/interests.example.md, or drafted from your activity).Schedule it (run as the
hermesuser). The job needs only thedelegationtoolset 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
jevbackend speaks the decisions API (POSTmodel,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'shttps://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 toOPENROUTER_API_KEY). It can be empty for a server without auth.SUBSTACK_JEV_MODEL: the model id. Defaulttypesafe/jev-1.13; TypeSafe's own API usesjev-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.mdfrom what you pay for, save, heart, finish and dismiss.
Everything about it is in docs/classifier.md.
Logging in
Browser (default):
loginopens 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-runningloginusually needs no typing.Paste:
login --paste(or--stdin) takes thesubstack.sidcookie in any format: the bare value, aCookie:header, a Cookie-Editor export, orcookies.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
loginwindow.Google sign-in sometimes refuses an automated window; use email or password instead.
Command | What it does |
| Log in ( |
| The logged-in account, subscription count, and whether the session works |
| Delete the session ( |
| Register with Claude Code ( |
Variable | Purpose |
| Session cookie, overriding the saved session (containers, CI) |
| Config directory (default |
| A Chromium-based browser for |
| When logged out, |
| A Cookie-Editor export from the original Python version, read as a fallback |
| Absolute path of the digest directory; setting it turns on the digest tools |
| Time zone for digest and chat times (default UTC) |
|
|
| Decisions-API endpoint for |
| Bearer token for that endpoint (default |
| Model id (default |
| Skip probability at which a post is set aside (default 0.7) |
| Rank below which reading a post is optional (default off) |
Privacy and security
Your session goes only to
substack.comand*.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, mode600. 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
subscribeandunsubscribechange 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.jsonpins every dependency; install withnpm ci.
Troubleshooting
Symptom | Fix |
"Not logged in" / "session was rejected" |
|
"Only a preview was returned" | No paid subscription to that publication, or the session expired; |
The server doesn't appear in | Restart Claude Code |
| Install Chrome, set |
"doesn't look like a Substack publication" | The newsletter may have left Substack |
The digest says | For |
Development
npm ci && npm test # vitest against a fake fetch; no network needed
npm run typecheck && npm run buildsrc/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 toolsauth_statusSubstack auth statusARead-only
Check whether a Substack session is configured and still valid, and which account it belongs to.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 threadsARead-only
Threads in a publication's chat, newest first, with reply counts. Pass before from a previous response to page back.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| before | No | Paging cursor: the nextBefore value from a previous call. | |
| publication | Yes | Publication name, subdomain, URL, or numeric publication id (from list_chats). |
TDQS
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.
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.
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.
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.
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.
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 feedARead-only
Recent posts across all subscriptions, newest first.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Total posts to return. | |
| since | No | Only posts after this point: an ISO date ("2026-09-01") or relative ("7d", "48h"). | |
| per_publication | No | Posts to look at per publication. |
TDQS
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.
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.
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.
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.
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.
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 postsCRead-only
Recent posts from one publication.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| offset | No | Skip this many posts, for paging back through the archive. | |
| publication | Yes | Publication name, subdomain, or URL (e.g. "Platformer", "platformer", "https://www.platformer.news"). |
TDQS
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.
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.
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.
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.
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.
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 chatsARead-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).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 subscriptionsARead-only
List every newsletter the logged-in user subscribes to (including subscriptions hidden from their public profile), with URL and membership state.
| Name | Required | Description | Default |
|---|---|---|---|
| refresh | No | Bypass the 10 minute cache. | |
| username | No | Only used when not logged in: read this user's *public* subscription list instead. |
TDQS
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.
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.
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.
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.
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.
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 threadBRead-only
A chat thread with its replies (and replies to replies) as a readable transcript.
| Name | Required | Description | Default |
|---|---|---|---|
| thread_id | Yes | Thread id from get_chat_threads. | |
| max_replies | No | Most recent replies to include. | |
| include_sub_replies | No | Also load replies to replies. |
TDQS
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.
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.
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.
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.
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.
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 messagesARead-only
Messages in one of the user's direct-message conversations. Only use when the user asks about their DMs.
| Name | Required | Description | Default |
|---|---|---|---|
| conversation_id | Yes | A direct_message id from list_chats. |
TDQS
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.
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.
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.
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.
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.
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 postARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Post URL (any Substack link format, including substack.com/home/post/p-123) or numeric post id. | |
| start | No | Character offset into the body, for paging. | |
| format | No | markdown | |
| max_chars | No |
TDQS
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.
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.
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.
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.
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.
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 publicationBRead-only
Search one publication's archive by keyword.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | Yes | ||
| publication | Yes | Publication name, subdomain, or URL. |
TDQS
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.
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.
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.
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.
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.
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)AIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| publication | Yes | Publication URL, domain, or subdomain (e.g. "https://www.platformer.news", "platformer"). |
TDQS
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.
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.
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.
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.
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.
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)ADestructiveIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| publication | Yes | Name, subdomain, or URL of a publication the user is subscribed to. |
TDQS
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.
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.
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.
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.
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.
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.
12 tool updates
v0.2.0- First observed
auth_status - First observed
get_chat_threads - First observed
get_feed - First observed
get_recent_posts - First observed
list_chats - First observed
list_subscriptions - First observed
read_chat_thread - First observed
read_dm - First observed
read_post - First observed
search_posts - First observed
subscribe - First observed
unsubscribe
TDQS
Scored across 12 tools
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.'
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.
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.
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
Related MCP Connectors
Your Gmail, Calendar, Drive, GitHub, Oura, wallet and confirmed profile facts in any MCP client.
OAuth MCP for Google, Meta, X, LinkedIn, Reddit, TikTok, GSC, GA4, WordPress and GHL.
OAuth Gate exchange MCP for CEX trading, account, wallet, unified account, and sub-account.
Hosted MCP server with managed OAuth for 15+ toolkits: Google Workspace, Fitbit, Oura, Kalshi, etc.
Related MCP Servers
- AlicenseAqualityCmaintenanceEnables read-only access to Finary portfolio data, including profiles, organizations, institution connections, portfolios, and transactions, through MCP.5MIT

Fenn MCP serverofficial
AlicenseNot gradedqualityAmaintenanceEnables MCP clients to read your family-location, chat, and calendar data, and optionally send messages with an explicitly minted send-access token.9 npmMIT- AlicenseNot gradedqualityCmaintenanceEnables 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
- AlicenseAqualityBmaintenanceEnables 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.14MIT