Doramagic 项目包 · 项目说明书

content-distribution-mcp 项目

生成时间:2026-05-27 16:37:37 UTC

首页

多渠道内容分发 MCP 服务器 | GitHub 仓库

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 它解决的问题

继续阅读本节完整说明和来源证据。

章节 工作原理

继续阅读本节完整说明和来源证据。

content-distribution-mcp

多渠道内容分发 MCP 服务器 | GitHub 仓库

项目概述

content-distribution-mcp 是一个基于 Model Context Protocol (MCP) 的服务器,用于将单一内容自动分发到 8+ 个平台(DEV.to、Hashnode、GitHub Discussions、Reddit、Bluesky、LinkedIn、Medium、Twitter/X),支持平台自适应幂等发布社区反垃圾规则集中式状态管理

资料来源:README.md

它解决的问题

内容规模化发布存在以下痛点:

痛点描述
格式差异Reddit 清除格式、Twitter 有字符限制、DEV.to 支持嵌入和富媒体
平台规则子版块强制冷却期和标签要求、社区有发帖模式和自动审核
状态混乱哪些帖子发布成功?失败后重试是否会重复发布?

资料来源:README.md

工作原理

graph LR
    A[AI Agent 生成<br/>平台适配内容] --> B[调用 MCP 工具<br/>post.publish]
    B --> C{平台适配器}
    C -->|DEV.to| D[API 直接发布]
    C -->|Hashnode| E[GraphQL 发布]
    C -->|Reddit| F[检查冷却期<br/>API 发布]
    C -->|Bluesky| G[AT Protocol 发布]
    C -->|LinkedIn| H[浏览器回退<br/>compose_url]
    C -->|Twitter| H
    C -->|Medium| H
    D --> I[返回 live_url]
    E --> I
    F --> I
    G --> I
    H --> J[返回 compose_url<br/>需手动完成]
    I --> K[存储发布状态<br/>YAML backend]
    J --> K

资料来源:src/adapters/index.ts

资料来源:README.md

核心功能

content-distribution-mcp 是一个基于 Model Context Protocol (MCP) 的多平台内容分发服务器。它通过单一的内容输入,自动将内容适配并发布到 8+ 个平台,同时保证幂等性、状态追踪和平台规则合规。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 整体架构图

继续阅读本节完整说明和来源证据。

章节 适配器架构

继续阅读本节完整说明和来源证据。

章节 post.publish 工具

继续阅读本节完整说明和来源证据。

功能概述

本项目的核心功能包括:

功能分类具体内容
多平台发布DEV.to、Hashnode、GitHub Discussions、Reddit、Bluesky、LinkedIn、Medium、Twitter/X
幂等性保证基于 (content.id, channel) 的唯一键防止重复发布
智能调度支持 ISO-8601 定时发布,通过 cron drain 触发
平台适配每个平台独立的字符限制、Markdown 支持、标签词汇表
状态追踪统一的发布状态管理(live/queued/needs_browser/failed)
凭证管理基于 YAML 配置文件的分布配置文件(Profile)系统

系统架构

整体架构图

graph TD
    A[AI Agent / 调用方] -->|MCP 工具调用| B[content-distribution-mcp Server]
    B --> C[State Backend<br/>~/.distribution-mcp/]
    B --> D[Channel Adapters]
    
    D --> E1[DEV.to Adapter<br/>API 集成]
    D --> E2[Hashnode Adapter<br/>GraphQL]
    D --> E3[GitHub Discussions<br/>GraphQL]
    D --> E4[Reddit Adapter<br/>OAuth + API]
    D --> E5[Bluesky Adapter<br/>AT Protocol]
    D --> E6[Browser Fallback<br/>LinkedIn/Medium/Twitter]
    
    C --> F1[profiles.yaml<br/>凭证配置]
    C --> F2[state.yaml<br/>发布状态]
    C --> F3[scheduled.yaml<br/>定时任务]
    C --> F4[subreddits.yaml<br/>子版块目录]

适配器架构

每个平台对应一个独立的适配器,实现统一的 ChannelAdapter 接口:

classDiagram
    class ChannelAdapter {
        <<interface>>
        +hints() ChannelHints
        +publish(variant, profile) PublishResult
        +unpublish(liveUrl, profile) [boolean, string]
    }
    
    class DevToAdapter {
        +hints() ChannelHints
        +publish(variant, profile) PublishResult
        +unpublish(liveUrl, profile) [boolean, string]
    }
    
    class HashnodeAdapter {
        +hints() ChannelHints
        +publish(variant, profile) PublishResult
        +unpublish(liveUrl, profile) [boolean, string]
    }
    
    class BlueskyAdapter {
        +hints() ChannelHints
        +publish(variant, profile) PublishResult
        +unpublish(liveUrl, profile) [boolean, string]
    }
    
    class BrowserAdapter {
        +hints() ChannelHints
        +publish(variant, profile) PublishResult
        +unpublish(liveUrl, profile) [boolean, string]
    }
    
    ChannelAdapter <|.. DevToAdapter
    ChannelAdapter <|.. HashnodeAdapter
    ChannelAdapter <|.. BlueskyAdapter
    ChannelAdapter <|.. BrowserAdapter

适配器注册表通过 buildAdapterMap() 函数构建,支持多个别名映射:

平台标识实际适配器
devtoDevToAdapter
hashnodeHashnodeAdapter
github_discussions / github-discussionsGitHubDiscussionsAdapter
redditRedditAdapter
blueskyBlueskyAdapter
medium / medium_browser / medium-browserBrowserAdapter (Medium)
linkedin / linkedin_browser / linkedin-browserBrowserAdapter (LinkedIn)
twitter / x / twitter_browser / twitter-browserBrowserAdapter (Twitter)

*资料来源:src/adapters/index.ts:17-40*

MCP 工具接口

本服务器暴露 8 个 MCP 工具,采用点号命名空间组织:

工具名称功能描述只读幂等
post.publish立即发布内容到指定渠道
post.schedule调度内容发布(定时立即执行)
post.drain触发所有到期定时任务
post.status查询内容发布状态
post.unpublish取消发布(最佳努力)
channel.hints获取平台元数据(字符限制、Markdown 支持等)
profile.list列出配置的分布配置文件
subreddit.list子版块目录查询(冷却期、标签词汇)

*资料来源:src/server.ts - 工具注册部分*

post.publish 工具

立即发布内容到指定渠道,同一 content.id + channel 组合不会重复发布

输入参数:

参数类型必填说明
contentContentInput内容对象
variantsVariantInput[]渠道适配变体数组
profile_namestring分布配置文件名

ContentInput 结构:

字段类型说明
idstring内容唯一标识符
titlestring内容标题
subtitlestring副标题(用于 Hashnode、DEV.to)
body_mdstringMarkdown 格式正文
cover_imagestring (URL)封面图片 URL
tagsstring[]标签列表
canonical_urlstring (URL)原始出处 URL
cta_blockstring行动号召块(Markdown)
authorstring作者显示名
source_task_idstring溯源任务 ID(不发送至平台)

VariantInput 结构:

字段类型说明
channelstring渠道标识,如 devto:mainreddit:ClaudeAI
titlestring渠道特定标题
bodystring渠道特定正文
tagsstring[]渠道特定标签(覆盖 content.tags)
canonical_urlstring (URL)该渠道的原始 URL(可选覆盖)
cta_blockstring该渠道的 CTA 块(可选覆盖)
schedule_atstringISO-8601 调度时间
extrasobject渠道特定参数(flair、category 等)

输出结构:

{
  results: PublishResult[]  // 每条变体的发布结果
}

PublishResult = {
  channel: string,           // 渠道标识
  state: "live" | "queued" | "needs_browser" | "failed",
  live_url: string | null,    // 已发布 URL
  draft_path: string | null,  // 本地草稿路径(浏览器回退)
  compose_url: string | null, // 平台编辑 URL(浏览器回退)
  error: string | null,       // 错误信息
  published_at: string | null // UTC ISO-8601 时间戳
}

channel.hints 工具

返回静态的平台元数据,帮助 AI Agent 在生成变体时遵守平台约束:

graph LR
    A[Agent 准备发布] --> B[调用 channel.hints<br/>获取平台限制]
    B --> C[了解字符限制]
    B --> D[了解 Markdown 支持]
    B --> E[了解标签词汇表]
    C --> F[生成合规变体]
    D --> F
    E --> F

返回字段:

字段说明示例
max_length最大字符数Twitter: 280, DEV.to: 100000
supported_md_features支持的 Markdown 特性["bold", "italic", "links"]
tag_vocab推荐标签词汇["ai", "mcp", "tutorial"]
cta_placementCTA 放置位置top / bottom / footer / none
canonical_url_supported是否支持原始 URLDEV.to: true, Twitter: false
browser_only是否需要浏览器回退LinkedIn/Medium/Twitter: true

平台适配器详解

自动 API 集成(支持 API)

#### DEV.to 适配器

通过 REST API 发布文章,支持完整的 Markdown:

  • 认证: DEV_TO_API_KEY
  • API 端点: https://dev.to/api/articles
  • 特点: 支持 canonical_url 设置系列(series)
// 资料来源:src/adapters/devto.ts
const res = await fetch(`${API}/articles`, {
  method: "POST",
  headers: { "Content-Type": "application/json", "api-key": apiKey },
  body: JSON.stringify({
    article: {
      title: variant.title,
      body_markdown: variant.body,
      tags: variant.tags.slice(0, 4),  // 最多 4 个标签
      canonical_url: variant.canonical_url,
      published: true,
      series: variant.extras?.series,
    },
  }),
});

#### Hashnode 适配器

通过 GraphQL API 发布:

  • 认证: HASHNODE_TOKEN + HASHNODE_PUBLICATION_ID
  • 端点: https://gql.hashnode.com
  • 特点: 最多 5 个标签,标签自动转换为 slug 格式

#### Bluesky 适配器

基于 AT Protocol 的去中心化社交网络:

  • 认证: BLUESKY_IDENTIFIER + BLUESKY_PASSWORD
  • 流程: 创建会话 → 获取 JWT → 创建记录
  • 限制: 正文 300 字符,仅支持链接

#### GitHub Discussions 适配器

通过 GraphQL API 在指定仓库创建 Discussion:

  • 认证: GITHUB_TOKEN
  • 参数: GITHUB_DISCUSSION_REPO

#### Reddit 适配器

支持 OAuth 认证,自动处理子版块冷却期和标签限制:

  • 认证: REDDIT_CLIENT_ID / SECRET / USERNAME / PASSWORD
  • 特点: 支持子版块级别冷却期追踪(防止被识别为垃圾信息)

浏览器回退适配器

以下平台当前没有公共 API,使用浏览器回退模式:

平台状态说明
LinkedInneeds_browser返回 compose_url 指向 linkedin.com/post/new
Mediumneeds_browser返回 compose_url 指向 medium.com/new-story
Twitter/Xneeds_browser返回预填充文本的推文编辑 URL

发布结果示例:

{
  "channel": "twitter",
  "state": "needs_browser",
  "compose_url": "https://twitter.com/compose/tweet?text=..."
}
社区规划:社区正在追踪 Twitter/X、LinkedIn 和 Medium 的原生 API 支持(当前为浏览器回退模式)。

幂等性保证

核心设计原则:同一 (content.id, channel) 组合不会产生重复发布

幂等性流程

graph TD
    A[post.publish 调用] --> B{检查 state.yaml<br/>content.id + channel}
    B -->|已存在且 live| C[返回现有 live_url]
    B -->|已存在且 queued| D[返回队列状态]
    B -->|已存在且 failed| E[尝试重新发布]
    B -->|不存在| F[调用平台 API]
    F -->|成功| G[写入 state.yaml]
    F -->|失败| H[记录错误状态]

状态持久化

发布状态存储在 ~/.distribution-mcp/state.yaml

# 示例结构
content-id-here:
  devto:main:
    state: live
    live_url: https://dev.to/user/article-slug
    published_at: "2026-05-20T10:30:00Z"
  reddit:ClaudeAI:
    state: failed
    error: "Rate limit exceeded"

调度机制

schedule_at 参数

在变体中指定 schedule_at 可实现定时发布:

{
  "variants": [
    {
      "channel": "devto:main",
      "title": "定时发布文章",
      "body": "...",
      "schedule_at": "2026-05-21T09:00:00+01:00"
    }
  ]
}

drain 命令

定时任务通过 post.drain 工具触发:

# 每 5 分钟检查并执行到期任务
*/5 * * * * npx -y content-distribution-mcp drain

调度数据存储在 ~/.distribution-mcp/scheduled.yaml

凭证管理

分布配置文件

凭证通过 YAML 配置文件管理(默认位置:~/.distribution-mcp/profiles.yaml):

default:
  credentials:
    DEV_TO_API_KEY: "your-devto-api-key"
    HASHNODE_TOKEN: "your-hashnode-token"
    HASHNODE_PUBLICATION_ID: "your-pub-id"
    GITHUB_TOKEN: "ghp_..."
    GITHUB_DISCUSSION_REPO: "owner/repo"
    REDDIT_CLIENT_ID: "..."
    REDDIT_CLIENT_SECRET: "..."
    REDDIT_USERNAME: "..."
    REDDIT_PASSWORD: "..."
    BLUESKY_IDENTIFIER: "you.bsky.social"
    BLUESKY_PASSWORD: "..."
  subreddits:
    - ClaudeAI
    - LocalLLaMA

凭证需求对照表

平台认证方式必需凭证
DEV.toAPI KeyDEV_TO_API_KEY
HashnodeToken + Publication IDHASHNODE_TOKEN, HASHNODE_PUBLICATION_ID
GitHub DiscussionsPersonal Access TokenGITHUB_TOKEN, GITHUB_DISCUSSION_REPO
RedditOAuthREDDIT_CLIENT_ID, REDDIT_CLIENT_SECRET, REDDIT_USERNAME, REDDIT_PASSWORD
BlueskyApp PasswordBLUESKY_IDENTIFIER, BLUESKY_PASSWORD
LinkedIn浏览器回退
Medium浏览器回退
Twitter/X浏览器回退

*资料来源:README.md - 配置凭证部分*

常见失败模式

API 认证失败

{
  "channel": "devto",
  "state": "failed",
  "error": "DEV_TO_API_KEY not set in profile"
}

解决: 确保 profiles.yaml 中包含对应平台的凭证。

平台速率限制

Reddit 和部分平台有严格的速率限制。适配器会返回 429 状态码:

{
  "channel": "reddit",
  "state": "failed", 
  "error": "reddit 429: Too Many Requests"
}

解决: 等待冷却期后重试,或使用 subreddit.list 查询子版块的 cooldown_days

浏览器回退平台

LinkedIn、Medium、Twitter 当前只能返回 needs_browser 状态:

{
  "channel": "twitter",
  "state": "needs_browser",
  "compose_url": "https://twitter.com/compose/tweet?text=..."
}

说明: 用户需要手动在浏览器中完成发布操作。

核心数据类型

ContentInput(内容输入)

{
  id: string;                    // 内容唯一标识
  title: string;                // 标题
  subtitle?: string;            // 副标题
  body_md: string;              // Markdown 正文
  cover_image?: string;         // 封面图片 URL
  tags: string[];               // 标签列表
  canonical_url?: string;       // 原始出处 URL
  cta_block?: string;           // 行动号召块
  author: string;               // 作者名
  source_task_id?: string;      // 溯源任务 ID
}

VariantInput(渠道变体)

{
  channel: string;             // 'platform' 或 'platform:account'
  title: string;               // 渠道特定标题
  body: string;                // 渠道特定正文
  tags?: string[];             // 渠道特定标签
  canonical_url?: string;      // 渠道特定原始 URL
  cta_block?: string;          // 渠道特定 CTA
  schedule_at?: string;        // ISO-8601 调度时间
  extras?: Record<string, unknown>;  // 渠道特定参数
}

ChannelHints(平台提示)

{
  max_length: number;                              // 最大字符数
  supported_md_features: string[];                  // 支持的 Markdown 特性
  tag_vocab?: string[];                            // 推荐标签词汇
  cta_placement: "top" | "bottom" | "footer" | "none";  // CTA 放置位置
  canonical_url_supported: boolean;                 // 是否支持原始 URL
  browser_only: boolean;                           // 是否需要浏览器回退
}

环境变量

变量默认值说明
DISTRIBUTION_BACKENDyaml状态后端类型
DISTRIBUTION_DIR~/.distribution-mcp配置目录位置

相关链接

来源:https://github.com/AutomateLab-tech/content-distribution-mcp / 项目说明书

系统架构

content-distribution-mcp 是一个基于 Model Context Protocol (MCP) 的多渠道内容分发服务器。其核心设计目标是:将单一内容自动适配并发布到 8+ 个不同平台,同时保证幂等性、状态追踪和平台规则合规性。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 系统组件概览

继续阅读本节完整说明和来源证据。

章节 MCP 服务器 (server.ts)

继续阅读本节完整说明和来源证据。

章节 工具接口设计

继续阅读本节完整说明和来源证据。

概览

content-distribution-mcp 是一个基于 Model Context Protocol (MCP) 的多渠道内容分发服务器。其核心设计目标是:将单一内容自动适配并发布到 8+ 个不同平台,同时保证幂等性、状态追踪和平台规则合规性。

该系统不包含 LLM 调用,仅负责内容分发逻辑的执行。内容创作和平台适配决策由外部 Agent 完成,系统提供各平台约束提示(字符限制、标签词汇表、排版规则等),由调用方根据提示生成平台化内容变体。

资料来源:README.md:1-15

架构设计原则

原则描述
适配器模式每个平台封装为独立 Adapter,解耦平台差异
幂等性发布相同 content.id + channel 组合不会重复发布
状态后端抽象状态存储支持 YAML 文件(默认)和可扩展的其他后端
工具化接口所有功能通过 MCP 工具暴露,支持类型检查和注解
无 LLM 依赖服务器纯执行分发逻辑,不含任何 AI 调用

资料来源:README.md:30-35

核心组件

系统组件概览

graph TB
    subgraph "MCP Host (Claude Desktop / Cursor / n8n)"
        A[Agent / User]
    end
    
    subgraph "content-distribution-mcp"
        B[MCP Server<br/>src/server.ts]
        C[Tool Registry]
        D[State Backend<br/>YAML]
        E[Adapter Map]
        
        F[DevToAdapter]
        G[HashnodeAdapter]
        H[GitHubDiscussionsAdapter]
        I[RedditAdapter]
        J[BlueskyAdapter]
        K[BrowserAdapter<br/>Medium / LinkedIn / Twitter]
    end
    
    A --> B
    B --> C
    C --> D
    C --> E
    E --> F
    E --> G
    E --> H
    E --> I
    E --> J
    E --> K
    
    F --> L[DEV.to API]
    G --> M[Hashnode GraphQL]
    H --> N[GitHub API]
    I --> O[Reddit API]
    J --> P[Bluesky AT Protocol]
    K --> Q[Browser Fallback]

MCP 服务器 (server.ts)

MCP 服务器是系统的中央调度器,负责:

  1. 工具注册 — 向 MCP Host 暴露 8 个标准化工具
  2. 请求路由 — 将工具调用分发给对应的适配器
  3. 响应格式化 — 统一返回结构化 JSON 响应
graph LR
    A[post.publish] --> B[构建 Variant]
    A --> C[查找适配器]
    C --> D[检查幂等性]
    D -->|已存在| E[返回 live_url]
    D -->|不存在| F[调用 Adapter.publish]
    F --> G[保存状态]
    G --> H[返回 PublishResult]

资料来源:src/server.ts:1-50

工具接口设计

系统定义了 8 个 MCP 工具,采用点号命名法形成导航树:

工具名用途副作用幂等性
post.publish立即发布内容变体写入发布状态✅ 基于 (content.id, channel)
post.schedule排程发布 + 立即发布混合写入排程状态
post.drain触发所有到期排程任务状态变更
post.status查询发布状态只读
post.unpublish取消发布状态变更
channel.hints获取平台约束元数据只读
profile.list列出配置的分发配置只读
subreddit.listReddit 子版块目录只读

每个工具都声明了 inputSchemaoutputSchema 和 MCP 注解(readOnlyHint、destructiveHint、idempotentHint、openWorldHint)。

资料来源:src/server.ts:60-140

适配器系统架构

#### 适配器接口定义

所有平台适配器实现统一的 ChannelAdapter 接口:

interface ChannelAdapter {
  hints(): ChannelHints;                        // 静态平台元数据
  publish(variant: Variant, profile: Profile): Promise<PublishResult>;
  unpublish(liveUrl: string, profile: Profile): Promise<[boolean, string | undefined]>;
}

资料来源:src/adapters/index.ts:7-13

#### 适配器注册表

适配器通过 buildAdapterMap() 构建,支持平台别名和变体:

return {
  devto: new DevToAdapter(),
  hashnode: new HashnodeAdapter(),
  github_discussions: new GitHubDiscussionsAdapter(),
  "github-discussions": new GitHubDiscussionsAdapter(),  // 别名
  reddit: new RedditAdapter(),
  bluesky: new BlueskyAdapter(),
  medium: makeBrowserAdapter("medium"),
  medium_browser: medium,
  "medium-browser": medium,                      // 别名
  linkedin: makeBrowserAdapter("linkedin"),
  twitter: makeBrowserAdapter("twitter"),
  x: twitter,                                     // Twitter 别名
};

资料来源:src/adapters/index.ts:15-38

#### 平台分类

平台适配器类型API 方式认证需求
DEV.toDevToAdapterREST APIDEV_TO_API_KEY
HashnodeHashnodeAdapterGraphQLHASHNODE_TOKEN + HASHNODE_PUBLICATION_ID
GitHub DiscussionsGitHubDiscussionsAdapterGraphQLGITHUB_TOKEN + GITHUB_DISCUSSION_REPO
RedditRedditAdapterREST APIOAuth (client_id/secret/username/password)
BlueskyBlueskyAdapterAT ProtocolBLUESKY_IDENTIFIER + BLUESKY_PASSWORD
MediumBrowserAdapter浏览器回退无 (返回 compose URL)
LinkedInBrowserAdapter浏览器回退无 (返回 compose URL)
Twitter/XBrowserAdapter浏览器回退无 (返回 compose URL)

资料来源:README.md:80-90

适配器实现详解

API 适配器

#### DEV.to 适配器

sequenceDiagram
    participant Agent
    participant Server
    participant DevToAdapter
    participant DEV_API
    
    Agent->>Server: post.publish (devto:main)
    Server->>DevToAdapter: publish(variant, profile)
    DevToAdapter->>DEV_API: POST /api/articles
    DEV_API-->>DevToAdapter: { url, id }
    DevToAdapter-->>Server: PublishResult { state: "live", live_url }
    Server-->>Agent: JSON response

关键特性:

  • 标签限制 4 个
  • 支持 canonical_url 设置 SEO 权威链接
  • 支持 series 额外字段
  • 反发布通过 PUT /articles/:id 设置 published: false

资料来源:src/adapters/devto.ts:1-60

#### Hashnode 适配器

使用 GraphQL mutation publishPost,标签限制 5 个,支持:

  • Markdown 完整格式
  • canonical_url SEO 设置
  • reposeries 额外字段
const query = `
  mutation PublishPost($input: PublishPostInput!) {
    publishPost(input: $input) { post { url } }
  }
`;

资料来源:src/adapters/hashnode.ts:1-50

#### Bluesky 适配器

使用 AT Protocol (atproto):

  1. 创建会话获取 accessJwtdid
  2. 调用 com.atproto.repo.createRecord 发布帖子
const sessionRes = await fetch(`${BSKY}/com.atproto.server.createSession`, {
  method: "POST",
  body: JSON.stringify({ identifier, password }),
});

资料来源:src/adapters/bluesky.ts:1-50

浏览器回退适配器

对于没有公开 API 或认证复杂的平台(Medium、LinkedIn、Twitter),系统采用浏览器回退策略

async publish(variant: Variant): Promise<PublishResult> {
  return {
    channel: variant.channel,
    state: "needs_browser",
    compose_url: COMPOSE_URLS[platform](variant),
  };
}

返回 state: "needs_browser"compose_url,由用户手动完成发布。

资料来源:src/adapters/browser.ts:20-30

路线图提示:根据社区反馈,Twitter/X、LinkedIn 和 Medium 的原生 API 支持已在路线图中规划。
资料来源:社区 Issue #1

状态后端设计

后端抽象

状态后端通过 StateBackend 接口抽象,支持可插拔实现:

interface StateBackend {
  read(): Promise<DistributionState>;
  write(state: DistributionState): Promise<void>;
  readScheduled(): Promise<ScheduledItem[]>;
  writeScheduled(items: ScheduledItem[]): Promise<void>;
}

资料来源:src/backends/base.ts:1-20

默认实现:YAML 后端

默认使用 YAML 文件存储,文件位置:

文件用途
~/.distribution-mcp/state.yaml发布状态追踪
~/.distribution-mcp/scheduled.yaml排程任务队列
~/.distribution-mcp/profiles.yaml分发配置和凭证
function buildBackend(): StateBackend {
  const name = (process.env.DISTRIBUTION_BACKEND ?? "yaml").toLowerCase();
  // 支持扩展其他后端实现
}

资料来源:src/server.ts:180-190

发布结果数据结构

const publishResultShape = {
  channel: z.string(),
  state: z.enum(["live", "queued", "needs_browser", "failed"]),
  live_url: z.string().nullable(),
  draft_path: z.string().nullable(),
  compose_url: z.string().nullable(),
  error: z.string().nullable(),
  published_at: z.string().nullable(),
};
状态值含义
live成功发布,返回 live_url
queued已加入排程队列,等待 post.drain 触发
needs_browser需要浏览器手动操作,返回 compose_url
failed发布失败,error 字段包含原因

配置系统

分发配置文件

配置存储在 ~/.distribution-mcp/profiles.yaml

default:
  credentials:
    DEV_TO_API_KEY: "your-devto-api-key"
    HASHNODE_TOKEN: "your-hashnode-token"
    HASHNODE_PUBLICATION_ID: "your-pub-id"
    GITHUB_TOKEN: "ghp_..."
    GITHUB_DISCUSSION_REPO: "owner/repo"
    REDDIT_CLIENT_ID: "..."
    REDDIT_CLIENT_SECRET: "..."
    REDDIT_USERNAME: "..."
    REDDIT_PASSWORD: "..."
    BLUESKY_IDENTIFIER: "you.bsky.social"
    BLUESKY_PASSWORD: "..."
  subreddits:
    - ClaudeAI
    - LocalLLaMA

资料来源:README.md:55-75

环境变量配置

变量默认值用途
DISTRIBUTION_BACKENDyaml状态后端类型
DISTRIBUTION_PROFILES_PATH~/.distribution-mcp/profiles.yaml配置文件路径

内容变体模型

Variant 数据结构

graph TB
    A[Variant] --> B[channel: string]
    A --> C[title: string]
    A --> D[body: string]
    A --> E[tags: string[]]
    A --> F[canonical_url?: string]
    A --> G[cta_block?: string]
    A --> H[schedule_at?: string]
    A --> I[extras: Record]
字段类型描述
channelstring频道标识符,格式 platformplatform:account
titlestring平台适配后的标题
bodystring平台适配后的正文
tagsstring[]平台适配后的标签
canonical_urlstring?SEO 权威链接
cta_blockstring?行动号召块
schedule_atstring?ISO-8601 排程时间
extrasobject平台特定参数(flair、category 等)

平台约束元数据 (Channel Hints)

通过 channel.hints 工具获取平台约束:

hints(): ChannelHints {
  return {
    max_length: 300,
    supported_md_features: ["links"],
    cta_placement: "bottom",
    canonical_url_supported: false,
    browser_only: false,
  };
}
平台最大长度Markdown 支持CTA 位置浏览器回退
DEV.to100,000完整bottom
Hashnode50,000完整bottom
GitHub Discussions10,000基础bottom
Reddit40,000none
Bluesky300仅链接bottom
Medium100,000完整bottom
LinkedIn3,000粗体/斜体/链接bottom
Twitter/X280仅链接none

发布流程

完整发布时序

sequenceDiagram
    participant Agent
    participant Server
    participant Backend
    participant Adapter
    participant PlatformAPI
    
    Note over Agent: 准备 content + variants
    
    Agent->>Server: post.publish(content, variants, profile_name)
    
    Server->>Backend: read() 获取当前状态
    Backend-->>Server: currentState
    
    loop 每个 Variant
        Server->>Server: 检查幂等性 (content.id + channel)
        
        alt 已发布
            Server-->>Agent: 返回现有 live_url
        else 未发布
            Server->>Adapter: 查找对应平台的 Adapter
            Adapter->>PlatformAPI: publish() API 调用
            
            alt 发布成功
                PlatformAPI-->>Adapter: live_url
                Adapter-->>Server: PublishResult { state: "live" }
                Server->>Backend: write() 保存状态
                Server-->>Agent: 返回成功结果
            else 发布失败
                PlatformAPI-->>Adapter: error
                Adapter-->>Server: PublishResult { state: "failed" }
                Server-->>Agent: 返回错误详情
            else 需要浏览器
                Adapter-->>Server: PublishResult { state: "needs_browser" }
                Server-->>Agent: 返回 compose_url
            end
        end
    end
    
    Note over Agent: 使用 live_url 完成后续工作流

排程发布流程

graph LR
    A[post.schedule] --> B{schedule_at 字段检查}
    B -->|有 schedule_at| C[写入 scheduled.yaml]
    B -->|无 schedule_at| D[立即 publish]
    C --> E[返回 queued 状态]
    E --> F[cron: post.drain]
    F --> G{检查到期任务}
    G -->|到期| H[执行发布]
    G -->|未到期| I[跳过]

排程任务通过 cron 触发:

# 每 5 分钟检查并发布到期任务
*/5 * * * * npx -y content-distribution-mcp drain

安全性设计

凭证隔离

  • 凭证存储在本地 profiles.yaml,不进入源码
  • 每个 profile 独立配置,支持多账号
  • 通过环境变量或配置文件注入,无硬编码密钥

平台合规

平台合规机制
Reddit子版块冷却期 (cooldown_days)、标签词汇表、最后发布时间追踪
所有平台幂等性防止重复发布、速率限制感知

资料来源:src/server.ts:150-180

扩展架构

添加新平台适配器

  1. src/adapters/ 创建新文件,实现 ChannelAdapter 接口
  2. src/adapters/index.tsbuildAdapterMap() 中注册
  3. profiles.yaml 添加对应凭证配置
export class NewPlatformAdapter implements ChannelAdapter {
  hints(): ChannelHints { /* 返回平台约束 */ }
  async publish(variant: Variant, profile: Profile): Promise<PublishResult> { /* 发布逻辑 */ }
  async unpublish(liveUrl: string, profile: Profile): Promise<[boolean, string?>] { /* 取消逻辑 */ }
}

添加新状态后端

实现 StateBackend 接口并在 buildBackend() 工厂函数中注册:

interface StateBackend {
  read(): Promise<DistributionState>;
  write(state: DistributionState): Promise<void>;
  readScheduled(): Promise<ScheduledItem[]>;
  writeScheduled(items: ScheduledItem[]): Promise<void>;
}

项目结构

content-distribution-mcp/
├── src/
│   ├── server.ts              # MCP 服务器主入口
│   ├── index.ts               # CLI 入口和 stdio 模式
│   ├── models.ts              # 数据类型定义
│   ├── adapters/
│   │   ├── index.ts           # 适配器注册表
│   │   ├── devto.ts           # DEV.to 适配器
│   │   ├── hashnode.ts        # Hashnode 适配器
│   │   ├── github-discussions.ts
│   │   ├── reddit.ts
│   │   ├── bluesky.ts
│   │   ├── browser.ts         # 浏览器回退适配器
│   │   └── ...
│   └── backends/
│       ├── base.ts            # 后端接口定义
│       └── yaml.ts            # YAML 后端实现
├── README.md
└── package.json

参见

资料来源:README.md:1-15

数据流与状态管理

Content Distribution MCP 的核心职责之一是管理多平台内容发布的状态与数据流。系统需要解决以下关键问题:

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 Content 内容模型

继续阅读本节完整说明和来源证据。

章节 Variant 变体模型

继续阅读本节完整说明和来源证据。

章节 PublishState 发布状态枚举

继续阅读本节完整说明和来源证据。

概述

Content Distribution MCP 的核心职责之一是管理多平台内容发布的状态与数据流。系统需要解决以下关键问题:

  1. 幂等性保障:同一内容在相同渠道的重复发布应返回已有结果,而非重复创建
  2. 状态追踪:记录每个渠道的发布状态(live、queued、needs_browser、failed)
  3. 去重机制:通过 content.id + channel 组合键实现发布去重
  4. 调度管理:支持定时发布,将未来时间的发布请求入队待执行

资料来源:src/models.ts:1-45

核心数据模型

Content 内容模型

interface Content {
  id: string;                    // 内容唯一标识符
  title: string;                 // 内容标题
  subtitle?: string;             // 副标题(Hashnode、DEV.to 支持)
  body_md: string;               // Markdown 格式的正文
  cover_image?: string;          // 封面图片 URL
  tags: string[];                // 标签列表
  canonical_url?: string;        // 权威原文 URL
  cta_block?: string;            // 行动号召区块
  author: string;                // 作者显示名称
  source_task_id?: string;       // 溯源任务 ID(如 Notion 任务 ID)
}

资料来源:src/models.ts:1-14

Variant 变体模型

每个渠道的适配内容称为变体,包含渠道特定的调整:

interface Variant {
  channel: string;              // 渠道标识符,格式为 'platform' 或 'platform:account'
  title: string;                // 渠道特定标题
  body: string;                 // 渠道适配后的正文
  tags: string[];               // 渠道特定标签
  canonical_url?: string;       // 该渠道的权威 URL 覆盖
  cta_block?: string;           // 该渠道的 CTA 覆盖
  schedule_at?: string;         // ISO-8601 定时发布时间
  extras: Record<string, unknown>; // 渠道特定参数(flair、category 等)
}

资料来源:src/models.ts:16-28

PublishState 发布状态枚举

状态值含义触发场景
live已成功发布平台 API 返回成功响应
queued已加入调度队列设置了未来时间的 schedule_at
needs_browser需要浏览器操作LinkedIn、Medium、Twitter 等无 API 的渠道
failed发布失败API 调用失败、认证缺失、平台限流等

资料来源:src/models.ts:30

PublishResult 发布结果

interface PublishResult {
  channel: string;              // 变体对应的渠道标识符
  state: PublishState;          // 当前状态
  live_url?: string;            // 线上帖子 URL(state=live 时)
  draft_path?: string;          // 本地草稿路径(浏览器回退渠道)
  compose_url?: string;         // 平台编辑 URL(needs_browser 时)
  error?: string;               // 错误信息(state=failed 时)
  published_at?: string;        // UTC ISO-8601 发布时间
}

资料来源:src/models.ts:32-45

架构组件

适配器架构

系统采用适配器模式,每种发布渠道对应一个适配器实现:

graph TD
    A[post.publish / post.schedule] --> B[Server 核心逻辑]
    B --> C{幂等性检查}
    C -->|已有发布记录| D[返回缓存结果]
    C -->|新发布请求| E[选择适配器]
    E --> F[devto: DevToAdapter]
    E --> G[hashnode: HashnodeAdapter]
    E --> H[reddit: RedditAdapter]
    E --> I[bluesky: BlueskyAdapter]
    E --> J[browser: makeBrowserAdapter]
    F --> K[平台 API]
    G --> K
    H --> K
    I --> K
    J --> L[返回 compose_url]
    K --> M[写入状态后端]
    M --> N[返回 PublishResult]

资料来源:src/adapters/index.ts:1-30

适配器接口定义

interface ChannelAdapter {
  hints(): ChannelHints;                           // 静态渠道元数据
  publish(variant: Variant, profile: Profile): Promise<PublishResult>;
  unpublish(liveUrl: string, profile: Profile): Promise<[boolean, string | undefined]>;
}
方法副作用确定性
hints()只读,无 HTTP 调用编译时常量,完全确定性
publish()写操作,调用平台 API依赖外部状态
unpublish()写操作,调用平台 API依赖外部状态

资料来源:src/adapters/index.ts:1-12

状态后端架构

后端类型

系统支持可插拔的状态后端,通过环境变量 DISTRIBUTION_BACKEND 配置:

后端类型默认值说明
yaml使用 YAML 文件存储状态(~/.distribution-mcp/
function buildBackend(): StateBackend {
  const name = (process.env.DISTRIBUTION_BACKEND ?? "yaml").toLowerCase();
  // 根据配置构建对应后端实例
}

资料来源:src/server.ts:180-185

状态存储结构

状态后端管理以下数据:

graph LR
    A[published.yaml] --> B[content.id → channel → PublishResult]
    C[scheduled.yaml] --> D[schedule_at → Variant 列表]
    E[profiles.yaml] --> F[profile_name → credentials + config]

published.yaml 存储已发布状态,键结构为 content.idchannel

"n8n-webhook-setup@2026-05-20":
  devto:main:
    state: live
    live_url: "https://dev.to/xxx"
    published_at: "2026-05-20T10:30:00Z"

scheduled.yaml 存储待调度任务:

- channel: reddit:ClaudeAI
  schedule_at: "2026-05-21T09:00:00+01:00"
  content: {...}
  variant: {...}

资料来源:README.md:60-80

幂等性机制

幂等性原理

系统的幂等性基于 (content.id, channel) 组合键实现:

sequenceDiagram
    participant Agent
    participant Server
    participant Backend
    participant Platform

    Agent->>Server: post.publish(content, variants)
    Server->>Backend: 查询 published.yaml
    Backend-->>Server: 已有记录?
    alt 已有发布记录
        Server-->>Agent: 返回缓存的 live_url
    else 无记录
        Server->>Platform: 调用平台 API
        Platform-->>Server: 发布结果
        Server->>Backend: 写入状态
        Server-->>Agent: 返回新结果
    end

幂等性特点

特性说明
组合键去重content.id + channel 共同决定唯一性
缓存返回重复请求直接返回缓存的 live_url
安全重试发布失败后可安全重试,不会重复创建
跨会话持久化状态存储在 YAML 文件中,重启后保持

资料来源:README.md:45-55

调度工作流

调度发布流程

使用 post.schedule 时,带有 schedule_at 的变体会被加入调度队列:

graph LR
    A[post.schedule] --> B{schedule_at?}
    B -->|有未来时间| C[写入 scheduled.yaml]
    B -->|立即发布| D[调用适配器]
    C --> E[drain 触发]
    E --> F{到时检查}
    F -->|未到时| G[跳过]
    F -->|已到时| H[执行发布]
    H --> I[写入 published.yaml]
    H --> J[从 scheduled.yaml 移除]

Drain 命令

post.drain 工具检查所有待调度的发布,触发已到期的任务:

# 建议通过 cron 每 5 分钟执行一次
*/5 * * * * npx -y content-distribution-mcp drain

调度格式要求:

  • 必须包含时区偏移量(如 +08:00
  • 正确格式:2026-05-21T09:00:00+01:00
  • 缺少时区会导致不可预期行为

资料来源:README.md:56-70

渠道适配器详解

API 直连渠道

以下渠道通过原生 API 发布:

渠道适配器认证要求
DEV.toDevToAdapterDEV_TO_API_KEY
HashnodeHashnodeAdapterHASHNODE_TOKEN + HASHNODE_PUBLICATION_ID
GitHub DiscussionsGitHubDiscussionsAdapterGITHUB_TOKEN + GITHUB_DISCUSSION_REPO
RedditRedditAdapterREDDIT_CLIENT_ID/SECRET/USERNAME/PASSWORD
BlueskyBlueskyAdapterBLUESKY_IDENTIFIER + BLUESKY_PASSWORD

#### DEV.to 发布流程示例

const res = await fetch(`${API}/articles`, {
  method: "POST",
  headers: { "Content-Type": "application/json", "api-key": apiKey },
  body: JSON.stringify({
    article: {
      title: variant.title,
      body_markdown: variant.body,
      tags: variant.tags.slice(0, 4),  // 最多 4 个标签
      canonical_url: variant.canonical_url,
      published: true,
    },
  }),
});

资料来源:src/adapters/devto.ts:20-38

#### Bluesky 发布流程示例

// 1. 创建会话获取 JWT
const sessionRes = await fetch(`${BSKY}/com.atproto.server.createSession`, {
  method: "POST",
  body: JSON.stringify({ identifier: BLUESKY_IDENTIFIER, password: BLUESKY_PASSWORD }),
});
const session = await sessionRes.json();

// 2. 使用 JWT 创建帖子
const postRes = await fetch(`${BSKY}/com.atproto.repo.createRecord`, {
  headers: { "Authorization": `Bearer ${session.accessJwt}` },
  body: JSON.stringify({...}),
});

资料来源:src/adapters/bluesky.ts:20-35

浏览器回退渠道

以下渠道无公开 API,需要浏览器操作:

渠道返回状态返回字段
Mediumneeds_browsercompose_urlhttps://medium.com/new-story
LinkedInneeds_browsercompose_urlhttps://www.linkedin.com/post/new
Twitter/Xneeds_browsercompose_url → 预填充推文链接
const COMPOSE_URLS: Record<Platform, (v: Variant) => string> = {
  medium: () => "https://medium.com/new-story",
  linkedin: () => "https://www.linkedin.com/post/new",
  twitter: (v) => `https://twitter.com/compose/tweet?text=${encodeURIComponent(v.body.slice(0, 280))}`,
};

资料来源:src/adapters/browser.ts:1-10

社区关注:根据路线图规划,Twitter/X、LinkedIn、Medium 的原生 API 支持正在计划中。资料来源:社区上下文 - Roadmap & feature tracker

工具接口

MCP 工具列表

工具用途副作用
post.publish立即发布所有变体幂等写操作
post.schedule队列定时发布 + 立即发布写 scheduled.yaml + 幂等写操作
post.drain触发所有到期调度执行 queued → live
post.status查询内容/渠道状态只读
post.unpublish尝试取消发布平台删除操作
channel.hints渠道静态元数据只读,无 HTTP
profile.list列出分发配置只读
subreddit.listReddit 子版块目录只读

发布结果输出模式

const publishOutputShape = {
  results: z.array(z.object({
    channel: z.string(),
    state: z.enum(["live", "queued", "needs_browser", "failed"]),
    live_url: z.string().nullable(),
    draft_path: z.string().nullable(),
    compose_url: z.string().nullable(),
    error: z.string().nullable(),
    published_at: z.string().nullable(),
  })),
};

资料来源:src/server.ts:95-110

常见故障模式

认证失败

渠道缺失凭证错误表现
DEV.toDEV_TO_API_KEYstate: "failed", error: "DEV_TO_API_KEY not set"
HashnodeHASHNODE_TOKENstate: "failed", error: "HASHNODE_TOKEN or HASHNODE_PUBLICATION_ID not set"
BlueskyBLUESKY_IDENTIFIERstate: "failed", error: "BLUESKY_IDENTIFIER and BLUESKY_PASSWORD required"

平台限流

当平台返回非 200 状态码时:

{
  "channel": "devto:main",
  "state": "failed",
  "error": "devto 429: Rate limit exceeded"
}

Reddit 反垃圾机制

Reddit 渠道具有自动限流检测:

  • 子版块冷却期(cooldown_days)
  • 发帖频率超过限制时返回 needs_browser
  • 可通过 subreddit.list 查看上次成功发帖时间

浏览器回退渠道状态

对于 needs_browser 状态的应用流程:

graph TD
    A[post.publish 返回 needs_browser] --> B[获取 compose_url]
    B --> C[用户在浏览器中手动发布]
    D[可选: 手动记录 live_url] --> E[后续请求可识别为 live]

资料来源:src/adapters/browser.ts:15-30

最佳实践

1. 设计稳定的 content.id

推荐格式:{slug}@{date},例如:

{
  "id": "n8n-webhook-setup@2026-05-20"
}

2. 合理使用调度

  • 避免过密的调度间隔
  • 建议 Reddit 渠道间隔至少 24-48 小时
  • 时区必须明确指定

3. 渠道特定调整

发布前使用 channel.hints 获取约束:

{
  "channel": "devto",
  "result": {
    "max_length": 100000,
    "supported_md_features": ["bold", "italic", "code_inline", "links"],
    "tag_vocab": ["ai", "automation", "mcp", "claude"],
    "cta_placement": "bottom",
    "canonical_url_supported": true,
    "browser_only": false
  }
}

4. 错误处理策略

const result = await callTool("post.publish", {...});

// 检查状态
if (result.state === "failed") {
  console.error(`发布失败: ${result.error}`);
} else if (result.state === "needs_browser") {
  console.log(`请手动发布: ${result.compose_url}`);
} else if (result.state === "live") {
  console.log(`已发布: ${result.live_url}`);
}

相关文档

参见

资料来源:src/models.ts:1-45

适配器概述

适配器(Adapter)是 content-distribution-mcp 项目的核心组件,负责将统一格式的内容变体(Variant)发布到不同的内容平台。每个平台适配器封装了该平台的 API 调用规范、认证方式、格式限制和发布逻辑,使 MCP 服务器能够以一致的方式处理多平台内容分发。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 适配器接口

继续阅读本节完整说明和来源证据。

章节 适配器注册机制

继续阅读本节完整说明和来源证据。

章节 适配器选择流程

继续阅读本节完整说明和来源证据。

概述

适配器(Adapter)是 content-distribution-mcp 项目的核心组件,负责将统一格式的内容变体(Variant)发布到不同的内容平台。每个平台适配器封装了该平台的 API 调用规范、认证方式、格式限制和发布逻辑,使 MCP 服务器能够以一致的方式处理多平台内容分发。

适配器系统采用适配器模式(Adapter Pattern),将平台差异抽象为统一的接口。调用方只需通过 ChannelAdapter 接口与各平台交互,无需关心底层 API 的具体实现细节。资料来源:src/adapters/index.ts:1-20

架构设计

适配器接口

所有平台适配器必须实现 ChannelAdapter 接口,该接口定义了三个核心方法:

方法返回类型说明
hints()ChannelHints返回平台的静态元数据(字符限制、Markdown 支持等)
publish(variant, profile)Promise<PublishResult>将内容变体发布到目标平台
unpublish(liveUrl, profile)Promise<[boolean, string?]>取消发布(返回成功状态和可选错误信息)

资料来源:src/adapters/index.ts:11-15

适配器注册机制

MCP 服务器通过 buildAdapterMap() 函数构建所有适配器的映射表,并将其存储在内存中供工具处理器调用:

export function buildAdapterMap(): Record<string, ChannelAdapter> {
  const medium = makeBrowserAdapter("medium");
  const linkedin = makeBrowserAdapter("linkedin");
  const twitter = makeBrowserAdapter("twitter");

  return {
    devto: new DevToAdapter(),
    hashnode: new HashnodeAdapter(),
    github_discussions: new GitHubDiscussionsAdapter(),
    "github-discussions": new GitHubDiscussionsAdapter(),
    reddit: new RedditAdapter(),
    bluesky: new BlueskyAdapter(),
    medium,
    medium_browser: medium,
    "medium-browser": medium,
    linkedin,
    linkedin_browser: linkedin,
    "linkedin-browser": linkedin,
    twitter,
    twitter_browser: twitter,
    "twitter-browser": twitter,
    x: twitter,
  };
}

资料来源:src/adapters/index.ts:24-48

注意:部分平台提供了多个别名(如 github_discussionsgithub-discussions),以兼容不同的命名习惯。

适配器选择流程

当用户调用 post.publishpost.schedule 工具时,服务器根据变体中的 channel 字段选择对应的适配器:

graph TD
    A[用户调用 post.publish] --> B[解析 channel 字段<br/>格式: 'platform' 或 'platform:account']
    B --> C[提取平台前缀<br/>如 'devto', 'reddit', 'linkedin']
    C --> D[从 adapters 映射表<br/>查找对应适配器]
    D --> E{适配器是否存在?}
    E -->|是| F[调用 adapter.publish]
    E -->|否| G[抛出错误: No adapter for 'channel']
    F --> H[返回 PublishResult]

资料来源:src/server.ts:server.registerTool 调用逻辑

平台适配器分类

API 直连型适配器

以下平台具有官方公开 API,支持自动化发布:

平台适配器类API 类型必需凭证
DEV.toDevToAdapterREST APIDEV_TO_API_KEY
HashnodeHashnodeAdapterGraphQLHASHNODE_TOKEN + HASHNODE_PUBLICATION_ID
GitHub DiscussionsGitHubDiscussionsAdapterGraphQLGITHUB_TOKEN + GITHUB_DISCUSSION_REPO
RedditRedditAdapterOAuth RESTREDDIT_CLIENT_ID/SECRET/USERNAME/PASSWORD
BlueskyBlueskyAdapterAT Protocol (REST)BLUESKY_IDENTIFIER + BLUESKY_PASSWORD

资料来源:src/adapters/index.ts:26-31

浏览器回退型适配器

以下平台目前没有公开或稳定的内容发布 API,采用浏览器回退机制:

平台状态返回值用户操作
LinkedIn浏览器回退needs_browser + compose_url手动在浏览器中完成发布
Medium浏览器回退needs_browser + compose_url手动在浏览器中完成发布
Twitter/X浏览器回退needs_browser + compose_url手动在浏览器中完成发布

资料来源:src/adapters/browser.ts:1-45

社区规划路线图与功能追踪 显示社区正在计划为这些平台添加原生 API 支持。

平台 hints 元数据

每个适配器的 hints() 方法返回平台静态元数据,帮助调用方了解平台约束:

ChannelHints 数据结构

interface ChannelHints {
  max_length: number;                    // 正文字符上限
  supported_md_features: string[];       // 支持的 Markdown 特性列表
  cta_placement: "top" | "bottom" | "footer" | "none";  // CTA 放置位置
  canonical_url_supported: boolean;      // 是否支持 canonical_url
  browser_only: boolean;                 // 是否为浏览器回退模式
  tag_vocab?: string[];                  // 可选:标签词汇表
}

各平台 hints 对比

平台最大长度Markdown 特性CTA 位置canonical_url浏览器回退
DEV.to100,000bold, italic, code_inline, code_block, links, headers, images, lists, tables, blockquotebottom
Hashnode50,000bold, italic, code_inline, code_block, links, headers, images, lists, tables, blockquotebottom
GitHub Discussions65,536bold, italic, code_inline, code_block, links, headers, listsbottom
Reddit40,000bold, italic, code_inline, linksbottom
Bluesky300linksbottom
LinkedIn3,000bold, italic, linksbottom
Medium100,000bold, italic, code_inline, code_block, links, headers, imagesbottom
Twitter/X280linksnone

资料来源:src/adapters/devto.ts:11-18, src/adapters/hashnode.ts:11-17, src/adapters/bluesky.ts:11-16, src/adapters/browser.ts:15-43

发布结果状态

publish() 方法返回 PublishResult 对象,包含发布操作的结果信息:

状态含义live_urlcompose_urldraft_path
live发布成功✓ 公开链接
queued已加入定时队列
needs_browser需手动浏览器发布✓ 平台编辑链接
failed发布失败

资料来源:src/server.ts:publishResultShape 定义

认证机制

API Key 认证

DEV.to 使用简单的 API Key 认证:

const res = await fetch(`${API}/articles`, {
  method: "POST",
  headers: { "Content-Type": "application/json", "api-key": apiKey },
  body: JSON.stringify({ article: { ... } }),
});

资料来源:src/adapters/devto.ts:22-29

OAuth 认证

Reddit 使用 OAuth 2.0 认证流程获取访问令牌:

const tokenRes = await fetch("https://www.reddit.com/api/v1/access_token", {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded", "Authorization": basicAuth },
  body: new URLSearchParams({
    grant_type: "password",
    username: redditUsername,
    password: redditPassword,
  }),
});

会话认证

Bluesky 使用会话认证,先创建会话获取 JWT,再使用 JWT 发布内容:

const sessionRes = await fetch(`${BSKY}/com.atproto.server.createSession`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ identifier: BLUESKY_IDENTIFIER, password: BLUESKY_PASSWORD }),
});
const session = await sessionRes.json() as { accessJwt: string; did: string };

const postRes = await fetch(`${BSKY}/com.atproto.repo.createRecord`, {
  method: "POST",
  headers: { "Content-Type": "application/json", "Authorization": `Bearer ${session.accessJwt}` },
  body: JSON.stringify({ ... }),
});

资料来源:src/adapters/bluesky.ts:23-37

浏览器回退适配器实现

对于尚未支持原生 API 的平台,makeBrowserAdapter() 工厂函数创建浏览器回退适配器:

export function makeBrowserAdapter(platform: Platform) {
  return {
    hints(): ChannelHints {
      return HINTS[platform];
    },
    async publish(variant: Variant): Promise<PublishResult> {
      return {
        channel: variant.channel,
        state: "needs_browser",
        compose_url: COMPOSE_URLS[platform](variant),
      };
    },
    async unpublish(_liveUrl: string): Promise<[boolean, string]> {
      return [false, `${platform} posts must be deleted manually via the platform UI`];
    },
  };
}

关键特性:

  • publish() 始终返回 needs_browser 状态
  • compose_url 包含预填充的内容(如 Twitter 可携带推文草稿)
  • unpublish() 永远返回失败,需用户手动在平台界面操作

资料来源:src/adapters/browser.ts:45-65

定时发布支持

适配器本身不处理定时逻辑,定时功能由后端状态管理处理。适配器只需正确处理 schedule_at 字段:

  1. schedule_at 存在时,服务器将变体存入 scheduled.yaml
  2. 定时时间到达后,post.drain 工具读取队列并调用适配器 publish()
  3. 适配器无需感知定时机制

常见失败模式

认证失败

错误信息原因解决方案
DEV_TO_API_KEY not set in profile未配置 API Keyprofiles.yaml 中配置 DEV_TO_API_KEY
Bluesky auth failed: 401Bluesky 凭证无效检查 BLUESKY_IDENTIFIERBLUESKY_PASSWORD
HASHNODE_TOKEN or HASHNODE_PUBLICATION_ID not setHashnode 配置不完整配置 HASHNODE_TOKENHASHNODE_PUBLICATION_ID

平台限制

错误信息原因解决方案
devto 422: ...DEV.to 验证失败检查标签数量(最多4个)、标题长度
Reddit cooldown同一 subreddit 发帖过于频繁遵守 cooldown_days 间隔

扩展新平台适配器

如需添加新平台支持,需创建新适配器类并注册到 buildAdapterMap()

  1. src/adapters/ 目录创建新文件(如 newplatform.ts
  2. 实现 ChannelAdapter 接口的三个方法
  3. src/adapters/index.ts 中导入并添加到返回对象
  4. src/server.ts 的工具处理逻辑中添加对应的工具名处理

相关文档

资料来源:src/adapters/index.ts:11-15

DEV.to 适配器

DEV.to 适配器是 content-distribution-mcp 项目中负责将内容发布到 DEV.to 平台的适配器实现。它属于自动发布层级(Auto Tier),通过 DEV.to 原生 REST API 实现内容发布,支持完整的 Markdown 渲染、标签管理、系列文章组织以及规范化 URL(canonical URL)设置。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 适配器定位

继续阅读本节完整说明和来源证据。

章节 通道标识符

继续阅读本节完整说明和来源证据。

章节 平台约束参数表

继续阅读本节完整说明和来源证据。

概述

DEV.to 适配器是 content-distribution-mcp 项目中负责将内容发布到 DEV.to 平台的适配器实现。它属于自动发布层级(Auto Tier),通过 DEV.to 原生 REST API 实现内容发布,支持完整的 Markdown 渲染、标签管理、系列文章组织以及规范化 URL(canonical URL)设置。

DEV.to 适配器位于适配器架构的自动发布通道,与 Hashnode、GitHub Discussions、Reddit、Bluesky 同属一类——即平台提供了公开 API,可直接通过 HTTP 请求完成发布,无需浏览器交互。

资料来源:src/adapters/index.ts:8-11

技术架构

适配器定位

graph TD
    A[post.publish / post.schedule] --> B{平台类型判定}
    B -->|browser_only: false| C[自动发布通道]
    B -->|browser_only: true| D[浏览器回退通道]
    C --> E[DEV.to 适配器]
    C --> F[Hashnode 适配器]
    C --> G[GitHub Discussions 适配器]
    C --> H[Reddit 适配器]
    C --> I[Bluesky 适配器]
    
    style E fill:#90EE90

DEV.to 适配器实现了 ChannelAdapter 接口,提供了三个核心方法:

方法功能副作用
hints()返回平台元数据(字符限制、Markdown 支持特性、标签词汇表等)无,仅读取编译时常量
publish(variant, profile)将内容变体发布到 DEV.to写入外部 API
unpublish(liveUrl, profile)将已发布的文章设为草稿状态写入外部 API

资料来源:src/adapters/devto.ts:1-70

通道标识符

在 content-distribution-mcp 中,DEV.to 通道使用以下标识符:

标识符说明示例
devto主通道标识符"devto"
devto:account带账户后缀的完整标识符"devto:main", "devto:username"

在使用 post.publishpost.schedule 时,variants[].channel 字段可接受上述格式。

资料来源:src/server.ts:25-27

ChannelHints 元数据

DEV.to 适配器的 hints() 方法返回以下平台约束信息:

graph LR
    subgraph "DEV.to 平台特性"
        A["max_length: 100,000"]
        B["tag_vocab: 10个推荐标签"]
        C["cta_placement: bottom"]
        D["canonical_url_supported: true"]
        E["supported_md_features: bold, italic, code_inline..."]
    end

平台约束参数表

参数说明
max_length100,000正文字符最大长度
supported_md_featuresbold, italic, code_inline, code_block, links, headers, images, lists, tables, blockquote支持的 Markdown 功能完整列表
tag_vocabai, automation, mcp, claude, llm, devops, tutorial, productivity, javascript, python推荐使用的标签词汇
cta_placementbottomCTA 区块放置位置(正文底部)
canonical_url_supportedtrue是否支持规范化 URL
browser_onlyfalse是否需要浏览器回退(否,可自动发布)

资料来源:src/adapters/devto.ts:9-22

发布流程

发布状态流转

stateDiagram-v2
    [*] --> 认证检查
    认证检查 --> |API Key 存在| API调用
    认证检查 --> |API Key 缺失| 认证失败: state: "failed"
    
    API调用 --> |HTTP 200| 发布成功: state: "live"
    API调用 --> |HTTP !200| 发布失败: state: "failed"
    
    发布成功 --> 返回: live_url, published_at
    认证失败 --> 返回: error message
    发布失败 --> 返回: error message

publish 方法详解

async publish(variant: Variant, profile: Profile): Promise<PublishResult>

认证要求:

DEV.to API 需要 DEV_TO_API_KEY 凭证,该凭证存储在分发配置文件的 credentials 字段中:

# ~/.distribution-mcp/profiles/default.yaml
credentials:
  DEV_TO_API_KEY: "your-devto-api-key-here"

资料来源:src/adapters/devto.ts:24-26

API 请求构建:

适配器向 https://dev.to/api/articles 端点发送 POST 请求,请求体结构如下:

字段来源说明
article.titlevariant.title文章标题
article.body_markdownvariant.bodyMarkdown 正文
article.tagsvariant.tags.slice(0, 4)标签(最多4个,超出部分截断)
article.canonical_urlvariant.canonical_url(可选)规范化 URL
article.publishedtrue立即发布
article.seriesvariant.extras.series(可选)系列名称

资料来源:src/adapters/devto.ts:28-44

响应处理:

  • 成功响应(HTTP 200):解析 JSON 返回的 url 字段,构建 PublishResult 对象
  • state: "live"
  • live_url: 解析得到的文章 URL
  • published_at: 当前 UTC 时间戳
  • 认证失败:返回 state: "failed" + 错误信息 "DEV_TO_API_KEY not set in profile"
  • API 错误:返回 state: "failed" + 错误信息 "devto {status}: {response_text}"

资料来源:src/adapters/devto.ts:45-52

幂等性保证

DEV.to 适配器支持幂等发布。当相同 content.id + channel 组合的内容被再次提交时,post.publish 工具会先检查状态后端,如果该内容已在 DEV.to 上线,则直接返回现有的 live_url,避免重复发布。

下线(Unpublish)流程

unpublish 方法详解

async unpublish(liveUrl: string, profile: Profile): Promise<[boolean, string | undefined]>

DEV.to 平台不支持直接删除文章,适配器采用设为草稿的方式实现下线:

sequenceDiagram
    参与者 A as 调用方
    参与者 B as DEV.to API
    
    A->>B: GET /articles/me/published?per_page=100
    B-->>A: 文章列表 [{id, slug}, ...]
    
    alt 找到匹配文章
        A->>B: PUT /articles/{id} {article: {published: false}}
        B-->>A: 200 OK
        A-->>A: return [true, undefined]
    else 未找到匹配
        A-->>A: return [false, "article not found"]
    end

下线步骤:

  1. 使用 DEV_TO_API_KEY 调用 GET /articles/me/published?per_page=100 获取当前用户已发布的文章列表
  2. 在列表中匹配 liveUrl 中包含的文章 slug
  3. 若找到匹配项,调用 PUT /articles/{id}published 设为 false
  4. 返回 [true, undefined] 表示成功,或 [false, errorMessage] 表示失败

资料来源:src/adapters/devto.ts:54-76

配置指南

环境变量与凭证配置

配置项说明必需
DEV_TO_API_KEYDEV.to 个人 API Key

获取 API Key 的步骤:

  1. 登录 DEV.to
  2. 进入 SettingsExtensionsDEV API Keys
  3. 创建一个新的 API Key

profile.yaml 配置示例

# ~/.distribution-mcp/profiles/my-profile.yaml
credentials:
  DEV_TO_API_KEY: "ABCdefGHIjklMNOpqrsTUVwxyz123456"

Variant 配置示例

{
  "content": {
    "id": "my-article-2026-05-20",
    "title": "Getting Started with MCP Servers",
    "body_md": "# Introduction\n\nThis is the full article body...",
    "tags": ["mcp", "automation", "tutorial", "devops", "productivity"],
    "canonical_url": "https://myblog.com/mcp-getting-started",
    "author": "Your Name"
  },
  "variants": [
    {
      "channel": "devto:main",
      "title": "Getting Started with MCP Servers",
      "body": "# Introduction\n\nThis is the DEV.to specific content...",
      "tags": ["mcp", "automation", "tutorial"],
      "canonical_url": "https://myblog.com/mcp-getting-started",
      "extras": {
        "series": "MCP Tutorial Series"
      }
    }
  ],
  "profile_name": "my-profile"
}

资料来源:README.md:52-75

平台特性与限制

DEV.to 特有的 extras 参数

参数类型说明
seriesstring将文章分配到某个系列(Series),用于组织系列教程或专题内容

Markdown 支持详情

DEV.to 是所有通道中 Markdown 支持最完整的平台之一:

功能支持说明
粗体 text
斜体 *text*
行内代码 ` code `
代码块 ``` `lang ```支持语法高亮
链接 text
标题 # ## ###支持 h1-h6
图片 !alt
列表 - item / 1. item
表格 `colcol`
引用 > quote

资料来源:src/adapters/devto.ts:16-18

标签处理规则

  • 每个变体的 tags 数组最多保留 4 个标签
  • 超出限制的标签将被自动截断
  • 建议使用 channel.hints 返回的 tag_vocab 中的标签以提高发现率

CTA 区块处理

DEV.to 的 cta_placementbottom,意味着 content.cta_blockvariant.cta_block 的内容会被追加到文章正文的末尾。

常见问题与故障排除

认证失败

症状: 返回 state: "failed" + "DEV_TO_API_KEY not set in profile"

解决方案:

  1. 确认 profile.yamlcredentials.DEV_TO_API_KEY 已正确配置
  2. 确认 API Key 未过期,可前往 DEV.to 设置页面重新生成
  3. 确认使用的 profile 名称与配置一致

文章未找到(下线失败)

症状: unpublish 返回 [false, "article not found in published list"]

原因:

  • 传入的 liveUrl 中的 slug 与已发布列表中的任何文章都不匹配
  • 文章已被平台手动删除

解决方案:

  • 手动在 DEV.to 网站上将文章设为草稿

API 限流

症状: 返回 "devto 429: ..."

解决方案:

  • DEV.to API 有请求频率限制
  • 等待一段时间后重试
  • 避免短时间内发布大量文章

与其他适配器的对比

特性DEV.toHashnodeGitHub DiscussionsRedditBluesky
自动化等级AutoAutoAutoAuto-gatedAuto
API 类型RESTGraphQLGraphQLOAuthAT Protocol
正文上限100,00050,00065,00040,000300
Markdown 支持完整完整完整受限仅链接
标签限制45无限制多个
canonical URL
系列文章✅ (extras.series)✅ (extras.series)
下线支持设为草稿

路线图与社区计划

根据社区路线图,DEV.to 适配器目前处于稳定状态,暂无已计划的改进项。

社区关注的其他平台改进方向:

  • [ ] Twitter/X 原生 API 支持(目前使用浏览器回退)
  • [ ] LinkedIn 原生 API 支持(目前使用浏览器回退)
  • [ ] Medium 原生 API 支持(目前使用浏览器回退)

资料来源:社区路线图

相关页面

资料来源:src/adapters/index.ts:8-11

Hashnode 适配器

Hashnode 适配器是 content-distribution-mcp 项目中用于与 Hashnode 博客平台进行集成的核心组件。它负责将内容变体(Variant)发布到 Hashnode 出版物(Publication),并提供平台特定的功能支持,包括标签规范化、系列文章关联和规范化 URL 设置。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 适配器角色

继续阅读本节完整说明和来源证据。

章节 接口实现

继续阅读本节完整说明和来源证据。

章节 静态元数据详解

继续阅读本节完整说明和来源证据。

概述

Hashnode 适配器是 content-distribution-mcp 项目中用于与 Hashnode 博客平台进行集成的核心组件。它负责将内容变体(Variant)发布到 Hashnode 出版物(Publication),并提供平台特定的功能支持,包括标签规范化、系列文章关联和规范化 URL 设置。

Hashnode 是 content-distribution-mcp 支持的 8+ 渠道之一,属于自动发布渠道类别,即该渠道拥有公开 API,无需浏览器回退即可完成发布操作。资料来源:README.md

架构设计

适配器角色

Hashnode 适配器实现了 ChannelAdapter 接口,该接口定义了所有渠道适配器必须实现的标准方法。适配器作为 MCP 服务器与 Hashnode GraphQL API 之间的桥梁,负责:

  1. 转换:将通用的内容模型转换为 Hashnode API 所需的 GraphQL 变异格式
  2. 验证:确保必填凭证存在且有效
  3. 发布:通过 GraphQL 变异将内容发布到 Hashnode 出版物
  4. 反解析:提供渠道特定的元数据(ChannelHints)
graph TD
    A[MCP 服务器] -->|post.publish| B[Hashnode 适配器]
    B --> C{Hashnode GraphQL API}
    C -->|成功| D[返回 live_url]
    C -->|失败| E[返回 state: failed + error]
    
    F[Profile 凭证] -->|HASHNODE_TOKEN<br>HASHNODE_PUBLICATION_ID| B
    
    G[channel.hints] -->|静态元数据| H[调用方 Agent]

接口实现

Hashnode 适配器实现了 ChannelAdapter 接口定义的三个核心方法:

方法作用是否涉及网络请求
hints()返回平台静态元数据否(纯函数)
publish(variant, profile)发布内容到 Hashnode
unpublish(liveUrl, profile)取消发布文章

资料来源:src/adapters/index.ts:5-12

渠道元数据(ChannelHints)

静态元数据详解

hints() 方法返回的 ChannelHints 对象描述了 Hashnode 平台的约束和功能支持情况:

属性说明
max_length50,000正文字符最大长度限制
supported_md_featuresbold, italic, code_inline, code_block, links, headers, images, lists, tables, blockquote支持的 Markdown 功能
tag_vocabai, automation, mcp, claude, devops, tutorial, productivity, llm推荐标签词汇表
cta_placementbottom行动号召块(CTA)放置位置
canonical_url_supportedtrue是否支持规范化 URL
browser_onlyfalse是否需要浏览器回退(否)

资料来源:src/adapters/hashnode.ts:8-17

与其他渠道的对比

graph LR
    A[Hashnode] -->|max_length| B[50,000]
    A -->|Markdown| C[完整支持]
    A -->|canonical| D[✓ 支持]
    
    E[Twitter] -->|max_length| F[280]
    E -->|Markdown| G[仅 links]
    E -->|canonical| H[✗ 不支持]
    
    I[LinkedIn] -->|max_length| J[3,000]
    I -->|Markdown| K[bold, italic, links]
    I -->|canonical| L[✗ 不支持]

发布流程

发布逻辑详解

publish 方法遵循以下执行流程:

flowchart TD
    A[publish 调用] --> B{凭证检查}
    B -->|HASHNODE_TOKEN 缺失| C[返回 failed]
    B -->|HASHNODE_PUBLICATION_ID 缺失| C
    B -->|凭证完整| D[构建 GraphQL 变异]
    
    D --> E[发送 POST 请求到 gql.hashnode.com]
    E --> F{HTTP 响应}
    
    F -->|ok| G[解析 response.data]
    F -->|!ok| H[返回 failed + 状态码]
    
    G --> I{url 存在?}
    I -->|是| J[返回 live, live_url, published_at]
    I -->|否| K[返回 failed + errors]

GraphQL 变异构造

Hashnode 适配器使用 GraphQL mutation 将内容发布到 Hashnode API。核心变异语句结构如下:

mutation PublishPost($input: PublishPostInput!) {
  publishPost(input: $input) { 
    post { url } 
  }
}

请求体构建

适配器根据 Variant 数据构建 GraphQL 变量对象:

Variant 字段映射到 Hashnode API处理逻辑
variant.titleinput.title直接映射
variant.bodyinput.contentMarkdown直接映射
variant.tagsinput.tags截取前 5 个,转换为 {slug, name} 格式
variant.canonical_urlinput.canonicalUrl仅当存在时添加
variant.extras.seriesinput.seriesId仅当 extras.series 存在时添加

资料来源:src/adapters/hashnode.ts:19-43

标签转换逻辑

Hashnode 的标签格式要求与通用格式不同,需要进行转换:

tags: variant.tags.slice(0, 5).map(t => ({
  slug: t.toLowerCase().replace(/\s+/g, "-"),  // 转小写,空格替换为连字符
  name: t                                        // 保留原始名称
}))

发布结果

成功发布后,适配器返回以下结构:

{
  channel: variant.channel,           // 渠道标识,如 "hashnode:main"
  state: "live",                      // 状态:已上线
  live_url: json.data.publishPost.post.url,  // Hashnode 文章 URL
  published_at: new Date().toISOString()      // UTC 时间戳
}

反发布支持

Hashnode 适配器实现了 unpublish 方法,但当前版本存在限制:

async unpublish(liveUrl: string, profile: Profile): Promise<[boolean, string]> {
  return [false, "Hashnode unpublish not yet implemented — use the Hashnode UI or API directly"];
}

当前行为:返回 [false, "Hashnode unpublish not yet implemented — use the Hashnode UI or API directly"]

这意味着:

  • 无法通过 MCP 服务器直接取消发布 Hashnode 文章
  • 用户需要手动在 Hashnode 后台进行操作
  • 这是一个已知的功能缺失,社区 roadmap 中未列出此功能

资料来源:src/adapters/hashnode.ts:57-59

凭证配置

所需环境变量

Hashnode 适配器需要在分发配置文件(~/.distribution-mcp/profiles.yaml)中配置两个凭证:

凭证名称说明获取方式
HASHNODE_TOKEN个人访问令牌Hashnode Settings → Access Tokens → Generate new access token
HASHNODE_PUBLICATION_ID出版物 IDHashnode 出版物设置页面的 URL 中获取

配置示例

# ~/.distribution-mcp/profiles.yaml
default:
  credentials:
    HASHNODE_TOKEN: "your-hashnode-token"
    HASHNODE_PUBLICATION_ID: "your-publication-id"
  subreddits: []

凭证验证失败处理

当凭证缺失时,适配器返回:

{
  channel: variant.channel,
  state: "failed",
  error: "HASHNODE_TOKEN or HASHNODE_PUBLICATION_ID not set in profile"
}

适配器注册与路由

适配器映射表

Hashnode 适配器通过 buildAdapterMap() 函数注册到主适配器映射中:

return {
  devto: new DevToAdapter(),
  hashnode: new HashnodeAdapter(),     // ← Hashnode 主适配器
  github_discussions: new GitHubDiscussionsAdapter(),
  reddit: new RedditAdapter(),
  bluesky: new BlueskyAdapter(),
  // ... 其他渠道
};

注意:Hashnode 适配器仅在 hashnode 键下注册,不提供 hashnode-browser 等变体,因为该渠道原生支持 API。

资料来源:src/adapters/index.ts:14-26

平台路由解析

在 MCP 服务器中,渠道标识符通过冒号分隔的平台前缀进行解析:

const platform = channel.split(":")[0];  // "hashnode:main" → "hashnode"
const adapter = adapters[platform];       // 获取 HashnodeAdapter 实例

这意味着用户可以使用 hashnodehashnode:your-account 格式来指定 Hashnode 渠道。

错误处理

常见错误场景

场景返回结果错误消息示例
HASHNODE_TOKEN 未设置state: "failed""HASHNODE_TOKEN or HASHNODE_PUBLICATION_ID not set in profile"
HASHNODE_PUBLICATION_ID 未设置state: "failed""HASHNODE_TOKEN or HASHNODE_PUBLICATION_ID not set in profile"
GraphQL 请求失败state: "failed" + HTTP 状态码"hashnode {status}"
GraphQL 返回错误state: "failed" + errorsJSON.stringify(result.errors ?? "no URL returned")

API 限流

Hashnode GraphQL API 可能有速率限制。当遇到限流时,适配器会返回非 200 状态码,触发错误处理流程。

与其他适配器的比较

特性HashnodeDEV.toGitHub DiscussionsBluesky
API 方式GraphQLRESTGraphQLAT Protocol
正文长度限制50,000100,000依 GitHub300
Markdown 支持完整完整基础仅链接
规范化 URL
系列文章✓ (via extras.series)✓ (via extras.series)
反发布支持✗ (未实现)
浏览器回退

使用示例

通过 MCP 工具发布

// post.publish 工具调用
{
  "content": {
    "id": "my-article-2026-05-20",
    "title": "Building with MCP",
    "body_md": "# Introduction\n\nThis article...",
    "tags": ["automation", "mcp", "tutorial"],
    "canonical_url": "https://myblog.com/mcp-guide",
    "author": "Your Name"
  },
  "variants": [
    {
      "channel": "hashnode:main",
      "title": "Building with MCP: A Complete Guide",
      "body": "# Introduction\n\nThis article...",
      "tags": ["automation", "mcp", "tutorial", "productivity"],
      "canonical_url": "https://myblog.com/mcp-guide",
      "extras": {
        "series": "mcp-tutorials"  // 可选:关联到 Hashnode 系列
      }
    }
  ],
  "profile_name": "default"
}

获取 Hashnode 渠道提示

// channel.hints 工具调用
{
  "channel": "hashnode"
}

// 返回
{
  "max_length": 50000,
  "supported_md_features": ["bold", "italic", "code_inline", "code_block", "links", "headers", "images", "lists", "tables", "blockquote"],
  "tag_vocab": ["ai", "automation", "mcp", "claude", "devops", "tutorial", "productivity", "llm"],
  "cta_placement": "bottom",
  "canonical_url_supported": true,
  "browser_only": false
}

限制与已知问题

v2.2.x 版本的限制

  1. 反发布功能未实现:当前无法通过 MCP 服务器取消发布 Hashnode 文章
  2. series 参数仅接受字符串:无法直接通过 API 创建或查询系列 ID,需手动在 Hashnode 后台获取系列 slug
  3. 标签数量限制:最多发送 5 个标签,超出部分被截断
  4. 封面图片支持:适配器未实现封面图片上传功能

社区反馈

根据社区 roadmap,Hashnode 适配器的原生 API 支持已完全实现,无需浏览器回退。未来的改进方向可能包括:

  • 封面图片上传支持
  • 系列文章自动创建
  • 增强的反发布功能

参见

资料来源:src/adapters/index.ts:5-12

Reddit 适配器

Reddit 适配器是 content-distribution-mcp 项目中负责将内容发布到 Reddit 平台的组件。它被归类为 Auto-gated(自动门控) 频道,意味着该适配器需要 Reddit OAuth 凭证才能正常工作,但在内容发布时会受到 Reddit 社区的反垃圾规则的限制。

章节 相关页面

继续阅读本节完整说明和来源证据。

概述

Reddit 适配器是 content-distribution-mcp 项目中负责将内容发布到 Reddit 平台的组件。它被归类为 Auto-gated(自动门控) 频道,意味着该适配器需要 Reddit OAuth 凭证才能正常工作,但在内容发布时会受到 Reddit 社区的反垃圾规则的限制。

Reddit 适配器的核心职责包括:

  • 通过 Reddit API 进行身份认证
  • 发布内容到指定的子版块(subreddit)
  • 管理每帖子状态的幂等性
  • 强制执行子版块级别的冷却期(cooldown)规则
  • 处理帖子置顶和精选功能(可选)

资料来源:src/adapters/index.ts:14

资料来源:src/adapters/index.ts:14

Bluesky 适配器

Bluesky 适配器是 content-distribution-mcp 项目中负责将内容发布到 Bluesky 社交平台的组件。Bluesky 是一个基于 AT Protocol(ATP)的去中心化社交网络,适配器通过调用 Bluesky 的官方 API 实现原生发布功能。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 平台元数据 (Channel Hints)

继续阅读本节完整说明和来源证据。

章节 发布流程

继续阅读本节完整说明和来源证据。

章节 取消发布

继续阅读本节完整说明和来源证据。

概述

Bluesky 适配器是一个 原生 API 集成 组件,不依赖浏览器模拟器即可完成内容发布。该适配器实现了 ChannelAdapter 接口,提供平台元数据查询、发布和取消发布功能。

属性
平台标识符bluesky
API 类型原生 REST API (AT Protocol)
浏览器回退不需要
发布方式AT Protocol com.atproto.servercom.atproto.repo
资料来源src/adapters/bluesky.ts:1-61

核心功能

平台元数据 (Channel Hints)

适配器通过 hints() 方法返回 Bluesky 平台的约束信息:

参数说明
max_length300最大字符限制
supported_md_features["links"]仅支持链接格式
cta_placementbottomCTA 块放置在底部
canonical_url_supportedfalse不支持规范 URL
browser_onlyfalse使用原生 API

资料来源:src/adapters/bluesky.ts:10-16

发布流程

sequenceDiagram
    participant MCP as MCP Server
    participant ATP as AT Protocol API
    participant Bluesky as Bluesky Server

    MCP->>MCP: 验证凭证 (BLUESKY_IDENTIFIER, BLUESKY_PASSWORD)
    MCP->>ATP: POST /com.atproto.server.createSession
    ATP-->>MCP: { accessJwt, did }
    MCP->>ATP: POST /com.atproto.repo.createRecord
    Note over MCP: 发送到 app.bsky.feed.post
    ATP-->>MCP: { uri }
    MCP->>MCP: 解析 uri 生成 webUrl
    MCP-->>User: { state: "live", live_url }

#### 认证流程

  1. 调用 com.atproto.server.createSession 端点
  2. 传入 identifier(用户名/邮箱)和 password
  3. 获取 accessJwt(访问令牌)和 did(去中心化标识符)

#### 发布流程

  1. 使用 accessJwt 作为 Bearer Token 调用 com.atproto.repo.createRecord
  2. collection 设置为 app.bsky.feed.post
  3. repo 设置为用户的 did
  4. record 包含 text(最多 300 字符)、$typecreatedAt

#### 返回值处理

响应中的 uri 格式为 at://did:plc:xxx/app.bsky.feed.post/rkey,适配器提取最后的 rkey 生成网页 URL:

https://bsky.app/profile/{did}/post/{rkey}

资料来源:src/adapters/bluesky.ts:17-53

取消发布

Bluesky 适配器的 unpublish 方法目前未实现

async unpublish(_liveUrl: string, _profile: Profile): Promise<[boolean, string]> {
  return [false, "Bluesky post deletion not yet implemented — delete via bsky.app"];
}
操作状态说明
发布✅ 已实现通过 AT Protocol API
取消发布❌ 未实现需手动在 bsky.app 删除

资料来源:src/adapters/bluesky.ts:55-58

适配器注册

在适配器索引文件中,Bluesky 适配器被注册到适配器映射表中:

return {
  devto: new DevToAdapter(),
  hashnode: new HashnodeAdapter(),
  github_discussions: new GitHubDiscussionsAdapter(),
  reddit: new RedditAdapter(),
  bluesky: new BlueskyAdapter(),  // 注册在此
  // ...
};

资料来源:src/adapters/index.ts:19

配置要求

环境变量

变量名必需说明
BLUESKY_IDENTIFIERBluesky 用户名或邮箱
BLUESKY_PASSWORDBluesky 密码或 App Password

Profile 配置

在 YAML 后端配置文件中:

profiles:
  default:
    channels:
      - bluesky:me
    credentials:
      BLUESKY_IDENTIFIER: "your-handle.bsky.social"
      BLUESKY_PASSWORD: "app-password-or-password"

资料来源:README.md

使用示例

发布请求

{
  "content": {
    "id": "bluesky-announce-001",
    "title": "新功能发布公告",
    "body_md": "我们很高兴宣布 MCP 内容分发服务器现已支持 Bluesky!",
    "author": "AutomateLab"
  },
  "variants": [
    {
      "channel": "bluesky",
      "title": "新功能发布公告",
      "body": "我们很高兴宣布 MCP 内容分发服务器现已支持 Bluesky!了解更多: https://automatelab.tech",
      "tags": ["mcp", "automation", "bluesky"]
    }
  ],
  "profile_name": "default"
}

成功响应

{
  "results": [
    {
      "channel": "bluesky",
      "state": "live",
      "live_url": "https://bsky.app/profile/did:plc:xxx/app.bsky.feed.post/xyz",
      "published_at": "2024-01-15T10:30:00.000Z"
    }
  ]
}

失败响应

{
  "results": [
    {
      "channel": "bluesky",
      "state": "failed",
      "error": "BLUESKY_IDENTIFIER and BLUESKY_PASSWORD required in profile"
    }
  ]
}

API 端点参考

端点方法用途
https://bsky.social/xrpc/com.atproto.server.createSessionPOST创建认证会话
https://bsky.social/xrpc/com.atproto.repo.createRecordPOST创建帖子记录

Create Session 请求体

{
  "identifier": "user.bsky.social",
  "password": "xxxx-xxxx-xxxx"
}

Create Record 请求体

{
  "repo": "did:plc:xxx",
  "collection": "app.bsky.feed.post",
  "record": {
    "$type": "app.bsky.feed.post",
    "text": "帖子内容(最多300字符)",
    "createdAt": "2024-01-15T10:30:00.000Z"
  }
}

资料来源:src/adapters/bluesky.ts:22-48

限制与已知问题

问题状态说明
取消发布❌ 未实现需手动在平台删除
图片支持⚠️ 限制当前版本仅支持文本
Markdown 格式⚠️ 限制仅支持链接
线程支持❌ 未实现暂不支持回复链

与其他平台的比较

平台API 类型字符限制Markdown 支持取消发布
Bluesky原生 API300仅链接❌ 未实现
DEV.to原生 API100,000完整✅ 已实现
Hashnode原生 GraphQL50,000完整✅ 已实现
Twitter浏览器回退280仅链接⚠️ 部分支持
LinkedIn浏览器回退3,000部分⚠️ 部分支持

资料来源:src/adapters/browser.tssrc/adapters/devto.ts

相关文件

文件说明
src/adapters/bluesky.tsBluesky 适配器实现
src/adapters/index.ts适配器注册表
src/server.tsMCP 服务器与工具定义
README.md项目整体文档

参见

资料来源:src/adapters/bluesky.ts:10-16

GitHub Discussions 适配器

GitHub Discussions 适配器是 content-distribution-mcp 项目中的核心组件之一,负责将内容分发到 GitHub 仓库的讨论区。该适配器通过 GitHub GraphQL API 与 GitHub Discussions 进行交互,支持创建新讨论、获取讨论分类信息等操作。

章节 相关页面

继续阅读本节完整说明和来源证据。

章节 支持的 Markdown 特性

继续阅读本节完整说明和来源证据。

章节 内容长度限制

继续阅读本节完整说明和来源证据。

章节 CTA 放置规则

继续阅读本节完整说明和来源证据。

概述

GitHub Discussions 适配器是 content-distribution-mcp 项目中的核心组件之一,负责将内容分发到 GitHub 仓库的讨论区。该适配器通过 GitHub GraphQL API 与 GitHub Discussions 进行交互,支持创建新讨论、获取讨论分类信息等操作。

作为内容分发 MCP 服务器的一部分,该适配器与其他平台适配器(如 DEV.to、Hashnode、Reddit、Bluesky 等)协同工作,为用户提供统一的多平台内容分发体验。适配器遵循通道适配器接口规范,提供只读元数据查询和发布操作两个主要功能。

功能特性

支持的 Markdown 特性

GitHub Discussions 适配器支持丰富的 Markdown 格式,这使得内容创作者可以充分利用 GitHub 的富文本渲染能力。根据源码实现,该适配器支持以下 Markdown 功能:

功能类型支持状态说明
粗体✅ 支持使用 text 语法
斜体✅ 支持使用 *text*_text_ 语法
行内代码✅ 支持使用 ` code ` 语法
代码块✅ 支持支持多行代码和语法高亮
链接✅ 支持自动转换为可点击链接
标题✅ 支持支持 H1-H6 标题层级
图片✅ 支持支持嵌入图片
列表✅ 支持支持有序和无序列表
表格✅ 支持支持 Markdown 表格
引用块✅ 支持使用 > 语法的引用

资料来源:src/adapters/github-discussions.ts:9-11

内容长度限制

该适配器对发布内容的长度有明确限制,最大支持 65,000 个字符的内容体,这为长篇技术文章、详细的问题解答和深入的教程提供了充足的空间。相比之下,Bluesky 适配器仅支持 300 字符,Twitter 适配器支持 280 字符,LinkedIn 支持 3,000 字符。

资料来源:src/adapters/github-discussions.ts:10

CTA 放置规则

适配器将行动号召(CTA)块放置在页脚位置(footer),这意味着 CTA 块会被追加到讨论正文的末尾。这与某些其他平台(如 DEV.to 和 Hashnode 将 CTA 放在底部,Reddit 放在顶部)有所不同。

资料来源:src/adapters/github-discussions.ts:12

Canonical URL 支持

与其他部分平台不同,GitHub Discussions 适配器不支持 canonical_url 功能。这是因为 GitHub Discussions 本身不提供设置规范链接的 API 选项。内容创作者在使用该适配器时应注意这一点。

资料来源:src/adapters/github-discussions.ts:13

架构设计

适配器注册机制

content-distribution-mcp 项目中,所有平台适配器通过统一的适配器注册表进行管理。GitHub Discussions 适配器在适配器映射表中有两个注册键:github_discussionsgithub-discussions(带连字符版本),这提供了更好的命名灵活性。

graph TD
    A[适配器索引 index.ts] --> B[buildAdapterMap 函数]
    B --> C[GitHubDiscussionsAdapter 实例]
    C --> D{channel 参数解析}
    D -->|github_discussions| C
    D -->|github-discussions| C
    
    style C fill:#90EE90

资料来源:src/adapters/index.ts:14-24

发布流程

GitHub Discussions 适配器的发布流程涉及多个步骤,包括身份验证、仓库查询、分类获取和讨论创建。以下是该流程的详细架构:

sequenceDiagram
    participant 客户端
    participant 适配器 as GitHubDiscussionsAdapter
    participant GitHub API as GitHub GraphQL API
    
    客户端->>适配器: publish(variant, profile)
    适配器->>适配器: 验证凭证和参数
    适配器->>GitHub API: GraphQL 查询仓库信息
    GitHub API-->>适配器: 返回仓库 ID 和讨论分类
    适配器->>适配器: 确定 categoryId
    适配器->>GitHub API: GraphQL mutation 创建讨论
    GitHub API-->>适配器: 返回讨论 URL
    适配器->>客户端: PublishResult { state: "live", live_url }

资料来源:src/adapters/github-discussions.ts:17-53

配置要求

必需凭证

GitHub Discussions 适配器需要以下凭证才能正常工作:

凭证名称必需说明
GITHUB_TOKEN必需GitHub 个人访问令牌(PAT),需要 reporead:discussion 权限
GITHUB_DISCUSSION_REPO必需目标仓库,格式为 owner/repo,例如 AutomateLab-tech/content-distribution-mcp
GITHUB_DISCUSSION_CATEGORY_ID可选讨论分类 ID,可通过 API 或仓库设置获取

资料来源:src/adapters/github-discussions.ts:22-23

凭证优先级

适配器在确定实际使用的参数值时采用以下优先级规则:

graph LR
    A[variant.extras] -->|优先级最高| B[实际使用值]
    C[profile.credentials] -->|优先级次之| B
    D[默认值] -->|最低优先级| B
    
    style A fill:#FFD700
    style C fill:#FFA500
    style D fill:#D3D3D3

对于 repo 参数:优先使用 variant.extras.repo,其次使用 profile.credentials["GITHUB_DISCUSSION_REPO"]

对于 categoryId 参数:优先使用 variant.extras.category_id,其次使用 profile.credentials["GITHUB_DISCUSSION_CATEGORY_ID"]

资料来源:src/adapters/github-discussions.ts:22-23

Profile 配置示例

~/.distribution-mcp/profiles.yaml 中的配置示例:

default:
  credentials:
    GITHUB_TOKEN: "ghp_your_personal_access_token_here"
    GITHUB_DISCUSSION_REPO: "your-org/your-repo"
    GITHUB_DISCUSSION_CATEGORY_ID: "DIC_kwDOHAX7vs4CXXXX"

Variant 参数

必需字段

字段名类型必需说明
channelstring必需通道标识,格式为 github_discussionsgithub_discussions:account
titlestring必需讨论标题,将作为 GitHub Discussion 的标题显示
bodystring必需讨论正文内容,支持 Markdown 格式

可选字段

字段名类型默认值说明
extrasobject{}扩展参数,包含 repocategory_id
canonical_urlstring-不支持,将被忽略
cta_blockstring-CTA 内容,追加到正文章节末尾(footer)
schedule_atstring-ISO-8601 格式的调度时间

资料来源:src/server.ts:30-40

extras 字段详解

extras 对象支持以下平台特定参数:

{
  "extras": {
    "repo": "owner/repo",
    "category_id": "DIC_kwDxxxxxxxxxxxxx"
  }
}
  • repo: 覆盖 profile 中配置的默认仓库
  • category_id: 指定讨论的分类 ID,如果不提供,适配器会尝试使用 profile 中的配置

发布结果

成功响应

成功发布后,适配器返回以下结构的结果:

字段类型说明
channelstring通道标识,与请求中的 channel 一致
stateenum发布状态,值为 "live" 表示成功
live_urlstringGitHub Discussion 的完整 URL
published_atstringUTC ISO-8601 格式的发布时间戳
draft_pathnull该适配器不使用,始终为 null
compose_urlnull该适配器不使用,始终为 null
errornull成功时为 null

资料来源:src/server.ts:52-60

失败响应

发布失败时,返回以下结构:

字段类型说明
channelstring通道标识
stateenum值为 "failed" 表示失败
live_urlnull失败时为 null
draft_pathnull失败时为 null
compose_urlnull失败时为 null
errorstring错误消息,描述失败原因
published_atnull失败时为 null

错误处理

常见错误类型

适配器实现了完善的错误处理机制,以下是可能遇到的错误情况:

错误场景错误消息解决方案
缺少 GITHUB_TOKEN"GITHUB_TOKEN and GITHUB_DISCUSSION_REPO required in profile"在 profile 中配置有效的 GitHub Token
缺少 GITHUB_DISCUSSION_REPO"GITHUB_TOKEN and GITHUB_DISCUSSION_REPO required in profile"在 profile 或 extras 中指定目标仓库
仓库不存在或无权限"state: 'failed' (GraphQL 返回空 data)确认仓库存在且 Token 有访问权限
分类 ID 无效"state: 'failed' (未找到对应分类)检查分类 ID 是否正确,或省略让适配器自动选择

资料来源:src/adapters/github-discussions.ts:26-35

取消发布限制

需要特别注意:GitHub Discussions 适配器尚未实现取消发布功能。调用 unpublish 方法将返回 [false, "GitHub Discussions delete not yet implemented — use the GitHub UI or API directly"]

资料来源:src/adapters/github-discussions.ts:56-57

与其他适配器的比较

GitHub Discussions 适配器在多个维度上与其他平台适配器存在差异:

特性GitHub DiscussionsDEV.toHashnodeRedditBlueskyMediumLinkedInTwitter
原生 API⚠️ 自动门控❌ 浏览器❌ 浏览器❌ 浏览器
最大长度65,000100,00050,00040,000300100,0003,000280
支持 Markdown✅ 完整✅ 完整✅ 完整❌ 纯文本❌ 仅链接✅ 完整❌ 有限❌ 仅链接
Canonical URL
CTA 放置footerbottombottomtopbottombottombottomnone
取消发布❌ 未实现
注:⚠️ 自动门控表示 Reddit 需要额外的自动审核配置

资料来源:src/adapters/github-discussions.ts:8-13src/adapters/devto.ts:8-13src/adapters/hashnode.ts:8-13src/adapters/bluesky.ts:8-12src/adapters/browser.ts:14-40

路线图与未来计划

根据社区路线图信息,GitHub Discussions 适配器目前没有明确的计划变更。然而,项目正在考虑以下改进方向:

  • 原生 API 支持改进:所有标记为"Browser fallback"的平台(Medium、LinkedIn、Twitter/X)都在路线图上,未来可能实现原生 API 支持
  • 取消发布功能:当前未实现的 unpublish 功能可能在未来版本中添加
  • 分类自动发现优化:改进当未指定分类 ID 时的默认分类选择逻辑

资料来源:社区路线图 - Roadmap & feature tracker

使用示例

基础发布调用

{
  "content": {
    "id": "mcp-intro-2026-05-20",
    "title": "Model Context Protocol 入门指南",
    "body_md": "# 什么是 MCP\n\nModel Context Protocol (MCP) 是一个...",
    "tags": ["mcp", "ai", "automation"],
    "author": "YourName"
  },
  "variants": [
    {
      "channel": "github_discussions",
      "title": "Model Context Protocol 入门指南",
      "body": "# 什么是 MCP\n\nModel Context Protocol (MCP) 是一个...",
      "extras": {
        "repo": "your-org/your-repo",
        "category_id": "DIC_kwDxxxxxxxxxxxxx"
      }
    }
  ],
  "profile_name": "default"
}

带 CTA 的发布

{
  "channel": "github_discussions",
  "title": "使用 MCP 自动化工作流程",
  "body": "# MCP 工作流自动化\n\n本文介绍如何...",
  "cta_block": "---\n\n💡 想了解更多信息?访问 [我们的网站](https://example.com)"
}

故障排除

诊断步骤

当 GitHub Discussions 发布失败时,请按以下步骤排查:

  1. 验证凭证:确认 GITHUB_TOKEN 具有 reporead:discussion 权限
  2. 检查仓库访问:确保 Token 可以访问指定的仓库
  3. 验证分类 ID:确认分类 ID 格式正确且分类存在
  4. 查看 GraphQL 错误:检查返回的 error 字段中的详细错误信息

调试技巧

启用调试模式查看详细的 GraphQL 请求和响应:

DEBUG=* npx -y content-distribution-mcp publish --content-id "your-id"

相关文档

参见

资料来源:src/adapters/github-discussions.ts:9-11

失败模式与踩坑日记

保留 Doramagic 在发现、验证和编译中沉淀的项目专属风险,不把社区讨论只当作装饰信息。

medium 能力判断依赖假设

假设不成立时,用户拿不到承诺的能力。

medium 维护活跃度未知

新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。

medium 下游验证发现风险项

下游已经要求复核,不能在页面中弱化。

medium 存在评分风险

风险会影响是否适合普通用户安装。

Pitfall Log / 踩坑日志

项目:automatelab-tech/content-distribution-mcp

摘要:发现 6 个潜在踩坑项,其中 0 个为 high/blocking;最高优先级:能力坑 - 能力判断依赖假设。

1. 能力坑 · 能力判断依赖假设

  • 严重度:medium
  • 证据强度:source_linked
  • 发现:README/documentation is current enough for a first validation pass.
  • 对用户的影响:假设不成立时,用户拿不到承诺的能力。
  • 建议检查:将假设转成下游验证清单。
  • 防护动作:假设必须转成验证项;没有验证结果前不能写成事实。
  • 证据:capability.assumptions | mcp_registry:io.github.AutomateLab-tech/content-distribution:2.2.1 | https://registry.modelcontextprotocol.io/v0.1/servers/io.github.AutomateLab-tech%2Fcontent-distribution/versions/2.2.1 | README/documentation is current enough for a first validation pass.

2. 维护坑 · 维护活跃度未知

  • 严重度:medium
  • 证据强度:source_linked
  • 发现:未记录 last_activity_observed。
  • 对用户的影响:新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
  • 建议检查:补 GitHub 最近 commit、release、issue/PR 响应信号。
  • 防护动作:维护活跃度未知时,推荐强度不能标为高信任。
  • 证据:evidence.maintainer_signals | mcp_registry:io.github.AutomateLab-tech/content-distribution:2.2.1 | https://registry.modelcontextprotocol.io/v0.1/servers/io.github.AutomateLab-tech%2Fcontent-distribution/versions/2.2.1 | last_activity_observed missing

3. 安全/权限坑 · 下游验证发现风险项

  • 严重度:medium
  • 证据强度:source_linked
  • 发现:no_demo
  • 对用户的影响:下游已经要求复核,不能在页面中弱化。
  • 建议检查:进入安全/权限治理复核队列。
  • 防护动作:下游风险存在时必须保持 review/recommendation 降级。
  • 证据:downstream_validation.risk_items | mcp_registry:io.github.AutomateLab-tech/content-distribution:2.2.1 | https://registry.modelcontextprotocol.io/v0.1/servers/io.github.AutomateLab-tech%2Fcontent-distribution/versions/2.2.1 | no_demo; severity=medium

4. 安全/权限坑 · 存在评分风险

  • 严重度:medium
  • 证据强度:source_linked
  • 发现:no_demo
  • 对用户的影响:风险会影响是否适合普通用户安装。
  • 建议检查:把风险写入边界卡,并确认是否需要人工复核。
  • 防护动作:评分风险必须进入边界卡,不能只作为内部分数。
  • 证据:risks.scoring_risks | mcp_registry:io.github.AutomateLab-tech/content-distribution:2.2.1 | https://registry.modelcontextprotocol.io/v0.1/servers/io.github.AutomateLab-tech%2Fcontent-distribution/versions/2.2.1 | no_demo; severity=medium

5. 维护坑 · issue/PR 响应质量未知

  • 严重度:low
  • 证据强度:source_linked
  • 发现:issue_or_pr_quality=unknown。
  • 对用户的影响:用户无法判断遇到问题后是否有人维护。
  • 建议检查:抽样最近 issue/PR,判断是否长期无人处理。
  • 防护动作:issue/PR 响应未知时,必须提示维护风险。
  • 证据:evidence.maintainer_signals | mcp_registry:io.github.AutomateLab-tech/content-distribution:2.2.1 | https://registry.modelcontextprotocol.io/v0.1/servers/io.github.AutomateLab-tech%2Fcontent-distribution/versions/2.2.1 | issue_or_pr_quality=unknown

6. 维护坑 · 发布节奏不明确

  • 严重度:low
  • 证据强度:source_linked
  • 发现:release_recency=unknown。
  • 对用户的影响:安装命令和文档可能落后于代码,用户踩坑概率升高。
  • 建议检查:确认最近 release/tag 和 README 安装命令是否一致。
  • 防护动作:发布节奏未知或过期时,安装说明必须标注可能漂移。
  • 证据:evidence.maintainer_signals | mcp_registry:io.github.AutomateLab-tech/content-distribution:2.2.1 | https://registry.modelcontextprotocol.io/v0.1/servers/io.github.AutomateLab-tech%2Fcontent-distribution/versions/2.2.1 | release_recency=unknown

来源:Doramagic 发现、验证与编译记录