Skip to main content
Glama
JedSearch MCP Server

JedSearch MCP Server

by Jedius

JedSearch MCP Server

MCP server that gives agents a web search tool and a web reader, with two search engines fused per query and a tiered page reader. Plain streamable-HTTP MCP, API-key protected.

Tools

Tool

What it does

Measured cost

search

Queries both engines in parallel, merges the rankings, returns title / URL / domain / query-focused snippet / age. No page downloads.

0.5–1.9 s, 4–6 KB

read

Readable markdown text of specific URLs, tiered (see below).

0.5–3 s per page, budgeted

search_read

search plus the full text of the top max_pages results in one call.

~5 s, budgeted

Split by cost on purpose: an agent that only needs a fact pays ~5 KB, not the 17–64 KB per call that the previous single web_search tool always returned.

Related MCP server: MCP MyZ Search

Search legs

Leg

Role

Notes

Yandex Cloud Search API v2

Russian intents, region=ru

Keyed. passages are query-focused excerpts, so snippets carry real answers rather than page titles.

SearXNG (self-hosted)

Global coverage, key-free

Engine list is measured on this host — google/duckduckgo/brave are blocked or challenged from here, google cse/bing/duckduckgo web/gmx/mwmbl answer.

Legs run concurrently and are merged with reciprocal-rank fusion (1/(60+rank), tie-break prefers Yandex). A result with "agree": 2 was ranked by both engines — the strongest signal available. Each leg reports {ok, ms, n} and, for SearXNG, engines_failed, so a degraded leg is visible to the caller instead of silently shrinking the result set.

Two Yandex API quirks are handled in yandex_client.py: FORMAT_JSON is rejected (only FORMAT_XML works, base64 in rawData), and sites[], pages[], maxpassages, window/timeInterval are accepted but silently ignored — hence site: in the query text and post-filtering by modtime for freshness.

Reader tiers

Per URL, cheapest first, stopping as soon as the text is usable:

  1. httpx with SSRF validation on every redirect hop + trafilatura extraction (markdown, keeps the page date).

  2. The Yandex cached copy of that page (yandexwebcache.net), learned from the search leg's saved-copy-url — gets past login walls, bot checks and geo-blocked origins.

  3. Playwright sidecar (ws://127.0.0.1:3000) for JS-only sites. Images, fonts, media and stylesheets are aborted; service workers blocked.

render: "always" | "never" forces tier 3 on or off. Every page reports reliable plus the reason it failed, so an unreadable page is not mistaken for a missing fact. Pages are cached (TTL 1 h), searches cached for 10 min.

SSRF

net_guard.py validates scheme, host and every resolved IP before any request, for both the HTTP client and the browser's initial navigation, and re-validates each redirect target. Blocked: RFC1918/loopback/link-local (including 169.254.169.254), CGNAT, IPv6 private ranges, IPv4-mapped v6, and single-label hostnames — which is what stops the reader from reaching a container service name (http://<service>:9000) from inside the docker network. SSRF_ALLOW_PRIVATE=true or SSRF_ALLOWED_HOSTS= relax it deliberately.

Layout

config.py         every environment knob in one place
models.py         tool argument models (also the JSON schema agents see)
server.py         MCP wiring: three tools, auth middleware, logging
yandex_client.py  Yandex v2 XML client
searxng_client.py SearXNG JSON client
fusion.py         URL normalisation, dedupe, reciprocal-rank fusion, budgets
reader.py         tiered page reader (httpx → Yandex copy → Playwright)
net_guard.py      SSRF validation + redirect-safe GET
page_cache.py     TTL/LRU caches + counters
entrypoint.sh     Playwright sidecar + server

Configuration

See .env.example. Everything is read in config.py; nothing is hardcoded. SEARXNG_SECRET is for SearXNG itself (openssl rand -hex 32), not for this server.

Running

Both legs — compose.yaml starts SearXNG and jedsearch on one private network:

cp .env.example .env    # set YANDEX_SEARCH_API_KEY, JEDSEARCH_API_KEY, SEARXNG_SECRET
docker compose up -d    # jedsearch on :3001, SearXNG internal (no published port)

Single container, Yandex leg only:

docker build -t jedsearch .
docker run -d --name jedsearch --env-file .env -p 3001:3001 jedsearch

SEARXNG_URL empty disables the second leg, and backend: "yandex" skips it per call. searxng/settings.yml is the engine list that was measured on one network — re-measure on yours: GET /config lists every engine, GET /search?q=x&format=json&engines=<name> shows whether one actually answers from your egress. The container runs as uid 977 and chowns /etc/searxng at startup, so expect that directory to come back owned by it.

Smoke test — 401 without the key, 200 with it:

curl -s -o /dev/null -w '%{http_code}\n' -X POST localhost:3001/mcp \
  -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
  -H "x-api-key: $JEDSEARCH_API_KEY" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"c","version":"1"}}}'

The container runs two processes: the Playwright browser server (port 3000, internal) and the MCP server (port 3001, /mcp). entrypoint.sh waits for the browser and, if it never comes up, logs a warning and starts anyway — tier 3 is lost, the rest keeps working. Healthcheck is a TCP probe on 3001.

mcp is pinned <2 because the agents' MCP client (and this code) target the 1.x FastMCP API; SDK 2.x renamed it to MCPServer.

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
      F
      maintenance
      Enables web search across Bing, DuckDuckGo, and Google using Playwright automation, with tools for retrieving search results and extracting page text content.
      12 npm
      MIT
    • A
      license
      Not graded
      quality
      F
      maintenance
      Enables Google search automation and web content extraction using Playwright. Performs Google searches and fetches main content from web pages, returning structured results in JSON format.
      42 npm
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Provides browser-grade web access by rendering pages to markdown, taking screenshots, and fetching images using headless Chromium/Firefox (Playwright) and httpx.
      MIT