searxng-mcp
# searxng-mcp
[中文文档](README.zh.md)
[](LICENSE)
[](pyproject.toml)
[](.github/workflows/ci.yml)
An [MCP (Model Context Protocol) server](https://modelcontextprotocol.io) for **web search backed by [SearXNG](https://docs.searxng.org/)** — a self-hosted, privacy-focused metasearch engine.
No API keys, no vendor lock-in, no commercial quotas. Point it at any SearXNG instance and give your MCP client (Qwen Code, Claude, etc.) real web access.
## Features
| Tool | Description |
|------|-------------|
| `web_search` | General & vertical search (categories: `news`, `science`, `it`, `files`, `images`, `videos`, `music`, `map`, `social media`). Supports pagination, language, freshness filter (`day`/`week`/`month`/`year`) and safesearch. Returns ranked results (title / URL / snippet / source engines) plus direct answers, corrections and suggestions when SearXNG provides them. |
| `web_search_news` | News-vertical search with time filtering. Same result shape as `web_search`. |
| `web_extract` | Fetch a page and return its main content as clean plain text (scripts, navigation and boilerplate removed). Truncates to `max_chars`. Use it to read a search result in full. |
## How it works
```
MCP client ──stdio──> searxng-mcp ──HTTP JSON API──> your SearXNG instance ──> upstream engines
```
The server queries the SearXNG **JSON API** (`GET /search?format=json`) and normalizes the response into a compact, LLM-friendly shape. Errors are actionable: if the instance hasn't enabled the JSON format, the tool tells you exactly what to add to `settings.yml`.
## Quick start
### 1. Run a SearXNG instance (skip if you already have one)
```bash
cd examples
docker compose up -d # or: podman run -d --name searxng -p 8080:8080 \
# -v $PWD/searxng-settings.yml:/etc/searxng/settings.yml:ro \
# searxng/searxng:latest
```
The instance runs at `http://localhost:8080`. The example settings enable the JSON API — **required** for this server:
```yaml
search:
formats:
- html
- json
```
### 2. Install
```bash
git clone https://github.com/BG-Titan/searxng-mcp.git
cd searxng-mcp
uv sync # or: python -m venv .venv && .venv/bin/pip install -e .
```
### 3. Run
```bash
SEARXNG_URL=http://localhost:8080 uv run searxng-mcp
# stdio transport by default; set SEARXNG_MCP_TRANSPORT=streamable-http to switch
```
### 4. Register with your MCP client
See [`examples/mcp-client.example.json`](examples/mcp-client.example.json) (Claude Desktop / Qwen Code / Claude Code compatible `.mcp.json` format). Replace the path placeholder with your checkout:
```json
{
"mcpServers": {
"searxng": {
"command": "uv",
"args": ["run", "--directory", "/abs/path/to/searxng-mcp", "searxng-mcp"],
"env": { "SEARXNG_URL": "http://localhost:8080" }
}
}
}
```
Without `uv`, point `command` at the installed entry point (e.g. `/path/to/.venv/bin/searxng-mcp`).
### 5. Verify
```bash
uv run pytest # unit tests, schema checks and a stdio end-to-end test
```
## Configuration
Environment variables read at startup:
| Variable | Default | Description |
|----------|---------|-------------|
| `SEARXNG_URL` | `http://localhost:8080` | Base URL of your SearXNG instance. |
| `SEARXNG_TIMEOUT` | `15` | Per-request timeout in seconds. |
| `SEARXNG_MCP_TRANSPORT` | `stdio` | MCP transport: `stdio` or `streamable-http`. |
## Project layout
```
searxng-mcp/
├── .github/workflows/ci.yml # GitHub Actions: uv sync + pytest
├── examples/
│ ├── docker-compose.yml # one-shot local SearXNG
│ ├── mcp-client.example.json # MCP client config example
│ └── searxng-settings.yml # SearXNG settings with JSON API enabled
├── src/searxng_mcp/
│ ├── client.py # SearXNG JSON API client (httpx)
│ ├── extract.py # page fetch + readable-text extraction
│ ├── server.py # MCPServer (mcp SDK 2.x) & tool registration
│ └── __main__.py
├── tests/
│ ├── test_client.py # parsing, params, error paths (MockTransport)
│ ├── test_extract.py # HTML -> text
│ ├── test_server.py # tool schemas & invocation
│ └── test_smoke.py # stdio handshake + real-HTTP e2e test
└── pyproject.toml
```
## Development
```bash
uv sync # create .venv, install deps + dev group
uv run pytest -q # run the test suite
```
## Troubleshooting
- **`SearXNG returned HTML instead of JSON`** — the instance has the JSON format disabled. Add `search.formats: [html, json]` to its `settings.yml` and restart.
- **HTTP 429** — the instance limiter is active; set `server.limiter.on: false` in `settings.yml` for local use.
- **`Cannot connect to SearXNG`** — check the instance is up and `SEARXNG_URL` points to it.
- **Few/empty results** — SearXNG results depend on its enabled upstream engines and languages; try a different `language` or the `general` category.
## Contributing
Issues and pull requests are welcome. Run `uv run pytest` before submitting.
## License
[BSD-3-Clause](LICENSE) © BG-Titan
TDQS
Scored across 3 tools
web_search and web_search_news overlap somewhat, but web_search_news is clearly scoped to time-filtered news while web_search covers general/vertical search. web_extract is completely distinct, so an agent should be able to choose correctly with the descriptions.
web_search and web_search_news follow a clear web_search[_modifier] pattern, and web_extract is also snake_case and readable. The slight inconsistency is that web_extract uses a different verb style than the web_search* tools, but the overall naming is predictable.
Three tools is a well-scoped set for a search-focused MCP server: general search, news search, and page extraction. Each tool serves a distinct need without unnecessary bloat.
The server covers the core search workflow: searching the web, searching news with time filters, and extracting page content for deeper reading. SearXNG categories are exposed via web_search, so verticals like images or videos are also reachable, leaving no obvious dead ends.