{"id":50718204,"url":"https://github.com/HarimxChoi/google-surf-mcp","last_synced_at":"2026-06-26T22:00:42.433Z","repository":{"id":354808045,"uuid":"1225316611","full_name":"HarimxChoi/google-surf-mcp","owner":"HarimxChoi","description":"Google search MCP. One MCP replaces search + fetch + academic-paper extractor.","archived":false,"fork":false,"pushed_at":"2026-06-13T17:06:31.000Z","size":2481,"stargazers_count":230,"open_issues_count":1,"forks_count":26,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-13T18:24:39.196Z","etag":null,"topics":["academic-research","fetch","google-search","mcp","model-context-protocol","pdf-processing","playwright","self-healing"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HarimxChoi.png","metadata":{"files":{"readme":"README.ko.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-30T06:53:47.000Z","updated_at":"2026-06-13T18:06:24.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/HarimxChoi/google-surf-mcp","commit_stats":null,"previous_names":["harimxchoi/google-surf-mcp"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/HarimxChoi/google-surf-mcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarimxChoi%2Fgoogle-surf-mcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarimxChoi%2Fgoogle-surf-mcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarimxChoi%2Fgoogle-surf-mcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarimxChoi%2Fgoogle-surf-mcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HarimxChoi","download_url":"https://codeload.github.com/HarimxChoi/google-surf-mcp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarimxChoi%2Fgoogle-surf-mcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34834415,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-26T02:00:06.560Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["academic-research","fetch","google-search","mcp","model-context-protocol","pdf-processing","playwright","self-healing"],"created_at":"2026-06-09T21:00:25.964Z","updated_at":"2026-06-26T22:00:42.421Z","avatar_url":"https://github.com/HarimxChoi.png","language":"TypeScript","funding_links":[],"categories":["*Ops for AI"],"sub_categories":["AI Orchestration \u0026 Deployment"],"readme":"\u003cimg src=\"./assets/icon256.png\" width=\"128\" align=\"right\" alt=\"google-surf-mcp\"/\u003e\n\n# google-surf-mcp\n\n[English](./README.md) | 한국어\n\n[![npm version](https://img.shields.io/npm/v/google-surf-mcp)](https://www.npmjs.com/package/google-surf-mcp)\n[![npm downloads](https://img.shields.io/npm/dm/google-surf-mcp)](https://www.npmjs.com/package/google-surf-mcp)\n[![ci](https://github.com/HarimxChoi/google-surf-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/HarimxChoi/google-surf-mcp/actions/workflows/ci.yml)\n[![google-surf-mcp MCP server](https://glama.ai/mcp/servers/HarimxChoi/google-surf-mcp/badges/score.svg)](https://glama.ai/mcp/servers/HarimxChoi/google-surf-mcp)\n\n![demo](./assets/demo.gif)\n\n\u003e 실제 사용은 기본 **headless**로 동작합니다 (Chrome 창 안 보임). 영상처럼 보이게 하려면 `SURF_HEADLESS=false` 설정\n\n무료 Google 검색 MCP가 전부 안 돼서 직접 만든 MCP\n\nMCP 1개가 3개 역할: 검색 + URL fetcher + 학술 페이퍼 추출\n\n✅ 실제로 작동 (무료 MCP 6개 테스트, 전부 fail)  \n✅ 1개 MCP로 검색 + 본문 + 학술 PDF 추출 (기존: search + fetch + 학술검색 MCP 3개 조합)  \n✅ 학술 PDF 인라인 추출: arxiv, biorxiv, Nature, OpenReview, NeurIPS, JMLR, PMLR, Springer, PubMed (PMC 경유)  \n✅ `search_extract` 기본 abstract 모드 (~1500자/결과, 토큰 절약), `mode=\"full\"`로 본문 전체  \n✅ 스폰서 광고 + 지식 패널 자동 제거 (geometric verification, 텍스트 매칭 아님)  \n✅ CAPTCHA 자동 복구 4모드: OS 알림 (기본) / `SURF_HEADLESS=false` / `SURF_REMOTE_DEBUG` / `SURF_CLOUD_MODE` (fail-fast)  \n✅ API 키 / 프록시 / 솔버 X  \n\n도구 5개: `search` / `search_parallel` / `extract` / `search_extract` / `health`\n\n## How\n\nMCP 클라이언트에 설정시 Google 검색 도구로 사용 가능, anti-bot은 warm Chrome profile + stealth로 처리  \nCAPTCHA는 사람이 직접 함 (프로필 평판 유지 → 지속가능한 운영)\n\n첫 호출 시 프로필 자동 부트스트랩. 로컬 전용 — headless / 서버리스 환경은 `SURF_CLOUD_MODE=true` (CAPTCHA fail-fast, 워커 풀 비활성)\n\n## Numbers\n\n| | 결과 |\n|---|---|\n| sequential | ~1.5s/query (첫 호출은 ~4s, 셋업 포함) |\n| parallel x4 | ~1.5s wall (첫 호출은 ~9s, pool warm 포함) |\n| parallel x10 | ~4.5s wall |\n| search_extract x5 (abstract, 기본) | ~3s wall |\n| search_extract x5 (full) | ~5s wall (검색 + 5개 병렬 추출) |\n\n워크스테이션 1Gbps 환경에서 측정\n\n## Stack\n\n- Playwright + 영구 Chrome 프로필\n- `playwright-extra` stealth (cascade fallback tier)\n- Multi-strategy SERP parser + geometric verification (sponsored / knowledge_panel / related 드롭)\n- PDF는 `@llamaindex/liteparse`(PDFium spatial parsing, 선택적 OCR), HTML 본문은 Mozilla Readability + Turndown\n- 이미지 / 미디어 / 폰트 차단 (속도)\n- 첫 호출 자동 부트스트랩, pool warm 3회 실패 시 single-context로 폴백\n- Self-healing 2단계: 런타임 parser-strategy 재배열 (deterministic) + 일일 cron repair PR (synthesis → 선택적 LLM → triple-gate 검증, 사람이 리뷰)\n\n## Install\n\nNode 18+, 시스템에 Google Chrome (또는 Chromium) 필요\n\n```bash\nnpx google-surf-mcp   # 실제 MCP, 클라이언트 config에 등록\n```\n\n첫 호출 시 프로필 자동 워밍 (Chrome 창이 잠깐 보일 수 있음)\n\n또는 로컬 클론:\n\n```bash\ngit clone https://github.com/HarimxChoi/google-surf-mcp\ncd google-surf-mcp\nnpm install\n```\n\n자동 부트스트랩 실패 시 (드묾) 수동 실행:\n```bash\nnpm run bootstrap\n```\n\n경로 오버라이드:\n```bash\nCHROME_PATH=/path/to/chrome SURF_TZ=America/New_York npm run bootstrap\n```\n\n## Claude Code에서 사용\n\n`~/.claude.json`에 이거 붙여넣기:\n\n```json\n{\n  \"mcpServers\": {\n    \"google-surf\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"google-surf-mcp\"]\n    }\n  }\n}\n```\n\nClaude Code 재시작\n\n다른 MCP 클라이언트도 같은 JSON 구조 그대로 (config 파일 경로만 다름)\n\n로컬 클론 사용 시:\n```json\n{\n  \"mcpServers\": {\n    \"google-surf\": {\n      \"command\": \"node\",\n      \"args\": [\"/abs/path/to/google-surf-mcp/build/index.js\"]\n    }\n  }\n}\n```\n\n## Tools\n\n- `search(query, limit?)` - 단일 검색, ~1.5초. title / url / snippet 반환. 스폰서 광고 + 지식 패널 자동 제거 (응답에 `dropped` 카운트 + `dropped_reasons` 포함). 결과 24h 캐시 (`SURF_CACHE_TTL_SEARCH_MS=0`으로 우회)\n- `search_parallel(queries[], limit?)` - 4-워커 풀, 호출당 최대 10개 쿼리\n- `extract(url, max_chars?, mode?)` - URL 가져와서 본문 반환\n  - `mode=\"full\"` (기본): 본문 전체, PDF는 `liteparse`(spatial parsing, 다단 읽기)\n  - `mode=\"abstract\"`: ~1500자 요약 (PDF 1페이지 또는 HTML meta description). 본문 가져오기 전 관련성 판단용\n  - `mode=\"metadata\"`: PDF 페이지 수만\n  - 응답: `content`, `title`, `excerpt`, `length`, `is_pdf`, `page_count`, `extraction_quality`. 실패는 `{ error }` 반환, throw 안 함\n- `search_extract(query, limit?, max_chars?, mode?)` - 검색 + 병렬 추출 한 번에. 기본 `mode=\"abstract\"`는 SERP 결과에 ~1500자 요약 붙여서 반환 (저렴한 트리아지). 실제 본문 필요 시 `mode=\"full\"` (느림, 토큰 많이 씀)\n- `health()` - 서버 상태. 응답: `cascade` / `pool` (`warmFailures` + `fallback`) / `rateLimiter` / `cache` / `telemetry` / `selfHealing` (현재 strategy 순서 + 통계) / `config`. 검색이 실패하면 호출 — `pool.fallback=true` 또는 `cascade.totalCaptchas` 증가가 보통 원인\n\n## Env vars\n\n| 변수 | 기본값 | 설명 |\n|---|---|---|\n| `CHROME_PATH` | 자동 감지 | Chrome 바이너리 절대 경로 |\n| `SURF_PROFILE_ROOT` | `~/.google-surf-mcp` | warm 프로필 위치 |\n| `SURF_LOCALE` | `en-US` | 브라우저 로케일 |\n| `SURF_TZ` | 시스템 tz | 예: `America/New_York` |\n| `SURF_HEADLESS` | `true` | `false`로 설정 시 Chrome 보이게 동작 (데모 / 디버깅용). `false`면 CAPTCHA 복구 시 OS 알림 생략 (사용자가 이미 보고 있음). |\n| `SURF_REMOTE_DEBUG` | `false` | headless 서버 + 원격 DevTools 환경에서 `true`. CAPTCHA 발생 시 DevTools 포트 안내 후 throw, 별도 창 안 띄움. 로컬 머신에서 SSH 포트포워드 + `chrome://inspect`로 풀고 재시도. |\n| `SURF_IDLE_CLOSE_MS` | `30000` | sequential ctx와 pool을 idle 후 닫는 ms. `0`이면 비활성화. 낮으면 빠른 정리, 높으면 띄엄띄엄 호출에 캐시 유지. |\n| `SURF_ALLOW_PRIVATE` | `false` | `true`로 설정 시 `extract`가 사설/loopback 주소(`localhost`, `127.0.0.1`, `10.x`, `192.168.x`, `169.254.x` 등) 접근 허용. 기본은 SSRF 차단으로 막음. |\n| `SURF_EXTRACT_MAX_CHARS` | `8000` | `extract` 기본 truncation (200–50000); per-call `max_chars`가 우선 |\n| `SURF_EXTRACT_OCR` | `false` | 스캔/이미지 PDF를 Tesseract로 OCR (느림; 기본 off) |\n| `SURF_CLOUD_MODE` | `false` | headless/서버리스 모드: TLS 우회 + `--no-sandbox` + `--disable-dev-shm-usage` + 워커 풀 비활성 + CAPTCHA fail-fast |\n| `SURF_CASCADE_DISABLED` | `false` | 3-tier 자동 cascade 대신 단일 stealth 모드(`SURF_USE_STEALTH`로 선택)로 고정 |\n| `SURF_USE_STEALTH` | `true` | 초기 stealth tier — `SURF_CASCADE_DISABLED=true`일 때만 적용 |\n| `SURF_HUMANLIKE_MODE` | `off` | `off` / `background` (결과 반환 후 비동기 실행) / `inline` (반환 전 대기, 더 느림) — opt-in humanlike 브라우징 |\n| `SURF_RATE_LIMIT_PER_MIN` | `10` | 분당 Google 요청 내부 상한 |\n| `SURF_CACHE_TTL_SEARCH_MS` | `86400000` | search 캐시 TTL (24h); `0`이면 캐시 비활성화 |\n| `SURF_CACHE_MAX_ENTRIES` | `1000` | 캐시 namespace별 LRU 상한 |\n| `SURF_CACHE_ROOT` | `\u003cprofile\u003e/cache` | 캐시 디렉토리 |\n| `SURF_INSECURE_TLS` | `=SURF_CLOUD_MODE` | `--ignore-certificate-errors` (cloud 모드에서 자동 on) |\n| `SURF_NO_SANDBOX` | `=SURF_CLOUD_MODE` | `--no-sandbox` (cloud 모드에서 자동 on) |\n| `SURF_TELEMETRY` | `false` | `true`로 설정 시 jsonl 이벤트 로깅 활성화 (검색 결과, 캐시 hit/miss, tool 에러, parser staleness 기록). self-healing 파이프라인의 입력으로 사용. 기본 OFF. |\n| `SURF_TELEMETRY_ROOT` | `\u003cprofile\u003e/telemetry` | jsonl 파일 디렉토리. UTC 기준 날짜별 파일 1개 (`YYYY-MM-DD.jsonl`). |\n| `SURF_SELF_HEALING` | `true` | strategy별 성공/실패 추적 + 영속 재배열. leader가 runner-up보다 3승 차이 이상일 때만 재배열 발동. `false`로 끄면 기본 strategy 순서 고정 |\n| `SURF_SELF_HEALING_FILE` | `\u003cprofile\u003e/.heal/strategy-order.json` | self-healing 상태 영속 경로. atomic tmp+rename 쓰기, 5초 디바운스 |\n| `SURF_LLM_HEAL` | `false` | workflow 전용 `repairWithLLM`의 LLM 호출 opt-in. 기본 OFF → 외부 LLM 요청 절대 안 나감. `true`로 켜면 `ANTHROPIC_API_KEY` (본인 키) 필요. 패키지는 유지보수자 키를 절대 포함하지 않음 |\n| `ANTHROPIC_API_KEY` | — | 본인 Anthropic 키. `SURF_LLM_HEAL=true`일 때만 읽음. 런타임 self-healing (`SURF_SELF_HEALING`)은 deterministic이라 이 변수 안 읽음 |\n\n## Troubleshooting\n\n- CAPTCHA 4모드 (env로 자동 결정):\n  - 기본 (로컬 데스크탑): OS 알림 발송, headed Chrome 열림, 사람이 풀면 자동 재시도\n  - `SURF_HEADLESS=false`: headed Chrome 열림, 알림 생략\n  - `SURF_REMOTE_DEBUG=true`: DevTools 포트 안내 출력, 로컬에서 `chrome://inspect`로 attach해서 풀기\n  - `SURF_CLOUD_MODE=true`: `CAPTCHA_REQUIRED` 에러로 fail-fast\n- **headed Chrome이 CAPTCHA 대신 그냥 검색창으로 열림**: 그냥 아무 검색어 입력하고 Enter 치면 됨. 이후 호출은 정상 동작\n- \"Chrome not found\": Chrome 설치 또는 `CHROME_PATH` 설정\n- 셀렉터 깨짐: 2단계 대응 — 런타임 strategy 재배열 (`SURF_SELF_HEALING`, deterministic) + 일일 cron이 후보 셀렉터로 draft PR 자동 생성 (`SURF_LLM_HEAL` 선택, 사람이 리뷰)\n- 검색이 Numbers 표보다 느려짐: `health().pool.fallback` 확인. `true`면 워커 풀이 warm 3회 실패 후 single-context로 폴백된 상태. `npm run bootstrap`으로 seed 프로필 갱신하면 보통 회복됨\n- SSRF: `extract`는 기본적으로 `localhost`, 사설 IP, AWS metadata 차단. `SURF_ALLOW_PRIVATE=true`로 우회\n\n## Changelog\n\n[CHANGELOG.md](./CHANGELOG.md)\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHarimxChoi%2Fgoogle-surf-mcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FHarimxChoi%2Fgoogle-surf-mcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHarimxChoi%2Fgoogle-surf-mcp/lists"}