trackmcp
Back to directory
hypersniper05

MCP-WebSearch-SearXNG

View on GitHub

Self-hosted SearXNG metasearch + MCP server. Privacy-respecting web search and image retrieval for LLMs, agents, and tool-using applications. HTTP Streamable transport, multi-query parallel search, base64 image rendering with smart resizing.

0 stars JavaScriptOthers Updated Sep 4, 2026

Documentation

WebSearch SearXNG + MCP Server

A self-hosted SearXNG metasearch engine paired with the `mcp-searxng` MCP server, built for LLMs, agents, and tool-using applications. Lets your model (or autonomous agent) browse the web, run searches across many engines at once, and surface images directly in your front end. Runs entirely on your own machine via Docker, accessed from your browser, and speaks MCP over HTTP Streamable transport so it plugs into any MCP-compatible client (Claude Desktop, Cline, Open WebUI, LM Studio, custom agent frameworks, etc.). Enhanced with image/video/category search, offset pagination, and smart base64 image resizing for vision-capable models. Both services stay on your local network, so nothing leaves your box unless you ask it to. No ads, no telemetry, no third-party trackers, no accounts — the SearXNG frontend is privacy-by-design and the MCP patches add zero phone-home of their own.

SearXNG frontend — the privacy-respecting metasearch UI at localhost:8888. Same backend the MCP server queries internally.

Autonomous tool chain — model fires multiple searxng_web_search calls in parallel, then chains into web_url_read to pull full article text.

Clean text extraction — long-form article content pulled by web_url_read, boilerplate stripped, ready for the model to summarize.

Inline image in news briefing — image fetched and resized through web_url_read's sharp pipeline. No external CDN, served as a base64 MCP image block.

Multi-query parallel search — queries separated by | fan out several searxng_web_search calls at once, landing on the "It's Gonna Be May" meme.

Native image blocks — multiple images returned inline. The detail parameter (low/medium/high) controls resize quality vs. token cost.

Installation

> Quick note on privacy before you start. This stack runs entirely on your machine and is privacy-respecting by design (no telemetry, no accounts, no third-party trackers), but the defaults are tuned for "just works," not maximum privacy. Before exposing it beyond your own computer, to your LAN, the internet, or anyone else please read **ADVANCED.md**

Prerequisites

  • Docker and Docker Compose must be installed
  • Node.js >= 20 must be installed

Step 1: Clone the Repository

bash
git clone https://github.com/hypersniper05/MCP-WebSearch-SearXNG.git
cd MCP-WebSearch-SearXNG

This gives you the `docker-compose.yml`, the SearXNG `settings.yml`, the MCP patches, and the custom `Dockerfile` already laid out — so the next step is straight to configuration.

> Prefer to set everything up by hand instead of cloning? See **Building from scratch (without cloning)** in ADVANCED.md.

Step 2: Configure SearXNG Settings

The shipped `searxng/config/settings.yml` is already preconfigured for this stack — JSON output enabled, rate limiter off, fast engine retries, a curated set of engines, and engine suspension times tuned so a blocked engine is not retried on every single query. The only thing you need to touch here is the `secret_key`.

> Want to change which engines are enabled, the request timeout, or any other SearXNG default? See **Modifying the default settings** in ADVANCED.md.

Set the `secret_key` (or use the env var)

The shipped `settings.yml` has a placeholder `secret_key: "CHANGE_ME_BEFORE_RUNNING"`. SearXNG will start with the placeholder, so for a localhost-only personal instance you can leave it and come back to this later. But if you ever bind to `0.0.0.0`, expose this to your LAN, or share it with anyone, generate a real one — otherwise an attacker can forge image-proxy and CSRF tokens against your instance.

Two ways to set it (pick one):

Option A — env var (recommended):

Create a `.env` file next to `docker-compose.yml`:

code
SEARXNG_SECRET=

The env var overrides whatever is in `settings.yml`, so the placeholder can stay in the tracked file forever.

Option B — edit `settings.yml` directly:

Replace the placeholder with your generated key. If you do this, make sure not to leak your `settings.yml`.

Generate a key with whichever tool you have installed:

powershell
# PowerShell (Windows — built in)
[System.BitConverter]::ToString([System.Security.Cryptography.RandomNumberGenerator]::GetBytes(32)).Replace('-','').ToLower()
bash
# Node.js (already installed for the MCP server)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

# Docker (works anywhere this stack does)
docker run --rm alpine sh -c "apk add --no-cache openssl > /dev/null && openssl rand -hex 32"

# Git Bash / Linux / macOS
openssl rand -hex 32

# Python
python -c "import secrets; print(secrets.token_hex(32))"

For the rest of the privacy/hardening trade-offs (LAN exposure, MCP auth, Tor routing), see **ADVANCED.md**.

Step 3: Build and Start Everything

bash
docker compose build
docker compose up -d

Step 4: Verify Services

Health check:

bash
curl -s http://localhost:3001/health

Expected: `{"status":"healthy","server":"ihor-sokoliuk/mcp-searxng","version":"0.9.2-enhanced","transport":"http"}`

Test search:

bash
curl -s "http://localhost:8888/search?q=test&format=json" | head -c 200

Step 5 (only if accessing from another machine): Open the bindings to your LAN / Tailscale

By default the shipped `docker-compose.yml` binds the services to `127.0.0.1` — reachable only from the machine running Docker. If you want another device on your LAN, your Tailscale tailnet, or anywhere else off-host to reach the SearXNG UI or the MCP endpoint, you have to change the bindings to `0.0.0.0`.

Quick diagnostic to spot this:

cmd
docker compose ps

Look at the PORTS column. If you see:

What you seeWhat it means
`0.0.0.0:8888->8080/tcp`Reachable from anywhere on the network ✓
`127.0.0.1:8888->8080/tcp`Localhost only — the *exact* symptom that makes external access "time out"

If both rows show `127.0.0.1:` (or one does and one doesn't, like SearXNG locked down while MCP is open), edit `docker-compose.yml` and change the binding(s):

yaml
services:
  searxng:
    ports:
      - "0.0.0.0:8888:8080"   # was 127.0.0.1:8888:8080

  mcp-searxng:
    ports:
      - "0.0.0.0:3001:3001"   # was 127.0.0.1:3001:3001

Then a full down + up is required (port changes don't apply on a plain `restart`):

cmd
docker compose down
docker compose up -d
docker compose ps

Both rows should now show `0.0.0.0:...`.

If `docker compose ps` already shows `0.0.0.0` and external access *still* fails, it's the OS firewall. On Windows, allow the ports from an admin PowerShell:

powershell
New-NetFirewallRule -DisplayName "MCP SearXNG (8888)" -Direction Inbound -Protocol TCP -LocalPort 8888 -Action Allow
New-NetFirewallRule -DisplayName "MCP Server (3001)"   -Direction Inbound -Protocol TCP -LocalPort 3001 -Action Allow

> Security note: switching to `0.0.0.0` exposes the services to anyone who can reach your machine on those ports. The MCP server has no authentication. If you're going beyond a trusted home/Tailscale network, read ADVANCED.md → Tier 2 for adding a reverse proxy with auth.

Endpoints Summary

ServiceURLPurpose
SearXNG UIhttp://localhost:8888Web search interface
SearXNG APIhttp://localhost:8888/searchJSON search API
MCP Serverhttp://localhost:3001/mcpMCP Streamable HTTP
MCP Healthhttp://localhost:3001/healthHealth check endpoint

> Need TLS for an HTTPS-only client? The cleanest path is **Tailscale Serve** if both ends are on a tailnet (real Let's Encrypt-trusted cert, zero per-client setup), or a reverse proxy like Caddy / nginx with your own cert otherwise. Either is added in front of the existing HTTP MCP endpoint without any changes to this stack.

MCP Tools

Web search with category, pagination, and filtering support.

ParameterTypeDescription
`query`stringSearch query (required)
`categories`string`general`, `images`, `videos`, `news`, `music`, `files`, `it`, `science`, `social media`, `map`
`max_results`numberResults per batch (default: 20 general, 10 images/videos)
`offset`numberSkip N results for pagination (e.g., 10 for results 11-20)
`pageno`numberSearXNG page number
`time_range`string`day`, `month`, `year`
`language`stringLanguage code (e.g., `en`, `fr`)
`safesearch`number`0` (none), `1` (moderate), `2` (strict)

2. web_url_read

Fetches and reads URLs. Auto-detects image URLs and returns resized base64 image blocks.

ParameterTypeDescription
`url`stringURL to read (required)
`detail`stringImage resize level: `low` (448px, ~256 tokens), `medium` (768px, ~756 tokens, default), `high` (1280px, ~2048 tokens)
`startChar`numberCharacter offset for text pagination
`maxLength`numberMax characters to return
`section`stringExtract content under a heading
`paragraphRange`stringParagraph range (e.g., `1-5`)
`readHeadings`booleanReturn headings only

Image Search Flow

1. `searxng_web_search({query: "sunset", categories: "images"})` → returns image URLs + metadata

2. Pick an image → `web_url_read({url: "https://example.com/sunset.jpg", detail: "medium"})` → returns resized base64 image

3. To see more results → `searxng_web_search({query: "sunset", categories: "images", offset: 10})` → results 11-20

Custom Patch Files

All patches are in `mcp-searxng-patches/` and mounted read-only into the container:

FilePurpose
`Dockerfile`Extends base image with `sharp` for image resizing
`types.js`Tool schemas with `categories`, `offset`, `max_results`, `detail` parameters
`search.js`Category-aware search formatting, offset pagination, image/video result fields
`url-reader.js``fetchImage()` with sharp resizing (3 detail presets), base64 MCP image blocks
`index.js`Tool handler wiring, image URL detection, detail passthrough

Updating SearXNG

SearXNG is pinned to `:latest` in `docker-compose.yml`, so updates are pull-and-restart. Config in `searxng/config/settings.yml` is volume-mounted and survives the update.

powershell
cd MCP-WebSearch-SearXNG
docker compose pull searxng        # grab newest :latest image
docker compose up -d searxng       # recreate container with new image
docker compose logs searxng --tail 100   # check for config-schema warnings
docker image prune -f              # remove the old image

Major releases occasionally rename keys in `settings.yml` or add required fields. Check the SearXNG releases page for breaking changes before updating.

Pin to a specific version (safer / reproducible)

Replace `:latest` in `docker-compose.yml` with a date-tagged release, e.g.:

yaml
image: docker.io/searxng/searxng:2026.4.15-abc1234

Updates then become an explicit edit — review release notes, bump tag, `docker compose up -d`.

Automatic updates

OptionProsCons
Watchtower containerSet-and-forgetSilent break on config-schema changes
Scheduled `docker compose pull && up -d` (Task Scheduler or `/schedule` agent)You control cadenceSame schema-break risk
GitHub release notifications + manual updateSafestSlowest

A reasonable middle ground: schedule a weekly job that runs `docker compose pull` and reports whether a new image is available, but does not auto-apply it.

Updating the MCP server

`mcp-searxng` is built locally from `mcp-searxng-patches/Dockerfile` with custom JS patches

mounted on top. The base image is pinned to `isokoliuk/mcp-searxng:1.16.0` — see the

comment in that Dockerfile before changing it.

After pulling this repo on another machine, or to pick up upstream MCP changes:

bash
docker compose build --pull mcp-searxng
docker compose up -d --force-recreate mcp-searxng

`--force-recreate` is not optional. A plain `docker compose up -d` rebuilds the image but

leaves the existing container running on the old image, so the update silently does

nothing. Symptom: the container crash-loops with

`ERR_MODULE_NOT_FOUND: Cannot find package '@modelcontextprotocol/sdk'` while SearXNG

itself keeps working fine.

Confirm the container is actually on a freshly built image — these two ids must match:

bash
docker inspect mcp-searxng --format '{{.Image}}' && docker images --no-trunc --format '{{.ID}}' mcp-searxng-enhanced:latest

And confirm which base it was built on. `sdk` is correct; `core node server` means the

image is stale and still on the 2.x base:

bash
docker run --rm --entrypoint sh mcp-searxng-enhanced:latest -c "ls /app/node_modules/@modelcontextprotocol/"

If upstream renames internal modules or changes function signatures, the volume-mounted JS patches may need to be rebased manually.

Tested With

  • Qwen3.6 35B A3B — runs both `searxng_web_search` (including multi-query and category filters) and `web_url_read` (text + image modes) reliably. Tool selection, parameter inference, and result interpretation all work well with this model.

Notes

  • MCP server uses HTTP Streamable transport (not stdio or SSE-only)
  • SearXNG connects internally via Docker hostname `searxng:8080`
  • All data stays local — no external API keys needed
  • Custom patches are MIT-licensed, audited, no filesystem/shell access, no telemetry
  • Search engines: Bing, Yahoo, mwmbl, Seznam and Wiby are the general-web

engines enabled by default, plus Wikipedia and WolframAlpha for direct

answers. Brave, DuckDuckGo, Startpage and Mojeek stay configured but are

frequently blocked — see below.

  • If results look thin, it is usually your exit IP, not this stack. Brave

(HTTP 429), DuckDuckGo and Startpage (CAPTCHA), Mojeek (empty result page)

and Ecosia (HTTP 403) all reject self-hosted instances on shared or flagged

addresses — VPN exits, CGNAT and datacenter ranges are the common causes.

Every one of them fails on the very first query from a cold container, so it

is not a rate-limiting problem you can tune your way out of. Check what

SearXNG reports:

bash
curl -s "http://localhost:8888/search?q=test&format=json" | jq .unresponsive_engines

Options, roughly in order of effort: search from a residential IP without a

VPN, put the outgoing requests behind proxies (`outgoing.proxies` in

`searxng/config/settings.yml`), or supply an API key for an engine that sells

one (`braveapi`). Adding more blocked engines does not help.

Frequently asked questions

What is MCP-WebSearch-SearXNG?

MCP-WebSearch-SearXNG is Self-hosted SearXNG metasearch + MCP server. Privacy-respecting web search and image retrieval for LLMs, agents, and tool-using applications. HTTP Streamable transport, multi-query parallel search, base64 image rendering with smart resizing.

How do I install MCP-WebSearch-SearXNG?

Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.

Is MCP-WebSearch-SearXNG open source?

Yes — it is hosted on GitHub at https://github.com/hypersniper05/MCP-WebSearch-SearXNG.

Related MCP tools

Run your own MCP server? See who uses it and what to fix.

Measure it with TrackMCP