Skip to main content
Glama

Aether

A local, self-hosted web search and page-fetching MCP server.

Aether exposes two tools to any MCP-compatible client:

  • web_search — query a self-hosted SearXNG instance and return titles, URLs, snippets, and publication dates.

  • fetch_page — download a public webpage and extract its readable text.

Everything runs on your machine: SearXNG handles the searching, and Aether talks to it over localhost. No API keys, no third-party search subscriptions.

Features

  • Self-hosted meta-search via SearXNG (Docker)

  • Plain-text extraction from HTML pages with BeautifulSoup

  • Search results capped at 10 per query; page text capped at 12,000 characters

  • fetch_page refuses non-public hosts (loopback, private, link-local, etc.), credentials in URLs, and redirects

  • Hard 1 MB response limit for fetched pages

  • Returned page content is clearly flagged as untrusted data

Related MCP server: Basic MCP Tools

Architecture

MCP client  ──stdio──▶  server.py (Aether)  ──HTTP──▶  SearXNG (Docker)
                                                          │
                                                          ▼
                                                   upstream search
                                                      engines

server.py is both the MCP server and the SearXNG client. It runs locally over the stdio transport, so your MCP client launches it as a child process.

Prerequisites

  • Docker with the Docker Compose plugin

  • Python 3.10+

  • git

  • An MCP-compatible client (Claude Desktop, Claude Code, OpenCode, etc.)

Setup

1. Clone the repository

git clone https://github.com/DenivalHeitz/aether.git
cd aether

2. Create the environment file

cp .env.example .env

Set SEARXNG_SECRET to a random value:

sed -i "s/change-me/$(openssl rand -hex 32)/" .env

Or edit .env by hand:

SEARXNG_SECRET=your-random-secret-here

3. Start SearXNG

docker compose up -d

Check that it is running:

docker compose ps

4. Verify the SearXNG JSON API

Aether requires SearXNG's JSON output format, which is already enabled in searxng/settings.yml.

curl "http://127.0.0.1:8080/search?q=test&format=json" | head -c 300

You should get back JSON containing a results array. If you get HTML or a 403, confirm the search.formats list in searxng/settings.yml includes json and restart the container:

docker compose restart

5. Install the Python dependencies

python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

On Windows, activate with .venv\Scripts\activate instead.

If you only need to run the server and don't need the MCP Inspector, pip install -e . is enough.

pyproject.toml pins the MCP SDK to the 1.x line (mcp>=1.30,<2), which is the version that provides the FastMCP 1.x API used by server.py. The dev extra adds the mcp CLI for mcp dev. Do not upgrade to mcp 2.x without updating the code.

6. Test the server (optional)

server.py uses stdio, so running it directly just waits for an MCP client. To poke at the tools interactively, use the MCP Inspector:

mcp dev server.py

7. Connect your MCP client

Point the client at the absolute path of the virtualenv's Python and server.py.

Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "aether": {
      "command": "/absolute/path/to/aether/.venv/bin/python",
      "args": ["/absolute/path/to/aether/server.py"]
    }
  }
}

Claude Code:

claude mcp add aether -- /absolute/path/to/aether/.venv/bin/python /absolute/path/to/aether/server.py

OpenCode (opencode.json):

{
  "mcp": {
    "servers": {
      "local-web-search": {
        "type": "local",
        "command": [
          "/absolute/path/to/aether/.venv/bin/python",
          "/absolute/path/to/aether/server.py"
        ],
        "environment": {
          "SEARXNG_URL": "http://127.0.0.1:8080"
        },
        "codemode": false // codemode not needed for this mcp tool
      }
    }
  }
}

Restart your client after saving the config.

Usage

Once connected, ask your client something that needs the live web, for example:

Search the web for the latest Python release notes.

The client will call web_search, then optionally call fetch_page on a promising result to read it in full.

Tools

Parameter

Type

Default

Description

query

string

—

Search terms. Empty queries are rejected.

limit

int

5

Number of results, clamped to the range 1–10.

Returns:

{
  "query": "latest python release",
  "count": 2,
  "results": [
    {
      "title": "Python 3.14 release notes",
      "url": "https://docs.python.org/3/whatsnew/3.14.html",
      "snippet": "...",
      "published_date": null
    }
  ]
}

fetch_page

Parameter

Type

Description

url

string

Public http/https webpage URL.

Returns url, title, text, truncated, and a warning noting that page content is untrusted. Failures return {"error": "..."}.

Configuration

Variable

Default

Used by

Description

SEARXNG_URL

http://127.0.0.1:8080

server.py

Base URL of the SearXNG instance. Set it if you changed the port.

SEARXNG_SECRET

—

Docker Compose

Secret key for SearXNG. Required; Compose fails without it.

If SearXNG runs elsewhere, pass SEARXNG_URL through your client's MCP config, for example:

{
  "mcpServers": {
    "aether": {
      "command": "/absolute/path/to/aether/.venv/bin/python",
      "args": ["/absolute/path/to/aether/server.py"],
      "env": { "SEARXNG_URL": "http://127.0.0.1:8080" }
    }
  }
}

Tunable limits live at the top of server.py (TIMEOUT, MAX_PAGE_BYTES, MAX_TEXT_CHARS, MAX_CONNECT_ADDRESSES).

Security notes

  • SearXNG is bound to 127.0.0.1 only and is not exposed to your network.

  • .env is git-ignored; only .env.example is committed.

  • fetch_page resolves the hostname once, rejects every non-public address, and then connects to that validated IP while preserving the original Host header and TLS SNI. This prevents DNS rebinding between the check and the request.

  • IPv6 addresses must be in global unicast space (2000::/3), which also blocks transition ranges such as NAT64 that embed an IPv4 address.

  • Redirects are not followed, so a public URL cannot bounce to a private one.

  • HTTP proxy environment variables are ignored (trust_env=False), so searches and fetches do not silently leave through a configured proxy.

  • Fetched page text is untrusted input and is returned with an explicit warning. Do not treat it as agent instructions.

Stopping and cleaning up

docker compose down       # stop SearXNG
docker compose down -v    # also remove Docker volumes

Troubleshooting

Symptom

Fix

set SEARXNG_SECRET in .env

Create .env from .env.example and set a value.

Search failed: HTTPStatusError

SearXNG is not reachable or returned an error. Check docker compose ps and docker compose logs searxng.

Search returns 403

JSON format is disabled. Ensure json is in search.formats in searxng/settings.yml, then restart the container.

Agent can't see the tools

The client is probably pointing at the wrong Python or server.py path. Use absolute paths and restart the client.

Fetch failed / Host is not public

The URL is unreachable, redirects, or resolves to a private address — all rejected by design.

Project structure

.
├── docker-compose.yml     # SearXNG service definition
├── pyproject.toml         # Project metadata and deps (MCP pinned to 1.x)
├── server.py              # MCP server with web_search and fetch_page
├── searxng/
│   └── settings.yml       # SearXNG config (enables JSON output)
├── .env.example           # Template for local secrets
└── README.md

License

MIT — see LICENSE.

Tool Schema Changelog

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

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables privacy-focused web search via SearXNG for MCP clients, allowing users to perform searches with customizable parameters through natural language.
      223 npm
      MIT
    • F
      license
      Not graded
      quality
      B
      maintenance
      A self-hosted MCP server providing private web search, web page fetching, and current date/time tools, powered by a bundled SearXNG instance for API-key-free local search.
      2
      -
    • A
      license
      Not graded
      quality
      D
      maintenance
      MCP server that provides web search scraping from DuckDuckGo (with Mojeek fallback) and URL content fetching as markdown/text or raw HTML.
      1
      ISC