TrendShift 딥다이브 · 2026-07-18 · Daily #25

KnockOutEZ/wigolo 딥다이브
— AI 코딩 에이전트에게 "열쇠도 클라우드도 없는 웹"을 통째로 쥐여 주는 로컬 MCP 서버

wigolo내 컴퓨터에서 도는 MCP 서버 하나로, AI 코딩 에이전트에게 검색·수집(fetch)·크롤·추출·리서치를 전부 제공하는 도구다. 저장소가 내건 한 줄은 이것 — "Local-first web intelligence over MCP — no keys, no cloud, no metered bill."(열쇠도, 클라우드도, 종량 요금도 없는 로컬 우선 웹 인텔리전스). 먼저 오해를 풀자 — 이건 검색 API가 아니다. API 키를 사는 대신, 공개 검색엔진의 HTML을 직접 긁고, 순위 계산과 임베딩은 내 기기 안에서(온디바이스 ONNX) 돌린다. 그래서 "쿼리당 0원"이 성립한다. 두 번째로 각인할 것 — Firecrawl·Exa·Tavily 같은 유료 서비스를 겨냥한 "키 없는 로컬 대체제"이고, 캐시·모델·설정·키가 전부 ~/.wigolo/ 폴더에 산다. 이 문서는 소스를 직접 클론해 "이 저장소에서 무엇을 배울 수 있는가"를 파고든다. (저장소: KnockOutEZ/wigolo · TypeScript · MCP 서버 · AGPL-3.0-only © 2026 Towhid Khan · v0.2.0 · 최신 커밋 2026-07-17 c0afc83 · TrendShift Daily #25)
목차
  1. 프로젝트 한 줄 요약
  2. 왜 지금 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트
  7. 시스템 · 실행 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한 줄 요약

AI 에이전트를 위한 "키 없는 로컬 웹 두뇌" — 검색·수집·크롤·추출·리서치를 MCP 도구 10개로

KnockOutEZ/wigolo내 컴퓨터에서 도는 MCP(Model Context Protocol) 서버다. Claude Code·Cursor·Codex 같은 AI 코딩 에이전트에 붙여 두면, 에이전트가 "이거 검색해 줘 / 이 페이지 읽어 줘 / 이 사이트 크롤해 줘 / 이 표만 뽑아 줘 / 이 주제 리서치해 줘" 같은 웹 작업을 단 하나의 창구로 시킬 수 있다. package.json의 설명 그대로 "Local-first web intelligence MCP server for AI coding agents"이다.

가장 먼저 풀어야 할 오해 — 이름이 "위골로"라 낯설지만, 이건 Firecrawl·Exa·Tavily처럼 돈 받고 API를 파는 웹 서비스의 "키 없는 로컬 버전"이다. 보통 이런 도구는 회원가입 → API 키 발급 → 쿼리마다 과금이지만, wigolo는 공개 검색엔진(DuckDuckGo·Bing·Mojeek…)의 결과 HTML을 직접 긁어 오고, 그 결과의 순위 매기기·유사도 계산을 내 CPU에서 도는 작은 AI 모델(ONNX)로 처리한다. 그래서 검색 키가 필요 없고, 쿼리당 비용이 0이다.

핵심 용어 · 이게 뭐냐
MCP(Model Context Protocol) — AI에게 도구를 꽂는 표준 USB 포트
MCP는 AI 모델(에이전트)이 외부 도구를 부르는 방식을 표준화한 프로토콜이다. AI가 "웹 검색"이라는 능력을 쓰고 싶을 때, 각 도구마다 제각각인 API를 익힐 필요 없이 MCP라는 공통 규격으로 대화한다. wigolo는 이 규격을 따르는 서버라서, MCP를 지원하는 어떤 에이전트(Claude Code·Cursor·Codex·Gemini CLI·VS Code·Windsurf·Zed…)에도 그대로 꽂힌다. 연결 통로는 stdio(표준 입출력) — 즉 네트워크 포트를 열지 않고 프로그램끼리 파이프로 대화한다.
핵심 용어 · 이 프로젝트의 심장
"키 없음 · 클라우드 없음 · 종량 요금 없음"의 실제 원리
세 가지 "없음"은 마케팅 구호가 아니라 코드로 확인된다. ① 키 없음 = 검색은 공개 엔진의 HTML을 linkedom으로 파싱해 긁는다(src/search/engines/duckduckgo.tslite.duckduckgo.com/lite를 긁는 식). ② 클라우드 없음 = 순위 재정렬(리랭킹)과 임베딩을 기기 안 ONNX 모델로 돌린다. ③ 종량 요금 없음 = 캐시·모델·설정이 전부 ~/.wigolo/에 있어 재질의는 로컬 캐시가 답한다. 단, 이 "무료"는 핵심 6개 도구(search·fetch·crawl·extract·cache·find_similar)에 해당하고, 글을 써 주는 research·agent는 LLM이 필요하다(뒤에서 상술).
한 문장 비유

"검색·수집을 구독 서비스로 사 쓰던 것을, 내 책상 위 만능 사서 로봇으로 되돌린 개인 리서치 비서"

Firecrawl·Exa 같은 유료 API는 외부 대행사와 같다 — 검색·크롤을 대신 해 주지만, 요청 한 건마다 요금이 붙고 내 질의가 그 회사 서버를 지난다. wigolo는 이 대행사를 내 책상 위 사서 로봇으로 바꾼다. 로봇은 공개 도서관(공개 검색엔진)에 직접 가서 목록을 베껴 오고, 어떤 자료가 더 쓸모 있는지 스스로(기기 안 작은 AI로) 판단해 순위를 매긴다.

로봇이 한 번 찾아 온 자료는 책상 서랍(~/.wigolo/ 캐시)에 넣어 두니, 같은 질문엔 인터넷 없이도 즉시 답한다. 요금 청구서도, 회원 카드도 없다. 다만 "긴 보고서를 문장으로 써 달라"는 부탁만은 글솜씨 좋은 작가(LLM)를 따로 불러야 한다 — 그 작가는 내 키로 부르거나, 동네 작가(로컬 Ollama)를 써도 된다.

2왜 지금 주목받는가

TrendShift Daily #25 — "유료 검색 API의 키 없는 로컬 대체 + 에이전트 시대의 웹 접근 표준화"

wigolo가 트렌딩에 오른 이유는 "또 하나의 크롤러"여서가 아니다. AI 에이전트가 폭발적으로 늘면서 "에이전트에게 웹을 어떻게 쥐여 줄 것인가"가 병목이 됐는데, 지금까지의 답은 대부분 Firecrawl·Exa·Tavily 같은 유료 API였다. wigolo는 그걸 키도 클라우드도 없이, 내 기기에서 공짜로 하겠다고 나선 정면 도전이다. README 스스로 밝히길, 공개 직후 48시간 만에 별 대부분이 몰렸다(2026년 7월 초).

포인트내용
정체로컬 우선 웹 인텔리전스 MCP 서버. 검색·수집·크롤·추출·캐시·유사문서·리서치·에이전트·diff·watch까지 웹 작업 10종을 하나의 서버로. 조각이 아니라 완결된 도구 세트다.
차별점 ①검색 키가 필요 없다. 공개 엔진 HTML을 직접 긁고(duckduckgo·bing·mojeek…), 순위는 기기 안 크로스 인코더로 재정렬. Firecrawl/Exa/Tavily 대비 "쿼리당 0원".
차별점 ②클라우드가 없다. 캐시(SQLite)·임베딩(fastembed)·모델(ONNX)·설정·API 키가 전부 ~/.wigolo/에. 내 질의가 제3자 서버를 안 지난다(LLM 합성은 선택).
차별점 ③"코드가 모델을 이긴다"는 설계. 정규화·중복제거·순위융합·해싱 같은 확정적 작업은 LLM을 절대 안 거치고, LLM이 채운 값은 원문과 대조해 없으면 지운다(환각 차단).
제작자Towhid Khan(GitHub 핸들 KnockOutEZ) 1인. 라이선스는 AGPL-3.0(카피레프트)이고 "wigolo"는 상표로 주장.

주목 포인트 1 — "에이전트에게 웹을 주는 법"을 표준화하되, 값을 0으로 눌렀다

지금 AI 에이전트 생태계의 뜨거운 병목은 "모델은 똑똑한데 최신 웹을 못 본다"는 것이다. 그 틈을 Firecrawl·Exa·Tavily 같은 유료 API가 메워 왔다. wigolo는 같은 일을 MCP 도구 10개로 표준화해 어떤 에이전트에도 꽂히게 하면서, 비용은 공개 엔진 스크래핑 + 온디바이스 ML로 0에 눌렀다. "에이전트 시대의 인프라를 오픈소스로, 그것도 공짜로"라는 서사가 트렌딩의 동력이다.

주목 포인트 2 — 검색 품질을 "모델 없이" 끌어올리는 파이프라인

단순 스크래핑은 결과가 지저분하다. wigolo가 흥미로운 건 여러 엔진 결과를 합치고 순위를 매기는 과정을 전부 확정적 알고리즘 + 작은 기기 모델로 처리한다는 점이다. 여러 엔진 순위를 RRF(상호 순위 융합)로 합치고, 기기 안 크로스 인코더(ms-marco-MiniLM)로 질의–결과 적합도를 재계산해 재정렬한다. "큰 LLM 없이도 검색 품질을 어떻게 올리나"의 실전 교재다.

주목 포인트 3 — "한 서버, 다섯 얼굴"의 실전 패키징

wigolo는 사실 MCP 서버이자, CLI이자, REST API 서버이자, npm 라이브러리이자, SDK 묶음이다. 같은 구현을 stdio(MCP)·터미널 명령·127.0.0.1:3333 REST로 동시에 노출한다. "하나의 핵심 로직을 여러 진입점으로 파는" 현대적 도구 패키징을 통째로 관찰할 수 있다. 게다가 브라우저 자동화(Playwright)·SQLite 벡터검색·온디바이스 임베딩·TLS 지문 위장까지, 한 저장소 안에 요즘 인프라 기술이 밀집해 있다.

균형 잡힌 시각
과대평가 금물 — 아직 public beta이고, "무료"에는 조건이 있다

냉정히 보면 wigolo는 아직 public beta다(README가 "충분히 많은 사람이 써 보고 별을 줄 때까지 beta로 둔다"고 명시). 배포 채널도 지금 검증된 건 npm 하나뿐이고, curl | sh·Homebrew·컨테이너 레지스트리·in-repo SDK는 "정식 릴리스 때 열린다"는 예고 상태다. 그리고 "키 없음/무료"는 핵심 6개 도구에만 해당한다 — research·agent처럼 문장을 써 주는 기능은 LLM이 필요하고, 첫 실행 때 리랭커·임베딩 ONNX 모델을 수백 MB 내려받고 디스크 ~1.5GB를 쓴다. 즉 "완성된 무료 만능툴"이라기보다 "아키텍처가 알찬, 진행 중인 실전 인프라"로 보는 게 정확하다.

3기술 스택 전체 지도

언어 = TypeScript(ESM) · 브라우저 = Playwright · 저장 = SQLite(+벡터) · 지능 = 온디바이스 ONNX

wigolo는 TypeScript로 짠 Node ≥ 20 프로그램이다("type": "module" ESM). 코드의 압도적 다수가 .ts(1,073개)이고, .py(44개)는 별도로 버전 관리되는 파이썬 SDK·프레임워크 연동일 뿐 핵심이 아니다. 스택을 ① 프로토콜·런타임, ② 웹 수집(브라우저·HTTP), ③ 저장·검색(SQLite), ④ 지능(온디바이스 ML), ⑤ 부가(LLM·CLI UI)로 나눠 지도를 그려 보자.

① 프로토콜 · 런타임 · 빌드

영역기술 · 버전
MCP 서버@modelcontextprotocol/sdk ^1.29.0Server + StdioServerTransportstdio(JSON-RPC) 통신. 사용 안내 마크다운을 MCP Resource로도 노출
런타임 · 언어Node ≥ 20(engines.node), ESM, TypeScript ^6.0.2
빌드tsup(esbuild 기반, bundle:false=파일별 ESM 트랜스파일) + tsc로 타입 선언 생성. 개발은 tsx
테스트vitest ^4.1.4633개 테스트 파일(README의 "7,600 테스트"는 케이스 수)
패키징npm(주 배포) · @yao-pkg/pkg 6.21.0 + esbuild 0.28.0(단일 실행파일) · Docker(node:22-bookworm-slim, slim/full 2종)

② 웹 수집 — 3단 사다리로 "막히면 세게"

수집은 가벼운 것부터 시도하고 막히면 더 강한 수단으로 올라가는 계단식이다. 이게 src/fetch/router.ts(63KB, 저장소에서 가장 큰 파일)의 SmartRouter다.

단계기술 · 역할
1단 · 순수 HTTPhttp-client.ts — 그냥 HTTP 요청. 가장 빠르고 가볍다. 대부분의 정적 페이지는 여기서 끝
2단 · TLS 위장tls-tier.ts(28KB) + 선택 의존성 wreq-js ^2.3.1TLS 지문을 실제 브라우저처럼 위장해 봇 차단을 통과
3단 · 헤드리스 브라우저Playwright 1.60.0MultiBrowserPool(browser-pool.ts)이 chromium/firefox/webkit을 라운드로빈. JS로 그리는 SPA·챌린지 페이지를 실제 렌더
본문 추출@mozilla/readability ^0.6.0 + defuddle ^0.16.0(본문 골라내기), linkedom ^0.18.12(서버 DOM 파싱), turndown ^7.2.4(HTML→마크다운), pdf-parse ^2.4.5(PDF), tinyld(언어감지)

③ 저장 · 검색 — SQLite 하나에 "단어 검색 + 벡터 검색"을 얹다

구분기술 · 버전
로컬 DBbetter-sqlite3 ~12.9.0~/.wigolo/wigolo.db에 캐시·색인 저장
단어 검색FTS5(url_cache_fts 가상 테이블) — SQLite 내장 전문 검색, BM25 점수
벡터 검색sqlite-vec ^0.1.9(vec0 가상 테이블) — 같은 SQLite 안에서 코사인 유사도(의미 검색)
토큰 계산gpt-tokenizer ^3.4.0 — 결과 길이를 토큰 예산에 맞춰 잘라 주기
스키마 검증ajv 8.17.1extract가 받는 커스텀 JSON Schema 검증

④ 지능 — 임베딩·리랭킹을 전부 "기기 안에서"

기능기술 · 모델
임베딩fastembed ^2.1.0(네이티브 ONNX) — 모델 BAAI/bge-small-en-v1.5(384차원), ~/.wigolo/fastembed에 캐시
리랭킹(재정렬)@huggingface/transformers 3.5.0(Transformers.js) — 크로스 인코더 Xenova/ms-marco-MiniLM-L-6-v2로 질의–결과 적합도 재계산
순위 융합src/search/rrf.ts — 여러 엔진 순위를 RRF(Reciprocal Rank Fusion)로 합침. LLM 없이 확정적 계산

⑤ 부가 — LLM(선택) · 키 보관 · 터미널 UI

LLM전적으로 선택이며 문장 합성(research·agent·search format=answer)에만 쓴다 — @anthropic-ai/sdk·openai·@google/genai·groq-sdk, 또는 로컬 Ollama. API 키는 선택 의존성 @napi-rs/keyring ^1.3.0으로 OS 키체인에 넣거나 AES 암호화 파일로 보관한다(config.json엔 절대 평문 저장 안 함). 터미널 UI(설정 마법사·대화형 셸)는 ink ^5.2.1 + react ^18.3.1 + @inquirer/prompts로 그린다.

핵심 용어 · wigolo 설계 철학
"코드가 모델을 이긴다(Code beats model)"
wigolo의 설계 원칙이다. URL 정규화·중복 제거·순위 융합(RRF)·해싱처럼 답이 정해진 작업은 절대 LLM을 안 거친다 — 느리고 비싸고 틀릴 수 있으니까. LLM은 오직 "문장으로 요약/서술"이 필요할 때만 부르고, 그때도 LLM이 채운 필드를 원문과 대조해 근거가 없으면 null로 지운다(환각 차단). 큰 모델에 다 떠넘기지 않고 확정적 코드로 뼈대를 세우는 이 태도가 "키 없이 무료"를 가능케 한 진짜 이유다.

4아키텍처 심화 분석

에이전트 → MCP(stdio) → 도구 10개 → (수집 사다리 · 검색 파이프라인) → ~/.wigolo/ 캐시

wigolo의 데이터 흐름은 "에이전트의 요청이 MCP 창구로 들어와, 도구 계층을 지나, 웹과 로컬 캐시를 오가며 답을 만들어 돌아가는" 구조다. 전체 지도를 먼저 그리고, 두 개의 핵심 엔진(수집 사다리 · 검색 파이프라인)을 확대해 보자.

┌──────────────────────────────────────────────────────────────┐ │ wigolo 전체 데이터 흐름 │ └──────────────────────────────────────────────────────────────┘ [AI 코딩 에이전트] Claude Code · Cursor · Codex · Gemini CLI … │ │ MCP over stdio (JSON-RPC) ※ 네트워크 포트 없음 ▼ ┌──────────────────────────────────────────────┐ │ wigolo MCP 서버 (src/server.ts) │ │ createMcpServer / initSubsystems │ └──────────────────────────────────────────────┘ │ 도구 호출 (CallToolRequest) ▼ ┌──────────────────────────────────────────────┐ │ 도구 계층 src/tools/ (10개 핸들러) │ │ search · fetch · crawl · extract · cache │ │ find_similar · research · agent · diff · watch│ └──────────────────────────────────────────────┘ │ │ │ (수집) │ (검색) ▼ ▼ ┌───────────────┐ ┌───────────────────────┐ │ SmartRouter │ │ intent-router → │ │ 3단 수집 사다리 │ │ vertical 엔진 → RRF → │ │ (router.ts) │ │ 크로스인코더 재정렬 │ └───────────────┘ └───────────────────────┘ │ │ ▼ ▼ [공개 웹] ←── 긁기/렌더 ── [공개 검색엔진 HTML] │ │ └──────────┬──────────────┘ ▼ ┌─────────────────────────────────────────┐ │ 로컬 저장 ~/.wigolo/ │ │ wigolo.db (url_cache · FTS5 · vec0 …) │ │ fastembed 모델 · 설정 · (키는 OS 키체인) │ └─────────────────────────────────────────┘ │ 재질의는 캐시가 즉답(오프라인 가능) ▼ [에이전트로 결과 반환]

핵심 설계 ① — SmartRouter: "막히면 한 단계 세게" 올라가고, 그 판단을 도메인별로 학습

웹 수집의 난제는 "어떤 페이지는 그냥 HTTP로 되고, 어떤 페이지는 봇 차단 때문에 진짜 브라우저가 필요하다"는 것이다. wigolo는 가장 싼 수단부터 시도하고, 실패 신호(403·SPA 빈 껍데기·본문 빈약·챌린지 페이지)를 보면 한 단계 올라간다. 그리고 결정적으로 — 어느 도메인이 몇 단을 요구하는지 학습해 SQLite(domain_routing)에 저장한다. 다음번 같은 도메인은 처음부터 맞는 단계로 간다.

┌──────────────────────────────────────────────────────────────┐ │ SmartRouter 3단 escalation (src/fetch/router.ts) │ └──────────────────────────────────────────────────────────────┘ 요청 URL │ ▼ ┌──────────────┐ 성공? ──► 결과 (본문 → 마크다운) │ 1단 HTTP │ ──────────► 가장 빠름·가벼움 │ http-client │ └──────┬───────┘ │ 403 / 봇차단 / 얇은본문 신호 ▼ ┌──────────────┐ 성공? ──► 결과 │ 2단 TLS 위장 │ ──────────► 브라우저인 척 지문 위조 │ tls-tier + │ (선택: wreq-js) │ wreq-js │ └──────┬───────┘ │ 여전히 막힘 / JS 렌더 필요(SPA) ▼ ┌──────────────┐ 성공? ──► 결과 │ 3단 브라우저 │ ──────────► 실제 렌더 (chromium/ │ Playwright │ firefox/webkit 풀) │ browser-pool │ └──────┬───────┘ │ ▼ [domain_routing 테이블에 "이 도메인엔 N단이 필요"를 기록·학습] → 다음 요청은 처음부터 N단으로 시작 (wigolo tune 으로 확인)

핵심 설계 ② — 검색 파이프라인: 의도 분류 → 버티컬 엔진 → 융합 → 재정렬

wigolo의 search는 그냥 한 엔진에 던지는 게 아니다. 먼저 질의의 "의도"를 분류(intent-router.ts)해 버티컬(general·news·papers·docs·code·images)을 정하고, 그 버티컬에 맞는 엔진 묶음만 병렬로 부른다. 예컨대 논문 질의면 arXiv·Semantic Scholar가, 코드 질의면 GitHub 코드검색·StackOverflow가 붙는 식이다. 그렇게 모은 여러 순위를 RRF로 융합하고, 기기 안 크로스 인코더로 최종 재정렬한 뒤, 인용 ID가 달린 근거·하이라이트로 돌려준다.

┌──────────────────────────────────────────────────────────────┐ │ 검색 파이프라인 (src/search/core/ · verticals/) │ └──────────────────────────────────────────────────────────────┘ 질의 (배열 가능 = 여러 개 병렬) │ ▼ intent-router ── 질의 의도 분류 ──► vertical 결정 │ general / news / papers │ docs / code / images ▼ vertical별 엔진 묶음만 병렬 호출 (17개 어댑터 중 부분집합) │ ddg · bing · mojeek · marginalia (일반) │ arxiv · semantic-scholar (논문) │ github-code · stackoverflow · mdn (코드/문서) │ hn-algolia · lobsters · wikipedia … ▼ RRF 융합 (rrf.ts) ── 여러 순위표를 하나로, LLM 없이 확정적 │ ▼ 크로스 인코더 재정렬 (ms-marco-MiniLM, 온디바이스 ONNX) │ 질의와 각 결과의 "진짜 적합도"를 다시 채점 ▼ 근거·하이라이트 + 설명 가능한 점수 + 인용 ID │ (선택) format=answer 이면 LLM이 문장으로 합성 ▼ 에이전트로 반환 + 결과를 ~/.wigolo 캐시에 저장

핵심 설계 ③ — 로컬 저장소: SQLite 한 파일이 캐시·색인·학습 데이터를 겸한다

모든 로컬 상태는 ~/.wigolo/wigolo.db 하나에 모인다(src/cache/db.ts). 표 하나에 여러 검색 방식을 얹은 게 특징이다.

테이블역할
url_cache수집한 페이지 본문·메타데이터. embedding·embedding_model·embedding_dims 열이 붙어 벡터까지 동거
url_cache_ftsFTS5 가상 테이블 — 단어(BM25) 검색용
vec0 (sqlite-vec)임베딩 벡터의 코사인 유사도 검색 — 의미 검색
domain_routing도메인별 "몇 단 수집이 필요한가" 학습값 — SmartRouter가 읽고 씀
domain_boilerplate사이트 공통 군더더기(헤더·푸터) 지문 — 크롤 시 중복 제거에 사용
search_cache검색 결과 캐시 — 같은 질의 재질의를 즉답(오프라인 가능)
비유 · "정직한 실패"라는 원칙

wigolo의 또 다른 설계 미덕은 honest degradation(정직한 성능 저하)이다. 캐시가 낡았거나, 어떤 엔진이 실패했거나, 결과가 잘렸거나, 봇 차단(blocked_by_challenge)에 걸렸으면 — 그 사실을 결과에 그대로 표시한다. "빈 성공"으로 위장하지 않는다. 각 엔진은 wrapWithRetryAndBreaker(재시도 + 서킷 브레이커)로 감싸, 한 엔진이 죽어도 전체가 멈추지 않는다. 에이전트가 "이 답이 완전한지 아닌지"를 알 수 있게 하는, 신뢰성 설계다.

5디렉토리 구조 해부

추적 파일 1,543개 · src/가 두뇌(71K줄), tests/가 절반(633파일) — "한 서버, 여러 진입점"

저장소를 열면 압도되기 쉽다(파일 1,543개, 코드 20만 줄). 하지만 뼈대는 단순하다 — src/가 실제 두뇌이고 나머지는 테스트·배포·연동·문서다. 진입점부터 따라가 보자.

wigolo/ ├── src/ ← 두뇌 (71,326 줄, ts/tsx) │ ├── index.ts ← CLI 진입점 (#!/usr/bin/env node) │ │ bin.wigolo → dist/index.js │ │ bare `wigolo` / `wigolo mcp` = MCP 서버 기동 │ ├── server.ts ← MCP 서버 코어 (initSubsystems·createMcpServer) │ ├── tools/ (10개) ← 도구별 핸들러 하나씩 │ │ fetch·search·crawl·extract·cache·find-similar │ │ research·agent·diff·watch │ ├── server/ ← MCP 도구 JSON 스키마 (tool-schemas.ts 25KB) │ ├── search/ (84개) ← 최대 서브시스템 │ │ engines/ (18파일=17엔진+UA헬퍼) · core/(intent+verticals) │ │ reranker/ · hybrid/ · find-similar/ · rrf.ts · dedup.ts │ ├── fetch/ (21개) ← router.ts(SmartRouter 63KB)·browser-pool │ │ tls-tier·playwright-tier·http-client·stealth/challenge │ ├── extraction/ (43개) ← 구조화 추출 (선택자·표·JSON-LD·스키마) │ ├── cache/ (17개) ← db.ts (SQLite: FTS5 + vec0) │ ├── embedding/ (4개) ← fastembed-provider (BAAI/bge-small 384d) │ ├── research/ ← 질문 분해→탐색→합성→인용 보고서 │ ├── agent/ ← planner→executor→rank→relevance 루프 │ ├── crawl/ (10개) ← BFS/DFS/sitemap · robots.txt · 속도제한 │ ├── providers/ ← embed/rerank/search/extract/vector-store 추상화 │ ├── plugins/ ← ~/.wigolo/plugins/ 에서 커스텀 엔진·추출기 로드 │ ├── searxng/ ← (선택) SearxNG 애그리게이터 연동 │ ├── daemon/ + watch/ ← serve 모드 + 변경 감시 스케줄러 │ └── repl/ + cli/ (133개) ← 대화형 TUI·설정 마법사 (ink + react) ├── tests/ (633 파일) ← 122,471 줄 — 코드보다 테스트가 많다 ├── benchmarks/ ← 추출·검색·에이전트·임베딩 벤치 (골든 픽스처) ├── packages/ (4개) ← 프레임워크 연동 래퍼 ├── sdks/ ← TypeScript + Python REST 클라이언트 (미발행) ├── site/ ← Next.js 마케팅·문서 사이트 ├── skills/ ← 에이전트용 스킬 11종 카탈로그 ├── mcpb/ ← MCP Bundle 매니페스트 ├── packaging/ ← binary · docker · release 자동화 ├── package.json ← 진실원: version 0.2.0 · mcp.tools · engines ├── README.md (56KB) · SKILL.md · CHANGELOG.md └── Dockerfile · Makefile · tsup.config.ts · vitest.config.ts

진입점 읽는 순서는 이렇다. ① src/index.ts(CLI 라우팅 — 무슨 명령이 뭘 하는지) → ② src/server.ts(MCP 서버가 어떻게 뜨고 도구를 등록하는지) → ③ src/server/tool-schemas.ts(각 도구가 받는 입력 스키마) → ④ 관심 도구의 src/tools/*.ts → ⑤ 그 도구가 부르는 서브시스템(search/ 또는 fetch/). 가장 큰 파일 src/fetch/router.ts(63KB)와 src/types.ts(71KB)가 각각 "수집 두뇌"와 "타입 지도"라, 이 둘을 훑으면 전체 그림이 빠르게 잡힌다.

함정 · 자주 헷갈리는 것
"엔진 18개"와 "도구 8개"는 둘 다 살짝 함정

엔진: src/search/engines/엔 파일이 18개지만 하나(user-agents.ts)는 헬퍼라 실제 엔진 어댑터는 17개다(arxiv·bing·bing_news·brave·brave-image·ddg-image·devdocs·duckduckgo·github-code·hn-algolia·lobsters·marginalia·mdn·mojeek·semantic-scholar·stackoverflow·wikipedia). 게다가 한 질의에 다 쓰지 않는다 — intent-router가 버티컬별로 부분집합만 고른다. 도구: package.jsonmcp.tools 배열엔 8개만 적혀 있지만(diff·watch 누락), 실제 src/server.ts가 등록하는 건 10개다. 문서·코드가 맞고 package.json 필드가 낡았다 — 코드(10개)를 믿어라.

6학습 포인트

이 저장소 하나에 밀집한 "요즘 인프라 기술" — 기술별로 무엇을 배울 수 있나

wigolo가 학습 표본으로 훌륭한 이유는 MCP 서버 설계, 웹 스크래핑, 브라우저 자동화, SQLite 벡터 검색, 온디바이스 ML, TLS 위장, 다중 진입점 패키징이 한 저장소에 모여 있기 때문이다. 관심사별로 배울 것을 짚어 보자.

① MCP 서버를 "제대로" 짜는 법

src/server.ts + src/server/tool-schemas.ts는 MCP 서버의 실전 레퍼런스다. 도구를 어떻게 등록하고(ListToolsRequestSchema/CallToolRequestSchema), 입력 스키마를 어떻게 선언하며, 사용 안내를 MCP Resource로 어떻게 노출하는지 볼 수 있다. 특히 도구 10개가 하나의 stdio 서버에 깔끔히 얹힌 구조는, 직접 MCP 서버를 만들 때 그대로 베낄 만한 골격이다.

② "봇 차단을 어떻게 우회하나"의 실전 계단

src/fetch/는 웹 스크래핑의 현실을 담은 교재다. 순수 HTTP → TLS 지문 위장 → 헤드리스 브라우저로 올라가는 escalation 전략, 실패 신호(403·SPA 껍데기·챌린지)를 읽는 법, 그리고 도메인별로 필요한 강도를 학습·저장해 다음번을 빠르게 하는 최적화까지. "왜 어떤 페이지는 requests로 안 되는가"를 코드로 이해하게 된다.

③ "큰 LLM 없이 검색 품질 올리기"

src/search/는 정보 검색(IR)의 압축 강의다. 여러 엔진 순위를 RRF로 융합하고, 크로스 인코더로 재정렬하고, 중복을 지우고, 근거·하이라이트를 뽑는 전 과정이 확정적 코드 + 작은 온디바이스 모델로 짜여 있다. "검색은 무조건 큰 LLM"이라는 통념 대신, 값싼 부품으로 품질을 조립하는 법을 배운다.

④ SQLite 하나로 "단어 + 의미" 하이브리드 검색

src/cache/db.ts + src/embedding/SQLite에 FTS5(단어)와 sqlite-vec(벡터)를 함께 얹어 하이브리드 검색을 만드는 법을 보여 준다. 임베딩을 기기 안 fastembed(ONNX)로 뽑아 url_cache 테이블에 벡터째 저장하는 구조는, RAG를 "서버 없이 로컬로" 만들고 싶은 사람에게 최고의 참고 예시다.

⑤ "하나의 코어, 여러 얼굴"의 패키징

같은 로직을 MCP(stdio)·CLI·REST(127.0.0.1:3333)·npm 라이브러리로 동시에 파는 구조(src/index.ts·src/daemon/)는 현대적 도구 배포의 정석이다. tsup로 빌드하고 @yao-pkg/pkg로 단일 실행파일을 만들고 Docker 이미지를 굽는 packaging/까지, "라이브러리를 여러 채널로 배포"하는 전 과정을 통독할 수 있다.

실습 아이디어 · 작게 시작하기
지금 당장 손에 익힐 3가지
npx wigolo search "your query"CLI 한 방을 때려 보고, 나온 결과에 붙은 설명 가능한 점수·인용 ID를 관찰한다. ② ~/.wigolo/wigolo.db를 SQLite 뷰어로 열어 url_cache·domain_routing 테이블이 어떻게 채워지는지 본다. ③ wigolo tune으로 도메인별 학습된 수집 단계를 확인한다. 이 세 가지만 해 봐도 "이 도구가 안에서 뭘 하는지"가 손에 잡힌다.

7시스템 · 실행 요구사항

Node ≥ 20 · 디스크 ~1.5GB · 첫 실행 때 브라우저·ONNX 모델 다운로드(수백 MB)

wigolo는 클라우드 인프라가 필요 없다. 대신 첫 실행 때 브라우저 엔진 바이너리와 온디바이스 ONNX 모델을 ~/.wigolo/로 한 번 내려받는다(수백 MB). 이후 유휴 메모리는 ~47MB로 가볍다(v0.2.0 기준).

항목요구/설명
런타임Node ≥ 20(engines.node 명시), ESM
디스크~1.5GB 여유 — 브라우저 바이너리 + ONNX 모델(임베딩·리랭커) + 캐시 DB
OSmacOS · Linux · Windows (Docker 이미지엔 브라우저 OS 라이브러리 내장)
메모리유휴 ~47MB. 모델·브라우저는 첫 사용 시 지연 로드(lazy)
API 키핵심 6개 도구는 불필요. research·agent·answer만 LLM 키(또는 로컬 Ollama) 필요. Brave·GitHub 키는 검색을 강화하는 선택지일 뿐
네트워크실시간 웹 접근 필요(공개 엔진·대상 사이트). 재질의만 캐시로 오프라인 가능

설치 · 실행 명령

# 가장 쉬운 길: 에이전트에 자동 연결 (MCP 설정 + 스킬 + 모델 다운로드 + 헬스체크)
npx wigolo init --agents=claude-code
#   지원: claude-code · cursor · codex · gemini-cli · vscode · windsurf · zed · antigravity

# 또는 MCP 클라이언트 설정에 직접 등록
#   mcpServers: { "wigolo": { "command": "npx", "args": ["-y", "wigolo"] } }

# CLI 한 방 (MCP 없이 바로 도구 실행)
npx wigolo search "rust async runtime comparison"
npx wigolo fetch  "https://example.com/article"

# 관리 명령
npx wigolo doctor      # 환경 점검
npx wigolo tune        # 도메인별 학습된 수집 단계 확인
npx wigolo serve       # REST API 모드 (127.0.0.1:3333, OpenAPI 3.1)
함정 · "무료·로컬"의 정확한 경계
"키 없음"은 진짜지만, "다운로드 없음·오프라인"은 아니다

"키 없음" ≠ "다운로드 없음" — 검색 키는 정말 필요 없지만, 리랭커·임베딩 ONNX 모델을 첫 실행 때 수백 MB 내려받고 디스크 ~1.5GB를 쓴다. ② "로컬" = 저장이 로컬이라는 뜻이지 오프라인이 아니다 — 도구는 실시간 웹을 친다. 캐시된 재질의만 오프라인으로 답한다. ③ research·agent는 LLM 필수 — 클라우드 키를 넣거나, 키 없이 가려면 로컬 Ollama를 깔아야 한다. "완전 공짜 만능"으로 오해하면 실제 사용에서 어긋난다.

8직접 해볼 수 있는 실습 과제

난이도별 5개 — CLI 관찰부터 나만의 엔진 어댑터·미니 하이브리드 검색까지
난이도 ★☆☆ · 초급

과제 1 — "설명 가능한 검색"을 눈으로 확인

npx wigolo search "your topic"를 실행하고, 결과에 붙은 per-result 점수·인용 ID·하이라이트를 관찰한다. 같은 질의를 format=answer로도 돌려(LLM 필요) "근거만 주는 것" vs "문장으로 써 주는 것"의 차이를 비교한다. 목표: wigolo가 "검색 → 융합 → 재정렬 → 근거 제시"를 어떻게 투명하게 보여 주는지 체감.

난이도 ★☆☆ · 초급

과제 2 — 캐시와 도메인 학습을 들여다보기

몇 개 사이트를 fetch한 뒤 ~/.wigolo/wigolo.db를 SQLite 뷰어로 열어 url_cache·domain_routing·domain_boilerplate 테이블을 관찰한다. 같은 도메인을 다시 fetch했을 때 수집 단계가 학습대로 바뀌는지 wigolo tune으로 확인한다. 목표: "재질의가 왜 빠른가"와 "도메인 학습"을 직접 목격.

난이도 ★★☆ · 중급

과제 3 — RRF(순위 융합)를 다른 언어로 재현

src/search/rrf.ts의 Reciprocal Rank Fusion을 파이썬/JS로 옮겨, "서로 다른 순위표 2~3개를 하나로 합치는" 작은 함수를 만든다. 같은 문서가 여러 순위표에서 어떻게 위로 올라오는지 실험한다. 목표: LLM 없이 검색 품질을 올리는 확정적 알고리즘의 원리를 코드로 증명.

난이도 ★★☆ · 중급

과제 4 — 나만의 검색 엔진 어댑터 만들기

examples/plugin-search-engine/src/search/engines/duckduckgo.ts를 참고해, 공개 검색/목록 사이트 하나를 긁는 어댑터~/.wigolo/plugins/에 얹어 본다. HTML을 linkedom으로 파싱해 제목·URL·스니펫을 뽑아 표준 결과 형태로 반환하면 된다. 목표: wigolo의 플러그인 확장점과 스크래핑 어댑터 패턴을 체득.

난이도 ★★★ · 고급

과제 5 — SQLite로 미니 하이브리드 검색 엔진 만들기

better-sqlite3 + FTS5(단어) + sqlite-vec(벡터)로, 문서 몇 개를 넣으면 BM25 점수와 임베딩 코사인 유사도를 합쳐 순위를 내는 작은 검색기를 직접 짠다. 임베딩은 fastembed로 기기 안에서 뽑는다. wigolo의 src/cache/db.ts·src/embedding/가 참고 예시. 목표: "서버 없는 로컬 RAG 검색"을 스스로 완성.

9관련 기술 심화 학습 로드맵

4주 코스 — MCP·에이전트 도구 → 웹 스크래핑·브라우저 자동화 → 정보 검색(IR) → 온디바이스 RAG
주차주제 · 학습 목표
1주차
MCP · 에이전트 도구
MCP와 에이전트 툴링을 이해한다. Model Context Protocol의 개념, stdio 전송, 도구 등록(ListTools/CallTool)과 입력 스키마 설계, Resource 노출. wigolo의 src/server.ts·tool-schemas.ts를 뜯어보며 "하나의 서버에 도구 10개를 얹는 법"을 익히고, 작은 MCP 서버를 직접 만들어 본다.
2주차
웹 스크래핑 · 브라우저
현실의 웹 수집. HTTP → TLS 지문 위장 → 헤드리스 브라우저(Playwright)로 올라가는 escalation, 봇 차단 신호 읽기, @mozilla/readability·defuddle로 본문 추출, turndown으로 마크다운화. 도메인별 수집 강도 학습(domain_routing)의 캐싱 전략까지.
3주차
정보 검색(IR)
모델 없이 검색 품질 올리기. BM25(FTS5)의 원리, RRF(상호 순위 융합)로 여러 엔진 합치기, 크로스 인코더 리랭킹(ms-marco-MiniLM)의 개념, 중복 제거와 근거·하이라이트 추출. wigolo src/search/를 교재로 "값싼 부품으로 좋은 검색 조립하기"를 실습.
4주차
온디바이스 RAG
서버 없는 로컬 지능. 임베딩·벡터 검색의 기초, sqlite-vec로 SQLite에 벡터 얹기, fastembed(ONNX)로 기기 안에서 임베딩 뽑기, FTS5 + 벡터 하이브리드 검색. 나아가 research·agent처럼 "분해→탐색→합성" 하는 에이전트 루프와 BYOK LLM 추상화를 살펴본다.

10핵심 키워드 사전

이 문서에서 반드시 챙길 용어 14개
용어 01
MCP(Model Context Protocol)
AI 에이전트가 외부 도구를 표준 방식으로 부르는 프로토콜. wigolo는 이 규격을 따르는 서버라 MCP를 지원하는 어떤 에이전트에도 꽂힌다. 통로는 stdio(포트 없이 파이프로 대화).
용어 02
로컬 우선(Local-first)
캐시·모델·설정·키가 전부 내 기기(~/.wigolo/)에 있는 설계. 단 "저장이 로컬"이라는 뜻이지 오프라인은 아니다 — 도구는 실시간 웹을 치고, 재질의만 캐시가 오프라인으로 답한다.
용어 03
"키 없음 · $0/쿼리"의 원리
공개 검색엔진의 HTML을 직접 긁고(스크래핑), 순위·임베딩을 기기 안 ONNX로 처리해 검색 API 키·과금이 없다. Firecrawl·Exa·Tavily 같은 유료 서비스의 로컬 대체제.
용어 04
SmartRouter · escalation
src/fetch/router.ts의 수집 두뇌. HTTP → TLS 위장 → 헤드리스 브라우저로 "막히면 한 단계 세게" 올라가고, 도메인별 필요 단계를 학습·저장(domain_routing)해 다음을 빠르게 한다.
용어 05
TLS 지문 위장(impersonation)
HTTP 요청의 TLS 핸드셰이크 지문을 실제 브라우저처럼 위조해 봇 차단을 통과하는 2단 기법. 선택 의존성 wreq-js가 담당.
용어 06
Playwright · MultiBrowserPool
3단 수집 수단인 헤드리스 브라우저 엔진. chromium/firefox/webkit 컨텍스트를 풀로 관리(browser-pool.ts)해 JS로 그리는 SPA·챌린지 페이지를 실제 렌더한다.
용어 07
intent-router · vertical
검색 질의의 의도를 분류해 버티컬(general·news·papers·docs·code·images)을 정하고, 그 버티컬에 맞는 엔진 묶음만 병렬 호출하는 라우터. 17개 어댑터 중 부분집합만 쓴다.
용어 08
RRF(Reciprocal Rank Fusion)
여러 검색엔진의 순위표를 하나로 합치는 확정적 알고리즘(rrf.ts). 여러 순위에서 자주 상위에 뜬 문서가 위로 올라온다. LLM 없이 검색 품질을 올리는 핵심.
용어 09
크로스 인코더 리랭킹
질의와 각 결과를 함께 넣어 "진짜 적합도"를 재계산하는 재정렬 단계. 기기 안 ONNX 모델 Xenova/ms-marco-MiniLM-L-6-v2로 돈다(클라우드·키 불필요).
용어 10
온디바이스 임베딩(fastembed)
텍스트를 벡터로 바꾸는 임베딩을 서버가 아니라 기기에서(fastembed+ONNX, 모델 BAAI/bge-small-en-v1.5 384차원) 계산. ~/.wigolo/fastembed에 캐시.
용어 11
FTS5 · sqlite-vec(vec0)
하나의 SQLite에 얹은 두 검색. FTS5 = 내장 전문(단어) 검색(BM25). sqlite-vec의 vec0 가상 테이블 = 벡터 코사인 유사도(의미 검색). 둘을 합치면 하이브리드 검색.
용어 12
"코드가 모델을 이긴다"
정규화·중복제거·순위융합 같은 확정적 작업은 LLM을 안 거치고, LLM이 채운 값은 원문과 대조해 근거 없으면 null로 지우는 설계 원칙(환각 차단). "무료·로컬"을 가능케 한 진짜 이유.
용어 13
MCP 도구 10종
search·fetch·crawl·extract·cache·find_similar(키 없는 핵심 6) + research·agent(LLM 합성) + diff·watch(비교·변경감시). package.json엔 8개만 적혀 있으나 코드는 10개를 등록.
용어 14
honest degradation · BYOK
honest degradation = 낡은 캐시·실패 엔진·차단(blocked_by_challenge)을 "빈 성공"으로 위장하지 않고 결과에 정직히 표시. BYOK(Bring Your Own Key) = LLM은 사용자 자신의 키로(또는 로컬 Ollama로) 부르고, 키는 OS 키체인에 보관.

11참고 링크

원본을 직접 열어 확인하는 습관 — 특히 버전·수치는 클론한 소스 기준

저장소 · 제품
· GitHub: github.com/KnockOutEZ/wigolo (AGPL-3.0-only © 2026 Towhid Khan · v0.2.0)
· 최신 커밋: c0afc83 (2026-07-17, PR #184 "fix/vercel-ai-sdk-ignore-scripts" 병합)
· 겨냥한 유료 대체 대상: Firecrawl · Exa · Tavily

핵심 파일(클론 후)
· src/server.ts — MCP 서버 코어(도구 10개 등록)
· src/index.ts — CLI 진입점(명령 라우팅)
· src/fetch/router.ts — SmartRouter 3단 수집 사다리(63KB, 최대 파일)
· src/search/core/ + verticals/ — 의도 분류 → 버티컬 엔진 → RRF → 재정렬
· src/cache/db.ts — SQLite(FTS5 + vec0) 로컬 저장
· src/embedding/fastembed-provider.ts — 온디바이스 임베딩

배경 지식
· Model Context Protocol — MCP 규격의 원전
· Playwright · sqlite-vec · fastembed
· Reciprocal Rank Fusion(RRF) — 순위 융합의 개념
· SQLite FTS5 — 내장 전문 검색

확인된 함정 모음 · 재확인 필수
읽기 전에 알아 둘 7가지

검색 API 아님 — 공개 엔진 HTML을 긁고 순위는 온디바이스 ML로 내는 로컬 도구다. ② "무료"의 경계 — 키 없는 건 핵심 6개 도구뿐, research·agent는 LLM 필요(클라우드 키 또는 로컬 Ollama). ③ "로컬" ≠ 오프라인 — 저장이 로컬이란 뜻이고 도구는 실시간 웹을 친다(재질의만 캐시로 오프라인). ④ 첫 실행 다운로드 — 브라우저·ONNX 모델 수백 MB, 디스크 ~1.5GB. ⑤ 도구는 10개package.jsonmcp.tools는 8개만 적었으나(diff·watch 누락) 코드가 10개를 등록한다. ⑥ 배포는 아직 npm만 검증curl|sh·Homebrew·컨테이너·in-repo SDK는 "정식 릴리스 때" 예고(public beta). ⑦ 제작자 이름 vs 핸들 — 저작권자 Towhid Khan = GitHub 핸들 KnockOutEZ(동일인, Docker Hub는 towhid69420). 라이선스는 AGPL-3.0(카피레프트)이니 상용 통합 시 조항 확인.