mcpField
W
web-fetcher

web-fetcher

本地服务
github.com/vojtisprime11浏览器自动化013 周前更新

关于 web-fetcher

web-fetcher 是只读 MCP 服务,可将网页转换为 Markdown、结构化元数据和分类链接,供大模型工作流使用。抓取页面时支持分页,并提供 SSRF 防护。

主要功能

  1. 01将网页内容转换为简洁 Markdown
  2. 02分页读取长页面
  3. 03提取结构化元数据和标题
  4. 04分类并筛选链接
  5. 05阻止访问内网和回环地址

适用场景

  1. 01排除页面杂项以阅读文章
  2. 02不抓取正文而检查页面元数据
  3. 03收集站内或站外链接
  4. 04在响应长度限制后继续读取页面

MCP 相关

通过 stdio 提供三个只读 MCP 工具:获取页面 Markdown、提取元数据和提取链接。每个工具接受绝对 HTTP 或 HTTPS URL,并返回结构化结果。

配置

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

mcp.json · 通用配置
{
"mcpServers": {
"vojtisprime11-web-fetcher": {
"command": "npx",
"args": [
"-y",
"mcp-server-web-fetcher"
],
"env": {
"WEB_FETCHER_TIMEOUT_MS": "<WEB_FETCHER_TIMEOUT_MS>",
"WEB_FETCHER_MAX_BYTES": "<WEB_FETCHER_MAX_BYTES>",
"WEB_FETCHER_USER_AGENT": "<WEB_FETCHER_USER_AGENT>",
"WEB_FETCHER_ALLOW_PRIVATE_HOSTS": "<WEB_FETCHER_ALLOW_PRIVATE_HOSTS>",
"WEB_FETCHER_RESPECT_ROBOTS": "<WEB_FETCHER_RESPECT_ROBOTS>"
}
}
}
}
启动命令npx -y mcp-server-web-fetcher
需要配置的环境变量
WEB_FETCHER_TIMEOUT_MSRequest timeout in milliseconds (1000-120000). Defaults to 15000.
WEB_FETCHER_MAX_BYTESMaximum bytes downloaded per response. Defaults to 5000000.
WEB_FETCHER_USER_AGENTUser-Agent header sent with every request.
WEB_FETCHER_ALLOW_PRIVATE_HOSTSSet to true to allow fetching loopback and private network addresses. Disables the SSRF guard.
WEB_FETCHER_RESPECT_ROBOTSSet to true to enforce robots.txt before fetching.

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

安装包
npmmcp-server-web-fetcher0.1.0

工具

3

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

参数类型必填说明
urlstring是要获取页面的绝对 http(s) URL。
maxLengthinteger否单次调用返回的 Markdown 字符数上限。
timeoutMsinteger否请求超时时间,单位为毫秒(1000-120000)。
startIndexinteger否起始字符偏移量。使用 `nextStartIndex` 翻页获取较长页面。
includeLinksboolean否保留 Markdown 链接。为 false 时,链接文本将以内联纯文本形式呈现。
includeImagesboolean否将图片保留为 Markdown 图片语法。
includeMetadataboolean否包含简短的元数据摘要(标题、描述、规范链接、语言)。
mainContentOnlyboolean否移除导航、页眉、页脚和侧边栏,仅保留内容最密集的区块。

详细介绍

mcp-server-web-fetcher

CI npm version License: MIT TypeScript Node.js MCP MCP Registry

一个快速、依赖精简的 Model Context Protocol 服务,可将任意网页转换为大模型真正可读的内容:整洁的 Markdown、结构化元数据和分类后的链接列表。

网页有 90% 都是无关内容。脚本、Cookie 横幅、导航栏、侧边栏和跟踪像素会占用上下文并干扰推理。这个服务会在获取网页时剔除这些内容,让模型只看到文章本身。

┌────────────┐   stdio/JSON-RPC   ┌──────────────────────┐   HTTPS   ┌──────────┐
│ MCP client │ ─────────────────► │ mcp-server-web-      │ ────────► │ web page │
│ (Claude,   │ ◄───────────────── │ fetcher              │ ◄──────── │          │
│  Kiro, …)  │  Markdown + JSON   │ fetch → clean → md   │           └──────────┘
└────────────┘                    └──────────────────────┘

功能特性

  • 整洁的 Markdown 输出。 采用类似 Readability 的正文检测、GFM 表格、带语言标记的代码围栏和绝对链接。没有残留 HTML,复杂的嵌套表格也不例外。
  • 感知上下文窗口。 长网页会通过 startIndex / nextStartIndex 分页,而不是被悄悄截断。
  • 结构化元数据。 标题、描述、canonical、lang、作者、发布日期和修改日期、Open Graph、Twitter 卡片、JSON-LD、hreflang 替代版本、RSS/Atom 订阅源、h1–h6 大纲和原始 HTTP 标头。
  • 链接智能处理。 绝对 URL、锚文本、rel、nofollow、内部与外部分类、范围筛选和去重。
  • SSRF 安全加固。 每次重定向都会拦截环回、私有、链路本地和云元数据地址,而不仅仅是首次请求。URL 中的凭据会被剥除。
  • 可预测的失败处理。 每个错误都包含稳定的机器可读 code(TIMEOUT、HTTP_ERROR、BLOCKED_HOST、RESPONSE_TOO_LARGE 等)、是否可重试标记和恢复提示,让模型能够自行纠正,而不是猜测。
  • 端到端类型安全。 严格的 Zod 输入架构和已发布的 outputSchema,让客户端获得经过验证的 structuredContent,而无需重新解析文字内容。
  • 运行可靠。 字节数上限、超时、重定向次数限制、字符集检测(包括旧式编码)、小型 TTL 缓存、瞬时故障重试和可选的 robots.txt 强制执行。
  • 115 项测试,无需网络。 Vitest 单元测试和内存中的 MCP 协议测试。

快速开始

需要 Node.js 20.18.1 或更高版本(这是从 cheerio 继承的要求,因为它需要 undici 7)。

# run it without installing
npx -y mcp-server-web-fetcher

# or install globally
npm install -g mcp-server-web-fetcher
mcp-server-web-fetcher

该服务通过 stdio 使用 MCP,因此单独运行时只会等待客户端连接。将客户端指向它:

Claude Desktop

macOS 上的 ~/Library/Application Support/Claude/claude_desktop_config.json, Windows 上的 %APPDATA%\Claude\claude_desktop_config.json:

{
  "mcpServers": {
    "web-fetcher": {
      "command": "npx",
      "args": ["-y", "mcp-server-web-fetcher"]
    }
  }
}

配置完成后,使用全局安装:

{
  "mcpServers": {
    "web-fetcher": {
      "command": "mcp-server-web-fetcher",
      "env": {
        "WEB_FETCHER_TIMEOUT_MS": "20000",
        "WEB_FETCHER_RESPECT_ROBOTS": "true"
      }
    }
  }
}

重启 Claude Desktop,然后让它总结某个 URL。

Claude Code
claude mcp add web-fetcher -- npx -y mcp-server-web-fetcher
Kiro、Cursor、Windsurf 和其他 MCP 客户端

格式相同,配置在 .kiro/settings/mcp.json 或客户端的 MCP 配置文件中:

{
  "mcpServers": {
    "web-fetcher": {
      "command": "npx",
      "args": ["-y", "mcp-server-web-fetcher"],
      "disabled": false,
      "autoApprove": ["fetch_page_markdown", "extract_metadata", "extract_links"]
    }
  }
}
MCP Registry

该服务已列入官方 MCP Registry,名称为 io.github.vojtisprime11/web-fetcher,因此可直接被读取 Registry 的客户端发现:

curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.vojtisprime11/web-fetcher"
Docker
docker build -t mcp-server-web-fetcher .
{
  "mcpServers": {
    "web-fetcher": {
      "command": "docker",
      "args": ["run", "--rm", "-i", "mcp-server-web-fetcher"]
    }
  }
}

镜像以非特权 node 用户运行,且只包含生产依赖。CI 会构建镜像,并使用真实 MCP 客户端驱动它,因此已验证它能够启动并响应内省请求。

从源代码构建
git clone https://github.com/vojtisprime11/mcp-server-web-fetcher.git
cd mcp-server-web-fetcher
npm install
npm run build
node dist/index.js          # or: npm run inspect
{
  "mcpServers": {
    "web-fetcher": {
      "command": "node",
      "args": ["/absolute/path/to/mcp-server-web-fetcher/dist/index.js"]
    }
  }
}

工具

这三个工具均为只读、幂等且开放世界(在协议中标注为此类),并且都接受绝对 http(s) URL。

fetch_page_markdown

下载页面并返回干净、适合大模型处理的 Markdown。

参数类型默认值说明
urlstring必填绝对 http(s) URL。
maxLengthinteger 500–100000025000每次调用返回的 Markdown 字符数。
startIndexinteger ≥ 00字符偏移量;使用上次调用返回的 nextStartIndex。
mainContentOnlybooleantrue删除导航栏、页眉、页脚和侧边栏,保留内容最密集的区块。
includeLinksbooleantrue保留 Markdown 链接(false 时仅保留文本)。
includeImagesbooleanfalse将图片保留为 ![alt](src)。
includeMetadatabooleantrue附加简短的元数据摘要。
timeoutMsinteger 1000–120000env / 15000每个请求的超时时间。

请求:

{
  "name": "fetch_page_markdown",
  "arguments": {
    "url": "https://example.com/blog/caching",
    "maxLength": 8000,
    "mainContentOnly": true,
    "includeImages": false
  }
}

structuredContent:

{
  "url": "https://example.com/blog/caching",
  "requestedUrl": "https://example.com/blog/caching",
  "status": 200,
  "contentType": "text/html; charset=utf-8",
  "title": "How caching works",
  "markdown": "# How caching works\n\nCaching is the art of **not** doing work twice...",
  "markdownLength": 7984,
  "totalLength": 21874,
  "startIndex": 0,
  "endIndex": 7984,
  "nextStartIndex": 7984,
  "truncated": true,
  "wordCount": 1203,
  "bytesDownloaded": 148213,
  "elapsedMs": 412,
  "fromCache": false,
  "redirects": [],
  "metadata": {
    "title": "How caching works",
    "description": "A deep dive into HTTP caching.",
    "canonical": "https://example.com/blog/caching",
    "language": "en",
    "author": "Ada Lovelace",
    "publishedTime": "2026-01-15T09:00:00Z"
  }
}

要读取剩余内容,请再次调用并传入 "startIndex": 7984。只要 nextStartIndex 不为 null,就继续调用。

extract_metadata

提取模型对页面进行分类所需的全部信息,无需耗费上下文处理正文。

参数类型默认值说明
urlstring必填绝对 http(s) URL。
includeJsonLdbooleantrue包含解析后的 JSON-LD 区块(跳过格式错误的区块)。
includeHeadingsbooleantrue包含 h1–h6 大纲。
includeHttpHeadersbooleantrue包含响应标头,并将其转换为小写。
timeoutMsinteger 1000–120000env / 15000每个请求的超时时间。
{
  "name": "extract_metadata",
  "arguments": { "url": "https://example.com/blog/caching", "includeJsonLd": true }
}

structuredContent(节选):

{
  "url": "https://example.com/blog/caching",
  "status": 200,
  "charset": "utf-8",
  "title": "How caching works",
  "description": "A deep dive into HTTP caching.",
  "canonical": "https://example.com/blog/caching",
  "language": "en",
  "author": "Ada Lovelace",
  "publishedTime": "2026-01-15T09:00:00Z",
  "modifiedTime": null,
  "robots": "index, follow",
  "favicon": "https://example.com/favicon.ico",
  "openGraph": {
    "title": "How caching works",
    "type": "article",
    "image": "https://example.com/img/cover.png"
  },
  "twitter": { "card": "summary_large_image", "site": "@example" },
  "alternates": [{ "hreflang": "de", "href": "https://example.com/de/blog/caching" }],
  "feeds": [
    { "title": "Feed", "href": "https://example.com/feed.xml", "type": "application/rss+xml" }
  ],
  "jsonLd": [{ "@type": "Article", "headline": "How caching works" }],
  "headings": [
    { "level": 1, "text": "How caching works", "id": null },
    { "level": 2, "text": "Directives", "id": "directives" }
  ],
  "httpHeaders": { "content-type": "text/html; charset=utf-8", "x-cache": "HIT" },
  "wordCount": 1203,
  "redirects": [],
  "fromCache": false
}
extract_links
参数类型默认值说明
urlstring必填绝对 http(s) URL。
scopeall | internal | externalall同站、站外或两者。
includeAnchorsbooleanfalse包含页面内的 #fragment 链接。
deduplicatebooleantrue合并重复的 URL。
limitinteger 1–2000200返回链接的上限。
timeoutMsinteger 1000–120000env / 15000每个请求的超时时间。
{
  "name": "extract_links",
  "arguments": { "url": "https://example.com/blog/caching", "scope": "external", "limit": 50 }
}

structuredContent:

{
  "url": "https://example.com/blog/caching",
  "status": 200,
  "totalFound": 2,
  "returned": 2,
  "internalCount": 0,
  "externalCount": 2,
  "truncated": false,
  "links": [
    {
      "url": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching",
      "text": "MDN article",
      "title": null,
      "rel": "noopener nofollow",
      "internal": false,
      "nofollow": true
    }
  ],
  "fromCache": false
}

www.example.com 和 example.com 视为同一站点;blog.example.com 则不视为同一站点。

错误处理

失败会作为 MCP 工具错误返回(isError: true),不会静默返回空结果:

HTTP_ERROR: 404 Client Error for https://example.com/missing
Retryable: no
Hint: Check the URL, or retry later if the status is 429/5xx.

structuredContent.error 包含与 JSON 相同的信息:{ code, message, retryable, url, status }。

Code含义
INVALID_URL不是绝对 URL。
BLOCKED_SCHEMEScheme 不是 http/https。
BLOCKED_HOST环回、私有、链路本地或元数据地址(SSRF 防护)。
DNS_FAILURE主机无法解析。
TIMEOUT请求超出 timeoutMs。
HTTP_ERROR响应状态 ≥ 400,或重定向缺少 Location。
TOO_MANY_REDIRECTS重定向链超过限制。
RESPONSE_TOO_LARGE声明的正文大于字节上限。
UNSUPPORTED_CONTENT_TYPE不是文本/HTML/XML/JSON 文档。
ROBOTS_DISALLOWED被 robots.txt 阻止(仅在启用强制执行时)。
NETWORK_ERROR连接重置、TLS 故障等。
PARSE_ERROR无法解析文档。

超时、5xx、408 和 429 响应会在错误返回前自动重试一次。

配置

所有配置均为可选;默认值是安全的。

变量默认值描述
WEB_FETCHER_USER_AGENTmcp-server-web-fetcher/<version> (+repo url)发出的 User-Agent。
WEB_FETCHER_TIMEOUT_MS15000默认超时时间(1000–120000)。
WEB_FETCHER_MAX_BYTES5000000单个响应的字节上限(10000–50000000)。
WEB_FETCHER_MAX_REDIRECTS5允许的重定向次数(0–20)。
WEB_FETCHER_ALLOW_PRIVATE_HOSTSfalse仅在有意获取 localhost/LAN URL 时设为 true。
WEB_FETCHER_RESPECT_ROBOTSfalse获取前强制执行 robots.txt(RFC 9309 子集)。
WEB_FETCHER_CACHE_TTL_MS60000响应缓存 TTL;0 表示禁用缓存。
WEB_FETCHER_CACHE_MAX_ENTRIES50缓存响应的最大数量。

安全

  • 默认启用 SSRF 防护。 主机名会被解析,并检查是否属于环回、RFC 1918、CGNAT、链路本地(包括 169.254.169.254)、多播和 IPv4 映射 IPv6 地址范围——对初始 URL 和每个重定向目标都会进行检查。localhost、*.localhost 和 *.internal 会被直接拒绝。关闭防护需要明确选择。
  • 限制字节数和时间。 响应以流的形式传输,并在达到字节数上限时截断;每个请求都会在超时时间到达时中止。若 Content-Length 大于上限,则会在下载前拒绝。
  • 不执行代码。 永远不会运行 JavaScript;转换前会移除 <script>、<style>、<iframe> 等标签。抓取的内容是数据,不是指令——请将传递给大模型的页面文本视为不可信输入。
  • 移除凭据。 发起请求前会从 URL 中移除 user:pass@。
  • 只读。 此服务不提供写入、文件系统或 shell 接口,也不保留任何认证状态。

发现漏洞?请参阅 SECURITY.md。

架构

src/
├── index.ts              # stdio entry point (logs to stderr only)
├── server.ts             # transport-agnostic server factory + public API
├── types.ts              # Zod input/output schemas for every tool
├── tools/
│   ├── fetchPageMarkdown.ts
│   ├── extractMetadata.ts
│   ├── extractLinks.ts
│   ├── shared.ts         # result shaping, dependency injection
│   └── index.ts
└── lib/
    ├── config.ts         # env-driven configuration
    ├── errors.ts         # typed error codes + recovery hints
    ├── net.ts            # URL parsing, SSRF guard, link resolution
    ├── http.ts           # fetch pipeline: timeouts, caps, redirects, charset, cache
    ├── robots.ts         # optional robots.txt support
    ├── html.ts           # cheerio cleanup, metadata + link extraction
    ├── markdown.ts       # turndown configuration, tables, pagination
    └── cache.ts          # TTL + LRU cache
tests/                    # unit tests + in-memory MCP protocol tests

工具处理程序接受可注入的 fetchImpl,因此所有测试都可以通过脚本化的 HTTP 存根离线运行。该库也可以直接导入:

import { runFetchPageMarkdown } from 'mcp-server-web-fetcher';

const page = await runFetchPageMarkdown({ url: 'https://example.com' });
console.log(page.markdown);

开发

最低支持的 Node.js 版本为 20.18.1,这是由 cheerio 决定的(它依赖 undici 7,而 undici 7 需要在 Node 20 中引入的 File 全局对象)。CI 会在 Node 20/22/24 上运行测试套件,并设有单独的任务,使用恰好为 20.18.1 的版本与构建后的服务完成 MCP 握手,因此声明的最低版本经过了验证,而非凭空假定。

npm install
npm run dev            # tsx watch
npm run typecheck
npm run lint
npm test               # vitest run
npm run test:coverage  # thresholds enforced
npm run build
npm run inspect        # MCP Inspector against dist/index.js
node scripts/smoke.mjs https://example.com   # live end-to-end check

欢迎贡献——请参阅 CONTRIBUTING.md 和 CODE_OF_CONDUCT.md。

限制

  • 仅支持静态 HTML。客户端渲染的 SPA 会返回服务器发送的内容;不使用无头浏览器。
  • 不支持提取 PDF 或 Office 文档。
  • 受机器人防护的网站(Cloudflare 验证页面、登录墙)会返回 403。
  • 主内容检测采用文本密度启发式算法。若页面结构特殊,请使用 mainContentOnly: false。

路线图

  • search_page 工具:仅返回与查询匹配的章节。
  • 提供可选的无头渲染开关,以支持仅含 JS 的页面。
  • 在 stdio 之外增加可流式 HTTP 传输。
  • 在缓存层支持条件请求(ETag / If-Modified-Since)。

致谢

基于 MCP TypeScript SDK、 cheerio、turndown 和 zod 构建。

许可证

MIT © Vojta Holes 和贡献者

评论

支持 Markdown

第一次接触 MCP?

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

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