取警卫
 ](https://pepy.tech/project/fetch-guard)   
一 主控程序 服务器和CLI工具,用于获取URL并返回干净、LLM就绪的markdown。专门构建的提取管道对HTML进行净化,提取结构化元数据,检测即时注入尝试,并处理破坏天真获取者的边缘情况:机器人块、付费墙、登录墙、非HTML内容类型和需要JavaScript渲染的页面。
核心问题很简单:LLM需要网络内容,但原始HTML很嘈杂,而且可能充满敌意。提取的页面可以包含隐藏文本、不可见的Unicode、屏幕外元素,以及嵌入内容本身的直接提示注入尝试。此管道在内容到达模型之前剥离所有这些内容。
三层专门处理注射防御:
- 提取前消毒 删除隐藏元素(
display:none,visibility:hidden,opacity:0,font-size:0,transform:scale(0),clip:rect(0,0,0,0),零高度溢出容器,以及前景和背景颜色匹配的元素),通过CSS类/ID规则隐藏的元素 `标签、屏幕外定位的内容,aria-hidden元素,和` 标签和26类非打印Unicode字符,包括bidi隔离符和Unicode标签。这发生在内容提取之前,因此trafilatura永远看不到攻击向量。 - 图案扫描 对提取的文本和元数据字段运行四阶段扫描。第一阶段应用50个编译的正则表达式模式,涵盖系统提示覆盖、忽略以前的指令、角色注入、虚假对话标签和隐藏的指令标记,语言包括英语、西班牙语、法语、德语、日语、简体中文和葡萄牙。第二阶段通过NFKC和易混淆的字符映射对文本进行规范化,然后重新扫描以捕获同音字旁路(用西里尔字母或数学Unicode字符代替拉丁字母等)。第三阶段查找base64、十六进制编码和URL百分比编码的块,对其进行解码,并针对高严重性模式进行扫描。第四阶段使用ROT13对完整文档进行解码,并对高严重性模式进行扫描。元数据字段(title、description、og:title等)独立扫描,与源字段命名空间匹配。
- 会话盐渍输出包装 每次调用都会生成一个随机的8个字符的十六进制盐,并将主体包裹起来 `` 标签。由于盐是不可预测的,注入的内容物不能欺骗包装物的边界。
一个工具
这是一个单工具MCP服务器。它暴露了一个工具-- fetch --它在一致的接口后面运行一个完整的提取管道。无需选择工具,无需布线,无需多步骤工作流程。一个URL输入,一个结构化结果输出,可通过参数配置。
快速开始
先决条件
- Python 3.10+
- 点
安装
pip install fetch-guard对于JavaScript渲染(可选):
pip install 'fetch-guard[js]' && playwright install chromium配置您的MCP客户端
将以下内容添加到MCP客户端配置中。适用于Claude Code、Claude Desktop、Cursor或任何兼容MCP的客户端。
通过uvx(推荐):
{
"mcpServers": {
"fetch-guard": {
"command": "uvx",
"args": ["fetch-guard"]
}
}
}通过pip安装:
{
"mcpServers": {
"fetch-guard": {
"command": "fetch-guard"
}
}
}来源:
{
"mcpServers": {
"fetch-guard": {
"command": "python",
"args": ["-m", "fetch_guard.server"]
}
}
}通过Docker:
{
"mcpServers": {
"fetch-guard": {
"command": "docker",
"args": ["run", "-i", "--rm", "sterlsnyc/fetch-guard"]
}
}
}注: Docker镜像不包括Playwright。JavaScript渲染(js: true)通过Docker运行时不可用。使用uvx或pip如果你需要JS渲染,请安装。
验证
让你的人工智能助手获取任何URL。如果它返回带有状态头、元数据和风险评估的结构化内容,则表示你已连接。
命令行界面
fetch-guard-cli [options]
# or: python -m fetch_guard.cli [options]| 标志 | 默认值 | 描述 |
|---|---|---|
--timeout N | 180 | 请求超时(秒) |
--max-words N | none | 提取正文内容的字数上限。同时禁用自动尺寸保护 |
--js | off | 使用Playwright渲染JS页面 |
--strict | 关闭 | 退出高风险注射代码2 |
--links MODE | domains | domains 对于唯一的外部域, full 适用于所有带有锚文本的URL |
--header KEY:VALUE | none | 自定义HTTP标头(可重复) |
工具参数
MCP fetch 工具接受以下参数:
| 参数 | 类型 | 默认值 | 说明 | |
|---|---|---|---|---|
url | string | 必填 | 要获取的URL | |
timeout | integer | 180 | 请求超时(秒)。确保工具始终返回——没有挂起的取件 | |
max_words | integer | none | 提取的正文内容的字数上限。还禁用自动大小保护——当您希望在不达到默认限制的情况下对截断进行显式控制时使用 | |
strict | boolean | false | 当检测到true和高风险注射时,响应标记为错误 | |
js | boolean | false | 对JavaScript渲染的页面使用Playwright(需要 fetch-guard[js]) | |
links | "domains" | "full" | "domains" | "domains" 对于唯一的外部域, "full" 适用于所有带有锚文本的URL |
auth_token | string | none | 承载令牌 Authorization 标题(例如。 "my-api-key").用于GitHub经过身份验证的API和其他需要身份验证的端点 | |
headers | 对象 | 无 | 已弃用。 使用 auth_token 相反。将在下一个版本中删除 |
克劳德代码技能
复制 resources/fetch-guard/ 到 .claude/skills/fetch-guard/ 在您的项目中,或使用独立命令文件 resources/fetch-guard.md 作为克劳德代码命令。
它的作用
该管道从URL到结构化输出运行一个13步序列:
/llms.txt飞行前。 检查域根目录/llms.txt在全额提取之前。如果请求的URL是域根,并且/llms.txt如果存在,该内容将完全取代正常的HTML管道。这尊重了LLM友好型网站摘要的新兴惯例。
- 去拿。 静态HTTP请求通过
requests,或Playwright驱动的浏览器渲染,如果--js已设置。两者之间没有自动回退:--js是明确的选择加入。
- 边缘检测。 对机器人程序块(Cloudflare挑战、403/429/503块签名、LinkedIn自定义999)、付费墙(订阅提示、高级覆盖)和登录墙(登录重定向、仅限成员模式)的响应进行分类。
- 自动重试。 Bot块在报告之前会触发一次完整的Chrome用户代理字符串重试。付费墙和登录墙会立即报告,无需重试。
- 内容类型路由。 非HTML响应有一条快速路径:JSON被呈现为一个围栏代码块,RSS/Atom提要被解析为结构化摘要,CSV成为一个markdown表(最多2000行),纯文本直接通过。二进制内容类型被拒绝。
- HTML净化。 剥离隐藏元素(包括扩展的CSS可见性技术、颜色匹配的文本和 `
标签)、屏幕外定位的内容,aria-hidden节点,` 标签和非打印Unicode。返回删除的所有内容的计数。
- 内容提取。 trafilatura将经过净化的HTML转换为带有链接保护的markdown。
- 元数据提取。 按优先级顺序从三个来源提取标题、作者、日期、描述、规范URL和图像:JSON-LD、Open Graph,然后是元标签。
- 链接提取。 两种模式:
domains返回唯一外部域的排序列表,full返回所有按域分组的外部URL,并带有锚文本。
- 注射扫描。 四阶段扫描:针对所有50种模式的原始文本(英语+6种其他语言),NFKC标准化文本用于字形旁路,解码和扫描base64/hex/URL百分比编码的有效载荷,以及ROT13全文档扫描。元数据字段是独立扫描的,与源字段命名空间匹配。每个匹配记录模式名称、严重性(高/中)和60个字符的上下文片段。
- 尺寸保护+截断。 默认情况下,超过2MB(预提取)或20KB(提取后)的内容会引发错误,并建议
max_words价值。设置--max-words禁用这两个限制并截断——当您想要对到达模型的内容进行显式控制时,请使用它。
- 盐包装。 身体被包裹在腌制的标签中,以进行深度防御。
- 输出格式。 CLI生成五个明文部分(状态头、正文、元数据、链接、注入详细信息)。MCP服务器返回具有相同数据的结构化JSON字典。
输出
命令行界面
五个部分,打印到stdout:
- 状态标题: URL、获取时间戳、风险标志(
OK或INJECTION WARNING)、消毒计数、边缘病例信息(如果检测到) - 主体: 干净的羽绒被包裹起来 `` 标签
- 元数据: 带有标题、作者、日期、描述、规范URL、图像的JSON块
- 外部链接: 域列表或按域划分的完整URL
- 注射细节: 每个匹配的模式名称、严重性和上下文片段(仅在检测到模式时显示)
MCP 服务器
返回一个结构化字典:
status, url, fetched_at, body, content_type, metadata, links, links_mode,
risk_level, injection_matches, edge_cases, sanitization,
llms_txt_available, llms_txt_replaced, js_rendered, js_hint,
retried, truncated_atstatus 是一个快速浏览的摘要字符串,旨在在不展开完整结果的情况下可读:
"OK | html"
"HIGH | html | edge:paywall | sanitized:193 | retried | truncated:500"始终包括 risk 和 content_type.非默认值(edge, sanitized > 0, retried, js, truncated)仅在存在时附加。
当 --strict 已设置,风险等级为 HIGH,CLI以代码2退出,MCP服务器发出错误响应。在这两种情况下,完整的结果仍然可用。
退出代码
| 代码 | 含义 |
|---|---|
| 0 | 成功 |
| 1 | 提取错误(网络故障、空响应、二进制内容) |
| 2 | 检测到高风险注射(--strict 仅) |
建筑
fetch_guard/
├── pipeline.py # Core orchestration — 13-step sequence, shared by CLI and server
├── cli.py # CLI entry point — arg parsing, pipeline call, output
├── server.py # MCP server — FastMCP wrapper over the same pipeline
│
├── http/ # HTTP fetching layer
│ ├── client.py # Static HTTP fetch via requests
│ ├── playwright.py # JS rendering via Playwright (optional)
│ └── llms_txt.py # /llms.txt preflight check
│
├── extraction/ # Content extraction and edge detection
│ ├── content.py # trafilatura wrapper — HTML to markdown
│ ├── content_type.py # Non-HTML routing — JSON, XML/RSS, CSV, plain text
│ ├── edges.py # Bot block, paywall, login wall classification
│ ├── links.py # External link extraction (domain list or full URLs)
│ └── metadata.py # JSON-LD, Open Graph, meta tag extraction
│
├── security/ # Injection defense
│ ├── guard.py # Salt generation, content wrapping, four-phase scan, metadata scan, merge API
│ ├── normalize.py # NFKC + confusable-character normalization for homoglyph detection
│ ├── patterns.py # 14 English + 36 multilingual compiled regex patterns — single source of truth
│ ├── multilingual_patterns.json # Multilingual injection patterns (ES, FR, DE, JA, ZH, PT)
│ └── sanitizer.py # Hidden element, CSS rule, color-match, and non-printing character removal
│
└── output/ # Formatting
└── formatter.py # CLI output assembly每个模块都是一个单一的责任单元,其接口为公共功能。 pipeline.py 是共享的核心:两者 cli.py 和 server.py 呼叫 pipeline.run() 并以自己的方式处理结果。
测试
测试套件有两层。
单元测试 (424,全部被嘲笑——没有网络通话):
pytest每个模块都有相应的测试文件。CI在每次推送和PR时都会在Python 3.10、3.12和3.13上运行完整的套件。
实时集成测试 (54个条目,真实网络):
pytest -m live -v实时套件不是硬编码的测试函数,而是数据驱动的: tests/catalogs/ 使用类型化断言定义URL条目。单个参数化转轮(test_catalog.py)评估所有这些。
| 目录 | 条目 | 内容 |
|---|---|---|
html.yaml | 13 | 元数据丰富的页面、非英语内容、重定向、政府/学术网站 |
injection.yaml | 9 | OWASP备忘单、arXiv论文、受控高严重性有效载荷要点 |
edge_cases.yaml | 10 | 登录墙(GitHub、Reddit、Steam)、机器人块(LinkedIn、Glassdoor、WSJ) |
content_types.yaml | 12 | RSS/Atom提要、GitHub API JSON、原始文本文件、XML网站地图 |
llms_txt.yaml | 11 | 域名 /llms.txt (替换与可用),确认阴性 |
实时测试在发布时通过单独的 live-tests.yml 工作流程。
发展
# Run tests (414 unit tests, all mocked — no network calls)
pytest
# Run live integration tests (hits real URLs)
pytest -m live
# Lint
ruff check fetch_guard/ tests/CI通过推送和公关来运行 main 通过GitHub Actions,对Python 3.10、3.12和3.13进行测试。
致谢
与开发 克劳德代码.
