Skip to main content
Glama
README.md
# searxng-mcp

[中文文档](README.zh.md)

[![License: BSD-3-Clause](https://img.shields.io/badge/License-BSD--3--Clause-blue.svg)](LICENSE)
[![Python >= 3.10](https://img.shields.io/badge/Python-3.10%2B-blue.svg)](pyproject.toml)
[![CI](https://github.com/BG-Titan/searxng-mcp/actions/badge.svg)](.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

A4.2/5.0

Scored across 3 tools

Disambiguation4/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness5/5

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues