为什么 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 类 MCP | Fetch 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 转为 Markdown | url(必填,要抓取的网址)<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.json | mcpServers.fetch |
| Cursor | .cursor/mcp.json(项目或用户级) | mcpServers.fetch |
| VS Code | settings.json 或 ~/.continue/config.json | mcp.servers.fetch 或 mcpServers.fetch |
| Trae | ~/.trae/mcp.json 或 trae mcp add | mcpServers.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)→ 拿到最新文档 → 一次写对
完整步骤:
- 在对话中明确给出文档 URL
- 提示 Agent 先抓文档再写代码
- Agent 调用
fetch工具拉取内容 - 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" }
分析:接口实际返回的不是订单列表,而是校验错误。两个问题:
- limit=10 没问题(<= 50 的约束满足),但 status=pending 时 如果 limit > 5 必须带 cursor 做分页——这是新加的限制
- 修复:要么 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 Query | SWR | Reuse 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 就既能"现学"又能"记住",离一个真正好用的助手又近了一步。
相关阅读:
- Puppeteer MCP:让 AI 拥有一双会点浏览器的手 —— Fetch 搞不定的 JS 渲染页,交给它
- Filesystem MCP:让 Agent 拥有读写本地文件的能力 —— Fetch 拉下来想存本地?配这个
- Memory MCP:让 Agent 拥有跨会话记忆的能力 —— 抓过的内容别重复抓,记下来
- Brave Search MCP:让 Agent 拥有联网搜索的能力 —— 不知道 URL 时先用它找