All notable changes to mcp-searxng are documented here. Versions follow Semantic Versioning.
-
Configurable default search response format: Operators can set
SEARXNG_DEFAULT_RESPONSE_FORMATto the exact lowercase valuetextorjsonfor calls that omitresponse_format. Explicit per-call values continue to take precedence, and unset, blank, or invalid configuration preserves the existing text default. The tool schema no longer advertises the hard-codeddefault: "text"annotation so clients can omit the argument and allow the operator default to apply; clients that explicitly send or auto-injecttextcontinue to override it. The operator default also applies in lite-tools mode. (#225) -
Bounded stateless Streamable HTTP mode: Set
MCP_HTTP_STATELESS=trueto isolate everyPOST /mcpin a fresh MCP server and transport for serverless or horizontally scaled deployments that cannot preserve process-local sessions. Stateless requests are protected by configurable global and per-client-IP in-flight limits, a request lifetime, authorization and hardened Host/Origin checks before server construction, and bounded cleanup. Stateful sessions remain the default; stateless mode is POST-only and does not preserve cross-request sessions, resumable streams, standalone GET notification streams, or DELETE-based termination. (#226)
-
FlareSolverr-primary failover to Byparr: Operators can configure
FLARESOLVERR_URL,BYPARR_URL, or both. Dual mode always tries FlareSolverr first, advances to Byparr only for busy or transient-unavailable acquisition, and uses one uncached direct fetch only after every configured provider is busy or unavailable. Persistent 4xx, cancellation, solution-integrity failures, and solved non-2xx target status stop the chain. Provider timeouts and concurrency remain independent, cache entries use the winning provider, and canonically duplicate endpoints fail closed. Verified provider versions remain FlareSolverr 3.5.0 and Byparr 2.1.0 from 2026-07-30. (#220, #223, #224)Migration note: Browser-solver endpoints are now validated during startup. A
FLARESOLVERR_URLcontaining userinfo, a query, a fragment, or a non-HTTP(S) scheme now prevents startup instead of failing only when a URL read first uses it. -
Bounded PDF text extraction:
web_url_readnow extracts text-layer content fromapplication/pdfresponses using the new productionunpdfdependency in a resource-limited worker. Input and output are capped at the lower ofURL_READ_MAX_CONTENT_LENGTH_BYTESand 16 MiB, documents above 500 pages are rejected, parsing has a separate 30-second budget, and at most two extractions run concurrently. OCR is not supported. This supersedes the v1.10.0 behavior that rejected PDF responses. (#221) -
Expanded operator and client guidance: New documentation covers self-hosted and public SearXNG instances, MCP client configuration, evidence-focused research workflows, and measured deployment profiles. (#214, #215, #216, #217, #218)
-
HTTP rate-limit settings now honor the strict integer-validation contract:
MCP_RATE_WINDOW_MS,MCP_RATE_INIT_MAX, andMCP_RATE_SESSION_MAXreject fractional, unit-suffixed, exponent, non-decimal, non-positive, and unsafe values instead of accepting numeric prefixes. Invalid values fall back with a raw-value-free warning. Because previously accepted numeric prefixes may have produced a different effective limit, the documented default may be looser or stricter until the operator corrects the setting. (#219) -
Solver and PDF documentation now matches runtime boundaries: Security and deployment guidance consistently describes browser-solver disclosure, acquisition fallback, PDF parsing limits, and timeout behavior. (#222)
-
Configuration integers are now validated consistently: Cache limits, timeouts, HTTP settings, URL content limits, and search page numbers reject fractional, unit-suffixed, non-decimal, unsafe, and otherwise malformed values instead of accepting numeric prefixes or truncating fractions. (#201, #202, #211)
-
Search and URL caching now preserve only useful final results: Empty searches are no longer cached, URL-reader entries retain the final markdown instead of redundant raw payloads, and SearXNG requests use aligned fetch and dispatcher clients. (#203, #206, #207)
-
CLI metadata flags no longer initialize the MCP server:
--help,-h,--version, and-vreturn immediately through a minimal path, avoiding configuration and network startup side effects. (#204) -
MCP logging thresholds are isolated per server session: Changing one connected client's log level no longer changes the threshold used by other sessions. (#205)
-
The default Compose deployment now remains STDIO-only: HTTP transport is no longer exposed unless it is explicitly configured. (#198)
-
Server configuration now reports every supported proxy source: The
hasProxyindicator includes global, search-specific, and URL-reader-specific HTTP and HTTPS proxy variables in either case. (#212) -
Development and regression tooling is more reliable: Coverage works on supported Node releases, test environment mutations are restored even after failures, and documentation now accurately describes configuration exposure, LFU eviction, fallback errors, and available test commands. (#199, #200, #208, #209, #210, #213)
-
Published-package dependency verification is now fail-closed: The MCP SDK is updated from 1.29.0 to 1.30.0 so clean consumer installs can resolve the patched
@hono/node-server2.x line. The previous root-only adapter override was removed because npm consumers do not inherit dependency-owned overrides. The npm publication workflow now packs and installs the exact release artifact in an isolated consumer, rejects every resolved@hono/node-serverversion below2.0.5, requires a zero-vulnerability production audit, and smoke-tests the installed MCP CLI before publication. (#197) -
Security regression assertions now match complete diagnostic URLs: Exact-message tests prevent ambiguous safe substrings from hiding an unsafe credential-bearing URL. (#190)
- @app/dependabot - #193 chore(deps-dev): bump the development dependencies group
- @app/dependabot - #194 chore(deps): bump undici from 7.28.0 to 7.29.0
- @app/dependabot - #195 chore(deps): bump express-rate-limit from 8.6.0 to 8.6.1
- @app/dependabot - #196 chore(deps): bump the GitHub Actions group
-
Established HTTP sessions now receive the configured session rate limit: Each
POST /mcprequest now passes through exactly one limiter. Requests with a currently livemcp-session-iduse the session allowance, while initialization requests and missing, malformed, unknown, or stale session identifiers retain the stricter initialization limit. (#179) -
Logging now honors all eight MCP severity levels: Filtering recognizes
debug,info,notice,warning,error,critical,alert, andemergency, so selecting a threshold such asnoticeoremergencyno longer lets lower-severity messages through. (#178) -
TypeScript test launchers now work consistently on Windows, Linux, and WSL: Integration and end-to-end tests use Node's portable
--import tsxloader instead of trying to execute npm's platform-specific.bin/tsxshim directly. This changes development and CI launchers only; production runtime behavior is unchanged. (#176)
-
Authentication data is now sanitized at every diagnostic boundary: Process output, MCP logging notifications, JSON-RPC errors, and HTTP diagnostic errors pass through a centralized redaction layer that removes configured credentials, URL userinfo, authorization values, proxy secrets, and structured authentication fields. Malformed SearXNG entries are reported without echoing their raw values. MCP registry metadata now marks
SEARXNG_URL,AUTH_USERNAME, andAUTH_PASSWORDas secrets so compatible clients can mask and protect their values. If an older release exposed credentials in logs or client-visible errors, upgrade, rotate the affected credentials, and remove or restrict access to captured logs and telemetry. -
Hardened HTTP bearer authentication now uses constant-time comparison: Authorization accepts only the documented exact, case-sensitive
Bearer <token>form, fails closed when either value is absent, hashes the complete presented and expected strings with SHA-256, and compares the equal-length digests with timing-safe equality. Raw-token and malformed authorization headers are rejected. Before upgrading, clients that previously sent an undocumented bare token must change the header toAuthorization: Bearer <token>. (#177) -
Vulnerable transitive dependencies were patched:
fast-uriis updated to 3.1.4 for its host-confusion fixes, the root install overrides@hono/node-serverto patched 2.0.11, and the development graph usesbrace-expansion5.0.8. Applications that installmcp-searxngas a dependency may need their own@hono/node-server2.0.11 override until the MCP SDK admits the patched 2.x adapter, because npm does not apply dependency-owned overrides in a consuming application's root graph. (#189)
- @app/dependabot - #185 chore(deps): bump hono from 4.12.25 to 4.12.31
- @app/dependabot - #184 chore(deps): bump body-parser from 2.2.2 to 2.3.0
- @app/dependabot - #183 chore(deps-dev): bump brace-expansion from 5.0.6 to 5.0.7
- @app/dependabot - #182 chore(deps): bump the github-actions group with 4 updates
- @app/dependabot - #181 chore(deps): bump express-rate-limit from 8.5.2 to 8.6.0
- @app/dependabot - #180 chore(deps-dev): bump the dev-dependencies group with 3 updates
-
Hardened HTTP mode no longer rejects every request on non-default ports: With
MCP_HTTP_HARDENenabled andMCP_HTTP_ALLOWED_HOSTSleft unset, the default DNS-rebinding Host allowlist contained only the bare hostnames127.0.0.1andlocalhost. Because the transport matches the rawHostheader — port included — with an exact list-membership check, any bind to a port other than 80 caused every request (including the initialinitialize) to fail with403. The bind port is now threaded into the defaults, so the allowlist also accepts127.0.0.1:PORT,localhost:PORT, and[::1]:PORT(plus[::1]to mirror the SDK's own localhost default). An explicitMCP_HTTP_ALLOWED_HOSTSstill overrides these defaults unchanged. (BUG-012, #172) -
SEARXNG_TIMEOUT_MSis now validated and clamped: Non-integer, unit-suffixed (e.g.5000ms), decimal, non-positive, or otherwise malformed values are now rejected with a warning and fall back to the default10000. The value is also capped at the 32-bitsetTimeoutceiling (2147483647); a larger delay was previously clamped by Node to 1 ms, so an over-large timeout fired almost immediately instead of waiting. (BUG-013, #171) -
Corrected the HTTP transport example and refreshed the docs: README and
CONFIGURATION.mdwere synced with the current feature set and a misleading Streamable HTTP transport example was fixed. (#165)
MCP_RATE_*environment variables are now validated: Malformed values for the HTTP rate-limit settings —MCP_RATE_WINDOW_MS,MCP_RATE_INIT_MAX, andMCP_RATE_SESSION_MAX— are rejected with a warning and fall back to safe defaults instead of being applied verbatim, so a typo can no longer silently disable or misconfigure rate limiting. (SEC-025, #170)
-
In-memory search result cache: Repeated
searxng_web_searchcalls with identical arguments are now served from a per-process cache instead of re-querying the instance, mirroring the existing URL-reader cache. The cache key is a SHA-256 of the tool name plus the search arguments canonicalized with sorted object keys, so semantically identical requests hit the same entry regardless of argument order, while any change to the query or parameters caches separately. Two new variables tune it:SEARCH_CACHE_TTL_MS(default86400000, 24 hours) sets the entry lifetime, andSEARCH_CACHE_MAX_ENTRIES(default200) caps the cache, evicting the least-frequently-used entry first with the oldest entry as the tie-breaker. Invalid or non-positive values fall back to the defaults. (FEAT-008, #164) -
Per-instance HTTP Basic Auth from
SEARXNG_URLuserinfo: Credentials can now be embedded directly in eachSEARXNG_URLentry (https://user:pass@host), and each semicolon-separated replica carries its own credentials — so a mixed deployment of one auth-gated private instance and one public instance no longer sends the private credentials to the public host. The legacy globalAUTH_USERNAME/AUTH_PASSWORDvariables are now a fallback used only for entries that have no userinfo, preserving existing single-instance setups. Percent-encode special characters in the username or password (for example, writep@ssasp%40ss). (FEAT-049, #160) -
SEARCH_USER_AGENToverride for SearXNG-instance requests: A new per-groupSEARCH_USER_AGENTvariable sets theUser-Agentfor all SearXNG-instance traffic —searxng_web_search,/configcapability discovery, and search suggestions — independently of theweb_url_readgroup'sURL_READER_USER_AGENT. Both groups fall back toUSER_AGENTwhen unset, and if neither the group override norUSER_AGENTis set, noUser-Agentheader is added. (FEAT-050, #150)
-
Basic Auth and custom CA certs now applied on every SearXNG endpoint:
SEARXNG_URLBasic Auth credentials and theNODE_EXTRA_CA_CERTScustom CA bundle were previously honored on the main search request but not on the/configcapability-discovery and/autocompletersuggestion fetches, so those two paths failed against auth-gated or custom-CA instances. All three now go through the same authenticated, TLS-aware request path. A follow-up also fixes Windows, where settingNODE_EXTRA_CA_CERTShad dropped the bundled Mozilla root store instead of adding to it. (d33f7e9,2a037f5, #152) -
Clearer "content too large" message from
web_url_read: When a page exceeds the size limit, the error now reports the size with an explicit, unambiguous unit and gives accurate advice for narrowing the request, replacing the earlier misleading wording. (#148)
SEARXNG_URLuserinfo redacted in the config resource: Now that credentials can be embedded per instance, theconfigMCP resource redacts anyuser:pass@userinfo from the reported instance URLs, and thehasAuthindicator is userinfo-aware so it reflects embedded credentials as well as the legacyAUTH_USERNAME/AUTH_PASSWORDvariables — keeping embedded secrets out of client-visible configuration output. (2026bf9)
- @wchy1128 - #152 fix(auth+tls): Basic Auth on /config and /autocompleter; honor NODE_EXTRA_CA_CERTS everywhere
USER_AGENTnow applied to the/configand suggestions requests: The configuredUSER_AGENTheader is now sent on the SearXNG/configinstance-info fetch and on search-suggestion fetches. These two paths previously always used the default agent while the main search andweb_url_readpaths already honoredUSER_AGENT, so instances that filter or rate-limit by User-Agent behaved inconsistently. The header is now merged in one shared request-config helper covering every outbound instance request. (BUG-009, #145)
- SSRF guard now blocks CGNAT and the remaining IANA special-purpose IPv4 ranges: The private-address guard that protects
web_url_read— and the DNS-rebinding lookup hook that re-validates every resolved answer — previously only rejected RFC1918, loopback, link-local, and0.0.0.0/8. It now also blocks CGNAT (100.64.0.0/10, Tailscale's default range plus container overlays and ISP CGNAT), the TEST-NET ranges, benchmarking (198.18.0.0/15), IETF protocol assignments (192.0.0.0/24), 6to4 relay anycast, multicast (224.0.0.0/4), and reserved/broadcast (240.0.0.0/4). All blocked ranges are consolidated into a single auditable CIDR table (RFC 6890) enforced at both the literal-hostname and DNS-resolved paths; IPv4-mapped IPv6 delegates here and is covered too. (SEC-024, #147)
-
Content-type-aware
web_url_read: The URL reader now inspects the responseContent-Typebefore converting. HTML is converted to markdown as before; JSON (application/jsonand*+json) is pretty-printed in a fenced block; and plain text, YAML, TOML, and XML are returned as readable fenced text. Binary, media, archive, and PDF responses are now rejected with a short hint instead of being decoded into unreadable bytes — fixing the case where fetching a PDF URL fed garbage to the model. Responses whose declared type is missing or generic are sniffed for a NUL byte in the first kilobyte and rejected if they look binary, which also catches binaries mislabeled astext/plain; anything textual continues through the existing HTML pipeline unchanged. (FEAT-045, #142, resolves #133) -
Actionable errors when a SearXNG instance returns non-JSON: When a search gets a
200response whose body is not JSON — an HTML results page because the instance never enabledformat: json, or a Cloudflare/WAF interstitial — the error now names both fixes (enable- jsonundersearch.formatsin the instance'ssettings.yml, or setSEARXNG_HTML_FALLBACK=true) while still including the response preview, instead of failing with an opaque "Invalid JSON format". (FEAT-053, #141, resolves #137) -
Documented
NODE_EXTRA_CA_CERTSfor Windows and corporate-proxy TLS: A new "TLS / Corporate CA" section inCONFIGURATION.mdexplains that Linux and macOS auto-detect the system CA bundle, while Windows users behind a TLS-inspecting corporate proxy (Zscaler, Netskope, Palo Alto, Blue Coat) must export the proxy's root CA to PEM and point the standard Node.jsNODE_EXTRA_CA_CERTSvariable at it — with the PowerShell export steps and an explicit warning never to use the insecureNODE_TLS_REJECT_UNAUTHORIZED=0. No code change; the variable was already honored by Node/undici. (FEAT-054, #143, resolves #138)
- Configurable Express
trust proxyfor HTTP mode (MCP_HTTP_TRUST_PROXY): When the Streamable HTTP transport runs behind a trusted reverse proxy, setMCP_HTTP_TRUST_PROXYso Express resolves the real client IP fromX-Forwarded-Forbefore computing rate-limit keys and request logs. Acceptstrue, a trusted hop count such as1, or a subnet/preset such asloopbackor10.0.0.0/8; unset,false, or0disables it, which stays the secure default (enabling it without a real proxy in front lets clients spoofX-Forwarded-For). This is distinct from the outboundHTTP_PROXY/HTTPS_PROXYsettings that govern this server's own requests. (FEAT-051, #140)
-
HTTP session recovered after a server restart: The Streamable HTTP
sessionsmap is in-memory, so a client that reused itsmcp-session-idacross a server restart got wedged — a freshinitializestill carried the stale header and fell through to400 / -32000.initializeis now accepted regardless of any stale session header, and unknown session IDs on non-initializePOSTs return404 / -32001 "Session not found"(matching the MCP SDK's own shape) so clients can detect a dead session and re-initialize. (BUG-010, #139) -
Search JSON-parse errors keep the real response preview: A
fetchresponse body is single-use, and the old path calledresponse.text()in the catch afterresponse.json()had already consumed it, so a JSON-parse failure always degraded to[Could not read response text]. The body is now read as text first and then parsed, so the error carries the actual response preview — making misconfigured or HTML-returning instances far easier to diagnose. (BUG-008, #131)
SEARXNG_URLcredentials redacted in errors, logs, and provenance: Embedded userinfo (user:pass@host) inSEARXNG_URLno longer leaks into model-visible error messages, client logs, orservedByprovenance. A shared redaction helper is now applied at every instance-URL emission point — the aggregate failover error, theECONNREFUSEDnested message, request/fallback logs, error context, andservedBy. (BUG-007, #136)
-
Multi-instance failover and optional parallel fanout for
SEARXNG_URL:SEARXNG_URLnow accepts several semicolon-separated SearXNG replica URLs that are treated as interchangeable. In the default failover mode a search tries each instance in order until one returns results; an instance with 3 consecutive hard failures is skipped for 60 seconds, while a200 OKwith an empty result set is treated as healthy and does not trigger cooldown. Set the newSEARXNG_FANOUT=trueto instead query all healthy instances in parallel and merge results — deduplicated by canonical URL, keeping the highest-scoring copy and ordered by descending score. A single-URLSEARXNG_URLbehaves exactly as before, so no configuration change is required. (FEAT-047, #128) -
Capability discovery aggregated across all instances for filter guidance:
searxng_instance_infoand thecategories/enginessearch parameters now aggregate live/configcapabilities from every reachable configured instance instead of a single one. The tool reportscommoncategories and engines (supported on every reachable instance, so safe for consistent multi-instance results) alongside best-effortavailablevalues, keeping filter guidance accurate when replicas differ in their enabled engines. A/configendpoint that fails is skipped for about 60 seconds, or retried immediately whensearxng_instance_infois called withrefresh=true. (FEAT-048, #130)
-
safesearchaccepted as a string enum and honoring the instance default when omitted:safesearchis now declared as a string enum ("0","1","2") so MCP clients that send every tool argument as a string — notably Gemini and Antigravity — no longer fail schema validation. The schema default was also dropped, so omittingsafesearchnow falls back to each instance's server-side default instead of forcing a value. (BUG-006, #127) -
Docker Compose HTTP transport reachable from the host: The HTTP transport in the provided
docker-composesetup now binds to0.0.0.0instead of a loopback address, so the mapped port is reachable from the host rather than only from inside the container.
- Container image now runs as a non-root user (UID 1000): The published Docker image previously ran as
root, so Kubernetes deployments using therunAsNonRoot: truepod security context were rejected at admission. The image now sets a numericUSER 1000(thenodeaccount already present in thenode:lts-alpinebase), which satisfiesrunAsNonRootwithout an additionalrunAsUseroverride and reduces the container's blast radius. No configuration change is required. (Reported by @nogweii, #122)
- DNS-resolved private-address SSRF in
web_url_readblocked (GHSA-mrvx-jmjw-vggc): The URL reader previously validated only the literal hostname string, so a public-looking hostname that DNS-resolves to a private, loopback, or link-local address (for example a domain pointing at127.0.0.1/10.0.0.0/8or a cloud metadata endpoint like169.254.169.254) bypassed the SSRF guard. Direct (no-proxy) reads now validate every resolved DNS answer before connecting and pin the connection to the validated address, closing the DNS-rebinding window. TheMCP_HTTP_ALLOW_PRIVATE_URLS=trueopt-out still applies. When a URL-reader proxy is configured the proxy performs DNS resolution, so those deployments must rely on egress/firewall controls (documented inSECURITY.md). - Unbounded response-body read in
web_url_readcapped (GHSA-xcqx-9jf5-w339): The page-size limit was advisory only — a server using chunked transfer encoding, a failing/absent HEAD response, or a body larger than its reportedContent-Lengthcould force the entire response into memory (denial of service). The body is now read through a bounded stream that enforcesURL_READ_MAX_CONTENT_LENGTH_BYTES(default 5 MB) against the decompressed size and stops once the cap is exceeded, before any conversion or caching.
- HTML-search fallback (
SEARXNG_HTML_FALLBACK=true): Opt-in compatibility mode for SearXNG instances that disable JSON output. When a search hits a403/404or a non-JSON response, it is automatically retried withoutformat=jsonand results (title, URL, snippet) are parsed from the regular HTML results page and markedsourceFormat: "html". Triggers strictly on format rejections — never on401,5xx, network, or timeout errors. Enabling JSON on a SearXNG instance you control remains the recommended setup; see the README troubleshooting section.
undiciupgraded to 7.28.0 — resolves two HIGH advisories affecting 7.0.0–7.27.2: GHSA-vmh5-mc38-953g (TLS certificate validation bypass in the SOCKS5 ProxyAgent) and GHSA-pr7r-676h-xcf6 (cross-user information disclosure via shared-cache whitespace bypass).form-dataupgraded to 4.0.6 — clears a CRLF-injection advisory (GHSA-hmw2-7cc7-3qxx) in the test toolchain.
-
enginesparameter onsearxng_web_search: A comma-separated list routes a search to specific SearXNG engines (e.g.google,bing,duckduckgo) instead of the category defaults. Omitting it preserves the previous behaviour. -
Validated & normalized
categories/engines: Values are now trimmed and matched case-insensitively against the connected instance's live/config, and the canonical names are sent to SearXNG. Unknown values are rejected up front with the available options listed — fixing silent search degradation from miscased or invalid engine/category names. -
Configurable URL cache controls:
CACHE_TTL_MSsets the URL cache TTL (default86400000ms = 24 h) andCACHE_MAX_ENTRIESsets the maximum cached URLs (default500). -
Bounded URL cache eviction: URL cache entries now track hit counts and use LFU eviction with oldest-entry tie-breaking, keeping the cache within the configured size limit.
- URL cache TTL default: The URL cache now reuses cached pages for up to 24 h within a running server unless entries expire or are evicted. Previous default was 60 s.
-
Least-privilege Docker workflow permissions:
security-events: writeis now isolated to a dedicated image-scan job in both the publish and rebuild workflows, withid-token: writeconfined to the publish/sign job and workflow-level permissions kept read-only. -
Patched bundled
hono: Pinned the transitivehonodependency to ≥ 4.12.25 (via npmoverrides) to resolve CVE-2026-54290 — a CORS middleware flaw that reflected any origin with credentials — in the published Docker image.
- Added a CI workflow that runs lint plus unit and integration tests on every pull request and push to
main.
-
searxng_suggestionstool: Returns search autocomplete suggestions from the SearXNG instance. Useful for exploring related queries before committing to a full search. -
searxng_instance_infotool: Discovers the capabilities of the connected SearXNG instance — enabled engines, supported categories, available languages, and safe-search settings. -
JSON response format:
searxng_web_searchaccepts a newresponse_formatparameter ("text"or"json"). The"json"format returns raw structured data instead of the formatted Markdown text, enabling programmatic result processing. -
Search metadata in text output:
searxng_web_searchtext responses now include SearXNG answers, spelling corrections, infoboxes, and autocomplete suggestions when the instance returns them — giving richer context alongside the ranked web results.
-
Metadata (answers, corrections, infoboxes) is now preserved in text output even when
min_scorefilters out all web results. Previously the metadata was silently dropped. -
Unresponsive engines are no longer listed in text output.
-
searxng_suggestionsandsearxng_instance_inforequests now route through the configured search proxy and default TLS dispatcher, matching the behaviour ofsearxng_web_search.
-
Result count control:
num_resultsparameter onsearxng_web_search(1–20) lets callers request only as many results as they need.SEARXNG_MAX_RESULTSenv var sets an operator-level hard cap that applies even whennum_resultsis omitted — useful for reducing token spend across all callers. -
Token budget limits:
SEARXNG_MAX_RESULT_CHARSenv var truncates each search result snippet to a character limit (appending…) before returning.URL_READ_MAX_CHARSenv var sets a defaultmaxLengthfor URL reads when the caller omits it — both controls are recommended for local models with small context windows. -
HEAD preflight for URL reader: A fast HEAD request is made before every URL fetch to check
Content-Length. If the server reports a size aboveURL_READ_MAX_CONTENT_LENGTH_BYTES(default 5 MB), the download is blocked and a descriptive message withreadHeadings/sectionpagination hints is returned instead of downloading an unbounded body. -
categoriesparameter onsearxng_web_search: Routes searches to specific SearXNG categories —general,news,images,videos,it,science,files,social media. Omitting the parameter uses the SearXNG instance default (general). -
Configurable search defaults:
SEARXNG_DEFAULT_LANGUAGEandSEARXNG_DEFAULT_SAFESEARCHenv vars set operator-level defaults for language and safe-search level. Per-call parameters still take precedence. InvalidSEARXNG_DEFAULT_SAFESEARCHvalues (not0,1, or2) are logged and ignored. -
Configurable timeouts:
SEARXNG_TIMEOUT_MScontrols the search request timeout andFETCH_TIMEOUT_MScontrols the URL reader fetch timeout (both default to10000ms). -
Lite tool schemas (
SEARXNG_LITE_TOOLS=true): When set, registers minimalquery-only andurl-only tool schemas instead of the full parameter list. Reduces context overhead for local models with small context windows while still forwarding any extra arguments the caller provides.
- Pinned the npm trusted publishing installer step in the publish workflow to a full commit SHA to guard against tag-swap supply-chain attacks.
- Docker images are now signed with Cosign (keyless OIDC). Verify a published image with:
cosign verify docker.io/isokoliuk/mcp-searxng:latest \ --certificate-identity-regexp 'https://github.com/ihor-sokoliuk/mcp-searxng/.github/workflows/docker-publish.yml@.*' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com - Expanded fuzz test coverage: search parameter handling and URL read arguments are now fuzz-tested on every CI run.
- Tightened GitHub Actions workflow permissions to least-privilege and switched to reproducible
npm ciinstalls in the publish pipeline.
test:coveragescript now enforces the coverage threshold mechanically.- Gitignored AI process artifacts (plans, drafts) so they can never be committed.
- Docker base image (
node:lts-alpine) is now pinned by digest and bumped automatically via Dependabot. - Added a weekly rebuild workflow: when upstream patches the base image, the published Docker image is rebuilt from the latest release tag, re-scanned with Trivy, and republished under the same version tags. Published images now embed the
org.opencontainers.image.base.digestOCI label for auditability.
- Expanded
SearXNGWebresponse interface to include all fields returned by the API. - Search requests now use
AbortControllerto enforce the configured timeout and prevent hanging.
- Pinned all GitHub Actions workflow steps to full commit SHAs to guard against tag-swap supply-chain attacks.
- Added CodeQL static analysis, Trivy Docker image scanning, and ClusterFuzzLite continuous fuzzing.
- Added Dependabot for automated npm and GitHub Actions dependency updates.
- Verified
mcp-publisherbinary integrity with SHA-256 checksum before use.
- Hotfix: corrected
binentry inpackage-lock.jsonthat caused install failures in some environments.
- Server silently exiting when launched via
npx, Claude Desktop, opencode, or mcpo (#91). Root cause: theisMainModulepath comparison introduced in v1.2.0 fails when Node runs through an npm.bin/symlink. Replaced with a dedicatedsrc/cli.tsentrypoint — works on every Node version and invocation method.
- Breaking: HTTP server now binds to
127.0.0.1by default instead of0.0.0.0. Operators who need network-wide access must opt in withMCP_HTTP_HOST=0.0.0.0. - Added
express-rate-limitto all HTTP routes — configurable viaMCP_RATE_WINDOW_MS,MCP_RATE_INIT_MAX,MCP_RATE_SESSION_MAX.
- Hotfix for issue #91 (server exit on npx invocation).
weekoption forsearxng_web_searchtime_rangeparameter.min_scorefilter parameter forsearxng_web_search.
- Added
MCP_HTTP_AUTH_TOKENbearer token authentication for HTTP transport. - Enabled TLS certificate verification options (
MCP_TLS_*).
- Minor stability fixes for HTTP transport.
MCP_HTTP_HOSTenvironment variable to customise server address binding.
- URL fetch tool (
web_url_read) reliability improvements.
- Escape user input in
extractSectionregex to prevent ReDoS (CWE-1333) (#71). - Add
mcp-protocol-versionto CORSallowedHeaders(#77).
- Improved
searxng_web_searchtool description to prevent LLM usingpromptinstead ofquery(#80).
- Create a new
McpServerper HTTP session to preventAlready connectedcrash (#66).
- Enhanced
SEARXNG_URLvalidation, error handling, and documentation (#64).
- Updated all dependencies to latest versions to address known vulnerabilities.