Web Search について
Web Searchは、エージェント向けのローカルCLIとスキルで、複数エンジンの検索、ページ取得、Markdown抽出をAPIキーなしで行えます。専用のMCPサーバーはなく、自前で検索環境を運用する用途に向いています。
主な機能
- 01複数エンジンでのキー不要検索
- 02検索結果の重複排除と順位付け
- 03ページのMarkdown抽出
- 04キャッシュしたページの分割閲覧
- 05arXivとGitHubの検索
活用シーン
- 01検索APIキーなしでウェブページを探す
- 02コンテキスト制限内で長いページを読む
- 03論文やリポジトリを検索する
- 04セルフホストのSearXNG経由で検索する
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
6掲載時のツール一覧 · 2 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| site | any | いいえ | 単一のホストに限定します(例:"docs.python.org")。 |
| query | string | はい | 検索クエリ。例:"rust ownership model" または "site:nature.com crispr"。 |
| detail | string | いいえ | "concise"(デフォルト)では、トークンを節約するため結果ごとのエンジンとスコアを省略します。 "detailed" では、それらも含めます。 |
| country | any | いいえ | ISO 3166-1 alpha-2 国コード(例:"us")。省略するとエンジンのデフォルトが使われます。 |
| language | any | いいえ | ISO 639-1 言語コード(例:"en")。省略するとエンジンのデフォルトが使われます。 |
| freshness | string | いいえ | 次のいずれか:"any"、"day"、"week"、"month"、"year"(可能な範囲で新しさを考慮)。 |
| safesearch | string | いいえ | 次のいずれか:"off"、"moderate"、"strict"。 |
| max_results | integer | いいえ | 返す結果の数(デフォルトは8)。調査では増やし、簡単な検索では減らします。0 は上限なし(エンジンが返したすべての結果)を意味します。 |
詳細
websearch-skill
AI エージェント向けの、オープンソースのマルチエンジン Web 検索とコンテンツ抽出ツールです。内部レイヤーは、バージョン管理された JSON Schema コントラクトを介して通信します。
ステータス:初期段階です。 初回公開日は 2026-06-22 です。現在のバージョンは
CHANGELOG.mdをご覧ください。現在利用できる機能は、キー不要の検索、Markdown 抽出、5 つのエージェントコマンド、セルフホスト可能な SearXNG、2 つの環境変数で有効化と固定を行う NordVPN エグレスプロキシです。これらはテストスイートでカバーされています。MCP サーバーはありません。CLI を直接使うか、スキルをインストールしてください(「MCP なし」の項を参照)。強力なボット対策への対応とローカルでの再ランキングは、今後のロードマップに含まれています。機密データとともに使用する前に、バージョンを固定し、サンドボックスでテストしてください。
概要
API キーや有料の検索サービスを必要としない、AI エージェント向けのセルフホスト可能な Web 検索・ページリーダーです。複数のエンジンにクエリを送信し、結果を統合して重複を除去した後、ページをクリーンな Markdown として取得します。CLI はローカルで実行され、設定されたエンジンにクエリを直接送信します。
主なコマンド:
initは設定ファイルを読み込み、ローカルの SearXNG(このレイヤーが有効な場合は Tor も)を起動し、利用可能な機能を報告します。web_searchはページを検索します。エンジンを横断して、順位付けと重複除去を行った結果を返します。それぞれの結果には再利用可能なhandleが付いています。web_fetchはページを読み込みます。信頼できないコンテンツとしてフェンスで囲んだクリーンな Markdown を返し、長いページでもコンテキストを超過しないようページ分割します。web_openは、すでに取得したドキュメントをキャッシュからページを戻って読み込みます。ネットワークへの再アクセスはありません。
キー不要の追加コマンド:
arxivは arXiv の論文を検索し、構造化メタデータ(著者、要旨、カテゴリ、要旨と PDF へのリンク)を返します。githubは GitHub リポジトリを検索し、並べ替え可能な型付きフィールド(スター数、言語、トピック)を返します。torはローカルの Tor を起動し、すべての通信を Tor 経由にします。これにより.onionにアクセスでき、Tor のインデックスを--onionで検索できます。デフォルトでは無効です。詳しくは「Tor」の項をご覧ください。
1 つのランタイム、1 回の呼び出し
ツールキット全体は、1 つの CLI websearch を備えた 1 つの Python パッケージです。Node サービスも npm ランタイムも、独自のデーモンもなく、組み合わせて構成する必要もありません。必要なのは uv だけで、互換性のある Python を自動で取得します。「インストール」の項にある npx skills add の行は、スキルのフォルダーをエージェントに一度コピーするだけです。その後、Node の処理は実行されません。
エージェントは、1 回の呼び出しですべてを利用可能にします:
websearch init
init は設定ファイルを読み込み、ローカルの SearXNG がすでに稼働していない場合は起動し、このレイヤーが有効なら Tor も起動して、セルフテストを実行したうえで、機能と次の操作を報告します。先に確認すべきことも、決まった手順もありません。同じ呼び出しが、新しいマシンでも、すでにすべてが起動しているマシンでも適切に機能します。
SearXNG は、ツールキットが運用する任意追加のエンジンカタログです。init(または websearch searxng up)を実行すると、SearXNG のクローンを作成し、専用の仮想環境にインストールして、デタッチされたローカル Python プロセスとして実行します。Docker は不要です。docker/searxng/ は、コンテナを好むホスト向けの代替手段としてのみ用意されています。
キー不要で、すぐに使える検索エンジン
デフォルトのエンジンは、キー不要のメタ検索ライブラリ ddgs で、Google、Brave、DuckDuckGo、Yandex、Yahoo、Startpage、Mojeek、Wikipedia、Grokipedia を横断して検索します。各クエリでは、応答したプロバイダーを利用します。エージェント向けの web-search コマンドは、アカウント、サービス、エンジンフラグのいずれも必要ありません。低レベルの search コマンドでは、--ddgs-backends google,brave,mojeek を指定してプロバイダーを限定できます。プロバイダーの利用可否はネットワークによって異なります。websearch doctor でプロバイダーごとの状態を確認できます。
より幅広く、信頼性の高い検索を行うには、自分で SearXNG を実行できます(約280のエンジンを利用可能で、自分のサーバーを使用し、キーも不要です)。ルーターはこれを ddgs と統合し、両者で共有されるエンジンの相関を低減します。websearch searxng up を実行すると、git と Python がある任意のマシンにセットアップして起動できます。Docker ホストでは、docker/searxng/ が、接続環境で応答するすべてのエンジンを有効にするプローブを含む、厳選されたスタックです。詳しくは、以下の「SearXNG のセルフホスト」をご覧ください。公開 SearXNG インスタンスは、意図的にデフォルトにはしていません。多くのインスタンスでは JSON API が無効化され、自動クライアントがレート制限されるため、依存すると新規インストール時に動作しなくなる可能性があります。
このプロジェクトは MIT ライセンスです。オプションの統合機能には、セルフホスト可能な SearXNG、キーが必要なエンジン(Brave、Exa、Tavily)、有料の外部通信アダプターがあります。
以下のスコアカードでは、検索結果の品質、レイテンシー、コスト、プライバシー、取得制限を比較しています。ソフトウェアレイヤーは無料でセルフホスト可能で、複数エンジンの検索、重複排除、Markdown 抽出を備えています。保護されたサイトでは、計画中の有料外部通信アダプターが必要になる場合があります。レジデンシャルプロキシや CAPTCHA の解決には、信頼できる無料の代替手段はありません。
レイヤーの状態
| レイヤー | 機能 | 状態 |
|---|---|---|
| レイヤー 1:検索 | マルチエンジンルーター(多くのエンジンを横断するキー不要の ddgs、オプションのセルフホスト可能な SearXNG)、正規化、重複排除、相関を低減した RRF 融合 | 実装済み |
| レイヤー 2A:取得+抽出 | 段階的な取得(httpx、curl_cffi によるなりすまし)、Trafilatura による Markdown とメタデータへの抽出 | 実装済み |
| レイヤー 2B:整形+保存 | ページ分割された Markdown と JSON サイドカー、段階的開示のインデックス/リゾルバー、MinHash による重複排除、一時的な SQLite-FTS5 ストア | 実装済み |
| レイヤー 3:エージェント入出力 | 統合された web_search/web_fetch/web_open、信頼できないコンテンツの境界、SKILL.md | 実装済み |
| 追加ツール | キー不要の arxiv(論文検索)と github(リポジトリ検索)。どちらも同じ Envelope 上で単独動作 | 実装済み |
| Doctor | websearch doctor:オプションレイヤー、すべてのエンジン、ツール、両方の取得層を対象とした、機能ごとのセルフテスト | 実装済み |
| Init | websearch init:設定ファイルを読み込み、SearXNG と Tor を起動して診断を実行し、機能の状態を報告 | 実装済み |
| Tor | websearch tor:Docker 不要のローカル Tor、オニオン検索(Ahmia、SearXNG の onions カテゴリ)、.onion の取得。既存のプロキシを置き換えず、その後段に連結 | 実装済み |
コントラクトは JSON Schema 2020-12 として固定されています:[email protected]、[email protected]、[email protected]、[email protected]、[email protected]、[email protected]、[email protected]、[email protected]、[email protected]、[email protected]、[email protected]、[email protected]、[email protected]、[email protected]。すべてのレスポンスは、1つの Envelope { contract_version, ok, data, error, meta } でラップされます。
レイヤー 1:検索
検索ではリクエストを正規化し、設定済みの各 EngineAdapter に送信して、URL を正規化・重複排除したうえで、重み付き Reciprocal Rank Fusion(RRF、k=60)を使ってランキングを統合します。キー不要のデフォルトは、Google、Brave、DuckDuckGo、Yandex、Yahoo、Startpage、Mojeek、Wikipedia、Grokipedia を対象とするメタ検索ライブラリ ddgs です。下位レベルの search コマンドでは、--ddgs-backends でプロバイダーのサブセットを選択し、--engines で ddgs アダプターと SearXNG アダプターを選択します。エージェント向けの web-search コマンドではデフォルト設定が使われます。セルフホスト可能な SearXNG インスタンスを追加するには、WEBSEARCH_SEARXNG_URL を設定してください。Brave、Exa、Tavily など、API キーが必要な検索エンジンは今後対応予定です。
ddgs はキー不要の Python ライブラリで、プロセス内で実行されます。SearXNG は、多数の検索エンジンを利用できる、別個のセルフホストサーバーです。検索では、どちらか一方のアダプターを使用することも、両方を組み合わせることもできます。SearXNG が提供するのは検索結果のみで、ページの取得と抽出はレイヤー 2A が担います。
重複排除により、上流データの重複がランキングに影響するのを防ぎます。SearXNG と ddgs はどちらも Google や Bing などのクローラーを使うため、単純に結果を結合すると、同じ情報源を二重に数える可能性があります。結果は correlation_group ごとにグループ化され、各グループは最上位の順位に基づく RRF 項を 1 つだけ加算します。また、コンセンサスボーナスでは、異なるグループの数だけが使われます。SearXNG と ddgs の一致は独立した投票 1 つ分として数えられますが、SearXNG とニューラルインデックスの一致は 2 つ分として数えられます。重複排除が適用されると、レスポンスに警告が含まれます。
各結果には、それを返した検索エンジンと、各検索エンジン内での順位が記録されます。
レイヤー 2A:取得+抽出
取得と抽出には個別のインターフェースが用意されており、それぞれ独立して置き換えられます。
取得では、まず httpx を使用し、ボット対策チャレンジを検出した場合に限り、curl_cffi によるブラウザーの偽装を使って再試行します。404、レート制限、認証失敗、法的または地理的なブロックでは再試行しません。同じネットワークアドレスから再試行しても、結果が変わる可能性が低いためです。検出では、まずレスポンスヘッダーを確認し、続いて Cloudflare、DataDome、PerimeterX、Akamai、Imperva の既知のレスポンス本文マーカーを確認します。Imperva のブロックは HTTP 200 を返すことがあるため、本文マーカーはステータスを問わず確認します。ブラウザー層とステルス層(Crawl4AI、nodriver)は契約上定義されていますが、基本インストールには含まれません。
抽出では、デフォルトでヒューリスティック抽出器の Trafilatura を使用します。2026 年 5 月の WCXB ベンチマークによると、品質とコストの両面でヒューリスティック抽出器がニューラル抽出器を上回ります(ニューラル抽出器は実行コストが数十倍から数百倍高くなります)。Trafilatura は CPU 上で約 100ms の処理時間で F1 値が約 0.79 となり、記事ページでは F1 値が約 0.93 で頭打ちになります。アダプターは lxml で生の HTML を一度だけ解析し、生の schema.org JSON-LD ブロックと og:type を取得します(Trafilatura は JSON-LD をメタデータに統合し、生のブロックを公開しません)。続いて Trafilatura で Markdown 本文、プレーンテキスト、メタデータを抽出し、以下を算出します。
- 実行時のシグナル(テキスト密度、単語数の飽和度、段落数、リンク密度の逆数、JSON-LD の有無、タイトルの適切さ)に基づくヒューリスティックな
quality_score(0~1)。ソフト 404 とシェルページはハード判定で除外されます。約 0.80 未満のページはフォールバック候補です。 - JSON-LD の
@type、次にog:type、最後に URL の形式に基づいて判定する、簡易なpage_type。
ニューラル抽出エンジン(crawl4ai、jina_readerlm など)は契約上の列挙型で定義されていますが、デフォルトでは無効です。デフォルトの依存関係は、Apache-2.0(trafilatura)と MIT/BSD/MPL の依存関係からなる、制約の緩い構成です。
抽出では content_markdown を切り詰めません。--max-bytes はダウンロードするレスポンスのサイズを制限し、--max-bytes 0 にすると、この転送時の制限が無効になります。値を 0 にすると、--max-results、--page-size-tokens、--per-page も無効になります。ただし、GitHub の 1 ページあたり 100 件、arXiv の 1 リクエストあたり 2,000 件など、プロバイダー側の上限は適用されます。
レイヤー 2B:フォーマットと保存
このレイヤーでは、エージェント向けに結果をフォーマットし、後でアクセスできるようにページ全体を保存します。
フォーマットでは、レイヤー 1 および 2A から受け取ったベンダーに依存しない結果を、レイアウトが安定した Markdown ドキュメントと、同じデータを含む JSON サイドカーとして出力します。結果は関連性順に並べられ、設定されたトークン予算に応じてページ分割されます。最初に近似重複の除去が行われます。まずバイト単位で完全一致するもの(正規化された SHA-256)を対象とし、次に純粋な Python による MinHash(単語の 4-gram シャingle、128 個の置換、Jaccard 係数 0.9)を union-find でクラスタリングします。各クラスタのうちスコアが最も高いページを残し、残りは dropped_duplicates に記載します。デフォルトのしきい値では、ミラーや配信転載のようなほぼ同一のページは統合されますが、トピックが似ているだけのページは保持されます。定型文が多いコーパスでは、jaccard_threshold を低くしてください。この実装は datasketch に依存しません。段階的な情報開示により、インラインで表示する量を調整します。
auto(デフォルト)では、ページがトークン予算内に収まる場合は本文全体をインライン表示し、収まらない場合はインデックス(プレビューと安定したid)に切り替えます。indexでは常にプレビューと解決方法のヒントを表示し、fullでは常に本文全体をインライン表示します。
オプションの anthropic_search_result_blocks ビューは、Anthropic の search_result コンテンツブロックに 1 対 1 で対応します(source は文字列のみ、空でないテキストブロックを 1 つ以上含み、引用はすべて含めるか一切含めないかのどちらかです)。デフォルトでは無効で、標準のレスポンス形式とは別になっています。
サイドカーとストアは、どのモードでも本文全体を保持します。body_char_budget は、レンダリングされた Markdown ビューからリゾルバーへコンテンツを移し、検索ヒントを追加しますが、保存されたコンテンツは変更しません。--no-truncate を指定すると、本文全体がインラインで保持されます。
保存では、PageIndex インターフェース(add / search / get / resolve_index)の背後で取得したページをインデックス化します。クエリごとの結果セットは通常の Python オブジェクトのまま保持され、インデックスに追加されるのは取得したページのみです。デフォルトのアダプターは BM25 を使用する SQLite FTS5 で、サードパーティーパッケージは不要です。ローカルの SQLite ビルドに FTS5 がない場合は、同じレスポンス形式の純粋な Python による BM25 インデックスにフォールバックします。URL とコンテンツハッシュに基づいて追加処理を冪等にし、FTS5 に渡す前にクエリをエスケープします。また、ファイルパスを指定すると永続化できます。オプションのベクトルバックエンドや Rust バックエンドも、同じインターフェースを実装できます。
レイヤー 3:エージェント入出力
レイヤー 3 では、web_search(検索)、web_fetch(URL の読み取り)、web_open(取得済みドキュメントのページ送り)を公開します。各コマンドは同じ Envelope を返します。CLI と移植可能な SKILL.md は、同じ実装を使用します。このスキルは Agent Skills 形式に準拠しており、Claude Code、Codex、OpenCode など、互換性のあるエージェントにインストールできます。
コマンドでは、人が読める ハンドル(site~shorthash。例:en.wikipedia.org~3a1f9c2b5e6f)でページを指定します。web_fetch はレイヤー 2B のストアにページ全体をインデックス化し、トークン予算に応じた 1 ページ分を返します。web_open は URL を再取得せずに、そのストアから後続のページを読み取ります。各コマンドは新しいプロセスで実行されるため、デフォルトではストアは env ファイルの隣、または XDG キャッシュにファイルとして保存されます。--persist-path off を指定すると、メモリー内に保持されます。ページ送りによってコンテンツが破棄されることはなく、本文全体をページごとに引き続き利用できます。低レベルの search / fetch / open コマンドも、デバッグや組み合わせに使用できます。
取得したページのテキストは信頼できないため、web_fetch と web_open は各ページをフェンスで囲みます(「セキュリティ」を参照)。フェンスで囲まれたコンテンツは、ユーザーの指示とは別にツール出力として返されます。
追加のキー不要ツール
一般的なウェブ検索ではうまく扱えない情報源に対応する、単体で使えるツールが2つあります。どちらもキー不要で、同じ Envelope を使用します。
arxivは公式の Atom API を通じて arXiv を検索し、構造化された論文情報(タイトル、著者、要約、カテゴリ、要約と PDF のリンク)を返します。フィールドを指定した検索(--field title|author|abstract)や、日付または関連度による並べ替えに対応し、GET を使用するため arXiv のキャッシュを活用できます。また、2026 年のレート制限時にはバックオフします。githubは認証不要の REST API を通じて GitHub のリポジトリを検索し、型付きフィールド(フルネーム、スター数、フォーク数、言語、トピック、更新日)を返します。認証なしの検索は毎分約 10 リクエストまでです。レート制限されたリクエストはrate_limitedエラーを返します。コード検索にはトークンが必要なため、キー不要の経路には含まれません。
uv run websearch arxiv "diffusion models for protein design" --max-results 5 --sort-by submittedDate
uv run websearch github "vector database" --language Rust --sort stars --per-page 10
2026 年時点では、Reddit と X のどちらにも利用規約に準拠したキー不要の検索 API がないため、専用の Reddit ツールや X ツールはありません(2026 年 5 月時点で Reddit の匿名 JSON エンドポイントは 403 を返し、X は有料 API またはログイン済みアカウントを必要とします)。オープンウェブのサイトフィルターを使用してください。
uv run websearch web-search "rust async" --site reddit.com
uv run websearch web-search "frontier model release" --site x.com
MCP なし(0.3.0 以降)
バージョン 0.3.0 で MCP サーバーを削除しました。このサーバーは、起動時に設定を読み込み、エンジンのセットをキャッシュする長時間稼働プロセスでした。プロキシ、セルフホスト可能な SearXNG、エンジンの選択、または env ファイルを変更するには、クライアントの再起動が必要でした。CLI コマンドは呼び出しごとに新しいプロセスを起動し、その都度設定を読み込みます。websearch init はセットアップを行い、最終的な機能を報告します。
既存の websearch mcp をインストールしている場合は、npx skills add hec-ovi/websearch-skill でスキルをインストールしてください。これにより、エージェントのシェルからコマンドを利用できます。
インストール
利用方法に合った方法を選んでください。すべてキー不要でインターネット接続が必要です。唯一の必須要件は uv です。各環境向けの詳しい手順(Claude Code、Codex、OpenCode、Cursor、Hermes、OpenClaw、および PyPI への公開)は docs/INSTALL.md にあります。
インストール不要の CLI ツールとして(uvx が一時的な環境を構築して実行します):
uvx websearch-skill web-search "your query"
# or straight from git, for an unreleased commit:
uvx --from git+https://github.com/hec-ovi/websearch-skill websearch web-search "your query"
エージェントスキルとして skills CLI を通じて 40 以上のエージェントで使用する場合(検出された各エージェントに skills/web-search/ ディレクトリをインストールします):
npx skills add hec-ovi/websearch-skill # all detected agents
npx skills add hec-ovi/websearch-skill -a claude-code -a codex -s web-search
Claude Code プラグインとして:
/plugin marketplace add hec-ovi/websearch-skill
/plugin install web-search@websearch-skill
ソースから(開発用、uv ネイティブ):
git clone https://github.com/hec-ovi/websearch-skill
cd websearch-skill
uv sync
uv run websearch web-search "your query"
クイックスタート
セットアップなしですぐに検索できます。キー不要のメタ検索 ddgs がデフォルトです。エージェント向けの web-search(レイヤー 3)では、エンジンのフラグは不要です。低レベルの search(レイヤー 1)はデバッグや高度な用途向けで、--engines、--ddgs-backends、--no-ddgs を指定できる唯一のコマンドです。
# Layer 1: search (keyless, multi-engine via ddgs)
uv run websearch search "rust ownership" --json
# force specific keyless engines (search command only, not web-search)
uv run websearch search "rust ownership" --ddgs-backends google,brave,mojeek
# add a self-hosted SearXNG (see docker/searxng/) as a second, broader engine
export WEBSEARCH_SEARXNG_URL=http://127.0.0.1:8888
uv run websearch search "rust ownership" --engines searxng,ddgs
# Layer 2A: fetch + extract one page to clean Markdown
uv run websearch fetch "https://en.wikipedia.org/wiki/Rust_(programming_language)" --json
# Layer 2B: open several pages into one paginated, deduplicated Markdown document,
# and full-text search the passages across them
uv run websearch open \
"https://en.wikipedia.org/wiki/Rust_(programming_language)" \
"https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html" \
--search "ownership borrow checker"
# Layer 3: the fenced, handle-keyed agent interface
uv run websearch web-search "rust ownership" --json
uv run websearch web-fetch "https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html" \
--page-size-tokens 4000
# page through the rest of a fetched doc by its handle, from cache (no refetch, no flags:
# the page index is on disk by default, so a later command resolves the same handle)
uv run websearch web-open "doc.rust-lang.org~<hash>" --page 2
# extra keyless tools: arXiv papers and GitHub repos
uv run websearch arxiv "mixture of experts scaling laws" --max-results 5
uv run websearch github "llm agent framework" --language Python --sort stars
# Tor (off by default): start it, then search the onion indexes and read .onion pages
uv run websearch tor up
uv run websearch web-search "hidden wiki" --onion
uv run websearch web-fetch "http://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion/"
# initialize local services and report available capabilities
uv run websearch init
# check configured capabilities and providers
uv run websearch doctor
各コマンドはデフォルトで簡潔な人間向けの表示を出力します。また、--json を指定すると生の JSON Envelope を出力します(リクエストレベルのエラーがなければ終了コード 0、エラーがあれば 1)。fetch コマンドでは、--output-format {markdown,text,json} で人間向け表示に出力する本文の形式を選択します(text はプレーンテキスト形式で出力します)。パイプで渡す場合は、--quiet を指定すると抽出した本文のみを出力します。open コマンドでは、--mode {auto,index,full} で段階的な情報開示を制御し、--no-truncate ですべての本文全体をインライン表示し、--search QUERY で開いたページを対象に BM25 による文章検索を実行できます。また、--anthropic-blocks を指定すると、サイドカーに Anthropic の search_result ビューを追加します。エージェント向けの web-search では、--max-results、--detail、--freshness、--site、--language、--country、--safesearch、--offset、--searxng-url を指定できます。エンジン選択フラグ(--engines、--ddgs-backends、--no-ddgs)は、--searxng-url(または WEBSEARCH_SEARXNG_URL)とともに、低レベルの search コマンドでのみ使用できます。フラグの一覧は uv run websearch <command> --help を参照してください。
fetch --json のレスポンスは次のようになります。
{
"contract_version": "1.0.0",
"ok": true,
"data": {
"source": { "final_url": "https://...", "status": 200, "fetched_via": "http", "blocked": false },
"result": {
"title": "Rust (programming language)",
"page_type": "article",
"quality_score": 0.91,
"word_count": 8123,
"content_markdown": "# Rust ...",
"metadata": { "og_type": "article" }
}
},
"error": null,
"meta": { "layer": "extract", "backend": "http", "elapsed_ms": 412 }
}
ライブラリとして使用する場合:
from websearch.layer1_search import build_router, SearchRequest
router = build_router(searxng_url="http://127.0.0.1:8888", enable_ddgs=True)
envelope = router.search(SearchRequest(query="rust ownership", count=10))
for r in envelope.data["results"]:
print(r["fused_score"], r["url"], [s["engine"] for s in r["sources"]])
レイヤー 2B(フォーマット + 保存)も同じ方法で使用します。run は Envelope を返し、その data は JSON の FormatPayload です。そのため、markdown とサイドカー全体には通常の辞書アクセスでアクセスできます。
from websearch.layer2_format import (
FormatRequest, ResultInput, build_format_pipeline,
build_page_index, StoreConfig, PageInput, SearchPageRequest,
)
env = build_format_pipeline().run(
FormatRequest(
query="rust ownership",
results=[ResultInput(url="https://x.test/a", title="A", score=0.9, body_markdown="# A ...")],
)
)
document = env.data["markdown"] # the layout-stable document
sidecar = env.data["sidecar"] # identical data with full page bodies
# an ephemeral page index for passage search and resolve-by-id
index = build_page_index(StoreConfig()) # in-memory SQLite FTS5 (BM25)
index.add([PageInput(url="https://x.test/a", markdown="# A ...")])
hits = index.search(SearchPageRequest(query="borrow checker"))
オプションのレイヤー(すべてデフォルトでは無効)
基本インストールでは、直接接続を通じてキー不要のエンジンを検索します。4つのオプション設定を追加すると、VPN の検証、プロキシ経由のルーティング、Tor、またはセルフホストの SearXNG を利用できます。websearch doctor は、設定されている各設定を検証します。
| レイヤー | 切り替え | 追加される機能 |
|---|---|---|
| VPN | WEBSEARCH_VPN=nordvpn または any | 送信トラフィックをトンネル経由にすることを宣言します。これにより、doctor はそれを前提とせずに検証します。これ自体でトラフィックをルーティングすることはありません。トンネルは VPN アプリが担います |
| 送信プロキシ | NORDVPN_USER + NORDVPN_PASS、または WEBSEARCH_PROXY=<url> | このマシンから外部へ向かうすべての経路で、2つのプロセスの両方に同じプロキシを使います。CLI 自身のリクエストと、ローカルの SearXNG が検索対象のエンジンへ送るリクエストが対象です。2つの NordVPN 認証情報だけで有効になり、送信トラフィックはそのプロキシに固定されます。websearch proxy setup が設定手順を案内し、websearch proxy use <city> で出口を選択できます。WEBSEARCH_PROXY=off を指定すると直接接続します |
| Tor | WEBSEARCH_TOR=on | すべての経路がローカルの Tor を経由し、.onion にアクセスできるようになり、--onion で onion インデックスを検索します。websearch tor up を実行すると Tor を起動し、この設定を自動で行います |
| SearXNG | WEBSEARCH_SEARXNG_URL=<url> | セルフホストのメタ検索インスタンスが Layer-1 のファンアウトに加わります。websearch searxng up を実行すると、Docker の有無にかかわらずインスタンスを起動し、この設定を自動で行います |
設定の保存場所
エクスポート済みの変数は常に優先され、1回の実行では --vpn/--proxy/--tor/--searxng-url がほかのすべての設定より優先されます。それ以外の場合、設定は該当する設定を定義している最初のファイルから読み込まれます。
WEBSEARCH_ENV_FILEで指定したファイル(設定ディレクトリをマウントするコンテナでは、この方法を使います)。- プロジェクト固有の
.env(.env.exampleをコピーしてください)。 $XDG_CONFIG_HOME/websearch/.env(通常は~/.config/websearch/.env)。コマンドを実行する場所にかかわらず適用され、searxng upとtor upが起動した内容を記録する場所です。
すでに設定されている値が上書きされることはありません。そのため、ファイルは不足している値だけを補います。また、どちらのファイルもリポジトリの外にあるか、Git で追跡されないため、NordVPN サービスの認証情報がシェルの履歴に残りません。websearch init は、読み込んだファイルと確認したファイルを表示します。
SearXNG のセルフホスト
SearXNG は任意です。キー不要の ddgs エンジンは、SearXNG なしで動作します。SearXNG をセルフホストすると、公開インスタンスや API キーに依存せず、より多くのエンジンを利用できます。websearch doctor で結果を ddgs と比較すれば、パーサーの失敗なのか、プロバイダーによる IP アドレスのブロックなのかを判別できます。
Docker が利用可能かどうかに応じてセットアップを選んでください。
Docker なし(エージェントのサンドボックスを含め、どこでも動作します):
websearch searxng up # clone, install, start detached, wire it in
websearch web-search "your query" # now fuses SearXNG + ddgs
websearch searxng status # where it lives, whether it answers
websearch searxng down # stop it
最初の up では、上流の SearXNG をクローンし、その隣に仮想環境を構築します。所要時間は約 15~30 秒で、数百 MB を使用します。以降は起動するだけです。すべてのデータは 1 つの状態ディレクトリに保存されます(WEBSEARCH_SEARXNG_HOME。デフォルトは WEBSEARCH_ENV_FILE の隣、または XDG キャッシュ内です)。また、WEBSEARCH_SEARXNG_URL が環境ファイルに自動で書き込まれるため、次回の検索ではほかに何もしなくても利用できます。サーバーはコマンドの子プロセスではなく、独立したセッションで起動します。これにより、エージェントの CLI が実行したすべてのシェルコマンドのプロセスグループを終了しても、サーバーを稼働し続けられます。WEBSEARCH_SEARXNG_PORT でポートを 8888 から変更できます。websearch init を実行すると、すべてのセットアップの一環としてこの手順も行われます。
Docker を使用する場合:
./docker/searxng/searxng.sh up # generates a per-machine secret, waits for health
export WEBSEARCH_SEARXNG_URL=http://127.0.0.1:8888
uv run websearch web-search "your query"
コンテナは 1 つだけで、ホストへのインストールは不要です。また、SearXNG 自身のエンジンリクエストを、送信トラフィック用のプロキシ経由でルーティングできます。SearXNG には約 280 のエンジンが含まれていますが、その大半は無効です。そのため、標準のインスタンスでは、一般的なクエリに対して約 6 つのエンジンから回答が返ります(家庭用回線では Brave と Startpage が CAPTCHA を返します)。searxng.sh engines は、使用中の回線からすべてのエンジンをプローブし、応答するものを有効にするとともに、スキップしたエンジンには理由を記録します。ここでの測定では、一般的なクエリの結果が、2 つのエンジンからの 26 件から、3~4 秒で 21~29 のエンジンからの 155~240 件に増えました。
コンテナは設定を読み取り専用でマウントし、実行時の状態を Docker ボリュームに保存します。また、すべてのケイパビリティを削除し、デフォルトではループバックにバインドします。シークレットはコミットされません。トレントトラッカーとシャドウライブラリはデフォルトのエンジンセットから除外されていますが、名前を指定すれば引き続きクエリできます。localhost の外部に公開する前に、docker/searxng/ を確認してください。
どちらの方法もループバックにバインドし、JSON API を有効にして、インスタンス自身のエンジンリクエストを送信トラフィック用のプロキシ経由でルーティングします。異なるのはエンジンリストです。Docker を使わない方法では上流のデフォルト設定を実行します(ここでは 278 のうち 83 のエンジンが有効)。一方、Docker スタックのプローブでは、使用中の回線から応答するすべてのエンジンが有効になります(同じマシン、同じ日に 279 のうち 213)。Docker が使える場合は Docker を、使えない場合は websearch searxng up を使用してください。
送信トラフィック用プロキシ
NordVPN のサービス認証情報だけでセットアップできます。環境変数(または設定ファイル)に NORDVPN_USER と NORDVPN_PASS があれば、すべてのネットワーク通信が NordVPN の SOCKS5 プロキシ経由になり、送信トラフィックはそこに限定されます。アプリも、ほかの切り替え設定も不要です。これらはアカウントのログイン情報ではなく、Nord Account ダッシュボードの「Set up NordVPN manually」に表示されるサービス認証情報です。ファイルを探さなくても、websearch proxy でセットアップできます。
websearch proxy setup # creates the settings file and prints its path, with both keys empty
websearch proxy locations # the exits NordVPN runs: Netherlands, Sweden, nine US cities
websearch proxy use dallas # writes NORDVPN_HOST, restarts a running SearXNG onto that exit
websearch proxy status # what is set, what is missing, and where the traffic actually comes out
setup で指定されたファイル内の認証情報 2 行は、自分で入力してください。認証情報が読み戻されたり、表示されたりすることはありません。status はプロキシ経由で NordVPN に接続先の所在地を問い合わせるため、選択が反映されなかった場合は、選択した都市と異なる都市が表示されます。1 回限りのコマンドで --location dallas を指定すると、その実行に限って出口を変更することもできます。
WEBSEARCH_PROXY で別のプロキシを選択したり、プロキシを無効にしたりできます。
export WEBSEARCH_PROXY=socks5h://user:pass@host:1080 # any proxy URL (http:// works too)
export WEBSEARCH_PROXY=nordvpn # the NordVPN shorthand, explicitly
export WEBSEARCH_PROXY=off # direct connection, even with credentials set
ロック
設定済みのプロキシは、独自に送信トラフィックをロックします。このマシンの外部へは、プロキシを経由しない通信は一切送信されません。プロキシを経由しない経路は、直接接続にフォールバックするのではなく、error.code: "egress_locked" として拒否されます。対象には、2 つのフェッチ層、検索エンジン、ローカル SearXNG のエンジンリクエスト、SearXNG と Tor のインストール、および doctor --baseline が含まれます。ローカル SearXNG がプロキシを経由せずに稼働していることを検索時に検出すると、クエリを実行する前に、現在の出口を経由するよう再起動します。ループバックと LAN の宛先には影響しません。これらはマシンの外部へ出ないためです。
フォールバックが発生するのは、プロキシが利用できない日であり、監視している日ではありません。だからこそロックが存在します。
websearch proxy lock # WEBSEARCH_EGRESS_LOCK=on: locked even with the proxy off
websearch proxy unlock # WEBSEARCH_EGRESS_LOCK=off: a missing proxy may go direct
WEBSEARCH_EGRESS_LOCK は、どちらの方向にも明示的にオーバーライドできます。on の間は、ロックを解除しない限り websearch proxy off は拒否されます。
すべてのネットワークコマンドでは、--proxy <url|nordvpn|off> も指定できます。これはその実行時に変数をオーバーライドするため、--proxy off を使えば、何も設定解除せずに直接接続できます。socks5:// より socks5h:// を推奨します。DNS がプロキシ経由で解決され、ホスト名がローカルのリゾルバーに送られないためです。リクエスト単位の fetch --proxy は、プロセス全体に適用される既定値より優先されます。
WEBSEARCH_PROXY が設定されている場合、このマシンの外部へ出るすべての経路でこれが使われます。2 つのフェッチ層、ddgs エンジン、Ahmia、arXiv、GitHub、doctor のプローブ、SearXNG と Tor のインストール、ローカル SearXNG インスタンス独自のエンジンリクエストが対象です。テストでは、次の 2 つの重要な動作を検証します。
- DNS 解決。 プロキシが設定されている場合、SSRF ガードはホスト名をローカルで解決しません。
socks5h://は出口ノードで解決します。リテラル IP は、http://127.0.0.1や169.254.169.254のメタデータエンドポイントも含め、検索なしで引き続き拒否されます。 - 障害時の動作。 プロキシが利用できない場合、すべてのコンポーネントは直接接続で再試行せず、エラーを返します。これには、Python の外部でソケットを開く
ddgs(Rust)とcurl_cffi(libcurl)も含まれます。
オプトインの websearch doctor --baseline チェックは、直接接続時とプロキシ経由時の出口 IP を比較するため、直接リクエストを 1 回送信します。直接接続時の IP をエコーサービスに送信するため、既定では無効になっています。また、プロキシが設定されている間は、トンネルを離れる唯一のリクエストであるため、追加で WEBSEARCH_EGRESS_LOCK=off が必要です。
セルフホストの SearXNG リクエストには 2 つのホップがあり、それぞれ意図的に異なる方法でプロキシ経由にします。クライアントはローカル SearXNG の 127.0.0.1 に直接接続します。このホップはマシンの外部へ出ず、リモートの出口から自身の localhost への接続は常に失敗するためです。その後、SearXNG 自身が Google、Brave、Mojeek などにクエリを送信します。このホップはマシンの外部へ出るため、両方のスタックでプロキシを outgoing.proxies に書き込みます(SOCKS の出口では往復分の時間がかかるため、extra_proxy_timeout も設定します)。これにより、検索はほかのツールと同じ出口から送信されます。websearch searxng status はそのアドレスを egress として表示し、websearch proxy use <city> は稼働中のインスタンスを新しい出口経由に再起動します。また、Docker スタックの 2 つの IP は searxng.sh egress で表示できます。インスタンスから直接送信するには、SEARXNG_OUTGOING_PROXY=off を設定します。リモート SearXNG アドレスには、ほかのホストと同様にクライアントのプロキシ経由で接続します。
ネットワーク監視者から見えるもの
セキュリティに携わっている場合を除き、このセクションは読み飛ばしてください。通常の接続を使う通常のマシンでは該当しない状況で、誰かがご自身のネットワーク経路上の通信を記録している場合にのみ関係する例外について説明しています。
クエリとページのコンテンツは、TLSによってエンドツーエンドで保護されています。HTTPSセッションは、このマシンからプロキシを経由してウェブサイトまで確立されるため、プロキシも、回線を監視している人も、検索クエリや返されるページを読み取ることはできません。SOCKS5で回線上の監視者に隠せないのはメタデータです。NordVPNサーバーと通信していること、宛先ホスト名(SOCKSハンドシェイクとTLS SNI内)、そしてこのプロトコルが平文で送信するSOCKSのユーザー名とパスワードが該当します。これらはプロキシサービス専用の認証情報であり、Nordアカウントへのログインには使えません。また、ダッシュボードで再生成できます。ウェブサイトに見えるのは出口IPアドレスだけです。NordVPNのTLSプロキシ(ポート89)は、サービス認証情報に対してHTTP 407を返すため(実環境で確認済み。制限事項を参照)、プロトコルレベルではTLS接続されたプロキシを利用できません。この残存リスクは、インストール不要のセットアップを実現するための代償です。
完全に解消するには、OSレベルで対応する必要があります。プロバイダーのVPNアプリ(WireGuard)をキルスイッチを有効にして実行し、WEBSEARCH_PROXY=offを設定して、WEBSEARCH_VPN=nordvpnを指定すると、doctorがトンネルを推測で判断せず検証します。そうすると、このツールが送信するデータも含め、マシンが送信するすべてのバイトが暗号化されて外部に送られ、回線上にプロキシハンドシェイクは一切発生しません。キルスイッチがあることで、「トンネル接続中は暗号化される」が「直接通信は決して行われない」状態になります。キルスイッチがなければ、トンネルが切断された際に暗号化されていない通信に切り替わります。アプリを実行しつつプロキシも有効にしておく方法もあります(その場合、ハンドシェイクはトンネル内で行われ、プロキシによって出口の都市が固定されます)が、オフにするのが簡単です。
Tor
起動するまでオフです。通常モードと Tor モードで使うコマンドは、切り替え方が異なるだけです。websearch tor up を実行するとレイヤーが有効になり、それ以降のすべてのコマンドが Tor 経由になります。--tor on を指定すると、1 つのコマンドだけで有効になります。検索時に --onion を指定すると onion インデックスが選択され、レイヤーが有効なときは web-fetch で .onion URL を利用できます。web-search-tor スキルには、これらの機能がそのまままとめられています。セットアップは websearch tor up だけです。
websearch tor up # start Tor, verify the exit, turn the layer on
websearch web-search "query" --onion # search the onion indexes
websearch web-fetch "http://<addr>.onion/" # read an onion page, same output as any other
websearch tor status # listening, and is the traffic really Tor
websearch tor down # stop it and turn the layer off
up は、すでに待ち受けている Tor を使います。なければ PATH 上の tor を使い、それもなければ公式 Tor Expert Bundle(約 30 MB)を状態ディレクトリにダウンロードし、同じ場所で公開されている sha256 と照合します。バイナリは GPG 検証されません。信頼の根拠となるのは、torproject.org への TLS 接続とそのダイジェストです。Tor はコマンド終了後も動作し続けるよう、SearXNG と同じ方法で独立したセッション内で起動されます。up はブートストラップを待ってから、check.torproject.org に問い合わせて、実際に Tor 経由でトラフィックが送信されているかを確認します。ポートから応答があることと、そのポートが Tor であることは同じではありません。そのため、is_tor は reachable とは別のフィールドであり、doctor は「何かが待ち受けている」ことを準備完了と見なさず、false の場合に失敗します。
レイヤーが有効な間は、検索、取得、arXiv、GitHub など、すべての経路が Tor を経由します。.onion アドレスも利用できます。レイヤーがオフの場合は、名前解決を行う前に拒否されます。失敗するまでの間に onion 名をローカルで検索すると、結局リゾルバーに漏れてしまうためです。
--onion を指定すると、クリアネット用エンジンが onion 用エンジンに切り替わります(Ahmia に加え、ローカルインスタンスの SearXNG では onion カテゴリーも使われ、Torch が追加されます)。これはエンジンを追加するのではなく、置き換えるものです。クリアネット用エンジンは onion サービスをインデックス化せず、onion 用インデックスはクリアネットをクロールしません。そのため、混合検索では互いに重ならないコーパスの空に近いリストを 2 つ混ぜることになります。onion 検索には 10〜30 秒かかるため、それに合わせてデフォルトのタイムアウトも長くなっています。
Tor はプロキシの代わりにはなりません。 WEBSEARCH_PROXY が設定されている場合、その値は Tor 自身の torrc に上流プロキシ(Socks5Proxy / HTTPSProxy)として書き込まれます。そのため、経路はあなた、プロキシ、Tor の順になります。VPN の経由先は引き続き ISP から Tor の使用を隠し、経由先を追加するためのレイヤーを有効にしたときに、経由先が暗黙に取り除かれることはありません。WireGuard など、Tor が接続できないスキームのプロキシは、経由先が黙って省略されるのではなく、明示的なエラーになります。変わるのは VPN チェックだけです。Tor が有効な間はチェックがスキップされ、その理由も表示されます。Tor の背後では外部から見えるのは出口ノードだけなので、外部からその前段にある VPN を確認することはできません。
独自の Tor ディレクティブは、生成された torrc と同じ場所にある torrc.local に記述してください。このファイルはインクルードされ、再生成後も保持されます。WEBSEARCH_TOR_SOCKS は、すでに実行中の Tor を指定します(socks5h://127.0.0.1:9150 は Tor Browser の設定です)。WEBSEARCH_TOR_PORT はポートを変更し、WEBSEARCH_TOR_BINARY はダウンロードを省略し、WEBSEARCH_TOR_VERSION はバンドルのリリースを固定します。
onion ページは独立したファイル(通常のインデックスと同じ場所にある pages-onion.json)にインデックス化されます。そのため、Tor 経由で読み取った内容がクリアネットのストアに混ざることはなく、履歴だけを個別に削除できます。また、信頼できないコンテンツに対する境界線には、ソースが onion サービスであることを示す一文が加わります。意図的に帰属を特定できないため、その背後にいる人を報告したりブロックしたりすることはできません。そのため、人ではなくエージェント向けに書かれたペイロードに遭遇する可能性が最も高い場所です。
これは Tor であり、Tor Browser ではありません。リクエストの送信元は隠されますが、サイトごとに回線を分離することはなく、送信内容を匿名化することもありません。ブラウザーフィンガープリントの対象となる要素はありません(JavaScript も、キャンバスも、取得間で共有される Cookie の保存領域もありません)。ただし、すべてのリクエストには、すべての出口で共通のクライアントシグネチャが付くため、それらをまたいで関連付けられる可能性があります。
VPN
WEBSEARCH_VPN は想定されるトンネルの状態を記録しますが、ルーティングは設定しません。トンネルが切断されると、doctor は失敗を報告します。nordvpn は、呼び出し元がそのネットワーク内にいるかどうかを報告する NordVPN のキー不要接続エンドポイントを使って検証されます。any は、プロバイダー固有のエンドポイントがないため、アクティブなトンネルインターフェースがあるかどうかだけを確認します。doctor は、出力プロキシが設定されている場合はそれを経由し、設定されていない場合は直接確認します。
nordvpn は HTTP チェックなので、すべてのプラットフォームで動作します。any はインターフェース名を読み取ります。これらの名前は Linux(tun、wg、nordlynx)と macOS(utun)では意味がありますが、ethernet_32770 のような名前になる Windows では意味がありません。そのため、Windows では推測せず、トンネルを未確認として報告します。
websearch init
websearch init は設定ファイルを読み取り、そのレイヤーが有効な場合は Tor を起動し、--skip-searxng が設定されていない場合はローカルの SearXNG を起動して、doctor チェックを実行します。
uv run websearch init # bring everything up, then report
uv run websearch init --json # the same run as an Envelope
uv run websearch init --quick # skip the slow sweep; unprobed capabilities read "unknown"
uv run websearch init --skip-tor # leave Tor alone even with the layer on
ok env read /config/websearch.env
skip tor off (WEBSEARCH_TOR is not set); egress is not going through Tor
ok searxng SearXNG 2026.7.25 answering at http://127.0.0.1:8888, 83 of 278 engines
warn doctor 18 ok, 4 warn, 0 fail, 3 skipped
capabilities
web_search ok
web_fetch ok
arxiv ok
github ok
searxng ok
proxy ok
vpn off
tor off
ready: search online (5 of 9 keyless providers + SearXNG, 83 engines); fetch, arxiv, github ok; egress through socks5h://***:***@nl.socks.nordhold.net:1080
data.ready を待ちます。data.state は ready、degraded(検索は動作しますが、要求された機能が不足しています)、または broken(検索が動作しません)です。data.capabilities には利用可能なコマンドが、data.next_actions には修復手順が一覧表示され、data.doctor には完全な診断ペイロードが含まれます。検索が動作している場合は、結果が degraded でも終了コードは 0 で、動作していない場合は 1 です。
websearch doctor
websearch doctor は現在の機能とプロバイダーの状態を確認します。
uv run websearch doctor # everything
uv run websearch doctor --quick # skip the engine fanout, tools, and fetch tiers
uv run websearch doctor --check engines --check proxy
uv run websearch doctor --check tor # just the Tor layer
uv run websearch doctor --json # the same run as an Envelope
4 つのオプションレイヤーの状態を出力した後、Python と依存関係一式、直接接続によるインターネット接続と出口 IP、出力プロキシと出口 IP が実際に変わったかどうか、Tor(リッスン状態、check.torproject.org による Tor の確認、および実際の onion フェッチ)、宣言された VPN、セルフホストされた SearXNG(ヘルス状態、アクティブなエンジン数、ライブ JSON クエリ)、各 ddgs プロバイダーを個別に、arXiv と GitHub、さらに両方のフェッチ階層を確認します。失敗がある場合に限り、終了コードは 1 です。無効になっているオプションレイヤーはスキップされ、レート制限のあるプロバイダーが 1 つある場合は警告となります。HTTP クライアントのエラーテキスト内も含め、プロキシの認証情報が出力に含まれることはありません。
SearXNG との相互確認は、ddgs の失敗の分類に役立ちます。CAPTCHA、レート制限、またはパーサーが読み取れないマークアップを含む成功レスポンスに対して、ddgs は「結果が見つかりません」と報告します。SearXNG が実行中の場合、doctor は SearXNG が別途保守しているスクレーパーを通じて、同じプロバイダーにクエリを送信します。
warn engine:ddgs:google No results found. (SearXNG reached it: 260 results)
-> ddgs's parser, not a block on your IP. Query it through SearXNG.
warn engine:ddgs:startpage No results found. (SearXNG got nothing from it either)
-> startpage is refusing this IP. Only a different egress exit changes that.
2026 年 7 月、同じ自宅の接続環境で数分間隔で実行した 2 回のチェックでは、9 つの ddgs プロバイダーで異なる結果が得られました。Brave、Yahoo、Yandex は両方とも応答しました。Google と Mojeek は ddgs 経由では応答しませんでしたが、SearXNG 経由ではそれぞれ 181 件と 10 件の結果を返し、ddgs のパーサーの失敗が示されました。Startpage と Wikipedia はどちらの経路でも結果がなく、ブロックされていることが示されました。DuckDuckGo と Grokipedia は実行ごとに結果が変わり、NordVPN の共有 SOCKS 出口では、さらに 1 つのプロバイダーが失われました。最新の結果を確認するには doctor を実行してください。
セキュリティ
Fetch は任意の URL とページコンテンツを受け取るため、SSRF とプロンプトインジェクションへの対策を備えています。
- SSRF ガード(組み込み): fetch は http(s) スキームの許可リストを適用し、すべてのホストを解決して、プライベート、ループバック、リンクローカル(
169.254.169.254クラウドメタデータエンドポイント)、予約済み、マルチキャストの各アドレスを拒否します。リダイレクトは手動でたどり、各ホップで同じチェックを行うため、公開 URL から内部ネットワークへリダイレクトされることはありません。意図的に内部のコンテンツを取得する場合は、リクエストごとに--allow-private-hostsで上書きできます。 - 信頼できないコンテンツ(組み込み、レイヤー 3): 取得したページのテキストが指示として提示されることはありません。
web_fetchとweb_openは、2026 年の一次情報源によるガイダンスに基づき、各ページをフェンスで囲みます。フェンスには、開始マーカーと終了マーカーに含まれるインスタンスごとの 128 ビット乱数ノンス、データのみを示すディレクティブ、本文内にあるマーカーの複製の無害化が含まれます。任意のデータマーカー(--datamark)を追加すると、耐性を高められます。フェンスは境界の突破を防ぎますが、説得を防ぐことはできません。ツール出力の分離、最小権限、流出経路の遮断も引き続き必要です。低レベルのfetchコマンドは、パイプ処理や合成のため、フェンスなしで抽出した本文を返します。
アーキテクチャ
各レイヤーは、ポート(機能名を付けたインターフェース)と、その背後にある 1 つ以上のアダプターを持つフォルダーです。これらは、バージョン管理された JSON Schema 2020-12 コントラクトのみを介して接続されます。ポートのフィールドには機能名(snippet、fused_score、sources)が付けられ、バックエンド固有の形式は、そのバックエンドのアダプター内でポートの形式にマッピングされます。デフォルトのデプロイメントは速度を優先してインプロセスで実行されます。また、各レイヤーはコントラクトを介してのみ結合されているため、後からあるレイヤーをサブプロセスまたはローカルサービスに移行しても、隣接するレイヤーを変更する必要はありません。コントラクトへの追加変更は MINOR です(コンシューマーは未知のフィールドを無視します)。削除、名前変更、型変更は MAJOR であり、コンシューマー主導のコントラクトテストにより、記録済みフィクスチャを壊すプロデューサーの変更は失敗します。設計の詳細は docs/ARCHITECTURE.md を参照してください。
7 軸のスコアカード
スコアカードでは、検索品質、コスト、レイテンシ、運用上の制限を分けて評価します。
| 軸 | このツール | 備考 |
|---|---|---|
| 検索品質 | 一般的なクエリでは主要な API と同程度 | 2026 年の主要サービスは、品質において統計的に同等です |
| 新しさ | オンデマンドの新しさフィルター | エンジンごとに異なり、ベストエフォートです |
| 抽出再現率/ノイズ | 競争力があります(Trafilatura は F1 約 0.79、記事では約 0.93) | ヒューリスティック方式は品質とコストの両面でニューラル方式を上回ります |
| ボット対策の成功率 | 有料プロキシなしで約 70~90% | 保護されたサイトには住宅用の外向き接続が必要です |
| エンドツーエンドのレイテンシ | 応答性の高いエンジンによって異なります | 複数エンジンへの並列リクエストと統合によりレイテンシが増加します |
| コスト | ソフトウェア層ではほぼゼロ | インフラについては別途記載しています |
| 引用の正確性 | 出典に基づき、重複を除去した結果 | URL を捏造しません |
保護されたサイトでは、通常、有料の住宅用外向き接続が必要です(Firecrawl でさえ、独立してテストされた保護サイトでの成功率は約 34%です)。組み込みプロキシは、検索の地域ターゲティングとレート制限に応じたローテーションに対応しています。商用 VPN は、一般にそのようなシステムが検出するデータセンター IP を使用するため、ページ単位のボット対策システムを回避することはできません。住宅用の外向き接続アダプターを計画中です。
ベンチマーク
docs/BENCHMARK.md では、同じクエリを同じ時点で Claude Code のホスト型ウェブ検索と比較し、再現用のコマンドも掲載しています。どちらも、関連性が高く新しいページを同程度に見つけました。このプロジェクトは、ローカル抽出、複数エンジンの統合、arxiv および github コマンドを追加します。一方、ホスト型検索は 1 回の呼び出しで要約を返します。
検証
このコードベースを裏付ける証拠は2種類あります。動作を固定するオフラインのテストスイートと、実際のネットワークを使ったライブのエンドツーエンド実行です。
テストスイートは793件で、ソケットを開くテストは1件もありません。すべてのネットワーク境界(httpx のトランスポート、ddgs クライアント、curl_cffi、SearXNG、doctor の HTTP ポート)が注入されるため、エアギャップ環境のマシンでも CI でもテストスイートは同じように成功します。各契約上の約束に対して、実際のエントリーポイントを通すテストが用意されています。また、エグレスルールには専用の検証があり、プロキシ経由でのローカル DNS 解決の禁止、クラウドメタデータエンドポイントと CGNAT(RFC 6598)を含むリテラル IP への SSRF 拒否、リダイレクトの各ホップでの再チェック、オリジンをまたぐリダイレクト時の認証情報のスコープ制限、フェンスのノンス整合性、認証情報が出力に決して含まれないこと、診断の実行を含む、プロキシを経由しないあらゆる経路をロックが拒否することを検証します。
直近では2026-08-13に、実際の NordVPN SOCKS5 出口を使ってライブ検証を実施しました。
websearch proxy statusは、NordVPN 独自のインサイトエンドポイントを通じて出口を確認し、出口 IP、都市、protected: trueを表示しました。- doctor のエグレススイープでは、すべてのリクエストがプロキシ経由となり、直接のリクエストは0件でした。ロックが有効で使用可能なプロキシがない状態では、doctor を完全に実行してもネットワークリクエストは0件で、拒否された各項目が名前付きで報告されました。
web-searchは ddgs と SearXNG のライブ結果を統合し、web-fetchはフェンス付きのページ分割されたページを返しました。web-openはネットワークに触れず、後続ページをストアから提供しました。- コールド状態での
websearch tor upは Tor Expert Bundle(15.0.19)をダウンロードし、公開されているチェックサムを検証してブートストラップを実行しました。さらに、check.torproject.org によって出口が Tor と確認されました。--onion検索は onion の検索結果を返し、.onionの取得結果は、onion の来歴に関する注記付きのフェンス形式で返されました。 - 同じスイープをプロキシをオフにして再度実行し、直接接続がデフォルトであることを確認しました。search、fetch、open、arxiv、github、Tor のライフサイクル全体で拒否も制限もありませんでした。
制限事項とその責任の所在
以下の各制限は境界のプロバイダー側にあります。クライアント側はそれぞれ実装され、テスト済みです。
- SOCKS5 のメタデータ(NordVPN)。 上記のオブザーバーの項では、SOCKS5 が利用者自身の回線上でどのような情報を公開するかを説明しています。NordVPN はポート89で TLS プロキシ(
*.proxy.nordvpn.com、有効な Sectigo 証明書)を運用しており、サービス認証情報に対して HTTP 407 を返します(2026-08-13にライブ検証済み)。これらのサーバーが認証するのはブラウザー拡張機能のみで、認証情報を使う同社のサービスは SOCKS5 です。クライアントはすでにWEBSEARCH_PROXY=https://user:pass@host:portを受け付け、ホスト名と認証情報を暗号化したまま、そのようなプロキシに TLS 経由で接続します。そのため、認証情報付き TLS プロキシを提供するプロバイダーであれば、設定を1行追加するだけで解決でき、コードの変更は不要です。 - データセンターの出口とボット対策の壁。 商用 VPN の出口はデータセンター IP であり、クライアントにかかわらずボット対策システムに検出されます。取得階層ではブロックを検出するとブラウザーの偽装に段階的に切り替えますが、住宅回線からのエグレスを要求するサイトには、計画中の有料アダプターが利用可能になるまでアクセスできません。
- エンジンの利用可否はネットワークや日によって変わります。 キー不要のプロバイダーは、それぞれの都合に合わせてレート制限、ブロック、またはマークアップの変更を行います。
websearch doctorは現在の接続からどのプロバイダーが応答するかを測定し、SearXNG のファンアウトは ddgs で解析できなくなったエンジンを補います。 - キー不要 API の上限。 GitHub で認証なしに実行できる検索は1分あたり約10回で、それを超えると
rate_limitedが返されます。arXiv にはレート制限があり、クライアントは所定のスケジュールに従って再試行まで待機します。 - Tor の出口は公開されています。 多くのサイトは出口からの接続を明示的に拒否します。これは出口リストの性質によるもので、サイトが拒否する場合は、プロキシ経由のクリアネット経路が代替手段になります。
ロードマップ
現在の配布方法には、npx skills add、Claude Code のプラグインとマーケットプレイス、PyPI/uvx があります。各エージェントでサポートされている方法については、docs/INSTALL.md をご覧ください。
送信プロキシ(NordVPN の認証情報、WEBSEARCH_PROXY、または --proxy)は、セルフホストの SearXNG 自身によるエンジンへのリクエストも含め、マシンの外部へ出るすべての通信経路をカバーします。また、設定中は送信通信を制限し、プロキシを使用できない経路は直接接続にフォールバックせず拒否します。
今後の予定であり、まだ実装されていないもの:
- 有料送信アダプター: 保護対象のロングテール向けの住宅用プロキシアダプターです。
- ローカル再ランキング: 複数エンジンの検索結果を高精度化するための、クロスエンコーダーによる処理です。
- エンジンの追加: 既存の
EngineAdapterポートの背後に配置する、API キーが必要なアダプター(Brave、Exa、Tavily)と、オプションのニューラルインデックスです。
開発
uv sync # install deps (including the dev group)
uv run pytest # 748 tests, no network
uv run ruff check .
CI では Linux のみを対象に、uv を通じて Python 3.11、3.12、3.13 で ruff と pytest を実行します。パッケージ自体は OS 固有のインポートを含まない純粋な Python であり、コンパイルが必要な 2 つの依存関係(curl_cffi と ddgs の内部で使用される primp)は、x86-64 と arm64 の両方について Linux、macOS、Windows 用のホイールを公開しているため、macOS と Windows でも動作するはずです。ただし、これらの OS は CI の対象外です。問題がないことを確認する前に、各環境で uv run pytest と uv run websearch doctor を実行してください。プラットフォームに関する注意点は 2 つあります。websearch doctor は WEBSEARCH_VPN=any の場合にネットワークインターフェイス名を読み取りますが、Windows では有用な情報を取得できないため、トンネルは未確認として報告されます。一方、WEBSEARCH_VPN=nordvpn は HTTP チェックを使用するため、どの環境でも動作します。また、Docker の SearXNG スタックは bash スクリプトで制御されるため、Windows では WSL または Git Bash が必要です(websearch searxng up は Python で実装されているため、この要件はありません)。
コントラクトテストでは、凍結された JSON Schema に照らして実際の出力を検証するため、コントラクトの形式を崩す変更は CI で失敗します。バージョン管理されたコントラクトに従い、分離されたレイヤーを一度に 1 つずつ構築してください。エンジンや抽出器の追加、交換は、それぞれのアダプターモジュールだけに変更が及びます。
ライセンス
MIT です。詳細は LICENSE をご覧ください。AGPL コンポーネント(たとえば nodriver)に依存するオプションのアンチボット階層は、MIT のコアに同梱せず、個別にインストールする別枠のアダプターとして提供します。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。