mcpField
S
Substack MCP Server

Substack MCP Server

本地服务
implementing.substack.com社交媒体928 周前更新

关于 Substack MCP Server

Substack MCP Server 是一个 MCP 服务,让 LLM 客户端通过已登录的网页会话管理 Substack 发布物,包括草稿、文章、订阅者、数据分析、阅读源、评论和图片。它面向需要自动化 Substack 发布流程的作者和运营者。Substack 未提供公开 API,该项目基于会话令牌工作,因此需要妥善保管凭据。

主要功能

  1. 01共 27 个工具,覆盖发布与订阅者管理
  2. 02订阅者列表支持多条件筛选与分页
  3. 03订阅者导出可读取各项互动指标
  4. 04草稿正文支持结构化文档写入
  5. 05图片可从 URL 或本地文件上传至 Substack

适用场景

  1. 01通过 LLM 客户端创建和修改草稿文章
  2. 02按条件筛选订阅者并导出互动数据
  3. 03将本地或网络图片上传后插入文章

MCP 相关

该项目是一个 MCP 服务,向 MCP 客户端暴露 Substack 的发布、订阅者、数据和图片相关工具。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"marcomoauro-substack": {
"command": "npx",
"args": [
"-y",
"substack-mcp"
],
"env": {
"SUBSTACK_PUBLICATION_URL": "<SUBSTACK_PUBLICATION_URL>",
"SUBSTACK_SESSION_TOKEN": "<SUBSTACK_SESSION_TOKEN>",
"SUBSTACK_USER_ID": "<SUBSTACK_USER_ID>"
}
}
}
}
启动命令npx -y substack-mcp
需要配置的环境变量
SUBSTACK_PUBLICATION_URLFull URL of your Substack publication, including https://
SUBSTACK_SESSION_TOKEN密钥Value of the substack.sid or connect.sid cookie from your authenticated Substack session
SUBSTACK_USER_IDNumeric user ID returned by Substack's publication_user request

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmsubstack-mcp1.3.2
ocidocker.io/marcomoauro/substack-mcp:v1.3.2

工具

27

收录的工具列表 · 更新于 2 周前

参数类型必填说明
bodystring是文章正文。纯文本会按每行转换为一个段落——不会解析 Markdown,因此 `## Heading` 会按原样显示。也可以使用 Substack 文档的 JSON 字符串,并根据 set_post_body 发布时使用的同一组架构进行验证;无法识别的节点名称会报错,而不会生成格式错乱的文章。对于结构化内容,建议使用 set_post_body;该工具会公开架构并显示节点词汇。
titlestring是要创建的文章标题。
subtitlestring是要创建的文章副标题。

详细介绍

Substack MCP 服务

一个用于 Substack 的模型上下文协议(MCP)服务,让大模型客户端能够与 Substack 的 API 交互,以自动创建文章、管理草稿等。

Docker Pulls npm downloads

创建并发布文章、管理订阅者和分析数据、浏览读者动态、管理标签和评论,以及上传图片——通过一个 MCP 服务提供 27 种工具。

重要

Substack 不为这些操作提供公开 API。此服务使用经过身份验证的网页会话。请像对待密码一样处理会话令牌:将其保留在本地,切勿提交,也不要在错误报告中包含该令牌或完整的 Cookie 标头。

快速开始

最快的安装方式是使用 Node.js 22 或更高版本和 npx。

1. 收集 Substack 凭据

在浏览器中登录 Substack 并打开你的出版物控制面板。你需要三个值:

  • 出版物 URL——出版物的完整基础 URL,例如 https://your-publication.substack.com。
  • 会话令牌——打开浏览器开发者工具,选择 Network,筛选为 Fetch/XHR,然后重新加载控制面板。打开一个对出版物进行身份验证且成功的请求。在 Request Headers 下,找到 Cookie 标头,并找到名为 substack.sid 或 connect.sid 的会话 Cookie。复制其值,不要复制 Cookie 名称或标头的其余部分。 如果这两个名称都出现且值不同,请分别在本地使用第 3 步中的只读验证进行测试;切勿在问题中粘贴任一值。
  • 用户 ID——在同一个 Network 面板中,搜索一个成功的 publication_user 请求。在 其 JSON 响应中,复制 user 对象中的数字 id。

如果浏览器界面有所不同,图文并茂的凭据指南 展示了相同的请求。如果之后身份验证失效,请重新登录并重复这些步骤,以获取当前令牌。

2. 将服务添加到 MCP 客户端

对于接受 MCP JSON 配置的客户端,请添加:

{
  "mcpServers": {
    "substack": {
      "command": "npx",
      "args": ["-y", "substack-mcp@latest"],
      "env": {
        "SUBSTACK_PUBLICATION_URL": "https://your-publication.substack.com",
        "SUBSTACK_SESSION_TOKEN": "your-session-token",
        "SUBSTACK_USER_ID": "your-user-id"
      }
    }
  }
}

替换三个示例值,保存配置,然后重启 MCP 客户端。如果客户端使用其他配置格式,请查阅其文档。

3. 验证连接

向客户端提问:

列出我最近的五篇 Substack 草稿。

客户端应调用 list_posts,并传入 status: "drafts"。如果失败,请先检查客户端的 MCP 日志和下方的日志部分,然后再提交问题。

Docker 快速开始

如果要使用已发布的 Docker 镜像,而不是 Node.js,请添加以下服务配置:

{
  "mcpServers": {
    "substack": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "SUBSTACK_PUBLICATION_URL",
        "-e", "SUBSTACK_SESSION_TOKEN",
        "-e", "SUBSTACK_USER_ID",
        "marcomoauro/substack-mcp:latest"
      ],
      "env": {
        "SUBSTACK_PUBLICATION_URL": "https://your-publication.substack.com",
        "SUBSTACK_SESSION_TOKEN": "your-session-token",
        "SUBSTACK_USER_ID": "your-user-id"
      }
    }
  }
}

🛠 可用工具

create_draft_post - 创建草稿文章

输入:

  • title(字符串):文章标题
  • subtitle(字符串):文章副标题
  • body(字符串):文章正文。纯文本会按每行转换为一个段落——不会解析 Markdown,因此 ## Heading 会按原样显示。也可以使用 Substack 文档的 JSON 字符串,并根据 set_post_body 发布时使用的同一架构进行验证,因此无法识别的节点名称会报错,而不会悄悄生成格式错误的文章。

返回值:{draft_id, is_published}。将 draft_id 传给 get_draft,即可读取草稿。

对于任何结构化内容——标题、列表、链接、代码、图片、付费墙——请在创建草稿后使用 set_post_body:该工具会公开架构,因此调用模型可以读取节点词汇表,而不必猜测。

list_subscribers - 列出并筛选你的订阅者 提供与“订阅者”仪表板相同的筛选功能:48 个列、18 种运算符、自由文本搜索、排序和分页。

输入:

  • filters(数组,可选):使用 AND 组合条件,每项为 {column, operator, value}
  • search(字符串,可选):与订阅者姓名和电子邮件匹配的自由文本
  • sort_by(字符串,可选):任意可筛选列
  • sort_direction(asc | desc,可选):默认为 desc
  • limit(数字,可选):1–100,默认为 25
  • offset(数字,可选):用于分页

列可接受的运算符取决于其类型:

类型运算符
Intis is_not gt gte lt lte
Stringis is_not is_any_of contains starts_with ends_with includes_none
DateTimeis_on is_after is_on_or_after is_before is_on_or_before
Array(tag_ids、emails_enabled)includes_any includes_all includes_none
subscription_type、group_membershipis is_not is_any_of

这些列涵盖订阅者身份信息(姓名、电子邮件、国家/地区、州/省、群组成员身份)、订阅信息(类型、开始/到期/取消日期、收入、Stripe 方案、归因)、电子邮件互动情况(过去 7 天/30 天/6 个月的打开次数和独立打开次数、点击的链接、版块)以及网站互动情况(帖子浏览量、看到的独立帖子数、评论、分享、活跃天数、活跃度评级)。完整列表及其类型会通过此工具的 JSON Schema 传给客户端,因此模型无需猜测列名。

返回:{count, returned, limit, offset, subscribers}。count 是符合筛选条件的总数,不受 limit 影响,因此调用时设置 limit: 1,即可低成本估算某个细分群体的规模。

注意:此处可以筛选互动情况相关列,但它们不会包含在此工具返回的记录中——Substack 会根据出版物已保存的“显示”设置来决定返回哪些字段,并忽略每次请求指定的列列表。使用 export_subscribers 读取这些值。

不支持 OR 和嵌套条件:需要 OR 的筛选必须分多次调用。

export_subscribers - 导出包含所有列值的订阅者

此工具用于实际读取互动指标,这些指标只能由 list_subscribers 筛选:过去 7 天/30 天/6 个月的电子邮件打开次数、看到的独立电子邮件数、帖子浏览量、看到的独立帖子数、评论、分享、点击的链接、活跃天数和活跃度评级。

输入:

  • filters(数组,可选):与 list_subscribers 相同的条件,使用 AND 组合
  • search(字符串,可选):与订阅者姓名和电子邮件匹配的自由文本
  • columns(数组,可选):要包含的列,默认为全部
  • max_wait_seconds(数字,可选):1–600,默认为 120

返回:{count, columns, missing_columns, unmapped_columns, export_id, subscribers},其中每位订阅者的数据都以列名为键。

Substack 会异步生成文件,因此此工具会创建订阅者集合、请求导出、轮询等待导出就绪,然后下载文件。少量数据的导出只需几秒。如果等待时间超出限制,工具会说明情况并提供 export_id,而不会一直阻塞。

两个注意事项,均已通过实时 API 验证:

  • 无法导出 tag_ids 和 group_membership。Substack 会静默丢弃这些列,而不是报错,因此它们会列在 missing_columns 中——请求全部 48 列时只会返回 46 列。
  • 返回的值为格式化后的显示值,而非原始值:此处的收入为 "€50.00",通过 list_subscribers 返回的则是数字 50。日期为 ISO 字符串。

不支持分页:每次导出都会覆盖所有符合条件的数据。

list_posts - 列出草稿、已发布或已安排发布的帖子 **输入**: - `status`(`drafts` | `published` | `scheduled`):要读取的列表 - `search`(字符串,可选):与标题和内容匹配的自由文本 - `limit`(数字,可选):1–100,默认为 25 - `offset`(数字,可选):用于分页 - `sort_direction`(`asc` | `desc`,可选):草稿和已发布文章按时间从新到旧排序, 已安排的文章按时间从近到远排序

返回:{status, total, returned, limit, offset, posts},每篇文章均为摘要形式——如需查看未发布文章的完整内容,请使用 get_draft。

get_draft - 完整读取一篇草稿

输入:

  • draft_id(数字):list_posts 或 create_draft_post 返回的 ID

返回:Substack 存储的草稿,包含正文和受众/电子邮件设置。

set_post_body - 用结构化文档替换草稿正文

这是编写结构化内容的唯一方式:标题、列表、链接、代码块、引用、图片、按钮和付费墙。create_draft_post 接受纯文本;此工具接受 Substack 实际存储的文档,其架构已发布在 tools/list 中,因此调用模型可以读取节点类型,而不是自行猜测。

输入:

  • draft_id(数字):list_posts 或 create_draft_post 返回的 ID
  • body(对象):Substack ProseMirror 文档——{type: 'doc', content: [...]}

接受 15 种节点类型:paragraph、heading、bullet_list、ordered_list、list_item、 blockquote、highlighted_code_block、code_block、horizontal_rule、captionedImage、button、 paywall、youtube2,以及原样传递的 digestPostEmbed、substack_mentions 和 directMessage,以便通过 get_draft 读取的文档可以写回。标记:strong、em、code、 strikethrough、link。

返回:{draft_id, nodes},其中 nodes 按类型统计已存储的节点数量——因此,调用方可以确认自己请求的付费墙是否存在。验证无法报告从未发送的节点。

有三点值得注意:

  • 图片必须已托管在 Substack 上。 image2.src 指向外部 URL 时会被存储,但不会显示。请使用 upload_image 重新托管图片,以获取可用的 src。
  • 文档最多只能包含一个 paywall。 Substack 接受两个并都会显示,但无法确定哪一个会截断文章,因此此工具会拒绝第二个。
  • ordered_list 使用 attrs.order 而非 attrs.start 作为编号起点。 只提供 start 的列表会从 1 开始显示,且不会报错。
upload_image - 从 URL 或本地文件将图片托管到 Substack

Substack 的编辑器会将图片作为 base64 数据 URI 上传到 POST /api/v1/image,并由该接口返回 Substack 托管的 URL。set_post_body 中的 image2.src 和 update_draft 中的 cover_image 只能显示此类 URL,因此此工具负责在两者之间搭建桥梁。Substack 只会重新获取已存储在自身的 URL,因此图片会在此处编码,而不是转交给 Substack。

输入——url 和 path 中恰好提供一个:

  • url(字符串):要下载并重新托管的图片 http(s) URL
  • path(字符串):运行此服务的机器上图片文件的绝对路径,直接从磁盘读取,不进行下载
  • post_id(数字,可选):图片所属文章;其作用尚未确认

返回:{id, url, content_type, bytes, width, height}——调用 set_post_body 时,将 url 填入 image2.src;调用 update_draft 时,将其填入 cover_image。

下载受安全保护:仅允许 http/https;DNS 解析后会拒绝私有和环回地址的主机(每次重定向都会重新检查);内容类型必须是图片;不接受 HEIC,并会提示进行转换;图片大小不得超过 10 MB。

update_draft - 更改草稿标题、副标题或任意文章设置

更新是部分更新:只更改传入的字段,正文保持不变。

输入:

  • draft_id(数字):由 list_posts 或 create_draft_post 返回的 ID
  • draft_title(字符串,可选)
  • draft_subtitle(字符串,可选)
  • audience(everyone | only_paid | only_free | founding,可选)
  • write_comment_permissions(everyone | subscribers | only_paid | none,可选):允许哪些人发表评论
  • default_comment_sort(best_first | most_recent_first | oldest_first,可选)
  • cover_image(字符串,可选):社交预览图片。若 URL 已位于 substack-post-media.s3.amazonaws.com 或 substackcdn.com,则直接使用;否则会先下载并重新托管到 Substack,遵循与 upload_image 相同的限制
  • social_title(字符串,可选):文章在其他地方分享时使用的标题
  • description(字符串,可选):社交预览描述——不是副标题
  • search_engine_title(字符串,可选)
  • search_engine_description(字符串,可选)
  • slug(字符串,可选):文章的 URL 别名

返回:{draft_id, updated_fields, draft_title, draft_subtitle, audience, is_published, cover_image, cover_image_rehosted_from}。 如果没有要更改的字段,调用会被拒绝,而不是作为空操作发送。cover_image 是最终实际使用的 URL;如果图片经过重新托管,它就会与传入的 URL 不同。

publish_draft - 发布草稿

输入:

  • draft_id(数字):由 list_posts 或 create_draft_post 返回的 ID
  • send(布尔值,可选):是否通过电子邮件向订阅者发送文章。默认为 false,与 Substack API 自身的默认值不同——无论如何,文章都会在网站上发布,但电子邮件无法撤回,因此必须明确请求发送。

返回:{status, draft_id, post_id, title, slug, canonical_url, emailed, email_sent_at}。 emailed 表示请求发送的意图;email_sent_at 是服务自身记录的实际发送情况。

发送邮件的意图会在发布前写入草稿的 should_send_email 字段,并同时传入发布调用。仪表板会通过该字段保存发送决定,且其默认值为 true,因此如果只设置其中一处,端点读取草稿而非请求正文时,就可能会向整个订阅者列表发送邮件。

没有取消发布工具:发布后无法通过此服务撤销。

delete_draft - 删除尚未发布的草稿

输入:

  • draft_id(数字):由 list_posts 或 create_draft_post 返回的 ID

返回:{status, draft_id, draft_title}。

Substack 使用同一个端点删除草稿和已发布的文章,因此此工具会先读取目标,若文章已发布则拒绝操作——删除已上线的文章不可撤销,需通过仪表板操作。

get_publication - 查看出版物设置

输入:

  • full(布尔值,可选):返回全部 111 个字段(约 24 KB),而非精简视图。 默认为 false。 返回:默认返回投影结果——名称、子域名、自定义域名、首页宣传文案、版权信息、发件人 名称、徽标、套餐、付款状态和社区/播客标记——以及说明已省略字段数量的 _meta。完整载荷主要 包含通知开关,以及欢迎邮件、服务条款页面和隐私页面的原始 HTML。
get_user_profile - 读取此会话对应的账户

输入:

  • full(布尔值,可选):包含完整的 subscriptions 数组。默认为 false。

返回:{id, name, handle, bio, photo_url, publications, primary_publication_id, subscription_count}。publications 列出此会话拥有某种角色的所有出版物,可据此发现 SUBSTACK_PUBLICATION_URL 并非唯一可指向的出版物。

list_publication_tags - 列出你的出版物中定义的标签

输入:

  • include_hidden(布尔值,可选):包含导航中未显示的标签。默认为 true。

返回:{total, returned, tags},每个标签为 {id, name, slug, hidden}。标签 ID 是 UUID,而非 整数——与此 API 中的其他所有 ID 不同。

get_post_tags - 列出一篇文章的标签

输入:

  • post_id(数字):来自 list_posts 的 ID。草稿也适用。

返回:{post_id, count, tags},每个标签为 {post_tag_id, name, slug, hidden, association_id}。

底层端点只返回 UUID,因此此工具会根据出版物的标签列表解析名称。get_draft 和 list_posts 都不包含标签,所以这是唯一能读取标签的方式。

add_tag_to_post - 为文章添加标签

输入:

  • post_id(数字):来自 list_posts 的 ID。草稿也适用。
  • tag_name(字符串):与现有标签进行不区分大小写的匹配
  • create_if_missing(布尔值,可选):没有匹配名称时创建标签。默认为 true; 设为 false 时,拼写错误会被报告,而不会被转成新标签。

返回:{status, post_id, tag, tag_created, association_id},其中 status 为 tagged 或 already_tagged——对文章已有的标签重复添加会使上游直接返回 400,因此此工具会先检查。

此工具接受名称而非 ID,因为 ID 是 UUID,调用方通常不可能持有这些 ID。

get_post_comments - 读取你的一篇文章下的评论

输入:

  • post_id(数字):来自 list_posts 的 ID
  • limit(数字,可选):1–100,默认为 50

返回:{post_id, returned, automod_hidden_count, comments}。每条评论包含作者、 纯文本正文、表情回应数和回复数,以及其在线程中的位置(parent_comment_id、 depth)。被 Substack 自动审核隐藏的评论会计入数量,但不会合并到评论列表中——它们会在上游的 单独数组中返回;若静默丢弃,就会把“已拦截”变成“无人评论”。

comment_on_post - 在你的一篇文章下发表评论

输入:

  • post_id(数字):来自 list_posts 的 ID
  • body(字符串):纯文本;Substack 会在服务器端转换

返回:{status, post_id, comment}。

评论会以你的名义发布。请求发出前,完整文本会以 info 级别记录,因为日志是所说内容的唯一 记录。此服务不提供删除功能,但可以在 Substack 界面中删除评论——与 restack 不同,评论有自己的 ID。

以下七个工具读取的是 substack.com,而非你的出版物。它们关注的是作为 读者的账户——订阅了什么、收件箱和动态中有什么——这与上方出版方接口使用的主机和 ID 空间 不同。

list_subscriptions - 列出此账户订阅的内容 **输入**: - `limit`(数字,可选):1–500,默认值为 100 - `active_only`(布尔值,可选):排除已暂停和已过期的订阅。默认值为 `true`。

返回值:{returned, pages_fetched, subscriptions},每个订阅都包含方案、membership_state、 is_founding、is_favorite,以及是否已关闭邮件通知。最多在内部请求 20 页;如果达到此上限而停止,则会返回 truncated: true。

不要与 list_subscribers 混淆,后者列出的是订阅了你的内容的人。

list_reader_posts - 读者收件箱

输入:

  • limit(数字,可选):1–100,默认值为 20
  • after(字符串,可选):上次响应中的 next_after。这是一个时间戳,不是不透明游标——此端点自己的 cursor 字段始终为 null。

返回值:{returned, more, next_after, posts},每篇文章都包含阅读状态摘要(is_read、read_progress、is_saved)。收件箱会发送每篇文章的完整正文;此处会将其丢弃,因此请使用 get_reader_post 阅读单篇文章。

get_reader_post - 完整阅读任意文章

输入:

  • post_id(数字):来自 list_reader_posts 或 get_reader_feed
  • include_body(布尔值,可选):默认值为 true

返回值:文章的元数据及 body_html。body_truncated: true 表示正文受付费墙限制,本次会话无法解除限制——preview_text 仍包含预览内容。

正文保持为 HTML:转换正文需要新增依赖,或对标记进行正则处理,而正则 HTML 转换器会在不报错的情况下破坏嵌套列表和嵌入内容。

get_reader_feed - Notes 信息流

输入:

  • tab(字符串,可选):标签页 id——for-you(默认值)或 subscribed。不能使用显示名称:显示名称会本地化。
  • limit(数字,可选):1–50,默认值为 20
  • cursor(字符串,可选):上次响应中的 next_cursor
  • include_tabs(布尔值,可选):同时返回可用的标签页 id

返回值:{tab, returned, next_cursor, items}。每个项目都是一条 note 或一篇 post。 non_content_items_skipped 统计 Substack 混入数组的“关注推荐”区块数量,这些区块完全不包含内容。

get_profile_feed - 某个账号发布的内容

输入:

  • user_id(数字,可选):默认为 SUBSTACK_USER_ID——你自己的账号
  • type(all | notes | posts,可选):默认值为 all
  • limit(数字,可选):1–50,默认值为 20
  • cursor(字符串,可选)

返回值:{user_id, type, returned, next_cursor, items}。启用筛选时,read_from_profile 会报告该页面实际包含的条目数——否则,“从 20 个已读取条目中读到 3 条 notes”会让人误以为“这个账号只写了 3 条 notes”。

get_comment_thread - 阅读一条 Note 及其回复

输入:

  • comment_id(数字):不带 Substack 在 URL 中使用的 c- 前缀
  • include_replies(布尔值,可选):默认值为 true

返回值:{comment, branch_count, replies_returned, more_branches, next_cursor, branches}。每个分支包含一条直接回复及其后代回复,并解析出 parent_comment_id 和 depth。

restack_item - Restack 一条 Note

输入:

  • comment_id(数字):要 Restack 的 Note,来自 get_reader_feed 或 get_profile_feed
  • tab_id(字符串,可选):默认值为 for-you

返回值:{status, comment_id, restack_id, note}。

此操作是公开的,并会显示在你的个人资料中,且无法在此处撤销:Restack 没有独立的 id——它会通过 context: comment_restack 显示原始 Note——因此此 MCP 服务无法删除任何内容。请在 Substack 界面中将其移除。

仅限备注。无法重新置顶*帖子*:即使是你自己的刊物上已发布的帖子,该调用也会返回 `404`,因此传入 `post_id` 参数只会报错称帖子已不存在,而不是说明工具有误。
get_publication_stats - 查看关键统计数据

输入:无。

返回:订阅者总数和近期订阅者数、邮件和应用订阅者数、ARR、网站浏览量及近 30 天邮件打开率;Substack 提供变化数据的项目也会一并返回。如果某个底层端点调用失败,其余数据仍会返回,失败的端点会列在 errors 下。

如需更深入的数据,请使用 get_analytics。

get_post_stats - 按 43 项指标为帖子排名

哪篇帖子真正扩大了订阅列表,哪篇收益最高,哪篇导致你流失了订阅者?查看仪表板的“帖子”选项卡,支持排序和分页。

输入:

  • order_by(字符串,可选):43 项指标中的任意一项,默认为 post_date
  • order_direction(asc | desc,可选):默认为 desc
  • limit(数字,可选):1–100,默认为 25
  • offset(数字,可选):用于翻阅存档

值得关注的指标:

组别字段
转化signups subscribes founding_subscribes annual_subscribes monthly_subscribes free_trials free_to_paid_upgrades signups_within_1_day estimated_value
流失unsubscribes
阅读opens open_rate clicks click_through_rate views subscribers_finished_post
社交likes shares restacks engagement_rate unique_engagements
投递queued sent delivered dropped
视频 / 播客video_views video_minutes_watched downloads downloads_day30 …

返回:{total, returned, limit, offset, order_by, order_direction, posts}。total 表示整个存档的总数,而非当前页的数量;结果会附带 order_by 和 order_direction,确保查看排名时始终知道排名依据。

两点注意事项,均已验证:

  • 没有日期筛选。 此端点会忽略 from_date/to_date,total 不会改变,因此架构中不提供这些参数。请改用排序和分页缩小范围。
  • 按比率(open_rate、engagement_rate、click_through_rate)降序排名时,没有数据的帖子会排在最前面,因为 null 的排序位置在数字之前。工具不会将其过滤掉,以免悄然回答了另一个问题。

order_by 被有意定义为枚举:对于无法识别的字段,API 仍会返回 200 和任意顺序,因此拼写错误可能产生看似权威的排名。

get_analytics - 查看 16 种刊物级报告之一

仪表板“统计数据”选项卡背后的全部数据,都由这一个工具提供;它使用 report 枚举,而不是拆成十七个几乎相同的工具。

输入:

  • report(字符串):要查看的报告——见下表
  • from_date、to_date(字符串,可选):YYYY-MM-DD。仅供涵盖一段时间的报告使用;此类报告默认查看最近 30 天
  • limit(数字,可选):1–100,仅供 audience_overlap 和 subscriber_notes 使用
| 报告 | 说明 | |---|---| | `retention` | 群组留存率——每个注册群组在数月后仍处于订阅状态的比例 | | `retention_summary` | 第 1、6 和 12 个月的留存率概览 | | `unsubscribes` / `unsubscribes_timeseries` | 流失情况及用户给出的原因 | | `growth_sources` | 新订阅者的来源排名 | | `growth_events` | 指定时间段内的单个增长事件 | | `referrals_leaderboard` / `referrals_summary` | 推荐人数最多的用户;已发送、已接受和已转化的赠礼 | | `audience_overlap` | 与你的受众重叠的其他 Substacks——潜在合作对象名单 | | `audience_locations` | 订阅者遍布的国家和美国州的数量 | | `subscriber_notes` | 订阅者近期撰写的 Notes | | `paid_subscriber_growth` | 付费订阅增长率、新订阅和到期情况 | | `subscribers_timeseries`、`followers_timeseries`、`arr_timeseries` | 随时间变化的数量和收入 | | `network_attribution` | 通过 Substack 网络加入的订阅者占比 |

返回:{report, params, ignored_params, data}。params 是实际发送的参数,包含默认值——同一份报告在不同时间范围下的结果可能大不相同,因此不结合时间范围,数字就意义不大。ignored_params 会列出你传入但所选报告不接受的参数,而不是悄悄丢弃它们。

有两个相邻端点是刻意未公开的:audience_insights/location(订阅者地图)和 visitor_sources,即使是 Substack 自己的仪表板调用它们也会返回 400,因此问题出在上游,而不是调用方式有误。

🏗 从源代码运行

如果你想修改服务本身,请使用此方式。无需构建步骤——源代码是纯 ESM,可直接运行。

Node.js
git clone https://github.com/marcomoauro/substack-mcp.git
cd substack-mcp
npm ci

然后添加到你的 MCP 配置中:

{
  "mcpServers": {
    "substack-api": {
      "command": "node",
      "args": ["<FULL_PATH_TO_PROJECT>/src/index.js"],
      "env": {
        "SUBSTACK_PUBLICATION_URL": "<YOUR_PUBLICATION_URL>",
        "SUBSTACK_SESSION_TOKEN": "<YOUR_SESSION_TOKEN>",
        "SUBSTACK_USER_ID": "<YOUR_USER_ID>"
      }
    }
  }
}
Docker(从源代码构建)
git clone https://github.com/marcomoauro/substack-mcp.git
cd substack-mcp
docker build -t substack-mcp .

然后添加到你的 MCP 配置中:

{
  "mcpServers": {
    "substack-api": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "SUBSTACK_PUBLICATION_URL",
        "-e", "SUBSTACK_SESSION_TOKEN",
        "-e", "SUBSTACK_USER_ID",
        "substack-mcp"
      ],
      "env": {
        "SUBSTACK_PUBLICATION_URL": "<YOUR_PUBLICATION_URL>",
        "SUBSTACK_SESSION_TOKEN": "<YOUR_SESSION_TOKEN>",
        "SUBSTACK_USER_ID": "<YOUR_USER_ID>"
      }
    }
  }
}

🪵 日志

服务会将执行情况按每行一个 JSON 对象的形式记录到 stderr——MCP 客户端会将其收集到自己的日志文件中(在 macOS 上,Claude Desktop 会将日志写入 ~/Library/Logs/Claude/mcp-server-substack-api.log)。当调用结果不符合预期时,这是查看 LLM 实际发送内容最快捷的方式:

{"ts":"2026-08-07T10:12:03.114Z","level":"info","msg":"tool.call.start","tool":"create_draft_post","args":{"title":"My title","subtitle":"My subtitle","body":"…"}}
{"ts":"2026-08-07T10:12:03.402Z","level":"info","msg":"substack.response","status":200,"duration_ms":287}
{"ts":"2026-08-07T10:12:03.403Z","level":"info","msg":"create_draft_post.created","draft_id":167712345}

在凭据旁设置可选的 SUBSTACK_MCP_LOG_LEVEL 环境变量,即可更改日志记录的详细程度:

值记录内容
silent不记录任何内容
error仅记录失败的调用
warn除上述内容外,还记录客户端收到的每个错误响应——包括运行前因参数错误而被拒绝的调用
info (默认)除上述内容外,还记录每次工具调用、请求和响应
debug除上述内容外,还记录完整负载和每条 JSON-RPC 消息

任何日志级别都不会记录你的会话令牌。

💻 热门 MCP 客户端

如需查看 MCP 客户端及其功能支持的完整列表,请访问 MCP 官方客户端页面。

客户端描述
Claude DesktopClaude AI 的桌面应用
CursorAI 优先的代码编辑器
Cline for VS Code提供 AI 辅助功能的 VS Code 扩展
GitHub Copilot MCP用于集成 GitHub Copilot MCP 的 VS Code 扩展
WindsurfAI 驱动的代码编辑器和开发环境

🆘 支持

  • 如果 MCP 服务遇到问题:在 GitHub 上提交问题

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。