# Changelog

## [4.0.3] - 2026-05-11

### Fixed (사용자 제보: time_travel "임의시점 비교"에서 [NOT_FOUND]")

`chain_amendment_track`의 `time_travel` 시나리오가 자주 개정되는 법령(소득세법 시행령 등)에서 시점 매칭 실패. 원인 둘:

1. **lsHistory display 100건 한계**: 「소득세법 시행령」은 총 289건 연혁인데 `fetchHistoricalVersionsRaw`가 단일 호출 `display=100`만 사용 → 가장 오래된 연혁이 **2012-09-02** 까지만 닿음. 2012년 8월 이전 시점을 `fromDate`로 주면 모두 `[NOT_FOUND] 시점 매칭 실패` 반환. 법제처 API 문서엔 `max=100`이라 표기되어 있으나 실제는 `display=500`도 그대로 반환함을 검증.
2. **진단 메시지 부실**: "시점 매칭 실패. 가장 오래된 연혁: ..." 한 줄만 표기 → 사용자/LLM이 원인 추정 어려움.

- **페이징 회수기 신설** ([src/lib/historical-utils.ts](src/lib/historical-utils.ts)): `fetchHistoricalVersionsFull` — `display=500` + 페이징(최대 20p / 10,000건 안전상한) + 응답 HTML의 `총 N건` 파싱 + MST 중복 제거. 「소득세법 시행령」은 1회 호출로 288/289건 회수, 가장 오래된 연혁이 **1949-08-05**까지 닿음.
- **legacy `fetchHistoricalVersionsRaw` 호환 유지**: `@deprecated` 표기 후 내부적으로 `Full`을 호출, `versions`만 반환. 외부 호출자 영향 0.
- **진단 메시지 강화** ([src/tools/scenarios/time-travel.ts](src/tools/scenarios/time-travel.ts)):
  - 시점 매칭 실패 시: 연혁 범위(최초~최신 efYd), 수집 건수/법제처 총 건수, 페이지 수, 어떤 입력(fromDate/toDate)이 범위 밖인지 명시.
  - 본문 조회 실패 시: 시점 A/B의 MST와 efYd 표시.
  - 조문 추출 실패 시: MST와 추출 개수 표시 (새 분기).
  - 성공 시 헤더에 `연혁 N/M개 수집(Kp)` 줄 추가 (페이징 동작 가시화).
- **검증**: 「소득세법 시행령」 `time_travel` 호출 → 2024 vs 2026(평범), 2008 vs 2026(이전엔 실패), **1950 vs 2026**(최초 시행본 1949까지 닿음) 모두 정상 동작. 1940 vs 2026(범위 밖)은 친절한 [NOT_FOUND] 안내.

### Files
- 수정: [src/lib/historical-utils.ts](src/lib/historical-utils.ts) (페이징/총건수/중복제거), [src/tools/scenarios/time-travel.ts](src/tools/scenarios/time-travel.ts) (진단 강화, fetchHistoricalVersionsFull 사용)
- 신규 파일: 없음

## [4.0.2] - 2026-05-11

### Fixed (사용자 제보: "상법 검색 시 보상 관련 법령만 반환")

법제처 `lawSearch.do`는 법령명 **LIKE 검색 + 가나다순 정렬**로 동작한다. `query=상법`이면 totalCnt=56건 중 「상법」(상행위 법전)은 가나다순 **34위**에 위치하고, 1~33위는 "보상법/배상법/기상법/재해보상법" 시리즈가 점유한다. 여기에 MCP의 `display` 기본값이 20이라 사용자에게는 항상 보상 관련 법령만 노출되는 구조적 결함이 있었다. 「민법」「형법」「관세법」 등 두 글자 짧은 법령명 전부에 동일 패턴이 적용되어 LLM이 잘못된 법령을 인용하거나 웹검색으로 우회하던 사례 다수.

- **정확매칭 우선 후처리** ([src/tools/search.ts](src/tools/search.ts)): `법령명한글`/`법령약칭명`이 사용자 입력(또는 약칭 canonical)과 정확히 일치하는 결과를 분리해서 `📍 정확매칭` 섹션으로 최상단 노출. 나머지는 `📂 부분매칭` 섹션으로 분리.
- **`display` 기본값 20 → 50 상향**: 가나다순 정렬에서 짧은 법령명이 후순위로 밀려도 한 번에 회수되도록.
- **`api-client.searchLaw`에 `display` 인자 전달**: 기존엔 search-normalizer 단에서 display가 무시되던 호출 경로 수정.
- **단행법 alias entry 추가** ([src/lib/search-normalizer.ts](src/lib/search-normalizer.ts)): 「상법」「민법」「형법」「어음법」「수표법」 — 정식명이 자기 자신이라 alias 등록은 의미 약하나, `alternatives`로 관련 법령(시행령/시행법 등) 후보 제시 + `normalizeAliasKey` export로 정확매칭 비교 키 재사용.
- **검증**: `상법` → 1위 「상법」(MST 284143), `민법` → 1위 「민법」(MST 284415), `형법` → 1위 「형법」(MST 284025), `관세법` → 1위 「관세법」(MST 280363). 약칭 fallback(화관법/중처법/주임법) 정상 동작 확인.
- **`⚠️ 정확매칭 없음` 안내**: 정확매칭이 0건이면 LLM에게 부분 LIKE 결과임을 명시 → 환각 방지.

### Files
- 수정: [src/tools/search.ts](src/tools/search.ts) (정확매칭 분리, display 50, 안내문), [src/lib/search-normalizer.ts](src/lib/search-normalizer.ts) (단행법 5종 alias, normalizeAliasKey export)
- 신규 파일: 없음

## [4.0.1] - 2026-05-08

### Added (issue #35: 국세청 직접 회신 해석례 검색 미지원)

기존 `search_interpretations`는 법제처 정부유권해석(`target=expc`)만 조회하므로, 국세청이 직접 회신한 법령해석을 가져올 수단이 없었음. 키워드 매칭으로 국세청 관련 안건이 노출은 되지만 모두 `해석기관=법제처`라 사용자가 누락 사실조차 인지하기 어려웠던 문제.

- **`search_decisions`/`get_decision_text` 통합 도구의 `domain` enum에 `"nts"` 추가** — 법제처 API target `ntsCgmExpc`(국세청 법령해석 목록) 호출. 신규 노출 도구 0개.
- **응답 구조가 관세청(`kcsCgmExpc`)과 동일**해서 `customs-interpretations.ts`에서 `searchCgmExpcByTarget(target)` 헬퍼로 분기, `searchNtsInterpretations`/`getNtsInterpretationText` 두 entry만 추가.
- **본문 조회는 의도적으로 미구현** — 법제처 OPEN API가 국세청은 **목록 조회만 제공**하고 `lawService.do?target=ntsCgmExpc` 본문 endpoint를 제공하지 않음. `getNtsInterpretationText`는 `[NOT_SUPPORTED]` 안내 + 검색 단계의 `법령해석상세링크`(taxlaw.nts.go.kr) 외부 이동 안내만 반환 (LLM 환각 방지).
- **자연어 라우팅 패턴 추가** ([query-router.ts](src/lib/query-router.ts)):
  - `"국세청 양도소득세 해석"`, `"국세청 예규"`, `"법인세 예규 질의"` 등 → `search_decisions(domain=nts)`
  - 패턴: `/국세청\s*(?:법령\s*)?해석/`, `/(?:양도|소득|법인|부가가치|상속|증여|종합부동산)세\s*(?:해석|예규|질의)/`
- **검증**: `domain=nts`, `query="1세대 1주택 양도소득세"` 호출 시 812건 정상 매칭, 모두 `해석기관=국세청`. 1985~2020년대 국세청 직접 회신 해석례.

### Files
- 수정: [src/tools/customs-interpretations.ts](src/tools/customs-interpretations.ts) (target 분기 헬퍼 + nts 함수 2개), [src/tools/unified-decisions.ts](src/tools/unified-decisions.ts) (DOMAINS/LABELS/HANDLERS에 nts 추가), [src/lib/query-router.ts](src/lib/query-router.ts) (nts 자연어 패턴), [src/tool-registry.ts](src/tool-registry.ts) (search_decisions 설명 17→18 도메인)
- 신규 파일: 없음

### Design Note
v4.0의 "신규 도구 최소화, 기존 시스템 재활용" 원칙 유지. 신규 노출 도구 0개로 LLM 도구 선택 혼란 없음. V3_EXPOSED는 그대로 17개.

## [4.0.0] - 2026-05-07

### Added (3개 킬러 기능 한꺼번에 — 도구 추가는 1개로 최소화, 기존 시나리오 시스템 재활용)

#### 1. `impact_map` (신규 도구) — 조문 한 줄의 파급효과 그래프
**왜 필요했나**: 법령은 단독으로 살지 않는다. 한 조문(예: 민법 제103조)은 수십 건 판례에 인용되고, 헌재 결정의 근거가 되고, 자치법규에 묻어가고, 행정해석을 낳는다. 이 "조문 한 줄의 그림자"를 매뉴얼로 추적하면 법무팀 며칠 작업. 한 번에 보는 도구가 없었음.

- **입력**: `lawName` + `jo` (예: `민법`, `제103조`)
- **역방향 탐색** (병렬): 대법원 판례 / 헌재 결정 / 법령해석례 / 행정심판례 / 자치법규
- **정방향 탐색**: 그 조문 본문 안에서 인용된 다른 법령 자동 추출 (`「OO법」` 패턴)
- **출력**: 텍스트 트리 + **mermaid 그래프 코드** (claude.ai에서 시각화)
- **차별점**: 다른 모든 chain은 query 단방향. 이 도구는 "특정 조문 → 영향받는 모든 곳" 역방향 그래프.
- **검증**: `민법 제103조` 호출 시 판례 1건/헌재 6건/조례 2건 정확 추출 (테스트 완료)

#### 2. `time_travel` 시나리오 — 두 시점 본문 자동 diff
**왜 필요했나**: 기존 `compare_old_new`는 직전 개정 신구대조표만. "2024년 1월 vs 2026년 5월" 같은 임의 시점 비교 불가능. 법무팀/공무원/연구자 매뉴얼 비교 작업.

- **호스트**: `chain_amendment_track` (신규 도구 추가 X — 시나리오 확장)
- **신규 파라미터**: `fromDate`, `toDate` (YYYYMMDD)
- **처리**: 연혁(`lsHistory`)에서 두 시점에 시행 중이었던 MST 결정 → 본문 raw JSON 조회 → 조문 단위 자동 diff
- **출력**: 추가(+) / 삭제(-) / 변경(△) 조문 분류 + 변경 전후 본문 미리보기 + 자수 변화량
- **검증**: 개인정보보호법 2020-01-01 vs 2025-11-01 비교 시 제25조(영상정보처리기기→고정형 영상정보처리기기 명칭 변경, 자수 +222), 제26조(업무위탁 +326자), 제32조의2(인증기관 행정자치부장관→보호위원회 변경) 등 정확 검출

#### 3. `action_plan` 시나리오 — 시민 친화 5단계 실행 가이드
**왜 필요했나**: "전세금 못 받았어", "음주운전 걸렸어"처럼 시민이 자연어로 던지는 질문 → 기존 chain은 법령/판례 데이터 덤프만 줌. 시민이 "그래서 뭘 해야 하나"는 모름.

- **호스트**: `chain_full_research`
- **5단계 출력**:
  1. STEP 1 ─ 상황 진단 (적용 법령 자동 식별)
  2. STEP 2 ─ 권리·구제 수단 (실제 판례 시그널 + "패소 사유의 역 = 승소 조건")
  3. STEP 3 ─ 신청 기관 / 기한 (행정규칙 + 해석례)
  4. STEP 4 ─ 필요 서류 / 양식 (별표/별지서식 자동)
  5. STEP 5 ─ 함정 / 주의 (시효·개정·법률구조공단 안내)
- **시민 키워드 → 법률 도메인 자동 매핑**: 전세금→주택임대차보호법 보증금, 해고→근로기준법 부당해고, 음주운전→도로교통법, 체불→근로기준법 임금, 산재→산업재해보상보험법 등 10개 도메인
- **검증**: "전세금 못 받았어" → 주택임대차보호법 자동 매핑, 판례 20건/해석 4건/별표 2건/행정규칙 4건 일괄 수집

### Added (기존 도구 활용 자연어 라우팅)
- **`query-router.ts`** 신규 패턴 3개:
  - `impact_map`: "민법 제103조 영향그래프" / "민법 제103조 인용한 판례" → impact_map 직행
  - `time_travel`: "관세법 2024 vs 2026" / "관세법 시점 비교" → chain_amendment_track + scenario=time_travel + 자동 fromDate/toDate 추출
  - `action_plan`: "전세금 못 받았어" / "음주운전 걸렸어" / "해고 받았어" → chain_full_research + scenario=action_plan
- **specific_article 패턴 양보 로직**: "제N조 영향그래프/파급/인용한 판례" 키워드 동반 시 _skip → impact_map에 위임

### Fixed (4.0 작업 중 발견)
- **`api-client.fetchApi`**: `type=HTML` 응답에 `checkHtmlError`가 무조건 throw 하던 버그 → `type !== "HTML"`일 때만 적용하도록 수정. 기존 `searchHistoricalLaw`/`getHistoricalLaw` 등 lsHistory 기반 도구가 핫픽스(v3.5.5) 이후 깨져있던 것을 함께 복구.

### Changed
- **노출 도구 16 → 17개** (`impact_map` 추가)
- **시나리오 7 → 9개** (`time_travel`, `action_plan` 추가)
- `chainAmendmentTrackSchema`: `scenario` enum 확장(`timeline`, `time_travel`), `fromDate`/`toDate` 필드 신규
- `chainFullResearchSchema`: `scenario` enum 확장(`customs`, `action_plan`)
- `ScenarioContext`: `extras?: Record<string, unknown>` 필드 추가 — 시나리오별 추가 파라미터 전달용

### Files
- 신규: `src/tools/impact-map.ts`, `src/tools/scenarios/time-travel.ts`, `src/tools/scenarios/action-plan.ts`, `src/lib/historical-utils.ts`
- 수정: `src/tools/scenarios/types.ts`, `src/tools/scenarios/index.ts`, `src/tools/chains.ts`, `src/tool-registry.ts`, `src/lib/query-router.ts`, `src/lib/api-client.ts`

### Design Note
v4.0의 핵심 원칙은 **"신규 도구 최소화, 기존 시나리오 시스템 재활용"**. 3개 킬러 기능을 만들면서 새 도구는 1개(`impact_map`)만 추가하고 나머지 2개는 기존 chain의 시나리오로 통합. 도구 수 폭증 방지 + LLM이 도구 선택할 때 혼란 줄이기.

## [3.5.5] - 2026-05-06

### Fixed (긴급 핫픽스: 법제처 API 봇 차단 우회)

법제처 OPEN API가 Node.js 기본 User-Agent(`undici/...`)를 봇으로 분류해 거부하기 시작. fly.dev / Vercel 등 모든 클라우드 호스팅에서 `[EXTERNAL_API_ERROR] fetch failed` 또는 "사용자 정보 검증에 실패하였습니다" XML로 모든 도구가 죽는 현상.

**원인 진단의 함정**: 에러 메시지가 "정확한 서버장비의 IP주소 및 도메인주소를 등록해 주세요"라서 IP 화이트리스트 차단으로 오인되기 쉬움. 실제로는 IP 무관, **User-Agent 검증**. 같은 IP·같은 OC 키라도 브라우저 UA로는 통과, Node fetch UA로는 거부.

### Changed
- **`lib/fetch-with-retry.ts`** — 일반 Chrome 브라우저 UA를 기본 헤더로 주입. 옵션으로 넘어온 `headers`에 `user-agent`가 없을 때만 자동 추가 → 호출자 코드 변경 0
- `LAW_USER_AGENT` 환경변수로 override 가능 (정책 변경 시 빠른 대응)

### Impact
- claude.ai 커스텀 커넥터(`https://korean-law-mcp.fly.dev/mcp?oc=...`)로 사용하던 모든 사용자 즉시 영향 → v3.5.5 배포로 자동 복구
- npm 글로벌 설치(`npm i -g korean-law-mcp`) 사용자도 동일하게 적용
- IP 화이트리스트 / 한국 호스팅 이전 같은 큰 작업 불필요

## [Unreleased] - 2026-04-26

### Docs (issue #29: 플러그인 설치 시 SSH 키 미설정 사용자 지원)
- **README Troubleshooting 섹션 추가** — `/plugin install korean-law@korean-law-marketplace` 실행 시 `git@github.com: Permission denied (publickey)` 에러를 만나는 사용자를 위한 우회 방법 명시
  - **원인**: `marketplace.json`은 표준 github short form(`{ source: "github", repo: "..." }`)을 쓰지만, Claude Code 설치기가 SSH URL로 clone을 시도하는 동작에서 발생. SSH 키가 이미 설정된 개발자에게는 보이지 않는 실패 모드지만, 본 플러그인의 주 타겟인 법률 실무자/비개발자에게는 진입 장벽
  - **추가된 우회 안내**: (1) `git config --global url."https://github.com/".insteadOf "git@github.com:"` 한 줄로 HTTPS 강제 (추천), (2) `ssh-keygen` + GitHub Settings 등록
- 매니페스트 자체는 표준이라 변경 없음 — 향후 Claude Code 측에서 공개 GitHub 저장소에 HTTPS 기본 사용으로 개선되면 안내 제거 예정

## [3.5.4] - 2026-04-18

### Fixed (실사용 피드백: LLM이 조회 실패를 "성공"으로 오인하고 답변 생성)
사용자 피드백: "실사용하면 자꾸 답변 못 찾고 AI가 지맘대로 답변함. 못 찾으면 리턴값을 명확하게 주면 좋겠음."

**근본 원인**: 일부 도구가 조회 실패 시 `isError` 플래그를 설정하지 않거나, 응답 텍스트에 "없습니다"만 포함되어 LLM이 실패를 감지하지 못하고 창작 답변 생성.

### Added (환각 방지 명시 시그널)
- **`[NOT_FOUND]` / `[HALLUCINATION_DETECTED]` / `[API_ERROR]` 머신 파싱 가능 프리픽스** — 모든 실패 응답에 기계적으로 감지 가능한 마커 추가. LLM이 실패를 놓치지 않고 사용자에게 "검색 실패" 보고하도록 유도
- **`lib/errors.ts` `notFoundResponse(message, suggestions?)` 신규 헬퍼** — 특정 리소스 없을 때(조문/별표/파일 등) 일관된 NOT_FOUND 응답 생성
- **모든 "없습니다" 응답에 LLM 경고문 삽입** — "⚠️ LLM은 {조문/판례/법령}을 추측/생성하지 마세요" 문구 표준화

### Changed (isError 누락 수정 — 10+ 위치)
- `tools/annex.ts` — 별표 없음/선택자 매칭 실패/파일 링크 없음 3개 케이스 모두 `isError: true` 추가, `notFoundResponse` 사용
- `tools/verify-citations.ts` — `failCount > 0`일 때 `isError: true` 설정 + 헤더에 `[HALLUCINATION_DETECTED]` 마커 (가장 심각한 버그: 환각 검출됐는데 "검증 성공"으로 오인 가능)
- `tools/law-text.ts` / `tools/article-detail.ts` / `tools/article-history.ts` / `tools/historical-law.ts` — 법령/조문 없음 응답 강화
- `tools/law-linkage.ts` — 연계 법령 없음 응답에 `isError: true` 추가
- `tools/autocomplete.ts` / `tools/admin-rule.ts` / `tools/comparison.ts` — `isError: true` 누락 수정
- `tools/precedent-summary.ts` / `tools/precedent-keywords.ts` / `tools/knowledge-base.ts` / `tools/kb-utils.ts` / `tools/ordinance.ts` — NOT_FOUND 마커 + LLM 경고문
- `tools/precedents.ts` / `tools/treaties.ts` / `tools/ordinance-search.ts` — 검색 실패 응답 강화

### Changed (체인 도구 부분 실패 투명화)
- `tools/chains.ts` `secOrSkip()` — 에러 snippet 80자 → 200자 확장, 섹션 제목에 `[NOT_FOUND / FAILED]` 마커 + LLM 경고문 삽입
- 모든 silent-drop 패턴(`if (!result.isError) parts.push(sec(...))`) 제거 → `parts.push(secOrSkip(...))`로 일괄 전환. 체인 중 일부 단계가 실패해도 "왜 빠졌는지" 명시 노출
- `noResult()` — NOT_FOUND 마커 + "체인 실행 중단 — LLM은 추측 금지" 지시문 추가

### Impact
- LLM이 실패 응답을 기계적으로 감지 가능해져 창작/환각 답변 방지
- 체인 도구가 부분 실패해도 사용자에게 "어떤 데이터가 왜 빠졌는지" 명시적으로 노출
- 특히 `verify_citations`의 `isError` 누락은 환각 검출의 의미를 무력화하던 심각한 버그였음

## [3.5.3] - 2026-04-18

### Fixed (verify_citations 실제 검증 후 3개 치명 버그 수정)
실제 법제처 API로 5건 테스트 → 3건 false negative 발견 → 근본 원인 수정:

- **법제처 searchLaw 부분매칭 오매칭** — "민법" 검색 시 "난민법"이 1위로 리턴되던 문제. 기존 `chains.ts`의 `findLaws`/`scoreLawRelevance`가 이미 이 문제를 해결하고 있었으나 verify_citations가 재사용하지 않고 자체 로직으로 중복 구현했던 것. 공용 모듈 `lib/law-search.ts`로 추출하여 chains/verify 모두 재사용
- **원숫자(①②③…) 항번호 파싱 실패** — 법제처 API가 `항번호`를 "① "/"② " 형태로 리턴하는데 기존 `parseInt(raw.replace(/[^\d]/g, ""))`가 유니코드 원숫자를 제거하여 NaN. 근로기준법 제60조 제1항이 실존함에도 "최대 제0항" 오판정. `lib/article-parser.ts`에 `parseHangNumber()` 유틸 추가 (원숫자 매핑 + 일반 숫자 fallback)
- **짧은 법령명("상법") 검색 실패** — 법제처 lawSearch API가 "상법" 검색 시 부분매칭으로 "1980년해직공무원의보상등에관한특별조치법" 등을 먼저 리턴, 실제 "상법"은 결과 34번째. 기본 display=20으로는 못 찾음. `apiClient.searchLaw`에 display 파라미터 추가 + `findLaws`에 `searchDisplay` 옵션 추가, verify_citations에서 `searchDisplay=100`으로 호출

### Changed
- `lib/law-search.ts` 신규 — `findLaws`, `scoreLawRelevance`, `parseLawXml`, `stripNonLawKeywords`, `NON_LAW_NAME_RE`, `LawInfo` 타입을 `chains.ts`에서 추출하여 공용화
- `tools/chains.ts` — 중복 정의 제거, `law-search.ts` import
- `tools/verify-citations.ts` — 자체 법령 검색 로직 제거하고 `findLaws` + `parseHangNumber` 재사용 (중복 구현 금지 원칙)
- `lib/api-client.ts` — `searchLaw(query, apiKey, display?)` 시그니처 확장 (backward compatible)

### Verified
실제 법제처 API로 5건 테스트 — 5/5 정확 판정:
- ✓ 민법 제750조(불법행위) / 근로기준법 제60조 제1항(연차휴가) / 도로교통법 제44조(음주운전) 실존
- ✗ **상법 제401조의2 제7항 — 제7항 없음(최대 제2항) 환각 정확 탐지**
- ✗ 형법 제9999조 — 해당 조문 없음(존재 범위: 제1조~제372조)

## [3.5.2] - 2026-04-18

### Changed
- **kordoc 2.3.0 → 2.4.0** — 별표/서식 파싱 엔진 업데이트
  - 영향: `src/lib/annex-file-parser.ts` (HWP/HWPX/PDF 통합 파서)
  - API 호환 (minor bump, `parse`/`ParseResult`/`FileType` 시그니처 유지)

## [3.5.1] - 2026-04-18

### Removed (Dead Code)
- **lite/full 프로필 체계 완전 제거** — V3_EXPOSED 16개 고정 노출 도입 후 실질 미사용 상태였던 죽은 코드 정리
  - `lib/tool-profiles.ts`: `LITE_TOOLS` set(15개 엔트리), `parseProfile()`, `filterToolsByProfile()`, `ToolProfile` 타입 제거 (37줄 순감)
  - `tool-registry.ts`: `registerTools(server, apiClient, profile?)` → `registerTools(server, apiClient)` 시그니처 단순화. `filterToolsByProfile` import 제거
  - `index.ts`: `MCP_PROFILE` 환경변수 처리 제거, `parseProfile` / `ToolProfile` import 제거, `createServer(profile?)` → `createServer()`
  - `server/http-server.ts`: `?profile=` 쿼리 파라미터 파싱 제거, `createServer(profile)` 호출부 단순화
- 헬스 엔드포인트(`GET /`) 응답에서 거짓 `profiles: { lite, full }` 필드 제거 → `tools: { exposed: 16, total: 92 }` 정확 안내로 교체
- `mcp-lite: "/mcp?profile=lite"` 엔드포인트 안내 제거 (원래부터 무시되던 값)

### Why
- v3 통합 후 `tool-registry.ts`가 `V3_EXPOSED.has(t.name)`로만 필터링하고 `filterToolsByProfile`은 import만 되어 있고 호출 안 됨 → `?profile=lite`든 `?profile=full`든 **완전히 동일하게 16개 도구** 반환
- 헬스 엔드포인트는 여전히 `lite: "14 tools"` 안내 문구 노출 → 클라이언트에 **거짓 정보 전달** 중
- 배포된 상태에서 breaking change 아님: 기존 `?profile=lite` 호출은 지금도 이미 무시되던 값이므로 동작 변화 없음

### How to apply
- STDIO 모드: `MCP_PROFILE` 환경변수 이제 무시됨 (설정 안 해도 됨)
- HTTP 모드: `?profile=` 쿼리 파라미터 이제 무시됨 (모든 클라이언트 동일 16개 도구)
- 문서/튜토리얼에서 lite/full 언급 있으면 제거 권장 (CHANGELOG 역사 맥락은 유지)

## [3.5.0] - 2026-04-18

### Added (Killer Feature)
- **`verify_citations`** — LLM 환각 방지 인용 검증 도구 (신규 `src/tools/verify-citations.ts`, ~200줄):
  - 입력 텍스트에서 `제N조`/`제N조의M`/`제N조 제K항` 형식 인용을 정규식으로 자동 추출
  - 직전 30자 lookback으로 법령명(`XX법/법률/시행령/시행규칙/규칙/규정/조례`) 역추적
  - 각 인용에 대해 `search_law` + `get_law_text`(jo) 병렬 호출로 실존·내용·항 번호 교차검증
  - 결과: ✓(실존) / ✗(없음, 존재 범위 힌트) / ⚠(법령명 불명확/일시 실패)
  - `V3_EXPOSED`에 노출 — 15개 → 16개 도구. 자연어 라우팅(`인용검증`·`조문실존` 등)에도 연결
  - 타겟: 법률AI 서비스, 로펌, 법학생, 계약서 검토 — ChatGPT/Claude 답변의 조문 인용 환각 실시간 탐지

### Fixed (Critical)
- **`get_decision_text` `full` 옵션이 12개 도메인에서 묵묵히 무시되던 문제** — `unified-decisions.ts`는 `args.full`을 전달했지만 tax_tribunal/customs/ftc/pipc/nlrc/acr/appeal_review/acr_special/school/public_corp/public_inst/treaty/english_law/interpretation 핸들러가 스키마에 `full` 필드가 없어 탈락. 이제 `compactLongSections()` 후처리로 12개 도메인에도 축약 적용 (`precedent`/`constitutional`/`admin_appeal`은 자체 적용되므로 skip 리스트)
- `decision-compact.ts:132` `densifyPrecedentRefs` 날짜 정규식에 경계 가드(`(^|[\s,(\[;/])`) 추가 — 문서 중간 `제2020. 3. 26. 개정` 같은 숫자 오탐 방지
- `decision-compact.ts:59` `compactBody` TAIL 경계에서 `". "` 제외 + `"한다. "` / `"라. "` 추가 — `"1,234.00 원"`·`"No. 3"` 오탐 방지
- `decision-compact.ts:166` `stripRepeatedSummary` 종료점 탐지 강화 — 요약 끝 60자 매칭으로 실제 end 계산, 매칭 실패 시 보수적으로 `s.length`만 제거 (요약 뒤 본문 같이 날아가는 사고 방지)

### Fixed (Security)
- `fetch-with-retry.ts:72` 타임아웃/네트워크 에러 메시지에 API 키 포함 URL이 그대로 노출되던 문제 — `maskSensitiveUrl()` 신규로 `OC=***`·`apiKey=***` 등 마스킹 후 throw
- `http-server.ts:136` `console.error("[POST /mcp] Error:", error)`에서 원본 에러 로깅 시 키 노출 가능성 — `scrubError()` 경유로 통일
- `http-server.ts:19` `trust proxy true` → `TRUST_PROXY` 환경변수 (기본 `1`, 첫 프록시만 신뢰). `X-Forwarded-For` 스푸핑으로 rate limit 우회 + 메모리 DoS 위험 차단
- body limit 환경변수화(`MCP_BODY_LIMIT`, 기본 `100kb`)

### Changed (UX)
- **체인 도구 8개 description 구체화** — LLM이 `search_law` vs `chain_law_system` 중 선택 가능하게. 각 체인에 구체적 사용 예시(`"관세법 체계"`, `"음식점 영업정지 근거"`, `"서울시 주차 조례 전국 비교"` 등) + 언제 쓰지 말아야 하는지 명시
- `search_law`/`search_ordinance`/`search_precedents` 결과에 "💡 다음: get_law_text(mst=...)" 형태 **다음 단계 힌트** 추가 — 검색→조회 흐름 자동 유도
- `search_law` 0건 시 **`expandLawQuery` 자동 재시도** — 약칭(`"근기법"` → `"근로기준법"`)/오타 확장으로 성공률 상승
- `query-router.ts` **5개 패턴 추가** — `verify_citations`(인용검증 키워드), 법령 비교(`vs`/`와/과 차이`), 시간 필터(`최근 N년 개정`), 민사책임(`손해배상`/`과실비율`), 계약서 검토(`독소조항`)
- `tool-profiles.ts` **`TOOL_ALIASES`** 맵 추가 — `"조세심판원"` → `search_tax_tribunal_decisions`, `"김영란법"` → 청탁금지법 등 27개 한국어 별칭. `discover_tools`가 별칭 매칭하면 카테고리/도구 즉시 반환

### Why
- 프로덕션 리뷰(code-reviewer + security-reviewer + UX 갭 분석) 결과 Critical 1 / 보안 High 2 / 품질 High 3 / UX 갭 5 발견
- v3.4.0 "판례 응답 토큰 74% 감축" 기능이 12개 도메인에서 무효화된 채 배포된 상태 — 즉시 핫픽스
- 2026년 AI 시대 법령 RAG 차별화 포인트는 **환각 방지**. `verify_citations`가 법제처 공식 API만 가능한 killer 기능

### How to apply
- `verify_citations` 사용: LLM 답변/계약서/판결문 텍스트를 `text`로 넘기면 자동 인용 추출 + 병렬 검증
- `full` 옵션은 14개 도메인 전체에서 정상 작동 (이제 `full=true` 보내면 실제로 전문 반환)
- API 키 로그 유출 방지를 위해 프로덕션 환경은 `TRUST_PROXY=1` 명시 설정 권장 (Fly.io는 기본값으로 충분)
- 별칭 매칭은 `discover_tools(intent="조세심판원")` 같은 자연어 입력에서 자동 적용

## [3.4.0] - 2026-04-16

### Added
- `lib/decision-compact.ts` — 판례/헌재/행심 응답 토큰 최적화 유틸 신규:
  - `compactBody(text, opts)` — 본문 계단식 축약 (앞 800자 + 중략 + 뒤 400자, 문장 경계 가드)
  - `densifyLawRefs(text)` — 참조조문 괄호 설명 제거 + 구분자 정리
  - `densifyPrecedentRefs(text)` — 참조판례 "선고/판결" 제거 + 날짜 공백 압축
  - `stripRepeatedSummary(body, summaries)` — 본문 앞쪽에 반복 기재된 판시/요지 제거
- `get_decision_text`에 `full?: boolean` 파라미터 추가 — `true`=전문 그대로, 미지정(기본)=축약
- 개별 핸들러(`get_precedent_text`, `get_constitutional_decision_text`, `get_admin_appeal_text`)에도 동일 파라미터 전파

### Changed
- **판례 응답 토큰 평균 -74%** (실측: `b4875a3` vs `69f6918`, 3개 도메인 × 8건 고정 ID):
  - 판례: 5,230 → 3,049 chars (-42%)
  - 헌재: 8,368 → 1,703 chars (-80%)
  - 행심: 8,429 → 1,491 chars (-82%)
  - 긴 결정례(15,000자 이상)에서 80~89% 절감 — 판시/요지/주문은 full 유지, 본문만 축약
- **ListTools 페이로드 -14%** (9,671 → 8,296 bytes, 344 토큰↓):
  - `chain_*` 8개 description 간결화 (`[⛓체인]` → `[⛓]`, 예시 구문/메타 문구 제거)
  - `search_decisions`/`get_decision_text` 필드 describe 다이어트 (17 도메인 이중 기재 제거)
  - `discover_tools`/`execute_tool` description 축약

### Why
- 3개 MCP 동시 운용 환경에서 판례 호출 1회가 12.5k 토큰 상한(50KB)을 먹어 컨텍스트 블랙홀화
- 법령 RAG 관점에서 판시사항·판결요지·주문은 규범 재사용 핵심이라 full 유지, "이유" 전문은 사안별 사실관계 나열이라 축약해도 손실 미미
- 중략된 구간은 `full=true`로 재호출 가능 — backward compatible

### How to apply
- 적용 도메인: `precedent`, `constitutional`, `admin_appeal` (판례/헌재/행심)
- 해석례·기타 짧은 도메인은 미적용 (원래 짧음)
- 사용자는 자연어로 "전문 그대로", "full로 다시"라고 요청하거나 LLM이 description 보고 자동 판단
- 응답 중간의 `⋯ 중략 N자 (full=true로 전문 조회) ⋯` 마커가 힌트

## [3.2.2] - 2026-04-12

### Added
- `get_annexes`를 V3_EXPOSED에 추가 (14개 → 15개 노출). `discover_tools` → `execute_tool` 왕복 없이 별표/서식 직접 조회 가능
- `chains.ts` `detectExpansions`: 환불·반환·배상·수강료·이용료·회비·N만원 키워드 추가 — 소비자분쟁 질의에서 `chain_full_research`가 별표 자동 포함

### Why
- 트레이스 `ld-1775959823220` (헬스장 1년권 환불, 79s) 분석 결과: 별표 3의2 조회를 위해 `discover_tools` × 2 + `execute_tool(get_annexes(...))` 헛발질로 ~15초 손실
- 노출 기준: 체인 도구가 fallback으로 자주 호출하는 종착 도구 + discover→execute 왕복으로 5초+ 손실
- `tool-registry.ts` 상단 주석에 제거 금지 경고 명시

## [3.0.2] - 2026-04-08

### Added
- `npx korean-law-mcp setup` — 대화형 설치 마법사 (API 키 입력 → 8개 클라이언트 자동 설정)
- 지원 클라이언트: Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, Gemini CLI, Zed, Antigravity
- STDIO 모드에서 `MCP_PROFILE` 환경변수 지원

### Fixed
- API 커버리지 수치 39개 → 41개로 정정 (실제 사용 target 기준 재집계)

## [3.0.1] - 2026-04-08

### Added
- get_ordinance: `jo` 파라미터 추가 — 특정 조문 본문 직접 조회 가능 (#19)
- 대형 조례(20개 초과) 목차 반환 시 `jo` 사용법 안내 메시지 추가

### Fixed
- get_ordinance: 조문 필터링을 조제목 텍스트 매칭에서 조문번호(JO 코드) 기반으로 변경 — API 응답의 조제목에 조번호가 없는 구조 대응
- get_ordinance: "제20조" 검색 시 "제20조의2" 등 의X 조문이 잘못 매칭되는 문제 수정

## [2.2.0] - 2026-04-01

### Added
- 23개 신규 도구: 조약(2), 법령-자치법규 연계(4), 학칙/공단/공공기관(6), 특별행정심판(4), 감사원(2), 약칭(1), 행정규칙 신구대조(1), 조항호목(1), 문서분석(1), chain_document_review(1)
- date-parser: 자연어 시간 표현 → YYYYMMDD 변환 (10개 패턴)
- document-analysis: 8종 문서유형 분류, 17개 리스크규칙, 금액/기간 추출, 조항 충돌 탐지
- 판례/해석례 날짜 필터 (fromDate/toDate)

### Changed
- 에러 처리 통일: 40개 도구의 인라인 에러 → formatToolError 전환
- 중복 XML 파서 6개 → 공용 parseSearchXML 통합
- cli.ts 분리: cli-format.ts + cli-executor.ts + cli.ts (689줄 → 443+181+227)
- annex.ts: AnnexItem 타입 정의, any 12회 제거

### Security
- sse-server.ts: CORS * → CORS_ORIGIN 환경변수 기반
- sse-server.ts: API 키 쿼리스트링 경로 제거 (헤더만 허용)
- sse-server.ts: 보안 헤더 추가 (X-Content-Type-Options, X-Frame-Options, Referrer-Policy)
- sse-server.ts: 세션 ID 로그 마스킹 (첫 8자만 출력)

### Fixed
- 조약 XML 아이템태그 대소문자 (trty→Trty), 본문 JSON 키 (BothTrtyService)
- 연계 fetchApi type 기본값 제거 (type=XML 시 500 발생)
- api-client.ts: type 파라미터 미지정 시 생략

- 총 도구 수: 64 → 87

## [1.9.0] - 2026-03-15

### Fixed
- HWP 구형 파서: `controls` 내 테이블(표) 추출 지원
  - `hwp.js`의 `paragraph.controls[].content` 경로에서 테이블 구조(rows/cells) 탐색
  - 기존에는 `paragraph.content`만 탐색하여 표 형식 HWP 파싱 실패

## [1.8.1] - 2026-03-15

### Changed
- MCP 도구 스키마 최적화: description 압축 + apiKey 은닉

## [1.8.0] - 2026-03-10

### Added
- 체인 도구 7개: chain_law_system, chain_action_basis, chain_dispute_prep, chain_amendment_track, chain_ordinance_compare, chain_full_research, chain_procedure_detail
- get_batch_articles: `laws` 배열 파라미터로 복수 법령 일괄 조회 지원
- search_ai_law: `lawTypes` 필터로 법령종류별 결과 필터링
- truncateSections(): 체인 도구 섹션별 응답 크기 최적화
- truncateResponse summary 모드: 긴 응답 자동 요약
- unwrapZodEffects: .refine() 스키마의 MCP 호환성 개선
- 구조화된 에러 포맷: [에러코드] + 도구명 + 제안

### Changed
- formatToolError: ZodError 자동 감지, 구조화된 출력
- toMcpInputSchema: ZodEffects unwrap 후 JSON Schema 변환
- 총 도구 수: 57 → 64
