Skip to main content
Glama
EmericLaberge

Hermes SearXNG MCP Server

Hermes SearXNG MCP Server

Clean, production-ready MCP server for self-hosted SearXNG Designed for Hermes Agent, compatible with Claude Code, OpenClaw, and other MCP clients

Python Version License MCP

Overview

This MCP server provides web search and content retrieval capabilities using a self-hosted SearXNG instance. It's designed to be a drop-in replacement for paid web search APIs, offering:

  • Privacy-preserving: Self-hosted, no data sent to third parties

  • Cost-free: No API keys, no rate limits

  • Flexible: 70+ search engines aggregated through SearXNG

  • MCP-compliant: Works with any MCP client (Hermes, Claude Desktop, OpenClaw, etc.)

  • Content extraction: Automatic Markdown extraction from search results

Related MCP server: SearXNG MCP Server

Features

  • Search across 70+ search engines via SearXNG

  • Support for multiple categories (general, news, images, videos, IT, science, etc.)

  • Language filtering

  • Time-based filtering (day, week, month, year)

  • Optional full content fetching for results

Content Retrieval (get_content)

  • Extract LLM-ready Markdown from any URL

  • Specialized handlers for:

    • GitHub Issues

    • Stack Exchange (Stack Overflow, Server Fault, Super User, etc.)

    • Wikipedia

    • arXiv

  • Universal fallback using Trafilatura + BeautifulSoup

  • Handles paywalls and anti-bot measures (best-effort)

  • Convenience wrapper optimized for news articles

  • Default time filtering (last week)

  • Aggregates from news-focused search engines

Security & Production Features

  • SSRF Protection: Blocks private IPs, localhost, cloud metadata endpoints (169.254.169.254)

  • Input Validation: All tool parameters validated (query length, categories, language codes, time ranges)

  • Response Size Limits: Configurable max content size (default: 5MB) with streaming

  • Connection Pooling: Shared HTTP client with keep-alive connections and graceful shutdown

  • In-Memory Caching: 5-minute TTL cache for search results (100 entries max)

  • Docker Support: Multi-stage Dockerfile with docker-compose (includes SearXNG sidecar)

  • CI/CD: GitHub Actions workflow (Python 3.10/3.11/3.12, lint, typecheck, test)

Installation

From Source

# Clone the repository
git clone https://github.com/EmericLaberge/hermes-searxng-mcp.git
cd hermes-searxng-mcp

# Install in development mode
pip install -e .

From PyPI (when published)

pip install hermes-searxng-mcp-server

Configuration

SearXNG Instance

You need a running SearXNG instance. If you don't have one:

# Using Docker
docker run -d -p 8888:8080 \
  -v $(pwd)/searxng:/etc/searxng \
  searxng/searxng:latest

# Or follow the official guide:
# https://docs.searxng.org/admin/installation.html

Make sure JSON output is enabled in your SearXNG settings.yml:

search:
  formats:
    - html
    - json  # Required!

Environment Variables

Variable

Required

Default

Description

SEARXNG_BASE_URL

Yes

-

URL of your SearXNG instance (e.g., http://localhost:8888)

SEARXNG_TIMEOUT_SECONDS

No

30

Request timeout in seconds

SEARXNG_USER_AGENT

No

Chrome UA

Custom User-Agent header

SEARXNG_HEADERS_JSON

No

{}

JSON object with custom HTTP headers

SEARXNG_MAX_CONTENT_BYTES

No

5242880

Maximum content size in bytes (default: 5MB)

SEARXNG_TRANSPORT

No

stdio

Transport type: stdio or http

SEARXNG_HOST

No

0.0.0.0

HTTP host (only for HTTP transport)

SEARXNG_PORT

No

8000

HTTP port (only for HTTP transport)

Proxy Support

The server supports proxy configuration through standard environment variables. Proxy handling is implemented natively by httpx via its trust_env feature (enabled by default).

Standard proxy variables:

Variable

Description

HTTP_PROXY

Proxy URL for HTTP requests (e.g., http://proxy:8080)

HTTPS_PROXY

Proxy URL for HTTPS requests (e.g., http://proxy:8080)

NO_PROXY

Comma-separated list of hosts to bypass the proxy

NO_PROXY patterns supported:

  • Exact host match: localhost, searxng.local

  • Domain suffix: .example.com (matches example.com, www.example.com)

  • Wildcard: * (bypasses all hosts)

  • Port-based: localhost:8888

Proxy authentication:

Include credentials in the proxy URL:

export HTTP_PROXY="http://user:pass@proxy:8080"
export HTTPS_PROXY="http://user:pass@proxy:8080"

TLS certificates:

For corporate proxies that inspect SSL traffic, configure custom CA certificates:

export SSL_CERT_FILE=/path/to/ca-bundle.crt
# or
export REQUESTS_CA_BUNDLE=/path/to/ca-bundle.crt

Note: The proxy is used for outbound connections from the MCP server to SearXNG and fetched URLs. The SearXNG instance itself should be configured separately if it also needs proxy access.

Usage

With Hermes Agent

# Add the MCP server
hermes mcp add searxng \
  --command python3 \
  --args -m hermes_searxng_mcp.server

# Restart Hermes to load the server
hermes gateway restart

Then in Hermes, just ask naturally:

You: Search for the latest news about AI
Hermes: [Uses search_news tool automatically]

You: Find documentation on FastAPI
Hermes: [Uses web_search tool automatically]

You: What's the content of https://example.com?
Hermes: [Uses get_content tool automatically]

With Claude Desktop

Add to ~/.config/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "searxng": {
      "command": "python3",
      "args": ["-m", "hermes_searxng_mcp.server"],
      "env": {
        "SEARXNG_BASE_URL": "http://localhost:8888"
      }
    }
  }
}

Restart Claude Desktop.

With OpenClaw

Add to your OpenClaw MCP configuration:

mcp_servers:
  searxng:
    command: python3
    args: ["-m", "hermes_searxng_mcp.server"]
    env:
      SEARXNG_BASE_URL: "http://localhost:8888"

Standalone Testing

# Set environment variable
export SEARXNG_BASE_URL="http://localhost:8888"

# Run the server
python -m hermes_searxng_mcp.server

# In another terminal, test with MCP client
# or use curl for manual testing of SearXNG
curl "http://localhost:8888/search?q=test&format=json"

With Docker

# Build and run with SearXNG sidecar
docker compose up -d

# Or build manually
docker build -t hermes-searxng-mcp .
docker run -e SEARXNG_BASE_URL=http://searxng:8080 hermes-searxng-mcp

HTTP Transport

For remote access, set SEARXNG_TRANSPORT=http:

export SEARXNG_TRANSPORT=http
export SEARXNG_HOST=0.0.0.0
export SEARXNG_PORT=8000
python -m hermes_searxng_mcp.server

Test with curl:

curl http://localhost:8000/mcp

Tools Reference

Search the web and return results.

Parameters:

  • query (string, required): Search query

  • num_results (int, optional): Number of results (default: 5, max: 50)

  • categories (string, optional): Category - general, news, images, videos, it, science, files, music

  • language (string, optional): Language code (default: "en")

  • time_range (string, optional): Time filter - day, week, month, year

  • include_content (bool, optional): Fetch full page content (default: false)

Returns:

{
  "results": [
    {
      "title": "Result Title",
      "url": "https://example.com",
      "snippet": "Search snippet from SearXNG...",
      "content": "# Full Markdown content...",
      "engine": ["google", "bing"]
    }
  ]
}

get_content

Fetch a URL and extract Markdown content.

Parameters:

  • url (string, required): URL to fetch

Returns:

{
  "url": "https://example.com",
  "content": "# Extracted Markdown content..."
}

search_news

Search recent news articles (convenience wrapper).

Parameters:

  • query (string, required): News topic

  • num_results (int, optional): Number of articles (default: 10)

  • time_range (string, optional): Time filter (default: "week")

  • language (string, optional): Language code (default: "en")

Returns: Same as web_search

Development

Setup

# Clone repository
git clone https://github.com/EmericLaberge/hermes-searxng-mcp.git
cd hermes-searxng-mcp

# Create virtual environment
python -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Install in development mode with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run linting
ruff check .
ruff format .

Project Structure

hermes-searxng-mcp/
├── src/hermes_searxng_mcp/
│   ├── __init__.py          # Package init
│   ├── server.py            # MCP server, tools, and input validation
│   ├── searxng.py           # SearXNG search with caching
│   ├── content_extractor.py # Content extraction with size limits
│   ├── http_client.py       # Shared HTTP client (connection pooling)
│   ├── ssrf_protection.py   # SSRF URL validation
│   ├── cache.py             # In-memory TTL cache
│   └── models.py            # Pydantic models
├── tests/                   # Test suite (41 tests)
├── .github/workflows/ci.yml # CI/CD pipeline
├── Dockerfile               # Multi-stage Docker build
├── docker-compose.yml       # Docker Compose with SearXNG
├── pyproject.toml           # Project configuration
├── README.md                # This file
└── LICENSE                  # MIT license

Adding New Features

  1. Add specialized content handler: Extend content_extractor.py with new patterns

  2. Add new search categories: Update searxng.py to support more SearXNG categories

  3. Add new MCP tools: Add @mcp.tool() decorators in server.py

Troubleshooting

"SEARXNG_BASE_URL is not set"

Set the environment variable:

export SEARXNG_BASE_URL="http://localhost:8888"

"SearXNG returned 403 Forbidden"

JSON output is disabled in SearXNG. Enable it in settings.yml:

search:
  formats:
    - html
    - json  # Add this line

Then restart SearXNG.

"Could not fetch content"

This is normal for:

  • Paywalled content (NYT, WSJ, etc.)

  • Protected content (requires login)

  • Unsupported content types (PDFs, videos)

  • Bot detection (some sites block automated requests)

Tools not appearing in Hermes

# Check MCP server is running
hermes mcp list

# Test connection
hermes mcp test searxng

# Restart Hermes
hermes gateway restart

Slow performance

  • Reduce num_results parameter

  • Set include_content=False (default)

  • Check SearXNG instance performance

  • Reduce SEARXNG_TIMEOUT_SECONDS if network is slow

Comparison with Alternatives

Feature

Hermes SearXNG MCP

Serper/Tavily

Built-in Web Search

Self-hosted

✅ Yes

❌ No

❌ No

Privacy

✅ No data leaves

❌ Sends to third-party

❌ Sends to third-party

Cost

✅ Free

❌ Paid

❌ Paid (often)

Rate Limits

✅ None

⚠️ Limited

⚠️ Limited

Search Engines

✅ 70+ via SearXNG

⚠️ Google only

⚠️ Varies

Content Extraction

✅ Yes

❌ No

⚠️ Varies

News Search

✅ Yes

✅ Yes

⚠️ Varies

MCP Compliant

✅ Yes

✅ Yes

✅ Yes

Docker Support

✅ Yes

❌ No

❌ No

SSRF Protection

✅ Yes

❌ No

❌ No

In-Memory Cache

✅ Yes

❌ No

❌ No

CI/CD

✅ GitHub Actions

⚠️ Varies

⚠️ Varies

Architecture

The server follows a clean, modular architecture:

┌─────────────────────────────────────────────────┐
│              MCP Client (Hermes)                │
└───────────────────┬─────────────────────────────┘
                    │ MCP Protocol (stdio)
┌───────────────────▼─────────────────────────────┐
│          FastMCP Server Layer                   │
│  ┌──────────────────────────────────────────┐   │
│  │  @mcp.tool() Decorators                  │   │
│  │  - web_search()                          │   │
│  │  - get_content()                         │   │
│  │  - search_news()                         │   │
│  └──────────────────────────────────────────┘   │
└───────────────────┬─────────────────────────────┘
                    │
        ┌───────────┼───────────┐
        │           │           │
┌───────▼──────┐ ┌─▼────────┐ ┌▼──────────────┐
│  SearXNG     │ │ Content  │ │   Models      │
│  Search      │ │ Extractor│ │   (Pydantic)  │
│  + Cache     │ │ + SSRF   │ │               │
│  + Validation│ │ + Limits │ │               │
└──────────────┘ └──────────┘ └────────────────┘
        │
        ▼
┌──────────────────────┐
│  SearXNG Instance    │
│  (HTTP + JSON API)   │
└──────────────────────┘

Contributing

Contributions are welcome! Please:

  1. Fork the repository

  2. Create a feature branch

  3. Make your changes

  4. Add tests if applicable

  5. Submit a pull request

License

MIT License - see LICENSE file for details.

Acknowledgments

Support


Available Tools

3 tools
get_contentA
Fetch a URL and extract LLM-ready Markdown content.

Use this when you already have a specific URL and want to read its content.
If you need to discover URLs first, use web_search() instead.

Args:
    url: URL to fetch and extract content from.

Returns:
    Dictionary with:
    - url: The requested URL
    - content: Extracted Markdown content

Example:
    >>> await get_content("https://example.com")
    {
        "url": "https://example.com",
        "content": "# Example Domain

This domain is for use in illustrative examples..." }

Notes:
    - Specialized handlers for GitHub Issues, Stack Exchange, Wikipedia, and arXiv.
    - Falls back to Trafilatura and BeautifulSoup for general websites.
    - Content extraction is best-effort and may fail for paywalled or protected content.
    - Some content types (PDFs, videos) may not be supported.

Raises:
    ContentExtractionError: If fetching or extraction fails.
ParametersJSON Schema
NameRequiredDescriptionDefault
urlYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses specialized handlers, fallback behavior, best-effort extraction, potential failures (paywalled/protected content), unsupported content types, and raises an exception. This is rich behavioral context beyond what the schema provides. It doesn't mention rate limits or auth, but for a fetch tool this is strong coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (Args, Returns, Example, Notes, Raises). It is somewhat verbose but every section earns its place: the example clarifies output, notes disclose limitations, and raises documents errors. The core purpose is front-loaded in the first line.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has only 1 parameter, no annotations, and an output schema, the description is complete. It covers what the tool does, when to use it, what it returns, what can go wrong, and limitations. An agent has everything needed to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It explains the single parameter 'url' as 'URL to fetch and extract content from' and provides a concrete example with expected output. This adds meaning beyond the bare schema, though it doesn't add format validation details (e.g., must be http/https).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Fetch a URL and extract LLM-ready Markdown content') and clearly identifies the resource (URL). It distinguishes itself from siblings by explicitly saying to use it when you already have a URL, versus web_search for discovering URLs. This is a clear, specific purpose.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says 'Use this when you already have a specific URL and want to read its content' and names the alternative: 'If you need to discover URLs first, use web_search() instead.' This provides clear when-to-use and when-not-to-use guidance, plus a named alternative.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_newsA

Search recent news articles using SearXNG.

This is a convenience wrapper around web_search() optimized for news.

Args: query: News topic or event to search for. num_results: Number of articles to return (default: 10). time_range: Time filter (day, week, month, year). Defaults to "week". language: Language code for results (default: "en").

Returns: Dictionary with "results" list of news articles.

Example: >>> await search_news("artificial intelligence breakthrough", num_results=5) { "results": [ { "title": "Major AI Breakthrough Announced", "url": "https://example.com/ai-breakthrough", "snippet": "Researchers announce...", "content": "", "engine": ["google"] } ] }

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYes
languageNoen
time_rangeNoweek
num_resultsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the transparency burden. It discloses the backend (SearXNG), the wrapper relationship to web_search(), and the return shape via the Returns section and example. It omits caveats like failure modes or rate limits, but the behavior of a read-oriented search tool is described well.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The docstring is front-loaded with the core purpose, then structured Args/Returns/Example sections. The example is concrete but not bloated, and every line adds useful information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a four-parameter search tool with no annotations, the description supplies purpose, backend, parameter semantics, returns, and an example. There is no obvious missing information an agent would need to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the Args section must compensate. It does: query is defined as 'News topic or event,' time_range lists allowed values, and num_results and language get defaults and meanings. This fully adds value beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence states a specific verb and resource: 'Search recent news articles using SearXNG.' The second sentence, 'convenience wrapper around web_search() optimized for news,' immediately distinguishes it from the web_search sibling.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It clearly implies use for news queries by calling itself optimized for news and a wrapper around web_search(). However, it does not explicitly say 'use web_search for non-news or general web queries,' so the when-not guidance is slightly implicit rather than fully explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updatesv1.0.0
    • First observedget_content
    • First observedsearch_news
    • First observedweb_search

TDQS

A4.5/5.0

Scored across 3 tools

Disambiguation4/5

web_search and get_content are clearly distinct: one discovers URLs, the other fetches a known URL. search_news overlaps with web_search since it is a news-specific wrapper, but the descriptions clearly frame it as a convenience for news queries, so an agent can usually disambiguate.

Naming Consistency5/5

All tool names follow the same lowercase snake_case verb_noun pattern: web_search, get_content, and search_news. There are no vague verbs or mixed naming conventions.

Tool Count4/5

At 3 tools, the set is close to the ideal size and covers the core search-and-fetch workflow. search_news is somewhat redundant with web_search(categories='news'), so it does not fully earn its place, but the overall count is still reasonable.

Completeness4/5

The main workflow of discovering results and extracting page content is covered well. Minor gaps exist, such as pagination/offset for browsing deeper result sets, but agents can work around these via num_results and the supported categories.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables AI agents to perform privacy-respecting web searches through SearXNG, with support for multiple search engines, categories, and advanced filtering options.
    26
    -
  • A
    license
    A
    quality
    D
    maintenance
    Enables web search, image search, and news search through a self-hosted SearXNG instance. Provides privacy-focused meta-search capabilities aggregating results from multiple search engines.
    3
    1
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Enables AI assistants to perform web searches and read URL content via a SearXNG instance.
    2
    13 npm
    MIT