substack-publisher-mcp
This server provides read-only access to Substack publication data via MCP tools.
List configured publications – see which Substack publications your API keys cover.
List posts – retrieve published posts with filters (dates, type, sort, pagination).
Search posts – full-text search across published posts.
Get post details – fetch a specific post's metadata and body (Markdown, ProseMirror, or none) by URL slug.
Get post stats – view engagement metrics like opens, clicks, views, and subscriptions for a post.
Get subscriber counts – daily subscriber totals broken down by free/paid/trial/etc. over a date range.
Get subscriber – look up an individual subscriber by email and see their subscription details.
Multi-publication support – when multiple publications are configured, target a specific one with the
publicationparameter.
Provides tools for managing and analyzing Substack publications via the official Publisher API, including listing posts, retrieving subscriber counts, and fetching post engagement stats.
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-publisher-mcpShow my recent posts with stats"
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-publisher-mcp
MCP server for Substack's official Publisher API
Note: This is an unofficial, community-developed tool and is not affiliated with, endorsed by, or supported by Substack, Inc.
An MCP server for Substack's official Publisher API. Search and read posts, pull post analytics and subscriber counts, and look up subscribers from Claude, Cursor, or any MCP client. All tools are read-only.

Why this server?
substack-publisher-mcp | Other Substack MCP servers | |
API | Official Publisher API | Unofficial internal API |
Auth | API key (stable) | Browser cookies (fragile) |
Stability | Official, documented API | Breaks when Substack changes internals |
Multi-publication | Built-in support | Not available |
Related MCP server: substack-mcp
Prerequisites
Node.js 22+. Check with
node --version; install from nodejs.org if missing.Substack Publisher API key. Generate one from your publication's Substack dashboard. If you don't see a Publisher API option there, it may not be enabled for your publication yet; see the Publisher API docs for availability.
Quick Start
1. Install
git clone https://github.com/dkships/substack-publisher-mcp.git
cd substack-publisher-mcp
npm install && npm run build2. Configure your MCP client
Add to your client's MCP config file (create the file if it doesn't exist):
Client | Config file |
Claude Desktop (macOS) |
|
Claude Desktop (Windows) |
|
Claude Code |
|
Cursor |
|
{
"mcpServers": {
"substack": {
"command": "node",
"args": ["/path/to/substack-publisher-mcp/dist/index.js"],
"env": {
"SUBSTACK_API_KEY": "your-api-key-here"
}
}
}
}Claude Code users: Add
"type": "stdio"to the server config.
Restart your MCP client after editing the config — servers load at startup.
3. Start using it
Ask Claude (or your MCP client):
"Which Substack publications do I have configured?"
"Show me my posts from the last month"
"Find my posts about pricing"
"Pull up my post with the slug my-latest-post"
"How many opens and clicks did my latest post get?"
"What are my subscriber counts for the last 30 days?"
"Look up subscriber jane@example.com"
Installing through an AI agent or registry? See llms-install.md for a condensed, machine-readable setup guide.
Tools
Tool | Description | Key Parameters |
| List configured publications | None |
| List published posts |
|
| Full-text search across published posts |
|
| Get a post and its body by URL slug |
|
| Get engagement stats for a post |
|
| Get daily subscriber counts by type |
|
| Look up a subscriber by email |
|
All tools except list_publications accept an optional publication parameter when multiple publications are configured.
get_post returns the post body as Markdown by default. Substack sends it as a JSON-encoded ProseMirror document, typically about twice the size. Pass bodyFormat: "prosemirror" for the raw document or "none" for metadata only.
Date filters take YYYY-MM-DD. In list_posts, endDate is exclusive; in get_subscriber_counts, it is inclusive.
Example responses
[
{
"date": "2025-01-15",
"total_email_subscribers": 25000,
"paid_subscribers": 500,
"free_trial_subscribers": 10,
"comp_subscribers": 50,
"gift_subscribers": 15,
"lifetime_subscribers": 0,
"founding_subscribers": 25
}
]{
"clicks": 320,
"opens": 5400,
"post_id": 12345678,
"recipients": 10000,
"views": 6100,
"new_free_subscriptions": 80,
"new_paid_subscriptions": 5,
"estimated_revenue_increase": 400
}{
"posts": [
{
"post_id": 12345678,
"title": "My Latest Post",
"audience": "only_paid",
"subtitle": "A deep dive into the topic",
"postDate": "2025-01-15T12:00:00.000Z",
"urlSlug": "my-latest-post",
"coverImage": "https://substackcdn.com/image/..."
}
],
"next": "abc123cursor"
}next is null on the last page.
Multiple publications
If you manage multiple Substack publications, configure a separate API key for each using the SUBSTACK_API_KEY_<NAME> pattern:
{
"mcpServers": {
"substack": {
"command": "node",
"args": ["/path/to/substack-publisher-mcp/dist/index.js"],
"env": {
"SUBSTACK_API_KEY_MAIN": "your-main-blog-key",
"SUBSTACK_API_KEY_TECH": "your-tech-newsletter-key",
"SUBSTACK_API_KEY_COMPANY": "your-company-updates-key"
}
}
}
}Then specify which publication to query:
"Show me subscriber counts for main" "List recent posts from the tech publication"
Use list_publications to see all configured publication names.
Troubleshooting
Issue | Solution |
| Verify your API key is correct. The key goes directly in the |
| Two env vars map to the same publication name (names are case-insensitive, and |
Server won't start | Make sure you ran |
| Set |
Server doesn't appear in your client | Check the config file is valid JSON (no trailing commas), then restart the client. |
| Node.js isn't installed or isn't on your PATH. Check |
Still stuck | Check your client's MCP logs. Claude Desktop on macOS: |
API Reference
This server wraps the Substack Publisher API. See Substack's documentation for details on available data and rate limits.
Contributing
See CONTRIBUTING.md for guidelines.
License
MIT License. See LICENSE for details.
Substack is a trademark of Substack, Inc. This project is not affiliated with Substack, Inc. Use of the Substack name is for descriptive purposes only.
Available Tools
7 toolsget_postGet PostARead-onlyIdempotent
Get a post by its URL slug, including its full body. Returns post_id, title, subtitle, audience, postDate, urlSlug, coverImage, authors, and body (Markdown by default).
| Name | Required | Description | Default |
|---|---|---|---|
| urlSlug | Yes | The URL slug of the post (from list_posts results or the post URL). | |
| bodyFormat | No | How to return the body: 'markdown' (default), 'prosemirror' (the raw JSON string from the API), or 'none' to omit it and return metadata only. | |
| publication | No | Publication name (e.g., 'ny', 'la'). Required if multiple publications are configured. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only, idempotent, and open-world behavior. The description adds value beyond those by specifying the exact return fields and the default body format (Markdown), which is useful given there is no output schema.
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 sentence that front-loads the action and then provides a compact, useful list of returned fields. No filler or redundant explanation.
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, spelling out the returned fields is valuable and mostly sufficient. The minor gap is the absence of any mention of the publication disambiguation scenario, though the schema does cover it.
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?
Input schema description coverage is 100%, so the schema already documents urlSlug, bodyFormat, and publication. The description only echoes the URL-slug mechanism and Markdown default, adding no new parameter meaning beyond the schema.
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?
States a specific verb and resource ('Get a post by its URL slug') and clarifies it returns the full body. The enumerated return fields make it easy to distinguish from sibling tools like list_posts, search_posts, and get_post_stats.
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 intended use case is implied by the URL-slug parameter and the nature of the tool, but there is no explicit guidance on when to use it versus alternatives like search_posts or list_posts. No exclusions or alternative routing are mentioned.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_post_statsGet Post StatsARead-onlyIdempotent
Get engagement statistics for a post by its URL slug: recipients, opens, clicks, views, new free and paid subscriptions, and estimated revenue increase, plus podcast and video metrics when applicable.
| Name | Required | Description | Default |
|---|---|---|---|
| urlSlug | Yes | The URL slug of the post (from list_posts results or the post URL). | |
| publication | No | Publication name (e.g., 'ny', 'la'). Required if multiple publications are configured. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already convey read-only, idempotent, and open-world behavior, so the description does not need to restate those. It adds modest context by noting metrics are conditional ('when applicable') and revenue is 'estimated,' but it does not describe response shape or edge cases.
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 sentence front-loads the action and resource, then compactly lists the included metrics with no filler. Every clause adds useful information.
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 stats tool with no output schema, the description adequately enumerates the return categories and conditional metrics. The main gaps are usage alternatives and output formatting, which are already addressed in other dimensions.
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 input schema already documents both parameters at 100% coverage, including the urlSlug source and publication requirement. The description adds no new parameter-level meaning beyond referring to the slug, 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 states a specific verb and resource: 'Get engagement statistics for a post by its URL slug,' and enumerates the exact metrics returned. This clearly separates it from content-focused sibling get_post and from subscriber-count tools.
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 when-to-use guidance is provided. It does not explain when to prefer this over get_post or get_subscriber_counts, nor does it mention that list_posts can supply the needed slug. Usage context is only implicit in the resource type.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_subscriberGet SubscriberARead-onlyIdempotent
Look up a subscriber by email address. Returns membershipType (paying, comp, free, gift), expiry, firstPaymentAt, nextChargeDate, subscription plan details, hasBounce, and social handles. Errors with 404 if the address is not a subscriber.
| Name | Required | Description | Default |
|---|---|---|---|
| Yes | The subscriber's email address. | ||
| publication | No | Publication name (e.g., 'ny', 'la'). Required if multiple publications are configured. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish readOnlyHint, idempotentHint, and openWorldHint, so the safety profile is covered. The description adds valuable behavioral context beyond annotations: it specifies the 404 error for unknown emails and summarizes the return payload. No contradiction with 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 sentences with no filler: the purpose is front-loaded, followed by a compact list of returned fields and the error behavior. Every sentence 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 lookup, the description covers the purpose, the key returned fields, and the error case. Parameter details are fully handled by the schema, and the return-field list compensates for the absence 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 100%, so the schema already documents both parameters, including the email format and the conditional requirement for publication. The description adds no parameter-level meaning beyond what the schema provides, so 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 uses a specific verb ('Look up') and identifies the resource ('a subscriber by email address'), and it enumerates the returned fields. It is clearly distinct from sibling tools like get_subscriber_counts, though it does not explicitly name or contrast itself with any sibling.
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 about when to choose this tool over siblings such as get_subscriber_counts, nor any exclusions or alternative routing. The description implies usage through its purpose statement but provides no decision-making context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_subscriber_countsGet Subscriber CountsARead-onlyIdempotent
Get daily subscriber counts, newest first. Each row has date, total_email_subscribers (free + paid), paid_subscribers, and free_trial, comp, gift, lifetime, and founding counts. Free subscribers = total minus paid. Without dates, returns roughly the last year.
| Name | Required | Description | Default |
|---|---|---|---|
| endDate | No | End of date range (YYYY-MM-DD, inclusive). | |
| startDate | No | Start of date range (YYYY-MM-DD, inclusive). | |
| publication | No | Publication name (e.g., 'ny', 'la'). Required if multiple publications are configured. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint/idempotentHint annotations, the description discloses sort order, the exact row fields, the derived 'Free subscribers = total minus paid' relationship, and the default date window. This material detail helps an agent predict output even though no output schema is provided.
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, information-dense sentences with the core purpose and ordering front-loaded. Every sentence adds value: row contents, the derived field relationship, and the default date behavior. There is no redundancy with the schema or annotations.
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 no output schema, the description names all returned fields, explains their relationship, and states the sort order and default range. Combined with schema-described parameters and safety annotations, the definition is complete for a low-complexity read-only tool.
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 descriptions already cover all three parameters, so the schema carries the baseline documentation burden. The description adds meaning by clarifying that date parameters are optional and that omitting them yields roughly the last year, which is not stated in the schema.
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 ('Get'), a precise resource ('daily subscriber counts'), and an ordering ('newest first'). It clearly distinguishes itself from sibling tools like get_subscriber or get_post_stats by describing time-series subscriber count rows rather than post stats or an individual subscriber.
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 makes it clear that this tool is for retrieving daily subscriber counts over time, and it adds useful default behavior: 'Without dates, returns roughly the last year.' It does not explicitly name alternatives or when-not-to-use conditions, but the context is clear enough for an agent to route correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_postsList PostsARead-onlyIdempotent
List posts published by a Substack publication. Returns { posts, next }: each post has post_id, title, subtitle, audience, postDate, urlSlug, and coverImage. next is a cursor for the following page, or null on the last page. Use urlSlug with get_post or get_post_stats.
| Name | Required | Description | Default |
|---|---|---|---|
| next | No | Pagination cursor from a previous list_posts response. Pass this to get the next page of results. | |
| type | No | Filter by post type. | |
| sortBy | No | Sort order. Defaults to newest. | |
| endDate | No | Filter posts published before this date (YYYY-MM-DD, exclusive). For a single day D, use startDate D and endDate D+1. | |
| startDate | No | Filter posts published on or after this date (YYYY-MM-DD). | |
| maxResults | No | Maximum number of posts per page. Default 100. Prefer paginating with `next` over large values. | |
| publication | No | Publication name (e.g., 'ny', 'la'). Required if multiple publications are configured. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already provide readOnlyHint, openWorldHint, and idempotentHint, so the safety profile is covered. The description adds useful behavioral details: the response shape, the pagination cursor contract, and the fact that `next` is null on the last page.
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 sentences with no filler. It front-loads the operation, then provides the return shape and pagination semantics, all of which are essential for correct use.
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?
Since there is no output schema, the description appropriately explains the return object and its fields. It leaves parameter details to the schema, which is acceptable given the high schema coverage. Minor details like sort defaults are handled by the schema descriptions.
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 all parameters are already well documented. The description adds marginal reinforcement around `next` and urlSlug but does not need to explain filters or sorting because the schema covers them.
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 ('List') and resource ('posts published by a Substack publication') and describes the return shape, making the tool's purpose immediately clear. It also mentions using urlSlug with get_post or get_post_stats, which helps distinguish it from the single-post sibling tools.
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 establishes clear context: this tool lists posts and supports pagination via the `next` cursor. It does not explicitly contrast with search_posts or state when not to use this tool, but the purpose is specific enough that an agent can infer when listing is appropriate.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_publicationsList PublicationsARead-onlyIdempotent
List all configured Substack publications and their names. Use these names as the 'publication' parameter in other tools.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and openWorldHint. The description adds no further behavioral details beyond stating it lists names, which is adequate but not extra.
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, no wasted words, front-loaded with purpose. Every sentence 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 list tool with no output schema, the description adequately explains what is returned (publication names) and how to use them, making it complete.
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?
There are no parameters, so baseline 4 applies. The description adds no param info, but none is 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 clearly states the tool lists all configured Substack publications and their names, with a specific verb 'list' and resource 'publications'. It distinguishes from sibling tools focused on posts, stats, and subscribers.
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 explicitly tells agents to use the output names as the 'publication' parameter in other tools, providing clear usage context. It doesn't mention when not to use, but the guidance is sufficient.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_postsSearch PostsARead-onlyIdempotent
Full-text search across a publication's published posts, ordered by relevance. Returns { posts } with the same fields as list_posts (no pagination).
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search terms. | |
| maxResults | No | Maximum number of posts to return (1-100). Default 20. | |
| publication | No | Publication name (e.g., 'ny', 'la'). Required if multiple publications are configured. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish that this is read-only, open-world, and idempotent, so the description doesn't need to restate safety. It adds valuable behavior beyond those hints: search is scoped to published posts, results are relevance-ordered, and the response has no pagination. This is solid context without redundancy.
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 tight sentences with zero filler. The core behavior is front-loaded, and the return-shape reference and pagination note each earn their place by clarifying expectations for the caller.
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 low-complexity search tool with full schema coverage and safety annotations, everything needed is present: scope, ordering, return shape, and pagination behavior. Pointing to list_posts for field definitions is sufficient even without 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 100%, so the parameters are already fully documented. The description adds no param-specific semantics beyond what the schema provides, but it also doesn't need to because the schema carries the burden. 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?
States a specific verb ('search'), a clear resource ('a publication's published posts'), and a distinctive behavioral detail (ordered by relevance). It also names the sibling list_posts as the source of the return shape, which helps differentiate it from the other read tools.
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 full-text search purpose implicitly tells the agent when to choose this tool over list_posts or get_post, but it never explicitly says 'use this when you need full-text search' or names an alternative to avoid. The reference to list_posts is helpful but not a direct usage-route.
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.
6 tool updates
v1.2.0- Changed
get_post2 fields changed- added
Input schema / properties / bodyFormatAdded value: +{ + "description": "How to return the body: 'markdown' (default), 'prosemirror' (the raw JSON string from the API), or 'none' to omit it and return metadata only.", + "enum": [ + "markdown", + "prosemirror", + "none" + ], + "type": "string" +} - added
Input schema / properties / urlSlug / minLengthAdded value: +1
- Changed
get_post_stats1 field changed- added
Input schema / properties / urlSlug / minLengthAdded value: +1
- Changed
get_subscriber1 field changed- changed
Input schema / properties / email / patternPrevious value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
- Changed
get_subscriber_counts6 fields changed- changed
Input schema / properties / endDate / descriptionPrevious value: -"End of date range (YYYY-MM-DD)."New value: +"End of date range (YYYY-MM-DD, inclusive)." - added
Input schema / properties / endDate / formatAdded value: +"date" - changed
Input schema / properties / endDate / patternPrevious value: -"^\\d{4}-\\d{2}-\\d{2}$"New value: +"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" - changed
Input schema / properties / startDate / descriptionPrevious value: -"Start of date range (YYYY-MM-DD)."New value: +"Start of date range (YYYY-MM-DD, inclusive)." - added
Input schema / properties / startDate / formatAdded value: +"date" - changed
Input schema / properties / startDate / patternPrevious value: -"^\\d{4}-\\d{2}-\\d{2}$"New value: +"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
- Changed
list_posts6 fields changed- changed
Input schema / properties / endDate / descriptionPrevious value: -"Filter posts published on or before this date (YYYY-MM-DD)."New value: +"Filter posts published before this date (YYYY-MM-DD, exclusive). For a single day D, use startDate D and endDate D+1." - added
Input schema / properties / endDate / formatAdded value: +"date" - changed
Input schema / properties / endDate / patternPrevious value: -"^\\d{4}-\\d{2}-\\d{2}$"New value: +"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" - changed
Input schema / properties / maxResults / descriptionPrevious value: -"Maximum number of posts to return. Default 100."New value: +"Maximum number of posts per page. Default 100. Prefer paginating with `next` over large values." - added
Input schema / properties / startDate / formatAdded value: +"date" - changed
Input schema / properties / startDate / patternPrevious value: -"^\\d{4}-\\d{2}-\\d{2}$"New value: +"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
- Added
search_posts
5 tool updates
v1.1.0- Changed
get_post1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
get_post_stats1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
get_subscriber3 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - added
Input schema / properties / email / formatAdded value: +"email" - added
Input schema / properties / email / patternAdded value: +"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
- Changed
get_subscriber_counts3 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - added
Input schema / properties / endDate / patternAdded value: +"^\\d{4}-\\d{2}-\\d{2}$" - added
Input schema / properties / startDate / patternAdded value: +"^\\d{4}-\\d{2}-\\d{2}$"
- Changed
list_posts5 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - added
Input schema / properties / endDate / patternAdded value: +"^\\d{4}-\\d{2}-\\d{2}$" - added
Input schema / properties / maxResults / maximumAdded value: +9007199254740991 - added
Input schema / properties / maxResults / minimumAdded value: +1 - added
Input schema / properties / startDate / patternAdded value: +"^\\d{4}-\\d{2}-\\d{2}$"
6 tool updates
v1.0.0- First observed
get_post - First observed
get_post_stats - First observed
get_subscriber - First observed
get_subscriber_counts - First observed
list_posts - First observed
list_publications
TDQS
Scored across 7 tools
Each tool has a clear, distinct purpose: search vs. list posts, get post vs. get stats, and subscriber counts vs. individual lookup. No tools overlap ambiguously.
All tool names follow a consistent verb_noun pattern (e.g., search_posts, list_publications, get_post) with uniform snake_case. No deviations.
Seven tools is well-scoped for a read-only Substack publisher API, covering post retrieval and subscriber metrics without unnecessary bloat.
The surface covers core read operations for posts and subscribers, but lacks bulk subscriber listing or publication-level analytics, which are minor gaps given the server's likely purpose.
Maintenance
Related MCP Connectors
- SupabaseOAuthcom.supabase
MCP server for interacting with the Supabase platform
Analytics for MCP servers. Query your tool calls, first-call success, retries and schema cost.
MCP server for structured access to Lenny Rachitsky podcast transcripts. For content creators.
MCP server for querying and analyzing data from ad platforms, analytics tools, and spreadsheets
Related MCP Servers
- AlicenseNot gradedqualityNot gradedmaintenanceAn MCP server that enables AI assistants like Claude to interact with Substack newsletters, allowing for post retrieval, content searching, and author information access through a standardized interface.MIT
- AlicenseAqualityCmaintenanceMCP server for Substack that lets Claude Code create drafts, upload images, set cover thumbnails, schedule, and publish posts on your Substack publication.1115MIT
- AlicenseAqualityDmaintenanceA tool that connects to Substack's official Publisher API to access posts, newsletters, analytics, and subscribers, compatible with MCP clients like Claude and Cursor.63MIT
- AlicenseNot gradedqualityDmaintenanceMCP server for Substack that enables reading articles, comments, feed, and subscriptions from AI clients like Cursor and Claude, with optional authentication for paid content.41 npm3MIT