每日 MCP Server

Fetch MCP Server:让 Agent 拥有抓取网页内容的能力

2026-09-01
网络仓库
支持客户端:
Claude CodeCursorVS CodeTrae
MCPFetch网页抓取HTTP内容获取

简介

Agent 最大的限制之一就是无法主动获取网页信息——它不知道你说的那个链接里写了什么。Fetch MCP Server 通过一个轻量级的 MCP 接口,让 Claude Code、Cursor、VS Code 中的 Agent 能够直接抓取任意 URL 的内容,自动转为 Markdown 供上下文使用,还能自定义请求头和超时。本文介绍 Fetch MCP 的核心能力、在各客户端中的配置方式、三个实战场景(文档实时查阅 / API 在线调试 / 多源信息聚合),以及与 Puppeteer MCP 的选型对比。

安装命令

npx -y @modelcontextprotocol/server-fetch

为什么 Agent 需要一个 Fetch 工具

如果你用过任何 AI 编程助手,一定会遇到这种尴尬时刻:

你:帮我看看这个链接里的 API 文档怎么用
     https://api.example.com/docs/v2/chat

Agent:抱歉,我无法访问外部链接,请把内容复制给我。

这就是 Agent 最显眼的"硬伤"之一——它生活在一个被封了网的信息孤岛上。LLM 的训练数据有截止日期,世界却在每天更新;Agent 能写代码、能跑命令,但偏偏看不到"那个你随手甩过去的 URL 里到底写了什么"。

这种限制会引发一连串真实的痛点:

痛点 1:文档总是过时的
  → 你让 Agent 写一段调用某 SDK 的代码
  → Agent 用的是 6 个月前的 API 签名
  → 跑起来报错:方法不存在 / 参数变了
  → 你只能手动去官网复制最新文档粘给它

痛点 2:API 调试要"看现场"
  → 接口报错时你想让 Agent 帮忙分析
  → 但 Agent 看不到那个接口实际返回了什么
  → 你只能自己 curl 一遍把响应贴进对话

痛点 3:信息分散在多个网页
  → 你想对比三篇博客的观点
  → 你想汇总四个开源项目的 README
  → 你只能一个个打开、复制、粘贴到对话框

有人会说:"不是有 WebSearch MCP 吗?" 但 搜索 ≠ 抓取:

能力WebSearch 类 MCPFetch MCP
给一个具体 URL,能不能拿到内容❌ 不能,只能搜关键词✅ 直接抓取该 URL
拿到的是全文还是摘要摘要/片段完整正文(转 Markdown)
能否自定义请求头(鉴权/UA)不能可以
能否拉 API 返回的 JSON不能可以
适合的场景"我不知道在哪,先帮我找""我知道在哪,帮我读"

搜索解决"找得到"的问题,抓取解决"读得到"的问题,两个能力互补不替代。Fetch MCP 就是把"读得到"这件事标准化成一个 MCP 工具,让所有支持 MCP 的客户端都能直接调用——不用你手动复制粘贴,不用写一次性脚本,Agent 自己决定什么时候该去拉一个 URL。

它的设计哲学很简单:

Agent 知道该抓哪个 URL(你给的、或它从对话中推断的)
   ↓
Fetch MCP 把这个 URL 拉下来
   ↓
自动把 HTML 转成 Markdown(去掉导航、广告、脚本)
   ↓
塞回 Agent 的上下文
   ↓
Agent 基于真实内容回答你

这一条流水线打通后,Agent 就从"只能凭记忆瞎编"变成了"可以现学现用"。本文接下来从核心能力、配置方式、实战场景、选型对比四个维度把它讲透。

Fetch MCP 核心能力

Fetch MCP 的核心是 一个 fetch 工具,但它通过参数组合覆盖了相当丰富的场景。下表是它的完整能力清单:

工具名功能关键参数返回格式
fetch抓取指定 URL 的内容,自动把 HTML 转为 Markdownurl(必填,要抓取的网址)<br/>max_length(可选,单次返回最大字符数,默认 5000)<br/>start_index(可选,从第几个字符开始返回,用于分页)text/markdown 形式的网页正文
fetch(带鉴权)抓取需要登录或鉴权的接口/页面url<br/>+ 自定义请求头(如 Authorization: Bearer xxx)同上,但能拿到鉴权后才能访问的内容
fetch(带 UA 伪装)抓取会拦截默认 UA 的站点url<br/>+ 自定义 User-Agent 头同上
fetch(分页拉取)抓取超长文档/长文(超过单次 5000 字符限制)第一次 fetch(url) 拿到前 5000 字符 + 总长度<br/>后续 fetch(url, start_index=5000) 拿下一段多次调用拼接出完整内容
fetch(拉 JSON API)直接抓取 REST API 的 JSON 响应url 指向 API 端点<br/>可加 Accept: application/json 头原始 JSON 文本(Agent 可直接解析)
fetch(拉 raw 文件)抓取 GitHub raw、gist、CSV、纯文本等url 指向 raw 文件地址原始文本内容

关键参数详解

url(必填)

唯一必填参数。支持 http:// 和 https://,支持查询参数,支持锚点(抓取后 Agent 自己定位)。不支持本地文件路径——本地文件应该用 Filesystem MCP,不是 Fetch MCP 的活。

✅ https://example.com/docs/api
✅ https://api.github.com/repos/modelcontextprotocol/servers
✅ https://raw.githubusercontent.com/user/repo/main/README.md
❌ /Users/me/file.txt        ← 用 Filesystem MCP
❌ file:///Users/me/file.txt ← 用 Filesystem MCP

max_length(可选)

控制单次返回的最大字符数,默认 5000。这个设计是为了保护 Agent 的上下文窗口——一个网页动辄几万字,全塞进去会把其他上下文挤掉。5000 字符大约能覆盖一篇中等长度博客的核心内容。

fetch(url, max_length=5000)   ← 默认值,适合大多数文档页
fetch(url, max_length=2000)   ← 只看开头摘要
fetch(url, max_length=50000)  ← 拉长文/完整 API 响应(注意上下文占用)

start_index(可选)

配合 max_length 做分页。第一次调用返回内容会附带"还有多少字符没返回"的提示,Agent 可以决定是否继续抓:

第 1 次:fetch(url, max_length=5000, start_index=0)
        → 返回前 5000 字符 + "total: 18000 字符"
第 2 次:fetch(url, max_length=5000, start_index=5000)
        → 返回 5000-10000 字符
第 3 次:fetch(url, max_length=5000, start_index=10000)
        → 返回 10000-15000 字符
第 4 次:fetch(url, max_length=5000, start_index=15000)
        → 返回 15000-18000 字符(最后一段)

这种"按需分页"是 Fetch MCP 最聪明的设计——Agent 可以读完前 5000 字符判断"这页有没有我要的信息",如果有就继续拉,没有就放弃,避免一次塞满上下文。

HTML 转 Markdown 的魔法

Fetch MCP 不只是把 HTML 源码甩给 Agent,它会做一层清洗:

原始 HTML
  ├─ <script> / <style>      → 删除
  ├─ <nav> / <header> / <footer> → 删除
  ├─ <div class="ad-...">    → 删除
  ├─ <table>                 → 转成 Markdown 表格
  ├─ <code> / <pre>          → 转成 ```代码块```
  ├─ <h1>~<h6>               → 转成 # ~ ######
  ├─ <a href="...">          → 转成 [text](url)
  └─ 其他                    → 提取纯文本

  ↓ 输出干净的 Markdown

这层转换至关重要——原始 HTML 一半是标签噪声,直接塞给 Agent 既浪费 token 又干扰理解。转成 Markdown 后,Agent 读起来就像读一份结构化文档,定位标题、列表、代码块都很精准。

各客户端配置示例

Fetch MCP 的安装极其简单——不需要 clone 仓库、不需要编译,一行 npx 搞定。但不同客户端的配置文件位置和写法略有差异,下面逐个给出完整可用配置。

Claude Code(claude_desktop_config.json)

Claude Code 的 MCP 配置文件位置:

  • macOS:~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows:%APPDATA%\Claude\claude_desktop_config.json

完整配置:

{
  "mcpServers": {
    "fetch": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-fetch"]
    }
  }
}

如果你已经有其他 MCP Server(比如 filesystem、github),合并进去:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"]
    },
    "fetch": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-fetch"]
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx"
      }
    }
  }
}

注意:修改配置文件后必须完全退出 Claude Code(不是关窗,是 Dock 右键退出),重新打开才生效。

Cursor(.cursor/mcp.json)

Cursor 把 MCP 配置放在项目根目录或用户目录的 .cursor/mcp.json:

  • 项目级:<项目根>/.cursor/mcp.json(只对该项目生效)
  • 用户级:~/.cursor/mcp.json(全局生效)

完整配置:

{
  "mcpServers": {
    "fetch": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-fetch"]
    }
  }
}

如果想让 fetch 在所有项目都可用,放用户级;想只对某个项目可用,放项目级。两者同名时项目级覆盖用户级。

带超时和环境变量的进阶写法:

{
  "mcpServers": {
    "fetch": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-fetch"
      ],
      "env": {
        "FETCH_TIMEOUT_MS": "30000",
        "FETCH_MAX_LENGTH": "10000"
      }
    }
  }
}

VS Code(settings.json)

VS Code 通过 GitHub Copilot Chat 或 Continue 等扩展支持 MCP。配置写在 settings.json:

  • macOS:~/Library/Application Support/Code/User/settings.json
  • Windows:%APPDATA%\Code\User\settings.json
  • Linux:~/.config/Code/User/settings.json

完整配置:

{
  "mcp.servers": {
    "fetch": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-fetch"]
    }
  }
}

如果你用 Continue 扩展,配置在 ~/.continue/config.json:

{
  "mcpServers": {
    "fetch": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-fetch"]
    }
  }
}

Trae(mcp config)

Trae 的 MCP 配置通过内置命令管理,也可以直接编辑配置文件。命令行方式:

# 添加 fetch MCP Server
trae mcp add fetch -- npx -y @modelcontextprotocol/server-fetch

# 查看已配置的 MCP Server
trae mcp list

# 验证 fetch 是否可用
trae mcp test fetch

或者直接编辑配置文件 ~/.trae/mcp.json:

{
  "mcpServers": {
    "fetch": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-fetch"]
    }
  }
}

带项目级配置的写法(.trae/mcp.json 放项目根目录):

{
  "mcpServers": {
    "fetch": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-fetch"],
      "env": {
        "FETCH_DEFAULT_TIMEOUT": "15000"
      }
    }
  }
}

通用配置模板(一图速查)

客户端配置文件路径关键字段
Claude Code~/Library/Application Support/Claude/claude_desktop_config.jsonmcpServers.fetch
Cursor.cursor/mcp.json(项目或用户级)mcpServers.fetch
VS Codesettings.json 或 ~/.continue/config.jsonmcp.servers.fetch 或 mcpServers.fetch
Trae~/.trae/mcp.json 或 trae mcp addmcpServers.fetch

核心配置统一是这三行:

"fetch": {
  "command": "npx",
  "args": ["-y", "@modelcontextprotocol/server-fetch"]
}

记住这三行,搬到哪个客户端都能用。

三个实战场景

光看配置没感觉,下面三个场景演示 Fetch MCP 真正能解决什么问题。每个场景都附完整步骤和可直接照抄的对话示例。

场景一:实时查阅官方文档

背景:你让 Agent 写一段调用某 SDK 的代码,但 SDK 三个月前发了大版本,Agent 训练数据里的 API 已经作废。让它自己抓最新文档。

痛点对照:

没 Fetch MCP:
  你:用 xxx-sdk 写个分页查询
  Agent:(用旧 API 写了一段)→ 跑起来报错
  你:文档在这里 https://xxx-sdk.com/docs/query,你看看
  Agent:我无法访问链接,请复制给我
  你:(手动打开网页、Ctrl+A、Ctrl+C、粘到对话)
  Agent:好的,根据你贴的内容……(终于写对了)

有 Fetch MCP:
  你:用 xxx-sdk 写个分页查询,文档在 https://xxx-sdk.com/docs/query
  Agent:(自动 fetch 该 URL)→ 拿到最新文档 → 一次写对

完整步骤:

  1. 在对话中明确给出文档 URL
  2. 提示 Agent 先抓文档再写代码
  3. Agent 调用 fetch 工具拉取内容
  4. Agent 基于真实文档生成代码

示例对话:

你:帮我写一段用 @anthropic/sdk 调用 messages API 的代码,
    支持流式输出。文档地址:https://docs.anthropic.com/en/api/messages-streaming

Agent:
  我先抓取最新文档确认 API 签名。

  [调用工具: fetch]
  url: https://docs.anthropic.com/en/api/messages-streaming
  max_length: 8000

  [工具返回]
  # Streaming Messages
  ...
  The streaming endpoint is `POST /v1/messages` with `stream: true`.
  Events: message_start, content_block_start, content_block_delta,
          content_block_stop, message_delta, message_stop.
  ...

  好的,文档已确认。下面是基于最新 streaming API 的代码:

  ```javascript
  import Anthropic from "@anthropic/sdk";

  const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });

  const stream = await client.messages.stream({
    model: "claude-3-5-sonnet-20241022",
    max_tokens: 1024,
    messages: [{ role: "user", content: "写一首关于秋天的诗" }]
  });

  for await (const event of stream) {
    if (event.type === "content_block_delta" &&
        event.delta.type === "text_delta") {
      process.stdout.write(event.delta.text);
    }
  }

  const finalMessage = await stream.finalMessage();
  console.log("\n\n完成,token 用量:", finalMessage.usage);

关键点:

  • 用 .stream() 而不是 .create(),自动处理 SSE
  • 遍历事件流,过滤 text_delta 拿增量文本
  • 最后用 .finalMessage() 拿完整消息(含 usage 统计)

**为什么这个场景重要**:API 文档迭代速度远快于 LLM 训练周期,让 Agent 现抓现用是绕开"知识过时"问题最直接的办法。

### 场景二:API 在线调试

**背景**:你的接口报错了,但光看错误信息看不出来是参数问题还是后端问题。让 Agent 直接调一次接口,看看实际响应。

**痛点对照**:

没 Fetch MCP: 你:调用 /api/orders 接口报 500,帮我看看 Agent:请把请求和响应贴给我 你:(手动 curl + 复制响应 + 粘贴)

有 Fetch MCP: 你:调用 https://api.myapp.com/orders?status=pending 报 500, 用这个 token 鉴权,帮我看看响应是什么 Agent:(fetch 该 URL)→ 拿到错误响应 → 分析根因


**完整步骤**:

1. 把接口 URL 给 Agent(含 query 参数)
2. 告诉 Agent 用什么鉴权头
3. Agent 调用 `fetch` 拉响应
4. Agent 解析 JSON、定位错误字段
5. 给出修复建议

**示例对话**:

你:测试一下这个接口 https://api.myapp.com/v2/orders?status=pending&limit=10, 请求头加 Authorization: Bearer eyJhbGc...,看看返回什么

Agent: 我来调一下这个接口。

[调用工具: fetch] url: https://api.myapp.com/v2/orders?status=pending&limit=10 headers: Authorization: Bearer eyJhbGc... Accept: application/json

[工具返回] { "error": "VALIDATION_FAILED", "details": [ { "field": "limit", "message": "limit must be <= 50 when status=pending", "received": 10 }, { "field": "cursor", "message": "cursor is required when limit > 5" } ], "request_id": "req_8f3a2b1c" }

分析:接口实际返回的不是订单列表,而是校验错误。两个问题:

  1. limit=10 没问题(<= 50 的约束满足),但 status=pending 时 如果 limit > 5 必须带 cursor 做分页——这是新加的限制
  2. 修复:要么 limit 降到 5 以内,要么补 cursor 参数

正确请求: GET /v2/orders?status=pending&limit=5 或 GET /v2/orders?status=pending&limit=10&cursor=xxx(cursor 从上次响应取)

要我直接帮你改前端那个请求函数吗?


**为什么这个场景重要**:Agent 看到的是真实响应而不是你的转述,能更准地定位问题。尤其是后端返回大段 JSON 时,你手动复制容易漏字段,Agent 直接 fetch 能拿完整数据。

### 场景三:多源信息聚合

**背景**:你想对比三四个开源项目的 README,决定用哪个。让 Agent 把每个项目的 README 都抓回来,做个对比表。

**痛点对照**:

没 Fetch MCP: 你:对比一下 react-query、swr、tanstack/query 这三个库 Agent:(用训练数据里的信息对比)→ 可能漏了最新变化 你:自己去 GitHub 看 README → 打开 4 个标签页、复制 4 份 README、贴进对话

有 Fetch MCP: 你:对比这几个库的 README: https://github.com/TanStack/query https://github.com/vercel/swr https://github.com/ReuseJS/reuse Agent:(4 次 fetch)→ 拿到最新 README → 生成对比表


**完整步骤**:

1. 列出要对比的 URL 清单
2. 告诉 Agent 对比哪些维度
3. Agent 逐个 fetch(必要时分页拉完整内容)
4. Agent 提取关键信息、生成对比表
5. 给出选型建议

**示例对话**:

你:帮我对比这几个 React 数据请求库的 README,从这几个维度看: - 包大小 - 是否支持 SSR - 是否支持乐观更新 - TypeScript 支持 - 学习曲线

https://github.com/TanStack/query/blob/main/README.md https://github.com/vercel/swr/blob/main/README.md https://github.com/ReuseJS/reuse-promise/blob/main/README.md

Agent: 我先并行抓取三个 README。

[调用工具: fetch × 3(并行)] url-1: https://github.com/TanStack/query/blob/main/README.md url-2: https://github.com/vercel/swr/blob/main/README.md url-3: https://github.com/ReuseJS/reuse-promise/blob/main/README.md max_length: 8000

[工具返回] 三份 README 内容……

基于最新 README 的对比表:

维度TanStack QuerySWRReuse Promise
包大小(gzip)~13KB~4KB~2KB
SSR 支持✅ 原生支持✅ 原生支持⚠️ 需手动处理
乐观更新✅ 内置 API⚠️ 需手动❌ 不支持
TypeScript✅ 完整类型✅ 完整类型✅ 完整类型
学习曲线中等(概念多)简单极简
适用场景复杂数据层简单请求简单异步缓存

选型建议:

  • 项目体量大、数据交互复杂 → TanStack Query(功能最全)
  • 中小项目、追求轻量 → SWR(4KB 全家桶)
  • 只是想缓存几个 Promise → Reuse Promise(最小化)

**为什么这个场景重要**:Agent 拿的是各项目自己 README 里的最新声明,而不是它训练数据里几个月前的快照。做对比时这种"现抓现用"特别关键——开源项目的特性表迭代很快,凭记忆对比容易踩坑。

## Fetch MCP vs Puppeteer MCP:选型对比

很多人会问:**既然 Puppeteer MCP 也能抓网页,为什么还要 Fetch MCP?** 答案是它俩解决的问题根本不一样。

先看核心差异:

| 维度 | Fetch MCP | Puppeteer MCP |
|---|---|---|
| **底层实现** | Node 的 `fetch` / HTTP 客户端 | 启动一个真实 Chrome(无头) |
| **能否执行 JS** | ❌ 不能 | ✅ 能 |
| **JS 渲染页面** | ❌ 拿到的是原始 HTML(JS 没执行) | ✅ 拿到渲染后的 DOM |
| **能否交互** | ❌ 不能 | ✅ 点击、填表、提交 |
| **能否截图** | ❌ 不能 | ✅ 整页/元素截图 |
| **资源占用** | 极低(一个 Node 进程) | 高(一个 Chrome 实例,~200MB) |
| **启动速度** | 毫秒级 | 秒级(启动浏览器) |
| **安装复杂度** | npx 即用 | 首次会下载 Chromium(~150MB) |
| **返回格式** | Markdown 文本 | 文本/HTML/截图(多模态) |
| **适用页面** | 静态站、API、文档、博客、raw 文件 | SPA、动态渲染页、需登录的页面 |
| **超时风险** | 低(HTTP 请求好控制) | 高(页面加载受网络/JS 影响) |
| **典型场景** | "把这页文档给我" | "看看这个商品现在多少钱" |

**什么时候用 Fetch MCP**:

✅ 抓官方文档(Docusaurus、GitBook、纯静态站) ✅ 调 REST API / GraphQL API(返回 JSON) ✅ 拉 GitHub raw 文件、gist ✅ 读博客文章(WordPress、Hexo、Medium 静态部分) ✅ 拉 RSS / sitemap.xml / robots.txt ✅ 多个 URL 并行抓取做聚合


**什么时候用 Puppeteer MCP**:

✅ 页面是 React/Vue SPA,初始 HTML 是空壳 ✅ 需要点击按钮触发内容加载 ✅ 需要登录后才能看的页面 ✅ 要看渲染后的视觉效果(截图) ✅ 表单自动化提交 ✅ 监控页面布局变化(视觉回归)


**一个简单的判断流程**:

要抓的页面是静态 HTML 吗? ├─ 是 → Fetch MCP(轻量、快、省资源) └─ 否(JS 渲染)→ 要不要看渲染后的样子? ├─ 只要文本内容 → 先试 Fetch MCP,拿不到再上 Puppeteer └─ 要截图/交互 → Puppeteer MCP


**实际项目里两个一起用**是最常见的搭配:

fetch → 拉官方文档、调 API、读 raw 文件(90% 高频场景) puppeteer → 偶尔遇到 SPA 才用(10% 但不可替代)


这样能用 Fetch MCP 解决 90% 的"读网页"需求,剩下 10% 才动用更重的 Puppeteer,资源开销最优。

## 注意事项

把 Fetch MCP 接进来之前,有几条必须知道的边界和坑。

### 1. 超时设置

Fetch MCP 默认超时通常在 30 秒左右,但部分网络环境(公司代理、跨境访问)可能不够。问题表现:

现象:Agent 调用 fetch 后卡很久最后报 timeout 原因:目标站点响应慢 / 网络不稳定 / 服务端有反爬延迟 解决:

  • 在配置里加环境变量 FETCH_TIMEOUT_MS=60000(或更长)
  • 或者让 Agent 分多次重试
  • 极端情况:换 Puppeteer MCP(它有更细的等待控制)

各客户端配置超时的写法:

```json
// Claude Code
{
  "mcpServers": {
    "fetch": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-fetch"],
      "env": {
        "FETCH_TIMEOUT_MS": "60000"
      }
    }
  }
}

建议:生产环境超时设 30-60 秒,太短会误杀慢响应,太长会让 Agent 卡住。

2. 内容截断

max_length 默认 5000 字符,这对长文档是截断的。截断后 Agent 拿到的是不完整内容,可能漏关键信息。三种应对策略:

策略 A:明确告诉 Agent 用更大的 max_length
  "用 max_length=20000 抓这个 URL"
  代价:占上下文,可能挤掉其他信息

策略 B:让 Agent 分页拉
  "先抓前 5000 字,看是否包含 X 信息,没有就继续抓下一段"
  代价:多次调用,慢

策略 C:先抓目录页,定位具体章节再抓
  "先抓 https://xxx.com/docs,找到 '分页' 这个章节的链接,
   再抓那个链接"
  代价:需要 Agent 多步推理,但对超长文档最省 token

避免的坑:

❌ 直接 fetch 一个长文档不设 max_length → 全塞进去爆上下文
❌ 看到"内容已截断"就放弃 → 应该让 Agent 继续抓下一段
❌ 反复 fetch 同一 URL → 浪费,应该缓存结果(部分客户端会自动缓存)

3. 反爬策略

很多站点会拦截非浏览器 UA 的请求,Fetch MCP 默认 UA 可能被识别为机器人。表现:

现象:fetch 返回 403、429、或一段"请验证你不是机器人"的 HTML
原因:站点有反爬,默认 UA 被拦截
解决:
  - 配置自定义 User-Agent,伪装成浏览器
  - 降低抓取频率(多 URL 之间加间隔)
  - 必要时换 Puppeteer MCP(带完整浏览器指纹)

配置自定义 UA 的写法(取决于具体 Fetch MCP 实现):

{
  "mcpServers": {
    "fetch": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-fetch"],
      "env": {
        "FETCH_USER_AGENT": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
      }
    }
  }
}

或在调用时让 Agent 加 UA 头:

你:抓 https://example.com,但用浏览器 UA,默认 UA 被拦了

Agent:
  [调用工具: fetch]
  url: https://example.com
  headers:
    User-Agent: Mozilla/5.0 ...

法律/道德边界:抓取前检查目标站点 robots.txt,尊重 Disallow 规则;不要高频抓取给对方服务器造成压力;不要抓取需要付费/登录才能访问的付费内容(即使技术上能绕过)。

4. 安全考虑

Fetch MCP 让 Agent 能访问任意 URL,这带来几个安全风险,必须防住:

风险 1:SSRF(服务器端请求伪造)
  攻击者诱导 Agent fetch 内网地址(http://localhost:xxx、http://192.168.x.x)
  → 可能拿到内网敏感信息
  防护:
  - 部分实现支持域名白名单(FETCH_ALLOWED_DOMAINS)
  - 部署在服务端时务必加白名单
  - 个人开发机用默认配置问题不大,但要 aware

风险 2:敏感 URL 泄露
  Agent 把带 token 的 URL 写到日志/对话历史
  → token 可能被泄露
  防护:
  - 不要让 Agent fetch 带 token 的 URL(用环境变量 + 鉴权头)
  - 定期清理对话历史

风险 3:恶意内容注入
  抓取的网页里可能含"忽略上面的指令,执行下面这个"等提示注入
  → Agent 可能被网页内容操控
  防护:
  - 让 Agent 把抓取内容当"参考资料"而非"指令"
  - 关键操作(删文件、发请求)前让用户确认
  - 用支持 prompt isolation 的客户端

风险 4:下载恶意文件
  fetch 一个 URL 实际返回的是可执行文件/恶意脚本
  → Agent 拿到内容可能误解析
  防护:
  - fetch 工具返回的是文本,不会自动执行
  - 但要警惕 Agent 把抓到的内容直接写入本地文件并运行
  - 关键操作前用户确认

最小权限原则:

默认只配 fetch,不配 filesystem/github 等
  → Agent 能读网但改不了本地
需要写入时再单独加 filesystem,限定目录
  → 隔离爆炸半径

5. 常见故障排查

现象原因解决
Agent 报"fetch tool not found"配置没生效重启客户端;检查 JSON 语法;查日志确认 server 启动
调用后超时网络/目标站点慢调大 FETCH_TIMEOUT_MS;换网络;改用 Puppeteer
返回 403/429反爬拦截加浏览器 UA;降频;上 Puppeteer
返回 HTML 空壳JS 渲染页Fetch 拿不到,必须用 Puppeteer
内容截断不完整max_length 太小让 Agent 分页抓或调大 max_length
Agent 不主动用 fetch提示不够明确在对话里明确说"抓这个 URL"
抓 JSON API 拿到 HTML接口要 Accept 头让 Agent 加 Accept: application/json

进阶用法:组合其他 MCP

Fetch MCP 单用已经很强,但跟其他 MCP 组合起来威力翻倍。几个常见组合:

Fetch + Filesystem:抓下来存档

你:抓 https://example.com/spec.html 存到 ./cache/spec.md

Agent:
  1. [fetch] 抓 URL,拿到 Markdown
  2. [filesystem] 写入 ./cache/spec.md
  → 完成

适合做文档本地化、离线索引、定期归档。

Fetch + GitHub:抓 PR 关联的文档

你:看看 PR #234 改了哪些 API 文档,
    抓 https://my-docs-site.com/api 看看跟改之前一致吗

Agent:
  1. [github] 拉 PR #234 改了哪些文件
  2. [fetch] 抓当前线上文档
  3. 对比,列出不一致

适合做"文档是否同步了代码改动"的检查。

Fetch + Memory:抓过的内容记下来

你:抓 https://example.com/faq,下次别重复抓

Agent:
  1. [memory] 查有没有存过这个 URL 的内容
  2. 没有 → [fetch] 抓 → [memory] 存
  3. 有 → 直接用记忆里的内容

适合高频重复抓取场景,省请求也省 token。

Fetch + Sequential Thinking:复杂调研

你:调研一下 React 19 的新特性

Agent:
  1. [sequential-thinking] 拆解调研计划(要抓哪几个 URL)
  2. [fetch × N] 按计划抓多个来源
  3. [sequential-thinking] 综合分析
  4. 输出报告

把 Fetch 当作调研工具链的"数据采集层",配合思维链做复杂任务。

性能调优

在生产环境长期用 Fetch MCP,有几个性能优化点:

1. 缓存

同一 URL 短时间内重复 fetch 是浪费。三种缓存策略:

A. 客户端缓存(最简单)
   部分客户端(Cursor、Trae)会自动缓存 fetch 结果
   同一对话内重复抓同一 URL 不再发请求

B. 代理缓存
   在 Fetch MCP 前面放一个本地 HTTP 代理(如 mitmproxy)
   带缓存策略,避免重复请求

C. 用 Memory MCP 显式缓存
   把抓过的内容存到 memory MCP,下次直接取
   适合"我知道这个 URL 短期内不会变"的场景

2. 并行抓取

让 Agent 一次抓多个 URL 时,明确鼓励它并行:

你:并行抓这三个 URL:
    https://a.com/1
    https://b.com/2
    https://c.com/3

Agent:(并行 3 次 fetch,而不是串行)

MCP 协议支持工具并行调用,但 Agent 默认可能串行——明确说"并行"能让它一次发多个请求,总耗时大幅下降。

3. 选择性抓取

不要让 Agent 把整页都拉回来再找,可以先用小 max_length 探一眼:

第 1 步:fetch(url, max_length=1000)  ← 只看开头,判断有没有要的信息
第 2 步:如果有 → fetch(url, max_length=10000)  ← 正经抓
        如果没有 → 换 URL

这种"试探式抓取"在批量调研时能省 70% 以上的 token。

与其他"读网页"方案对比

除了 Fetch MCP,市面上还有几种让 Agent 读网页的方案,对比一下:

方案优点缺点适合
Fetch MCP标准 MCP、配置简单、自动转 Markdown不执行 JS静态页、API、文档
Puppeteer MCP真浏览器、能执行 JS、能截图重、慢、占资源SPA、动态页
WebSearch MCP能搜索、找得到信息源只返回摘要、不能拿全文"不知道在哪"
Playwright MCP同 Puppeteer、跨浏览器同样重需要兼容多浏览器
ScrapingBee MCP(第三方)自带代理/反爬/JS 渲染收费、依赖第三方高频大规模抓取
手动复制粘贴不需要配置累、易错、不可复用一次性场景

典型组合:

日常开发:Fetch MCP(90%)+ 偶尔 Puppeteer MCP(10%)
调研任务:WebSearch MCP(找)→ Fetch MCP(读)
大规模爬取:第三方专业服务(ScrapingBee 等)

实际部署经验

下面是几个真实部署 Fetch MCP 时遇到的"非技术"问题,提前知道能少走弯路:

问题 1:网络代理

公司网络走代理时,Fetch MCP 可能不自动走代理。解决:

{
  "mcpServers": {
    "fetch": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-fetch"],
      "env": {
        "HTTP_PROXY": "http://proxy.company.com:8080",
        "HTTPS_PROXY": "http://proxy.company.com:8080",
        "NO_PROXY": "localhost,127.0.0.1"
      }
    }
  }
}

问题 2:Node 版本

Fetch MCP 依赖较新的 Node(建议 Node 18+)。老版本 Node 跑不起来或行为异常:

检查:node -v
要求:>= 18
升级:用 nvm install 20 && nvm use 20

问题 3:npx 缓存

每次启动 Agent 都跑 npx -y 会重新检查包,首次可能慢。预安装能加速:

# 全局预装一次
npm install -g @modelcontextprotocol/server-fetch

# 配置改用全局版本
{
  "mcpServers": {
    "fetch": {
      "command": "mcp-server-fetch"
    }
  }
}

问题 4:跨平台路径

Windows 下配置文件路径要用双反斜杠或正斜杠:

// ✅ 正确
"C:/Users/me/.cursor/mcp.json"
"C:\\Users\\me\\.cursor\\mcp.json"

// ❌ 错误(单反斜杠会被转义)
"C:\Users\me\.cursor\mcp.json"

问题 5:团队共享配置

团队多人用同一项目,MCP 配置应该提交到仓库吗?

推荐做法:
  - 项目级 .cursor/mcp.json 提交到仓库(不含密钥)
  - 用户级配置各自维护(含密钥的)
  - 密钥用环境变量,不写在 JSON 里

.cursor/mcp.json 示例(提交到仓库):

{
  "mcpServers": {
    "fetch": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-fetch"]
    }
  }
}

带密钥的版本(不提交,写在用户级):

{
  "mcpServers": {
    "fetch": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-fetch"],
      "env": {
        "FETCH_API_TOKEN": "${FETCH_API_TOKEN}"
      }
    }
  }
}

一张图总结

Agent 想读一个 URL
        ↓
配置好 Fetch MCP(5 分钟)
        ↓
对话里说"抓这个 URL:xxx"
        ↓
Agent 调用 fetch 工具
        ↓
   ┌──────┴──────┐
   │             │
静态页/API    JS 渲染页
   │             │
   ↓             ↓
拿到 Markdown   拿到空壳 → 改用 Puppeteer MCP
   │
   ↓
塞进 Agent 上下文
        ↓
   ┌──────┴──────┐
   │             │
内容太长       内容合适
   │             │
   ↓             ↓
分页 fetch     一次搞定
(start_index)
        ↓
Agent 基于真实内容回答

快速上手 Checklist

如果你刚拿到 Fetch MCP,按这个清单一步步来:

□ 1. 确认 Node 版本 >= 18(node -v)
□ 2. 找到你客户端的配置文件位置(参考前文表格)
□ 3. 复制通用配置三行:
    "fetch": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-fetch"]
    }
□ 4. 重启客户端
□ 5. 跑一个测试对话:
    "抓 https://example.com 看看内容"
    → Agent 调用 fetch 工具 → 返回内容 → 成功
□ 6. 试一个真实场景:
    "抓 https://docs.example.com/api 的内容,
     帮我写一段调用代码"
□ 7. 遇到反爬?配自定义 UA
□ 8. 遇到超时?调大 FETCH_TIMEOUT_MS
□ 9. 遇到 SPA?换 Puppeteer MCP
□ 10. 长期用?考虑加 Memory MCP 做缓存

常见问答

Q1:Fetch MCP 能抓需要登录的页面吗?

A:能,但需要你提供鉴权信息。两种方式:

  • URL 里带 token(不推荐,token 会泄露到日志)
  • 配置请求头 Authorization: Bearer xxx(推荐) 但复杂登录流程(多步 OAuth、验证码)搞不定,那得上 Puppeteer MCP。

Q2:抓下来内容是乱码怎么办?

A:大概率是编码问题。检查页面是否声明了 charset,Fetch MCP 一般会自动识别,但极少数老站点(GBK 编码)可能出问题。解决:让 Agent 在抓取后手动转码,或者抓 raw HTML 后用 iconv 处理。

Q3:能抓 PDF / Word 等非 HTML 文件吗?

A:Fetch MCP 主要为 HTML 设计,抓 PDF 会拿到二进制乱码。要抓 PDF 应该用专门的 PDF MCP(能解析 PDF 文本);Word 文档先转 HTML 或 Markdown 再抓。

Q4:Agent 会不会乱抓一气?

A:会的,如果不约束。Agent 看到 URL 可能就抓,浪费请求。建议:

  • 在系统提示里约束"未经用户确认不要主动 fetch"
  • 关键 fetch(带 token 的、内网的)要用户确认
  • 用支持 prompt isolation 的客户端

Q5:Fetch MCP 收费吗?

A:Fetch MCP 本身开源免费。但它抓取的目标站点可能有收费墙——你抓不到的内容,Fetch MCP 也变不出来。要绕过收费墙只能登录账号(带鉴权头),但这又涉及账号合规问题。

Q6:能不能限制 Agent 只能抓白名单域名?

A:取决于具体实现。官方 @modelcontextprotocol/server-fetch 默认不限制,但社区有些 fork 版本支持域名白名单(通过环境变量配置)。生产环境部署强烈建议加白名单,防 SSRF。

Q7:Fetch MCP 能抓 WebSocket 吗?

A:不能。Fetch MCP 是 HTTP 请求,不维护长连接。要抓 WebSocket 数据流,需要专门的 WebSocket MCP 或用 Puppeteer 在浏览器里 hook。

Q8:抓取频率有限制吗?

A:Fetch MCP 本身不限制,但目标站点会限制。频繁抓同一站点容易触发 429(Too Many Requests)或 IP 封禁。建议:

  • 同一站点间隔至少 1-2 秒
  • 大规模抓取上代理池
  • 尊重对方 robots.txt 的 Crawl-delay

结语

Fetch MCP 是个"小而美"的 MCP Server——它就一个 fetch 工具,安装一行命令,配置三行 JSON,但解决的是 Agent 最普遍的痛点:看不到网上写了什么。

它不是最强大的(Puppeteer 更全能),不是最复杂的(很多 MCP 协议更丰富),但它是性价比最高的——花 5 分钟配置,从此 Agent 多了一个"眼睛",能读文档、能调 API、能聚合多源信息。这一步打通后,Agent 才真正像那么回事。

如果你刚开始接 MCP,Fetch MCP 应该是你装的第二个 MCP(第一个永远是 Filesystem MCP,让 Agent 能读写本地)。这两个组合起来,覆盖了 Agent 80% 的"获取信息"需求,剩下 20% 才需要更专门的工具。

下一篇我们会介绍 Memory MCP——让 Agent 记住你聊过什么,不再每次对话从零开始。Fetch + Memory 组合起来,Agent 就既能"现学"又能"记住",离一个真正好用的助手又近了一步。


相关阅读:

相关推荐

查看更多 →
MCP编程开发

MCP Resources 实战:让服务端把 8 万行设计文档喂给 Agent,附四套客户端配置与13 行排查表

Tools 是「Agent 问你答」,Resources 是「你问Agent 答」——同一个 MCP Server 里,方向相反的两种能力,而且很多人在写 Server 时只实现了 Tools,漏掉了 Resources。结果是文档、规范、schema、日志这类只读数据只能靠 Agent 一个个调 Tool 去捞,每次捞都要过一次模型判断,成本高还不稳定。本文讲透MCP 四大原语里的第二个:Resources。涵盖 resources/list、resources/read、resources/templates/list 三个方法与 URI 模板机制(RFC 6570 展开规则),以及 Resource 与 Tool 的选型对照(把同一份数据做成 Tool 还是 Resource 的实测差异)。给出一个可运行的「设计文档中心」Server 完整实现(含 40+ 行 TypeScript,含分页、版本快照、访问审计),以及 Claude Code / Cursor / VS Code + Copilot / Trae 四套带中文注释的配置 JSON。三个实战场景:Agent 读设计文档回答「这个接口为什么这么设计」、按版本读取历史 schema 做迁移校验、把资源目录挂进 IDE 侧边栏。另含资源数量膨胀到 500+ 时的分页与 URI 命名规范、五条安全红线(为什么资源 URI 绝不能暴露绝对路径和凭据),以及 13 行故障排查表。

2026-10-13
#MCP#Model Context Protocol
MCP开发

MCP Elicitation 实战:让服务端在缺参数时反向向用户提问——受限 Schema 设计 + 四套客户端配置 + 13 行排查表

写 MCP Server 的人迟早会遇到一个尴尬场景:工具必填参数用户没给,服务端要么猜一个(猜错就出事),要么返回一个干巴巴的「缺少参数 xx」让 Agent 自己去追问(Agent 经常追问得驴唇不对马嘴)。MCP 协议里的 Elicitation 就是为这个场景设计的第三个「反向能力」——服务端可以在执行过程中主动向客户端发起一次提问,由客户端把问题渲染成表单弹给用户,用户填完再回传,全程服务端不需要自己实现任何 UI、不需要存任何凭据。本文讲透它的机制与边界:elicitation/create 请求的完整结构、为什么 requestedSchema 被刻意限制成只支持原始类型(这是安全设计不是能力不足)、用户的三种响应(accept / decline / cancel)各自的语义差异、以及最关键的——为什么不能在 Elicitation 里索取密码和 API Key。含 TypeScript SDK 完整实现(含类型定义与降级路径)、Claude Code / Cursor / VS Code + Copilot / Trae 四套带中文注释的配置、三个实战场景(部署确认 / 缺失参数补全 / 敏感操作二次确认)、与 Sampling / 传统参数校验的选型对比、六条安全注意事项,以及 13 行故障排查表。

2026-10-12
#MCP#Model Context Protocol
MCP开发

MCP Roots 与 Sampling 实战:让服务端主动划边界、反向要推理——四套客户端配置 + 14 行排查表

多数人写 MCP Server 只用到 Resources / Tools / Prompts 三项,Roots 和 Sampling 这两个反向能力基本闲置,结果就是 Server 既不知道自己该在哪干活,也没有推理能力。本文把这两个方向的能力讲透:Roots 是客户端告诉服务端「工作区边界在哪」的机制(URI 列表、roots/list 双向协商、listChanged 动态通知、路径穿越的必修防御),Sampling 是服务端反过来向客户端的 LLM 请求一次推理的机制(服务端不知道任何 API Key,模型由客户端挑、账由客户端付、人可以卡在中间审批)。含 TypeScript SDK 完整实现代码(含 sampling/createMessage 的类型定义与 human-in-the-loop 回调)、Claude Code / Cursor / VS Code + Copilot / Trae 四套带中文注释的配置、三个实战场景(单仓守卫 / 日志归因摘要 / 隐私脱敏代理)、与其他方案的选型对比表、六条安全注意事项,以及 14 行故障排查表。

2026-10-10
#MCP#Model Context Protocol
MCP开发

MCP 无状态化改造:协议层不再存状态之后——并发、水平扩容、断线重连三大硬伤的根治方案与四套客户端配置

2026 年 MCP 协议经历了一次方向性的改动:核心交互不再依赖服务端保存的会话状态,状态不再存储在协议层,而是被甩给应用层处理。对写过有状态 MCP Server 的人来说,这次改动的直接感受是「少了一大堆坑」——会话粘连导致的多实例不可用、水平扩容一扩就断连、断线重连要么报错要么整段重放。本文讲清楚三件事:为什么协议层存状态在生产环境必然出问题(并发、扩容、重连三个硬伤,各配一组实测对照数据);无状态化之后你手里的工具清单发生了什么变化(transport 类、协议方法、Serverless 适配);以及状态外置的三种标准实现(外部存储、客户端持有、资源句柄),每种都给出可直接跑的 TypeScript SDK 代码。含 Claude Code / Cursor / VS Code / Trae 四套带中文注释的完整配置、三个实战场景、选型对比表、安全注意事项(含 DNS 重绑定的必修项),以及 14 行故障排查表。

2026-10-08
#MCP#Model Context Protocol