searxng-mcp
searxng-mcp
自己ホスト型の SearXNG インスタンスを介したプライベート Web 検索のための MCP サーバーです。結果はローカル ML モデルで再ランク付けされ、全ページのコンテンツは Firecrawl で取得され、オプションの Ollama インスタンスがクエリ拡張と LLM による要約を提供します。
サードパーティの検索 API にクエリを送信せずに Web 検索を必要とする Claude Code および LibreChat エージェントでの使用を想定しています。
Claude Code と homelab-agent のマルチエージェントワークフローを使用して構築されています。このプラットフォームは、AI 支援リサーチのために searxng-mcp を本番環境で使用しているのと同じものです。
クイックスタート
実行中の SearXNG インスタンスが必要です。キャッシュバックエンドを強く推奨します。
最小構成 — Dragonfly/Valkey キャッシュバックエンドを起動し、searxng-mcp を実行します:
docker compose -f docker-compose.example.yml up -d
SEARXNG_URL=http://localhost:8081 CACHE_URL=redis://localhost:6381 npx @tadmstr/searxng-mcpFirecrawl、Crawl4AI、Ollama、Kiwix、アドブロックプロキシ、NATS を含む完全なローカルトポロジーについては、docker-compose.full.yml を参照してください。
Related MCP server: searxng-mcp-bridge
ツール
ツール | 説明 | 主要パラメータ |
| ローカル再ランク付けを伴う SearXNG 経由の検索。より広い結果プールを取得し、関連性で再ランク付けして上位 N 件を返します。SearXNG のネイティブな直接回答、インフォボックス、スペル修正、関連提案は、リストの上と |
|
| 検索、再ランク付け、その後フェッチカスケード(Firecrawl → Crawl4AI → 生 HTTP)を使用して上位結果の完全なコンテンツを取得します。 |
|
| 検索、上位結果の取得、その後 Ollama( |
|
| 任意の公開 URL から読み取り可能な Markdown を取得して抽出します。GitHub ホストは GitHub 高速パスを利用し、YouTube ビデオ URL はトランスクリプトを返し、Reddit スレッド URL は投稿とコメントを返します(いずれも robots 経由でオプトイン、下記参照)。その他はすべてフェッチカスケード(Firecrawl → Crawl4AI → 生 HTTP)を使用します。トークン予算(デフォルト約 8,000 文字)に切り詰められます。 |
|
| サイト全体をクロールし、各ページの URL/タイトル/スニペットのマニフェストを返します。最初に Firecrawl クロールを試み、次にサイトマップ解析、その後オプションの BFS にフォールバックします。ページの完全なコンテンツは Valkey にキャッシュされるため、後続の |
|
| 検索キャッシュ、フェッチキャッシュ、クロールマニフェストキャッシュ、またはすべてを消去します。キャッシュされた結果が古くなる可能性のある急速に変化するトピックを調査するときに便利です。 |
|
| ドメイン機能データベース の読み取り専用ビュー。 |
|
パラメータ
category — general(デフォルト)、news、it、science
time_range — day、week、month、year — 公開日で結果を制限します。全期間の結果が必要な場合は省略します。
fetch_count — 完全なコンテンツを取得する上位再ランク付け結果の数(search_and_fetch のデフォルトは 1、最大 3。search_and_summarize のデフォルトは 3、最大 5)。
domain_profile — 名前付きドメインフィルタープロファイルを適用します: homelab(自己ホスト型/Linux ドキュメントを表示)または dev(Stack Overflow、MDN、npm を表示)。デフォルトのフィルターを使用する場合は省略します。
expand — true の場合、検索前に Ollama(OLLAMA_EXPAND_MODEL)を介してクエリを書き換え、再現率を向上させます。OLLAMA_URL が必要です。デフォルトは EXPAND_QUERIES 環境変数の値です。
language — BCP-47 言語コード(例: en、de)または all で特定の言語に制限します。省略すると SearXNG インスタンスのデフォルトを使用します。search、search_and_fetch、search_and_summarize で利用できます。
engines — 検索を制限するカンマ区切りの SearXNG エンジン名(例: google,duckduckgo)。そのまま転送されます。不明または無効なエンジンは、エラーになる代わりに結果が少なくなります。3 つの検索ツールすべてで利用できます。
site — 結果を 1 つのドメインまたはリストに制限します(例: github.com または ["github.com", "gitlab.com"])。site: クエリ演算子としてベストエフォートで適用されます。ほとんどのエンジン(Google、Bing、DDG、Brave)はこれを尊重しますが、一部は無視します。3 つの検索ツールすべてで利用できます。
max_tokens(fetch_url)— 返されるコンテンツのおおよそのトークン予算(文字 ≈ トークン × 4)。省略するとデフォルトの約 2,000 トークン / 8,000 文字になります。最大 10,000 トークン。
target_selector(fetch_url)— 抽出を特定の要素に限定する CSS セレクター(例: article、main .content)。Firecrawl/Crawl4AI ではネイティブに尊重され、生 HTTP 層ではクライアント側で適用されます。高速パスや一致するものがない場合は無視されます。
wait_for_selector(fetch_url)— JS レンダリングページで抽出前に待機する CSS セレクター。レンダリング層(Firecrawl/Crawl4AI)で尊重されます。生 HTTP(JS なし)では無視されます。
アーキテクチャ
MCP client (stdio)
│
▼
searxng-mcp ──────────────→ cache ($CACHE_URL) → result cache (search 1h, fetch 24h, crawl 6h)
│
├── expand (optional) → Ollama ($OLLAMA_URL) → rewritten query (qwen3:4b)
├── search ───────────→ SearXNG ($SEARXNG_URL) → raw results
├── rerank ───────────→ Reranker ($RERANKER_URL) → ranked results
│ (fallback: SearXNG order if reranker unavailable)
├── fetch content ────┬→ GitHub API (github.com) → markdown
│ ├→ Kiwix ($KIWIX_URL) → ZIM content (Wikipedia/SO/Arch Wiki, fast path)
│ ├→ Hister ($HISTER_URL) → browsing-history index (login-walled/JS-heavy fast path)
│ ├→ Firecrawl ($FIRECRAWL_URL) → page markdown (tier 1)
│ ├→ Crawl4AI ($CRAWL4AI_URL) → page markdown (tier 2, optional; via $ADBLOCK_PROXY_URL if set)
│ ├→ Raw HTTP + Readability → page markdown (tier 3 fallback; via $ADBLOCK_PROXY_URL if set)
│ └→ Wayback Machine (opt-in) → archived page markdown (tier 4, $WAYBACK_ENABLED)
├── crawl_site ───────┬→ Firecrawl crawl → page manifest (phase 1)
│ ├→ Sitemap parsing → page manifest (phase 2 fallback, fast-xml-parser)
│ └→ BFS crawl (opt-in) → page manifest (phase 3, $CRAWL_BFS_ENABLED)
└── summarize (opt.) → Ollama ($OLLAMA_URL) → synthesized summary ($OLLAMA_SUMMARIZE_MODEL)flowchart TD
entry["fetchPage(url)"]
cache{"Valkey cache hit?"}
cached["→ return cached { title, url, text }"]
github{"GitHub host?\ngithub.com · raw · api"}
gh_fetch["GitHub API / raw.githubusercontent.com / api.github.com\n→ return"]
llms{"llms.txt domain?"}
llms_fetch["Probe /llms-full.txt\nextract matching section\n→ return"]
kiwix{"Kiwix host?\nKIWIX_URL set"}
kiwix_fetch["Local Kiwix ZIM\nWikipedia · Stack Overflow · Arch Wiki\n→ cache + return"]
pdf{".pdf URL?"}
robots["robots.txt pre-check — tiers 1–3\ndisallowed → RobotsDisallowedError (cached 24h)"]
tier_skip(["Per-domain tier skip\nsuccess rate <30% over ≥10 tries\nor tier_skip operator override"])
t1["Tier 1 — Firecrawl\n$FIRECRAWL_URL"]
t2["Tier 2 — Crawl4AI\n$CRAWL4AI_URL · optional\nadblock proxy if $ADBLOCK_PROXY_URL"]
t3["Tier 3 — Raw HTTP + Readability\nfallback: raw HTML slice\nadblock proxy if $ADBLOCK_PROXY_URL"]
t4["Tier 4 — Wayback Machine CDX API\narchived snapshot · WAYBACK_ENABLED=true"]
post["Post-extraction\nJSON-LD Article · title cascade\nog:title → twitter:title → title → h1 → URL"]
result["→ return { title, url, text }"]
entry --> cache
cache -->|hit| cached
cache -->|miss| github
github -->|yes| gh_fetch
github -->|no| llms
llms -->|yes| llms_fetch
llms -->|no| kiwix
kiwix -->|yes| kiwix_fetch
kiwix -->|no| pdf
pdf -->|"yes — skip tier 1"| t2
pdf -->|no| robots
robots --> tier_skip
tier_skip --> t1
t1 -->|success| post
t1 -->|"empty / error"| t2
t2 -->|success| post
t2 -->|"empty / error"| t3
t3 -->|success| post
t3 -->|"empty / error"| t4
t4 -->|success| result
post --> result
style entry fill:#ffffff,stroke:#333333,color:#000000
style cache fill:#ffffff,stroke:#333333,color:#000000
style cached fill:#ffffff,stroke:#333333,color:#000000
style github fill:#dae8fc,stroke:#6c8ebf,color:#000000
style gh_fetch fill:#dae8fc,stroke:#6c8ebf,color:#000000
style llms fill:#dae8fc,stroke:#6c8ebf,color:#000000
style llms_fetch fill:#dae8fc,stroke:#6c8ebf,color:#000000
style kiwix fill:#fff9c4,stroke:#b8860b,color:#000000
style kiwix_fetch fill:#fff9c4,stroke:#b8860b,color:#000000
style pdf fill:#ffffff,stroke:#333333,color:#000000
style robots fill:#ffffff,stroke:#333333,color:#000000
style tier_skip fill:#f5f5f5,stroke:#666666,color:#000000
style t1 fill:#d5e8d4,stroke:#5a8a4a,color:#000000
style t2 fill:#d5e8d4,stroke:#5a8a4a,color:#000000
style t3 fill:#d5e8d4,stroke:#5a8a4a,color:#000000
style t4 fill:#f8cecc,stroke:#a03030,color:#000000
style post fill:#e1d5e7,stroke:#7a5a8a,color:#000000
style result fill:#ffffff,stroke:#333333,color:#000000SearXNG と Firecrawl は必須です。Crawl4AI、Valkey、Ollama、Kiwix、および再ランク付けはオプションです。サーバーは、これらのいずれかが利用できない場合でもグレースフルに劣化します。
アドブロッキング
searxng-mcp は、フェッチ層グループごとに 1 つずつ、2 つの独立したアドブロッキングサイドカーを使用します:
サイドカー | 層 | メカニズム |
| 層 1(Firecrawl) | CDP レベルのインターセプト — 完全な HTTPS フィルタリング、同じブラウザプロセス |
| 層 2+3(Crawl4AI、生フェッチ) | HTTP フォワードプロキシ — プレーン HTTP 広告ドメインをフィルタリング |
層 1 — Puppeteer アドブロック
Firecrawl が使用する firecrawl-puppeteer サービスは、@ghostery/adblocker-puppeteer をアップストリームの trieve/puppeteer-service-ts に重ねるカスタムイメージ(docker/puppeteer-adblock/)を実行します。EasyList + EasyPrivacy は起動時にロードされ、168 時間ごとに更新されます。ブロッカーは Firecrawl が作成するすべてのページに適用されます。広告の多いサイトのフェッチを高速化し、レンダリングされた DOM サイズを縮小します。
環境変数:
変数 | デフォルト | 説明 |
| 未設定 |
|
| EasyList + EasyPrivacy | フィルターリスト URL のカンマ区切りリスト。 |
|
| ブロッカーが設定された URL から再構築する頻度。 |
ベースイメージは SHA256 ダイジェストで固定されています。変更をデプロイするには、サービスを再構築して再起動します:
docker compose -f ~/docker/firecrawl-simple/docker-compose.yml up -d --build firecrawl-puppeteerドメインごとのバイパス: domains.json は、将来のオペレーターオーバーライドのために adblock_skip スロットを予約しています。配線はまだ実装されていません。Firecrawl がカスタムヘッダーを puppeteer-service に転送する必要がありますが、これは現在の API の一部ではありません。スコープクリープ項目 I として追跡されています。
層 2+3 — アドブロックプロキシ
ADBLOCK_PROXY_URL(例: http://adblock-proxy:8118)を設定すると、Crawl4AI と生の Node fetch リクエストが、広告とトラッカーリクエストをフィルタリングする HTTP フォワードプロキシを経由します。HTTPS CONNECT トンネルは変更されずに通過します。MITM はないため、フィルタリングはプレーン HTTP 広告ドメインにのみ適用されます。層 1 の puppeteer フックは、その層の完全な HTTPS フィルタリングをすでに処理しています。プロキシは、層 2 と 3 で漏れるものをカバーします。
サービス定義、構成オプション、デプロイ手順については、docker/adblock-proxy/ を参照してください(docker-compose.full.yml に含まれています)。
データ駆動型層ルーティング
フェッチカスケードを呼び出す前に、searxng-mcp はドメインの tier_stats_30d(ドメイン機能データベース を参照)を読み取り、少なくとも 10 回の試行で成功率が 30% 未満の層をスキップします。コールドスタートドメイン(10 回未満の試行)はデフォルトのカスケードを維持します。各スキップは reason: low_success_rate を含む searxng.fetch.tier.skipped NATS イベントを発行し、searxng_fetch_total{outcome=skipped} をインクリメントします。
オペレーターオーバーライド。 domains.json に tier_skip マップを追加して、統計に関係なく層を強制的にスキップします:
{
"tier_skip": {
"example-bot-blocked.com": ["tier1"],
"another-site.example": ["tier1", "tier2"]
}
}tier_skip キーは、ベアドメイン(example.com はドメインとすべてのサブドメインに一致)またはドメイン + パスプレフィックス(example.com/api/)にできます。ファイルはホットリロードされます。再起動は不要です。手動オーバーライドは reason: operator_override を発行します。
コンテンツタイプ高速パス
構造化された非HTMLコンテンツ(application/json、任意の*+json、XML、YAML、TOML、CSV、またはtext/plain)を提供するURLは、HEADプローブで検出され、完全なFirecrawl/Crawl4AIカスケードではなく、生のHTTP層に直接ルーティングされます。JSONはフェンス付きコードブロック内でプリティプリントされて返されます。以前は、ヘッドレスブラウザにJSON APIレスポンスやCDNアセットをレンダリングするよう依頼すると空のマークダウンが返されたため、APIおよびCDNエンドポイント(registry.npmjs.org、api.osv.dev、cdn.jsdelivr.netなど)は単に失敗していました。
保証事項:
プローブはフェイルオープンです。到達不能なホスト、
HEADを拒否するサーバー、または読み取り不能/解析不能なContent-Typeヘッダーはすべて、通常のカスケードにそのままフォールスルーします。application/xhtml+xmlは意図的に除外されています。これはブラウザ向けのマークアップであり、構造化データではありません。サーバーが
text/plainと誤ってラベル付けしたHTMLは、生のテキストブロックとしてダンプされるのではなく、HTMLとして解析されます。
ドメイン機能データベース
各フェッチは、searxng-mcpがターゲットドメインについて学習した内容をValkeyのdomain:<hostname>(90日TTL、schema_version 5)に記録します。レコードごとにキャプチャされるもの:
tier_stats_30d.{tier1,tier2,tier3,tier4,github}.{attempts, ok, fail, last_fail_reason, window_start_ms}— 30日間のローリングウィンドウにおけるティアごとのフェッチ成功率。カットオフは読み取り時に適用され、ティアルーティングの決定とdomain_statsレポートの両方で共有されるため、両者が矛盾することはありません。一度フェッチされてからアイドル状態のままのドメインは、次の書き込みまで古い数値が残るのではなく、実際に空のウィンドウを報告します。tier4(Wayback Machine)スロットは、WAYBACK_ENABLED=trueの場合にのみ記録されます。githubスロットは、GitHub高速パス(raw.githubusercontent.com/api.github.com/github.comREADMEフェッチ)を記録します。これはティアカスケードをバイパスしますが、ここでは追跡されます。schema_versionのバンプは既存のレコードを新規に再構築します。現在アイドル状態のドメインの累積ウィンドウは破棄されます(1→2、2→3、3→4、4→5のバンプ全体で前例があります)。capabilities.metadata_fetch.{attempts, ok, fail, last_fail_reason}— メタデータサイドチャネルフェッチ(fetchRawHtmlForMetadata、JSON-LD/og:titleサンプリングに使用)の成功/失敗。これは「このドメインにそもそも到達可能か」に答えるものであり、「フルコンテンツ配信が成功したか」ではないため、tier_stats_30dとは別に追跡されます。capabilities.seen_in_search.{count, last_seen_ms}— ドメインがsearch結果にどのくらい頻繁に現れるか。searxSearch()によってすべてのリターンパス(キャッシュヒットを含む)でファイアアンドフォーゲットで書き込まれ、フェッチは実行されないため、ドメインはフェッチされる前に追跡できます。capabilities.robots_txt.{present, fetched, allows_us}— robots.txtの存在と、それが私たちを許可するかどうかcapabilities.llms_full_txt.{present, size_bytes, last_checked}— ドメインが/llms-full.txtを提供するかどうかcapabilities.json_ld_article.{sampled, present, last_sampled_at}— ページがArticleスキーマのJSON-LDをまったく持っているかどうか(Schema.orgのArticle/NewsArticle/BlogPosting/TechArticleおよびScholarlyArticle/OpinionNewsArticle/LiveBlogPostingなどのサブタイプ。ベア名または完全修飾されたhttps://schema.org/...@typeで一致)。そのスキーマに抽出可能な本文テキストがあるかどうかとは無関係です。多くのサイトはarticleBodyのない見出し/メタデータJSON-LDを公開しており、これは抽出後処理が実際にそれを使用するかどうかとは別の懸念事項です。capabilities.og_title.{sampled, present, last_sampled_at}—<meta property="og:title">についても同様preferred_strategy— 現在は、存在するプローブが着地したときにllms_full_txtに設定されます。将来のフェーズではこれを使用してティアカスケードをスキップします。
バンドルされたCLIでレコードを検査するか、エージェントからdomain_statsツール(単一ドメインまたは集計、ツールを参照)を介してクエリします:
pnpm dump-domain docs.anthropic.comdump-domainは、ウィンドウが期限切れになった場合と、ティアにデータがまったくない場合を区別し、両方を同じように表示することはありません。
同じホスト名に対する同時更新(1回のフェッチ中に並行して発火するティア試行、robotsプローブ、抽出後サンプルレコーダー)は、サーバー側のLua compare-and-setと、単一プロセス自身のライター間の競合を除去するプロセス内のキーごとのキューを組み合わせて直列化され、CASはプロセス間の真に同時の書き込みのみを調停する必要があります。v3.17.0より前のバージョンでは、共有接続に対するWATCH/MULTI/EXECの読み取り-変更-書き込みを使用していましたが、これは実際には同時ライターを直列化しません。その結果、v3.17.0より前に収集されたデータは大幅に不完全でした。アップグレードすると、スキーマバンプを介して既存のティア統計が破棄されます。アップグレード直後はdomain_statsがほぼ空に読み取られ、その後数日かけて再充填されると予想されます。
ドメインDBの永続化
ドメインDBは90日TTLと30日間のローリングウィンドウの下でValkeyにのみ存在するため、キャッシュフラッシュまたはTTLの期限切れにより、再取得にコストがかかる機能学習が消去されます。2つのCLIがそれを永続化します:
pnpm domain-db-maintenance # SCAN all domain:* records → write a dated JSON snapshot (+ prune) and emit OTel gauges
pnpm restore-domain-db # re-seed the domain-db from the newest snapshot after a flushdomain-db-maintenanceはスタンドアロンのジョブです(cronまたはPM2 cron-restartを介してスケジュールで実行します。searxng-mcpはそれぞれが発火する複数の並行するエージェントごとのstdio子プロセスとして実行されるため、プロセス内タイマーとしては実行しないでください)。1つの境界付きSCANが両方の出力を供給します:永続的な日付付きスナップショットと、OTEL_EXPORTER_OTLP_ENDPOINTが設定されている場合は、終了前に強制フラッシュされるゲージ(searxng_domains_tracked、searxng_domains_failing、searxng_domain_tier_success_ratio{tier})。restore-domain-dbは、欠落しているキー、またはライブレコードがスナップショットよりも厳密に古いキー(last_fetchを比較)のみを再シードします。より新しいか等しいライブレコードを上書きすることはないため、ライブで部分的に入力されたValkey(例:自動フラッシュリカバリのためのサービス起動シーケンス内)に対して実行しても安全です。
環境変数 | デフォルト | 目的 |
|
| 日付付きスナップショットが書き込まれる/読み取られる場所。デプロイメントでは永続パス(appdataまたはNFSマウント)に設定します。 |
|
| 保持するスナップショットの数。古いものはメンテナンス実行ごとに削除されます。 |
llms.txt高速パス
domains.json(llms_txt配列)のホワイトリスト登録されたドキュメントドメインに対して、fetchPageは最初に<origin>/llms-full.txtを試し、任意のティアを呼び出す前に要求されたURLに一致するセクションを抽出します。これにより、十分に計装されたドキュメントサイトに対してpuppeteerを実行することを回避し、クリーンなマークダウンセクションを直接返します。プローブの結果と完全な本文はValkey(llms:<origin>:full、存在する/存在しないで24時間/7日)にキャッシュされます。デフォルトのホワイトリスト:docs.anthropic.com、docs.openai.com、docs.stripe.com、docs.crawl4ai.com、docs.firecrawl.dev、docs.cursor.com。domains.jsonを編集して拡張します。ファイルはホットリロードされます。
Kiwix高速パス
KIWIX_URLが設定されている場合、既知のオフライン対応ホストに対するフェッチリクエストはFirecrawl/Crawl4AIカスケードの前にインターセプトされ、ローカルのKiwix ZIMアーカイブから提供されます。これにより、Wikipediaのようなサイト(ヘッドレススクレイパーをブロックする)の100%のティア1失敗率が排除され、外部ネットワークトラフィックゼロでクリーンで読み取り可能なコンテンツが返されます。
サポートされているホストとZIMブック(kiwix-serveは--nodatealiases / -zで実行する必要があります):
ホスト | ZIMブック |
|
|
|
|
|
|
Kiwixパスはllms-txt高速パスの後、robotsゲートの前に実行されます。Kiwixリクエストが失敗するか空を返した場合、完全なティアカスケードが通常どおり実行されます。KIWIX_URLが設定されていない場合、この機能はオーバーヘッドを追加しません。isKiwixHost()は即座にfalseを返します。
KIWIX_URLをkiwix-serveのベースURL(例:http://localhost:8292)に設定します。
YouTubeおよびReddit高速パス
fetch_urlはYouTubeビデオURL(youtube.com、youtu.be)とRedditスレッドURLを認識し、レンダリングされたページをスクレイピングする代わりに直接提供できます:
YouTube — ウォッチページからビデオのキャプショントラックを抽出し、トランスクリプトを返します。
YOUTUBE_TRANSCRIPT_ENABLED(デフォルトオン)で有効になります。Reddit — 公開
.jsonビューをフェッチし、投稿とトップコメントを標準の{title, url, text}形式で返します。HTTP 429ではフォールスルーします。REDDIT_FASTPATH_ENABLED(デフォルトオン)で有効になります。
どちらも非公式で文書化されていないエンドポイント(YouTubeのtimedtext API、Redditの.json)に依存しています。ベストエフォートでSLAはありません。どちらも上流の変更で壊れる可能性があるため、キルスイッチがあります。ミスが発生すると、リクエストは通常のティアカスケードにフォールスルーします(それでもYouTubeページのタイトル/説明を取得できます)。
robots.txt: 両方のエンドポイントはサイトのrobots.txtによって禁止されています(Redditはすべてを禁止し、YouTubeはトランスクリプトが存在する/api/を禁止します)。デフォルトでは、これらの高速パスはそれを尊重して休止状態のままで、カスケードにフォールスルーします。独自のインスタンスでは、YOUTUBE_IGNORE_ROBOTS=true / REDDIT_IGNORE_ROBOTS=trueで直接フェッチをオプトインできます。
サイトクローリング
crawl_siteはサイト全体をクロールし、見つかった各ページのURL/タイトル/スニペットのマニフェストを返します。3フェーズの戦略カスケードを使用します:
Firecrawlクロール — Firecrawlにクロールジョブを送信し(
/crawlエンドポイント)、完了するまでポーリングし、完全なページリストを返します。FIRECRAWL_CRAWL_POLL_INTERVAL_MSとFIRECRAWL_CRAWL_MAX_WAIT_MSによって制御されます。サイトマップ解析 — Firecrawlが失敗するか空を返した場合、
/sitemap.xml(およびリンクされたサイトマップ)をフェッチし、タイトル/スニペット付きのURLを抽出します。サイトマップXML解析にはfast-xml-parserを使用します。BFSクロール(オプトイン) — サイトマップ解析も失敗した場合、指定されたURLから
CRAWL_BFS_MAX_DEPTHリンクホップまで幅優先クロールを実行します。CRAWL_BFS_ENABLED=trueまたはbfsツールパラメータがtrueの場合にのみ実行されます。
クロール中にフェッチされた完全なページコンテンツはValkeyにキャッシュされます(TTL:CRAWL_MANIFEST_TTL_SECONDS、デフォルト6時間)。マニフェスト内の任意のURLに対する後続のfetch_url呼び出しは、キャッシュから即座に返されます。フォローアップ読み取りのフェッチオーバーヘッドはゼロです。
マニフェストキャッシュはclear_cache(target="crawl")でクリアできます。
Wayback Machineフォールバック
WAYBACK_ENABLED=trueの場合、3つの主要なティアすべてが失敗したときに、4番目のティアがWayback Machine CDX APIにアーカイブされたスナップショットを照会します。返されたコンテンツには、発信元ヘッダー([Archived snapshot – <timestamp> – <original_url>])がプレフィックスとして付けられ、呼び出し元はコンテンツが現在のページ状態を反映していない可能性があることを認識できます。
フェッチ品質
任意のティアが生のHTMLを含むコンテンツを返した後、抽出後パスがタイトルと本文の品質を向上させます:
JSON-LD Article抽出 — Schema.orgの
Article/NewsArticle/BlogPosting/TechArticleブロックは、ティア1のクロームスクレイピングよりもクリーンなheadlineとarticleBodyを提供します(スクリプトタグごとに1 MBのサイズ上限)。タイトルカスケード —
og:title→twitter:title→<title>(パブリッシャーサフィックス削除付き)→ 最初の<h1>→ URLの順にフォールバックします。ティア2 Readability比較 — Crawl4AIがマークダウンを返す場合、JSDOM+Readabilityもその生のHTMLに対して実行され、そのテキストがより長い場合(またはCrawl4AIが500文字未満を返す場合は無条件に)優先されます。
回復力
キャッシュは検索を決してハングさせません。 Valkey クライアントは
CACHE_COMMAND_TIMEOUT_MS/CACHE_CONNECT_TIMEOUT_MS/CACHE_MAX_RETRIES_PER_REQUESTによって制限されます(設定 を参照)。ストールまたは CPU スパイクしたキャッシュバックエンドは、永久にハングする代わりにコマンドを拒否します。既存のフェイルソフト処理は、その拒否をスローではなくキャッシュミス(ライブで提供)に格下げします。キャッシュ接続失敗、クライアントエラー、コマンドごとのエラーは、スロットルされた[searxng-mcp]stderr 行を出力します(キーごとに重複排除されるため、持続的な障害は洪水ではなく定期的なパンくずリストを残します)。stderr は、デプロイされた PM2 プロセスに配線された唯一のテレメトリシンクです。プロセスクラッシュハンドラ —
uncaughtExceptionはログを記録してから終了コード 1 で終了します(クリーンな PM2 再起動)。unhandledRejectionはログを記録して続行し、共有プロセスを静かにクラッシュさせません。グレースフルデグラデーション警告 — リランカーのフォールバックと Ollama/LLM の expand + summarize フォールバックは、品質を静かに低下させるとき(リランカーが利用不可、LLM バックエンドに到達不能)に、それぞれスロットルされた stderr 行を 1 行出力します。
バージョンは単一ソース で、実行時に
package.jsonから取得されます(src/version.ts)。McpServerのバージョン、OTel トレーサー/メーターのバージョン、および送信USER_AGENTはすべてそれを追跡するため、独立してドリフトすることはありません。
可観測性(オプトイン)
トレーシング、メトリクス、イベント公開は完全にオプトインです。以下の環境変数が設定されていない場合、サーバーは可観測性のオーバーヘッドがゼロで、実行時に OpenTelemetry や NATS パッケージを読み込むことはありません。
OpenTelemetry(トレース + メトリクス) — OTEL_EXPORTER_OTLP_ENDPOINT をコレクターの HTTP エンドポイントに設定すると、サーバーは以下を出力します:
スパン(リクエストごと):
tool.<name>→expand_query? →searxng_request→rerank→fetch(×N)→tier1_firecrawl|tier2_crawl4ai|tier3_rawfetch→post_extract。さらにsearch_and_summarize用のsummarize_llm。カウンター:
searxng_search_total{profile, expand}、searxng_fetch_total{tier, outcome}、searxng_cache_total{namespace, outcome}、searxng_errors_total{stage, error_type}。ヒストグラム:
searxng_search_duration_seconds{profile}、searxng_fetch_duration_seconds{tier, outcome}。
標準の OTEL 環境変数が適用されます(OTEL_SERVICE_NAME はデフォルトで searxng-mcp)。
NATS イベント — NATS_URL(例:nats://localhost:4222)を設定すると、サーバーはすべての検索、フェッチ、キャッシュヒット/ミス、robots スキップ、エラーに対して構造化イベントを公開します。認証は NATS_CREDS(JWT クレデンシャルファイル)または NATS_USER/NATS_PASSWORD(bcrypt ユーザー名/パスワード)を介して行われます。両方が設定されている場合はクレデンシャルファイル認証が優先されます。サブジェクト:
Subject | When |
| 検索ツールが呼び出されたとき |
| 検索が返されたとき(ソース、レイテンシ、リランク適用を含む) |
|
|
| ティアが空を返したか例外を投げたとき |
| robots.txt で禁止されたとき |
| フェッチが解決したとき( |
| Valkey ルックアップのたびに |
| ステージタグ付きエラー |
各エンベロープには request_id と(OTel が有効な場合)trace_id が含まれるため、サブスクライバーは 2 つのストリームを結合できます。サブジェクトプレフィックスは NATS_SUBJECT_PREFIX で上書き可能です。検索クエリは search.* イベントを通じて流れます。下流のコンシューマーは PII のスクラビングに責任があります。
ポライトネス
正直な User-Agent — 送信リクエストは
searxng-mcp/<version> (+https://github.com/TadMSTR/searxng-mcp; personal research)として識別されます。robots.txt 準拠 —
/robots.txtはオリジンごとに 1 回取得され、Valkey のrobots:<origin>キーで 24 時間キャッシュされます。禁止されたパスはティアが実行される前にスキップされ、skipped_robots url=… reason=…としてログに記録されます。
トランスポート
stdio(デフォルト)— Claude Code MCP プラグインおよび LibreChat の stdio 設定と互換性があります。
HTTP — SEARXNG_MCP_TRANSPORT=http を設定すると、マルチクライアントデプロイメントや Docker ベースのセットアップに適した共有 HTTP/SSE サーバーとして実行されます。SEARXNG_MCP_HOST:SEARXNG_MCP_PORT(デフォルト 127.0.0.1:3001)にバインドします:
SEARXNG_MCP_TRANSPORT=http SEARXNG_MCP_PORT=3001 npx @tadmstr/searxng-mcpClaude Code を HTTP サーバーに対して登録する:
claude mcp add-json searxng --scope user '{
"type": "http",
"url": "http://localhost:3001/mcp"
}'セッションは Mcp-Session-Id ヘッダーでキー付けされるため、複数のクライアントが同じ共有プロセスに同時に接続できます。アイドルセッションは HTTP_SESSION_IDLE_TIMEOUT_MS 後に掃除され、HTTP_MAX_SESSIONS でハードキャップされます — 設定 を参照してください。
HTTP トランスポート認証
HTTP トランスポートはデフォルトでは認証なしです。これはデフォルトで 127.0.0.1 にバインドするため安全です。SEARXNG_MCP_HOST を他の値(コンテナで実行するために必要な 0.0.0.0 を含む)に変更する場合は、SEARXNG_MCP_AUTH_TOKEN も設定してください:
SEARXNG_MCP_AUTH_TOKEN=$(openssl rand -hex 32)設定されている場合、GET /health を除くすべてのリクエストは、RFC 6750 のベアラークレデンシャルとしてトークンを保持する必要があります:
Authorization: Bearer <token>それ以外のもの(ヘッダーなし、異なるスキーム、間違ったトークン)は、WWW-Authenticate: Bearer と JSON-RPC エラーボディを伴う 401 を受け取ります。応答は 3 つのケースすべてで同一であり、提示されたクレデンシャルを決してエコーしません。トークンは SHA-256 ダイジェストとして比較されるため、比較は定数時間であり、長さ情報を漏らしません。
認証済みサーバーを Claude Code に登録する:
claude mcp add-json searxng --scope user '{
"type": "http",
"url": "http://localhost:3001/mcp",
"headers": {"Authorization": "Bearer <token>"}
}'変数を未設定のままにすると、以前の動作が正確に維持されるため、stdio ユーザーと既存のループバックバインド HTTP デプロイメントは変更を必要としません。呼び出し元ごとの認可モデルはありません。単一のトークンはサーバーへのアクセスを認証し、特定のクライアント ID を認証するわけではありません。起動時に、トークンなしの非ループバックバインドは警告をログに記録します。
GET /health は意図的にチェックから除外されています。 これはコンテナのヘルスチェックとモニタリングの liveness プローブであり、入力を受け取らず、その応答(status、cache、sessions)には秘密情報が含まれません。
GET /health — 認証なしの liveness プローブで、MCP エンドポイントとともに localhost にバインドされています。制限付きキャッシュコマンドタイムアウトを通じて Valkey に ping を送信し(チェック自体がハングすることはありません)、以下を返します:
{"status": "ok", "cache": "up", "sessions": 3}または、キャッシュバックエンドに到達できない場合:
{"status": "degraded", "cache": "degraded", "sessions": 3}sessions はライブ HTTP セッション数です。キャッシュバックエンドを直接計測せずに MCP 側から劣化したキャッシュを検出するためのシステム管理者モニタリングに役立ちます。
前提条件
Node.js 20+
pnpm(または npm)
実行中の SearXNG インスタンス
実行中の Firecrawl インスタンス
Jina 互換の
/v1/rerankエンドポイントを公開する実行中のリランカー(オプション)実行中の Valkey または Redis 互換インスタンス(オプション、結果キャッシュ用)
qwen3:4bおよび/またはqwen3:14bをプルした実行中の Ollama インスタンス(オプション、クエリ拡張と要約用)
SearXNG
SearXNG は JSON 出力形式を有効にする必要があります。settings.yml で:
search:
formats:
- html
- jsonリランカー
リランカーは Jina 互換の /v1/rerank エンドポイントを公開する必要があります。軽量な FlashRank ラッパーが適しています — homelab-agent の docker/reranker/ リファレンスを参照してください。
Firecrawl
Firecrawl 互換のインスタンスならどれでも動作します。ローカルの firecrawl-simple デプロイメントで十分です。インスタンスが認証を必要とする場合は FIRECRAWL_API_KEY を設定してください(認証をスキップするローカルデプロイメントではデフォルトで placeholder-local になります)。
Crawl4AI
Crawl4AI は、Firecrawl が空のコンテンツを返す場合(ボットブロックされたページ、JS 多用サイト)に使用されるオプションの第 2 ティアフェッチフォールバックです。有効にするには CRAWL4AI_URL を設定します。未設定の場合、カスケードは生の HTTP フェッチにスキップします。
docker run -d -p 11235:11235 unclecode/crawl4ai:0.8.6インスタンスが API トークン認証を必要とする場合は、CRAWL4AI_API_TOKEN を設定します。
search_and_summarize パスでは、Crawl4AI リクエストはノイズフィルタリングされたコンテンツ抽出に fit_markdown を使用します。他の呼び出し元(search_and_fetch、fetch_url)は raw_markdown を使用します。
Kiwix(オプション)
kiwix-serve は ZIM アーカイブを HTTP で提供します。必要な ZIM ファイルをダウンロードし、--nodatealiases(-z)を付けて kiwix-serve を実行して、ブック名を安定させてください:
kiwix-serve --port 8292 --nodatealiases /path/to/zims/サポートされている各ホストに必要な ZIM ファイル:
Wikipedia:
wikipedia_en_all_mini(またはmaxi)Stack Overflow:
stackoverflow.com_en_allArch Wiki:
archlinux_en_all_maxi
ZIM ファイルは library.kiwix.org からダウンロードできます。
Hister(オプション)
Hister は Firefox 拡張機能によって入力されるブラウジング履歴インデックスです。HISTER_URL が設定されている場合、fetchPage はティアカスケードを呼び出す前に履歴インデックスをチェックします。ログイン壁や JS 多用ページでスクレイパーが失敗する場合に役立ちます。
HISTER_URL に Hister インスタンスのベース URL を設定し、ベアラートークン認証が必要な場合は HISTER_TOKEN を設定します。
Valkey / Redis
Redis 互換のインスタンスならどれでも動作します。Valkey が推奨されます。検索結果は 1 時間キャッシュされ、フェッチされたページは 24 時間キャッシュされます。利用できない場合、サーバーはキャッシュなしで動作します。
Ollama
expand と search_and_summarize に必要です。必要なモデルをプルしてください:
ollama pull qwen3:4b # query expansion
ollama pull qwen3:14b # summarizationthink: false の動作は自動的に処理されます — 追加の Ollama 設定は不要です。
設定
すべてのサービス URL は環境変数で設定可能です。
Variable | Default | Description |
|
| SearXNGインスタンスのURL |
|
| FirecrawlインスタンスのURL |
|
| RerankerインスタンスのURL |
|
| Firecrawl APIキー(必要な場合) |
| (unset) | GitHub個人アクセストークン — レート制限を60から5,000リクエスト/時に引き上げます |
| (unset) | Ollama APIベースURL — |
| (unset) | 認証付きOllamaプロキシ用のBearerトークン — 設定すると |
|
| クエリ拡張( |
|
|
|
| (unset) |
|
| (unset) | OpenAI互換バックエンドのモデルID。設定すると |
| (unset) | OpenAI互換バックエンド用のBearerトークン — 設定すると |
|
|
|
|
| Redis互換URL — 結果のキャッシュを有効にします。エイリアスとして |
|
| Valkeyコマンドタイムアウト — 停止/CPUスパイクしたキャッシュバックエンドはハングせずに拒否します( |
|
| Valkey接続タイムアウト。 |
|
| Valkeyコマンドが拒否するまでの最大再試行回数。 |
|
| 検索結果キャッシュのTTL(秒) |
|
| 取得したページキャッシュのTTL(秒) |
|
| クロールマニフェストとページコンテンツキャッシュのTTL(秒)(6時間) |
|
|
|
|
|
|
|
| BFSクロールの最大リンクホップ深度 |
|
| Firecrawlクロールジョブの完了を待つときのポーリング間隔 |
|
| サイトマップにフォールバックする前にFirecrawlクロールジョブを待つ最大時間 |
|
| クエリ拡張をグローバルに有効にするには |
| (unset) | Crawl4AIインスタンスURL — Firecrawlが失敗した場合の第2層フェッチフォールバックを有効にします |
| (unset) | APIトークン保護付きのCrawl4AIインスタンス用のオプションのBearerトークン |
|
| Wayback Machine第4層フォールバックを有効にするには |
| (unset) | 第2層(Crawl4AI)と第3層(生のNode fetch)の広告ブロック用HTTPプロキシURL — 例: |
| (unset) | kiwix-serveベースURL(例: |
| (unset) | Hister閲覧履歴インデックスのベースURL — ログイン必須ページやJS多用ページに対して、層カスケードの前にHister高速パスを有効にします。未設定の場合は機能が無効になり、オーバーヘッドはゼロです。 |
| (unset) | Hister API認証用のBearerトークン。 |
|
|
|
|
| YouTubeの |
|
|
|
|
| Redditの |
|
| トランスポートモード: |
|
| HTTPリッスンポート(HTTPトランスポートモードのみ)。 |
|
| HTTPリッスンアドレス(HTTPトランスポートモードのみ)。 |
| (unset) | HTTPトランスポートのみ。設定すると、 |
|
| HTTPトランスポートのみ。これより長くアイドル状態のセッションはバックグラウンドスイープによって追い出されます(処理中のリクエストがあるセッションは除外されるため、長時間の |
|
| HTTPトランスポートのみ。ハードキャップのバックストップ — セッションマップがこれを超えた場合、アイドルタイムアウトに関係なく、最も最近使用されていないアイドルセッションが追い出されます。 |
| (unset) | bcryptユーザー名/パスワード認証用のNATSユーザー名。 |
| (unset) | NATSパスワード — |
インストール
npm(推奨)
npm install -g @tadmstr/searxng-mcpまたは npx で直接実行する:
npx @tadmstr/searxng-mcpソースから
git clone https://github.com/TadMSTR/searxng-mcp.git
cd searxng-mcp
pnpm install
pnpm build出力: build/src/index.js
MCP クライアント設定
Claude Code (CLI)
推奨される方法は、claude mcp add-json を使用して、完全な環境変数サポートでサーバーを登録することです:
claude mcp add-json searxng --scope user '{
"command": "npx",
"args": ["-y", "@tadmstr/searxng-mcp"],
"env": {
"SEARXNG_URL": "http://localhost:8081",
"FIRECRAWL_URL": "http://localhost:3002",
"RERANKER_URL": "http://localhost:8787",
"OLLAMA_URL": "http://localhost:11434",
"CACHE_URL": "redis://localhost:6379",
"CACHE_TTL_SECONDS": "3600",
"FETCH_CACHE_TTL_SECONDS": "86400",
"EXPAND_QUERIES": "false",
"CRAWL4AI_URL": "http://localhost:11235"
}
}'これは ~/.claude.json に書き込まれます。~/.claude/settings.json に searxng を追加しないでください。そのファイルは Claude Code での MCP 環境変数の注入には使用されません。
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"searxng": {
"command": "npx",
"args": ["-y", "@tadmstr/searxng-mcp"],
"env": {
"SEARXNG_URL": "http://localhost:8081",
"FIRECRAWL_URL": "http://localhost:3002",
"RERANKER_URL": "http://localhost:8787",
"OLLAMA_URL": "http://localhost:11434",
"CACHE_URL": "redis://localhost:6379",
"CRAWL4AI_URL": "http://localhost:11235"
}
}
}
}LibreChat (librechat.yaml)
mcpServers:
searxng:
type: stdio
command: node
args:
- /path/to/searxng-mcp/build/src/index.js
env:
SEARXNG_URL: http://localhost:8081
FIRECRAWL_URL: http://localhost:3002
RERANKER_URL: http://localhost:8787
OLLAMA_URL: http://localhost:11434
CACHE_URL: redis://localhost:6379
CRAWL4AI_URL: http://localhost:11235GitHub URL
GitHub URL は Firecrawl を使わずにネイティブに処理されます。githubFetch はホスト名に基づいて振り分けます:
リポジトリルート (
github.com/owner/repo) — GitHub API 経由で README を取得しますファイル blob (
github.com/owner/repo/blob/branch/path/to/file) —raw.githubusercontent.comに書き換えて raw コンテンツを取得しますRaw ファイル (
raw.githubusercontent.com/...) — そのまま直接取得しますAPI (
api.github.com/...) — レスポンスをデコード(base64 のcontentフィールド)または JSON として整形出力します
以前は、raw.githubusercontent.com と api.github.com の直接 URL は github.com のみにマッチし、HTML スクレイピングの階層カスケードにフォールスルーしていました。そのカスケードは raw テキストファイルや素の JSON レスポンスをレンダリングできないため、100% 失敗していました。現在は GitHub の高速パスを利用します。
未認証のリクエストは 1 時間あたり 60 回に制限されています。GITHUB_TOKEN を設定すると、1 時間あたり 5,000 回に引き上げられます。
セキュリティ
URL の安全性(SSRF)
呼び出し元が影響を与える、または検出された URL へのすべてのアウトバウンドフェッチ(raw-HTTP 層、robots.txt / llms.txt / Wayback / sitemap プローブ、BFS クロールのリンク取得、GitHub 高速パス)は、2 つの方法で保護されています:
文字列チェック (
assertPublicUrl) — 非 HTTP(S) URL とプライベート/内部 IP リテラル を拒否します: RFC1918 (10.x,192.168.x,172.16–31.x)、ループバック (127.x,::1)、リンクローカル / クラウドメタデータ (169.254.x)、CGNAT (100.64/10)、IPv6 ULA (fc00::/7) とリンクローカル (fe80::/10)、IPv4 マップ、マルチキャスト/予約範囲。接続時 DNS 検証 — 共有 undici ディスパッチャーで、その
connect.lookupが 解決された アドレス(ソケットが接続する正確なアドレス)を検証します。これにより、公開ホスト名がプライベートアドレスに解決される DNS リバインディング / TOCTOU のギャップを閉じ、すべてのリダイレクトホップ で再実行されるため、リダイレクトチェーンが内部ネットワークに跳ね返ることはありません。
Firecrawl(tier1)と Crawl4AI(tier2)はターゲット URL を自分で解決して取得するため、上記の接続時ディスパッチャーではカバーできません。fetchPage と crawlSite は、いずれかのサービスにディスパッチする直前に assertResolvedPublic(url) を呼び出します。これは、プライベートまたは予約済みの結果を拒否する 1 回限りのホスト名解決であり、そのパスでの一般的な DNS リバインディングのケースを閉じます(サービスが再解決するため、接続時ガードよりも TOCTOU ウィンドウが狭い)。
設定された内部サービス(Firecrawl、Crawl4AI、SearXNG、Ollama、Reranker)は、それぞれの URL で到達され、意図的にガードされていません。
リダイレクト保護
raw-HTTP と GitHub 高速パスのフェッチは、さらに redirect: "manual" を使用し、3xx レスポンスを完全に拒否します(Location ヘッダーは呼び出し元にエコーバックされません)。リダイレクトを追跡するプローブ(robots.txt、llms.txt、sitemap)は、上記の接続時 DNS 検証でカバーされ、各ホップを再チェックします。
トランスポートの露出
stdio にはネットワーク面がありません。HTTP トランスポートはデフォルトで 127.0.0.1 にバインドされ、その構成では認証されません。SEARXNG_MCP_AUTH_TOKEN を設定せずにループバックから外すと、任意の URL の fetch_url や破壊的な clear_cache を含むすべてのツールが、そのポートにルーティングできるものに公開されます。HTTP トランスポート認証 を参照してください。
依存関係の監査
CI はプッシュのたびに pnpm audit を実行します。ロックファイル(pnpm-lock.yaml)は、再現可能で監査可能なビルドのためにコミットされています。
資格情報の取り扱い
サーバーは資格情報を保存またはログ記録しません。API キー(FIRECRAWL_API_KEY、GITHUB_TOKEN、CRAWL4AI_API_TOKEN)は環境変数から読み取られ、それぞれのサービスへのアウトバウンドリクエストでのみ使用されます。
入力検証
環境変数は起動時に検証されます。RERANK_RECENCY_WEIGHT は NaN、負の値、または 1.0 を超える値に対して警告します。数値ツールパラメータは、範囲制約付きの z.coerce.number() を使用します。
コントリビューション
セットアップ手順、コミット規約、PR プロセスについては CONTRIBUTING.md を参照してください。
統合テスト
ドメイン DB の並行性をカバーする実際の Valkey 統合スイートは VALKEY_TEST_URL でゲートされ、未設定の場合は完全にスキップされるため、Valkey が存在しなくても通常の pnpm test は動作します:
VALKEY_TEST_URL=redis://:<password>@<host>:<port>/<scratch-db> pnpm testスクラッチデータベースインデックス を使用してください。スイートは domain:* キーを書き込みおよび削除し、安全ガードとしてインデックス 0 または 1 に対しては実行を拒否します。
ライセンス
MIT
Available Tools
7 toolsclear_cacheA
Purge the search and/or fetch result cache. Useful when researching fast-moving topics where cached results from the past hour may be stale.
| Name | Required | Description | Default |
|---|---|---|---|
| target | No | Which cache to clear: search results, fetched pages, crawl manifests, or all (default all) | all |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description correctly indicates a destructive action ('purge') and specifies the cache targets. However, it lacks details on rate limits, auth requirements, or side effects of clearing different caches.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, both relevant. No fluff. Slightly more structured formatting (e.g., bullet points) could improve, but it's already efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (1 param, no output schema), the description adequately covers purpose and typical scenario. Could mention that clearing cache is immediate or note persistence, but not necessary.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the parameter 'target' is fully described in the schema with enum and default. The description adds no additional meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states the tool purges search and/or fetch result cache, distinguishing it from sibling tools that perform searches, fetches, or crawls. The verb 'purge' and specific resources are explicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides a specific use case: 'researching fast-moving topics where cached results from the past hour may be stale.' Does not explicitly mention when not to use, but the context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
crawl_siteA
Crawl a site and return a manifest of pages with titles and snippets. Full page content is cached — call fetch_url on any page URL for the full text. Strategy: Firecrawl (JS rendering) → sitemap-first → BFS (if enabled).
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Base URL to crawl | |
| max_pages | No | Maximum pages to crawl (default 20, max 100) | |
| exclude_path | No | Exclude URLs matching this path prefix (e.g. '/blog') | |
| include_path | No | Only include URLs matching this path prefix (e.g. '/docs') | |
| same_domain_only | No | Restrict crawl to the same domain (default true) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided; description carries full burden. Discloses caching behavior and crawl strategy (Firecrawl, sitemap-first, BFS). Could mention rate limits or error handling, but sufficient.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with core purpose, each sentence adds value. No redundant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers caching, strategy, and relationship to fetch_url. Lacks detailed return structure of manifest, but adequate for a tool with no output schema and good parameter descriptions.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, baseline 3. Description adds no extra meaning beyond parameter names and default values in schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states 'Crawl a site and return a manifest of pages' (specific verb+resource+output). Distinguishes from sibling fetch_url by noting it caches full page content for later retrieval.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Implied guidance: use fetch_url after crawling for full text. No explicit when-to-use or when-not-to-use compared to search or other siblings. Lacks exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
domain_statsDomain capability statsA
Read the searxng-mcp domain capability database — what it has learned about hosts from fetches: per-tier success rates (tier1-3 cascade, tier4 wayback, github fast path), llms.txt/robots presence, metadata reachability, and search appearances. Provide hostname for one domain's record, or omit it for an aggregate across all tracked domains (per-tier success rates, worst failing domains, seen-but-never-fetched count). Read-only. Aggregate mode reports truncated: true if the internal scan cap is hit.
| Name | Required | Description | Default |
|---|---|---|---|
| hostname | No | Hostname or URL to look up (e.g. 'docs.anthropic.com'). Omit for an aggregate over all tracked domains. |
Output Schema
| Name | Required | Description |
|---|---|---|
| mode | Yes | |
| found | Yes | |
| record | Yes | |
| hostname | Yes | |
| aggregate | Yes | |
| unavailable | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description explicitly labels the operation as read-only and states that aggregate mode reports whether the internal scan cap was hit. It does not describe error behavior or output formatting, but the read-only nature and data scope are well conveyed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is focused and information-dense, listing the relevant statistics and parameter behavior without unnecessary filler. The repeated read-only statement is minor but not harmful, and the overall structure is easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the single optional parameter and the presence of an output schema, the description adequately covers what the tool does, what data it returns, and how to select between per-domain and aggregate modes. No critical usage context appears to be missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The only parameter, hostname, is fully described in both the schema and the description, including an example value and the behavior when omitted. The optional/aggregate distinction is explicit, leaving no ambiguity about parameter usage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool reads the searxng-mcp domain capability database and enumerates the specific statistical data it exposes. It is easily distinguished from the search/fetch sibling tools because its focus is domain capability statistics rather than retrieving or searching content.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains how to use the tool: provide a hostname for one domain or omit it for an aggregate. It does not explicitly contrast when to use this tool versus the sibling search/fetch tools, but the domain-stats purpose makes the appropriate context reasonably clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fetch_urlA
Fetch and extract readable content from any URL. GitHub URLs are fetched via the GitHub API; all others go through a fetch cascade: Firecrawl → Crawl4AI → raw HTTP. Returns clean markdown where possible. Content is trimmed to a token budget (default ~2000 tokens / 8000 chars; raise with max_tokens). Results cached for 24 hours. Blocked domains and private/internal addresses are refused.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | URL to fetch and extract content from | |
| max_tokens | No | Approximate token budget for the returned content (chars ≈ tokens × 4). Omit for the ~2000-token / 8000-char default; max 10000 tokens. | |
| domain_profile | No | Named domain profile to apply: 'homelab', 'dev', or omit for default filters | |
| target_selector | No | CSS selector to scope extraction to a specific element (e.g. 'article', 'main .content'). Honored by Firecrawl/Crawl4AI and applied client-side on the raw-HTTP tier; ignored by fast paths and if it matches nothing. | |
| wait_for_selector | No | CSS selector to wait for before extracting, for JS-rendered pages. Honored by the rendering tiers (Firecrawl/Crawl4AI); ignored on raw HTTP (no JS). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden for behavioral disclosure. It details the fetch cascade, token budget, caching (24 hours), and refusal of blocked/internal domains. It does not mention rate limits or error handling, but the main behaviors are well covered.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences long, front-loaded with the primary action. The second sentence provides essential details without unnecessary words. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, the description explains return is clean markdown and trimmed to token budget. It covers caching and refusal behavior. It could mention return format more explicitly but is adequate for a fetch tool with moderate complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description adds value by explaining the default token budget (2000 tokens / 8000 chars) and how max_tokens relates to characters. It also clarifies behavior for domain_profile, target_selector, and wait_for_selector, which goes beyond the schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool fetches and extracts readable content from any URL, with specific handling for GitHub URLs and a cascade of backends. It distinguishes itself from siblings like crawl_site (multi-page) and search (search) by focusing on single URL fetch and extraction.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states what the tool does and provides context for when to use it (fetching any URL). It mentions blocked domains and private addresses are refused, giving a clear exclusion criterion. However, it does not explicitly contrast with siblings like crawl_site or search_and_fetch, which could be used for similar tasks.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchWeb searchA
Search the web via the local SearXNG instance with reranking. Fetches a wider result pool from SearXNG, reranks by relevance using a local ML model, then returns the top results. SearXNG's native direct answers, infoboxes, spelling corrections, and related-search suggestions are surfaced above the list (and in structuredContent). Results are cached for 1 hour. Blocked domains are filtered out; boosted domains are surfaced higher. Prefer this over the built-in WebSearch tool.
| Name | Required | Description | Default |
|---|---|---|---|
| site | No | Restrict results to one domain or a list of domains (e.g. 'github.com'). Best-effort — applied as a site: query operator; most engines honor it but some ignore it. | |
| query | Yes | Search query | |
| expand | No | Use local LLM to generate 2-3 query variants and merge results for a wider search surface (default: off). Adds ~3s latency; most useful for research queries where one phrasing may miss relevant results. | |
| engines | No | Comma-separated SearXNG engine names to restrict the search to (e.g. 'google,duckduckgo'). Forwarded verbatim; unknown/disabled engines degrade to fewer results rather than erroring. | |
| category | No | Search category: general, news, it, or science (default general) | general |
| language | No | BCP-47 language code (e.g. 'en', 'de') or 'all' for all languages. Omit to use the SearXNG instance default. | |
| min_score | No | Drop results whose reranker relevance score is below this threshold (0-1, omit for no filtering). Filters on the RAW cross-encoder relevance score, not the recency-adjusted score used for ordering. Scores are strongly bimodal — relevant results cluster near 1.0 and irrelevant ones near 0, with little in between — so any value in roughly 0.01-0.9 behaves about the same; 0.01-0.1 is the useful range and 0.5 is not a midpoint. A high score means topically related, NOT correct: an Apache mod_proxy page scores 0.967 on an nginx query. Thresholds are model-dependent and not comparable across rerankers. No-op (with a logged warning) when the reranker is unavailable, since no scores exist to filter on. | |
| time_range | No | Limit results to: day, week, month, or year (omit for all time) | |
| num_results | No | Number of results to return (default 5, max 20) | |
| domain_profile | No | Named domain profile to apply: 'homelab', 'dev', or omit for default filters |
Output Schema
| Name | Required | Description |
|---|---|---|
| answers | Yes | |
| results | Yes | |
| infoboxes | Yes | |
| corrections | Yes | |
| suggestions | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations available, the description carries the full disclosure burden and exceeds it: it explains the reranking pipeline, the surfacing of direct answers and infoboxes in structuredContent, the 1-hour cache, and domain blocking/boosting behavior. These operational details give an agent accurate expectations beyond the basic act of searching.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but compact: the core operation is front-loaded, and each subsequent sentence contributes a distinct behavioral fact such as structured content, caching, filtering, or tool preference. There is no filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 10-parameter tool, the description plus the detailed input schema and output schema cover the important behavioral, filtering, caching, and result-format semantics. The description is complete enough for an agent to invoke the tool correctly and understand what will happen.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3 even though the main description adds little parameter-specific semantics. The description does not need to repeat parameter meaning because the input schema already documents each parameter clearly; the narrative adds only behavioral context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence identifies a specific verb, resource, and pipeline: searching the web via the local SearXNG instance with reranking. This is clear and specific, but it does not explicitly differentiate from sibling tools such as search_and_fetch or search_and_summarize; the only alternative named is the built-in WebSearch tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says to prefer this tool over the built-in WebSearch tool, which is a useful direct comparison. It also notes that the expand option is most useful for research queries. However, it does not provide when-not-to-use guidance for sibling search-related tools, so the guidance is good but not exhaustive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_and_fetchA
Search the web, rerank results, then fetch the full content of the top result(s). GitHub URLs are fetched via the GitHub API; all others go through a fetch cascade: Firecrawl → Crawl4AI → raw HTTP. Results and fetched pages are cached. Blocked domains are filtered. Returns the result list plus clean markdown of the fetched pages.
| Name | Required | Description | Default |
|---|---|---|---|
| site | No | Restrict results to one domain or a list of domains (e.g. 'github.com'). Best-effort — applied as a site: query operator; most engines honor it but some ignore it. | |
| query | Yes | Search query | |
| expand | No | Use local LLM to generate 2-3 query variants and merge results for a wider search surface (default: off). Adds ~3s latency. | |
| engines | No | Comma-separated SearXNG engine names to restrict the search to (e.g. 'google,duckduckgo'). Forwarded verbatim; unknown/disabled engines degrade to fewer results rather than erroring. | |
| category | No | Search category: general, news, it, or science (default general) | general |
| language | No | BCP-47 language code (e.g. 'en', 'de') or 'all' for all languages. Omit to use the SearXNG instance default. | |
| min_score | No | Drop results whose reranker relevance score is below this threshold (0-1, omit for no filtering). Filters on the RAW cross-encoder relevance score, not the recency-adjusted score used for ordering. Scores are strongly bimodal — relevant results cluster near 1.0 and irrelevant ones near 0, with little in between — so any value in roughly 0.01-0.9 behaves about the same; 0.01-0.1 is the useful range and 0.5 is not a midpoint. A high score means topically related, NOT correct: an Apache mod_proxy page scores 0.967 on an nginx query. Thresholds are model-dependent and not comparable across rerankers. No-op (with a logged warning) when the reranker is unavailable, since no scores exist to filter on. | |
| time_range | No | Limit results to: day, week, month, or year (omit for all time) | |
| fetch_count | No | Number of top results to fetch full content for (default 1, max 3) | |
| domain_profile | No | Named domain profile to apply: 'homelab', 'dev', or omit for default filters |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure — and it delivers substantially. It reveals the multi-step pipeline (search → rerank → fetch), the GitHub-API special path, the fallback cascade order (Firecrawl → Crawl4AI → raw HTTP), caching of results and pages, blocked-domain filtering, and the return shape (result list + clean markdown). This far exceeds what the name or schema alone would convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences with the core purpose front-loaded in sentence one, followed by the most operationally important details in priority order: GitHub special-casing, fetch cascade, caching/filtering, and return format. Every sentence earns its place; there is zero redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex 10-parameter tool with no annotations and no output schema, this is close to complete: it covers the pipeline, fetch strategy, caching, filtering, and return format. The gaps are minor — no explicit error behavior, rate limits, or detailed result-entry structure — but the highly descriptive schema compensates for most of what the description omits.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema itself is unusually rich — e.g., `min_score` documents bimodal score distribution, the 0.01-0.1 useful range, model-dependence, and no-op behavior. The description adds pipeline-level framing that helps connect parameters to stages (rerank → min_score, top results → fetch_count), but provides no per-parameter details beyond the schema. The baseline 3 applies as schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb chain and resource: 'Search the web, rerank results, then fetch the full content of the top result(s).' This clearly differentiates it from sibling tools `search` (search only) and `fetch_url` (fetch only) by describing the combined pipeline. The GitHub-API special case and fetch cascade further pin down exactly what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The pipeline description makes the usage context clear: this tool is for when an agent needs both ranked search results AND full page content. The details about caching, blocked-domain filtering, and the fetch cascade give a concrete picture of behavior. However, it never explicitly names alternatives or states when NOT to use it (e.g., 'if only URLs are needed, use search'), so exclusion guidance is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_and_summarizeA
Search, rerank, fetch top results, then synthesize a summary with citations using a local LLM (OLLAMA_SUMMARIZE_MODEL). Returns a structured answer with source attribution. If the LLM is unavailable it falls back to raw fetched content, and that fallback is always announced by a leading '--- summarization unavailable (: ) --- ' marker: if you do not see that marker, what you received is a real synthesis. Best for deep research where you want pre-digested synthesis rather than raw pages.
| Name | Required | Description | Default |
|---|---|---|---|
| site | No | Restrict results to one domain or a list of domains (e.g. 'github.com'). Best-effort — applied as a site: query operator; most engines honor it but some ignore it. | |
| query | Yes | Research query to search for and summarize | |
| expand | No | Use query expansion before searching (default: off) | |
| engines | No | Comma-separated SearXNG engine names to restrict the search to (e.g. 'google,duckduckgo'). Forwarded verbatim; unknown/disabled engines degrade to fewer results rather than erroring. | |
| category | No | Search category: general, news, it, or science (default general) | general |
| language | No | BCP-47 language code (e.g. 'en', 'de') or 'all' for all languages. Omit to use the SearXNG instance default. | |
| min_score | No | Drop results whose reranker relevance score is below this threshold (0-1, omit for no filtering). Filters on the RAW cross-encoder relevance score, not the recency-adjusted score used for ordering. Scores are strongly bimodal — relevant results cluster near 1.0 and irrelevant ones near 0, with little in between — so any value in roughly 0.01-0.9 behaves about the same; 0.01-0.1 is the useful range and 0.5 is not a midpoint. A high score means topically related, NOT correct: an Apache mod_proxy page scores 0.967 on an nginx query. Thresholds are model-dependent and not comparable across rerankers. No-op (with a logged warning) when the reranker is unavailable, since no scores exist to filter on. | |
| time_range | No | Limit results to: day, week, month, or year (omit for all time) | |
| fetch_count | No | Number of top results to fetch and synthesize (default 3, max 5) | |
| domain_profile | No | Named domain profile to apply: 'homelab', 'dev', or omit for default filters |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so thoroughly: it discloses reranking, fetching limits, local LLM synthesis, the explicit fallback marker, min_score no-op warnings, and per-parameter default behaviors.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although long, the description is information-dense and every sentence adds operational detail. The fallback marker is slightly repetitive but still useful for programmatic detection.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, the description covers search expansion, engine restrictions, categories, language, time range, fetch count, scoring thresholds, profiles, and fallback behavior. It is sufficiently complete without an output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
All 10 parameters are documented with meaningful detail beyond the schema. Especially strong for min_score, explaining the bimodal distribution, non-comparability across rerankers, and the no-op warning when unavailable.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states a multi-step purpose: search, rerank, fetch top results, and synthesize a cited summary. Explicitly distinguishes itself from raw-page tools with 'rather than raw pages' and 'deep research'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Tells when to use it ('Best for deep research where you want pre-digested synthesis') and contrastively implies when not to use it (raw pages). Also documents fallback behavior, which helps manage expectations.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
v3.29.0- Changed
domain_stats2 fields changed- changed
Output schema / properties / aggregate / anyOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "domains_tracked": { - "type": "number" - }, - "failing_count": { - "type": "number" - }, - "seen_never_fetched": { - "type": "number" - }, - "tiers": { - "$ref": "#/properties/record/anyOf/0/properties/tiers" - }, - "top_failing": { - "items": { - "additionalProperties": false, - "properties": { - "attempts": { - "type": "number" - }, - "domain": { - "type": "string" - }, - "ok": { - "type": "number" - }, - "success_rate": { - "type": "number" - } - }, - "required": [ - "domain", - "attempts", - "ok", - "success_rate" - ], - "type": "object" - }, - "type": "array" - }, - "truncated": { - "type": "boolean" - } - }, - "required": [ - "domains_tracked", - "seen_never_fetched", - "tiers", - "failing_count", - "top_failing", - "truncated" - ], - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "domains_tracked": { + "type": "number" + }, + "failing_count": { + "type": "number" + }, + "schema_version": { + "type": "number" + }, + "seen_never_fetched": { + "type": "number" + }, + "tiers": { + "$ref": "#/properties/record/anyOf/0/properties/tiers" + }, + "top_failing": { + "items": { + "additionalProperties": false, + "properties": { + "attempts": { + "type": "number" + }, + "domain": { + "type": "string" + }, + "ok": { + "type": "number" + }, + "success_rate": { + "type": "number" + } + }, + "required": [ + "domain", + "attempts", + "ok", + "success_rate" + ], + "type": "object" + }, + "type": "array" + }, + "truncated": { + "type": "boolean" + }, + "window": { + "additionalProperties": false, + "properties": { + "elapsed_ms": { + "type": [ + "number", + "null" + ] + }, + "oldest_sample_ms": { + "type": [ + "number", + "null" + ] + }, + "ttl_ceiling_ms": { + "type": "number" + } + }, + "required": [ + "oldest_sample_ms", + "elapsed_ms", + "ttl_ceiling_ms" + ], + "type": "object" + } + }, + "required": [ + "schema_version", + "window", + "domains_tracked", + "seen_never_fetched", + "tiers", + "failing_count", + "top_failing", + "truncated" + ], + "type": "object" + }, + { + "type": "null" + } +] - added
Output schema / properties / unavailableAdded value: +{ + "type": [ + "string", + "null" + ] +}
4 tool updates
v3.23.0- Changed
domain_stats1 field changed- changed
Output schema / properties / record / anyOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "capabilities": { - "additionalProperties": false, - "properties": { - "llms_full_txt": { - "type": "boolean" - }, - "metadata_fetch_rate": { - "type": [ - "number", - "null" - ] - }, - "robots_allows_us": { - "type": [ - "boolean", - "null" - ] - }, - "seen_in_search": { - "type": "number" - } - }, - "required": [ - "llms_full_txt", - "robots_allows_us", - "metadata_fetch_rate", - "seen_in_search" - ], - "type": "object" - }, - "domain": { - "type": "string" - }, - "first_seen": { - "type": "string" - }, - "last_fetch": { - "type": "string" - }, - "preferred_strategy": { - "type": [ - "string", - "null" - ] - }, - "tiers": { - "additionalProperties": false, - "properties": { - "github": { - "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1" - }, - "tier1": { - "additionalProperties": false, - "properties": { - "attempts": { - "type": "number" - }, - "fail": { - "type": "number" - }, - "ok": { - "type": "number" - }, - "success_rate": { - "type": [ - "number", - "null" - ] - } - }, - "required": [ - "attempts", - "ok", - "fail", - "success_rate" - ], - "type": "object" - }, - "tier2": { - "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1" - }, - "tier3": { - "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1" - }, - "tier4": { - "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1" - } - }, - "required": [ - "tier1", - "tier2", - "tier3", - "tier4", - "github" - ], - "type": "object" - } - }, - "required": [ - "domain", - "first_seen", - "last_fetch", - "preferred_strategy", - "tiers", - "capabilities" - ], - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "capabilities": { + "additionalProperties": false, + "properties": { + "llms_full_txt": { + "type": "boolean" + }, + "metadata_fetch_rate": { + "type": [ + "number", + "null" + ] + }, + "robots_allows_us": { + "type": [ + "boolean", + "null" + ] + }, + "seen_in_search": { + "type": "number" + } + }, + "required": [ + "llms_full_txt", + "robots_allows_us", + "metadata_fetch_rate", + "seen_in_search" + ], + "type": "object" + }, + "domain": { + "type": "string" + }, + "first_seen": { + "type": "string" + }, + "last_fetch": { + "type": "string" + }, + "preferred_strategy": { + "type": [ + "string", + "null" + ] + }, + "tiers": { + "additionalProperties": false, + "properties": { + "crawl": { + "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1" + }, + "github": { + "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1" + }, + "solver": { + "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1" + }, + "tier1": { + "additionalProperties": false, + "properties": { + "attempts": { + "type": "number" + }, + "fail": { + "type": "number" + }, + "ok": { + "type": "number" + }, + "success_rate": { + "type": [ + "number", + "null" + ] + } + }, + "required": [ + "attempts", + "ok", + "fail", + "success_rate" + ], + "type": "object" + }, + "tier2": { + "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1" + }, + "tier3": { + "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1" + }, + "tier4": { + "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1" + } + }, + "type": "object" + } + }, + "required": [ + "domain", + "first_seen", + "last_fetch", + "preferred_strategy", + "tiers", + "capabilities" + ], + "type": "object" + }, + { + "type": "null" + } +]
- Changed
search1 field changed- added
Input schema / properties / min_scoreAdded value: +{ + "description": "Drop results whose reranker relevance score is below this threshold (0-1, omit for no filtering). Filters on the RAW cross-encoder relevance score, not the recency-adjusted score used for ordering. Scores are strongly bimodal — relevant results cluster near 1.0 and irrelevant ones near 0, with little in between — so any value in roughly 0.01-0.9 behaves about the same; 0.01-0.1 is the useful range and 0.5 is not a midpoint. A high score means topically related, NOT correct: an Apache mod_proxy page scores 0.967 on an nginx query. Thresholds are model-dependent and not comparable across rerankers. No-op (with a logged warning) when the reranker is unavailable, since no scores exist to filter on.", + "maximum": 1, + "minimum": 0, + "type": "number" +}
- Changed
search_and_fetch1 field changed- added
Input schema / properties / min_scoreAdded value: +{ + "description": "Drop results whose reranker relevance score is below this threshold (0-1, omit for no filtering). Filters on the RAW cross-encoder relevance score, not the recency-adjusted score used for ordering. Scores are strongly bimodal — relevant results cluster near 1.0 and irrelevant ones near 0, with little in between — so any value in roughly 0.01-0.9 behaves about the same; 0.01-0.1 is the useful range and 0.5 is not a midpoint. A high score means topically related, NOT correct: an Apache mod_proxy page scores 0.967 on an nginx query. Thresholds are model-dependent and not comparable across rerankers. No-op (with a logged warning) when the reranker is unavailable, since no scores exist to filter on.", + "maximum": 1, + "minimum": 0, + "type": "number" +}
- Changed
search_and_summarize1 field changed- added
Input schema / properties / min_scoreAdded value: +{ + "description": "Drop results whose reranker relevance score is below this threshold (0-1, omit for no filtering). Filters on the RAW cross-encoder relevance score, not the recency-adjusted score used for ordering. Scores are strongly bimodal — relevant results cluster near 1.0 and irrelevant ones near 0, with little in between — so any value in roughly 0.01-0.9 behaves about the same; 0.01-0.1 is the useful range and 0.5 is not a midpoint. A high score means topically related, NOT correct: an Apache mod_proxy page scores 0.967 on an nginx query. Thresholds are model-dependent and not comparable across rerankers. No-op (with a logged warning) when the reranker is unavailable, since no scores exist to filter on.", + "maximum": 1, + "minimum": 0, + "type": "number" +}
4 tool updates
v3.15.1- Changed
fetch_url3 fields changed- added
Input schema / properties / max_tokensAdded value: +{ + "description": "Approximate token budget for the returned content (chars ≈ tokens × 4). Omit for the ~2000-token / 8000-char default; max 10000 tokens.", + "maximum": 10000, + "minimum": 100, + "type": "integer" +} - added
Input schema / properties / target_selectorAdded value: +{ + "description": "CSS selector to scope extraction to a specific element (e.g. 'article', 'main .content'). Honored by Firecrawl/Crawl4AI and applied client-side on the raw-HTTP tier; ignored by fast paths and if it matches nothing.", + "maxLength": 500, + "type": "string" +} - added
Input schema / properties / wait_for_selectorAdded value: +{ + "description": "CSS selector to wait for before extracting, for JS-rendered pages. Honored by the rendering tiers (Firecrawl/Crawl4AI); ignored on raw HTTP (no JS).", + "maxLength": 500, + "type": "string" +}
- Changed
search3 fields changed- added
Input schema / properties / enginesAdded value: +{ + "description": "Comma-separated SearXNG engine names to restrict the search to (e.g. 'google,duckduckgo'). Forwarded verbatim; unknown/disabled engines degrade to fewer results rather than erroring.", + "type": "string" +} - added
Input schema / properties / siteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Restrict results to one domain or a list of domains (e.g. 'github.com'). Best-effort — applied as a site: query operator; most engines honor it but some ignore it." +} - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "answers": { + "items": { + "additionalProperties": false, + "properties": { + "answer": { + "type": "string" + }, + "url": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "answer", + "url" + ], + "type": "object" + }, + "type": "array" + }, + "corrections": { + "items": { + "type": "string" + }, + "type": "array" + }, + "infoboxes": { + "items": { + "additionalProperties": false, + "properties": { + "content": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "title", + "content", + "url" + ], + "type": "object" + }, + "type": "array" + }, + "results": { + "items": { + "additionalProperties": false, + "properties": { + "content": { + "type": [ + "string", + "null" + ] + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "title", + "url", + "content" + ], + "type": "object" + }, + "type": "array" + }, + "suggestions": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "answers", + "infoboxes", + "corrections", + "suggestions", + "results" + ], + "type": "object" +}
- Changed
search_and_fetch2 fields changed- added
Input schema / properties / enginesAdded value: +{ + "description": "Comma-separated SearXNG engine names to restrict the search to (e.g. 'google,duckduckgo'). Forwarded verbatim; unknown/disabled engines degrade to fewer results rather than erroring.", + "type": "string" +} - added
Input schema / properties / siteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Restrict results to one domain or a list of domains (e.g. 'github.com'). Best-effort — applied as a site: query operator; most engines honor it but some ignore it." +}
- Changed
search_and_summarize2 fields changed- added
Input schema / properties / enginesAdded value: +{ + "description": "Comma-separated SearXNG engine names to restrict the search to (e.g. 'google,duckduckgo'). Forwarded verbatim; unknown/disabled engines degrade to fewer results rather than erroring.", + "type": "string" +} - added
Input schema / properties / siteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "Restrict results to one domain or a list of domains (e.g. 'github.com'). Best-effort — applied as a site: query operator; most engines honor it but some ignore it." +}
1 tool update
v3.14.0- Added
domain_stats
6 tool updates
v3.12.0- First observed
clear_cache - First observed
crawl_site - First observed
fetch_url - First observed
search - First observed
search_and_fetch - First observed
search_and_summarize
TDQS
Scored across 7 tools
Each tool occupies a distinct stage of a research pipeline: search only, search+fetch, search+summarize, direct URL fetch, site crawl, cache control, and stats. The progressive search variants are explicitly differentiated by what they return and when they fetch. No two tools appear interchangeable.
Most tools use a clear verb or verb_and_verb pattern: search, search_and_fetch, search_and_summarize, fetch_url, crawl_site, clear_cache. domain_stats is the only noun-style outlier, but it is still readable and fits the utility's purpose. Overall the convention is predictable.
Seven tools is a well-scoped set for a SearXNG-backed research server. Each tool adds a distinct capability without redundancy. The count is appropriate for the purpose.
The surface covers the full research loop: search, retrieve content, summarize, crawl, cache control, and diagnostics. There are no obvious dead ends for common workflows. The set feels complete for the server's stated purpose.
Maintenance
Related MCP Connectors
MCP server for Firecrawl — web search, scraping, and biomedical/arXiv paper search.
Serper MCP — wraps the Serper Google Search API (serper.dev)
MCP server for Google search results via SERP API
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceMCP server for web search and content extraction using DuckDuckGo or SearXNG, with Playwright-based fetching and LLM-powered data extraction.140MIT
- AlicenseNot gradedqualityCmaintenanceA minimal MCP server that exposes a private SearXNG instance as a search tool over streamable-HTTP, enabling web search from the llama.cpp WebUI or any compatible MCP client.4MIT
- AlicenseNot gradedqualityFmaintenanceOffline-first MCP server for web search and content fetching. It requires no external API keys and uses local models for intent classification, optional cross-lingual search, semantic re-ranking, and direct-answer extraction.ISC
- AlicenseNot gradedqualityDmaintenanceA fully local MCP server that provides web search via self-hosted SearXNG and page-to-markdown conversion (static and JS-rendered), all aggregated behind a single endpoint for use with AI assistants.MIT