MCP-WebSearch-SearXNG

Lets your model (or autonomous agent) browse the web, run searches across many engines at once, and surface images directly in your front end.

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 web frontend at localhost:8888
SearXNG frontend — the privacy-respecting metasearch UI at localhost:8888. Same backend the MCP server queries internally.
LLM autonomously calling searxng_web_search and web_url_read
Autonomous tool chain — model fires multiple searxng_web_search calls in parallel, then chains into web_url_read to pull full article text.
Article continuation extracted via web_url_read
Clean text extraction — long-form article content pulled by web_url_read, boilerplate stripped, ready for the model to summarize.



Synthesized AI news briefing with inline image
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 meme search
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.
Multiple inline meme images returned by web_url_read
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

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 working engines (Bing, Mojeek, Yahoo, Startpage, etc.). 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:

SEARXNG_SECRET=<paste your generated 64-char hex string here>

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 (Windows — built in)
[System.BitConverter]::ToString([System.Security.Cryptography.RandomNumberGenerator]::GetBytes(32)).Replace('-','').ToLower()
# 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

docker compose build
docker compose up -d

Step 4: Verify Services

Health check:

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

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

Test search:

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:

docker compose ps

Look at the PORTS column. If you see:

What you seeWhat it means
0.0.0.0:8888->8080/tcpReachable from anywhere on the network ✓
127.0.0.1:8888->8080/tcpLocalhost 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):

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):

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:

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

1. searxng_web_search

Web search with category, pagination, and filtering support.

ParameterTypeDescription
querystringSearch query (required)
categoriesstringgeneral, images, videos, news, music, files, it, science, social media, map
max_resultsnumberResults per batch (default: 20 general, 10 images/videos)
offsetnumberSkip N results for pagination (e.g., 10 for results 11-20)
pagenonumberSearXNG page number
time_rangestringday, month, year
languagestringLanguage code (e.g., en, fr)
safesearchnumber0 (none), 1 (moderate), 2 (strict)

2. web_url_read

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

ParameterTypeDescription
urlstringURL to read (required)
detailstringImage resize level: low (448px, ~256 tokens), medium (768px, ~756 tokens, default), high (1280px, ~2048 tokens)
startCharnumberCharacter offset for text pagination
maxLengthnumberMax characters to return
sectionstringExtract content under a heading
paragraphRangestringParagraph range (e.g., 1-5)
readHeadingsbooleanReturn 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
DockerfileExtends base image with sharp for image resizing
types.jsTool schemas with categories, offset, max_results, detail parameters
search.jsCategory-aware search formatting, offset pagination, image/video result fields
url-reader.jsfetchImage() with sharp resizing (3 detail presets), base64 MCP image blocks
index.jsTool 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.

Manual update (recommended)

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.:

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 (base: isokoliuk/mcp-searxng:latest) with custom JS patches mounted on top. To pull upstream MCP changes:

  1. docker compose build --pull mcp-searxng — rebuilds from a freshly pulled base image
  2. docker compose up -d mcp-searxng — recreates the container
  3. Verify the patches in mcp-searxng-patches/*.js still apply cleanly against any upstream API changes (check docker compose logs mcp-searxng for errors)

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, Startpage, Mojeek, Yahoo active; Google with mobile UI (may intermittently block)

İlgili Sunucular

NotebookLM Web Importer

Web sayfalarını ve YouTube videolarını tek tıkla NotebookLM'e aktarın. 200.000'den fazla kullanıcı tarafından güveniliyor.

Chrome Eklentisini Yükle