searxng-mcp
A privacy-focused web search and content extraction MCP server routing queries through a self-hosted SearXNG instance with local ML reranking, multi-tier content fetching, and optional LLM-powered enhancements.
search— Search the web via SearXNG with local ML reranking; filter by category (general,news,it,science), time range, language, and domain profiles; optional LLM query expansion for wider recall.search_and_fetch— Search and rerank results, then automatically fetch full markdown content of top 1–3 results via a cascade: GitHub API → Firecrawl → Crawl4AI → raw HTTP (with optional Wayback Machine fallback).fetch_url— Extract readable markdown from any public URL, with fast paths for GitHub (API), Kiwix ZIM archives (Wikipedia/SO/Arch Wiki), andllms.txtdocs sites; results cached 24 hours.search_and_summarize— Search, fetch up to 5 top results, and synthesize a cited summary using a local LLM (Ollama); ideal for deep research.crawl_site— Crawl an entire website and return a manifest of pages (titles, URLs, snippets, up to 100 pages) via Firecrawl → sitemap parsing → optional BFS; fetched content is cached for instant follow-upfetch_urlcalls.clear_cache— Purge search, fetch, or crawl caches individually or all at once — useful for fast-moving topics.
Additional capabilities:
Per-domain capability database tracking fetch success rates, robots.txt compliance, and llms.txt presence for intelligent tier routing and operator overrides.
Two independent adblock mechanisms (Firecrawl puppeteer-level + HTTP proxy for Crawl4AI/raw fetch).
Security features: URL allowlisting, redirect protection, and honest User-Agent/robots.txt compliance.
Optional OpenTelemetry tracing/metrics and NATS event publishing for observability.
Flexible deployment:
stdiofor single-client agents or HTTP/SSE for multi-client/Dockerized environments.
Fetches content from GitHub URLs using the GitHub API, converting repositories and files to readable markdown.
Provides optional query expansion and LLM-synthesized summaries using a local Ollama instance, improving search recall and result synthesis.
Enables private web searches through a self-hosted SearXNG instance, supporting categories, time ranges, language filtering, and domain profiles.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@searxng-mcpsearch and summarize recent MCP server developments"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
searxng-mcp
An MCP server for private web search via a self-hosted SearXNG instance. SearXNG is the only requirement; everything else is optional and layers on top — a local ML model reranks results, a three-tier cascade (Firecrawl, Crawl4AI, in-process raw fetch) retrieves full-page content, and an Ollama instance provides query expansion and LLM-synthesized summaries.
Designed for use with Claude Code and LibreChat agents that need web search without sending queries to a third-party search API.
Built with Claude Code using the multi-agent workflow from homelab-agent — the same platform that uses searxng-mcp in production for AI-assisted research.
Quick Start
A running SearXNG instance is the only requirement. Everything else is optional and improves a specific dimension — see Prerequisites.
SearXNG only — nothing else deployed:
SEARXNG_URL=http://localhost:8081 \
FIRECRAWL_ENABLED=false CACHE_URL= RERANKER_URL= \
npx @tadmstr/searxng-mcpWhat works in this configuration: search returns ranked results, and fetch_url /
search_and_fetch return extracted page content via tier 3 (raw HTTP fetch + Readability, wholly
in-process). What does not: semantic reranking (results keep SearXNG's ordering), query expansion
and search_and_summarize (no LLM), caching (every call is live), JS-heavy page rendering, and
offline serving.
The startup capability line reports three states per capability, not two:
on (self-contained — nothing remote has to work for it to be true), unverified (configured,
but this process has never contacted the backend), and off (not configured). Nothing probes at
startup, so unverified is not a health failure — it's the honest absence of a health claim, not
an error. The command above prints exactly this:
capabilities on=tier3 unverified=none off=tier1,tier2,cache,reranker,llm,kiwix,hister,solver,wayback,otel,natstier3 is the only thing on, because it is the only capability that is true without anything
remote working. wayback is off until you set WAYBACK_ENABLED=true — it is a feature flag,
not a default.
The three overrides are what make it a clean minimal run rather than merely a working one.
FIRECRAWL_URL, CACHE_URL and RERANKER_URL all have non-empty defaults, so left alone the
server spends the whole run talking to services that were never deployed — every fetch first
attempts a Firecrawl that is not there and books the failure into the domain capability
database, which then routes around a tier that was never broken, only absent.
FIRECRAWL_ENABLED=false is a real kill switch. CACHE_URL and RERANKER_URL have none, so
the lever is an empty string.
The compose ladder
Four compose files in examples/, each a strict superset of the one above it. Start
at the rung that matches what you are willing to run; every rung's header comment states what
works, what does not, and which environment variable moves you up.
Rung | File | Adds | Containers | RAM | Unlocks |
1 | SearXNG + searxng-mcp | 2 | ~600 MB | The least you can run | |
2 | cache, Crawl4AI | 4 | ~6 GB | JS rendering, real caching | |
3 | reranker | 5 | ~8 GB | Relevance reranking, | |
4 | Firecrawl, Ollama, Kiwix, NATS, adblock | 12 | ~24 GB | Summarisation, PDFs, offline corpora |
Rung 2 is the recommended starting point — the cache is the single largest latency win, and Crawl4AI gets you JavaScript rendering for one container instead of Firecrawl's three.
cd examples
export SEARXNG_SECRET=$(openssl rand -hex 32)
docker compose -f compose.crawl4ai.yml up -dMoving up a rung is docker compose -f compose.<next>.yml up -d — all four share a compose
project name, so compose reconciles in place rather than starting a second copy of what you
already had.
As a container — published to GHCR on every release:
docker pull ghcr.io/tadmstr/searxng-mcp:latestTags, uid, and provenance verification: Deployment.
The reranker ships in this repo — cd docker/reranker && docker compose up, or pull ghcr.io/tadmstr/searxng-mcp-reranker. It is CPU-only, needs no API key, and has the model baked in so there is no cold start. See docker/reranker/README.md.
Two adblocking sidecars are also published as images, one per fetch-tier group:
ghcr.io/tadmstr/searxng-mcp-adblock-proxy— ad/tracker filtering for tiers 2 and 3. It is an open forward proxy with no authentication, so loopback or a private network only — seedocker/adblock-proxy/README.mdfor placement. Its filter lists are fetched at runtime, fromADBLOCK_FILTERS_URL(defaulted to EasyList + EasyPrivacy) and reloaded on a schedule — the opposite approach from the playwright sidecar below, deliberately: only that image sits in front of an SSRF guard.ghcr.io/tadmstr/searxng-mcp-playwright-adblock— EasyList/EasyPrivacy filtering for Firecrawl v2's renderer (tier 1). This is an upgrade of upstream's existingAD_SERVING_DOMAINStoken list, not a new capability — that list still applies underneath. It targets the v2 renderer only;docker/puppeteer-adblock/covers v1 and does nothing on a v2 deployment. NoteFIRECRAWL_API_VERSIONdefaults tov1insrc/config.ts, so a stack wiring the playwright sidecar must set it tov2explicitly or tier-1 adblocking silently does nothing —examples/compose.full.ymlsets it and says why. Its filter lists are vendored at build time (docker/playwright-adblock/lists/, checksum-verified againstSHA256SUMSduring the build) rather than downloaded fromeasylist.to, so the build no longer depends on that host being reachable. Refresh withnode docker/playwright-adblock/fetch-lists.mjs, or let the weeklyadblock-lists-refresh.ymlworkflow do it — it opens a PR that carries no CI checks (GitHub does not trigger workflows for a PR opened withGITHUB_TOKEN), so review the workflow run itself rather than looking for a green check; it verifies the lists before opening the PR.ADBLOCK_FILTERS_URLstill exists on this image too, as an opt-in runtime override — unset by default, so the vendored lists are what ships. Seedocker/playwright-adblock/README.md.
For a full local topology including Firecrawl, Crawl4AI, Ollama, Kiwix, the adblock proxy, and NATS, see rung 4 of the ladder above — examples/compose.full.yml.
Related MCP server: searxng-mcp-bridge
Why searxng-mcp?
There are a number of SearXNG MCP servers. Most wrap the search endpoint and stop there. The differentiators here are in what happens after the search:
searxng-mcp | Typical SearXNG MCP server | |
SearXNG search | yes | yes |
ML reranking of results | local cross-encoder, reorders by relevance — ships in | SearXNG's own ordering |
Full-page content retrieval | three-tier cascade — Firecrawl, Crawl4AI, in-process raw fetch + Readability | none, or a single raw fetch |
Per-domain routing | domain capability database learns which tier works per domain and skips the ones that do not | none |
Summarisation | Ollama, with citations back to source URLs | none |
Site crawling |
| none |
Caching | persistent, shared across clients (Valkey/Redis) | in-process or none |
Challenge handling | detection-gated solver tier, plus a Wayback fallback | none |
Observability | OpenTelemetry traces and metrics, NATS events, structured logs | none |
Only SearXNG is required. Everything in the table above degrades gracefully: with nothing
else deployed, fetch_url still returns extracted content from the in-process tier 3, and the
startup capability line tells you exactly what's configured, what's configured but not yet
verified, and what's off — see Quick Start above.
Tools
Tool | What it does |
| Search via SearXNG with local ML reranking, plus SearXNG's own direct answers, infoboxes and suggestions. |
| Search, rerank, then fetch full content of the top result(s) through the fetch cascade. |
| Search, fetch, then synthesize a cited summary via Ollama. On failure, falls back to raw fetched content led by an explicit |
| Fetch and extract readable markdown from any public URL, via fast paths or the fetch cascade. |
| Crawl a site and return a URL/title/snippet manifest. Page content is cached, so follow-up |
| Purge the search, fetch or crawl cache. |
| Read-only view of the domain capability database — per-tier success rates, as structured output. |
Full parameter reference: docs/tools.md.
Resources
Two read-only MCP Resources, additive to the tools above.
URI | What it returns |
| Effective configuration and capability state — which backing services are wired, the three-state capability line, behaviour switches and tunables, and which credentials are configured. |
| Aggregate view of the domain capability database, mirroring the aggregate mode of |
config://searxng-mcp never includes a credential value. Secrets are reported as a
boolean — configured or not — and every URL is stripped of inline userinfo before it is
emitted, because Basic Auth in SEARXNG_URL and an inline password in CACHE_URL are both
supported. The payload is built from an allowlist rather than by dumping config and removing
known secrets, so a newly-added field cannot leak by default.
If you're behind an MCP proxy rather than talking to this server directly, verify it forwards
resources/*. Many proxies re-register upstream tools on their own server instead of
forwarding the wire protocol generically, and drop Resources silently as a result — the tools
still work, only the Resources go missing. See
Deployment for a worked example of checking
this.
Architecture
The fetch cascade, in full. Each stage is optional and skipped cleanly when unconfigured.
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"]
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\nserves PDFs under FIRECRAWL_API_VERSION=v2"]
t2["Tier 2 — Crawl4AI\n$CRAWL4AI_URL · optional\ndirect — no adblock proxy"]
t3["Tier 3 — Raw HTTP + Readability\nfallback: raw HTML slice\nadblock proxy if $ADBLOCK_PROXY_URL"]
challenge(["Challenge detected\non this URL, this request?"])
solver["Solver — Byparr\n$SOLVER_URL · opt-in, SOLVER_ENABLED=true\nSSRF-guarded replay"]
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| 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"| challenge
challenge -->|"yes, SOLVER_ENABLED"| solver
challenge -->|no| t4
solver -->|success| post
solver -->|"miss / disabled"| 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 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 challenge fill:#f5f5f5,stroke:#666666,color:#000000
style solver 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:#000000Read the rest — tier semantics, the domain capability database, adblocking, every fast path,
resilience and observability — in docs/architecture.md.
Documentation
Every environment variable, and setup for each optional backing service. | |
Full per-tool parameter reference. | |
Install, transports, HTTP auth, and MCP client recipes. | |
Fetch cascade, tier semantics, domain capability database. | |
SSRF, redirects, transport exposure, bounded reads, credentials. |
Index: docs/index.md.
Contributing
See CONTRIBUTING.md for setup instructions, commit conventions, and the PR process.
Integration tests
A real-Valkey integration suite covering domain-DB concurrency is gated on VALKEY_TEST_URL and skipped entirely when it's unset, so a plain pnpm test still works with no Valkey present:
VALKEY_TEST_URL=redis://:<password>@<host>:<port>/<scratch-db> pnpm testUse a scratch database index — the suite writes and deletes domain:* keys and refuses to run against index 0 or 1 as a safety guard.
License
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
Serper MCP — wraps the Serper Google Search API (serper.dev)
MCP server for Google search results via SERP API
Docs: https://docs.keenable.ai/mcp-server Keenable is a free, remote MCP server that gives agents access to the web index. Search the web with ranked results and date/site filters, then fetch any indexed page as clean markdown. Works out of the box with no account or API key.
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.139MIT
- AlicenseNot gradedqualityAmaintenanceA 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.3MIT
- AlicenseNot gradedqualityBmaintenanceOffline-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 gradedqualityCmaintenanceA 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