Skip to main content
Glama

searxng-mcp

Built with Claude Code CI License: MIT npm

자체 호스팅 SearXNG 인스턴스를 통한 비공개 웹 검색용 MCP 서버입니다. 결과는 로컬 ML 모델로 재순위화되고, 전체 페이지 콘텐츠는 Firecrawl을 통해 가져오며, 선택적 Ollama 인스턴스는 쿼리 확장 및 LLM 합성 요약을 제공합니다.

Claude Code 및 LibreChat 에이전트와 함께 사용하도록 설계되었으며, 제3자 검색 API에 쿼리를 보내지 않고 웹 검색이 필요한 경우에 적합합니다.

Claude Codehomelab-agent의 멀티 에이전트 워크플로우로 구축되었습니다. homelab-agent는 AI 지원 연구를 위해 searxng-mcp를 프로덕션에서 사용하는 동일한 플랫폼입니다.

빠른 시작

실행 중인 SearXNG 인스턴스가 필요합니다. 캐시 백엔드를 강력히 권장합니다.

최소 스택 — Dragonfly/Valkey 캐시 백엔드를 시작하고 searxng-mcp를 실행합니다:

docker compose -f docker-compose.example.yml up -d
SEARXNG_URL=http://localhost:8081 CACHE_URL=redis://localhost:6381 npx @tadmstr/searxng-mcp

Firecrawl, Crawl4AI, Ollama, Kiwix, 광고 차단 프록시 및 NATS를 포함한 전체 로컬 토폴로지는 docker-compose.full.yml을 참조하세요.

Related MCP server: searxng-mcp-bridge

도구

도구

설명

주요 매개변수

search

로컬 재순위화로 SearXNG를 통해 검색합니다. 더 넓은 결과 풀을 가져와 관련성별로 재순위화하고 상위 N개를 반환합니다. SearXNG의 기본 직접 답변, 정보 상자, 맞춤법 수정 및 관련 제안은 목록 위와 structuredContent에 표시됩니다.

query, num_results (1–20), category, time_range, domain_profile, expand, language, engines, site

search_and_fetch

검색, 재순위화 후 가져오기 캐스케이드(Firecrawl → Crawl4AI → 원시 HTTP)를 사용하여 상위 결과의 전체 콘텐츠를 가져옵니다.

query, category, time_range, fetch_count (1–3), domain_profile, expand, language, engines, site

search_and_summarize

검색, 상위 결과 가져오기 후 Ollama(OLLAMA_SUMMARIZE_MODEL)를 통해 인용이 포함된 요약을 합성합니다. Ollama를 사용할 수 없으면 원시 가져온 콘텐츠로 대체됩니다.

query, fetch_count (1–5), category, time_range, domain_profile, expand, language, engines, site

fetch_url

공개 URL에서 읽을 수 있는 마크다운을 가져와 추출합니다. GitHub 호스트는 GitHub 빠른 경로를 사용하고, YouTube 비디오 URL은 트랜스크립트를 반환하며, Reddit 스레드 URL은 게시물+댓글을 반환합니다(둘 다 아래의 robots를 통해 옵트인). 그 외에는 가져오기 캐스케이드(Firecrawl → Crawl4AI → 원시 HTTP)를 사용합니다. 토큰 예산(기본 ~8,000자)으로 잘립니다.

url, domain_profile, max_tokens, target_selector, wait_for_selector

crawl_site

전체 사이트를 크롤링하고 각 페이지의 URL/제목/스니펫 매니페스트를 반환합니다. 먼저 Firecrawl 크롤링을 시도하고, 실패하면 사이트맵 파싱, 그 다음 선택적 BFS로 대체합니다. 전체 페이지 콘텐츠는 Valkey에 캐시되므로 후속 fetch_url 호출은 비용이 들지 않습니다.

url, max_pages (기본: CRAWL_MAX_PAGES_DEFAULT), bfs (bool, 옵트인 BFS)

clear_cache

검색 캐시, 가져오기 캐시, 크롤링 매니페스트 캐시 또는 전체를 제거합니다. 캐시된 결과가 오래되었을 수 있는 빠르게 변화하는 주제를 조사할 때 유용합니다.

target (search, fetch, crawl, all)

domain_stats

도메인 기능 데이터베이스의 읽기 전용 보기입니다. hostname 포함: 한 도메인의 계층별 성공률 및 기능 플래그. 미포함: 추적된 모든 도메인에 대한 집계(계층별 성공, 최악의 실패 도메인, 본 적 있지만 가져오지 않은 수). 프로그래밍 방식 임계값 처리를 위해 MCP 구조화 출력(structuredContent)을 반환합니다.

hostname (선택)

매개변수

categorygeneral (기본), news, it, science

time_rangeday, week, month, year — 게시 날짜별로 결과를 제한합니다. 전체 기간 결과는 생략합니다.

fetch_count — 전체 콘텐츠를 가져올 상위 재순위화 결과 수 (기본 1, search_and_fetch의 최대 3; 기본 3, search_and_summarize의 최대 5).

domain_profile — 명명된 도메인 필터 프로필 적용: homelab (자체 호스팅/Linux 문서 표시) 또는 dev (Stack Overflow, MDN, npm 표시). 기본 필터는 생략합니다.

expandtrue인 경우 검색 전에 Ollama(OLLAMA_EXPAND_MODEL)를 통해 쿼리를 다시 작성하여 재현율을 향상시킵니다. OLLAMA_URL이 필요합니다. 기본값은 EXPAND_QUERIES 환경 변수 값입니다.

language — BCP-47 언어 코드(예: en, de) 또는 all로 특정 언어로 제한합니다. 생략하면 SearXNG 인스턴스 기본값을 사용합니다. search, search_and_fetch, search_and_summarize에서 사용할 수 있습니다.

engines — 검색을 제한할 쉼표로 구분된 SearXNG 엔진 이름(예: google,duckduckgo). 그대로 전달됩니다. 알 수 없거나 비활성화된 엔진은 오류 대신 더 적은 결과로 저하됩니다. 세 가지 검색 도구 모두에서 사용할 수 있습니다.

site — 결과를 하나의 도메인 또는 목록(예: github.com 또는 ["github.com", "gitlab.com"])으로 제한합니다. site: 쿼리 연산자로 최선의 노력으로 적용됩니다. 대부분의 엔진(Google, Bing, DDG, Brave)은 이를 존중하지만 일부는 무시합니다. 세 가지 검색 도구 모두에서 사용할 수 있습니다.

max_tokens (fetch_url) — 반환된 콘텐츠의 대략적인 토큰 예산(문자 ≈ 토큰 × 4). 생략하면 기본 ~2,000토큰 / 8,000자; 최대 10,000토큰.

target_selector (fetch_url) — 특정 요소로 추출 범위를 지정하는 CSS 선택자(예: article, main .content). Firecrawl/Crawl4AI에서 기본적으로 지원되며 원시 HTTP 계층에서 클라이언트 측에 적용됩니다. 빠른 경로 및 일치하는 항목이 없으면 무시됩니다.

wait_for_selector (fetch_url) — JS 렌더링 페이지의 경우 추출 전에 대기할 CSS 선택자. 렌더링 계층(Firecrawl/Crawl4AI)에서 지원됩니다. 원시 HTTP(JS 없음)에서는 무시됩니다.

아키텍처

MCP client (stdio)
      │
      ▼
  searxng-mcp ──────────────→ cache ($CACHE_URL)           → result cache (search 1h, fetch 24h, crawl 6h)
      │
      ├── expand (optional) →  Ollama ($OLLAMA_URL)        → rewritten query (qwen3:4b)
      ├── search ───────────→ SearXNG ($SEARXNG_URL)      → raw results
      ├── rerank ───────────→ Reranker ($RERANKER_URL)    → ranked results
      │                       (fallback: SearXNG order if reranker unavailable)
      ├── fetch content ────┬→ GitHub API (github.com)    → markdown
      │                     ├→ Kiwix ($KIWIX_URL)         → ZIM content (Wikipedia/SO/Arch Wiki, fast path)
      │                     ├→ Hister ($HISTER_URL)       → browsing-history index (login-walled/JS-heavy fast path)
      │                     ├→ Firecrawl ($FIRECRAWL_URL) → page markdown (tier 1)
      │                     ├→ Crawl4AI ($CRAWL4AI_URL)  → page markdown (tier 2, optional; via $ADBLOCK_PROXY_URL if set)
      │                     ├→ Raw HTTP + Readability     → page markdown (tier 3 fallback; via $ADBLOCK_PROXY_URL if set)
      │                     └→ Wayback Machine (opt-in)  → archived page markdown (tier 4, $WAYBACK_ENABLED)
      ├── crawl_site ───────┬→ Firecrawl crawl           → page manifest (phase 1)
      │                     ├→ Sitemap parsing           → page manifest (phase 2 fallback, fast-xml-parser)
      │                     └→ BFS crawl (opt-in)        → page manifest (phase 3, $CRAWL_BFS_ENABLED)
      └── summarize (opt.) →  Ollama ($OLLAMA_URL)        → synthesized summary ($OLLAMA_SUMMARIZE_MODEL)
flowchart TD
    entry["fetchPage(url)"]
    cache{"Valkey cache hit?"}
    cached["→ return cached { title, url, text }"]
    github{"GitHub host?\ngithub.com · raw · api"}
    gh_fetch["GitHub API / raw.githubusercontent.com / api.github.com\n→ return"]
    llms{"llms.txt domain?"}
    llms_fetch["Probe /llms-full.txt\nextract matching section\n→ return"]
    kiwix{"Kiwix host?\nKIWIX_URL set"}
    kiwix_fetch["Local Kiwix ZIM\nWikipedia · Stack Overflow · Arch Wiki\n→ cache + return"]
    pdf{".pdf URL?"}
    robots["robots.txt pre-check — tiers 1–3\ndisallowed → RobotsDisallowedError (cached 24h)"]
    tier_skip(["Per-domain tier skip\nsuccess rate <30% over ≥10 tries\nor tier_skip operator override"])
    t1["Tier 1 — Firecrawl\n$FIRECRAWL_URL"]
    t2["Tier 2 — Crawl4AI\n$CRAWL4AI_URL · optional\nadblock proxy if $ADBLOCK_PROXY_URL"]
    t3["Tier 3 — Raw HTTP + Readability\nfallback: raw HTML slice\nadblock proxy if $ADBLOCK_PROXY_URL"]
    t4["Tier 4 — Wayback Machine CDX API\narchived snapshot · WAYBACK_ENABLED=true"]
    post["Post-extraction\nJSON-LD Article · title cascade\nog:title → twitter:title → title → h1 → URL"]
    result["→ return { title, url, text }"]

    entry --> cache
    cache -->|hit| cached
    cache -->|miss| github
    github -->|yes| gh_fetch
    github -->|no| llms
    llms -->|yes| llms_fetch
    llms -->|no| kiwix
    kiwix -->|yes| kiwix_fetch
    kiwix -->|no| pdf
    pdf -->|"yes — skip tier 1"| t2
    pdf -->|no| robots
    robots --> tier_skip
    tier_skip --> t1
    t1 -->|success| post
    t1 -->|"empty / error"| t2
    t2 -->|success| post
    t2 -->|"empty / error"| t3
    t3 -->|success| post
    t3 -->|"empty / error"| t4
    t4 -->|success| result
    post --> result

    style entry fill:#ffffff,stroke:#333333,color:#000000
    style cache fill:#ffffff,stroke:#333333,color:#000000
    style cached fill:#ffffff,stroke:#333333,color:#000000
    style github fill:#dae8fc,stroke:#6c8ebf,color:#000000
    style gh_fetch fill:#dae8fc,stroke:#6c8ebf,color:#000000
    style llms fill:#dae8fc,stroke:#6c8ebf,color:#000000
    style llms_fetch fill:#dae8fc,stroke:#6c8ebf,color:#000000
    style kiwix fill:#fff9c4,stroke:#b8860b,color:#000000
    style kiwix_fetch fill:#fff9c4,stroke:#b8860b,color:#000000
    style pdf fill:#ffffff,stroke:#333333,color:#000000
    style robots fill:#ffffff,stroke:#333333,color:#000000
    style tier_skip fill:#f5f5f5,stroke:#666666,color:#000000
    style t1 fill:#d5e8d4,stroke:#5a8a4a,color:#000000
    style t2 fill:#d5e8d4,stroke:#5a8a4a,color:#000000
    style t3 fill:#d5e8d4,stroke:#5a8a4a,color:#000000
    style t4 fill:#f8cecc,stroke:#a03030,color:#000000
    style post fill:#e1d5e7,stroke:#7a5a8a,color:#000000
    style result fill:#ffffff,stroke:#333333,color:#000000

SearXNG와 Firecrawl이 필요합니다. Crawl4AI, Valkey, Ollama, Kiwix 및 재순위화기는 선택 사항입니다. 서버는 이러한 항목 중 하나라도 사용할 수 없을 때 정상적으로 저하됩니다.

광고 차단

searxng-mcp는 가져오기 계층 그룹당 하나씩 두 개의 독립적인 광고 차단 사이드카를 사용합니다:

사이드카

계층

메커니즘

docker/puppeteer-adblock/

계층 1 (Firecrawl)

CDP 수준 차단 — 전체 HTTPS 필터링, 동일한 브라우저 프로세스

docker/adblock-proxy/

계층 2+3 (Crawl4AI, 원시 가져오기)

HTTP 전달 프록시 — 일반 HTTP 광고 도메인 필터링

계층 1 — Puppeteer 광고 차단

Firecrawl이 사용하는 firecrawl-puppeteer 서비스는 업스트림 trieve/puppeteer-service-ts 위에 @ghostery/adblocker-puppeteer를 계층화하는 사용자 지정 이미지(docker/puppeteer-adblock/)를 실행합니다. EasyList + EasyPrivacy는 시작 시 로드되고 168시간마다 새로 고쳐집니다. 차단기는 Firecrawl이 생성하는 모든 페이지에 적용됩니다. 광고가 많은 사이트의 가져오기 속도를 높이고 렌더링된 DOM 크기를 줄입니다.

환경 변수:

변수

기본값

설명

ADBLOCK_DISABLE

설정 안 됨

true로 설정하면 필터 로딩을 완전히 건너뜁니다.

ADBLOCK_FILTERS_URL

EasyList + EasyPrivacy

쉼표로 구분된 필터 목록 URL 목록.

ADBLOCK_REFRESH_HOURS

168

차단기가 구성된 URL에서 다시 빌드하는 주기.

기본 이미지는 SHA256 다이제스트로 고정됩니다. 변경 사항을 배포하려면 서비스를 다시 빌드하고 다시 시작합니다:

docker compose -f ~/docker/firecrawl-simple/docker-compose.yml up -d --build firecrawl-puppeteer

도메인별 우회: domains.json은 향후 운영자 재정의를 위해 adblock_skip 슬롯을 예약합니다. 배선은 아직 구현되지 않았습니다. Firecrawl이 사용자 지정 헤더를 puppeteer-service로 전달해야 하며, 이는 현재 API의 일부가 아닙니다. 범위 확장 항목 I로 추적됩니다.

계층 2+3 — 광고 차단 프록시

ADBLOCK_PROXY_URL(예: http://adblock-proxy:8118)을 설정하여 광고 및 추적기 요청을 필터링하는 HTTP 전달 프록시를 통해 Crawl4AI 및 원시 Node fetch 요청을 라우팅합니다. HTTPS CONNECT 터널은 수정 없이 통과됩니다. MITM이 없으므로 필터링은 일반 HTTP 광고 도메인에만 적용됩니다. 계층 1 puppeteer 후크는 해당 계층에 대한 전체 HTTPS 필터링을 이미 처리합니다. 프록시는 계층 2 및 3에서 누출되는 것을 처리합니다.

서비스 정의, 구성 옵션 및 배포 지침은 docker/adblock-proxy/를 참조하세요(docker-compose.full.yml에 포함).

데이터 기반 계층 라우팅

가져오기 캐스케이드를 호출하기 전에 searxng-mcp는 도메인의 tier_stats_30d( 도메인 기능 데이터베이스 참조)를 읽고 최소 10회 시도에서 성공률이 30% 미만인 계층을 건너뜁니다. 콜드 스타트 도메인(<10회 시도)은 기본 캐스케이드를 유지합니다. 각 건너뛰기는 reason: low_success_rate와 함께 searxng.fetch.tier.skipped NATS 이벤트를 내보내고 searxng_fetch_total{outcome=skipped}를 증가시킵니다.

운영자 재정의. 통계와 관계없이 계층을 강제로 건너뛰려면 domains.jsontier_skip 맵을 추가합니다:

{
  "tier_skip": {
    "example-bot-blocked.com": ["tier1"],
    "another-site.example": ["tier1", "tier2"]
  }
}

tier_skip 키는 단순 도메인(example.com은 도메인 및 모든 하위 도메인과 일치) 또는 도메인 + 경로 접두사(example.com/api/)일 수 있습니다. 파일은 핫 리로드됩니다. 재시작이 필요 없습니다. 수동 재정의는 reason: operator_override를 내보냅니다.

콘텐츠 유형 빠른 경로

구조화된 비-HTML 콘텐츠(URL) — application/json, 모든 *+json, XML, YAML, TOML, CSV 또는 text/plain — 는 HEAD 프로브를 통해 감지되어 전체 Firecrawl/Crawl4AI 캐스케이드 대신 원시 HTTP 계층으로 직접 라우팅됩니다. JSON은 펜스 처리된 코드 블록 안에 보기 좋게 출력되어 반환됩니다. 이전에는 헤드리스 브라우저가 JSON API 응답이나 CDN 자산을 렌더링하도록 요청하면 빈 마크다운이 반환되어 API 및 CDN 엔드포인트(registry.npmjs.org, api.osv.dev, cdn.jsdelivr.net, …)가 단순히 실패했습니다.

보장 사항:

  • 프로브는 실패 시 열림(fail-open) 입니다. 연결할 수 없는 호스트, HEAD를 거부하는 서버, 읽거나 구문 분석할 수 없는 Content-Type 헤더는 모두 변경 없이 일반 캐스케이드로 폴백됩니다.

  • application/xhtml+xml은 의도적으로 제외됩니다. 이는 브라우저용 마크업이지 구조화된 데이터가 아니기 때문입니다.

  • 서버가 text/plain으로 잘못 표시한 HTML은 여전히 HTML로 구문 분석되며 원시 텍스트 블록으로 덤프되지 않습니다.

도메인 기능 데이터베이스

모든 가져오기는 searxng-mcp가 대상 도메인에 대해 학습한 내용을 Valkey의 domain:<hostname> 아래에 기록합니다(90일 TTL, schema_version 5). 레코드별로 캡처되는 항목:

  • tier_stats_30d.{tier1,tier2,tier3,tier4,github}.{attempts, ok, fail, last_fail_reason, window_start_ms} — 30일 이동 창에 대한 계층별 가져오기 성공률. 컷오프는 읽기 시간에 적용되며 계층 라우팅 결정과 domain_stats 보고가 공유하므로 두 결과가 서로 다를 수 없습니다. 한 번 가져온 후 유휴 상태로 남아 있는 도메인은 다음 쓰기까지 남아 있는 오래된 숫자가 아니라 실제로 빈 창을 보고합니다. tier4(Wayback Machine) 슬롯은 WAYBACK_ENABLED=true일 때만 기록됩니다. github 슬롯은 계층 캐스케이드를 우회하지만 여전히 여기에서 추적되는 GitHub 빠른 경로(raw.githubusercontent.com / api.github.com / github.com README 가져오기)를 기록합니다. schema_version 범프는 기존 레코드를 새로 다시 빌드합니다. 현재 유휴 도메인에 대한 누적 창은 폐기됩니다(1→2, 2→3, 3→4, 4→5 범프에 걸쳐 선례가 있음).

  • capabilities.metadata_fetch.{attempts, ok, fail, last_fail_reason} — 메타데이터 부수 채널 가져오기(fetchRawHtmlForMetadata, JSON-LD/og:title 샘플링에 사용)의 성공/실패. "이 도메인에 전혀 연결할 수 있는가"에 대한 답이지 "전체 콘텐츠 전달이 성공했는가"가 아니므로 tier_stats_30d와 별도로 추적됩니다.

  • capabilities.seen_in_search.{count, last_seen_ms} — 도메인이 search 결과에 나타나는 빈도. 가져오기를 수행하지 않고 모든 반환 경로(캐시 히트 포함)에서 searxSearch()에 의해 파이어 앤 포겟(fire-and-forget)으로 기록되므로 도메인은 가져오기 전에 추적될 수 있습니다.

  • capabilities.robots_txt.{present, fetched, allows_us} — robots.txt 존재 여부 및 당사 허용 여부

  • capabilities.llms_full_txt.{present, size_bytes, last_checked} — 도메인이 /llms-full.txt를 제공하는지 여부

  • capabilities.json_ld_article.{sampled, present, last_sampled_at} — 페이지에 Article 스키마 JSON-LD가 전혀 있는지 여부(Schema.org Article/NewsArticle/BlogPosting/TechArticleScholarlyArticle/OpinionNewsArticle/LiveBlogPosting과 같은 하위 유형, 단순 이름 또는 정규화된 https://schema.org/... @type로 일치). 해당 스키마에 추출 가능한 본문 텍스트가 있는지 여부와는 무관합니다. 많은 사이트가 articleBody 없이 헤드라인/메타데이터 JSON-LD만 게시하며, 이는 추출 후 실제로 사용하는 것과는 별개의 문제입니다.

  • capabilities.og_title.{sampled, present, last_sampled_at}<meta property="og:title">에 대해서도 동일

  • preferred_strategy — 현재는 프로브가 존재할 때 llms_full_txt로 설정됩니다. 향후 단계에서는 이를 사용하여 계층 캐스케이드를 건너뜁니다.

번들 CLI로 레코드를 검사하거나 에이전트에서 domain_stats 도구(단일 도메인 또는 집계, 도구 참조)를 통해 쿼리합니다:

pnpm dump-domain docs.anthropic.com

dump-domain은 만료된 창과 데이터가 전혀 없는 계층을 구별하여 둘 다 동일하게 표시하지 않습니다.

동일한 호스트 이름에 대한 동시 업데이트(한 번의 가져오기 중에 병렬로 실행되는 계층 시도, robots 프로브, 추출 후 샘플 레코더)는 서버 측 Lua 비교-설정(CAS)을 통해 직렬화되며, 단일 프로세스의 자체 작성자 간의 경합을 제거하는 프로세스 내 키별 대기열과 쌍을 이루므로 CAS는 프로세스 간의 실제 동시 쓰기만 중재하면 됩니다. v3.17.0 이전 버전은 공유 연결에 대해 WATCH/MULTI/EXEC 읽기-수정-쓰기를 사용했지만 실제로 동시 작성자를 직렬화하지 않으므로 v3.17.0 이전에 수집된 데이터는 결과적으로 상당히 불완전했습니다. 업그레이드하면 스키마 범프를 통해 기존 계층 통계가 폐기됩니다. 업그레이드 직후 domain_stats가 거의 비어 있는 것으로 읽히고 며칠에 걸쳐 다시 채워질 것으로 예상하세요.

도메인 DB 영속성

도메인 DB는 90일 TTL과 30일 이동 창 아래 Valkey에만 존재하므로 캐시 플러시 또는 TTL 만료로 인해 다시 획득하는 데 비용이 많이 드는 기능 학습이 지워집니다. 두 개의 CLI가 이를 영구적으로 만듭니다:

pnpm domain-db-maintenance   # SCAN all domain:* records → write a dated JSON snapshot (+ prune) and emit OTel gauges
pnpm restore-domain-db       # re-seed the domain-db from the newest snapshot after a flush
  • domain-db-maintenance 는 독립 실행형 작업입니다(크론 또는 PM2 크론 재시작을 통해 일정에 따라 실행 — searxng-mcp가 각각 실행하는 여러 동시 에이전트별 stdio 자식 프로세스로 실행되므로 프로세스 내 타이머가 아님). 하나의 제한된 SCAN이 두 출력을 모두 제공합니다: 내구성 있는 날짜 스냅샷과 OTEL_EXPORTER_OTLP_ENDPOINT가 설정된 경우 종료 전에 강제 플러시되는 게이지(searxng_domains_tracked, searxng_domains_failing, searxng_domain_tier_success_ratio{tier})입니다.

  • restore-domain-db 는 누락되었거나 라이브 레코드가 스냅샷보다 엄격히 오래된 키만 다시 시드합니다(last_fetch 비교). 더 새롭거나 동일한 라이브 레코드를 덮어쓰지 않으므로 라이브, 부분적으로 채워진 Valkey(예: 자동 플러시 복구를 위한 서비스 부팅 시퀀스)에 대해 실행해도 안전합니다.

환경 변수

기본값

용도

DOMAIN_DB_SNAPSHOT_DIR

./domain-db-snapshots

날짜 스냅샷이 기록/읽히는 위치. 배포 시 내구성 있는 경로(앱데이터 또는 NFS 마운트)로 설정합니다.

DOMAIN_DB_SNAPSHOT_RETENTION

14

유지할 스냅샷 수. 오래된 것은 각 유지 관리 실행 시 정리됩니다.

llms.txt 빠른 경로

domains.json(llms_txt 배열)의 허용 목록 문서 도메인의 경우 fetchPage는 계층을 호출하기 전에 먼저 <origin>/llms-full.txt를 시도하고 요청된 URL과 일치하는 섹션을 추출합니다. 이렇게 하면 잘 계측된 문서 사이트에 대해 puppeteer를 실행하지 않아도 되고 깨끗한 마크다운 섹션을 직접 반환합니다. 프로브 결과와 전체 본문은 Valkey(llms:<origin>:full, 존재/부재에 대해 24시간/7일)에 캐시됩니다. 기본 허용 목록: docs.anthropic.com, docs.openai.com, docs.stripe.com, docs.crawl4ai.com, docs.firecrawl.dev, docs.cursor.com. domains.json을 편집하여 확장하세요. 파일은 핫 리로드됩니다.

Kiwix 빠른 경로

KIWIX_URL이 설정되면 알려진 오프라인 지원 호스트에 대한 가져오기 요청은 Firecrawl/Crawl4AI 캐스케이드 전에 가로채어 로컬 Kiwix ZIM 아카이브에서 제공됩니다. 이렇게 하면 Wikipedia(헤드리스 스크레이퍼를 차단하는 사이트)와 같은 사이트의 100% 계층 1 실패율이 제거되고 외부 네트워크 트래픽 없이 깨끗하고 읽을 수 있는 콘텐츠가 반환됩니다.

지원되는 호스트 및 ZIM 책(kiwix-serve는 --nodatealiases / -z로 실행해야 함):

호스트

ZIM 책

en.wikipedia.org, wikipedia.org

wikipedia_en_all_mini

stackoverflow.com

stackoverflow.com_en_all

wiki.archlinux.org

archlinux_en_all_maxi

Kiwix 경로는 llms-txt 빠른 경로 이후, robots 게이트 이전에 실행됩니다. Kiwix 요청이 실패하거나 빈 결과를 반환하면 전체 계층 캐스케이드가 정상적으로 실행됩니다. KIWIX_URL이 설정되지 않으면 기능은 오버헤드를 추가하지 않습니다. isKiwixHost()는 즉시 false를 반환합니다.

KIWIX_URL을 kiwix-serve 기본 URL(예: http://localhost:8292)로 설정하세요.

YouTube 및 Reddit 빠른 경로

fetch_url은 YouTube 비디오 URL(youtube.com, youtu.be) 및 Reddit 스레드 URL을 인식하고 렌더링된 페이지를 스크래핑하는 대신 직접 제공할 수 있습니다:

  • YouTube — 시청 페이지에서 비디오의 자막 트랙을 추출하고 대본을 반환합니다. YOUTUBE_TRANSCRIPT_ENABLED(기본값 켜짐)로 활성화됩니다.

  • Reddit — 공개 .json 보기를 가져와 표준 {title, url, text} 형태로 게시물과 상위 댓글을 반환합니다. HTTP 429에서 폴백됩니다. REDDIT_FASTPATH_ENABLED(기본값 켜짐)로 활성화됩니다.

둘 다 비공식, 문서화되지 않은 엔드포인트(YouTube의 timedtext API, Reddit의 .json)에 의존합니다. SLA가 없는 최선의 노력이며, 업스트림 변경으로 인해 둘 중 하나가 중단될 수 있으므로 킬 스위치가 있습니다. 누락 시 요청은 일반 계층 캐스케이스로 폴백됩니다(여전히 YouTube 페이지의 제목/설명을 얻을 수 있음).

robots.txt: 두 엔드포인트 모두 사이트의 robots.txt에 의해 허용되지 않습니다(Reddit은 모든 것을 허용하지 않음, YouTube는 대본이 있는 /api/를 허용하지 않음). 기본적으로 이러한 빠른 경로는 이를 존중하고 휴면 상태를 유지하여 캐스케이드로 폴백됩니다. 자체 인스턴스에서 YOUTUBE_IGNORE_ROBOTS=true / REDDIT_IGNORE_ROBOTS=true로 직접 가져오기를 선택할 수 있습니다.

사이트 크롤링

crawl_site는 전체 사이트를 크롤링하고 발견된 각 페이지에 대한 URL/제목/스니펫 매니페스트를 반환합니다. 3단계 전략 캐스케이드를 사용합니다:

  1. Firecrawl 크롤 — Firecrawl(/crawl 엔드포인트)에 크롤 작업을 보내고 완료될 때까지 폴링한 다음 전체 페이지 목록을 반환합니다. FIRECRAWL_CRAWL_POLL_INTERVAL_MSFIRECRAWL_CRAWL_MAX_WAIT_MS로 제어됩니다.

  2. 사이트맵 구문 분석 — Firecrawl이 실패하거나 빈 결과를 반환하면 /sitemap.xml(및 연결된 사이트맵)을 가져와 제목/스니펫이 있는 URL을 추출합니다. 사이트맵 XML 구문 분석에는 fast-xml-parser를 사용합니다.

  3. BFS 크롤(옵트인) — 사이트맵 구문 분석도 실패하면 주어진 URL에서 시작하여 CRAWL_BFS_MAX_DEPTH 링크 홉까지 너비 우선 크롤을 수행합니다. CRAWL_BFS_ENABLED=true 또는 bfs 도구 매개변수가 true일 때만 실행됩니다.

크롤 중에 가져온 전체 페이지 콘텐츠는 Valkey에 캐시됩니다(TTL: CRAWL_MANIFEST_TTL_SECONDS, 기본 6시간). 매니페스트의 모든 URL에 대한 후속 fetch_url 호출은 캐시에서 즉시 반환됩니다. 후속 읽기에 대한 가져오기 오버헤드가 0입니다.

매니페스트 캐시는 clear_cache(target="crawl")로 지울 수 있습니다.

Wayback Machine 폴백

WAYBACK_ENABLED=true이면 세 가지 주요 계층이 모두 실패할 때 네 번째 계층이 Wayback Machine CDX API에서 보관된 스냅샷을 쿼리합니다. 반환된 콘텐츠에는 출처 헤더([Archived snapshot – <timestamp> – <original_url>])가 접두사로 붙어 호출자가 콘텐츠가 현재 페이지 상태를 반영하지 않을 수 있음을 알 수 있습니다.

가져오기 품질

모든 계층이 원시 HTML이 포함된 콘텐츠를 반환한 후 추출 후 패스가 제목과 본문 품질을 개선합니다:

  • JSON-LD Article 추출 — Schema.org Article / NewsArticle / BlogPosting / TechArticle 블록은 계층 1 크롬 스크래핑보다 더 깨끗한 headlinearticleBody를 제공합니다(스크립트 태그당 1MB 크기 제한).

  • 제목 캐스케이드og:titletwitter:title<title>(게시자 접미사 제거 포함) → 첫 번째 <h1> → URL 순으로 폴백됩니다.

  • 계층 2 Readability 비교 — Crawl4AI가 마크다운을 반환하면 JSDOM+Readability도 원시 HTML에 대해 실행되며 텍스트가 더 길 때(또는 Crawl4AI가 500자 미만을 반환할 때 무조건) 선호됩니다.

복원력

  • 캐시는 검색을 절대 중단시키지 않는다. Valkey 클라이언트는 CACHE_COMMAND_TIMEOUT_MS/CACHE_CONNECT_TIMEOUT_MS/CACHE_MAX_RETRIES_PER_REQUEST에 의해 제한된다(Configuration 참조). 중단되거나 CPU가 급증한 캐시 백엔드는 이제 영원히 대기하는 대신 명령을 거부한다 — 기존의 fail-soft 처리는 그 거부를 예외를 던지는 대신 캐시 미스(라이브 서빙)로 격하시킨다. 캐시 연결 실패, 클라이언트 오류, 명령별 오류는 스로틀링된 [searxng-mcp] stderr 라인을 출력한다(키별로 중복 제거되어 지속적인 장애 시 홍수가 아닌 주기적인 단서를 남긴다) — stderr는 배포된 PM2 프로세스에 연결된 유일한 텔레메트리 싱크다.

  • 프로세스 크래시 핸들러uncaughtException은 로그를 남긴 후 exit 1로 종료한다(깔끔한 PM2 재시작); unhandledRejection은 공유 프로세스를 조용히 크래시시키는 대신 로그를 남기고 계속 실행한다.

  • 점진적 성능 저하 경고 — reranker 폴백과 Ollama/LLM 확장 + 요약 폴백은 품질을 조용히 저하시킬 때(reranker 사용 불가, LLM 백엔드 연결 불가) 각각 스로틀링된 stderr 라인을 하나씩 출력한다.

  • 버전은 단일 소스로 런타임 시 package.json에서 가져온다(src/version.ts) — McpServer 버전, OTel tracer/meter 버전, 아웃바운드 USER_AGENT가 모두 이를 추적하므로 서로 독립적으로 어긋날 수 없다.

관측성(옵트인)

트레이싱, 메트릭, 이벤트 발행은 전적으로 옵트인이다 — 아래 환경 변수가 하나도 설정되지 않으면 서버는 관측성 오버헤드가 전혀 없으며 런타임에 OpenTelemetry 또는 NATS 패키지를 로드하지 않는다.

OpenTelemetry(트레이스 + 메트릭)OTEL_EXPORTER_OTLP_ENDPOINT를 수집기의 HTTP 엔드포인트로 설정하면 서버가 다음을 내보낸다:

  • 스팬(요청별): tool.<name>expand_query? → searxng_requestrerankfetch (×N) → tier1_firecrawl | tier2_crawl4ai | tier3_rawfetchpost_extract; search_and_summarize의 경우 summarize_llm 추가.

  • 카운터: searxng_search_total{profile, expand}, searxng_fetch_total{tier, outcome}, searxng_cache_total{namespace, outcome}, searxng_errors_total{stage, error_type}.

  • 히스토그램: searxng_search_duration_seconds{profile}, searxng_fetch_duration_seconds{tier, outcome}.

표준 OTEL 환경 변수가 적용된다(OTEL_SERVICE_NAME은 기본값 searxng-mcp).

NATS 이벤트NATS_URL(예: nats://localhost:4222)을 설정하면 서버는 모든 검색, 페치, 캐시 히트/미스, robots 스킵, 오류 시 구조화된 이벤트를 발행한다. NATS_CREDS(JWT creds 파일) 또는 NATS_USER/NATS_PASSWORD(bcrypt 사용자 이름/비밀번호)로 인증한다 — 둘 다 설정된 경우 creds 파일 인증이 우선한다. 주제:

주제

발행 시점

searxng.search.requested

검색 도구 호출 시

searxng.search.completed

검색 반환 시(소스, 지연 시간, rerank 적용 여부 포함)

searxng.fetch.requested

fetchPage 호출 시

searxng.fetch.tier.miss

티어가 빈 결과를 반환하거나 예외를 던진 경우

searxng.fetch.tier.skipped

robots.txt가 금지한 경우

searxng.fetch.completed

페치 해결 시(tier_served, text_len, 지연 시간 포함)

searxng.cache.hit / .miss

모든 Valkey 조회 시

searxng.error

단계 태그가 지정된 오류

각 봉투에는 request_id와(OTel이 활성화된 경우) trace_id가 포함되어 구독자가 두 스트림을 연결할 수 있다. 주제 접두사는 NATS_SUBJECT_PREFIX로 재정의할 수 있다. 검색 쿼리는 search.* 이벤트를 통해 흐른다 — 다운스트림 소비자가 PII 정리를 책임진다.

예의

  • 정직한 User-Agent — 아웃바운드 요청은 searxng-mcp/<version> (+https://github.com/TadMSTR/searxng-mcp; personal research)로 식별된다.

  • robots.txt 준수/robots.txt는 오리진당 한 번 가져와서 Valkey의 robots:<origin> 아래에 24시간 동안 캐시된다. 금지된 경로는 티어가 실행되기 전에 건너뛰고 skipped_robots url=… reason=…로 로그에 기록된다.

전송

stdio(기본값) — Claude Code MCP 플러그인 및 LibreChat stdio 구성과 호환된다.

HTTPSEARXNG_MCP_TRANSPORT=http로 설정하면 다중 클라이언트 배포 또는 Docker 기반 설정에 적합한 공유 HTTP/SSE 서버로 실행된다. SEARXNG_MCP_HOST:SEARXNG_MCP_PORT(기본값 127.0.0.1:3001)에 바인딩된다:

SEARXNG_MCP_TRANSPORT=http SEARXNG_MCP_PORT=3001 npx @tadmstr/searxng-mcp

HTTP 서버에 대해 Claude Code에 등록:

claude mcp add-json searxng --scope user '{
  "type": "http",
  "url": "http://localhost:3001/mcp"
}'

세션은 Mcp-Session-Id 헤더로 키가 지정되므로 여러 클라이언트가 동시에 동일한 공유 프로세스에 연결할 수 있다. 유휴 세션은 HTTP_SESSION_IDLE_TIMEOUT_MS 이후 정리되고 HTTP_MAX_SESSIONS에서 하드 상한이 적용된다 — Configuration 참조.

HTTP 전송 인증

HTTP 전송은 기본적으로 인증되지 않는다, 이는 기본적으로 127.0.0.1에 바인딩되기 때문에만 안전하다. SEARXNG_MCP_HOST를 다른 값으로 변경하는 경우 — 컨테이너에서 실행할 때 필요한 0.0.0.0을 포함 — SEARXNG_MCP_AUTH_TOKEN도 설정해야 한다:

SEARXNG_MCP_AUTH_TOKEN=$(openssl rand -hex 32)

설정된 경우 GET /health를 제외한 모든 요청은 RFC 6750 베어러 자격 증명으로 토큰을 전달해야 한다:

Authorization: Bearer <token>

그 외의 모든 경우 — 헤더 없음, 다른 스킴, 잘못된 토큰 — WWW-Authenticate: Bearer와 JSON-RPC 오류 본문과 함께 401을 받는다. 응답은 세 경우 모두 동일하며 제시된 자격 증명을 절대 에코하지 않는다. 토큰은 SHA-256 다이제스트로 비교되므로 비교는 상수 시간이며 길이 정보를 누출하지 않는다.

인증된 서버를 Claude Code에 등록:

claude mcp add-json searxng --scope user '{
  "type": "http",
  "url": "http://localhost:3001/mcp",
  "headers": {"Authorization": "Bearer <token>"}
}'

변수를 설정하지 않으면 이전 동작이 정확히 유지되므로 stdio 사용자와 기존 루프백 바인딩 HTTP 배포는 변경이 필요 없다. 호출자별 권한 부여 모델은 없다 — 단일 토큰은 특정 클라이언트 ID가 아닌 서버에 대한 접근을 인증한다. 시작 시 루프백이 아닌 바인딩에 토큰이 없으면 경고가 로그에 기록된다.

GET /health는 의도적으로 검사에서 제외된다. 컨테이너 헬스체크이자 모니터링 활성 프로브이며, 입력을 받지 않고, 응답(status, cache, sessions)에 비밀이 포함되지 않는다.

GET /health — 인증되지 않은 활성 프로브로, MCP 엔드포인트와 함께 localhost에 바인딩된다. 제한된 캐시 명령 시간 초과를 통해 Valkey를 핑하므로(검사 자체가 절대 중단될 수 없다) 다음을 반환한다:

{"status": "ok", "cache": "up", "sessions": 3}

또는 캐시 백엔드에 연결할 수 없는 경우:

{"status": "degraded", "cache": "degraded", "sessions": 3}

sessions는 실시간 HTTP 세션 수다. 캐시 백엔드를 직접 계측하지 않고 MCP 측에서 저하된 캐시를 감지하는 데 유용한 시스템 관리자 모니터링 용도다.

사전 요구 사항

  • Node.js 20+

  • pnpm(또는 npm)

  • 실행 중인 SearXNG 인스턴스

  • 실행 중인 Firecrawl 인스턴스

  • Jina 호환 /v1/rerank 엔드포인트를 노출하는 실행 중인 reranker(선택 사항)

  • 실행 중인 Valkey 또는 Redis 호환 인스턴스(선택 사항, 결과 캐싱용)

  • qwen3:4b 및/또는 qwen3:14b 모델이 pull된 실행 중인 Ollama 인스턴스(선택 사항, 쿼리 확장 및 요약용)

SearXNG

SearXNG는 JSON 출력 형식이 활성화되어 있어야 한다. settings.yml에서:

search:
  formats:
    - html
    - json

Reranker

reranker는 Jina 호환 /v1/rerank 엔드포인트를 노출해야 한다. 가벼운 FlashRank 래퍼가 잘 작동한다 — homelab-agentdocker/reranker/ 참조를 확인하라.

Firecrawl

Firecrawl 호환 인스턴스는 모두 작동한다. 로컬 firecrawl-simple 배포로 충분하다. 인스턴스가 인증을 요구하는 경우 FIRECRAWL_API_KEY를 설정하라(인증을 건너뛰는 로컬 배포의 경우 기본값은 placeholder-local).

Crawl4AI

Crawl4AI는 Firecrawl이 빈 콘텐츠를 반환할 때(봇 차단 페이지, JS 중심 사이트) 사용되는 선택적 2차 티어 페치 폴백이다. 활성화하려면 CRAWL4AI_URL을 설정하라. 설정하지 않으면 캐스케이드는 원시 HTTP 페치로 건너뛴다.

docker run -d -p 11235:11235 unclecode/crawl4ai:0.8.6

인스턴스가 API 토큰 인증을 요구하는 경우 CRAWL4AI_API_TOKEN을 설정하라.

search_and_summarize 경로에서 Crawl4AI 요청은 노이즈 필터링된 콘텐츠 추출을 위해 fit_markdown을 사용한다. 다른 호출자(search_and_fetch, fetch_url)는 raw_markdown을 사용한다.

Kiwix(선택 사항)

kiwix-serve는 HTTP를 통해 ZIM 아카이브를 제공한다. 필요한 ZIM 파일을 다운로드하고 책 이름이 안정적으로 유지되도록 --nodatealiases(-z) 플래그로 kiwix-serve를 실행하라:

kiwix-serve --port 8292 --nodatealiases /path/to/zims/

지원되는 각 호스트에 필요한 ZIM 파일:

  • Wikipedia: wikipedia_en_all_mini(또는 maxi)

  • Stack Overflow: stackoverflow.com_en_all

  • Arch Wiki: archlinux_en_all_maxi

ZIM 파일은 library.kiwix.org에서 다운로드할 수 있다.

Hister(선택 사항)

Hister는 Firefox 확장 프로그램이 채우는 브라우징 기록 인덱스다. HISTER_URL이 설정되면 fetchPage는 티어 캐스케이드를 호출하기 전에 기록 인덱스를 확인한다 — 스크레이퍼가 실패하는 로그인 벽 및 JS 중심 페이지에 유용하다.

HISTER_URL을 Hister 인스턴스 기본 URL로 설정하고 베어러 토큰 인증이 필요한 경우 HISTER_TOKEN을 설정하라.

Valkey / Redis

Redis 호환 인스턴스는 모두 작동한다. Valkey가 권장된다. 검색 결과는 1시간 동안 캐시되고, 페치된 페이지는 24시간 동안 캐시된다. 사용할 수 없으면 서버는 캐싱 없이 작동한다.

Ollama

expandsearch_and_summarize에 필요하다. 필요한 모델을 pull하라:

ollama pull qwen3:4b   # query expansion
ollama pull qwen3:14b  # summarization

think: false 동작은 자동으로 처리된다 — 추가 Ollama 구성이 필요 없다.

구성

모든 서비스 URL은 환경 변수로 구성할 수 있다.

Variable

Default

Description

SEARXNG_URL

http://localhost:8081

SearXNG 인스턴스 URL

FIRECRAWL_URL

http://localhost:3002

Firecrawl 인스턴스 URL

RERANKER_URL

http://localhost:8787

Reranker 인스턴스 URL

FIRECRAWL_API_KEY

placeholder-local

Firecrawl API 키(필요한 경우)

GITHUB_TOKEN

(설정 안 됨)

GitHub 개인 액세스 토큰 — 요청 한도를 시간당 60회에서 5,000회로 증가

OLLAMA_URL

(설정 안 됨)

Ollama API 기본 URL — expandsearch_and_summarize에 필요

OLLAMA_API_KEY

(설정 안 됨)

인증된 Ollama 프록시용 Bearer 토큰 — 설정 시 Authorization: Bearer <key> 헤더 추가

OLLAMA_EXPAND_MODEL

qwen3:4b

쿼리 확장(expand 매개변수)에 사용되는 모델. 재빌드 없이 재정의 가능.

OLLAMA_SUMMARIZE_MODEL

qwen3:14b

search_and_summarize에 사용되는 모델. 재빌드 없이 재정의 가능.

LLM_BASE_URL

(설정 안 됨)

expand + search_and_summarize용 OpenAI 호환 채팅 엔드포인트(예: vLLM, llama.cpp, LM Studio). API 경로를 포함해야 함 — 예: http://host:8000/v1 — 서버가 /chat/completions를 추가함. 설정 시 OLLAMA_URL보다 우선하므로 별도의 Ollama 모델을 실행하는 대신 이미 로드된 모델을 재사용할 수 있음.

LLM_MODEL

(설정 안 됨)

OpenAI 호환 백엔드용 모델 ID; 설정 시 OLLAMA_EXPAND_MODEL / OLLAMA_SUMMARIZE_MODEL을 재정의함.

LLM_API_KEY

(설정 안 됨)

OpenAI 호환 백엔드용 Bearer 토큰 — 설정 시 Authorization: Bearer <key> 추가.

LLM_DISABLE_THINKING

true

추론 모델(예: Qwen3)이 직접 출력을 반환하도록 chat_template_kwargs.enable_thinking: false를 전송. 해당 필드를 거부하는 서버에서는 false로 설정.

CACHE_URL

redis://localhost:6381

Redis 호환 URL — 결과 캐싱 활성화. VALKEY_URL 또는 REDIS_URL 별칭도 허용. Redis, Valkey, Dragonfly와 호환. 사용 불가 시 서버는 정상적으로 성능을 저하시킴.

CACHE_COMMAND_TIMEOUT_MS

2500

Valkey 명령 타임아웃 — 중단되거나 CPU가 급증한 캐시 백엔드는 멈추는 대신 거부함(cacheGet()은 모든 검색의 첫 번째 await). 잘못되었거나 양수가 아닌 값은 타임아웃을 비활성화하는 NaN이 되는 대신 기본값으로 대체됨.

CACHE_CONNECT_TIMEOUT_MS

3000

Valkey 연결 타임아웃. CACHE_COMMAND_TIMEOUT_MS와 동일한 대체 동작.

CACHE_MAX_RETRIES_PER_REQUEST

2

거부 전 Valkey 명령당 최대 재시도 횟수. CACHE_COMMAND_TIMEOUT_MS와 동일한 대체 동작.

CACHE_TTL_SECONDS

3600

검색 결과 캐시 TTL(초)

FETCH_CACHE_TTL_SECONDS

86400

가져온 페이지 캐시 TTL(초)

CRAWL_MANIFEST_TTL_SECONDS

21600

크롤 매니페스트 및 페이지 콘텐츠 캐시 TTL(초, 6시간)

CRAWL_MAX_PAGES_DEFAULT

20

max_pages가 전달되지 않을 때 crawl_site가 반환하는 기본 최대 페이지 수

CRAWL_BFS_ENABLED

false

crawl_site에서 BFS 대체를 전역적으로 활성화하려면 true로 설정. bfs 매개변수로 호출별로도 활성화 가능.

CRAWL_BFS_MAX_DEPTH

3

BFS 크롤의 최대 링크 홉 깊이

FIRECRAWL_CRAWL_POLL_INTERVAL_MS

2000

Firecrawl 크롤 작업 완료 대기 시 폴링 간격

FIRECRAWL_CRAWL_MAX_WAIT_MS

120000

사이트맵으로 대체하기 전 Firecrawl 크롤 작업 대기 최대 시간

EXPAND_QUERIES

false

쿼리 확장을 전역적으로 활성화하려면 true로 설정

CRAWL4AI_URL

(설정 안 됨)

Crawl4AI 인스턴스 URL — Firecrawl 실패 시 2차 가져오기 대체 활성화

CRAWL4AI_API_TOKEN

(설정 안 됨)

API 토큰 보호가 있는 Crawl4AI 인스턴스용 선택적 Bearer 토큰

WAYBACK_ENABLED

false

Wayback Machine 4차 대체를 활성화하려면 true로 설정 — 세 계층이 모두 실패하면 보관된 스냅샷을 가져옴

ADBLOCK_PROXY_URL

(설정 안 됨)

2차(Crawl4AI) 및 3차(원시 Node fetch) 광고 차단용 HTTP 프록시 URL — 예: http://adblock-proxy:8118. docker/adblock-proxy/ 참조.

KIWIX_URL

(설정 안 됨)

kiwix-serve 기본 URL(예: http://localhost:8292) — Wikipedia, Stack Overflow, Arch Wiki용 Kiwix 빠른 경로 활성화. 설정하지 않으면 기능이 비활성화되고 오버헤드가 없음.

HISTER_URL

(설정 안 됨)

Hister 브라우징 기록 인덱스 기본 URL — 로그인 장벽 및 JS 과다 페이지에 대해 계층 캐스케이드 전에 Hister 빠른 경로 활성화. 설정하지 않으면 기능이 비활성화되고 오버헤드가 없음.

HISTER_TOKEN

(설정 안 됨)

Hister API 인증용 Bearer 토큰. HISTER_URL이 설정되고 인스턴스에 토큰 인증이 활성화된 경우 필요.

YOUTUBE_TRANSCRIPT_ENABLED

true

fetch_url에서 YouTube 자막 빠른 경로 활성화. 비활성화하려면 false로 설정(예: 비공식 timedtext 엔드포인트가 상류에서 중단된 경우).

YOUTUBE_IGNORE_ROBOTS

false

YouTube의 robots.txt/api/를 금지함에도 YouTube 자막 가져오기를 선택. 기본값은 robots를 존중함(빠른 경로는 비활성 상태로 유지되고 캐스케이드로 넘어감).

REDDIT_FASTPATH_ENABLED

true

fetch_url에서 Reddit .json 빠른 경로 활성화. 비활성화하려면 false로 설정.

REDDIT_IGNORE_ROBOTS

false

Reddit의 robots.txt(Disallow: /)에도 불구하고 Reddit .json 가져오기를 선택. 기본값은 robots를 존중함(빠른 경로는 비활성 상태로 유지되고 캐스케이드로 넘어감).

SEARXNG_MCP_TRANSPORT

stdio

전송 모드: stdio(기본값, 단일 클라이언트) 또는 http(공유 HTTP/SSE 서버).

SEARXNG_MCP_PORT

3001

HTTP 수신 포트(HTTP 전송 모드 전용).

SEARXNG_MCP_HOST

127.0.0.1

HTTP 수신 주소(HTTP 전송 모드 전용).

SEARXNG_MCP_AUTH_TOKEN

(설정 안 됨)

HTTP 전송 전용. 설정 시 GET /health를 제외한 모든 요청은 Authorization: Bearer <token>을 보내야 하며 그렇지 않으면 401을 받음. 설정하지 않음(기본값)은 검사를 완전히 비활성화함. SEARXNG_MCP_HOST가 루프백이 아닐 때는 반드시 설정HTTP 전송 인증 참조.

HTTP_SESSION_IDLE_TIMEOUT_MS

600000

HTTP 전송 전용. 이 시간보다 오래 유휴 상태인 세션은 백그라운드 정리로 제거됨(진행 중인 요청이 있는 세션은 제외되므로 긴 crawl_site 호출이 요청 중간에 종료되지 않음). transport.onclose를 발생시키지 않는 중간에 종료된 클라이언트로 인한 세션 맵 증가를 제한함.

HTTP_MAX_SESSIONS

256

HTTP 전송 전용. 하드 상한 백스톱 — 세션 맵이 이 값을 초과하면 유휴 타임아웃과 관계없이 가장 오래 사용되지 않은 유휴 세션이 제거됨.

NATS_USER

(설정 안 됨)

bcrypt 사용자 이름/비밀번호 인증용 NATS 사용자 이름, NATS_PASSWORD와 함께 사용. NATS_CREDS도 설정된 경우 무시됨(creds 파일 JWT 인증이 우선).

NATS_PASSWORD

(설정 안 됨)

NATS 비밀번호 — NATS_USER 참조.

설치

npm (권장)

npm install -g @tadmstr/searxng-mcp

또는 npx로 직접 실행:

npx @tadmstr/searxng-mcp

소스에서 빌드

git clone https://github.com/TadMSTR/searxng-mcp.git
cd searxng-mcp
pnpm install
pnpm build

출력: build/src/index.js

MCP 클라이언트 구성

Claude Code (CLI)

권장 방법은 claude mcp add-json을 사용하여 전체 환경 변수 지원과 함께 서버를 등록하는 것입니다:

claude mcp add-json searxng --scope user '{
  "command": "npx",
  "args": ["-y", "@tadmstr/searxng-mcp"],
  "env": {
    "SEARXNG_URL": "http://localhost:8081",
    "FIRECRAWL_URL": "http://localhost:3002",
    "RERANKER_URL": "http://localhost:8787",
    "OLLAMA_URL": "http://localhost:11434",
    "CACHE_URL": "redis://localhost:6379",
    "CACHE_TTL_SECONDS": "3600",
    "FETCH_CACHE_TTL_SECONDS": "86400",
    "EXPAND_QUERIES": "false",
    "CRAWL4AI_URL": "http://localhost:11235"
  }
}'

이 명령은 ~/.claude.json에 기록합니다. searxng을 ~/.claude/settings.json에 추가하지 마십시오 — 해당 파일은 Claude Code에서 MCP 환경 변수 주입에 사용되지 않습니다.

Claude Desktop (claude_desktop_config.json)

{
  "mcpServers": {
    "searxng": {
      "command": "npx",
      "args": ["-y", "@tadmstr/searxng-mcp"],
      "env": {
        "SEARXNG_URL": "http://localhost:8081",
        "FIRECRAWL_URL": "http://localhost:3002",
        "RERANKER_URL": "http://localhost:8787",
        "OLLAMA_URL": "http://localhost:11434",
        "CACHE_URL": "redis://localhost:6379",
        "CRAWL4AI_URL": "http://localhost:11235"
      }
    }
  }
}

LibreChat (librechat.yaml)

mcpServers:
  searxng:
    type: stdio
    command: node
    args:
      - /path/to/searxng-mcp/build/src/index.js
    env:
      SEARXNG_URL: http://localhost:8081
      FIRECRAWL_URL: http://localhost:3002
      RERANKER_URL: http://localhost:8787
      OLLAMA_URL: http://localhost:11434
      CACHE_URL: redis://localhost:6379
      CRAWL4AI_URL: http://localhost:11235

GitHub URL

GitHub URL은 Firecrawl 없이 기본적으로 처리됩니다. githubFetch는 호스트 이름에 따라 분기합니다:

  • 저장소 루트 (github.com/owner/repo) — GitHub API를 통해 README를 가져옵니다

  • 파일 blob (github.com/owner/repo/blob/branch/path/to/file) — raw.githubusercontent.com의 원본 콘텐츠로 재작성하여 가져옵니다

  • 원본 파일 (raw.githubusercontent.com/...) — 그대로 직접 가져옵니다

  • API (api.github.com/...) — 응답을 디코딩하거나(base64 content 필드) JSON으로 보기 좋게 출력합니다

직접 raw.githubusercontent.comapi.github.com URL은 이전에 github.com과만 일치하여 HTML 스크래핑 계층 캐스케이드로 넘어갔는데, 이는 원본 텍스트 파일이나 순수 JSON 응답을 렌더링할 수 없어 100% 실패했습니다. 이제 GitHub 고속 경로를 사용합니다.

인증되지 않은 요청은 시간당 60회로 제한됩니다. GITHUB_TOKEN을 설정하면 시간당 5,000회로 상향됩니다.

보안

URL 안전성 (SSRF)

호출자에 의해 영향을 받거나 발견된 URL에 대한 모든 아웃바운드 요청 — 원시 HTTP 계층, robots.txt / llms.txt / Wayback / sitemap 프로브, BFS 크롤 링크 가져오기, GitHub 고속 경로 — 은 두 가지 방식으로 보호됩니다:

  1. 문자열 검사 (assertPublicUrl) — 비 HTTP(S) URL과 사설/내부 IP 리터럴을 거부합니다: RFC1918 (10.x, 192.168.x, 172.16–31.x), 루프백 (127.x, ::1), 링크-로컬 / 클라우드 메타데이터 (169.254.x), CGNAT (100.64/10), IPv6 ULA (fc00::/7) 및 링크-로컬 (fe80::/10), IPv4-매핑, 멀티캐스트/예약 범위.

  2. 연결 시 DNS 검증 — 공유 undici 디스패처의 connect.lookup해석된 주소(소켓이 실제로 연결하는 정확한 주소)를 검증합니다. 이는 공개 호스트 이름이 사설 주소로 해석되는 DNS 리바인딩 / TOCTOU 간격을 차단하며, 모든 리다이렉트 홉에서 다시 실행되므로 리다이렉트 체인이 내부 네트워크로 튈 수 없습니다.

Firecrawl (tier1) 및 Crawl4AI (tier2)는 대상 URL을 자체적으로 해석하고 가져오므로 위의 연결 시 디스패처가 이들을 커버할 수 없습니다. fetchPagecrawlSite는 두 서비스 중 하나로 디스패치하기 직전에 assertResolvedPublic(url) — 사설/예약 결과를 거부하는 일회성 호스트 이름 해석 — 을 호출하여 해당 경로의 일반적인 DNS 리바인딩 사례를 차단합니다(연결 시 가드보다 좁은 TOCTOU 창, 서비스가 다시 해석하므로).

구성된 내부 서비스(Firecrawl, Crawl4AI, SearXNG, Ollama, Reranker)는 자체 URL로 접근되며 의도적으로 보호되지 않습니다.

리다이렉트 보호

원시 HTTP 및 GitHub 고속 경로 요청은 추가로 redirect: "manual"을 사용하고 3xx 응답을 즉시 거부합니다(Location 헤더는 호출자에게 절대 반향되지 않습니다). 리다이렉트 추적 프로브(robots.txt, llms.txt, sitemap)는 각 홉을 다시 확인하는 위의 연결 시 DNS 검증으로 보호됩니다.

전송 노출

stdio는 네트워크 표면이 없습니다. HTTP 전송은 기본적으로 127.0.0.1에 바인딩되며 해당 구성에서는 인증되지 않습니다. SEARXNG_MCP_AUTH_TOKEN을 설정하지 않고 루프백에서 벗어나면 임의 URL fetch_url 및 파괴적인 clear_cache를 포함한 모든 도구가 포트에 라우팅할 수 있는 모든 것에 노출됩니다. HTTP 전송 인증을 참조하십시오.

의존성 감사

CI는 모든 푸시에서 pnpm audit를 실행합니다. 잠금 파일(pnpm-lock.yaml)은 재현 가능하고 감사 가능한 빌드를 위해 커밋됩니다.

자격 증명 처리

서버는 자격 증명을 저장하거나 기록하지 않습니다. API 키(FIRECRAWL_API_KEY, GITHUB_TOKEN, CRAWL4AI_API_TOKEN)는 환경 변수에서 읽어 해당 서비스에 대한 아웃바운드 요청에만 사용됩니다.

입력 검증

환경 변수는 시작 시 검증됩니다 — RERANK_RECENCY_WEIGHT는 NaN, 음수 또는 >1.0 값에 대해 경고합니다. 숫자 도구 매개변수는 범위 제약 조건과 함께 z.coerce.number()를 사용합니다.

기여

설정 지침, 커밋 규칙 및 PR 프로세스는 CONTRIBUTING.md를 참조하십시오.

통합 테스트

도메인 DB 동시성을 다루는 실제 Valkey 통합 스위트는 VALKEY_TEST_URL에 게이트되어 있으며 설정되지 않으면 완전히 건너뛰므로 Valkey가 없어도 일반 pnpm test가 여전히 작동합니다:

VALKEY_TEST_URL=redis://:<password>@<host>:<port>/<scratch-db> pnpm test

스크래치 데이터베이스 인덱스를 사용하십시오 — 스위트는 domain:* 키를 쓰고 삭제하며 안전 가드로 인덱스 0 또는 1에 대한 실행을 거부합니다.

라이선스

MIT

Install Server
A
license - permissive license
A
quality
A
maintenance

Maintenance

Maintainers
Response time
6dRelease cycle
19Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server for web search and content extraction using DuckDuckGo or SearXNG, with Playwright-based fetching and LLM-powered data extraction.
    140
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    A minimal MCP server that exposes a private SearXNG instance as a search tool over streamable-HTTP, enabling web search from the llama.cpp WebUI or any compatible MCP client.
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Offline-first MCP server for web search and content fetching. It requires no external API keys and uses local models for intent classification, optional cross-lingual search, semantic re-ranking, and direct-answer extraction.
    ISC
  • A
    license
    Not graded
    quality
    C
    maintenance
    A fully local MCP server that provides web search via self-hosted SearXNG and page-to-markdown conversion (static and JS-rendered), all aggregated behind a single endpoint for use with AI assistants.
    MIT

View all related MCP servers

Related MCP Connectors

  • Serper MCP — wraps the Serper Google Search API (serper.dev)

  • MCP server for Google search results via SERP API

  • Multi-engine search for AI agents. Trust scoring, local corpus, MCP-native. Self-hostable, BYOK.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/TadMSTR/searxng-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server