Web Fetcher

io.github.vojtisprime11由 GitHub 帳號 vojtisprime11 發布v0.1.0更新於 Oct 24, 2026

Turns any web page into clean Markdown, page metadata and classified links for LLMs.

發布方自己的服務:App Store data for agents

概覽

AI 產生的概覽

一個本機 MCP 伺服器,擷取網頁並回傳乾淨的 Markdown、結構化中繼資料與分類連結,供助理閱讀。

可以做什麼
擷取網頁並回傳乾淨、可分頁的 Markdown,可選擇保留連結、圖片與中繼資料摘要,並用 startIndex 在多次呼叫間續讀長頁面。擷取頁面中繼資料,如標題、描述、標準網址、語言、作者、日期、Open Graph、Twitter 卡片、JSON-LD、標題大綱、訂閱來源與 HTTP 回應標頭。擷取連結,含錨文字、rel 與 nofollow 標記、站內與站外分類、範圍篩選、去重與數量上限。失敗時回傳帶型別的錯誤碼、可重試標記與復原提示。
適用情境
適合助理需要閱讀或摘要網頁文章、依中繼資料判斷頁面類型,或整理外連而不把原始 HTML 塞進上下文時使用。適用於靜態 HTML 頁面;用戶端渲染的單頁應用、PDF 與需登入的網站不在範圍內。
執行需求
以 npm 套件(mcp-server-web-fetcher)形式透過 stdio 在本機執行,需要 Node.js 20.18.1 或更新版本;文件也提供 Docker 映像與原始碼建置方式。無需帳號或 API 金鑰。需要對外 HTTPS 網路存取。所有設定皆為選用,透過環境變數設定,例如 WEB_FETCHER_TIMEOUT_MS、WEB_FETCHER_MAX_BYTES、WEB_FETCHER_RESPECT_ROBOTS、WEB_FETCHER_USER_AGENT 與 WEB_FETCHER_ALLOW_PRIVATE_HOSTS。
安裝前請注意
擷取到的網頁文字屬於不可信輸入,可能包含提示注入內容;README 本身也說明應將其視為資料而非指令。將 WEB_FETCHER_ALLOW_PRIVATE_HOSTS 設為 true 會關閉 SSRF 防護,允許存取回環與私有網路位址,僅在確有需要時開啟。robots.txt 強制檢查預設關閉(WEB_FETCHER_RESPECT_ROBOTS)。工具為唯讀,不索取任何憑證。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Web Fetcher,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

mcp-server-web-fetcher

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

A fast, dependency-light Model Context Protocol server that turns any web page into something a language model can actually read: clean Markdown, structured metadata, and a classified list of links.

Web pages are 90% chrome. Scripts, cookie banners, navigation, sidebars and tracking pixels burn context and derail reasoning. This server strips all of that on the way in, so the model sees the article and nothing else.

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

Features

  • Clean Markdown output. Readability-style main-content detection, GFM tables, language-tagged code fences, absolute links. No leftover HTML, even for gnarly nested tables.
  • Context-window aware. Long pages are paginated with startIndex / nextStartIndex instead of being silently cut in half.
  • Structured metadata. Title, description, canonical, lang, author, publish/modify dates, Open Graph, Twitter cards, JSON-LD, hreflang alternates, RSS/Atom feeds, h1–h6 outline and raw HTTP headers.
  • Link intelligence. Absolute URLs, anchor text, rel, nofollow, internal vs external classification, scope filters, de-duplication.
  • SSRF hardening. Loopback, private, link-local and cloud-metadata addresses are blocked on every redirect hop, not just the first request. Credentials in URLs are stripped.
  • Predictable failures. Every error carries a stable machine-readable code (TIMEOUT, HTTP_ERROR, BLOCKED_HOST, RESPONSE_TOO_LARGE, …), a retryability flag and a recovery hint, so the model can self-correct instead of guessing.
  • Typed end to end. Strict Zod input schemas plus published outputSchema, so clients get validated structuredContent, not prose they have to re-parse.
  • Well behaved. Byte caps, timeouts, redirect limits, charset sniffing (including legacy encodings), a small TTL cache, transient-failure retries and optional robots.txt enforcement.
  • 115 tests, no network required. Vitest unit tests plus in-memory MCP protocol tests.

Quickstart

Requires Node.js 20.18.1 or newer (inherited from cheerio, which needs undici 7).

bash
# run it without installingnpx -y mcp-server-web-fetcher
# or install globallynpm install -g mcp-server-web-fetchermcp-server-web-fetcher

The server speaks MCP over stdio, so on its own it just waits for a client. Point a client at it:

Claude Desktop

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

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

With configuration, using a global install:

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

Restart Claude Desktop, then ask it to summarise a URL.

Claude Code

bash
claude mcp add web-fetcher -- npx -y mcp-server-web-fetcher

Kiro, Cursor, Windsurf and other MCP clients

Same shape, in .kiro/settings/mcp.json / the client's MCP config file:

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

MCP Registry

The server is listed in the official MCP Registry as io.github.vojtisprime11/web-fetcher, so clients that read the registry can discover it directly:

bash
curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.vojtisprime11/web-fetcher"

Docker

bash
docker build -t mcp-server-web-fetcher .
json
{  "mcpServers": {    "web-fetcher": {      "command": "docker",      "args": ["run", "--rm", "-i", "mcp-server-web-fetcher"]    }  }}

The image runs as the unprivileged node user and carries production dependencies only. CI builds it and drives it with a real MCP client, so it is verified to start and answer introspection.

From source

bash
git clone https://github.com/vojtisprime11/mcp-server-web-fetcher.gitcd mcp-server-web-fetchernpm installnpm run buildnode dist/index.js          # or: npm run inspect
json
{  "mcpServers": {    "web-fetcher": {      "command": "node",      "args": ["/absolute/path/to/mcp-server-web-fetcher/dist/index.js"]    }  }}

Tools

All three tools are read-only, idempotent and open-world (annotated as such in the protocol), and all take an absolute http(s) URL.

fetch_page_markdown

Downloads a page and returns clean, LLM-friendly Markdown.

ParameterTypeDefaultDescription
urlstringrequiredAbsolute http(s) URL.
maxLengthinteger 500–100000025000Markdown characters to return per call.
startIndexinteger ≥ 00Character offset; use nextStartIndex from the previous call.
mainContentOnlybooleantrueDrop nav/header/footer/sidebar, keep the densest block.
includeLinksbooleantrueKeep Markdown links (false inlines the text only).
includeImagesbooleanfalseKeep images as ![alt](src).
includeMetadatabooleantrueAttach a short metadata summary.
timeoutMsinteger 1000–120000env / 15000Per-request timeout.

Request:

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

structuredContent:

json
{  "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"  }}

To read the rest, call again with "startIndex": 7984. Keep going while nextStartIndex is not null.

extract_metadata

Everything a model needs to classify a page, without spending context on its body.

ParameterTypeDefaultDescription
urlstringrequiredAbsolute http(s) URL.
includeJsonLdbooleantrueInclude parsed JSON-LD blocks (malformed ones are skipped).
includeHeadingsbooleantrueInclude the h1–h6 outline.
includeHttpHeadersbooleantrueInclude response headers, lower-cased.
timeoutMsinteger 1000–120000env / 15000Per-request timeout.
json
{  "name": "extract_metadata",  "arguments": { "url": "https://example.com/blog/caching", "includeJsonLd": true }}

structuredContent (abridged):

json
{  "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

ParameterTypeDefaultDescription
urlstringrequiredAbsolute http(s) URL.
scopeall | internal | externalallSame-site, off-site, or both.
includeAnchorsbooleanfalseInclude in-page #fragment links.
deduplicatebooleantrueCollapse repeated URLs.
limitinteger 1–2000200Maximum links returned.
timeoutMsinteger 1000–120000env / 15000Per-request timeout.
json
{  "name": "extract_links",  "arguments": { "url": "https://example.com/blog/caching", "scope": "external", "limit": 50 }}

structuredContent:

json
{  "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 and example.com count as the same site; blog.example.com does not.

Error handling

Failures come back as MCP tool errors (isError: true), never as silent empty results:

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

structuredContent.error carries the same information as JSON: { code, message, retryable, url, status }.

CodeMeaning
INVALID_URLNot an absolute URL.
BLOCKED_SCHEMEScheme other than http/https.
BLOCKED_HOSTLoopback, private, link-local or metadata address (SSRF guard).
DNS_FAILUREHost does not resolve.
TIMEOUTRequest exceeded timeoutMs.
HTTP_ERRORResponse status ≥ 400, or a redirect without Location.
TOO_MANY_REDIRECTSRedirect chain longer than the limit.
RESPONSE_TOO_LARGEDeclared body larger than the byte cap.
UNSUPPORTED_CONTENT_TYPENot a text/HTML/XML/JSON document.
ROBOTS_DISALLOWEDBlocked by robots.txt (only when enforcement is on).
NETWORK_ERRORConnection reset, TLS failure, and similar.
PARSE_ERRORDocument could not be parsed.

Timeouts, 5xx, 408 and 429 responses are retried once automatically before the error surfaces.

Configuration

Everything is optional; the defaults are safe.

VariableDefaultDescription
WEB_FETCHER_USER_AGENTmcp-server-web-fetcher/<version> (+repo url)Outgoing User-Agent.
WEB_FETCHER_TIMEOUT_MS15000Default timeout (1000–120000).
WEB_FETCHER_MAX_BYTES5000000Per-response byte cap (10000–50000000).
WEB_FETCHER_MAX_REDIRECTS5Redirect hops allowed (0–20).
WEB_FETCHER_ALLOW_PRIVATE_HOSTSfalseSet to true only to fetch localhost/LAN URLs on purpose.
WEB_FETCHER_RESPECT_ROBOTSfalseEnforce robots.txt (RFC 9309 subset) before fetching.
WEB_FETCHER_CACHE_TTL_MS60000Response cache TTL; 0 disables caching.
WEB_FETCHER_CACHE_MAX_ENTRIES50Maximum cached responses.

Security

  • SSRF guard on by default. Hostnames are resolved and checked against loopback, RFC 1918, CGNAT, link-local (including 169.254.169.254), multicast and IPv4-mapped IPv6 ranges — for the initial URL and every redirect target. localhost, *.localhost and *.internal are refused outright. Turning the guard off is an explicit opt-in.
  • Byte and time caps. Responses are streamed and cut at the byte cap; every request is aborted at the timeout. Content-Length larger than the cap is rejected before download.
  • No code execution. JavaScript is never run; <script>, <style>, <iframe> and friends are removed before conversion. Fetched content is data, not instructions — treat page text reaching a model as untrusted input.
  • Credentials stripped. user:pass@ is removed from URLs before the request is made.
  • Read-only. The server has no write, filesystem or shell surface, and no authentication state.

Found a vulnerability? See SECURITY.md.

Architecture

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 cachetests/                    # unit tests + in-memory MCP protocol tests

Tool handlers take an injectable fetchImpl, so every test runs offline against a scripted HTTP stub. The library is also importable directly:

ts
import { runFetchPageMarkdown } from 'mcp-server-web-fetcher';
const page = await runFetchPageMarkdown({ url: 'https://example.com' });console.log(page.markdown);

Development

The minimum supported Node.js version is 20.18.1, which comes from cheerio (it depends on undici 7, and undici 7 needs the File global that landed in Node 20). CI runs the suite on Node 20/22/24 and has a separate job that completes an MCP handshake against the built server on exactly 20.18.1, so the declared floor is verified rather than assumed.

bash
npm installnpm run dev            # tsx watchnpm run typechecknpm run lintnpm test               # vitest runnpm run test:coverage  # thresholds enforcednpm run buildnpm run inspect        # MCP Inspector against dist/index.jsnode scripts/smoke.mjs https://example.com   # live end-to-end check

Contributions welcome — see CONTRIBUTING.md and CODE_OF_CONDUCT.md.

Limitations

  • Static HTML only. Client-rendered SPAs return whatever the server sends; no headless browser.
  • No PDF or Office document extraction.
  • Sites behind bot protection (Cloudflare challenges, login walls) will return 403.
  • Main-content detection is a text-density heuristic. Use mainContentOnly: false when a page has an unusual structure.

Roadmap

  • search_page tool: return only the sections matching a query.
  • Optional headless rendering behind a flag, for JS-only pages.
  • Streamable HTTP transport in addition to stdio.
  • Conditional requests (ETag / If-Modified-Since) in the cache layer.

Acknowledgements

Built on the MCP TypeScript SDK, cheerio, turndown and zod.

License

MIT © Vojta Holes and contributors

來源:README.md,提交 3458d5e

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

1
  1. v0.1.0最新Sep 16, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分

其他 Fetcher 整合