Skip to main content
Glama
README.md
# webcheck-mcp

AI 에이전트가 웹페이지를 브라우저로 열어 본문을 직접 읽고 판단하는 대신,
**순수 계산으로 끝낼 수 있는 작업은 코드로 처리**해서 토큰 사용량을 줄이는
범용 MCP 서버입니다.

특정 업무(홈페이지 점검 등)에 종속되지 않습니다. "여러 URL의 상태를 확인하고
싶다", "페이지의 특정 값이 최근에 갱신됐는지 알고 싶다", "URL을 키로 하는
마크다운 표를 갱신하고 싶다" — 이 세 가지가 필요한 어떤 업무에서든 쓸 수
있습니다.

## 왜 필요한가

AI 에이전트에게 "이 URL 200개가 다 정상인지 확인해줘"라고 시키면, 에이전트는
브라우저로 200번 페이지를 열어 본문을 읽습니다. 페이지당 수천~수만 토큰이
들어가므로 전체로는 수십만~수백만 토큰이 소요됩니다.

반면 HTTP 상태 확인이나 날짜 비교 같은 작업은 원래 결정적(deterministic)
계산입니다. 코드가 몇 초 만에 끝내는 일을 AI에게 "읽고 판단"하게 시키는 건
비용도 크고, 특히 날짜 계산 같은 건 AI가 오히려 실수하기 쉬운 영역입니다.

이 서버는 그 결정적 계산 부분만 떼어내 MCP 도구로 제공합니다. **판단이
필요한 작업(문구 검토, 맥락 파악 등)은 여전히 에이전트가 직접 합니다.**

## 설치

```bash
pip install git+https://github.com/deinteger/webcheck-mcp.git
```

또는 격리된 환경을 원하면 [pipx](https://pypa.github.io/pipx/)를 권장합니다.

```bash
pipx install git+https://github.com/deinteger/webcheck-mcp.git
```

설치하면 `webcheck-mcp` 명령이 생깁니다.

## MCP 클라이언트에 등록

### Claude Code

프로젝트 루트에 `.mcp.json`을 만듭니다.

```json
{
  "mcpServers": {
    "webcheck": {
      "command": "webcheck-mcp"
    }
  }
}
```

pipx 대신 프로젝트 전용 venv에 설치했다면 `command`를 그 venv의
`bin/webcheck-mcp` 절대/상대 경로로 지정합니다.

### Antigravity 등 다른 MCP 클라이언트

MCP를 지원하는 도구라면 필요한 건 실행 커맨드 하나뿐입니다.

- command: `webcheck-mcp` (또는 설치 경로의 절대 경로)

해당 클라이언트의 "로컬 MCP 서버 추가" 메뉴/설정에 이 커맨드를 등록하면
됩니다.

## 제공 도구

### `discover_urls(start_url, explicit_urls=None, allowed_domains=None, max_pages=200, max_depth=3, exclude_patterns=None, timeout=10, ...)`

점검 대상 URL 목록을 상황에 맞게 유연하게 수집합니다. 사이트마다 사정이
다르므로(sitemap이 있는 곳, 없는 곳, 이미 점검 대상이 정해진 곳) 우선순위를
두고 자동으로 전환합니다.

1. **`explicit_urls`가 있으면 그대로 사용** — 점검 대상이 이미 정해져 있는 경우.
2. **`sitemap.xml`(또는 `robots.txt`의 `Sitemap:` 위치)이 있으면 파싱** — sitemap
   index(하위 sitemap 여러 개)도 재귀적으로 처리합니다. 일부 CMS는 존재하지
   않는 경로도 HTTP 200으로 응답하므로(`check_links`가 다루는 문제와 동일),
   실제 XML sitemap 형식이 아니거나 에러 문구가 감지되면 "sitemap 없음"으로
   취급하고 다음 단계로 넘어갑니다.
3. **둘 다 없으면 `start_url`에서부터 같은 도메인 링크(`<a href>`)를 BFS로
   크롤링**합니다. `document.location.href="..."` 같은 JS 리다이렉트 스텁
   페이지(HTTP 리다이렉트가 아니라 크롤러가 못 따라가는 패턴)도 감지해서
   따라갑니다.

**한계**: 메뉴나 목록이 JavaScript로만 렌더링되는 사이트(SPA)는 sitemap도
없고 크롤링으로도 못 찾을 수 있습니다. 그런 경우 에이전트가 브라우저로 직접
메뉴를 열어 URL을 수집해야 합니다 — 이 도구는 그 수고를 줄여주는 용도이지
항상 대체하지는 못합니다.

```python
반환: {"method": "explicit" | "sitemap" | "crawl", "urls": [...],
        "count": int, "truncated": bool, "sitemap_url": str (있는 경우)}
```

**실제 사이트 검증 (2026-09-16)**: 대상 사이트는 sitemap.xml 요청 시 CMS가
커스텀 "페이지 없음" 화면을 HTTP 200으로 반환 — sitemap 없음으로 정확히
판정하고 크롤링으로 전환됨. 홈페이지 자체는 JS 리다이렉트 스텁
(`document.location.href="/usr/main/mainPage.do"`)이었는데, 이를 따라가서
실제 메뉴 15개(전체메뉴 페이지 포함)를 정상적으로 찾아냄.

### `check_links(urls, timeout=10, max_workers=10, error_indicators=None, use_default_indicators=True)`

URL 목록의 접속 가능 여부를 병렬로 확인합니다.

일부 CMS(특히 한국 공공기관 사이트에서 흔함)는 존재하지 않는 경로에도
**HTTP 200**을 반환하고 본문에 커스텀 에러 이미지만 표시합니다. 그래서
상태코드만으로는 부족할 수 있어, 본문에서 알려진 에러 문구도 함께 검사합니다.

- `use_default_indicators=False`로 한국 공공기관용 기본 문구를 끌 수 있습니다
  (다른 도메인/업무에서 오탐이 날 경우).
- `error_indicators`로 사이트·업무별 에러 문구를 추가할 수 있습니다.

```python
반환: [{"url": ..., "status_code": ..., "ok": bool, "error": str | None}]
```

### `discover_selector(url, timeout=15, min_group_size=3, top_n=3)`

목록형 페이지에서 최신 항목의 CSS 선택자를 자동으로 추정합니다. 표의 행
(`tr`), 목록의 항목(`li`) 등 3개 이상 반복되는 형제 요소 중 첫 항목의
텍스트에서 날짜 패턴이 발견되는 그룹을 찾아 선택자 후보를 만듭니다.

새 게시판을 온보딩할 때(=아직 `추출셀렉터`가 없을 때), 브라우저를 열어 직접
선택자를 찾는 대신 이 도구로 먼저 시도해볼 수 있습니다. 여러 기관/여러
게시판을 등록해야 하는 경우 이 단계가 반복적으로 드는 비용을 크게 줄여줍니다.

**단, 이 도구는 휴리스틱이며 항상 맞지는 않습니다.** 목록이 날짜순 정렬이
아니거나 상단 고정글이 있으면 자동으로 뽑힌 "첫 항목"이 실제 최신글이
아닐 수 있습니다. 반환된 `sample_text`와 `extracted_date`가 그럴듯한지
확인한 뒤 `update_page_cache`로 반영하는 것을 권장합니다. `confidence`가
낮거나 후보가 비어 있으면 브라우저로 직접 확인합니다.

```python
반환: {"url": ..., "candidates": [
    {"selector": ..., "extracted_date": "YYYY-MM-DD",
     "sample_text": ..., "group_size": int, "confidence": float},
    ...
]}
```

**실제 사이트 검증 (2026-09-15)**: 서로 다른 구조의 게시판 3곳
(테이블형 공지사항, 테이블형 보도자료, `li` 그리드형 포토 갤러리)에서
모두 정확한 선택자와 날짜를 신뢰도 1.0으로 찾아냄.

### `check_recency(cache_file, timeout=15, stale_after_months=3)`

URL과 CSS 선택자를 담은 마크다운 표 파일을 읽어, 각 URL에서 선택자로 뽑은
텍스트를 날짜로 해석하고 `stale_after_months` 기준(달력 기준 개월 수)으로
"최근에 갱신됐는지"를 판정합니다.

표 파일은 파이프(`|`) 마크다운 표여야 하며, 최소 다음 두 컬럼이 필요합니다
(대소문자 무시, 국문/영문 모두 허용):

- URL 컬럼: `URL` 또는 `url`
- 선택자 컬럼: `추출셀렉터` 또는 `selector`

선택자는 브라우저 `document.querySelector`와 동일한 CSS 선택자 문법을
씁니다. 값이 비어 있으면 실제 접속 없이 `수동확인필요(선택자없음)`를
반환합니다 — 이 경우 에이전트가 한 번 브라우저로 열어 선택자를 찾아
`update_page_cache`로 기록해야 합니다.

```python
반환: [{"콘텐츠영역": ..., "URL": ..., "최신게시일": "YYYY-MM-DD" | None,
        "판정": "정상" | "최신성 지연" | "수동확인필요(...)" | "접속오류",
        "경과일": int | None, "비고": str}]
```

**예시 표 (`targets.md`)**

```markdown
| 콘텐츠영역 | URL | 추출셀렉터 |
| --- | --- | --- |
| 공지사항 | https://example.go.kr/notice | table.BoardTable tbody tr:first-child td:nth-child(5) |
| 채용공고 | https://example.go.kr/jobs | .job-list li:first-child .date |
```

### `update_page_cache(cache_file, updates)`

URL을 키로 하는 마크다운 파이프 표 파일을 갱신합니다. 특정 업무에 종속되지
않은 범용 도구라, 헤더가 있는 파이프 표라면 무엇이든 이 방식으로 갱신할 수
있습니다 (점검 결과 캐시, 진행 상황 표, 담당자 배정 표 등).

`updates`의 각 항목은 `URL`(또는 `url`)을 반드시 포함해야 하며, 그 외 키는
테이블 헤더의 컬럼명과 정확히 일치할 때만 갱신됩니다.

```python
반환: {"applied": [...], "not_found": [...]}
```

## 설계 원칙

- 이 서버는 **판단이 아니라 계산만** 합니다. 문구 개선, 맥락 판단이 필요한
  항목은 여전히 에이전트가 직접 확인해야 합니다.
- 특정 조직·업무·언어에 종속되지 않도록 컬럼명·기준(개월 수)·에러 문구를
  모두 매개변수로 조정할 수 있게 설계했습니다.
- 별도 인프라(호스팅, 상시 구동)가 필요 없습니다. MCP 클라이언트가 필요할 때
  자식 프로세스로 실행하고 세션이 끝나면 종료합니다.

## 유래

이 프로젝트는 국립원예특작과학원 홈페이지 현행화 점검 도구
([website_monitoring_md](https://github.com/deinteger/website_monitoring_md))에서
쓰던 로직을 다른 기관/업무에서도 쓸 수 있도록 일반화해 분리한 것입니다.

## 라이선스

MIT

TDQS

A4.1/5.0

Scored across 3 tools

Disambiguation4/5

The three tools have distinct purposes: checking link availability, checking content recency, and updating a cache table. However, check_links and check_recency both involve URL checking, which could cause slight confusion, though their descriptions clarify the different intents.

Naming Consistency4/5

Tool names follow a consistent verb_noun pattern: check_links, check_recency, update_page_cache. The verbs 'check' and 'update' are clear, and the nouns are descriptive. Minor inconsistency: 'page_cache' is more specific than the actual generic table update capability, but the naming pattern is consistent.

Tool Count4/5

Three tools is a reasonable, focused set for a web monitoring/checking server. It is slightly on the smaller side but each tool serves a distinct, necessary function for the apparent purpose of link checking, recency monitoring, and cache management.

Completeness4/5

The tool set covers the core workflow: checking links, checking recency, and updating a cache table. A minor gap is the lack of a tool to read/retrieve the cache table or manage error indicators directly, but the described workflow is mostly complete for the stated purpose.

Maintenance

ActivityMaintained
ResponsivenessNo issues