多平台发布工具

一个用于将Markdown内容发布到Medium和Substack的Python FastMCP服务器和CLI工具。

  • by almoore
  • Python
  • 本地部署
  • 更新于
在 GitHub 查看 almoore/presskit-mcp

使用说明

印刷套件mcp

用于发布到Medium和Substack的Python FastMCP服务器和CLI。

稳定性警告:这两种集成都依赖于非官方/弃用的访问路径。 Medium的REST API已冻结(未颁发新令牌)。两个平台的内部 端点没有文档记录,可能会在不通知的情况下更改。


安装

pip install -e .

这将安装两个命令:

  • presskit --CLI用于从终端直接发布
  • publishing-mcp --与克劳德桌面/Claude代码一起使用的MCP服务器

CLI使用情况(presskit)

发布标记文件

# Publish to Medium as a draft
presskit publish medium --file docs/drafts/my-article.md

# Publish to Substack as a draft
presskit publish substack --file docs/drafts/my-article.md

# Publish to both platforms at once
presskit publish both --file docs/drafts/my-article.md

# Publish live (not draft)
presskit publish medium --file article.md --status public --tags "python,automation"

# Substack: publish to web only (no subscriber email)
presskit publish substack --file article.md --status public --no-email

CLI从markdown文件中读取YAML frontmatter:

---
title: "My Article Title"
subtitle: "Optional subtitle for Substack"
tags: [python, automation, infrastructure]
---

# My Article Title

Article body here...

如果没有正面 title 存在,第一个 # Heading 使用。

列出帖子

# List your Medium posts
presskit list medium --username alexander.g.moore1

# List Substack posts
presskit list substack --subdomain alexgmoore

# List Substack drafts
presskit drafts substack

CLI选项参考

presskit publish <platform> [options]
  --file, -f       Markdown file to publish (required)
  --status, -s     draft | public | unlisted (default: draft)
  --tags, -t       Comma-separated tags, e.g. "python,devops" (Medium)
  --subtitle       Post subtitle (Substack)
  --no-email       Publish to web only, skip subscriber email (Substack)

presskit list <platform> [options]
  --username, -u   Medium username without @ (Medium)
  --subdomain, -d  Substack subdomain (Substack)
  --limit, -n      Max results (default: 10)

presskit drafts substack

MCP服务器使用情况

运行服务器

# Direct
python server.py

# Or via installed entry point
publishing-mcp

添加到Claude代码(项目级)

创建 .claude/mcp.json 在您的项目中:

{
  "mcpServers": {
    "publishing": {
      "command": "python3",
      "args": ["/absolute/path/to/presskit-mcp/server.py"],
      "env": {
        "MEDIUM_SESSION_COOKIE": "your_sid_cookie",
        "MEDIUM_AUTH_STATE_FILE": "/path/to/medium-auth.json",
        "SUBSTACK_EMAIL": "you@example.com",
        "SUBSTACK_PASSWORD": "your_substack_password",
        "SUBSTACK_PUBLICATION_URL": "https://yourpub.substack.com"
      }
    }
  }
}

添加到克劳德桌面

编辑 ~/Library/Application Support/Claude/claude_desktop_config.json (Mac):

{
  "mcpServers": {
    "publishing": {
      "command": "python",
      "args": ["/absolute/path/to/presskit-mcp/server.py"],
      "env": {
        "MEDIUM_SESSION_COOKIE": "your_sid",
        "MEDIUM_AUTH_STATE_FILE": "/path/to/medium-auth.json",
        "SUBSTACK_EMAIL": "you@example.com",
        "SUBSTACK_PASSWORD": "your_password",
        "SUBSTACK_PUBLICATION_URL": "https://yourpub.substack.com"
      }
    }
  }
}

添加配置后重新启动Claude Desktop/Code。


FastMCP开发与测试

presskit mcp是基于 FastMCP。以下是在开发过程中如何使用它。

交互式检查工具

MCP检查器打开一个浏览器UI,您可以在其中调用任何工具、查看输入/输出和调试:

# Using the mcp CLI (installed with mcp[cli])
mcp dev server.py

这将启动服务器并在以下位置打开一个交互式检查器 http://localhost:5173。您可以:

  • 浏览所有15个已注册的工具
  • 填写参数并执行
  • 实时查看JSON响应
  • 测试错误处理

使用stdio传输运行服务器(默认)

# FastMCP defaults to stdio transport (what Claude Desktop/Code expects)
python server.py

使用SSE传输运行(用于远程/HTTP访问)

# Start as an HTTP server on port 8000
mcp run server.py --transport sse --port 8000

然后使用以下命令从任何MCP客户端连接 http://localhost:8000/sse.

直接通过调用工具 mcp call

# One-shot tool invocation without starting a persistent server
echo '{"username": "alexander.g.moore1", "limit": 5}' | \
  mcp call server.py medium_list_posts

列出所有已注册的工具

mcp tools server.py

测试环境变量

# Medium (session-based — no integration token needed)
export MEDIUM_SESSION_COOKIE="your_sid_value"
export MEDIUM_AUTH_STATE_FILE="/path/to/medium-auth.json"

# Medium (REST API — only if you have an existing token)
export MEDIUM_INTEGRATION_TOKEN="your_token"

# Substack
export SUBSTACK_EMAIL="you@example.com"
export SUBSTACK_PASSWORD="your_password"
export SUBSTACK_PUBLICATION_URL="https://yourpub.substack.com"

运行测试

python3 -m unittest discover -s tests -v

测试使用 sys.modules 修补存根第三方deps——不需要外部服务。


工具参考

中等(6个工具)

工具 验证 方法
medium_get_current_user 集成令牌 REST API
medium_get_publications 集成令牌 REST API
medium_create_post 集成令牌 REST API
medium_create_post_session 会话cookie GraphQL+Delta OT
medium_list_posts 会话cookie 非官方GraphQL
medium_get_post_stats 会话cookie 非官方GraphQL

子工具包(9个工具)

工具 验证 方法
substack_get_publication_info 电子邮件/密码或cookie python子堆栈
substack_get_all_publications 电子邮件/密码或cookie python子堆栈
substack_list_posts 无(公共) 原始HTTP
substack_get_post 无(公共) 原始HTTP
substack_search_publications 无(公共) 原始HTTP
substack_get_subscriber_count 电子邮件/密码或cookie python子堆栈
substack_list_drafts 电子邮件/密码或cookie python子堆栈
substack_create_draft 电子邮件/密码或cookie python子堆栈
substack_publish_post 电子邮件/密码或cookie python子堆栈

凭证

中期会话cookie

介质不再发布API令牌。基于会话的工具使用浏览器Cookie:

  1. 在浏览器中登录medium.com
  2. 提取 sid 来自DevTools的cookie→ 应用→ Cookie
  3. 要获得完整功能,请使用以下命令保存完整的浏览器状态 playwright-cli state-save medium-auth.json 并设置 MEDIUM_AUTH_STATE_FILE

子堆栈

直接使用您的Substack电子邮件和密码。如果您的帐户仅使用魔术链接:

  1. 退出Substack
  2. 点击“使用密码登录”
  3. 点击“设置新密码”

建筑

presskit-mcp/
├── server.py          # FastMCP server entry point (15 tools)
├── cli.py             # CLI entry point (presskit command)
├── medium/
│   ├── client.py      # REST API + GraphQL + Delta OT HTTP layer
│   └── tools.py       # 6 MCP tools with Pydantic input models
├── substack/
│   ├── client.py      # python-substack wrapper + raw HTTP
│   └── tools.py       # 9 MCP tools with Pydantic input models
├── tests/
│   └── test_static.py # Unit tests (no external deps needed)
├── pyproject.toml     # pip installable, entry points
└── config.yaml        # Credential reference (not loaded by code)

Medium session发布的工作原理

Medium的web编辑器通过基于增量的Operational Transform系统保存内容,而不是GraphQL或REST medium_create_post_session 工具复制了这一点:

  1. 图查询语言 createPost → 创建空草稿,返回 post_id
  2. POST /p/{post_id}/deltas → 将标题+段落作为OT增量
  3. 图查询语言 setPostTags → 最多设置5个标签
  4. 图查询语言 publishPost → 发布(可选)

这是通过反编译Medium的Android APK并从网络编辑器中捕获网络流量而发现的。


已知限制

  • 中期会话Cookie过期 --您需要刷新 medium-auth.json 定期(通过浏览器重新登录)
  • 中等REST API --没有新的集成令牌。只有现有持有人可以使用 medium_create_post
  • 中等内联格式 -API中的粗体/斜体标记位置需要精确的字符偏移;当前的markdown解析器发送纯文本段落
  • 子堆栈 -没有官方API。所有端点都经过逆向工程。将请求保持在1/秒以下
  • 图像 --这两个平台都不支持通过这些工具进行程序化内联图像上传