Fetch MCP Server

io.github.YawLabs由 GitHub 账号 YawLabs 发布v0.8.6更新于 Oct 31, 2026

HTTP fetch MCP server: SSRF protection, HTML-to-markdown, reader mode, metadata extraction

概览

AI 生成的概览

本地 stdio MCP 服务器,让助手发起 HTTP 请求,并把网页、订阅源、站点地图和 robots.txt 转成干净的文本、Markdown 与元数据。

可以做什么
  • 用任意方法(GET、POST、PUT、PATCH、DELETE、HEAD、OPTIONS)发送 HTTP 请求,支持自定义请求头、Basic 或 Bearer 认证、超时、大小上限与重试
  • 把抓取的 HTML 转成干净的 Markdown 或纯文本,或用阅读模式只提取正文
  • 提取页面头部元数据:标题、描述、canonical、Open Graph、Twitter Card、JSON-LD、图标与订阅源链接
  • 提取页面所有外链,解析为绝对地址并区分站内与站外
  • 解析 sitemap.xml(含 gzip 与 sitemap-index 链式展开)以及 RSS 2.0 或 Atom 订阅源
  • 检查站点 robots.txt,并针对指定路径与 user-agent 给出允许或拒绝的判定
适用场景
适合助手需要阅读网页、把文章正文或元数据放进上下文、查看 API,或遍历站点地图与订阅源的场景。适用于本地 MCP 客户端中的资料调研、内容提取和轻量 API 测试。
运行要求
以 stdio 方式在本地运行,通常通过 npx 或全局 npm 安装;在 Node 上运行需要 Node 22.19 或更高版本。未声明任何账号、API 密钥或请求头。需要能访问目标主机的网络。若要访问私有或回环地址,运维方还须设置 FETCH_MCP_ALLOW_PRIVATE_HOSTS。
安装前请注意
写方法(POST、PUT、PATCH、DELETE)可能修改或删除远端数据,且 POST 不具幂等性。通过 basic_auth 或 bearer_token 传入的凭据会发送给目标主机,认证头仅在跨源重定向时被剥离。开启 FETCH_MCP_ALLOW_PRIVATE_HOSTS 会让被提示注入的请求触达内网与云元数据端点,在云虚拟机上应保持关闭。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Fetch MCP Server,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

@yawlabs/fetch-mcp

[Add to Yaw MCP]

One click adds this to your local Yaw MCP config so it's available in every Yaw Terminal session. Or install manually below.

A comprehensive HTTP fetch MCP server for AI assistants. Bring-your-own client: runs as a stdio MCP server so any MCP-compatible client (Claude Code, Claude Desktop, Cursor, mcph, …) can fetch web content safely.

What it gives the model

ToolWhat it does
http_get / http_head / http_optionsBare HTTP requests with headers, auth, timeout, size cap, retry
http_post / http_put / http_patch / http_deleteWrite-method HTTP with JSON or raw body
fetch_html_to_markdownGET a page and convert to clean markdown (3–8× smaller than raw HTML)
fetch_html_to_textGET a page and convert to plain text with block structure preserved
fetch_readerReader-mode extraction — isolates the article body and returns title + markdown
fetch_metaExtract <head> metadata: title, description, OpenGraph, Twitter cards, JSON-LD, feeds, icons
fetch_linksExtract every outbound link, resolved to absolute URLs, classified internal/external
fetch_sitemapParse sitemap.xml (including gzipped and sitemap-index chaining)
fetch_feedParse an RSS 2.0 or Atom 1.0 feed into entries
fetch_robotsParse a site's robots.txt, return the verdict for a given path & user-agent

Safety

SSRF protection is on by default. The server refuses requests to:

  • Loopback (127.0.0.0/8, ::1)
  • RFC1918 private ranges (10/8, 172.16/12, 192.168/16)
  • Link-local (169.254/16, fe80::/10) — including the cloud metadata endpoint 169.254.169.254
  • CGNAT (100.64/10)
  • Unique-local IPv6 (fc00::/7)
  • Multicast / broadcast
  • IPv4-mapped IPv6 (::ffff:0:0/96) re-checked against the IPv4 rules, and likewise the IPv4 embedded in IPv4-compatible (::/96), IPv4-translated (::ffff:0:0:0/96) and 6to4 (2002::/16) addresses
  • Teredo (2001::/32), NAT64 (64:ff9b::/96, 64:ff9b:1::/48), site-local (fec0::/10) and discard-only (100::/64) IPv6
  • Any other IPv6 outside global unicast (2000::/3), and the non-global blocks inside it (3fff::/20, 2001:2::/48, 2001:10::/28)
  • The Azure WireServer address 168.63.129.16
  • Non-http/https schemes (file://, gopher://, javascript:, …)
  • Hostname localhost and any *.localhost

Every redirect hop is re-validated against all of the above -- scheme, literal IP and localhost* -- before it is dialed, so a 302 from a public host to http://127.0.0.1, http://169.254.169.254 or ftp://… is caught. For hostnames, DNS is resolved once per hop, every returned address is checked, and the verified IP is pinned into the HTTP dispatcher so the subsequent TCP connection dials that exact address — closing the DNS-rebinding TOCTOU window. Authorization, Cookie and Proxy-Authorization headers are stripped on cross-origin redirects.

Reaching private hosts (development)

The model chooses tool arguments, and the model is not trusted: a prompt injected through a fetched page can ask for anything. So the per-call allow_private_hosts: true opt-in is refused unless the operator enables it by launching the server with FETCH_MCP_ALLOW_PRIVATE_HOSTS=1 (true/yes/on also work; any unrecognised value is treated as off and named on stderr). With the variable set, a call still has to ask: requests without allow_private_hosts stay guarded.

json
{  "mcpServers": {    "fetch": {      "command": "npx",      "args": ["-y", "@yawlabs/fetch-mcp@latest"],      "env": { "FETCH_MCP_ALLOW_PRIVATE_HOSTS": "1" }    }  }}

Only set it where the model may legitimately reach your internal network -- a local dev box, not a cloud VM with a metadata endpoint.

Install & run

bash
# One-off (auto-updates each spawn via @latest)npx -y @yawlabs/fetch-mcp@latest
# Or globallynpm i -g @yawlabs/fetch-mcpfetch-mcp

Requires Node 22.19 or newer when running on Node (the floor of its HTTP client, undici 8; the launcher refuses older Nodes with a clear message instead of crashing). Under oam, which the launcher prefers when installed, the Node version does not matter.

Configure in Claude Code / Claude Desktop

Add to your client's MCP config (usually claude_desktop_config.json or ~/.claude.json):

json
{  "mcpServers": {    "fetch": {      "command": "npx",      "args": ["-y", "@yawlabs/fetch-mcp@latest"]    }  }}

Or via mcph:

bash
mcph add fetch

Tool reference

http_get, http_post, http_put, http_patch, http_delete, http_head, http_options

Common parameters:

FieldTypeDefaultMeaning
urlstring—Absolute URL
headersobject—Custom request headers
timeout_msint10000Timeout for each attempt, covering DNS, every redirect hop and the body. Retries get a fresh budget; a whole call is capped at 5 minutes. Cancelling the tool call stops the request
max_bytesint5242880 (5 MiB)Truncate body if larger
max_redirectsint5Redirect hops allowed
retriesint0Retry count on 408/425/429/5xx with backoff (honors Retry-After)
user_agentstring@yawlabs/fetch-mcp/<v>User-Agent override
basic_auth{username,password}—Injects Authorization: Basic …
bearer_tokenstring—Injects Authorization: Bearer …
allow_private_hostsboolfalseOpt this call into loopback / private / link-local targets. Refused unless the operator set FETCH_MCP_ALLOW_PRIVATE_HOSTS=1 (details)
decode_textboolautoWhen unset, auto-detects by response Content-Type (text for text/*, JSON, XML, JS, form-urlencoded; binary otherwise). Set explicitly true to force text decoding, false to force base64 in body_base64.

Body-capable tools (POST/PUT/PATCH/DELETE) also take:

FieldTypeMeaning
bodystringRaw request body
body_jsonanyStructured body — encoded as JSON, Content-Type: application/json set automatically
content_typestringOverrides Content-Type

Response shape:

ts
{  ok: boolean;  status: number;  statusText: string;  url: string;              // final URL after redirects  headers: Record<string,string>;  body_text?: string;  body_base64?: string;     // when decode_text=false  json?: unknown;           // auto-parsed when response is application/json  truncated?: boolean;      // set when max_bytes hit  redirects?: string[];     // chain of intermediate URLs  duration_ms: number;  error?: string;}

fetch_html_to_markdown

GET the URL, strip scripts/styles/iframes/svg/canvas plus <nav>, <footer>, <aside>, convert to atx-headed markdown with fenced code blocks and dash bullets. Intended for feeding pages into an LLM without blowing the context budget.

fetch_html_to_text

Same fetch, but emits plain text with block-level structure preserved as newlines. Useful when the model doesn't need markdown formatting.

fetch_reader

Isolates the main article body using, in order: <article>, <main>, itemprop="articleBody", common CMS class names (post-content, entry-content, etc.), then <body> as fallback. Returns:

ts
{  url: string;          // final URL after redirects  title?: string;       // og:title, then <title>, then <h1>  byline?: string;      // meta[name=author] / article:author  wordCount: number;  markdown: string;     // main content converted to markdown}

fetch_meta

GET a URL and return its head metadata without downloading the full body (caps at 2 MiB by default):

ts
{  url: string;  title?: string;  description?: string;  canonical?: string;  language?: string;  robots?: string;  og:      Record<string, string>;       // first value per key (og:title, og:image, og:type, ...)  twitter: Record<string, string>;       // first value per key  article: Record<string, string>;       // first value per key  ogAll:      Record<string, string[]>;  // only keys that appear > once (e.g. multiple og:image)  twitterAll: Record<string, string[]>;  articleAll: Record<string, string[]>;  icons:   Array<{ rel: string; href: string; sizes?: string }>;  feeds:   Array<{ href: string; title?: string; type?: string }>;   // RSS/Atom  jsonLd:  unknown[];                    // parsed application/ld+json blocks}

fetch_links

GET a page and return every <a href> with text, resolved to absolute URLs. Respects <base href>. Skips #, javascript:, mailto:, tel:, data:, file:. Each link is classified internal or external vs. the page host. Optional filter/dedupe/limit.

fetch_sitemap

Fetch a sitemap.xml or sitemap-index and return the URL list:

ts
{  sitemaps: string[];       // indexes followed, in order  urlCount: number;  truncated: boolean;       // hit max_urls  childSitemaps: string[];  // listed but not fetched (past max_depth or max_sitemaps)  warnings: Array<{ url: string; error: string }>;  urls: Array<{    loc: string;    lastmod?: string;    changefreq?: string;    priority?: number;  }>;}

Gzipped .xml.gz sitemaps are auto-decompressed. max_depth controls how many levels of sitemap-index to follow (default 1). Setting max_depth: 0 on a sitemap-index returns the index's childSitemaps list without fetching any child (useful to discover structure cheaply). Partial failures — one child sitemap 500s while others succeed — are returned under warnings rather than aborting the whole call. max_sitemaps (default 50, max 1000) caps how many sitemap documents one call fetches, index included; children past it are listed under childSitemaps with a warning. The whole call is capped at 5 minutes. max_bytes (default 20 MiB) applies to each sitemap, and to the decompressed size of a gzipped one; a sitemap larger than that is an error (a warning for a child) rather than a silently partial URL list.

fetch_feed

Parse an RSS 2.0 or Atom 1.0 feed:

ts
{  kind: "rss" | "atom" | "unknown";  title?: string;  description?: string;  link?: string;  updated?: string;  entryCount: number;  truncated: boolean;       // hit limit  entries: Array<{    title?: string;    link?: string;    id?: string;    published?: string;    updated?: string;    author?: string;    summary?: string;    content?: string;    categories?: string[];  }>;}

fetch_robots

Fetches <origin>/robots.txt, parses it, and returns:

ts
{  robotsUrl: string;  status: number;  userAgent: string;  path: string;  allowed: boolean;  matchedRule: string | null;  // the longest-match Allow/Disallow that decided it  crawlDelay: number | null;   // from the matched group  sitemaps: string[];          // top-level Sitemap: declarations  rawRobotsText: string;       // first 512KB  note?: string;               // only when robots.txt 404s}

A /robots.txt that returns 404 means there are no rules, so the verdict is allowed: true. The response keeps the same shape: status: 404, matchedRule: null, crawlDelay: null, sitemaps: [], rawRobotsText: "", plus the note. Any other non-2xx status is returned as an error.

The parser follows Google's rules: longest match wins, * is a wildcard segment, $ anchors the end of the path, specific user-agent group beats the * wildcard group when the UA matches (comparison uses the length of the actually-matched agent token, not the group's first agent). Allow beats Disallow on equal-length ties.

Development

bash
npm installnpm run buildnpm test         # vitestnpm run lint     # biomenpm run typecheck

Tests spin up a local loopback HTTP server on 127.0.0.1:0 to exercise the real request/response path — no mocking of HTTP. SSRF tests verify that the default-deny still applies to that local server unless the operator gate is on and the request opts into allow_private_hosts.

License

MIT © Yaw Labs

Links

[Follow @YawLabs on X]

来源:README.md,提交 4e76a9d

工具

15

http_get

Perform an HTTP GET. Returns status, headers, and body. Automatically parses JSON when the server responds with application/json. Follows redirects (each hop re-validated against SSRF rules). Refuses URLs that resolve to private/loopback/link-local addresses unless the operator enabled FETCH_MCP_ALLOW_PRIVATE_HOSTS and the call sets allow_private_hosts.

输入参数url必填headersretriesmax_bytesbasic_authtimeout_msuser_agentdecode_textbearer_tokenmax_redirectsallow_private_hosts

http_post

Perform an HTTP POST. Body can be given as a raw string (body) or as a JSON value (body_json — auto-sets content-type to application/json). NOT idempotent: calling twice submits twice.

输入参数url必填bodyheadersretriesbody_jsonmax_bytesbasic_authtimeout_msuser_agentdecode_textbearer_tokencontent_type另有 2 个

http_put

Perform an HTTP PUT. Use for full-resource replacement. Idempotent: the same PUT applied twice leaves the resource in the same state.

输入参数url必填bodyheadersretriesbody_jsonmax_bytesbasic_authtimeout_msuser_agentdecode_textbearer_tokencontent_type另有 2 个

http_patch

Perform an HTTP PATCH. Use for partial updates. Spec-wise NOT guaranteed idempotent — depends on the server's patch semantics.

输入参数url必填bodyheadersretriesbody_jsonmax_bytesbasic_authtimeout_msuser_agentdecode_textbearer_tokencontent_type另有 2 个

http_delete

Perform an HTTP DELETE. Idempotent (per spec): a repeated DELETE on an already-deleted resource typically returns 404/410.

输入参数url必填bodyheadersretriesbody_jsonmax_bytesbasic_authtimeout_msuser_agentdecode_textbearer_tokencontent_type另有 2 个

http_head

Perform an HTTP HEAD. Returns status + headers with no body. Useful for checking a resource exists, getting its size (Content-Length), or polling for changes cheaply.

输入参数url必填headersretriesmax_bytesbasic_authtimeout_msuser_agentdecode_textbearer_tokenmax_redirectsallow_private_hosts

http_options

Perform an HTTP OPTIONS. Returns the server's supported methods and CORS policy for a resource. Helpful for API discovery.

输入参数url必填headersretriesmax_bytesbasic_authtimeout_msuser_agentdecode_textbearer_tokenmax_redirectsallow_private_hosts

fetch_html_to_markdown

GET a URL, decode the HTML, and convert to clean markdown (headings, lists, links, code fences). Scripts, styles, iframes, nav, footer, and aside elements are stripped. Intended for feeding web pages into an LLM cheaply -- markdown is usually 3-8x smaller than raw HTML. Follows redirects, respects size/timeout limits, and blocks private-host requests by default.

输入参数url必填max_bytestimeout_msuser_agentmax_redirectsallow_private_hosts

fetch_html_to_text

GET a URL, decode the HTML, and return plain text with block-level structure preserved as newlines. Scripts, styles, and comments stripped; HTML entities decoded. Lighter than markdown when you only need the reading content.

输入参数url必填max_bytestimeout_msuser_agentmax_redirectsallow_private_hosts

fetch_robots

Fetch and parse the robots.txt for a given origin, then tell the caller whether a target URL is crawlable by a given user-agent. Follows the Google-style longest-match rule with Allow-wins-on-tie. Returns the raw robots.txt, parsed groups, sitemap references, and the allow/deny verdict with the matching rule.

输入参数url必填timeout_msuser_agentmax_redirectsallow_private_hosts

fetch_sitemap

Fetch a sitemap.xml (or sitemap-index) and return the contained URLs with their lastmod / changefreq / priority. Follows sitemap-index chaining up to max_depth levels. Gzipped .xml.gz payloads are auto-decompressed. A single sitemap document over 16 MiB once decoded is refused (a 50,000-URL sitemap is ~11 MiB). Partial failures (one child sitemap 500s while others work) are returned under 'warnings' without aborting the whole request. SSRF-protected by default.

输入参数url必填max_urlsmax_bytesmax_depthtimeout_msuser_agentmax_sitemapsmax_redirectsallow_private_hosts

fetch_meta

GET a URL and extract its head metadata: title, description, canonical, language, robots directive, Open Graph / Twitter Card / article: properties, icon links, RSS/Atom feed links, and any JSON-LD (schema.org) blocks. Keys that appear more than once (e.g. multiple og:image tags) are additionally returned via ogAll/twitterAll/articleAll arrays. Lists are capped: at most 100 icons, feeds, JSON-LD blocks and values per *All key, and 1000 keys per og/twitter/article; a JSON-LD block nested deeper than 64 levels is dropped. When anything is cut, `truncated` names the collections affected. Ideal for previewing a page before fully reading it.

输入参数url必填max_bytestimeout_msuser_agentmax_redirectsallow_private_hosts

fetch_feed

Fetch and parse an RSS 2.0 or Atom 1.0 feed. Returns feed-level metadata (title, description, link, updated) plus a list of entries (title, link, id, published, updated, author, summary, content, contentType, categories). Auto-detects RSS vs Atom.

输入参数url必填limitmax_bytestimeout_msuser_agentmax_redirectsallow_private_hosts

fetch_reader

GET a URL, locate the main article body (prefers <article>, <main>, itemprop=articleBody, or known CMS class names; falls back to <body>), strip navigation/footer/aside chrome, and convert to clean markdown. Returns { title, byline, markdown, wordCount }. Optimized for feeding long-form articles into an LLM without header/footer/sidebar noise.

输入参数url必填max_bytestimeout_msuser_agentmax_redirectsallow_private_hosts

版本历史

10
  1. v0.8.6最新Oct 31, 2026
  2. v0.8.5Oct 11, 2026
  3. v0.8.4Oct 8, 2026
  4. v0.8.3Oct 7, 2026
  5. v0.8.2Sep 30, 2026
  6. v0.8.1Sep 24, 2026
  7. v0.8.0Sep 22, 2026
  8. v0.7.1Sep 21, 2026
  9. v0.7.0Sep 17, 2026
  10. v0.6.2Sep 16, 2026

评分

已满足适用检查项的 90%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分
  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具必需15 分
  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分