# Content Extract MCP Service 设计草案 ## 1. 文档目的 本文档用于定义当前仓库中已经落地的 MCP 服务设计,即“内容提取 MCP”。 目标是明确: - 这个 MCP 服务当前真正负责什么 - 它对外暴露哪些 tool - 每个 tool 的输入输出结构是什么 - validator 与 LLM 摘要如何接在 MCP 之后 - 当前 MVP 已完成到哪一层 这份文档描述的是当前真实实现,而不是早期“摘要 MCP”设想。 --- ## 2. 服务定位 这个 MCP 服务的角色是:为上层 Agent、OpenClaw 或其他自动化流程提供统一的“文章内容提取”能力。 它在整条链路中的位置是: `RSS 聚合 -> Content Extract MCP -> LLM 摘要 -> 校验 -> 规则过滤 -> 入库 -> 推送` 它不是完整阅读流系统,也不是摘要服务本身,而是阅读流中的“结构化正文提取层”。 ### 2.1 服务负责的事情 - 接收 URL 或标准化 `item` - 获取正文或补全正文 - 抽取文章标题 - 抽取正文纯文本 - 返回结构化文章对象 - 返回质量标记和结构化错误 ### 2.2 服务不负责的事情 - 管理 RSS 订阅源 - 生成摘要 - 主题分类 - 价值判断 - 规则过滤 - 写入知识库 - 发送通知 - 执行全流程编排 结论: 当前 MCP 服务边界应保持干净,只负责把网页或 item 转成结构化文章数据。 --- ## 3. 总体架构 建议并且当前实现采用的是: ```text MCP Server Layer -> Tool Handlers -> extraction core -> normalizer -> content_loader -> extractor -> quality_checker -> mapper ``` ### 3.1 外层 MCP 层职责 - 注册 tools - 接收和校验 tool 输入 - 调用内部 extraction core - 将结果包装成 MCP tool 输出 ### 3.2 内层 extraction core 职责 - 处理正文获取、标题提取、正文抽取、质量检查、结果映射 - 与具体 MCP SDK 解耦 ### 3.3 为什么必须分层 如果把内容提取逻辑直接写死在 MCP handler 里,后续会出现这些问题: - 业务逻辑难测试 - 协议层与抽取逻辑耦合 - 以后想补 CLI、批处理或其他入口时需要重复实现 因此当前原则是: - MCP 是外壳 - extraction core 是内核 --- ## 4. 当前暴露的 Tools 当前实现只暴露两个 tool: - `extract_url_content` - `extract_item_content` ### 4.1 `extract_url_content` 角色: - 直接输入 URL - 适合单条文章测试 - 适合手动调试或上层 Agent 直接调用 建议输入: ```json { "url": "https://example.com/post/1", "language_hint": "zh" } ``` 建议输出: ```json { "success": true, "article": { "extract_id": "sha256:yyy", "item_id": null, "source_id": null, "url": "https://example.com/post/1", "title": "文章标题", "language": "zh", "content_kind": "article", "plain_text": "抽取后的正文", "quality_flags": { "is_paywalled": false, "is_truncated": false, "is_low_content": false }, "metadata": { "content_source": "fetched_html", "extractor": "trafilatura", "char_count": 1234 }, "pipeline_state": "extracted" }, "warnings": [], "debug": { "content_source": "fetched_html", "extractor": "trafilatura" } } ``` ### 4.2 `extract_item_content` 角色: - 输入标准化 `item` - 与上游 RSS 聚合层对接 - 保留 `item_id`、`source_id` 等追踪信息 建议输入: ```json { "item": { "item_id": "sha256:xxx", "source_id": "my-blog", "title": "文章标题", "url": "https://example.com/post/1", "published_at": "2026-03-23T08:00:00Z", "raw_summary": "RSS 摘要", "raw_content": "RSS 正文片段", "content_kind": "article", "language": "zh" } } ``` 建议输出: ```json { "success": true, "article": { "extract_id": "sha256:yyy", "item_id": "sha256:xxx", "source_id": "my-blog", "url": "https://example.com/post/1", "title": "文章标题", "published_at": "2026-03-23T08:00:00Z", "language": "zh", "content_kind": "article", "plain_text": "抽取后的正文", "quality_flags": { "is_paywalled": false, "is_truncated": false, "is_low_content": false }, "metadata": { "content_source": "item.raw_content", "extractor": "inline", "char_count": 1234 }, "pipeline_state": "extracted" }, "warnings": [], "debug": { "content_source": "item.raw_content", "extractor": "inline" } } ``` --- ## 5. 统一输出规范 当前两个 tool 都复用同一个输出结构,即 `ExtractionOutput`。 成功时: - `success = true` - 返回 `article` - 可附带 `warnings` - 可附带 `debug` 失败时: - `success = false` - 返回结构化 `error` - 不返回不完整的 `article` 这样做的好处是: - 上层流程只需要消费一个稳定 schema - 后续 LLM 摘要和 validator 可以独立接上 - OpenClaw 或其他 Agent 更容易编排 --- ## 6. 错误返回规范 建议错误结构如下: ```json { "success": false, "error": { "code": "CONTENT_FETCH_FAILED", "message": "Failed to fetch article content", "retryable": true, "stage": "fetch", "details": { "url": "https://example.com/post/1" } }, "warnings": [] } ``` 当前错误码包括: - `INVALID_INPUT` - `CONTENT_FETCH_FAILED` - `CONTENT_EXTRACTION_FAILED` - `CONTENT_TOO_SHORT` - `UNKNOWN_ERROR` 使用原则: - 抓取失败不等于程序崩溃 - 可以重试的错误应标记 `retryable = true` - 错误应指出具体阶段 --- ## 7. 当前技术选型 当前 MVP 使用: - Python - MCP Python SDK (`FastMCP`) - `httpx` 进行网页抓取 - `trafilatura` 进行正文抽取 - `beautifulsoup4` 作为 HTML 兜底解析 - `pydantic` 进行输入输出约束 设计原则是: - 外层 MCP - 内层 extraction core - LLM 摘要和校验作为 MCP 之后的独立环节 --- ## 8. 当前目录结构建议 当前实现大致如下: ```text summary_mcp/ server.py core/ normalizer.py content_loader.py extractor.py quality_checker.py mapper.py pipeline.py models/ item.py document.py summary_io.py llm_result.py validators/ llm_result.py ``` 说明: - `server.py`:MCP 服务入口 - `core/`:提取内核 - `models/`:提取和校验相关模型 - `validators/`:LLM 输出校验逻辑 --- ## 9. 与 LLM 摘要层的关系 当前 MCP 不负责摘要,但它与摘要层的接口已经明确: 1. MCP 产出结构化 `article` 2. 上层 LLM 根据 `article.title`、`article.url`、`article.plain_text` 等生成摘要 JSON 3. validator 对摘要 JSON 做 schema 校验与业务校验 4. 若不合格,进入修复重试 这条闭环当前已经通过本地脚本验证: - `scripts/run_summary_loop.py` 因此,当前正确的职责拆分是: - MCP 负责提取 - LLM 负责摘要 - validator 负责验收 --- ## 10. 当前阶段验收结论 当前 MVP 已完成以下验证: - 真实 URL 可以提取为结构化文章 JSON - 提取结果可以保存为文件 - 提取结果可以喂给 LLM 生成摘要 JSON - 摘要 JSON 可以通过 validator 校验 - 整条“提取 -> 摘要 -> 校验”最小闭环已经跑通 --- ## 11. 后续扩展方向 后续建议按下面顺序推进: - 接入真实 RSS 聚合结果并映射为 `item` - 定义规则过滤层 schema - 设计知识库入库格式 - 增加批量处理能力 - 引入 Playwright 作为动态页面兜底方案 - 重命名包和项目名,使其与当前职责一致 --- ## 12. 当前阶段一句话结论 当前仓库中的 MCP 已经从早期“摘要 MCP”演进为“内容提取 MCP”,它的职责是稳定产出结构化文章 JSON,并把摘要与验收环节留给后续的 LLM 和 validator 流程。