web-fetcher について
web-fetcher は、ウェブページを Markdown、構造化メタデータ、分類済みリンクに変換する読み取り専用 MCP サーバーです。LLM の処理向けに、ページ取得時の分割読み込みと SSRF 対策を備えています。
主な機能
- 01ページ内容を簡潔な Markdown に変換
- 02長いページの分割取得
- 03構造化メタデータと見出しの抽出
- 04リンクの分類と絞り込み
- 05プライベート・ループバックホストの遮断
活用シーン
- 01ページの余分な要素を除いて記事を読む
- 02本文を取得せずにページ情報を確認
- 03内部または外部リンクを収集
- 04応答上限を超えるページの続きを読む
MCP 対応
stdio 経由で、ページの Markdown 取得、メタデータ抽出、リンク抽出を行う3つの読み取り専用 MCP ツールを提供します。各ツールは絶対 HTTP または HTTPS URL を受け取り、構造化データを返します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
npx -y mcp-server-web-fetcherWEB_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 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
3掲載時のツール一覧 · 2 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| url | string | はい | 取得するページの絶対 http(s) URL。 |
| maxLength | integer | いいえ | 1 回の呼び出しで返す Markdown 文字数の上限。 |
| timeoutMs | integer | いいえ | リクエストのタイムアウト時間(ミリ秒、1000~120000)。 |
| startIndex | integer | いいえ | 開始する文字オフセット。長いページをページ分割して取得するには `nextStartIndex` を使用します。 |
| includeLinks | boolean | いいえ | Markdown リンクを保持します。false の場合、リンクテキストはインラインのプレーンテキストになります。 |
| includeImages | boolean | いいえ | 画像を Markdown の画像構文で保持します。 |
| includeMetadata | boolean | いいえ | 簡潔なメタデータの概要(タイトル、説明、canonical、言語)を含めます。 |
| mainContentOnly | boolean | いいえ | ナビゲーション、ヘッダー、フッター、サイドバーを除去し、最も内容が密集したブロックを残します。 |
詳細
mcp-server-web-fetcher
あらゆるウェブページを、言語モデルが実際に読み取れる形式、つまり整った Markdown、構造化メタデータ、分類済みリンク一覧に変換する、高速で依存関係の少ないModel Context Protocolサーバーです。
ウェブページの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 以降が必要です(undici 7 を必要とする cheerio に由来します)。
# 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"]
}
}
}
ツール
3 つのツールはすべて読み取り専用で、べき等かつオープンワールド(プロトコルでそのように注釈されています)であり、いずれも絶対 http(s) URL を受け取ります。
fetch_page_markdown
ページをダウンロードし、LLM が扱いやすいクリーンな Markdown を返します。
| パラメーター | 型 | デフォルト | 説明 |
|---|---|---|---|
url | string | required | 絶対 http(s) URL。 |
maxLength | integer 500–1000000 | 25000 | 呼び出しごとに返す Markdown の文字数。 |
startIndex | integer ≥ 0 | 0 | 文字オフセット。前回の呼び出しの nextStartIndex を使用します。 |
mainContentOnly | boolean | true | ナビゲーション、ヘッダー、フッター、サイドバーを除外し、最も情報量の多いブロックを残します。 |
includeLinks | boolean | true | Markdown リンクを保持します(false の場合はテキストのみをインライン化します)。 |
includeImages | boolean | false | 画像を  として保持します。 |
includeMetadata | boolean | true | 短いメタデータの概要を付加します。 |
timeoutMs | integer 1000–120000 | env / 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
ページ本文にコンテキストを使うことなく、そのページを分類するためにモデルが必要とするすべての情報を返します。
| パラメーター | 型 | デフォルト | 説明 |
|---|---|---|---|
url | string | required | 絶対 http(s) URL。 |
includeJsonLd | boolean | true | 解析済みの JSON-LD ブロックを含めます(形式が不正なものはスキップされます)。 |
includeHeadings | boolean | true | h1〜h6 の見出し構成を含めます。 |
includeHttpHeaders | boolean | true | レスポンスヘッダーを小文字にして含めます。 |
timeoutMs | integer 1000–120000 | env / 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
| パラメーター | 型 | デフォルト | 説明 |
|---|---|---|---|
url | string | required | 絶対 http(s) URL。 |
scope | all | internal | external | all | 同一サイト、外部サイト、または両方。 |
includeAnchors | boolean | false | ページ内の #fragment リンクを含めます。 |
deduplicate | boolean | true | 重複する URL をまとめます。 |
limit | integer 1–2000 | 200 | 返すリンクの最大数。 |
timeoutMs | integer 1000–120000 | env / 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 })が含まれます。
| コード | 意味 |
|---|---|
INVALID_URL | 絶対 URL ではありません。 |
BLOCKED_SCHEME | 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 のレスポンスは、エラーが表面化する前に自動で 1 回再試行されます。
設定
すべての設定は省略可能で、デフォルト値は安全です。
| 変数 | デフォルト | 説明 |
|---|---|---|
WEB_FETCHER_USER_AGENT | mcp-server-web-fetcher/<version> (+repo url) | 送信する User-Agent です。 |
WEB_FETCHER_TIMEOUT_MS | 15000 | デフォルトのタイムアウト(1000~120000)です。 |
WEB_FETCHER_MAX_BYTES | 5000000 | レスポンスごとのバイト数の上限(10000~50000000)です。 |
WEB_FETCHER_MAX_REDIRECTS | 5 | 許可されるリダイレクトの回数(0~20)です。 |
WEB_FETCHER_ALLOW_PRIVATE_HOSTS | false | localhost/LAN の URL を意図的に取得する場合のみ、true に設定してください。 |
WEB_FETCHER_RESPECT_ROBOTS | false | 取得前に robots.txt(RFC 9309 のサブセット)を適用します。 |
WEB_FETCHER_CACHE_TTL_MS | 60000 | レスポンスキャッシュの TTL です。0 にするとキャッシュが無効になります。 |
WEB_FETCHER_CACHE_MAX_ENTRIES | 50 | キャッシュするレスポンスの最大数です。 |
セキュリティ
- SSRF ガードはデフォルトで有効です。 ホスト名を解決し、ループバック、RFC 1918、
CGNAT、リンクローカル(
169.254.169.254を含む)、マルチキャスト、IPv4 マップ IPv6 の範囲に該当しないか確認します。初期 URL とすべてのリダイレクト先が対象です。localhost、*.localhost、*.internalは無条件に拒否されます。ガードを無効にするには、明示的なオプトインが必要です。 - バイト数と時間に上限があります。 レスポンスはストリーミングされ、バイト数の上限に達すると打ち切られます。各リクエストはタイムアウト時に中止されます。上限を超える Content-Length は、ダウンロード前に拒否されます。
- コードは実行されません。 JavaScript は実行されず、変換前に
<script>、<style>、<iframe>などが削除されます。取得したコンテンツはデータであり、指示ではありません。ページのテキストがモデルに渡る場合は、信頼できない入力として扱ってください。 - 認証情報は除去されます。 リクエストを行う前に、URL から
user:pass@が取り除かれます。 - 読み取り専用です。 サーバーには書き込み、ファイルシステム、シェルの機能がなく、認証状態も保持しません。
脆弱性を発見しましたか? 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ツール:クエリに一致するセクションのみを返します。- JavaScript のみで構成されたページ向けに、フラグで有効化できるオプションのヘッドレスレンダリング。
- stdio に加えて、Streamable HTTP トランスポートに対応。
- キャッシュレイヤーでの条件付きリクエスト(
ETag/If-Modified-Since)。
謝辞
MCP TypeScript SDK、 cheerio、turndown、 zod を基盤として構築されています。
ライセンス
MIT © Vojta Holes およびコントリビューター
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。