searxng-mcp
searxng-mcp
자체 호스팅 SearXNG 인스턴스를 통한 비공개 웹 검색용 MCP 서버입니다. 결과는 로컬 ML 모델로 재순위화되고, 전체 페이지 콘텐츠는 Firecrawl을 통해 가져오며, 선택적 Ollama 인스턴스는 쿼리 확장 및 LLM 합성 요약을 제공합니다.
Claude Code 및 LibreChat 에이전트와 함께 사용하도록 설계되었으며, 제3자 검색 API에 쿼리를 보내지 않고 웹 검색이 필요한 경우에 적합합니다.
Claude Code와 homelab-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-mcpFirecrawl, Crawl4AI, Ollama, Kiwix, 광고 차단 프록시 및 NATS를 포함한 전체 로컬 토폴로지는 docker-compose.full.yml을 참조하세요.
Related MCP server: searxng-mcp-bridge
도구
도구 | 설명 | 주요 매개변수 |
| 로컬 재순위화로 SearXNG를 통해 검색합니다. 더 넓은 결과 풀을 가져와 관련성별로 재순위화하고 상위 N개를 반환합니다. SearXNG의 기본 직접 답변, 정보 상자, 맞춤법 수정 및 관련 제안은 목록 위와 |
|
| 검색, 재순위화 후 가져오기 캐스케이드(Firecrawl → Crawl4AI → 원시 HTTP)를 사용하여 상위 결과의 전체 콘텐츠를 가져옵니다. |
|
| 검색, 상위 결과 가져오기 후 Ollama( |
|
| 공개 URL에서 읽을 수 있는 마크다운을 가져와 추출합니다. GitHub 호스트는 GitHub 빠른 경로를 사용하고, YouTube 비디오 URL은 트랜스크립트를 반환하며, Reddit 스레드 URL은 게시물+댓글을 반환합니다(둘 다 아래의 robots를 통해 옵트인). 그 외에는 가져오기 캐스케이드(Firecrawl → Crawl4AI → 원시 HTTP)를 사용합니다. 토큰 예산(기본 ~8,000자)으로 잘립니다. |
|
| 전체 사이트를 크롤링하고 각 페이지의 URL/제목/스니펫 매니페스트를 반환합니다. 먼저 Firecrawl 크롤링을 시도하고, 실패하면 사이트맵 파싱, 그 다음 선택적 BFS로 대체합니다. 전체 페이지 콘텐츠는 Valkey에 캐시되므로 후속 |
|
| 검색 캐시, 가져오기 캐시, 크롤링 매니페스트 캐시 또는 전체를 제거합니다. 캐시된 결과가 오래되었을 수 있는 빠르게 변화하는 주제를 조사할 때 유용합니다. |
|
| 도메인 기능 데이터베이스의 읽기 전용 보기입니다. |
|
매개변수
category — general (기본), news, it, science
time_range — day, week, month, year — 게시 날짜별로 결과를 제한합니다. 전체 기간 결과는 생략합니다.
fetch_count — 전체 콘텐츠를 가져올 상위 재순위화 결과 수 (기본 1, search_and_fetch의 최대 3; 기본 3, search_and_summarize의 최대 5).
domain_profile — 명명된 도메인 필터 프로필 적용: homelab (자체 호스팅/Linux 문서 표시) 또는 dev (Stack Overflow, MDN, npm 표시). 기본 필터는 생략합니다.
expand — true인 경우 검색 전에 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:#000000SearXNG와 Firecrawl이 필요합니다. Crawl4AI, Valkey, Ollama, Kiwix 및 재순위화기는 선택 사항입니다. 서버는 이러한 항목 중 하나라도 사용할 수 없을 때 정상적으로 저하됩니다.
광고 차단
searxng-mcp는 가져오기 계층 그룹당 하나씩 두 개의 독립적인 광고 차단 사이드카를 사용합니다:
사이드카 | 계층 | 메커니즘 |
| 계층 1 (Firecrawl) | CDP 수준 차단 — 전체 HTTPS 필터링, 동일한 브라우저 프로세스 |
| 계층 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 크기를 줄입니다.
환경 변수:
변수 | 기본값 | 설명 |
| 설정 안 됨 |
|
| EasyList + EasyPrivacy | 쉼표로 구분된 필터 목록 URL 목록. |
|
| 차단기가 구성된 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.json에 tier_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.comREADME 가져오기)를 기록합니다.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.orgArticle/NewsArticle/BlogPosting/TechArticle및ScholarlyArticle/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.comdump-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 flushdomain-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(예: 자동 플러시 복구를 위한 서비스 부팅 시퀀스)에 대해 실행해도 안전합니다.
환경 변수 | 기본값 | 용도 |
|
| 날짜 스냅샷이 기록/읽히는 위치. 배포 시 내구성 있는 경로(앱데이터 또는 NFS 마운트)로 설정합니다. |
|
| 유지할 스냅샷 수. 오래된 것은 각 유지 관리 실행 시 정리됩니다. |
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 책 |
|
|
|
|
|
|
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단계 전략 캐스케이드를 사용합니다:
Firecrawl 크롤 — Firecrawl(
/crawl엔드포인트)에 크롤 작업을 보내고 완료될 때까지 폴링한 다음 전체 페이지 목록을 반환합니다.FIRECRAWL_CRAWL_POLL_INTERVAL_MS및FIRECRAWL_CRAWL_MAX_WAIT_MS로 제어됩니다.사이트맵 구문 분석 — Firecrawl이 실패하거나 빈 결과를 반환하면
/sitemap.xml(및 연결된 사이트맵)을 가져와 제목/스니펫이 있는 URL을 추출합니다. 사이트맵 XML 구문 분석에는fast-xml-parser를 사용합니다.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 크롬 스크래핑보다 더 깨끗한headline및articleBody를 제공합니다(스크립트 태그당 1MB 크기 제한).제목 캐스케이드 —
og:title→twitter: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_request→rerank→fetch(×N) →tier1_firecrawl|tier2_crawl4ai|tier3_rawfetch→post_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 파일 인증이 우선한다. 주제:
주제 | 발행 시점 |
| 검색 도구 호출 시 |
| 검색 반환 시(소스, 지연 시간, rerank 적용 여부 포함) |
|
|
| 티어가 빈 결과를 반환하거나 예외를 던진 경우 |
| robots.txt가 금지한 경우 |
| 페치 해결 시( |
| 모든 Valkey 조회 시 |
| 단계 태그가 지정된 오류 |
각 봉투에는 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 구성과 호환된다.
HTTP — SEARXNG_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-mcpHTTP 서버에 대해 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
- jsonReranker
reranker는 Jina 호환 /v1/rerank 엔드포인트를 노출해야 한다. 가벼운 FlashRank 래퍼가 잘 작동한다 — homelab-agent의 docker/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_allArch 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
expand 및 search_and_summarize에 필요하다. 필요한 모델을 pull하라:
ollama pull qwen3:4b # query expansion
ollama pull qwen3:14b # summarizationthink: false 동작은 자동으로 처리된다 — 추가 Ollama 구성이 필요 없다.
구성
모든 서비스 URL은 환경 변수로 구성할 수 있다.
Variable | Default | Description |
|
| SearXNG 인스턴스 URL |
|
| Firecrawl 인스턴스 URL |
|
| Reranker 인스턴스 URL |
|
| Firecrawl API 키(필요한 경우) |
| (설정 안 됨) | GitHub 개인 액세스 토큰 — 요청 한도를 시간당 60회에서 5,000회로 증가 |
| (설정 안 됨) | Ollama API 기본 URL — |
| (설정 안 됨) | 인증된 Ollama 프록시용 Bearer 토큰 — 설정 시 |
|
| 쿼리 확장( |
|
|
|
| (설정 안 됨) |
|
| (설정 안 됨) | OpenAI 호환 백엔드용 모델 ID; 설정 시 |
| (설정 안 됨) | OpenAI 호환 백엔드용 Bearer 토큰 — 설정 시 |
|
| 추론 모델(예: Qwen3)이 직접 출력을 반환하도록 |
|
| Redis 호환 URL — 결과 캐싱 활성화. |
|
| Valkey 명령 타임아웃 — 중단되거나 CPU가 급증한 캐시 백엔드는 멈추는 대신 거부함( |
|
| Valkey 연결 타임아웃. |
|
| 거부 전 Valkey 명령당 최대 재시도 횟수. |
|
| 검색 결과 캐시 TTL(초) |
|
| 가져온 페이지 캐시 TTL(초) |
|
| 크롤 매니페스트 및 페이지 콘텐츠 캐시 TTL(초, 6시간) |
|
|
|
|
|
|
|
| BFS 크롤의 최대 링크 홉 깊이 |
|
| Firecrawl 크롤 작업 완료 대기 시 폴링 간격 |
|
| 사이트맵으로 대체하기 전 Firecrawl 크롤 작업 대기 최대 시간 |
|
| 쿼리 확장을 전역적으로 활성화하려면 |
| (설정 안 됨) | Crawl4AI 인스턴스 URL — Firecrawl 실패 시 2차 가져오기 대체 활성화 |
| (설정 안 됨) | API 토큰 보호가 있는 Crawl4AI 인스턴스용 선택적 Bearer 토큰 |
|
| Wayback Machine 4차 대체를 활성화하려면 |
| (설정 안 됨) | 2차(Crawl4AI) 및 3차(원시 Node fetch) 광고 차단용 HTTP 프록시 URL — 예: |
| (설정 안 됨) | kiwix-serve 기본 URL(예: |
| (설정 안 됨) | Hister 브라우징 기록 인덱스 기본 URL — 로그인 장벽 및 JS 과다 페이지에 대해 계층 캐스케이드 전에 Hister 빠른 경로 활성화. 설정하지 않으면 기능이 비활성화되고 오버헤드가 없음. |
| (설정 안 됨) | Hister API 인증용 Bearer 토큰. |
|
|
|
|
| YouTube의 |
|
|
|
|
| Reddit의 |
|
| 전송 모드: |
|
| HTTP 수신 포트(HTTP 전송 모드 전용). |
|
| HTTP 수신 주소(HTTP 전송 모드 전용). |
| (설정 안 됨) | HTTP 전송 전용. 설정 시 |
|
| HTTP 전송 전용. 이 시간보다 오래 유휴 상태인 세션은 백그라운드 정리로 제거됨(진행 중인 요청이 있는 세션은 제외되므로 긴 |
|
| HTTP 전송 전용. 하드 상한 백스톱 — 세션 맵이 이 값을 초과하면 유휴 타임아웃과 관계없이 가장 오래 사용되지 않은 유휴 세션이 제거됨. |
| (설정 안 됨) | bcrypt 사용자 이름/비밀번호 인증용 NATS 사용자 이름, |
| (설정 안 됨) | NATS 비밀번호 — |
설치
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:11235GitHub 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/...) — 응답을 디코딩하거나(base64content필드) JSON으로 보기 좋게 출력합니다
직접 raw.githubusercontent.com 및 api.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 고속 경로 — 은 두 가지 방식으로 보호됩니다:
문자열 검사 (
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-매핑, 멀티캐스트/예약 범위.연결 시 DNS 검증 — 공유 undici 디스패처의
connect.lookup이 해석된 주소(소켓이 실제로 연결하는 정확한 주소)를 검증합니다. 이는 공개 호스트 이름이 사설 주소로 해석되는 DNS 리바인딩 / TOCTOU 간격을 차단하며, 모든 리다이렉트 홉에서 다시 실행되므로 리다이렉트 체인이 내부 네트워크로 튈 수 없습니다.
Firecrawl (tier1) 및 Crawl4AI (tier2)는 대상 URL을 자체적으로 해석하고 가져오므로 위의 연결 시 디스패처가 이들을 커버할 수 없습니다. fetchPage와 crawlSite는 두 서비스 중 하나로 디스패치하기 직전에 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
Maintenance
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
- AlicenseNot gradedqualityCmaintenanceMCP server for web search and content extraction using DuckDuckGo or SearXNG, with Playwright-based fetching and LLM-powered data extraction.140MIT
- AlicenseNot gradedqualityAmaintenanceA 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.1MIT
- AlicenseNot gradedqualityBmaintenanceOffline-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
- AlicenseNot gradedqualityCmaintenanceA 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
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.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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