# amenbo 🐜💧 [English](./README.en.md) | 日本語 [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.21553636.svg)](https://doi.org/10.5281/zenodo.21553636) **Skims the web without making waves** — a Japanese-web-native MCP server for low-impact, token-efficient web collection: outline→section progressive disclosure and diff-only refetches keep context small. amenbo(アメンボ / water strider)は、Claude Code や Codex のようなコーディングエージェント向けの [MCP](https://modelcontextprotocol.io) サーバーです。水面に波を立てずに滑る虫のように、**収集先に負荷をかけず、少ないトークンで** Web から情報を集めます。とりわけ**日本語サイト**に最適化しています。MCP クライアントを持たないシェル環境からは、同じコアを共有する CLI としても使えます([CLIとして使う](#cliとして使う)参照)。 ## なぜ amenbo か 汎用のスクレイピングツールの多くは英語圏の Web を前提に作られており、日本語サイトでは次のような取りこぼしが起きがちです。amenbo はこれらの課題に対応します。 - **構造化が甘いサイト**:div の入れ子やテーブルレイアウトが多い日本語サイトでも、レンダリング結果のジオメトリ(見た目の配置)から本文領域を推定します - **文字化け**:Shift_JIS / EUC-JP / ISO-2022-JP を自動判別 - **ふりがな**:`` の振り仮名を除去し、本文の二重化を防止 - **画像で出す情報**:画像化された料金表やバナー中心のページは、テキスト抽出が貧弱なとき自動でスクリーンショットに切り替え - **表の欠落・崩れ**:リンク密度の高いデータ表(比較表など)は本文抽出時に丸ごと落ちることがあり、そうした表を検出して元の位置へ復元します。本文に残った表も colspan/rowspan・多段ヘッダを正規化し、列ズレを防ぎます - **見出しの消失**:見出しが編集リンク付きラッパーに包まれたページ(Wiki系など)では本文抽出時に見出し構造が丸ごと失われることがあり、本文が残っている節の見出しを検出して元の位置へ復元します(outline / section の段階開示が安定) - **国内主要サイト**:Qiita / Zenn / note / はてなブログ / Yahoo!ニュース / PR TIMES に専用アダプタ 類似ツール(公式 fetch MCP / Jina Reader / Playwright MCP / PixelRAG pixelshot)との実測比較は、記事「[エージェントのWeb取得、ツール次第でトークンが5000倍違った話](https://zenn.dev/rererr_engineer/articles/e571e5b6eb1d53)」を参照してください。ハーネスと生ログは [`bench/`](https://github.com/Rererr/amenbo/blob/main/bench/) にあります。 ## トークンを節約する仕組み - **段階開示**:`mode: outline` で見出しツリーと各節のトークン量だけ先に返し、必要な節だけ `section` 指定で取得。長大なページを丸ごと流し込みません - **CJK 対応の本文プルーニング**:句読点密度、文字種比率、リンク密度でナビ/広告/フッターを除去 - **差分応答**:一度取得した URL の再取得時、変更が無ければ `unchanged`、あれば変更された節だけを返します - **自動 Markdown/画像切替**:品質スコアが低いページだけスクリーンショットにし、壊れた Markdown を読ませて取り直す往復を避けます - **文字種別のトークン見積り**:日本語・韓国語・キリル文字・絵文字などは英語よりトークン単価が重いため、文字クラス別の係数(実測で校正)でページ分割の予算を計算します ## 収集先への低負荷 - **二段フェッチ**:まず素の HTTP GET。JS 描画が必要なページだけ headless Chromium に昇格するので、大半の取得でブラウザを起動しません - **礼儀正しいクローラ**:robots.txt と Crawl-Delay を尊重、同一ドメインへは直列 + 既定 1 req/秒(robots.txt の取得もこの1リクエストとして数えます)。リンク列挙は sitemap / RSS を優先しページを舐めません - **正直な User-Agent**:ボットであることを明示します。**anti-bot 回避は実装しません** - **キャッシュ**:ETag / If-Modified-Since で再検証し、無駄な再取得を避けます。有効期限は既定 15 分(`AMENBO_CACHE_TTL_MS`)で、`Cache-Control` は**延長方向のみ**採用します(`max-age` が 15 分より長ければそちらを使い、上限 24 時間。`no-store` は保存しません)。`max-age=0` や `no-cache` で期限を縮めることはしません — 主要サイトの実測ではその宣言が大半で、従うとツール呼び出しの度に取得しに行くことになり、低負荷という前提が崩れるためです ## インストール ```bash npm install -g amenbo ``` Markdown 取得(通常の `fetch` / `links`)はこれだけで動きます。JS 描画が必要な SPA への昇格やスクリーンショットなど、ブラウザ(Chromium)経由の取得を使う場合のみ、初回に一度だけ実行してください(約 170MB のダウンロード): ```bash npx -y amenbo install-browser ``` または開発用途: ```bash git clone https://github.com/Rererr/amenbo.git cd amenbo npm install npm run build ``` ### MCP クライアントへの登録 Claude Code(`--scope user` は全プロジェクト共通。プロジェクト単位なら外す): ```bash claude mcp add --scope user amenbo -- amenbo ``` Codex CLI: ```bash codex mcp add amenbo -- amenbo ``` VS Code: ```bash code --add-mcp '{"name":"amenbo","command":"amenbo"}' ``` その他のクライアント(Cursor / Cline など)は、各クライアントの MCP 設定(Cursor: `~/.cursor/mcp.json`、Cline: MCP Servers 画面の settings JSON)に次のエントリを追加します: ```json { "mcpServers": { "amenbo": { "command": "amenbo" } } } ``` グローバルインストールを避ける場合は `"command": "npx", "args": ["-y", "amenbo"]`、ローカルビルドを使う場合は `"command": "node", "args": ["/path/to/amenbo/dist/server.js"]` を指定してください。 stdio 経由で MCP の 2026-07-28(ステートレスコア)と 2025 系の両方に応答します。クライアントがどちらの版を話すかに関わらず、上記の設定のまま繋がります。 ### エージェントに使い方を教える(推奨プロンプト) ツール定義だけでは「段階開示で取る」といった使い方の作法までは伝わりません。以下を `CLAUDE.md` や `AGENTS.md` にコピペすると、エージェントが amenbo を効率よく使うようになります。 ```markdown ## Web取得は amenbo を使う - ページ取得は `fetch`(mode 既定 `auto`)。長そうなページや一部しか要らないページは、 まず `mode: "outline"` で見出しと各節のトークン量を確認し、必要な節だけ `section` 指定で取得する - 同じ URL の再取得で `unchanged` / `diff` が返るのは正常(変更なし / 変更節のみ)。 差分ではなく内容全体をもう一度受け取りたいときだけ `force_full: true` を使う - サイト内のページを探すときは URL を推測せず `links`(`filter` で絞り込み)で列挙する - シェルが使える環境で、キーワードで探したいだけの長いページや複数ページの一括収集は、 CLI で `amenbo fetch > page.md` に落として grep / 部分読みする(本文をコンテキストに入れない)。 構造を見ながら判断したいページは従来どおり MCP の outline → section が向く - 日本語以外のサイトにも使える(段階開示・キャッシュ・低負荷は言語非依存)。ただし本文抽出は 日本語向けに調整しているため、非日本語ページで本文が欠けて見えるときは `selector` 指定か `mode: "screenshot"` で取り直す - 料金表・レイアウトなど視覚情報が目的なら `screenshot`。`scale: 0.5` 程度で画像トークンを減らせる - robots.txt 拒否や bot 対策による取得失敗は仕様(回避しない)。失敗はそのままユーザーに報告する ``` `CLAUDE.md` に書かず、その場のセッションだけに読み込むこともできます。MCP プロンプト対応クライアントでは、サーバーが同じ作法を `usage` プロンプトとして配布しています(Claude Code では `/mcp__amenbo__usage`)。 ## CLIとして使う `amenbo` は MCP サーバーと同一のコア(取得、キャッシュ、politeness、抽出ロジック)を共有する CLI としても動作します。引数なし、または `amenbo serve` は従来通り MCP サーバーとして起動する(`.mcp.json` の `"command": "amenbo"` はそのまま動きます)ので、既存の MCP 登録には影響しません。 ```bash # ページをMarkdownとして取得(標準出力へ) amenbo fetch https://example.com/ # 長いページはまずoutlineで見出しとトークン量だけ確認 amenbo fetch https://example.com/ --mode outline # 出力をファイルに落として grep や部分読み(head/sed)する amenbo fetch https://example.com/ > page.md grep -A3 "料金" page.md # サイト内のリンクを列挙(sitemap/RSS優先) amenbo links https://example.com/ --filter "blog/*" # スクリーンショット(タイルPNGは--out-dirへ保存され、パスが標準出力に列挙される) amenbo screenshot https://example.com/ --viewport-only --scale 0.5 --out-dir ./shots ``` 各サブコマンドの詳細は `amenbo --help` を参照してください。 **MCP と CLI の使い分け**: - **MCP**:エージェントの主経路。ブラウザ(Chromium)がプロセス内でウォームに保たれ、スクリーンショット等の画像を会話へ直接返せる。claude.ai のようにシェルを持たないホストのエージェントにも届く - **CLI**:シェルスクリプト、CI、デバッグ用途、出力をファイルに落として `grep`/部分読みしたい場合、または MCP 非対応のエージェント/ツールチェーンから使う場合に向く。1 コマンド= 1 プロセスのためブラウザは毎回起動する キャッシュ、差分応答(`unchanged`/`diff`)、レート制御(robots.txt/ドメイン毎の直列アクセス)の状態は MCP サーバーと CLI で共有されます(同じ `~/.cache/amenbo` を使うため)。ただしレート制御のプロセス間共有はベストエフォートです。同一ドメインへの直列化は各プロセス内でのみ厳密に保証され、MCP サーバーと複数の CLI 実行が同時に同じドメインへアクセスした場合、最小間隔が多少すり抜けることがあります。 ## ツール ### `fetch` でページを取得 | パラメータ | 説明 | |---|---| | `url` | 取得対象 URL(http/https のみ。PDF 可) | | `mode` | `auto`(既定・品質スコアで Markdown/screenshot 自動切替) / `markdown` / `outline`(見出し要約) / `screenshot` | | `selector` | 本文を絞り込む CSS セレクタ | | `section` | outline で得た section ID。その節の Markdown のみ返す(祖先見出しがあれば応答に `section_path`(` › ` 区切りのパンくず)を付与) | | `page` | ページ番号(既定 1) | | `max_tokens` | 1 ページの概算トークン上限(既定 8000) | | `force_full` | true で差分応答・定型ブロック除去を無効化する(`max_tokens` によるページ分割は従来通り働く) | ### `links` でリンクを列挙 | パラメータ | 説明 | |---|---| | `url` | 起点 URL | | `filter` | URL/リンクテキストの部分一致、または `*` を使った glob | sitemap → RSS/Atom → ページ内リンクの順で探索します。 ### `screenshot` でスクリーンショットを撮影 | パラメータ | 説明 | |---|---| | `url` | 撮影対象 URL(http/https のみ) | | `fullPage` | 既定 true。false で最初のビューポート分のみ | | `width` | タイル幅 px(既定 1280) | | `scale` | 解像度スケール 0.5〜1.0(既定 1.0)。小さいほど画像トークン減 | ## 環境変数 | 変数 | 既定 | 説明 | |---|---|---| | `AMENBO_CACHE_DIR` | `~/.cache/amenbo` | キャッシュ(SQLite + PNG)の保存先 | | `AMENBO_CACHE_TTL_MS` | `900000`(15分) | キャッシュの有効期限 | | `AMENBO_MAX_BODY_BYTES` | `20971520`(20MB) | 取得ボディの上限サイズ | ## セキュリティ - **SSRF 対策**:http/https 以外のスキーム(`file:`, `ftp:` 等)を拒否。DNS 解決した接続先が private / loopback / link-local / 予約アドレスなら拒否。DNS rebinding(TOCTOU)対策として実接続を検証済み IP に固定します - **ボディサイズ上限**:巨大レスポンスによる OOM を防止 ## 既知の制限 - **本文抽出は日本語チューニング**:段階開示・キャッシュ・低負荷は言語非依存で、非日本語サイトでも動きます。ただし本文抽出のヒューリスティックは日本語ページで調整しているため、非日本語ページで本文が欠けて見えるときは `selector` 指定か `mode: "screenshot"` で取り直してください - **HTTP プロキシ非対応**:`HTTP_PROXY` / `HTTPS_PROXY` 等の環境変数は尊重しません。SSRF 対策として接続先を検証済み IP に固定する設計(DNS rebinding 対策)と、プロキシへ名前解決を委ねる方式が両立しないためです。上流プロキシ必須のネットワークでは現状ご利用いただけません - **anti-bot 回避は実装しません**:robots.txt 拒否やボット対策による取得失敗は仕様です。失敗はそのまま報告します([収集先への低負荷](#収集先への低負荷)参照) ## 開発 clone 後に1回、秘密情報検査(gitleaks)の pre-commit フックを有効化する: ```bash git config core.hooksPath githooks ``` ```bash npm run typecheck # strict 型チェック npm test # vitest npm run build # dist/ へビルド ``` ## 引用 記事や研究で参照する場合は Zenodo の DOI を使ってください。上のバッジの `10.5281/zenodo.21553636` は**全バージョン共通の Concept DOI** で、常に最新版へ解決されます。特定の版を指す場合は、その版の DOI を [Zenodo のレコード](https://zenodo.org/records/21553637)から取得してください。 機械可読な引用情報は [CITATION.cff](https://github.com/Rererr/amenbo/blob/main/CITATION.cff) にあります(GitHub の "Cite this repository" から BibTeX / APA を生成できます)。 ## ライセンス [MIT](./LICENSE)