search_decisions·get_decision_text에 노동 행정 도메인 3종 추가 — 법제처 OPEN API 가 이미 제공하지만 이 서버에서 닿지 않던 자료다moel: 고용노동부 법령해석(질의회시,moelCgmExpc, 9,636건). 관세청·국세청 1차 해석과 응답 구조가 같아 같은 핸들러를 쓴다. 목록·본문에안건번호(예: 근로기준정책과-2668)를 싣고,options.search=2로 본문검색. 회답·이유가 곧 해석 본문이라 해석례처럼 계단식 축약하지 않는다iaciac: 산업재해보상보험재심사위원회 결정문(934건). 목록 제목 태그가<사건>이라 기존 위원회 파서가 "(제목 없음)"으로 그리던 것을 읽는다. 본문에 사건분류(대>중>소)·원처분기관·쟁점·청구취지를 싣는다eiac: 고용보험심사위원회 결정문(118건). 본문의 화면 숨김 요약 span 을 버리고,<각주>·<img>를 [각주N]·[이미지] 표지로 바꾸며,각주목록을 "[1] 내용" 줄로 그린다
- 라우팅: "고용노동부/노동부 해석·질의회시" →
moel, "산재 재심사" →iaciac, "고용보험 재심사" →eiac. 일반 "해석례"는 기존대로 법제처 해석례
get_law_text로 부칙을 볼 수 없던 문제.jo="부칙"이면 그 버전의 부칙 원문(시행일·경과조치)을 최근 공포부터 낸다 — 헌법 부칙 <제10호, 1987.10.29> 제1조~제6조, 민법 부칙 37건. 목차·전문 끝에 부칙 건수와 조회 방법을 붙인다. 종전엔 응답에 부칙이 있어도 조문만 그려,applicable_law가 안내하던 "get_law_text 로 부칙 확인"이 막다른 길이었다
지난달·저번달·N개월 전·최근 N개월이 월말에 다음 달로 넘어가 엉뚱한 달을 검색하던 문제. 목표 달에 없는 날짜는 그 달 말일로 맞춘다 (3월 30일의 "지난달" → 2월) (#169, PR #171)- 사건번호 일련번호 자릿수를 라우팅·인용 추출·
cite_check정규화가 같은 규칙(최대 8자리, 긴 번호의 앞부분만 읽지 않음)으로 본다. 8자리 일련번호가 인용 검증에서 조용히 빠지거나 9자리 번호가 다른 사건으로 잘리던 문제 (#170, PR #172)
- Claude 플러그인에 법령 조회 스킬 추가 — API 응답 속 문장은 지시가 아닌 데이터로 다룬다
- 의존성 lock 갱신 (MCP SDK 1.32.1 등)
- 판례 검색범위
search="both": 판례명 검색과 본문검색을 함께 돌려 판례ID로 중복을 빼고, 건마다 어느 검색에서 맞았는지 표시한다.search_decisions(domain="precedent", options={search:"both"}). 기본 동작(판례명 우선)과 사건번호 exact 조회는 그대로다 (#167) - 판례명 검색이 보정 없이 3건 이하로 끝나면 본문검색 재검색 안내를 붙인다. 실측: "학원강사 근로자" 판례명 1건 · 본문 70건, "녹음파일 사본" 판례명 1건 · 본문 82건
search_decisionsoptions 로 온search:"2"(문자열)가 판례명 검색으로 처리되고 본문 폴백도 건너뛰던 문제search_decisions도구·options 설명에 판례 검색범위(search)를 노출한다
전체 기능을 새 API 표본과 검색·상세·분석 연결 경로로 다시 검수했다. 재현한 오류를 수정하며 기능·의존성은 추가하지 않는다.
- 조문 범위 인용에서 법령명과 가지번호를 함께 비교한다. 타법 범위와 범위 밖 가지를 요청 조문의 인용으로 인정하지 않는다
- 판례 상세는 본문 없이 메타데이터·참조만 있는 응답을 미확인 오류로 반환하고 캐시에 저장하지 않는다. 취소된 요청은 캐시 적중 시에도 중단한다
- 조문 코드로 판례를 검색할 때 자연어 조문번호를 전달하고 명시적 조문·타법 불일치를 제외한다. 판례 요약의 핵심 내용 부재와 한글 키워드 경계를 처리한다
- 공백형 사건번호가 법령 인용과 섞여도 검증 대상에 포함한다. 미검증·존재하지 않는 판례를 누락해 전체 검증 완료로 표시하지 않는다
- 결정문의 긴 섹션을 각각 축약하고 주문·요지·참조·법적 근거를 보존한다. 특별행정심판의
관계법령필드를 표시한다 - 법령 체계도에서 상위 법률과 시행령 아래의 시행규칙을 보존하고 실제 부모·자식 관계로 표시한다. 위임조문 트리의 표본·생략 안내를 유지한다
- 자리수 초과 JO가 다른 조문으로 잘리지 않도록 본문·상세·연혁에서 검증한다. 별표 가지 코드가 무번호 단일 항목으로 대체되지 않게 한다. 연계 목록의 설명과 실제 반환 대상, 조회 페이지 내 필터 한계를 일치시킨다
- 과거 시행본 조회가 빈 응답일 때 시행일이 다른 MST 본문으로 대체해 시점 비교를 확정하지 않는다
- 생활법령의 조문번호와 한글 개정 주석을 보존하고 용어 상세의 HTML 엔터티를 해독한다. 해석례·자치법규 공식 링크 경로를 바로잡는다
- MCP 직접·프록시 호출에서 인자로 제공한 인증키를 출력 마스킹에 포함한다. CLI 출력·오류도 마스킹하고 공통 입력 길이 제한을 적용한다
검수 범위·재현·검증 기록: 전체 기능 재검수.
전체 기능의 구현과 호출 경로를 검수했다. 노출 도구·입력 계약·의존성은 유지하며 재현한 오류를 수정한다.
- 판례 인용 추적은 공백이 포함된 2·4자리 연도 사건번호를 정규화한다. 사건번호 검색 결과가 대상과 다르면 다른 판례로 대체하지 않는다
- HTML 판례 복구에서 다음 hop 전에 이전 본문을 취소한다. 요약은 참조조문·전문·이유 섹션 경계를 지키고, 본문 중 메타데이터로 원래 사건 정보를 덮지 않는다
- 조문 상세·연혁·배치·행위시법은 JO 코드와 가지번호를 정확히 선택한다. 요청 조문이 전부 없으면 오류로 반환한다
- 별표 번호가 알려진 단일 항목이 요청 번호와 다르면 반환하지 않는다. 별표 정본·부칙 보조 조회의 취소와 호출 예산 소진을 전파한다
- 행정규칙 연혁의 현행·시행예정·폐지 표기를 실제 시행일 기준으로 판정한다
- 인용조문 3단비교(
knd=1)의 실제 응답 루트와 하위 조문 목록을 처리한다. 장·절 헤더를 제외하고 법률 본문과 연결된 하위 조문을 표시한다 - 대량 법령 검색의 마지막 배치에서도 취소를 전파한다. 부분매칭 후보를 요청 법령의 변경 없음으로 확정하지 않는다
- 조세심판·관세·소청·특별행정심판·기관 규정·조약·영문법령의 배열·wrapped 필드를 보존한다. 개인정보위·권익위 의결서 봉투와 노동위 판정 필드를 처리하고 기관 규정의 실제 기본정보를 표시한다. 노동위 등록일은 결정일과 구분한다
- 통합 상세 조회의 명시적 ID를 options가 덮지 못하게 한다. 영문법령 공포일을 시행일로, 편·장·절 헤더를 조문으로 표시하지 않는다
- 인용 검증 상한을 넘긴 법령·판례를 미검증으로 집계하고 전체 검증 표시를 하지 않는다. 취소 후 새 검증을 시작하거나 성공 결과를 반환하지 않는다
- 조례 정비 레이더는 날짜가 불명확하면 반영 완료로 판단하지 않는다.
같은 법은 앞에서 명시된 법령에 연결하고 무관한 시행령을 만들지 않는다 - 시점 비교는 달력 날짜를 검증하고 역방향 비교의 전부개정 경고를 보존한다. 조문 비교에서 한글 개정 주석을 보존한다
- 문서 위험 분석의 내부 500자 조문 절단을 제거해 조문 뒤쪽의 위험 문구도 분석한다. 출력 발췌·조문 수 상한은 유지한다
- 시나리오의 일시 조회 실패를 누락하지 않고 실패 섹션으로 표시한다. 경쟁법 분쟁 준비는 공정위 검색·상세 경로를 사용한다
- HTTP CORS에 MCP 프로토콜·지원 키 헤더를 허용하고 중복 OC 쿼리를 거부한다. 이미 취소된 fetch는 호출·예산 차감 전에 중단한다. 용어 매핑 폴백에서 명시적 키를 유지한다
- 공식 법령·자치법규 링크의 경로와 식별자를 바로잡고 URL 인자를 인코딩한다. 영문 링크는 확인 가능한 영문법령 검색 화면으로 제공한다
- CLI boolean 옵션을 보존하고 완전한 JSON 입력에 중복 필수 플래그를 요구하지 않는다. JSON의 API 키를 사용하고 텍스트 도구 목록에도 카테고리 필터를 적용한다
검수 범위·재현·검증 기록: 전체 기능 프로덕션 리뷰.
프로덕션 조회 경로의 정확성·실행 한도·메모리 사용을 점검했다. 기능 추가 없이 버그 수정과 성능 개선만 반영한다.
- 사건번호만 입력한 판례 검색은 해당 사건을 우선 조회한다. 번호 접두가 같은 다른 사건을 제외하고, 명시적 본문검색(
search=2)은 인용 검색을 유지한다 - 판례 fallback 관련성은 개별 판례에서 검증한다. 서로 다른 판례의 키워드를 합쳐 통과시키지 않으며, 요청 기간 밖 결과도 검증한다
- 판례 상세·검증·근거 조회는 취소와 호출 예산 소진을 전파한다. 취소 뒤 HTML 복구나 다른 검색어 재시도를 시작하지 않는다
- 판례 인용 추적은 정확한 대상 사건을 우선한다. 부분 입력으로 특정한 사건은 그 사건번호로 후속 판결을 검색하고, 정밀 스캔을 끄면 변경·폐기 여부를 미확정으로 표시한다. 사건번호 접두가 같은 다른 판례를 변경 신호로 오인하지 않는다
- 판례·헌재·행정심판·위원회 결정문·법령해석례의 배열과
#text/_JSON 필드를 본문·메타데이터에서 보존한다 - 업스트림 헤더 수신 뒤 검사와 본문 읽기에도 전체 데드라인을 적용한다. 본문이 조금씩 계속 오는 경우도 한도를 넘기지 않는다. 취소·HTTP 오류·안티봇 복구에서 본문 정리 완료를 기다리며 멈추지 않는다
- 행위시법 비교는 같은 MST의 서로 다른 시행일 슬라이스도 대조하고 목 본문을 보존한다. 존재하지 않는 달력 날짜는 거부한다
- 조문 조회는 업스트림이 JO 조건을 무시해도 요청 조문만 반환한다. 복수 조문 조회의 숫자 조문번호와 마지막 처리 중 취소를 처리한다
- 조문 개정 이력은 정확히 일치하는 법령이 없으면 다른 법령의 첫 검색 결과를 채택하지 않는다
- 별표 번호의 숫자·가지 경계를 확인한다. 별표 1에 10/1의2, 1의2에 1의20/1~5 묶음이 선택되지 않는다
- 체인 갈래 완료·실패 시 취소 리스너를 해제한다. CLI JSON 응답은 상세 조회 실패를
isError와 종료 코드에 반영한다
- 판례의 파싱된 원문을 전용 20건 캐시에 보관한다. full/축약 조회와 인용 추적이 공유하며, 같은 판례의 JSON 파싱과 상세 재요청을 줄인다. 원시 JSON 사본은 보관하지 않는다
- 복수 조문 조회 안에서 동일 법령의 진행 중 전문 조회를 공유한다. 동일 법령 동시 2회 요청은 업스트림 1회로 처리하고, 완료 후 진행 중 참조를 해제한다
- 상태 코드가 이미 오류인 HTTP 응답은 본문을 읽고 버퍼링하지 않고 취소한다
검토 범위와 회귀 검증 근거: 프로덕션 리뷰.
최근 릴리스(4.14.0~4.15.1)를 다시 감사해 조용히 틀린 답을 내거나 느려지던 곳을 고쳤다. 노출 도구와 인자는 그대로다. 테스트 1119 통과.
- 도구 출력의 다음 단계 안내: 판례 검색 결과가
get_precedent_text처럼 도구 목록에 없는 도구를 부르라고 안내해, 도구 호출이 약한 모델은 거기서 멈췄다. 22곳을get_decision_text(domain="precedent")같은 노출 도구나execute_tool(tool_name, params)형식으로 바꾸고, 같은 실수를 막는 게이트 테스트(tool-hints.gate.test.ts)를 넣었다. 없는 도구compare_law안내도 고쳤다 - 판례 본문의
<br/>: 판시사항·판결요지·전문에 줄바꿈 태그가 그대로 나왔다(616245: 77개). 줄바꿈으로 편다. 전문 축약이 날짜 ("2020. 12. 28.") 중간에서 끊기거나 꼬리가 ". "로 시작하던 경계도 고쳤다 - 본문 캐시 키: mst 와 lawId 를 한 공간에 둬서 같은 숫자의 다른 법령이 캐시에서 나갔다(001706: mst=사방사업법, lawId=민법). HTTP 모드에선 다른 이용자 응답까지 오염됐다
get_law_text기준일 보정: 503·타임아웃에도 보정을 돌아 시도와 시간이 두 배였다(무응답 90초). 보정은 그 시행일 버전이 없을 때만 한다. 일시 장애 재시도를 되살리고, 보정한 버전에 조문이 없으면 그 사실을, 기준일에 이미 폐지된 법령은 폐지일과 직전 버전을, 보정 결과가 현행·시행예정이면 그렇다고 밝힌다- 행정규칙 기준일 버전: 시행일이 가장 늦은 본을 골라, 먼저 발령되고 늦게 시행된 개정본이 뒤 발령본을 덮었다(스프링클러설비의 화재안전기준 2014.1.1.: 삭제된 30층 이상 조항이 살아 있었다). 시행 중인 것 가운데 나중 발령본을 고른다
- 폐지 후 같은 이름으로 재제정된 법령: 법령ID 계보가 신법만 줘서 근로기준법 1995.5.1.을 "시행 전"이라 답했다. applicable_law· time_travel·get_annexes(date)·get_law_text(efYd)·search_historical_law 가 동명 구법 연혁까지 본다
get_historical_law: 시행일이 아닌 efYd 에 4회 재시도 뒤 오류였다. 확인 1회 뒤 그 MST 본문으로 물러선다. 0 채움 없는 법령ID (1638)로 계보가 비던 것도 고쳤다- 체인 시간 한도: 기반 법령 탐색이 늦으면 이미 받은 AI·해석례·판례까지 버렸다(full_research 111자). 판례 상세가 늦으면 받은 검색
목록까지 빠졌다. 받은 섹션은 싣고 못 받은 자리만 표시한다. 시간 한도 밖이던 document_review 와 get_law_statistics 도 같은 한도
(
MCP_CHAIN_DEADLINE_MS)를 따른다 - 인용 검증: 옛 법령명 인용에서 호출 예산이 바닥나면 이미 검증한 인용까지 응답 전체가 오류로 바뀌었다. 폐지 법령 탐색은 예산 소진을 "없음"으로 삼켜 지어낸 법령으로 단정했다. 지어낸 법령명이 많은 문서에선 실재 인용 검증이 예산에 밀렸다. 특정되는 법령의 인용부터 검증한다
- 행정규칙 부분 조회: 편이 있는 규칙(금융투자업규정)에서 장 조회가 다른 편의 같은 번호 장을 섞었다.
chapter:"제4편 제3장"처럼 편을 가리고, 겹치면 후보를 보여 준다. keyword 가 부칙·별표를 안 보고 "없음"으로 단정하던 것, 발췌가 앞 2,500자 고정이라 매칭 줄이 안 보이던 것, 응답 한도를 낮추면 page 사이가 영구히 빠지던 것, 신구대조 폴백이 일시 오류를 "데이터 없음"으로 말하던 것을 고쳤다
- kordoc 4.18.2 고정: 범위 지정(^)이라 lockfile 없이 설치하는 배포 이미지가 검증 안 한 최신판을 받았다. 정확한 버전으로 고정한다 (별표 550건 코퍼스 출력이 4.17.0 과 같음). PDF 자동 OCR 은 끈다. OCR 모델이 깔린 환경에서만 파일당 최대 7초·메모리 2배로 결과가 갈렸다
- 행정규칙 캐시에 원문 XML 대신 파싱한 문서를 둔다. 큰 규칙(보험업감독업무시행세칙)의 캐시 적중 조회가 1초대에서 1ms 이하로
- 판례 원문을 캐시해 같은 판례 상세를 한 번만 받는다. 같은 기준일로 조문 여러 개를 볼 때 법령 계보를 다시 받지 않는다
별표를 원본 한글 문서에서 보이는 모습대로 읽는다(kordoc 4.17.0). 노출 도구와 인자는 그대로다. 테스트 1042 통과.
-
별표 본문 = 원본의 보이는 모습: 법제처 별표 272건 중 103건이 본문 전체를 테두리가 안 보이는 틀 표에 담아, 별표 한 장이 통째로
거대한 HTML 표로 나왔다(대기환경보전법 시행령 [별표 8]: 17열 29행 한 장). 이제 선이 없는 틀은 문단으로, 선이 보이는 부분만 표로 낸다.
같은 별표가 소제목 사이 표 5개로 나온다. 칸 두 개와 가로선으로 조립한 분수는 수식이다(할부거래법 시행령 [별표 1]
"A=P×r×
$\frac{(1+r)^{n}}{(1+r)^{n}-1}$ "). 원본에서 보이는 표 346개 기준 표 완전 일치: HWP 원본 66 → 346, 법제처 PDF 56 → 335. 위·아래첨자(㎡·H₂O)를 살리고, 목차 점선 탭 뒤 글이 잘리던 것도 고쳤다 - 묶음 별표의 "[별표 N]" 제목이 표 칸이 아니라 제목 줄로 나와
extractBundledSection이 표 구조에 덜 기댄다
get_law_text의efYd안내: "그 법령에 실재하는 시행일이어야 하고 기준일을 넣으면 NOT_FOUND" 라고 안내해, 4.15.0 부터 하는 기준일 보정과 어긋났다. 건축허가일·사건일 같은 기준일을 넣으면 그날 시행 버전으로 보정한다고 고쳤다. 버전을 못 찾았을 때 안내에legal_analysis(mode="applicable_law")를 더했다
연혁(시점) 조회를 다시 설계했다. 제명이 바뀐 법령은 바뀌기 전 연혁이 통째로 빠져, 소방시설처럼 법령명이 여러 번 바뀐 분야에서 과거 기준일 질문에 "시행 전"이라는 오답이 나왔다. 법령ID 계보로 버전을 특정하도록 바꾸고, 같은 방식을 별표·행정규칙까지 넓혔다. 노출 도구는 그대로 10개다(기존 도구에 선택 인자만 추가). 구 4.14.2 와 같은 17개 시나리오를 나란히 실행한 값을 적는다. 테스트 989 → 1042.
- 법령ID 계보 연혁:
lawSearch.do?target=eflaw&LID=<법령ID>한 번으로 이름과 무관하게 전 시행 버전을 받는다(시행 슬라이스 단위).search_historical_law·legal_analysis(applicable_law)·legal_research(amendment_track, time_travel)가 이 계보를 쓴다. 실측: 「소방시설 설치 및 관리에 관한 법률 시행령」 연혁 9건 → 79건(제명 변천 3단계 병기), 8개 법령 대조에서 lsHistory 행을 모두 포함하고 분리시행 슬라이스를 더 담았다(소득세법 시행령 +78). 계보를 못 잡으면 종전 이름 일치 연혁으로 물러선다 get_annexes의date(기준일): 그날 시행 중이던 버전의 별표·서식을 준다. 건축허가·착공·처분 시점의 설치기준·과태료표처럼 과거 기준이 필요한 질문용. 분리시행이면 시행일별로 다른 별표 파일을 고른다(실측: 같은 MST 의 2015.1.8.판·4.7.판 별표5가 다르다). 시점마다 별표 번호가 달라지므로(2015년 시행령 별표5 → 현행 별표4) 응답에 당시 법령명과 현행 명칭을 함께 적는다- 행정규칙의 기준일 판단:
legal_analysis(applicable_law)가 법령이 아닌 이름을 받으면 행정규칙 발령 연혁(행정규칙ID 계보)에서 기준일 시행 버전을 특정한다. 「스프링클러설비의 화재안전기준(NFSC 103)」은 2004년부터 2022.12.1. 「화재안전성능기준(NFPC 103)」 까지 한 계보(ID 35312)다. 발령·시행 순서가 엇갈린 구간(2013년 제2013-18호·21호)은 본문에 미시행 개정이 섞일 수 있음을 알린다. NFPC 버전이면 세부 기술기준이 NFTC 로 분리됐음을, 화재안전기준이면 「소방시설 설치 및 관리에 관한 법률」 제13조(강화된 기준의 적용 특례)를 함께 안내한다 search_admin_rule의history: 행정규칙ID별 발령 연혁표(옛 명칭 시절까지). 검색 결과에 시행일을 싣고,display를 업스트림에도 넘긴다(종전엔 기본 20건만 받아 잘랐다)search_law의 옛 법령명 안내: 옛 이름으로 찾으면 현행 명칭·법령ID·MST 를 알린다(종전: "정확매칭 없음"만)
get_law_text의 시행일 아닌efYd: "조회 기준일"로 오늘·사건일을 넣으면 NOT_FOUND 였다(#160 계열). 이제 그날 시행 중이던 버전으로 보정하고 그 사실을 첫 줄에 밝힌다. 조문(jo)까지 붙으면 법제처가 HTML 안내 페이지를 고정으로 줘서 재시도 4회(3.1초) 뒤 오류였던 경로도 확인 1회로 끊고 보정한다. NOT_FOUND 안내가 lawId 를mst=로 적던 것도 고쳤다verify_citations가 개명된 법령을 "폐지"로 판정: 옛 이름 인용이 연혁에만 잡히면 폐지로 보았고, 폐지 판정이 접두 일치로 다른 법령 (「… 법률」 대신 그 시행규칙)을 집었다. 폐지 판정은 이름 완전일치 우선(하위법령 꼬리 제외)으로, 같은 법령ID에 이름이 다른 현행이 있으면 제명 변경으로 보고 옛 이름 시절 마지막 버전에서 조문 실존을 확인한다verify_citations의 법령명 추출: "민법 제751조와 민법 제756조"의 두 번째가 "조와 민법"으로 잡혔다. 역추적 창을 직전 인용 뒤로 자른다get_article_history: 응답 행은 법령 버전이고 조문이 바뀐 버전에만 기록이 붙는데 한 쪽(20행)만 받았다. 소방시설법 시행령 제11조가 "총 58건"에 1건만 보였다. 조문을 지정하면 100행 단위로 전 버전을 받아 5건을 모두 싣고, 머리말을 "버전 N개 중 변경 M건"으로 적는다- 화재안전기술기준(NFTC)식 본문: "제N조" 없이 "2.7.3" 절 번호로 된 행정규칙에서
get_admin_rule의 jo·keyword 가 "keyword 를 쓰세요"라는 막다른 안내를 냈다. 절 번호 조회(jo: "2.7.3", 하위 절 포함)와 절 단위 키워드 검색을 지원한다 get_admin_rule: 머리말에 시행일·제개정구분을 싣고 연혁본이면 "현행 아님"을 표시한다. 본문 속 이미지 표(<img>)는 표식과 원문 링크로 바꾼다applicable_law: 기준일 이후 전부개정이 끼면 같은 조번호 현행 비교가 다른 조문끼리의 비교가 되므로, 비교를 생략하고 이유를 밝힌다. 당시 법령명이 지금과 다르면 인용은 당시 이름으로 하라고 적는다- 계보의 폐지 행(예: 소방법 2004.5.30. 타법폐지)은 시행 중 버전으로 보지 않는다. 폐지 뒤 기준일은 "이미 폐지", 연혁 목록은
[폐지]로 표시한다 - 계보 조회가 실패해 이름 일치 연혁으로 물러서면 그 사실을 밝히고, 옛 이름으로 들어온 경우 마지막 버전을 현행이라 단정하지 않는다
- 기준일 보정·행정규칙 본문 조회가 공개 도구를 거치며 예산 소진·요청 취소를 "시행일 없음"·"당시 미신설"로 바꾸던 경로를 올리도록 고쳤다
- "오늘"을 한국 시각으로 계산한다(UTC 로 셈하면 시행일 당일 오전 9시 전까지 새 버전이 시행예정으로 보였다)
- 법령명 해석이 입력이 더 긴 쪽의 느슨한 일치를 받지 않는다: 「개인정보 보호법 위반에 대한 과징금 부과기준」(고시)이 개인정보 보호법으로 풀리던 것
- 업스트림 본문 기본 한도 2/8 MiB → 8/24 MiB: 법령 전문 JSON 이 흔히 2 MiB 를 넘어(도로교통법 시행규칙 3.7 MB) npm 로컬 사용자만
get_annexes가 정본 조회에 실패하고 낡을 수 있는 인덱스 링크로 떨어졌다. 통합 호스트 런처 값과 맞췄다. 자체 HTTP 호스팅에서 메모리가 작은 VM(256MB 등)이면 요청 하나가 최대 24 MiB 본문을 받을 수 있으니MCP_MAX_TOTAL_UPSTREAM_BODY_BYTES로 낮출 수 있다 - 별표단위 목록 캐시(MST·시행일별 6시간): 별표 하나 보려고 호출마다 법령 전문을 받았다. 같은 별표 재호출 2.4초·5회 → 0.8초·4회
- 3단비교 렌더 캐시(1시간): 조문 필터가 없는 응답(관세법 1.98 MB)을 체인마다 다시 받았다. 처분근거 체인 14회 → 12회. 옛 MST 를 줘도 늘 현행 3단비교가 오므로 시행령·시행규칙 개정을 넘기지 않게 1시간만 둔다
- 법령 검색 폴백 단계 공유·캐시: 부가어 제거·법령명 추출 단계의 검색어도 요청 안에서 한 번만 치고 캐시한다. 캐시 키에서 max 를 뺐다. 앞 단계가 장애로 빠진 채 뒤 단계로 얻은 결과는 전역 캐시에 넣지 않는다(일시 장애 한 번이 한 시간 동안 검색을 열화시키던 기존 문제)
verify_citations: 법령명 앞머리가 될 수 없는 꼬리 후보("관한 법률"·"및 안전관리에 관한 법률")를 거르고 폐지 확인을 병렬로. 혼합 인용 2.1초 → 1.7초applicable_law: 부칙 조회를 조문 조회와 겹쳐 띄운다. 도로교통법 기준일 판단 1.36초 → 0.88초full_research: 판례·해석례 상세가 기반 법령 탐색을 기다리지 않는다(1.44초 → 1.13초).action_basis: 질의에 법령명이 없으면 의미검색을 처음부터 함께 띄운다(약 0.2초).procedure_detail: 하위법령이 없을 때 모법 별표를 두 번 받던 것 제거.cite_check: 후속 인용 검색을 먼저 출발. 별표 목록 2쪽 이후를 병렬로 받는다
- 조문 본문의 개정 표기가 지워지던 문제:
cleanHtml이<[^>]+>로 태그를 지우면서<개정 2012.8.1.>·<신설 2005.12.29>·부칙 <1999.01.11>같은 꺾쇠 표기까지 없앴다(민법 전문에서만 149개). 행위시법 판단의 근거라 영문 태그·주석만 지우고 한글 꺾쇠 표기는 남긴다.applicable_law부칙 머리글은 원문 줄 대신 공포번호·일자로 표준 표기(YYYY.MM.DD)를 조립한다
-
setup 마법사가 커밋되는 설정 파일에 API 키를 평문으로 쓰던 문제: 현재 폴더에 쓰는 Claude Code
.mcp.json에는${LAW_OC}환경변수 참조를, VS Code.vscode/mcp.json에는inputs비밀번호 입력(${input:law-oc})을 넣는다. 홈 디렉터리 설정(Claude Desktop·Cursor 등)은 커밋 대상이 아니라 종전대로 둔다
성능·품질 기술부채 정리. 영역 4개(업스트림 I/O·체인·파싱·도메인 도구)를 병렬 리뷰해 찾은 결함을 한 번에 고쳤다. 실호출 A/B(같은 12개 시나리오, 구 4.14.0 대 신 빌드) 결과를 수치로 적는다. 테스트 806 → 981, 테스트 시간 25.8초 → 약 10초.
get_law_text(MST 단독)가 업스트림 5회에서 1회로: 법제처가 eflaw 단건에 efYd 를 요구한 뒤(#153) MST 단독 eflaw 는 매번 HTML 안내 페이지로 실패했고, 재시도 계층이 그 HTML 을 점검 페이지로 보고 4회(백오프 약 2.1초) 두드린 뒤에야 target=law 폴백에 닿았다. 이제 MST 단독이면 target=law 로 바로 간다(결과는 폴백 본문과 같다). 실측: 조문 1개 3,065ms → 238ms, 전문 4,007ms → 794mslegal_analysis(verify_citations)가 다시 검증한다: 위 낭비 때문에 인용 15건이면 요청 예산(업스트림 48회)이 바닥나 ✓0 / ⚠15 (전부 "예산 초과")였다. 법령명은 요청당 한 번만 풀고 동시성 8로 검증한다. 실측: 업스트림 48회 → 16회, 결과 ✓15legal_analysis(impact_map)3,276ms → 590ms (대상 조문 조회가 매번 4회 재시도 후 실패하던 경로 제거)legal_research7종(document_review제외)이 체인 데드라인을 쓰고, 기반 법령을 찾은 뒤의 독립 갈래는 동시에 출발한다 (출력 순서는 그대로). 공용withChainDeadline헬퍼로 #131·#150 규칙을 한 곳에 모았다search_law: 시행예정 보조검색을 주 검색과 같이 출발시킨다. 0건 폴백은 그 약속을 재사용하고 폐지 이력 조회와 병렬로 기다린다- 업스트림 시간 한도: 시도마다 30초를 새로 주던 것을 전체 45초로 묶었다(매달린 업스트림에 약 122초가 걸려 60초에 포기하는 클라이언트가 오류 문구 없이 끊겼다). 헤더 뒤 본문이 멈추면 청크 간 20초에서 끊는다(종전 undici 기본 300초). law.go.kr 안티봇 프로브도 정지를 삼키지 않아, 정지 응답이 시도마다 두 번 기다리던 경로(재현 80초)가 45초 안에 끝난다
- 메모리·요청당 비용: kordoc 을 별표 파싱 때만 불러온다(유휴 RSS 약 114MB → 약 100MB). 요청마다 만들던 Ajv 검증기를 공유하고
tools/list 스키마 변환을 한 번만 한다(tools/list 21회 뒤 RSS 126
130MB → 100104MB). 파싱된 법령 전문 캐시를 전역 500건 캐시에서 전용 12건 캐시로 뗐다. lawId 만 준 현행 본문 캐시는 1시간(개정 시행일을 넘겨 낡지 않게) get_annexes: 한 요청 안에서 같은 법령 전문(3.8MB)을 두 번 받던 것을 한 번으로
- 서버 API 키 노출 차단: 법제처 검색 응답의 상세링크에는 그 요청에 쓴 OC 가 실려 오고, 여러 렌더러가 그 링크를 그대로 출력했다.
자체 키 없는 사용자에게 서버 LAW_OC 가 보여 폴백 한도를 우회할 수 있었다. tool-registry 최종 게이트에서 모든 도구 출력의 키를
가린다(
maskKeysInText). 실측: 12개 시나리오 출력에서 원문 키 25회 → 0회 - ReDoS 3경로: 인증 없는 요청 1건(100KB 이하)으로 이벤트 루프가 12~24초 멈췄다(
legal_research(document_review)의 금액·기간 정규식,get_annexes의 별표 표기 정규식, 검색어 정규화의 공백 백트래킹). 정규식을 선형으로 고치고(구·신 약 380만 건 퍼징에서 의도 외 출력 차이 0) 모든 문자열 인자에 길이 상한을 건다(조문·별표 번호류 100자, 일반 2,000자,text50,000자, 중첩 인자 포함). 의도한 출력 차이: 계약서 분석은 라벨과 값 사이 500자를 넘는 간격을 더 짝짓지 않고, 쉼표만 든 쓰레기 금액(", 원")을 만들지 않는다
-
"실패를 0건으로" 계열(가장 위험한 유형: LLM 이 없는 결과를 근거로 답한다)
- HTML 점검·안내 페이지 가드가
searchLaw·getAnnexes에만 있어 행정규칙·자치법규 검색, 조문·법령 연혁, 신구대조, 3단비교가 점검 페이지를 "검색 결과 없음"으로 냈다. 가드를 본문 읽기 단일 통로로 옮겼다. 따르면 더 나빠지던LAW_RESPONSE_TYPE=JSON우회 안내도 뺐다 - 시행예정 보조검색 실패가 "시행예정 없음"으로 바뀌어 1시간 캐시됐고,
search_law_bulkdiff 는 그 법령을 "본문 동일, 생략"으로 넘겼다. 이제 실패를 밝히고(결과는 1분만 캐시) diff 에선 "확인 실패, 재조회 필요"로 따로 보고한다. 확인된 법령이 0건이면 "변경 없음"이라 단정하지 않는다 - impact_map 의 인용 검색 실패가 "0건"으로, cite_check 의 상세 조회 실패가 "✅ 계속 인용 추정"으로, full_research 의 기반 검색 장애가
"법령 없음"으로 나가던 것 등 체인 곳곳의 무음 실패를 명시적 실패 표시로 바꿨다. 예산 소진·취소는 보조 조회가 삼키지 않는다(
rethrowIfFatal) advanced_search가 5xx·타임아웃을 "0건"으로 냈고, 자치법규 항목 태그를 잘못 읽어 늘 0건이었다
- HTML 점검·안내 페이지 가드가
-
잘못 읽던 응답 구조
get_law_statistics: 태그·필드명을 잘못 읽어 업스트림 31회 뒤 늘 "총 0건". 최신 날짜부터 돌고 MST 중복을 걷는다- 다자조약(
MultTrtyService)을 못 읽었고 양자조약은 모든 필드가 N/A 였다. 검색 결과의 다음 단계 안내 파라미터도 틀렸다 - 해석례 기간 검색이 한 페이지만 받아 로컬로 걸러 총건수가 틀렸다(2024년 "건축": 2건 → 55건). 업스트림
explYd로 넘긴다 search_law의 시행예정 병기가 연혁 행에 창을 빼앗겨 시행령·시행규칙 개정을 놓쳤다(eflawnw=2·100건). 도로교통법 시행규칙 2027-01-01 시행분이 이제 나온다- 생활용어 도구 2종이 존재하지 않는 조합의 target 을 불렀다(
dlytrm·dlytrmRlt로 교정, "월세" 0건 → 15건) get_article_history가제38조를 6자리 코드로 바꾸지 않아 늘 0건, 공정위 결정 사건번호(</사건번호 >) 유실, 영문 법령명 강조 태그 잔존, 해석례·자치법규 라벨 오기,search_historical_law의 폐지제정·날짜 표기 오류- 여러 래퍼가 업스트림 오류를 "판례를 찾을 수 없습니다" 같은 부존재 단정으로 덮어썼다
-
체인·시나리오
full_research가 데드라인 때 이미 받은 해석례를 버리고 "위까지가 시간 안에 받은 전부"라고 했다- penalty 시나리오가 없는 파라미터로 전체 목차를 "벌칙 조항"으로 냈고 같은 전문을 두 번 받았다. 벌칙 장·과태료 조문만 골라 싣는다. delegation 시나리오의 죽은 갈래(질의를 무시하는 소스)는 빼고 "자동 대조 미제공" 안내로 바꿨다
-
get_article_detail: 업스트림이 무시하는 항·호·목을 받은 조문에서 직접 거른다(종전엔 "제2항"이라 쓰고 조문 전체를 냈다) -
판례 축약이 서로게이트 쌍을 자를 수 있었고
[10]이상 참조 표시를 지웠다 -
독립 리뷰(컨텍스트 없는 리뷰어 3명) 반영:
advanced_search의 자치법규 ID 를get_ordinance가 받는 일련번호로,search_decisions해석례 기간에 숫자·구분자 날짜 허용(오류엔 필드명), 법령 통계가 전부 실패하면 첫 실패 원인 표기, 판례 부가 검색의 예산 소진이 이미 받은 조문을 버리지 않게, 공백만 다른 같은 법령 검색을 두 번 치던 것, 키 마스킹이 뒤 문자열을 삼키던 것
- SDK 가 요청을 거절한 사유(400 등)를
[mcp] …로그로 남긴다. 봉투는 method 와 id·params 의 타입만 싣는다(값 미기록, method 의 제어문자는 치환). 2026-09-23 기준 claude.ai 커넥터 POST 의 약 15% 가 사유 미상의 400 이었다 - 해석례 본문(
get_decision_textdomain=interpretation)은 기본 축약에서 뺀다. 라벨을 "이유"로 바로잡자 축약기가 해석례의 핵심인 이유를 절반 가까이 잘랐다(실측 4,608자 → 2,279자). 라벨 정정 전처럼 전문을 준다
get_admin_rule부분 조회 —jo·chapter·keyword·page(#162): 행정규칙 중 전문이 긴 규칙은 뒷부분 장을 영원히 읽을 수 없었다. 법제처 admrul 상세는 법령(target=law)과 달리 JO 파라미터가 없고 전문이<조문내용>통짜 1개 CDATA 로만 온다(외국환거래규정 기준 18.8만 자·조문 208개 — 실측).get_admin_rule {id, jo:"제9-5조"}는jo를 무시하고 제1장부터 뱉다가 5만 자에서 잘렸다.- 서버가 전문을 조문 단위로 파싱해 4가지 뷰로 자른다 — 우선순위
jo>chapter>keyword>page, 복수 지정 시 상위 하나만 적용하고 무시 목록을 응답에 명시 jo는"제9-5조"·"9-5"·"제2-6조의2"·"9-5-2"를 모두 수용하고context로 전후 조문을 함께 준다.keyword는 매칭 조문 번호 전량을 목록으로 주고 본문만 상위 N개로 줄인다(뒷장 조문이 목록에서 사라지면jo로 이어 갈 단서가 없다).page는 라인 경계 비중첩 청킹- 조문 헤더 판정은
^앵커 + 조문번호 단조증가 검사 — 라인 시작 역행 참조(제1-2조 제1항의 규정은…)를 새 조문으로 오분리하지 않는다. 조문 체계가 없는 항목식 훈령은 에러가 아니라 안내로 답한다 - 전문 XML 을
admrulxml:캐시(TTL 6h, LRU 20건)에 담아jo→keyword→page연속 조회의 재호출을 막는다 - 파라미터 없는 전문 조회는 종전 동작 그대로. 다만 잘릴 때 부분 조회 힌트를 본문 앞에 넣는다(뒤에 붙이면 잘려 사라진다)
- 서버가 전문을 조문 단위로 파싱해 4가지 뷰로 자른다 — 우선순위
compare_admin_rule_old_new제·개정이유 폴백: 행정규칙 신구대조는 API 데이터가 없는 경우가 많아[NOT_FOUND]다섯 글자만 나갔다. 대조 0건이면 같은 응답에 이미 들어 있는<제개정이유내용>을 발령번호·일자와 함께 반환하고, 그것도 없으면 law.go.kr 안내로 끝낸다
- 부분 조회 파서의 조용한 라인 유실 2건 (#162 리뷰): 단위 테스트로는 안 잡히고 "성공했는데 내용이 없는" 유형이다.
실데이터(외국환거래규정 564라인)로 커버리지를 재어 찾았다
- 장 헤더 뒤에 오는 절 헤더(
제2장다음 줄의제1절 외국환은행)가preamble로 빠졌는데,preamble은jo·chapter·keyword어느 뷰에서도 출력되지 않는다 → 첫 조문 이후의 떠도는 라인은 다음 조문 앞에 붙인다. 수정 후 564/564 라인 커버·preamble 0 - 장마다 조 번호가 1 로 리셋되는 체계(
제2장/제1조)는 단조증가 검사에서 탈락한 뒤cur=null직후라 본문에도 못 붙어 조문이 통째로 사라졌다 → 장 헤더에서 단조증가 기준을 리셋한다 - 빈 본문의
페이지 1/0표기, 공백뿐인keyword가includes("")로 전 조문에 매칭되던 문제 - 테스트 801 → 806
- 장 헤더 뒤에 오는 절 헤더(
fetch failed가 원인을 감추던 문제 (#161): claude.ai 커넥터에서search_law가[EXTERNAL_API_ERROR] fetch failed로 실패한다는 보고가 #78 에 이어 다시 왔다(2026-09-20 12:50 KST, 약 5분). 두 번 다 사용자 손에 남은 건 저 다섯 글자와 시각뿐이었다 — undici 는 DNS 실패·TCP 리셋·연결 타임아웃·TLS 검증 실패를 전부 같은 메시지로 던지고cause에만 code 를 싣는데, 그걸 버리고 있었다. 머신이 갈리면 서버 로그도 같이 사라지므로 사후에 리전 egress 드롭인지 법제처 점검인지 가를 근거가 없었다.describeFetchError:cause의 code·메시지·대상 호스트를 붙인다 —fetch failed (ECONNRESET: read ECONNRESET) - www.law.go.kr. API 키 마스킹은 그대로 거친다- 재시도를 다 태운 최종 실패는
[upstream] N회 시도 실패: …로 stderr 에 남긴다.fly logs에서 같은 시각의 원인 코드를 찾을 수 있다 - 테스트 758 → 763
ISO 법규준수 등록부(품질·환경·안전보건·정보보안·BCM·부패방지·AI·ESG 8개 영역 108개 법령)를 이 서버로 주간 감시하던 실사용 보고 3건(#157 · #158 · #159, @thkim660207-cmd)을 처리한다.
세 건 모두 "응답은 정상인데 실무에서 쓰기 어렵다"는 형태였다 — 호출 수(등록부 1회전에 search_law 98회),
응답 예산(이력 섹션이 5만 자를 혼자 소진해 정작 필요한 신구대조표가 잘림), 그리고 침묵(본문이 이미지뿐인데
아무 말이 없어 LLM 이 수치를 지어내기 좋은 자리가 됨). 테스트 738 → 758.
search_law_bulk— 등록부 대량 조회 + MST diff 감시 (#157): 법령명 배열(최대 40건)을 받아 건당법령ID · MST · 시행일 · 시행예정한 줄만 돌려준다. 부분매칭 목록·다음단계 안내처럼 감시 용도에 불필요한 본문을 싣지 않는다.previous={법령ID: 직전 MST}를 주면 MST 가 달라진 법령만 반환하는 diff 모드로 동작한다 — MST 는 개정마다 바뀌므로 그 자체가 변경 감지 키다. 응답 말미에 다음 감시에 그대로 넣을{법령ID: MST}스냅샷을 함께 준다. 노출 도구는 10개 그대로이고execute_tool(tool_name="search_law_bulk")경유로 접근한다(search_law설명과 discover_tools "법령검색" 카테고리에서 안내).- MST 가 같아도 시행예정이 있으면 침묵하지 않는다: 공포됐으나 미시행인 개정은 현행 MST 를 바꾸지 않아 diff 만 보면 "변경 없음"이다. 등록부가 시행일을 놓치는 자리라 별도로 표시한다
- 한 건이 실패해도 나머지를 버리지 않는다(
Promise.allSettled, 동시 5건). 요청 단위 업스트림 예산 (MCP_MAX_UPSTREAM_REQUESTS, 기본 48)이 소진되면 받은 데까지의 결과 + 남은 쿼리 목록을 돌려준다 - 현행 우선 정렬·정확매칭 분리 판정을
search-hits.ts(sortCurrentFirst·splitExactPartial)로 옮겨search_law와 공용한다 — 같은 법령명에 두 도구가 다른 법령을 고르면 감시가 조용히 어긋난다
amendment_track의 조문별 개정 이력이 opt-in (#158):legal_research(task="amendment_track")·chain_amendment_track이 제정 시점부터의 조문×개정 전건을 항상 싣던 것을includeHistory(기본false)로 바꾼다. 산업안전보건법(1981년 제정) 실측에서 이 섹션 혼자 49,990자 → 24,938자로 절단되며 5만 자 응답 상한을 소진해, 실무에서 정작 필요한 신구대조표가 잘려나갔다. 끈 자리에는 껐다는 사실과 켜는 법 (includeHistory=true·get_article_history(lawId=…))을 남긴다 — 침묵하면 이 서버가 이력을 못 준다고 오해된다. 실측(산업안전보건법lawId=001766, 2026-09-11): 기본 응답 6,542자 · 축약 표시 없음,includeHistory=true는 31,366자
- 프로덕션 의존성 취약점 4건 해소:
sharp0.35.4(high, libheif GHSA-rgj7-g3m4-5g8c) ·hono4.13.7(moderate 3건).kordoc은 lock 이 4.7.2 에 머물러 있던 것을 선언 범위(^4.7.2) 안의 최신 4.13.1 로 올렸다 —npm run verify:annex-runtime으로 별표 파싱이 optional 의존 없이 동작함을 재확인했다 - 남은 권고 4건은 상류에 패치가 없다:
adm-zip(GHSA-vwc7-r8mq-g2x9, moderate) 하나에서 파생한 것으로 경로는kordoc → @huggingface/transformers · onnxruntime-node → adm-zip이다. 권고 범위가>=0.5.9라 최신 0.6.0 에도 수정본이 없고,npm audit fix --force가 제시하는 유일한 해결책은kordoc@2.5.2다운그레이드(breaking)다. 취약점 성격은 zip 추출 시 심볼릭 링크 추종이고 해당 코드는 ONNX 런타임의 optional OCR 경로라 이 서버의 법령 조회 런타임에서 도달하지 않는다
- 이미지-only 별표가 아무 경고 없이 나가던 문제 (#159): 법제처는 별표의 기준 수치·적용 대상 목록을
<img id="…">태그로만 돌려줄 때가 있다(실측: 낙동강유역환경청 「수질오염물질의 배출허용기준 중 별도배출허용기준」 고시2100000248042— 이미지 6개 + 실텍스트 8자).get_admin_rule은 이것을 "본문 있음"으로 보고 그대로 출력해, 배출기준치처럼 심사원이 실제로 확인하는 수치 자리에서 LLM 이 추측하기 좋은 상태가 됐다. 이제 이미지 태그를 걷어낸 실텍스트가 100자 미만이면 본문 앞에 경고를 붙이고 원문 URL(law.go.kr/admRulInfoP.do?admRulSeq=…)과 첨부파일 원문(hwpx/pdf) 링크를 함께 안내한다. 첨부파일은 API 가 이미 주고 있었는데 "본문이 비어 있지 않다"는 이유로 안내 분기에 도달하지 못하고 묻혀 있었다
(소급 기록 — 릴리스 당시 CHANGELOG 절이 누락됐다. 내용은 커밋 이력 기준)
- 프로덕션 의존성 취약점 3건 (
c089b8f). v4.12.4 릴리스 게이트(npm audit --omit=dev)가 잡았다
(소급 기록 — 릴리스 당시 CHANGELOG 절이 누락됐다. 내용은 커밋 이력 기준)
- 시행예정 병기가 임박한 개정부터 조용히 잘라내던 문제 (#156,
293f252): API 응답 순서(먼 시행일부터)를 그대로 상위 5건만 남겨, 대기환경보전법 실측에서 가장 임박한 2026-09-18 이 잘리고 2027-01-10 이 남았다. 시행 임박순 정렬 후 자르고, 잘린 건수를 명시한다 efYd오용으로 난 NOT_FOUND 가 엉뚱하게mst를 탓하던 문제 (#160,00a54fa)
법령정보 지식베이스 연계 도구 4개가 에러 없이 항상 0건 이던 장애를 복구한다. #155 (@yoonkhsc) 머지. 테스트 722 → 733.
법제처는 실재하지 않는 target 에 HTTP 200 + 빈 본문 을 돌려준다. 에러도 아니고 XML 오류 메시지도 아니라 fetchApi 가 정상 반환하고 파서가 0건을 내는 것으로 끝나, 재시도·폴백에도 걸리지 않고 사용자에게는 "해당 자료가 없다"로만 보였다.
get_related_laws·get_term_articles·get_daily_to_legal·get_legal_to_daily가 상시 0건이던 장애 (#155):src/tools/knowledge-base.ts가 쓰던target3개(lstrmRel·lstrmJo·lawRel)가 실재하지 않는 값이었다. 각각lawService.do&target=lstrmRlt·lawService.do&target=lstrmRltJo·lawSearch.do&target=lsRlt로 바로잡는다.relType(DL/LD)은lstrmRlt에 없는 파라미터라 함께 제거했다. 재실측(2026-09-06, OC 로컬): 같은 조건type=XML응답 바이트가lstrmRel·lstrmJo·lawRel모두 0B — 아예 없는 대조군bogus123과 바이트 단위로 동일, 반면lstrmRlt4,659B /lstrmRltJo439,634B /lsRlt(건축법) 정상 응답target만 고쳐도 남던 0건 (#155): 이 세 응답은 항목을 한글 래퍼(<연계용어>·<연계법령>·<관련법령>)에 담고 필드명도 다른데, 공유 파서parseKBXML은 항목 태그를["lstrm","lstrmAI","law","jo","rel","item"]로 고정한다. 정상 동작 중인get_legal_term_kb·get_daily_term·fallbackTermSearch가 이 파서를 함께 쓰므로 공유 파서는 건드리지 않고, 연계 도구 4개 전용 파서(parseRelationXML)를 같은 파일에 두어 영향 범위를 고친 함수 4개로 닫았다- 총건수 오독 (#155):
<검색결과개수>는 연계 항목 수가 아니라 기준 용어·기준 법령의 개수(항상 1) 다. 파싱한 항목 수를 총건수로 쓴다.lawService.do는display를 무시하고 연계 조문 전문을 통째로 주므로(임대차기준 약 430KB) 건수 제한은 파서에서 건다 - 조가지번호 표기 (#155): 조번호
0024+ 조가지번호02를제24조의2로 조립한다(종전 경로의제24의2조는 존재하지 않는 조문 표기)
법제처 상류 변경으로 get_law_text 가 통째로 멈춘 장애를 복구한다. #154 (@Rillmo) 를 머지하고, 같은 이슈가 함께 보고한 get_historical_law 조문 파싱 결함을 후속 수리했다. 테스트 717 → 722.
2026-08-27부터 lawService.do?target=eflaw 단건 조회가 efYd 를 요구한다. MST 단독 요청의 실패 양상이 종전의 "200 + 빈 봉투"에서 "200 + HTML 안내 페이지"로 바뀌면서, 빈 봉투만 겨냥해 만들어 둔 target=law 폴백(v4.12.1)이 무력화됐다.
get_law_text(mst=…)가 실존 조문을 통째로 못 찾던 장애 (#153, #154):checkHtmlError가 폴백보다 먼저 throw 해서 기존target=law폴백에 도달하지 못했다. 두 실패 양상(빈 봉투·HTML 안내 페이지)은 "이 파라미터 조합으로는 못 푼다"는 같은 신호이므로 같은 폴백으로 흘리고, 폴백까지 법령 노드를 못 얻었을 때만 던진다. 폴백 대상이 아닌 호출(lawId단독·efYd동반)은 종전대로 즉시 던져 #152 의 "조용한 슬라이스 대체 금지" 불변식을 유지한다. 실측(2026-08-29):target=eflaw&MST=285697&JO=007500→ HTML 1,649B, 같은 파라미터target=law→ 정상 JSON 6,977B (법령.조문.조문단위[0]조문번호 75 / 조문제목 과태료 —eflaw&ID=000190결과와 일치)verify_citations가 실존 인용을 전건⚠로 보고하던 연쇄 장애 (#153): 이 도구는 위 경로만 타므로 복구가 그대로 전파된다.legal_analysis(mode=verify_citations)도 같다- 효과 없는 우회 안내 (#153 곁가지 1):
getLawText는type: "JSON"을 하드코딩하는데checkHtmlError는 환경변수 기본값(XML)을 봐서, 이미 JSON 으로 부른 호출에 "LAW_RESPONSE_TYPE=JSON 으로 우회하라" 는 안내가 항상 따라붙었다. 안내를 실제로 XML 을 보낸 호출로 좁혔다 get_historical_law가 조문을 한 건도 못 찾던 문제 (#153 곁가지 2):law.조문을 조문 객체의 배열로 읽는데 페이로드는법령.조문.조문단위[]로 한 겹 더 감싸는 구조라, 배열 길이가 1(래퍼 자신)이 되고 조문 목록이제undefined조한 줄로 무너졌다.verify_citations·applicable_law는 이미 조문단위를 읽고 있었다 — 이 도구만 어긋나 있었다. 함께 드러난 결함 둘을 같이 고친다: ①조문단위에 조문여부=전문으로 섞여 오는 장·절 헤더를 제외 (실측 아동복지법 MST 285697: 123개 중 12개) ②조문 본문은조문내용(제목줄뿐)이 아니라 항·호에 있으므로 그 결합의 단일 원본인formatArticleUnit사용 ③parseJoNumber의"75의2"를 조문번호·조문가지번호로 갈라 비교 (종전엔 가지번호 조문이 늘[NOT_FOUND])
분리시행(한 공포본이 조항별로 나뉘어 시행되는 경우) 처리 결함 배치. #152 (@herrozim-ship-it) 을 머지하고, 머지 전 리뷰에서 실측 재현된 파급 결함 2건을 후속 수리했다. 테스트 709 → 712.
근본 원인은 하나다 — MST는 "버전"이 아니라 "공포본" 식별자다. 조항별 시행일이 나뉜 공포본은 MST 하나에 시행 슬라이스가 여러 개 붙는데, 코드 세 곳이 MST를 버전 고유값으로 취급하고 있었다.
applicable_law가 현행 버전을 오판정 (#152):fetchHistoricalVersionsFull의 연혁 중복 제거 키가 MST 단독이라, lsHistory 가 같은 MST로 내보내는 시행일별 행 중 내림차순 첫 행(미래 시행분)만 남고 이미 시행된 슬라이스가 사라졌다.efYd <= 기준일탐색이 그 MST 를 통째로 건너뛰어 직전 공포본을 "현행"으로 답했다 (실측: 형사소송법 @2026-08-19 → 시행 2026.6.24. 반환, 실제 현행은 시행 2026.7.1.). 키를MST:시행일쌍으로 교체get_law_text(mst=…)가 현행 아닌 버전에[NOT_FOUND](#152):lawService.do?target=eflaw는 MST 단독(시행일 미동반)으로 현행이 아닌 버전을 풀지 못하고 200 +{}를 돌려준다 (실측: 시행 예정판 MST 288579·과거 연혁판 MST 280441 모두 빈 봉투). 법령 노드가 빈 응답이면target=law로 1회 폴백. 단, 시행일을 못박은 호출(efYd동반)에는 폴백하지 않는다 —target=law는 MST 만 받아 분리시행 공포본이면 마지막 슬라이스를 돌려주므로(실측:target=law&MST=281865→ 시행 20271231, 시행 중인 20260701 이 아님), 폴백을 붙이면[NOT_FOUND]보다 나쁜 조용한 다른-슬라이스 오답이 되어 행위시법 조문 비교를 오염시킨다time_travel이 실재하는 개정을 "변경 없음"으로 단정 (연혁 dedup 수정의 파급): 두 시점이 한 분리시행 공포본 안에서 갈리면oldVer.mst === newVer.mst조기 종료가 걸렸다. 실제로는 내용이 다르다 (형사소송법 MST 281865 → 시행 20260701 판 조문 644개 / 20271231 판 653개). 시행일까지 같을 때만 "변경 없음"으로 판정time_travel의 시점별 본문이 뒤섞이던 경로: 본문을target=law&MST로 집어, 분리시행 공포본이면 두 시점 모두 마지막 슬라이스를 받았다 — "시점 A: 2026.7.1. 시행" 머리말에 2027.12.31. 판 본문이 붙는다. 시행일을 동반한eflaw우선으로 교체하고 빈 봉투·시행일 부재 시에만 기존 경로로 물러선다
applicable_law시행 예정 개정 경고 (#152): 이미 확보한 연혁의 미래 시행분에서 추출 — 추가 API 호출 0. 현행 인용만 보고 답하면 곧 시행될 대개정을 통째로 놓친다 (실측: 형사소송법 2026.10.2. 시행 법률 제21857호 — 제215조의2 검사 직권 삭제, 제245조의7제3항 이의신청 3개월 기한 신설을 도구가 완전 침묵)
법률 자문 3축(법적 정합성·토큰 효율·응답 성능) 통합 배치. #150 (@humdrum00001010) 을 머지해 이슈 62건(#88~#149) 을 닫고, 머지 전 도메인별 독립 리뷰에서 실측 재현된 결함 31건을 후속 커밋으로 수리했다. 테스트 196 → 701.
- 날짜 표기 통일:
2024.1.5.→2024.01.05, 빈 시행일은""→"N/A"(formatDateDot단일 원본) discover_tools응답 형식: 노출 도구는 이름+포인터만(설명 전문 재인쇄 제거), 티어 랭킹·섹션 상한 5 적용. 16개 intent 합계 65% 감축, 정답 잔존 10/10. 무매칭 응답도 전체 목록 대신 유사 카테고리로 축약
- 체인 데드라인 (
MCP_CHAIN_DEADLINE_MS, 기본 45초,src/tools/chain-deadline.ts): 업스트림 꼬리에 이용자가 인질로 잡히지 않게 만료 시 받은 갈래까지 조립해 부분 결과를 돌려주고 못 받은 자리는 마커로 남긴다. 적용은legal_research8종 중 순차 사다리가 긴action_basis·full_research·dispute_prep3종. 기반 법령 탐색(프리픽스)부터 시계 안이며, HTTP 모드는 부팅 시점 fail-fast 검증 - 별표 목록 페이지네이션 (
src/tools/annex-list.ts): 업스트림 100건/페이지 창 밖의 별표에 도달(도로교통법 시행규칙 263건 중 별표28 본문 도달 실증). 페이지 상한·목표 도달·무진전 3중 중단 - 자치법규 조문 관련성 실측 대조 (
src/lib/ordinance-relevance.ts): 상위 N건 본문을 실제로 열어 조문 인용을 확인 (옵트인 — 상위 10건·병렬 1.9초·예산 1/5 실측 근거로 기본 꺼짐) - 판례 인용 검증 축 (
src/lib/case-citation.ts):verify_citations가 법령+판례 2축으로 확장. 실존불가/미확인을 구분하고, 실재 사건부호에 한해서만 단정
부존재 오단정 — 실재 자료를 "없다"고 말하던 경로 (하드닝)
fetch-with-retry: 미스 확정 조건이 시도 순번(attempt >= 1)이라 503·네트워크 오류 직후의 첫 빈 본문 1회 관측만으로 확정됐다(에러 문안은 "200ms 재확인 후에도 동일"이라고 진술). 관측 이력 기준으로 교체precedents: HTML 폴백의 부존재 판정이 음성 신호(마커 부재)만으로 성립해, 점검·안티봇 변형 페이지에서 실재 판례를[NOT_FOUND]로 단정했다(종전EXTERNAL_API_ERROR대비 단정 강도 격상). 양성 신호를 요구하도록 수정get_annexes: HTML 응답이 무음으로 빈 목록이 되고 사다리 전멸 시 "법제처 DB에 없습니다"를 단정하던 경로 —checkHtmlError적용 + 장애와 정상 0건 구분([UPSTREAM_NO_DATA]).ExecutionLimitError·취소는 삼키지 않고 전파api-client:singleRecordLookup을 endpoint 단위 → target 화이트리스트(prec/thdCmp/lsStmd)로. 빈 본문이 미스일 수 없는 target 까지 사다리가 잘리던 것 해소
법적 정합성 (#90 #93 #95 #98 #116 #118)
impact_map: 조번호+법령명 2축 앵커로 제1032조 혼입·JO 코드 조용한 0건 반환 해소. 하드닝으로 "구 OO법" 인용은 확정 배제가 아니라 보류로 완화 — 위헌심판은 행위시법을 심사해 "구 매장및묘지등에관한법률 제5조 위헌소원" 같은 사건명이 흔한데 옛 공식 제명은 붙여쓰기라 약칭 판정에 걸리지 않는다(군형법vs형법의 확정 제외는 유지). 제외 사유도 조문 축·법령 축으로 분리 표기verify_citations: 판례 인용 미추출로 검증이 법령에 한정되던 것 해소. 하드닝으로 수량 산문("2030도3000명 증가")이 사건번호로 추출돼 ✗ 단정되던 오탐 차단, 구 파산 부호파보강cite_check: 판시사항이 배열·{#text}객체로 와도 문장으로 읽는다(종전"[object Object]"무음 실패). 입력과 다른 판례가 특정되면 경고 병기time_travel: diff 항목에 공포번호·개정일 병기, 변경 지점 중심 신구대조. 하드닝으로 목(目)·조문 직속 호를 스냅샷에 합산 — 목만 개정된 조문이 "본문 동일"로 무음 누락되던 것 해소article-detail: 의X 조문 위치 라벨의 조 중복(제401조의2조) 수정
라우팅 (#99~#104 #119 #121~#123 #129 #130)
specific_article선점으로 조문번호가 든 질의의 후행 의도가 폐기되던 것, 날짜 파싱이 시점 패턴보다 먼저 실행돼 트리거가 소실되던 것,제생략 표기(44조)·다중 조문·별표번호·전문 요구가 추출에서 소실되던 것,조례명시 없는 자치법규 질의가 폴백 체인에 못 들어가던 것- O(n²) 접두 패턴 제거 + query 길이 상한(
.max(2000)): 8.5k자 입력 476ms, 적대 입력에서 종전 최대 45.9초 → 1~8ms - 하드닝:
statistics(pri 9)가specific_article의 양보를 가로채 "최근 개정된 근로기준법 제60조"가 법령·조문 없는 전역 통계로 가던 결함, 접속어 제거가 경계 없이 적용돼 "과학기술기본법" → "학기술기본법" 으로 잘리던 결함,dateRange주입이time_travel앵커를 덮어쓰던 결함, 지역 오인(지역구·놀이기구), 심판례 역선점, 고아 음절("헌재 결정문"→"문")
성능·안정 (#88 #105 #112~#115 #128 #131)
- 판례 미스 조회 12.5초·업스트림 5회 → 2.2초·2회, 부존재를 단정하지 않는
UPSTREAM_NO_DATA get_three_tier/get_law_system_tree미스 13.2초/11.7초 →2.1초/8.3초(XML+JSON 병렬화 — XML 제거는 실측 대조로 불가 판명)2.4초, 체계도 히트 10.611.6초 → 6.3- 2MiB 초과 본문에서 300초 무한 정지 → 13ms 명시 에러(v4.11.0 회귀), 예산 이중 청구 해소. 하드닝으로 안티봇 검사의 본문 이중 청구도 프리픽스 읽기로 전환
- 체인 기반 법령 탐색이 사용자 원문 그대로 AND 검색해
NOT_FOUND로 중단되던 것 → 3단 해석 + 갈래 병렬(12~14초)
응답·디스커버리 (#92 #102 #106~#111 #126 #145)
truncateResponse한도가 50KB가 아니라 5만 자(UTF-16 code unit) 임을 명확히 하고 문장 중간 절단 해소. 하드닝으로 서로게이트 쌍 반토막 절단(옛 한자), 요약 모드 재절단 시📋 요약 모드꼬리가 잘려 무표기 절단이 되던 자기모순 해소TOOL_ALIASES고아 키 4개로 한국어 별칭 10개가 증발하던 것, 별칭 양방향 부분일치로판례가 조세심판으로 해석되던 것, 카테고리 미등록 도구 13개,chain_*2-hop 안내(동일 기능 1-hop 존재)
- 이슈 전제가 실측과 달라 고치는 대신 반증한 건: #111(한글 낱말 경계 통일은 유해 —
심판3섹션→0), #117(원인은 폴백이 아니라 업스트림의 조번호 무시), #128(lsStmd XML 호출 제거 불가 — 4개 법령 전수 대조), #88(HTML 오분류가 아니라 0바이트 empty 경로) - #147 의 LexDiff 상류 불일치는 chrisryugj/lexdiff#103 으로 이관 후 상류를 ㊿ 범위로 수정 — 이식본
citation-content-matcher.ts와 동작 재정합 완료
- 전 수치는 로컬 IP·소규모 n 기준이다. Fly 프로덕션 IP 에서는 안티봇 층이 개입하므로 절대값이 다를 수 있다(비율 논증은 유지)
- 체인 지연 꼬리 일부는 업스트림발이며(A/B로 코드 무죄 입증) 데드라인+부분 반환이 방어선이다
- 체인 갈래 병렬화로 법제처 방향 피크 동시성이 요청당 3 → ~9로 오른다. 로컬 실측에서 스로틀 징후는 없었으나 다중 사용자 조건은 미검증
route-patterns.ts(규칙 테이블)는 200줄 규칙의 예외로 둔다
요청 경계·릴리스 경계 하드닝. #85 (@humdrum00001010) 을 v4.10.0 위로 머지하고 이슈 #80·#81·#82·#83·#84 을 닫는다.
- HTTP 바인드 기본값이
0.0.0.0→127.0.0.1. 외부에 노출하려면MCP_HTTP_HOST를 명시하고, 그와 함께MCP_AUTH_TOKEN또는MCP_ALLOW_UNAUTHENTICATED_REMOTE=1중 하나를 반드시 설정해야 한다(둘 다 없으면 기동 시점에 실패). 이 레포의 Dockerfile 은MCP_HTTP_HOST=0.0.0.0을 기본 주입하므로 컨테이너 사용자는 인증 값만 추가하면 된다. 통합 호스트(gomdori-mcp)는 launcher 가127.0.0.1:4001로 프록시하므로 영향 없음 TRUST_PROXY기본값이1→false, 허용값도1~10정수만. 직접 노출 배포에서X-Forwarded-For를 위조해 IP 버킷을 우회하던 경로를 막는다. 프록시 뒤 배포는 hop 수를 명시할 것(프로덕션은 이미TRUST_PROXY=1설정됨)get_batch_articles입력 상한: 법령 20개, 법령당 조문 50개, 요청당 조문 총 100개. 초과 시 Zod 검증 실패
- 요청 단위 실행 예산 (
src/lib/execution-limits.ts): 하나의 HTTP 요청(JSON-RPC 배치 포함)이 공유하는 upstream 호출 수·응답 본문 byte 한도. 재시도와 안티봇 hop 까지 같은 예산에서 차감해, 한 번 계수된 요청이 수백 건의 업스트림 호출로 증폭되는 것을 막는다.MCP_MAX_UPSTREAM_REQUESTS(기본 48) /MCP_MAX_UPSTREAM_BODY_BYTES/MCP_MAX_TOTAL_UPSTREAM_BODY_BYTES/MCP_MAX_TOOL_RESPONSE_CHARS - 취소 전파: HTTP 연결 끊김과 MCP item 취소 신호를 AsyncLocalStorage 로 도구·체인·업스트림 fetch·백오프 대기까지 전파. 종전에는 클라이언트가 끊어도 응답 전달만 멈추고 서버 작업은 끝까지 돌았다. 배치 형제 item 은 예산은 공유하되 취소 신호는 분리 (
combineAbortSignals) - 상한이 걸린 응답 본문 리더 (
src/lib/response-body.ts):response.text()는 크기 훅이 없어 업스트림이 보내는 만큼 전부 메모리에 올린다. reader 로 바꿔 byte 예산과 취소가 스트리밍 도중에 작동 - 시작 시점 설정 검증 (
src/server/http-config.ts): 보안 관련 숫자 env 를 리스너 열기 전에 정수로 검증. 종전에는 오타 하나가NaN이 돼 해당 비교가 통째로 무력화됐다(RATE_LIMIT_RPM=0이면 배치 상한까지 같이 꺼지던 것 포함 — 이제 배치 상한은 IP 한도와 독립) - 패키징 검증 (
scripts/clean-build.mjs,scripts/verify-package.mjs):build/를 매번 지우고 빌드하며, 소스가 없는 산출물·예상 밖 경로·exports대상 부재를prepack/prepublishOnly에서 차단 scripts/verify-annex-runtime.mjs: optional 의존성 없이도 별표 PDF 파싱이 되는지 CI 에서 실증CONTRIBUTING.md,.github/workflows/publish.yml(GitHub Release → OIDC trusted publishing + provenance)
- stale 산출물 배포 (#82): 4.9.6 tarball 에 소스가 삭제된
build/server/sse-server.js가 남아 있었다.exports와일드카드로 import 하면 없어진 세션 상태 심볼을 참조해 터진다. clean build + packed 파일 검증으로 차단 pdfjs-dist직접 의존을^5.5.207→4.10.38로 고정. 5.x 는src/에서 import 하지 않는데도 advisory 를 달고 설치됐고, 실제 별표 PDF 파싱은 kordoc 의 optionalpdfjs-dist@^4.10.38이 한다. 4.10.38 로 맞추면 dedupe 되어--omit=optional설치에서도 별표 파싱이 살아 있다(verify:annex-runtime로 실증)honooverride^4.13.1,ip-addressoverride^10.5.0— 전이 advisory 정리.npm audit --omit=dev0건
- Dockerfile:
node:20-alpine→ Node 22.12.0 Alpine (digest 고정), 빌드·prune 단계에서--ignore-scripts --omit=optional. 컨테이너는MCP_HTTP_HOST=0.0.0.0을 기본 주입 - CI: Node 20.19.0·22.12.0 매트릭스, action 을 커밋 SHA 로 고정,
npm ci --ignore-scripts, 패키지·optional 제외 프로덕션 그래프 검증 단계 추가 - 플러그인
mcpServers실행을npx -y --ignore-scripts --omit=optional로
engines는>=20.19.0유지 — 통합 호스트(gomdori-mcp)가 Node 20 런타임이라 선언만 올리면 실제와 어긋난다. Node 20 EOL 대응은 통합 호스트 런타임 이전과 함께 별도로 진행publish.yml은 npm 쪽 trusted publisher 등록 전까지 동작하지 않는다. 등록 전에는 로컬npm publish를 계속 쓴다
- 폐지 법령·행정규칙 감지: 폐지된 규정은 현행 검색에 안 잡혀 LLM 이 '존재하지 않는 규정'으로 오판한다.
search_admin_rule은 현행 0건이면nw=2연혁을 재검색해 폐지일·직전 버전·폐지사유·후속 통합 규정을 안내하고,search_law는 eflaw 연혁에서 법령ID별 최신 이력이 폐지·타법폐지인 건을 MST+efYd 조회 힌트와 함께 안내한다. 제명변경 케이스는 신명칭으로 연결 (src/lib/abolished-laws.ts)
- 공용 키 폴백 쿼터의 429 폭증: 서버 LAW_OC 폴백 게이트가 고정창(fixed window)이라, 무키 사용자 전원이 공유하는 전역 한도가 창 초반에 소진되면 나머지 사용자가 남은 창 내내 429를 맞았다. 프로덕션 실측에서 무키 요청 3건 중 2건이 즉시 429(
Shared API quota exceeded). 토큰버킷(연속 리필)으로 교체해 같은 평균 처리율에서 버스트를 흡수한다 (src/lib/rate-limit.ts신설,http-server.ts배선) - 429 응답의 클라이언트 처리 불가: IP 한도 초과 응답이
{"error":"Too many requests..."}평문이라 MCP 클라이언트가 JSON-RPC 에러로 파싱하지 못했다. JSON-RPC 형식으로 통일하고, 폴백·IP 양쪽 429에Retry-After헤더와 본문 대기 초 안내를 추가
FALLBACK_DAILY_CAP: 서버 키 폴백의 롤링 24시간 총량 캡 (기본0= 비활성). 분당 한도를 완화하면서 하루 총 호출량은 묶어 법제처 서버 키 quota를 보호한다FALLBACK_RATE_LIMIT_BURST: 폴백 토큰버킷 용량 (기본 =FALLBACK_RATE_LIMIT_RPM, 즉 1분치)rate-limit.test.ts11 케이스 (리필·용량 상한·배치 차감·일일 캡 롤링·거부분 미소모)
- 공개 서버(
mcp.gomdori.app/law) 설정:FALLBACK_RATE_LIMIT_RPM30 → 120,FALLBACK_DAILY_CAP43,200 (종전 30rpm의 24시간 이론 총량 — 총량 유지, 버스트 4배 완화). gomdori-mcpfly.production.toml
- 별표 목록에서도 신설 별표 노출 (#77 후속): 4.9.4가 추출 경로만 고쳐, 목록 조회(
get_annexes({ lawName }))로는 licbyl 인덱스 미등재 별표(별표11 해사국제상사법원 등)를 발견할 수 없었다. 법령 목록 조회 시 현행 본문 별표단위와 대조해 누락 항목을[현행 본문 신규 — 검색 인덱스 미등재]마커와 함께 병합 표시 (src/tools/annex.ts,annex-canonical.tsfindMissingUnits)
- kordoc
^4.5.0→^4.7.2: 별표 파싱 엔진 최신화 — PDF/HWPX/HWP5 밑줄(<u>) 보존, PDF 링크 어노테이션, 폐쇄망 배포 대응(KORDOC_OFFLINE 게이트) 반영. 별표3 HWP·PDF 폴백 경로 실 API 회귀 확인
get_annexes 별표 파일을 licbyl 검색 인덱스 링크로 받던 것을 현행 법령 본문(lawService)의 별표단위 링크 우선으로 교체. licbyl 인덱스는 별표 파일 재업로드를 늦게 반영해 구본(결함본)을 가리키거나 개정 신설 별표를 목록에서 통째로 누락한다. 파서(kordoc) 문제가 아니라 데이터 소스 문제였다.
-
별표3 조회 시 인천지방법원·부천지원 행 누락 (#77, @jeongsuho-lawyer): 「각급 법원의 설치와 관할구역에 관한 법률」 별표3·별표5에서 인천 계열 행이 통째로 빠진 채 반환되던 문제. 원인은 다층 rowspan 파싱이 아니라 licbyl 인덱스가 가리키는 첨부파일 자체의 결함 — 2026-03-17 개정(인천고등법원 신설로 별표 재편)에서 서울고법 블록의 인천 삭제만 반영되고 인천고법 블록 추가가 누락된 구본 파일(HWP·PDF 동일)이었다. 원본 HWP의 CFB 전 스트림에서 '인천' 0히트, 표 레코드도 61행으로 자기일관적이어서 파서 측 경고로는 잡을 수 없는 유형. 현행 본문이 참조하는 정본 파일에는 인천고법 블록(본원·북부·부천)이 온전히 있고 kordoc이 그대로 파싱한다. 이제 법령 별표는
lawService.do?target=law의 별표단위 링크를 우선 사용하고 조회 실패 시 licbyl 링크로 폴백 (src/lib/annex-canonical.ts,src/tools/annex.ts)licbyl 링크(구본): flSeq 162409759 → 인천 0회 (지방법원 17곳) lawService 링크(정본): flSeq 164115145 → 인천고법 블록 포함 (18곳 + 세종지법) -
개정 신설 별표가 licbyl 목록에 없어 조회 불가: 같은 개정으로 신설된 별표11(해사국제상사법원의 관할구역)이 licbyl 검색에 아예 나오지 않아
NOT_FOUND로 빠지던 문제. licbyl 매칭 실패 시 현행 본문 별표단위에서 번호로 재매칭해 신설 별표도 조회된다 (src/lib/annex-canonical.tspickAnnexUnit)
행정규칙 전문이 자연어·체인 경로에서만 조회되지 않던 문제 수정. get_admin_rule 직접 호출은 멀쩡했고 체인이 넘기는 식별자만 틀렸는데, 안내문이 이를 「법제처 API 제한」으로 단정해 원인 추적까지 막고 있었다(보고자는 한동안 법제처 제약으로 판단하고 우회 설계를 검토). 같은 파일에서 compare_admin_rule_old_new 가 응답 실형상과 어긋나 매 호출 빈 결과이던 것도 함께 수리. 로컬 회귀 129건 통과 + 실 API 대조.
-
행정규칙 전문 조회가 체인 경로에서 항상 NOT_FOUND (#72, @gonnarun):
lawService.do?target=admrul&ID=가 받는 값은 행정규칙일련번호(13자리)인데 체인이 검색 결과에서 행정규칙ID(4~5자리)를 뽑아 넘기고 있었다.search_admin_rule출력이 두 값을 나란히 내보내는 데다get_admin_rule스키마 설명도행정규칙ID라고 적혀 있어, 체인뿐 아니라 LLM 직접 호출도 같은 값을 고르게 유도했다.docs/API.md는 이미 13자리로 명시돼 있었고 코드만 어긋나 있던 케이스. 체인 정규식을행정규칙일련번호로 바꾸고, 스키마 설명·검색 출력 라벨에 어느 값이id인지 못 박음 (src/lib/tool-chain-config.ts,src/tools/admin-rule.ts)ID=2100000271110 (행정규칙일련번호) → 209,749 B · 조문내용 114블록 ID=36934 (행정규칙ID) → 138 B · "일치하는 행정규칙이 없습니다" -
compare_admin_rule_old_new가 매 호출 빈 결과: 검색은 항목 태그를admrul로 찾는데 신구법 응답의 실제 항목은<oldAndNew>(필드도신구법명·신구법일련번호)라 항상 0건, 본문은<구조문>/<신조문>을 찾는데 실제 구조는<구조문목록>/<신조문목록>안의<조문 no="N">이라 항상[NOT_FOUND] 신구법 대조 데이터가 없습니다였다. 본문 조회id역시 신구법일련번호(13자리)다. 구·신 시행일 병기, 개정 부분<P>표시를【 】로 보존(<신 설>·<단서 신설>같은 꺾쇠 본문 표기는 태그로 오인해 지우지 않는다) (src/tools/admin-rule.ts)
- 전문이 비어 있을 때 원인별 안내로 분리 (#72): 종전에는 원인과 무관하게 「법제처 API 제한: 일부 행정규칙은 전문 조회가 지원되지 않습니다」로 단정했다. 이제 ① 기본정보조차 없으면 식별자 오류(일련번호 13자리를 넘기라고 명시) ②
조문형식여부=N이면 첨부파일 전용 ③ 그 외에만 전문 미제공으로 안내한다. 「전문 조회 미지원」은 ②에만 해당하는 진단이었다 (src/tools/admin-rule.ts)
verify_citations 가 조용히 검증을 건너뛰던 표기 3종을 해소. 셋 다 "검증 실패"가 아니라 검증 미가동이라, 같은 텍스트에 없는 조문이 섞여 있어도 ✗ 가 나오지 않아 환각 게이트로 쓸 때 "통과"로 오독되던 문제다. 로컬 회귀 122건 통과 + 실 API 대조.
「법령명」 제N조에서 법령명 추출 실패 (#69, @BW-YU):LAW_NAME_REGEX는$앵커로 법령명 종단을 찾는데 표준 인용 표기의 닫는 낫표가 lookback 끝에 남아 앵커가 걸리지 않았다(기존 코드는 후행 공백만 제거). 닫는 인용기호(」』】〕>)를 함께 벗기고 로직을extractLawName으로 분리. 낫표 없는 평문 경로(#55 수식어 순차 축약)는 동작 불변 (src/tools/verify-citations.ts)- 가운뎃점 표기 차이로 공식 법령명과 불일치 (#69, @BW-YU): 법제처 공식 법령명은 한글 가운뎃점
ㆍ(U+318D) 를 쓰는데(식품 등의 표시ㆍ광고에 관한 법률) 실무 문서·판결문·LLM 출력은 라틴 중점·(U+00B7)가 보통이라,looseMatchLawName정규화가 공백만 제거해 표기만 다른 같은 법이 불일치로 떨어졌다.·ㆍ‧•・5종을 정규화에 편입 — 무관 법령 차단(민법→난민법)은 유지 (src/lib/law-search.ts) 같은 법 시행규칙 제N조가 무관 법령에 매칭 (#70, @gonnarun):「A법」 제N조 및 같은 법 시행규칙 제M조는 법제처 조문·행정규칙·관공서 서식의 표준 표기인데같은 법조응이 해소되지 않아 두 지점에서 어긋났다. ① 후보 축약의 마지막 단계가 접미사 단독 후보(시행규칙·법 시행규칙)를 만들어 어떤 문서에서든 무관 법령을 물어왔고(관측:119긴급신고의 관리 및 운영에 관한 법률 시행규칙), ② 30자 lookback 안에 선행 법령명이 있어도 승계되지 않았다.lawNameCandidates가 접미사뿐인 후보를 버리도록 하고(후보 0개면 검색을 시도하지 않고⚠ 법령명 불명확— 검색 0건을✗ NOT_FOUND로 낙인하지 않는다),parseCitations가 직전 법령명을 들고 가며같은 법·동법을 승계하도록 했다. 선행 법령명이 없거나 빈 줄로 문단이 바뀌면 승계하지 않는다(무관 법령을 근거로 판정하는 게 더 나쁜 오답). 조응 판별은 추출된 법령명이 아니라 lookback 꼬리를 보므로 앞 인용의 잔재(조 같은 법 시행령)에 흔들리지 않고,노동법의동법을 조응으로 오인하지 않는다 (src/tools/verify-citations.ts)
실 API 대조 (「노인장기요양보험법」 제38조제1항 및 같은 법 시행규칙 제30조):
before: ⚠ 0 실존 / 2 확인필요 — '119긴급신고의 관리 및 운영에 관한 법률 시행규칙'으로만 매칭
after : ✓ 2 실존 — 노인장기요양보험법 제38조 제1항 / 노인장기요양보험법 시행규칙 제30조
같은 텍스트의 제999조는 ✗ NOT_FOUND (존재 범위: 제1조~제44조) 로 탐지 — 환각 게이트 가동
- 고시·행정규칙 인용(
식품등의 표시기준 제2조)은LAW_NAME_REGEX접미사 목록 밖이라 여전히 미추출.admrul조회 경로가 필요하고, 잘못 붙이면 실존 고시가✗로 낙인될 위험이 있어 분리.
외부 기여 PR 5건 반영 — 행위시법 판단·연혁 파싱·검색 리졸버·재시도·폐지 법령 처리 정확도 개선. 로컬 회귀 106건 통과.
- DRF 간헐 404 재시도 +
type=HTML정상응답 재시도 증폭 제거 (#63, @zzocrypto): 유효한mst+jo조회가 간헐적으로 404를 반환하고 동일 파라미터 재시도 시 성공하는데,fetchWithRetry기본 재시도 대상([429,503,504])에 404가 빠져 1차 실패가 "조문 부재"로 오답되던 것 — DRF 호출 전체에[404,429,503,504]적용(소진 후에만 실패). 또lsHistory등type=HTML정상응답이 HTML-본문 감지 재시도에 걸려 대형 연혁(소득세법류)에서 ~40s까지 늘어지던 것을allowHtmlBody로 제외(HTML 에러페이지 감지는 유지) —40s→4s (src/lib/fetch-with-retry.ts,src/lib/api-client.ts) - 분리시행 법령의 적용 버전 오특정 등 행위시법 6건 (#64, @zzocrypto): 조항별 시행일이 나뉘는 법령(중대재해처벌법 50인 미만 3년 유예 등)에서
applicable_law가 잘못된 버전을 "기준일 시행 중"으로 특정하던 것을 eflaw 범위검색 시행 슬라이스 보정(fetchEffectiveSlices)으로 수정. 동일 시행일 tie-break(공포일·공포번호 내림차순), 적용 버전 자신의 부칙 발췌, 무패딩 날짜("2010.1.1") 파싱, '폐지제정' 오분류, jo 지정 시 조문번호 검증 포함. eflaw 실패 시 기존 lsHistory 결과 유지(보수적 폴백) (src/tools/applicable-law.ts,src/lib/historical-utils.ts) - 연혁 페이징 조기종료·NaN 정렬·조문 출력 훼손·제21항+ 미지원 (#65, @zzocrypto): 연혁 수집 종료를 필터 후 행수 대신 원시 총계(totalCount) 기준으로 변경(500행 초과 법령의 옛 본법 버전 누락 방지),
time_travel버전선택 정렬의 빈 시행일 NaN 폴백,get_historical_law의[object Object]·N/A출력을safeText평탄화로, 원숫자 매핑을 ①⑳에서 ㉑㊿까지 확장(제21항+ 인용을 "존재않는 항"으로 오판하던 것 수정) (src/lib/historical-utils.ts,src/lib/article-parser.ts,src/tools/historical-law.ts,src/tools/scenarios/time-travel.ts)- 병합 시 보강:
parseTotalCount가 콤마 표기(<strong>1,696</strong>)도 파싱하도록 방어 — #65가 totalCount를 페이징 종료 1차 기준으로 승격했는데\d+로만 잡으면 "1,696"이 "1"로 끊겨 대형 법령이 오히려 1페이지에서 조기 종료되는 역행을 막음
- 병합 시 보강:
findLaws기본 조회 20이 관련도 정렬을 굶겨 무관 법령을 반환 (#66, @zzocrypto):applicable_law(lawName="상법")같은 호출이 전혀 무관한 법령 분석을 확신형으로 출력하던 것 — 법제처 LIKE+가나다순 검색에서 정확매칭이 앞 20건에 없어 무관 부분매칭 1위를laws[0]로 신뢰하던 문제. 기본searchDisplay20→100(비용 동일 1콜),looseMatchLawNamelib 승격, 별칭 canonical 해소 대조(resolvedLawMatches),applicable_law·impact_map에 무관 1위 차단 가드(NOT_FOUND + 정식명 재확인 안내) (src/lib/law-search.ts,src/tools/applicable-law.ts,src/tools/impact-map.ts)
- 폐지 법령 인용을 '환각'으로 오탐하지 않도록 REPEALED 분리 보고 (#67, @yabooung):
verify_citations가 현행 검색만 써서 폐지 법령 인용(예 「국유재산관리특별회계법 제6조」, 2007 폐지)을 검색 0건→[HALLUCINATION_DETECTED](isError:true)로 낙인하던 것 — 현행 0건일 때만target=eflaw1회 폴백해현행연혁코드="연혁"이면⌛ [REPEALED_REFERENCE]로 분리 보고(폐지 건은failCount미포함 →isError불변). 정상(✓)·진짜 환각(✗) 판정은 불변, 정상 인용 추가 비용 0 (src/tools/verify-citations.ts,src/lib/law-search.ts)
get_law_abbreviations완전 복구 (#61): 항목 태그를 존재하지 않는<lsAbrv>로 찾아 매 호출 "약칭 데이터가 없습니다"로 실패하던 것 수정. 실제lawSearch.do?target=lsAbrv응답의 항목 태그는<law>, 필드는법령명한글/법령약칭명이다.display=100전달 + 총계를 응답totalCnt로 정직 표기(조회분을 총계로 위장하지 않음) (src/tools/utils.ts)- 연계 3종 루트 태그 +
get_law_tree파서 복구 (#62):lnkLsOrdJo/lnkDep/lnkOrd의 실제 응답 루트가lnkOrdJoSearch/lnkDepSearch/OrdinSearch인데 대문자 루트(LnkLsOrdJoSearch등)를 가정해 항상 0건(NOT_FOUND). 루트 매칭을 대소문자 무시로 변경.lnkDep은 법제처가 서버 검색 필터를 지원하지 않아(전체 덤프) 조회 페이지 내 클라이언트 필터 + "전수 아님" 한계 명시로 전환하고get_linked_ordinances사용을 안내.get_law_tree는get_three_tier실제 출력(법령명:헤더 +[시행령]/[시행규칙]인라인 마커) 기준으로 파서 재작성, 빈 입력은 스키마에서 거부, 데이터 없으면 빈 트리 대신 NOT_FOUND (src/tools/law-linkage.ts,src/tools/law-tree.ts)
search_law오법령 확장검색 차단 + 인공지능기본법 약칭 등록: 「인공지능 발전과 신뢰 기반 조성 등에 관한 기본법」의 통칭 "인공지능법"이 정식 제명의 부분문자열이 아니라 LIKE 검색이 0건이 되고, 키워드 확장이 만든 "AI법" 쿼리에 법제처가 검색어를 무시한 가나다순 무관 법령 50건을 반환 → LexDiff fast-path가 첫 항목(가맹사업법)을 집어가던 사고의 근원. 약칭(인공지능법/인공지능기본법/AI법/ai기본법) 등록 (src/lib/search-normalizer.ts)hasRelatedHit가드: 확장쿼리 결과에 법령명·약칭이 쿼리와 포함관계인 항목이 하나도 없으면(= API가 쿼리를 무시한 응답) 채택하지 않고 다음 확장쿼리·폴백으로 진행 (src/tools/search.ts). 약칭 해석 4종 +hasRelatedHit4종 테스트 추가
이 변경의 커밋 메시지에는
v4.6.7로 적혀 있으나 4.6.7은 발행된 적이 없으며, 실제로는 4.7.4로 배포되었습니다.
- 행정규칙(고시) 별표 조회 복구 (#58):
get_annexes가 「사료 등의 기준 및 규격」처럼 제목에 '고시·훈령·예규' 등 종류 키워드가 없는 행정규칙 별표를NOT_FOUND로 반환하던 문제 수정.detectLawType이 이런 이름을law로 분류해licbyl(0건)만 조회하고admbyl경로를 놓치던 것이 원인. admin(admbyl) fallback을 기존/규정/이름 제한에서 모든 미매칭 케이스로 일반화해,search_admin_rule로 검색되는 행정규칙이면 별표(예: 별표16 「사료 내 유해물질의 범위 및 허용기준」)도 정상 추출된다 (src/tools/annex.ts)
verify_citations수식어 앞 법령명 재시도 (#55): 법령명 앞에 수식어가 붙은 완전문장("절도죄는 형법 제329조…")에서 조문검증이⚠ PARTIAL_VERIFIED로 저하되어 환각(없는 조문·제목 불일치)이 탐지되지 않던 문제 수정.looseMatch실패 시 앞 어절을 순차 축약하며findLaws를 재시도하되, 다어절 법령명(「전자상거래 등에서의 소비자보호에 관한 법률」)은 전체 후보를 먼저 두어 보존 (src/tools/verify-citations.ts)
- hono 4.12.22 → 4.12.29:
npm auditHIGH 5건 해소.@modelcontextprotocol/sdk의 전이 의존성이라overrides로 고정 (#54)
PlayMCP 심사 피드백 대응.
legal_researchtask 오인 흡수: LLM이scenario값(penalty등)을task에 잘못 넣어도scenario로 재배치하고 호환 task로 승격해 툴콜 실패를 제거(의도 보존). 미지의 task 값은full_research로 폴백. 실행 경로·API 호출은 불변 (src/tools/legal-research.ts)ordinance_radarquery 별칭:query파라미터 별칭을 추가해search_law등 다른 도구와 규약을 통일 — 자연어 조례명으로 호출 시 실패하던 문제 해소 (src/tools/ordinance-radar.ts)
ordinance_radar— 조례 정비 레이더: 조례 제1조(목적)의 「」 인용에서 근거 상위법령(법률·시행령·시행규칙, "같은 법" 축약 포함)을 추출한 뒤 각 상위법의 현행 시행일과 조례 시행일을 대조해 정비 검토 대상을 자동 플래그. 법제처 자치법규 연계 API(lnkOrd)는 커버리지 부족(주차장 조례 0건)으로 미사용하고 본문 표준 표기 파싱으로 대체. 목적 조문만 스코핑해 별표의 무관 인용(공직선거법 등) 과잉경보를 배제(32 → 3건).V3_EXPOSED10번째 직노출 도구 (src/tools/ordinance-radar.ts)
- JSON-RPC 배치 증폭 차단: 배치의
tools/call을 개수만큼 rate limit·폴백 쿼터에 계수 — 단일 POST에 수백 개를 담아 서버LAW_OC쿼터를 소진시키는 증폭 벡터를 차단. 요청당tools/call상한 20 (MCP_MAX_BATCH_CALLS) (src/server/http-server.ts)
- graceful shutdown: idle keep-alive 연결을 끊지 않아 매 배포마다 10초 대기 후
exit(1)로 기록되던 문제 →closeIdleConnections()+exit(0)(src/server/http-server.ts) get_article_historylawName 정확매칭 우선: 검색 첫 결과를 무조건 채택해 '상법' 입력 시 가나다순 앞선 타법을 오조회하던 문제 수정get_ordinanceid 별칭: id 별칭으로 호출할 때 다음 단계 힌트가ordinSeq="undefined"로 출력되던 버그 수정maskSensitiveUrl대소문자 무시(gi): API 키 마스킹 회귀 방지 (src/lib/fetch-with-retry.ts)
- "간헐적으로 도구를 못 찾음" 근본 해결: 일반 rate limiter가 핸드셰이크(
initialize/tools/list)까지 429로 막던 문제 →tools/call만 게이트. claude.ai의 공유 egress IP가 60rpm 버킷을 나눠 쓰다 핸드셰이크에서 429를 맞으면 도구 목록이 통째로 유실되던 것이 원인 (v4.6.2의 폴백게이트 원칙을 일반 limiter까지 확장) (src/server/http-server.ts) get_ordinance데드엔드:id↔ordinSeq불일치로 조례 본문 조회가 막히던 문제 →id별칭 수용get_article_history상시 0건:lsJoHstInf에 날짜를 전달하지 않아 모든 법령의 조문 개정이력이 항상 0건이던 문제 → 날짜 미지정 시 전체기간(19480101~20991231) 자동 적용- 발견성:
search_law설명에 조례·행정규칙 진입점 명시 (src/tool-registry.ts)
- ToolAnnotations
destructiveHint추가: ListTools 광고 annotations에destructiveHint: false명시. 9개 도구 모두 법제처 read-only 조회라 파괴적 동작 없음을 명시적으로 선언 — 일부 MCP 호스트(카카오 등) 등록 심사가destructiveHint정의를 필수로 요구해 경고가 발생하던 문제 해소 (src/tool-registry.ts)
- MCP 도구 annotations 한글 title 제거: claude.ai 웹 클라이언트가 비-ASCII(한글)
annotations.title이 붙은tools/list를 인식하지 못해 "도구 없음"으로 뜨던 문제 대응. 서버·도구 호출 자체는 정상이었고(curl·GPT·Claude Code 세션 모두 작동) claude.ai 웹만 실패. v4.5.1에서 추가한TOOL_TITLES를 제거하고 영문name을 그대로 노출 (src/tool-registry.ts)
search_law자치법규 자동 폴백: '광진구 복무 조례' 같은 쿼리가NOT_FOUND+ 키워드 축소 힌트만 반환해 LLM이discover_tools→execute_tool2턴을 돌아가던 문제. 쿼리에 '조례'가 포함되거나 ○○시/군/구 토큰이 있으면search_ordinance를 자동 시도한다(행정규칙 폴백과 동일 패턴) (src/tools/search.ts)
- 폴백 쿼터 게이트를
tools/call만 적용:FALLBACK_RATE_LIMIT_RPM소진 시initialize/tools/list까지 429로 막혀 claude.ai 커넥터가 도구 목록 자체를 못 싣던 문제 수정. 법제처 쿼터를 실제 소모하는tools/call만 게이트한다 (src/server/http-server.ts)
- README 현행화: v4.6.0 릴리스 노트(인용 내용검증 + law.go.kr JS 안티봇 우회) 추가, tagline을 "인용 검증(실존+내용)"으로 갱신
- README-EN 현행화: 영문판이 v4.3부터 뒤처져 있던 것을 보강 — v4.4.0(도구 통폐합)·v4.4.1–4.4.3(안정성)·v4.5.0(시행예정)·v4.6.0 릴리스 노트 반영
- 코드 변경 없음(문서 전용 패치)
- 조문 제목 내용검증:
verify_citations/legal_analysis(mode=verify_citations)가 기존 "조문 실존" 검증에 더해, 인용한 조문제목이 실제 조문제목과 일치하는지 대조.민법 제750조(계약해제)처럼 존재하는 조문에 엉뚱한 제목을 붙인 내용 환각을[CONTENT_MISMATCH]로 탐지 (기존엔 750조만 실존하면 통과). LexDiff의citation-content-matcher(정규화 후 exact 30자 공통 substring + 문자 bigram Jaccard ≥ 0.25) 이식 (lib/citation-content-matcher.ts).제N조(제목)형태의 제목만 검증 대상, 개정이력·날짜·항호 참조 괄호는 제외
location.assign리다이렉트 추적: 클라우드 IP(GCP/AWS/Fly)에서 법제처가 API 데이터 대신 JS 안티봇 페이지를 반환할 때, 난독화 URL(concat/substr 2패턴)을 파싱해 토큰 URL로 우회 (lib/law-antibot.ts, 최대 3홉, 토큰 URL 404 시 원본 재시도). 로컬/등록 IP에선 no-op.fetch-with-retry가 law.go.kr 호스트 응답에만 적용해 방어층 추가
law-antibot.test.ts(3),citation-content-matcher.test.ts(8) 신설 — vitest 44 케이스 그린
- 광고 서비스명 정정: ListTools 광고 접두를 PlayMCP 등록명과 일치하도록 「국가법령정보 MCP」 →
Korean-law-mcp로 변경. 서비스명 검증은 등록폼 이름과 정확히 일치해야 통과 (src/tool-registry.ts)
- PlayMCP 등록용 tool annotations: 노출 도구의 ListTools 광고에 MCP annotations(read-only 조회·멱등·openWorld)를 추가하고 description에 서비스명을 접두. 원본
allTools정의는 그대로 두고registerTools광고 시점에만 주입해 STDIO·HTTP 양쪽에 반영. PlayMCP 검증 2건(annotations 미정의·서비스명 누락) 해소 (src/tool-registry.ts)
search_law시행예정 법령 감지: 제명변경 개정이 공포~시행 사이에 있으면 신명칭 검색 시 '정확매칭 없음'만 떠서 LLM이 "법령 없음"으로 오판하던 문제 해결(「데이터기반행정 활성화에 관한 법률」 → 「인공지능 및 데이터 기반 행정 활성화에 관한 법률」 사례).target=eflaw보조검색으로 시행예정만 추출해 MST별 시행일을 병합하고, 제명변경·개정예정·미시행 신규 3분류 노트를 생성한다 (src/lib/upcoming-laws.ts)- 검색 결과에 시행예정 노트를 병기하고, 현행이 0건이면 시행예정을 단독 안내(효력 없음 경고 포함).
api-client.searchLaw에target(law|eflaw) 파라미터 추가. 시행예정본 조회 힌트에efYd를 필수 병기 —efYd없이는 법제처 API가 404를 반환
- 번호 없는 단일 별표 매칭 폴백:
get_annexes가 별표 선택값("별표1" 등)으로 매칭에 실패해도, 해당 법령의 별표가 정확히 1건뿐이면 그 별표를 반환한다. 「여권법 시행령」의 '수수료 및 사무의 대행에 드는 비용(제39조 관련)'처럼 별표번호가000000인 번호 없는 단일 별표는 LLM이 임의로 "별표1"로 호출하면findMatchingAnnex매칭 0건 →NOT_FOUND로 새어 "참조 조문 부족" 답변을 유발했음. 별표가 유일 1건이면 선택값이 불일치해도 정답이 명확하므로 폴백 (src/tools/annex.ts)- 회귀 안전: 별표가 여러 건인 법령(관세법 24건)에서 존재하지 않는 번호는
NOT_FOUND유지
- 회귀 안전: 별표가 여러 건인 법령(관세법 24건)에서 존재하지 않는 번호는
이 변경(
fde094c)의 커밋 메시지에는v4.4.3으로 적혀 있으나, 원격에서 4.4.3(zod^4pin)이 먼저 npm에 게시되어 같은 버전으로는 publish가 거부됐습니다. 4.4.4로 올려 실제 배포했습니다.
z.toJSONSchema is not a function크래시:dependencies가 zod를^3.25.76 || ^4.0.0으로 선언했으나 코드는 zod v4 전용 API인z.toJSONSchema()를 호출. 새 npm 설치가 zod 3.25를 해석하면listTools첫 호출에서 크래시. zod를^4로 고정해 해결
- 행정규칙 별표 응답 키 버그 (#50):
get_annexes가 행정규칙 별표 응답 배열 키를admbyl로 파싱했으나 법제처 실제 키는admrulbyl—totalCnt > 0인데도 0건으로 처리돼 "법제처 DB에 없음"으로 응답하던 문제.admrulbyl우선 +admbyl폴백으로 수정 (tools/annex.ts). 실측 검증: "외부감사 및 회계 등에 관한 규정 시행세칙" 65건 정상 반환, 별표6 HWP 본문 추출까지 확인 - 행정규칙 자동 판별 누락:
detectLawType이 훈령/예규/고시/지침/내규만 admin으로 인식 —세칙추가로 "...시행세칙"이 1차부터target=admbyl로 조회되도록 보정 (lib/api-client.ts). "규정/규칙" 단독은 시행규칙 오분류 위험이 있어 기존 4차 fallback에 위임 - 소관부처 표시 누락: admin 별표 항목의 부처 필드가
소관부처명인데소관부처를 참조해 미표시되던 것 보정 - 별표/서식 동일 bylSeq 충돌 (#49): 자치법규·행정규칙에서
[별표 N]과[별지 제N호서식]이 같은 별표번호를 공유할 때 서식이 잘못 선택되던 문제 —별표종류+knd로 구분 (외부 기여: @hanbrook3) - 별표 의-번호 파싱 (#51):
별표1의2입력 시 정규식이의2를 법령명에 잔류시켜 NOT_FOUND 나던 문제 — 법제처 6자리 코드(000102)로 직접 변환 (외부 기여: @hoiyada7-maker)
- 광고 스키마 required 버그:
z.toJSONSchema()가 기본io:"output"모드라.default()필드를 required로 직렬화 —legal_research.task("미지정 시 full_research"인데 required로 광고),search_law.display가 강제 입력으로 노출되던 문제.{ io: "input" }명시로 수정 - scenario 무음 폐기 제거: task와 비호환인 scenario를 조용히 버리던 것을 응답 첫 줄 경고 노트로 명시 (
⚠ scenario=X는 task=Y와 비호환이라 무시하고 자동 감지로 대체). 호출 LLM이 파라미터 무시를 인지 가능 - task↔scenario 호환표 단일화: 수동
TASK_SCENARIOSSet +as캐스트 제거 → 체인 스키마(chains.ts)의shape.scenario에서 직접 파생(pickScenario). 체인 enum 변경 시 자동 추종, 드리프트 원천 차단 legal_analysis비용 옵션 패스스루:maxCitations·display·deepScan·includeOrdinances·includeMermaid를 하드코딩에서 optional 파라미터로 — 비싼 변형(deepScan 본문 스캔, 전국 조례 팬아웃, mermaid)을 노출 프로필에서 직접 끌 수 있음. 기본값은 원본 도구와 동일- 타입 통일:
legal-research.ts의Awaited<ReturnType<...>>핵 제거 →LooseToolResponse로 통일 - 테스트 신설:
test-legal-research-analysis-dispatch.cjs— 광고 스키마 계약(io:input required + apiKey 숨김), task×scenario 호환 매트릭스(6×9+미지정), 필수 파라미터 가드, withNote 주입, TOOL_COUNTS 파생값 - 문서 정리: CLAUDE.md stale 노출 수(19개→9개), API.md에 legal_analysis 패스스루 옵션 표기
MCP 클라이언트의 ListTools 컨텍스트 비용 ~15.1KB → ~7.2KB (실측, ≈6,000 → ≈2,900토큰).
legal_research신설:chain_*8개를task파라미터로 통합 (full_research·law_system·action_basis·dispute_prep·amendment_track·ordinance_compare·procedure_detail·document_review). scenario/domain/articles 등 기존 파라미터 전부 유지, task별 비호환 scenario는 무시하고 자동 감지에 위임legal_analysis신설: 킬러피처 4개(verify_citations·cite_check·applicable_law·impact_map)를mode파라미터로 통합. 세부 옵션(deepScan, includeMermaid 등)은 원본 기본값 적용- 하위호환 보장: 원본 12개 도구는
allTools에 유지 — CallTool 직접 호출·execute_tool경유 모두 기존대로 동작. 광고(ListTools)만 제외 - apiKey 스키마 노출 제거: 정식 경로는 HTTP 헤더(session-state)이므로 광고 스키마에서 숨김. 인자로 넘기는 기존 클라이언트는 Zod parse가 계속 수용
discover_tools설명의 하드코딩 도구 수(73개) 제거
최종 노출 9개: legal_research, legal_analysis, search_law, get_law_text, get_annexes, search_decisions, get_decision_text, discover_tools, execute_tool
"이 판례 아직 유효한가?" — 변경·폐기된 판례를 살아있는 것처럼 인용하는 사고 방지.
- 사건번호(
nb=)로 대상 판례 특정 → 본문검색(search=2)으로 그 사건번호를 인용한 후속 판례 역추적 - 전원합의체 우선 본문 정밀 스캔: "변경하기로 한다 / 폐기 / 더 이상 유지될 수 없다 / 배치되는 범위에서 변경" 감지
- 별칭 추적: 판결문이 "(이하 '2008년 전원합의체 판결'이라 한다)"로 별칭 정의 후 별칭으로 변경 선언하는 관행 대응 — 사건번호만 쫓으면 false negative (2007다27670 → 2018다248626 변경 케이스로 검증)
- 판정 4단계: ❌ 변경·폐기 신호 /
⚠️ 미스캔 전합 후속 존재 / ✅ 계속 인용 추정 / ℹ️ 후속 인용 없음 - 한계 명시: 법제처 수록 판례(대법원 중심) 범위 — 출력에 고지하여 과신 방지
"사건 시점(2023.5.10)에 적용되는 법은?" — LLM이 현행법으로 오답하는 것 방지.
- lsHistory 연혁으로 기준일에 시행 중이던 버전(MST) 특정 + 그 시점 조문 본문 (eflaw는 MST+efYd 동반 필수)
- 현행 조문과 동일/변경 비교 (변경 시 time_travel 연계 안내)
- 이후 개정 부칙에서 적용례·경과조치 자동 발췌 (공포번호 매칭, 조문 지정 시 해당 조문 언급 라인 우선)
- 행위시법(형법 §1)·제재처분 위반행위시법(행정기본법 §14③)·처분시법 법리 안내 — 해석은 하지 않고 발췌만 (사람/LLM 몫)
- V3_EXPOSED 17 → 19개 (cite_check, applicable_law 직노출), 내부 도구 93 → 95개
- query-router: 사건번호+유효성 키워드 → cite_check, 기준일+법령명 → applicable_law 자동 라우팅
- 행위시법 의도 쿼리는 날짜 제거 전 원문으로 매칭 (날짜 자체가 파라미터)
- specific_article이 "2023.5.10 당시 ... 제44조" 패턴을 applicable_law에 양보
- P0
findLaws가 타임아웃·5xx를 삼켜 "법령 없음"으로 둔갑 → 인프라 에러는 throw로 전파. 법제처 장애 중 verify_citations가 실존 조문을 NOT_FOUND로 오판하던 설계 모순 해소 - P1 HTTP API 키 수신 우선순위를 헤더 > 쿼리스트링으로 변경 (프록시 액세스 로그 평문 유출 방지)
- P1 서버 LAW_OC 폴백에 전역 상한 추가 (
FALLBACK_RATE_LIMIT_RPM, 기본 120rpm) — 키 없는 분산 요청의 quota 소진 방지 - P1 runScenario 무음 예외 삼킴 → [FAILED] 섹션 반환 (LLM이 "결과 없음"과 "실행 실패" 구분 가능)
- P1 get_law_text TOC 캐시 히트 시 50KB 절단 우회 수정 (절단본을 캐시)
- P2 graceful shutdown이 in-flight 요청 완료 대기 (최대 10초)
- P2 formatToolError에 maskSensitiveUrl 최종 방어선 추가
- P2 extractLawName 연속 키워드 제거 실패 수정 ("개정 연혁" → lookahead 경계)
- P2 tool-registry: 요청마다 93개 도구 재등록 → 모듈 로드 시 1회. 노출 수 하드코딩 →
TOOL_COUNTS파생값 - P2
toArray()헬퍼 도입 (Critical Rule 6 수동 패턴 8곳 치환), chains.ts 데드코드 제거
별표(HWPX/HWP5/PDF) 파싱 엔진 kordoc을 v3.0.0으로 업그레이드. API 변경 없음(parse() 그대로).
- HWPX 텍스트 재현율 99.699% → 99.998%, 표 구조 정확일치 100% (중첩표 343건 포함)
- 환각률(phantom) 0.019% → 0.006%, PDF consensus coverage 97.0% → 99.16%
- 중첩표 구조 보존, HWP5 BinData 이미지 추출, 한컴 PUA 기호 매핑, 머리말/각주/하이퍼링크 처리 강화
LLM이 도구 결과만 보고도 "이 본문이 현행인지"를 판단할 수 있도록 검색·본문 조회 출력에 현행성 메타데이터를 명시. (실사고: 소방 관련 질의에 2022년 분법 전 「화재예방, 소방시설 설치ㆍ유지 및 안전관리에 관한 법률」 기준 답변)
search_law: 법제처 응답의현행연혁코드·시행일자파싱 — 각 결과에[현행]/⚠️[연혁-과거버전]라벨 + 시행일 표기. 현행 우선 정렬, 첫 추천 항목이 연혁이면 현행 MST 사용 경고.get_law_text: 본문 헤더에 조회기준일 vs 시행일 비교 라벨 — 시행 예정 버전(미시행) 경고,efYd지정 시 "현행 아닐 수 있음" 경고, 연혁 MST 재확인 안내.get_law_text:이전법령명표기 — 개정/분법으로 명칭이 바뀐 법령은 "(구 법령명: …)"을 함께 출력해 LLM이 학습데이터의 옛 법령명과 혼동하지 않도록 함.
판례 검색을 공통 구조화 core로 모으고, 긴 자연어/개념형 질의의 누락을 줄이며, 검색→본문조회 연결을 안정화.
precedent-search-core.tssearchPrecedentsStructured(): 공통 판례 검색 진입점.hits/attempts/fallbackUsed/successfulAttempt구조화. 사건번호 우선 → 제목 검색 → 본문검색(search=2) 폴백. compact query로 긴 질의 보정. 날짜 필터 시 표시 hit·totalCount정합성 처리,date_relaxed폴백.precedent-evidence.tsfetchPrecedentEvidence(): 상위 hit를get_precedent_text에 연결(기본 2건/최대 5건). 부분 실패는 숨기지 않고 렌더링.validatePrecedentSearchResult()로 폴백 결과 질의 축 검증.compact-query-planner.ts확장: 법리축+사실축 후보 생성, 출처/점수/variant/검증 메타데이터 보존.search_decisions(domain="precedent", options.includeText=true)+options.detailLimit추가(기본 동작 유지, opt-in).- 체인 도구(
chain_full_research/chain_dispute_prep/chain_document_review) 판례 경로를 공통 core로 정리. - 조문 기반 도구(
article-with-precedents/impact-map)는fallbackPolicy: "none"으로 정확 검색 유지. docs/PRECEDENT-SEARCH-GUIDELINES.md추가.
기존 [id] 제목 렌더링·bracketed ID 추출 흐름 유지, 신규 노출 도구 없음.
search_decisions(includeText=true)가 상세조회 2건을 이어붙인 뒤 truncateResponse(50KB)를 한 번만 적용해, 합산이 한도를 넘으면 두 번째 판례가 통째로 잘리던 문제. fetchPrecedentEvidence가 성공 항목 본문에 건당 예산(MAX_RESPONSE_SIZE 균등 배분)을 미리 적용하도록 수정 → 모든 판례가 균형 있게 보존.
kordoc1.6.1 → 2.4.0 (별표 통합 파서 의존성 업데이트).
npm run build+ 판례 검색 관련 비-live 회귀 테스트 +test-precedent-evidence-budget.cjs(건당 예산 배분) + v4.0.8/4.0.9 회귀 테스트 통과.
법제처 OPEN API는 요청에 Referer 헤더가 없으면 OC 키가 유효해도 "사용자 정보 검증에 실패하였습니다(정확한 서버장비의 IP주소 및 도메인주소를 등록해 주세요)" XML을 반환한다(헤더 격리 테스트로 입증, 동일 키·동일 IP 기준 Referer 유무만으로 갈림). 메시지가 IP 화이트리스트 문제로 오인되기 쉬우나 실제 원인은 Referer 누락.
- v4.0.8의 빈/HTML 재시도는 증상(
missing root element)을 완화했을 뿐, 근본 원인은 이 Referer 누락이었음. fly 서버에서 재현 확인: Referer 없으면ECONNRESET/검증실패,Referer: https://www.law.go.kr/추가 시 정상 응답. 법제처가 최근(2026-05) 이 검증을 강화한 것으로 보임. fetch-with-retry.ts: 요청 호스트가law.go.kr계열일 때만 기본Referer주입(isLawGoKrHost). 호출자가 이미 지정했거나 다른 호스트(국세청taxlaw.nts.go.kr등)는 미주입.LAW_REFERER환경변수로 override 가능.
test/test-law-go-kr-referer.cjs: 호스트 판별·기본 주입·호출자 보존·override·서브도메인 통과.npm run build+ v4.0.8 빈/HTML 재시도 회귀 테스트 통과.
법제처 OPEN API가 간헐적으로 HTTP 200에 빈 본문 또는 HTML 점검 페이지를 반환할 때, search_law 등 XML 파싱 경로가 @xmldom의 missing root element(빈 본문) / Opening and ending tag mismatch(HTML) 예외로 터지던 문제. EXTERNAL_API_ERROR: missing root element로 노출되며 "됐다 안 됐다" 증상으로 보고됨.
원인 규명:
- IP 등록·OC 키 문제 아님. IP 미등록 시 법제처는 정상 형식의 XML(
<Response>사용자 정보 검증 실패</Response>)을 반환하고, 이 경우 도구는NOT_FOUND를 냄 —missing root element는 빈 응답/HTML(비-XML) 일 때만 발생. - 코드 회귀 아님. v4.0.6→v4.0.7 변경(
precedents.ts/external-https-proxy.ts)은search_law경로와 무관. 외부(법제처) 응답 불안정이 배포 시점과 우연히 겹친 것.
수정:
fetch-with-retry.ts: HTTP 200이어도 본문이 비었거나 HTML 페이지면 일시 장애로 간주해 재시도(exponential backoff). 정상 응답(XML<, JSON{/[)은 영향 없음. 모든 법제처 호출(법령·판례·조례 등)이 공통 혜택 —detectBadBody()추가.api-client.ts:searchLaw에checkEmptyResponse()(빈 응답 감지) +checkHtmlError()적용. 재시도 소진 후에도 빈/HTML이면missing root element대신 "법제처 API가 빈 응답을 반환했습니다. 일시적 장애일 수 있으니 잠시 후 다시 시도하세요" 안내.
- mock 서버 단위 검증: 빈 응답·HTML 응답 재시도 동작, 간헐 장애(빈 2회→정상 XML) 재시도 복구 확인.
npm run build통과.
법제처 JSON API에 본문이 비어 오는 판례(예: 616821)를 국세청 taxlaw.nts.go.kr에서 HTML로 보강하는 fallback 추가.
- 3갈래 fallback 진입: JSON 요청 실패 / JSON 파싱 실패 / 본문 누락(
isMissingPrecedentJson) 모두 HTML fallback 경로로 진입. 전체가 outer try-catch로 감싸져 fallback이 실패해도 안전하게 에러 반환. formatPrecedentText: 판례 출력 로직 함수화로 중복 제거.- 외부 HTTPS 프록시 지원 (
src/lib/external-https-proxy.ts):LAW_EXTERNAL_HTTPS_PROXY(선택) — 사내망/SSL inspection 환경의 국세청 판례 접근용 CONNECT 프록시.LAW_EXTERNAL_TLS_REJECT_UNAUTHORIZED=0(진단/임시용, 운영 금지)로 해당 경로 한정 TLS 검증 우회. - redirect 추적:
resolveTaxlawDetailUrl이 상세 URL location 헤더를 최대 3회 추적.
isMissingPrecedentJson죽은 코드 제거 —PrecServiceearly-return 이후 도달 불가능했던lawMessage분기 정리, 동작 유지하며return !obj.PrecService로 단순화.
LAW_API_PROTOCOL환경변수 추가(기본https). 폐쇄망/인증서 문제 환경에서http로 전환 가능.
- 판례 재검색 시 키워드 후보 생성 로직 개선으로 매칭 정확도 향상.
npm audit High 등급 4개 패키지 일괄 업그레이드. 모두 semver-major 변경 없는 patch/minor 업데이트로 안전.
- @xmldom/xmldom 0.9.8 → 0.9.10 (직접 의존성 + kordoc 간접, dedupe됨)
- GHSA-wh4c-j3r5-mjhp — XML injection via unsafe CDATA serialization
- GHSA-2v35-w6hq-6mfw — Uncontrolled recursion in XML serialization (DoS)
- GHSA-f6ww-3ggp-fr8h — XML injection through unvalidated DocumentType serialization
- GHSA-x6wf-f3px-wcqx — XML node injection through unvalidated processing instruction serialization
- GHSA-j759-j44w-7fr8 — XML node injection through unvalidated comment serialization
- @hono/node-server 1.19.9 → 1.19.14 (MCP SDK 간접) — 정적 미들웨어 경로 우회 (당 프로젝트는 미사용이나 트리 정리)
- express-rate-limit 8.2.1 → 8.5.2 (MCP SDK 간접) — IPv4-mapped IPv6 우회로 rate limit 회피
- fast-uri 3.1.0 → 3.1.2 (MCP SDK → ajv 간접) — path traversal / host confusion
npm audit→ found 0 vulnerabilitiesnpm run build→ TypeScript 빌드 통과@xmldom/xmldomDOMParser smoke test 통과 (hwpx-parser사용 코드 영향 없음)- xmldom DOMParser API는 0.9.x 내 안정 —
lib/annex-file-parser.ts의 HWPX 파싱 동작 변경 없음
- 수정: package.json (version), package-lock.json (의존성 트리)
- 코드 변경 없음
기존 resolveLawAlias는 query 전체가 등록된 약어와 정확 일치할 때만 canonical로 변환. "화관법" 단독은 매핑되지만 "화관법 시행령"/"화관법 제5조"/"산안법 위반 사례" 같이 약어가 다른 토큰과 결합된 query는 매핑 실패하여 search_law가 0건으로 떨어지던 문제 수정.
extractEmbeddedAliases(query)신규 (src/lib/search-normalizer.ts) — 정규화된 query에 포함된 약어를 길이 우선 탐색하여 풀네임 치환 변형 query 반환. 길이 2자 미만 alias 제외(오탐 방지), 동일 canonical 중복 제거expandLawQuery/expandOrdinanceQuery통합 — 정확 매칭에 더해 부분 매칭 결과를 expanded 배열에 추가.search_law의 기존 0건 fallback 흐름이 그대로 혜택- 차용 출처: korean-stats-mcp의
extractKeyword긴 키 우선 부분 매칭 패턴
- "화관법 시행령" → "화학물질관리법 시행령"
- "화관법 제5조" → "화학물질관리법 제5조"
- "산안법 시행규칙" → "산업안전보건법 시행규칙"
- "중처법 제4조 책임자" → "중대재해 처벌 등에 관한 법률 제4조 책임자"
- "국기법 제15조" → "국세기본법 제15조"
- "도정법 제17조" → "도시 및 주거환경정비법 제17조"
- 약어 미포함 query("경상남도 광역시" 등)는 빈 expanded 반환 — 회귀 0건
LexDiff 룰 영향 최소화: 신규 함수 추가 + 기존 expand* 함수에 매칭 결과 push. 기존 resolveLawAlias 본체 수정 없음.
chain_amendment_track의 time_travel 시나리오가 자주 개정되는 법령(소득세법 시행령 등)에서 시점 매칭 실패. 원인 둘:
- lsHistory display 100건 한계: 「소득세법 시행령」은 총 289건 연혁인데
fetchHistoricalVersionsRaw가 단일 호출display=100만 사용 → 가장 오래된 연혁이 2012-09-02 까지만 닿음. 2012년 8월 이전 시점을fromDate로 주면 모두[NOT_FOUND] 시점 매칭 실패반환. 법제처 API 문서엔max=100이라 표기되어 있으나 실제는display=500도 그대로 반환함을 검증. - 진단 메시지 부실: "시점 매칭 실패. 가장 오래된 연혁: ..." 한 줄만 표기 → 사용자/LLM이 원인 추정 어려움.
- 페이징 회수기 신설 (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):
- 시점 매칭 실패 시: 연혁 범위(최초~최신 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] 안내.
- 수정: src/lib/historical-utils.ts (페이징/총건수/중복제거), src/tools/scenarios/time-travel.ts (진단 강화, fetchHistoricalVersionsFull 사용)
- 신규 파일: 없음
법제처 lawSearch.do는 법령명 LIKE 검색 + 가나다순 정렬로 동작한다. query=상법이면 totalCnt=56건 중 「상법」(상행위 법전)은 가나다순 34위에 위치하고, 1~33위는 "보상법/배상법/기상법/재해보상법" 시리즈가 점유한다. 여기에 MCP의 display 기본값이 20이라 사용자에게는 항상 보상 관련 법령만 노출되는 구조적 결함이 있었다. 「민법」「형법」「관세법」 등 두 글자 짧은 법령명 전부에 동일 패턴이 적용되어 LLM이 잘못된 법령을 인용하거나 웹검색으로 우회하던 사례 다수.
- 정확매칭 우선 후처리 (src/tools/search.ts):
법령명한글/법령약칭명이 사용자 입력(또는 약칭 canonical)과 정확히 일치하는 결과를 분리해서📍 정확매칭섹션으로 최상단 노출. 나머지는📂 부분매칭섹션으로 분리. display기본값 20 → 50 상향: 가나다순 정렬에서 짧은 법령명이 후순위로 밀려도 한 번에 회수되도록.api-client.searchLaw에display인자 전달: 기존엔 search-normalizer 단에서 display가 무시되던 호출 경로 수정.- 단행법 alias entry 추가 (src/lib/search-normalizer.ts): 「상법」「민법」「형법」「어음법」「수표법」 — 정식명이 자기 자신이라 alias 등록은 의미 약하나,
alternatives로 관련 법령(시행령/시행법 등) 후보 제시 +normalizeAliasKeyexport로 정확매칭 비교 키 재사용. - 검증:
상법→ 1위 「상법」(MST 284143),민법→ 1위 「민법」(MST 284415),형법→ 1위 「형법」(MST 284025),관세법→ 1위 「관세법」(MST 280363). 약칭 fallback(화관법/중처법/주임법) 정상 동작 확인. ⚠️ 정확매칭 없음안내: 정확매칭이 0건이면 LLM에게 부분 LIKE 결과임을 명시 → 환각 방지.
- 수정: src/tools/search.ts (정확매칭 분리, display 50, 안내문), src/lib/search-normalizer.ts (단행법 5종 alias, normalizeAliasKey export)
- 신규 파일: 없음
기존 search_interpretations는 법제처 정부유권해석(target=expc)만 조회하므로, 국세청이 직접 회신한 법령해석을 가져올 수단이 없었음. 키워드 매칭으로 국세청 관련 안건이 노출은 되지만 모두 해석기관=법제처라 사용자가 누락 사실조차 인지하기 어려웠던 문제.
search_decisions/get_decision_text통합 도구의domainenum에"nts"추가 — 법제처 API targetntsCgmExpc(국세청 법령해석 목록) 호출. 신규 노출 도구 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):
"국세청 양도소득세 해석","국세청 예규","법인세 예규 질의"등 →search_decisions(domain=nts)- 패턴:
/국세청\s*(?:법령\s*)?해석/,/(?:양도|소득|법인|부가가치|상속|증여|종합부동산)세\s*(?:해석|예규|질의)/
- 검증:
domain=nts,query="1세대 1주택 양도소득세"호출 시 812건 정상 매칭, 모두해석기관=국세청. 1985~2020년대 국세청 직접 회신 해석례.
- 수정: src/tools/customs-interpretations.ts (target 분기 헬퍼 + nts 함수 2개), src/tools/unified-decisions.ts (DOMAINS/LABELS/HANDLERS에 nts 추가), src/lib/query-router.ts (nts 자연어 패턴), src/tool-registry.ts (search_decisions 설명 17→18 도메인)
- 신규 파일: 없음
v4.0의 "신규 도구 최소화, 기존 시스템 재활용" 원칙 유지. 신규 노출 도구 0개로 LLM 도구 선택 혼란 없음. V3_EXPOSED는 그대로 17개.
왜 필요했나: 법령은 단독으로 살지 않는다. 한 조문(예: 민법 제103조)은 수십 건 판례에 인용되고, 헌재 결정의 근거가 되고, 자치법규에 묻어가고, 행정해석을 낳는다. 이 "조문 한 줄의 그림자"를 매뉴얼로 추적하면 법무팀 며칠 작업. 한 번에 보는 도구가 없었음.
- 입력:
lawName+jo(예:민법,제103조) - 역방향 탐색 (병렬): 대법원 판례 / 헌재 결정 / 법령해석례 / 행정심판례 / 자치법규
- 정방향 탐색: 그 조문 본문 안에서 인용된 다른 법령 자동 추출 (
「OO법」패턴) - 출력: 텍스트 트리 + mermaid 그래프 코드 (claude.ai에서 시각화)
- 차별점: 다른 모든 chain은 query 단방향. 이 도구는 "특정 조문 → 영향받는 모든 곳" 역방향 그래프.
- 검증:
민법 제103조호출 시 판례 1건/헌재 6건/조례 2건 정확 추출 (테스트 완료)
왜 필요했나: 기존 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(인증기관 행정자치부장관→보호위원회 변경) 등 정확 검출
왜 필요했나: "전세금 못 받았어", "음주운전 걸렸어"처럼 시민이 자연어로 던지는 질문 → 기존 chain은 법령/판례 데이터 덤프만 줌. 시민이 "그래서 뭘 해야 하나"는 모름.
- 호스트:
chain_full_research - 5단계 출력:
- STEP 1 ─ 상황 진단 (적용 법령 자동 식별)
- STEP 2 ─ 권리·구제 수단 (실제 판례 시그널 + "패소 사유의 역 = 승소 조건")
- STEP 3 ─ 신청 기관 / 기한 (행정규칙 + 해석례)
- STEP 4 ─ 필요 서류 / 양식 (별표/별지서식 자동)
- STEP 5 ─ 함정 / 주의 (시효·개정·법률구조공단 안내)
- 시민 키워드 → 법률 도메인 자동 매핑: 전세금→주택임대차보호법 보증금, 해고→근로기준법 부당해고, 음주운전→도로교통법, 체불→근로기준법 임금, 산재→산업재해보상보험법 등 10개 도메인
- 검증: "전세금 못 받았어" → 주택임대차보호법 자동 매핑, 판례 20건/해석 4건/별표 2건/행정규칙 4건 일괄 수집
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에 위임
api-client.fetchApi:type=HTML응답에checkHtmlError가 무조건 throw 하던 버그 →type !== "HTML"일 때만 적용하도록 수정. 기존searchHistoricalLaw/getHistoricalLaw등 lsHistory 기반 도구가 핫픽스(v3.5.5) 이후 깨져있던 것을 함께 복구.
- 노출 도구 16 → 17개 (
impact_map추가) - 시나리오 7 → 9개 (
time_travel,action_plan추가) chainAmendmentTrackSchema:scenarioenum 확장(timeline,time_travel),fromDate/toDate필드 신규chainFullResearchSchema:scenarioenum 확장(customs,action_plan)ScenarioContext:extras?: Record<string, unknown>필드 추가 — 시나리오별 추가 파라미터 전달용
- 신규:
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
v4.0의 핵심 원칙은 "신규 도구 최소화, 기존 시나리오 시스템 재활용". 3개 킬러 기능을 만들면서 새 도구는 1개(impact_map)만 추가하고 나머지 2개는 기존 chain의 시나리오로 통합. 도구 수 폭증 방지 + LLM이 도구 선택할 때 혼란 줄이기.
법제처 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로는 거부.
lib/fetch-with-retry.ts— 일반 Chrome 브라우저 UA를 기본 헤더로 주입. 옵션으로 넘어온headers에user-agent가 없을 때만 자동 추가 → 호출자 코드 변경 0LAW_USER_AGENT환경변수로 override 가능 (정책 변경 시 빠른 대응)
- claude.ai 커스텀 커넥터(
https://korean-law-mcp.fly.dev/mcp?oc=...)로 사용하던 모든 사용자 즉시 영향 → v3.5.5 배포로 자동 복구 - npm 글로벌 설치(
npm i -g korean-law-mcp) 사용자도 동일하게 적용 - IP 화이트리스트 / 한국 호스팅 이전 같은 큰 작업 불필요
- 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 기본 사용으로 개선되면 안내 제거 예정
사용자 피드백: "실사용하면 자꾸 답변 못 찾고 AI가 지맘대로 답변함. 못 찾으면 리턴값을 명확하게 주면 좋겠음."
근본 원인: 일부 도구가 조회 실패 시 isError 플래그를 설정하지 않거나, 응답 텍스트에 "없습니다"만 포함되어 LLM이 실패를 감지하지 못하고 창작 답변 생성.
[NOT_FOUND]/[HALLUCINATION_DETECTED]/[API_ERROR]머신 파싱 가능 프리픽스 — 모든 실패 응답에 기계적으로 감지 가능한 마커 추가. LLM이 실패를 놓치지 않고 사용자에게 "검색 실패" 보고하도록 유도lib/errors.tsnotFoundResponse(message, suggestions?)신규 헬퍼 — 특정 리소스 없을 때(조문/별표/파일 등) 일관된 NOT_FOUND 응답 생성- 모든 "없습니다" 응답에 LLM 경고문 삽입 — "
⚠️ LLM은 {조문/판례/법령}을 추측/생성하지 마세요" 문구 표준화
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— 검색 실패 응답 강화
tools/chains.tssecOrSkip()— 에러 snippet 80자 → 200자 확장, 섹션 제목에[NOT_FOUND / FAILED]마커 + LLM 경고문 삽입- 모든 silent-drop 패턴(
if (!result.isError) parts.push(sec(...))) 제거 →parts.push(secOrSkip(...))로 일괄 전환. 체인 중 일부 단계가 실패해도 "왜 빠졌는지" 명시 노출 noResult()— NOT_FOUND 마커 + "체인 실행 중단 — LLM은 추측 금지" 지시문 추가
- LLM이 실패 응답을 기계적으로 감지 가능해져 창작/환각 답변 방지
- 체인 도구가 부분 실패해도 사용자에게 "어떤 데이터가 왜 빠졌는지" 명시적으로 노출
- 특히
verify_citations의isError누락은 환각 검출의 의미를 무력화하던 심각한 버그였음
실제 법제처 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으로 호출
lib/law-search.ts신규 —findLaws,scoreLawRelevance,parseLawXml,stripNonLawKeywords,NON_LAW_NAME_RE,LawInfo타입을chains.ts에서 추출하여 공용화tools/chains.ts— 중복 정의 제거,law-search.tsimporttools/verify-citations.ts— 자체 법령 검색 로직 제거하고findLaws+parseHangNumber재사용 (중복 구현 금지 원칙)lib/api-client.ts—searchLaw(query, apiKey, display?)시그니처 확장 (backward compatible)
실제 법제처 API로 5건 테스트 — 5/5 정확 판정:
- ✓ 민법 제750조(불법행위) / 근로기준법 제60조 제1항(연차휴가) / 도로교통법 제44조(음주운전) 실존
- ✗ 상법 제401조의2 제7항 — 제7항 없음(최대 제2항) 환각 정확 탐지
- ✗ 형법 제9999조 — 해당 조문 없음(존재 범위: 제1조~제372조)
- kordoc 2.3.0 → 2.4.0 — 별표/서식 파싱 엔진 업데이트
- 영향:
src/lib/annex-file-parser.ts(HWP/HWPX/PDF 통합 파서) - API 호환 (minor bump,
parse/ParseResult/FileType시그니처 유지)
- 영향:
- lite/full 프로필 체계 완전 제거 — V3_EXPOSED 16개 고정 노출 도입 후 실질 미사용 상태였던 죽은 코드 정리
lib/tool-profiles.ts:LITE_TOOLSset(15개 엔트리),parseProfile(),filterToolsByProfile(),ToolProfile타입 제거 (37줄 순감)tool-registry.ts:registerTools(server, apiClient, profile?)→registerTools(server, apiClient)시그니처 단순화.filterToolsByProfileimport 제거index.ts:MCP_PROFILE환경변수 처리 제거,parseProfile/ToolProfileimport 제거,createServer(profile?)→createServer()server/http-server.ts:?profile=쿼리 파라미터 파싱 제거,createServer(profile)호출부 단순화
- 헬스 엔드포인트(
GET /) 응답에서 거짓profiles: { lite, full }필드 제거 →tools: { exposed: 16, total: 92 }정확 안내로 교체 mcp-lite: "/mcp?profile=lite"엔드포인트 안내 제거 (원래부터 무시되던 값)
- v3 통합 후
tool-registry.ts가V3_EXPOSED.has(t.name)로만 필터링하고filterToolsByProfile은 import만 되어 있고 호출 안 됨 →?profile=lite든?profile=full든 완전히 동일하게 16개 도구 반환 - 헬스 엔드포인트는 여전히
lite: "14 tools"안내 문구 노출 → 클라이언트에 거짓 정보 전달 중 - 배포된 상태에서 breaking change 아님: 기존
?profile=lite호출은 지금도 이미 무시되던 값이므로 동작 변화 없음
- STDIO 모드:
MCP_PROFILE환경변수 이제 무시됨 (설정 안 해도 됨) - HTTP 모드:
?profile=쿼리 파라미터 이제 무시됨 (모든 클라이언트 동일 16개 도구) - 문서/튜토리얼에서 lite/full 언급 있으면 제거 권장 (CHANGELOG 역사 맥락은 유지)
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 답변의 조문 인용 환각 실시간 탐지
- 입력 텍스트에서
get_decision_textfull옵션이 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:132densifyPrecedentRefs날짜 정규식에 경계 가드((^|[\s,(\[;/])) 추가 — 문서 중간제2020. 3. 26. 개정같은 숫자 오탐 방지decision-compact.ts:59compactBodyTAIL 경계에서". "제외 +"한다. "/"라. "추가 —"1,234.00 원"·"No. 3"오탐 방지decision-compact.ts:166stripRepeatedSummary종료점 탐지 강화 — 요약 끝 60자 매칭으로 실제 end 계산, 매칭 실패 시 보수적으로s.length만 제거 (요약 뒤 본문 같이 날아가는 사고 방지)
fetch-with-retry.ts:72타임아웃/네트워크 에러 메시지에 API 키 포함 URL이 그대로 노출되던 문제 —maskSensitiveUrl()신규로OC=***·apiKey=***등 마스킹 후 throwhttp-server.ts:136console.error("[POST /mcp] Error:", error)에서 원본 에러 로깅 시 키 노출 가능성 —scrubError()경유로 통일http-server.ts:19trust proxy true→TRUST_PROXY환경변수 (기본1, 첫 프록시만 신뢰).X-Forwarded-For스푸핑으로 rate limit 우회 + 메모리 DoS 위험 차단- body limit 환경변수화(
MCP_BODY_LIMIT, 기본100kb)
- 체인 도구 8개 description 구체화 — LLM이
search_lawvschain_law_system중 선택 가능하게. 각 체인에 구체적 사용 예시("관세법 체계","음식점 영업정지 근거","서울시 주차 조례 전국 비교"등) + 언제 쓰지 말아야 하는지 명시 search_law/search_ordinance/search_precedents결과에 "💡 다음: get_law_text(mst=...)" 형태 다음 단계 힌트 추가 — 검색→조회 흐름 자동 유도search_law0건 시expandLawQuery자동 재시도 — 약칭("근기법"→"근로기준법")/오타 확장으로 성공률 상승query-router.ts5개 패턴 추가 —verify_citations(인용검증 키워드), 법령 비교(vs/와/과 차이), 시간 필터(최근 N년 개정), 민사책임(손해배상/과실비율), 계약서 검토(독소조항)tool-profiles.tsTOOL_ALIASES맵 추가 —"조세심판원"→search_tax_tribunal_decisions,"김영란법"→ 청탁금지법 등 27개 한국어 별칭.discover_tools가 별칭 매칭하면 카테고리/도구 즉시 반환
- 프로덕션 리뷰(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 기능
verify_citations사용: LLM 답변/계약서/판결문 텍스트를text로 넘기면 자동 인용 추출 + 병렬 검증full옵션은 14개 도메인 전체에서 정상 작동 (이제full=true보내면 실제로 전문 반환)- API 키 로그 유출 방지를 위해 프로덕션 환경은
TRUST_PROXY=1명시 설정 권장 (Fly.io는 기본값으로 충분) - 별칭 매칭은
discover_tools(intent="조세심판원")같은 자연어 입력에서 자동 적용
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)에도 동일 파라미터 전파
- 판례 응답 토큰 평균 -74% (실측:
b4875a3vs69f6918, 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_tooldescription 축약
- 3개 MCP 동시 운용 환경에서 판례 호출 1회가 12.5k 토큰 상한(50KB)을 먹어 컨텍스트 블랙홀화
- 법령 RAG 관점에서 판시사항·판결요지·주문은 규범 재사용 핵심이라 full 유지, "이유" 전문은 사안별 사실관계 나열이라 축약해도 손실 미미
- 중략된 구간은
full=true로 재호출 가능 — backward compatible
- 적용 도메인:
precedent,constitutional,admin_appeal(판례/헌재/행심) - 해석례·기타 짧은 도메인은 미적용 (원래 짧음)
- 사용자는 자연어로 "전문 그대로", "full로 다시"라고 요청하거나 LLM이 description 보고 자동 판단
- 응답 중간의
⋯ 중략 N자 (full=true로 전문 조회) ⋯마커가 힌트
get_annexes를 V3_EXPOSED에 추가 (14개 → 15개 노출).discover_tools→execute_tool왕복 없이 별표/서식 직접 조회 가능chains.tsdetectExpansions: 환불·반환·배상·수강료·이용료·회비·N만원 키워드 추가 — 소비자분쟁 질의에서chain_full_research가 별표 자동 포함
- 트레이스
ld-1775959823220(헬스장 1년권 환불, 79s) 분석 결과: 별표 3의2 조회를 위해discover_tools× 2 +execute_tool(get_annexes(...))헛발질로 ~15초 손실 - 노출 기준: 체인 도구가 fallback으로 자주 호출하는 종착 도구 + discover→execute 왕복으로 5초+ 손실
tool-registry.ts상단 주석에 제거 금지 경고 명시
npx korean-law-mcp setup— 대화형 설치 마법사 (API 키 입력 → 8개 클라이언트 자동 설정)- 지원 클라이언트: Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, Gemini CLI, Zed, Antigravity
- STDIO 모드에서
MCP_PROFILE환경변수 지원
- API 커버리지 수치 39개 → 41개로 정정 (실제 사용 target 기준 재집계)
- get_ordinance:
jo파라미터 추가 — 특정 조문 본문 직접 조회 가능 (#19) - 대형 조례(20개 초과) 목차 반환 시
jo사용법 안내 메시지 추가
- get_ordinance: 조문 필터링을 조제목 텍스트 매칭에서 조문번호(JO 코드) 기반으로 변경 — API 응답의 조제목에 조번호가 없는 구조 대응
- get_ordinance: "제20조" 검색 시 "제20조의2" 등 의X 조문이 잘못 매칭되는 문제 수정
- 23개 신규 도구: 조약(2), 법령-자치법규 연계(4), 학칙/공단/공공기관(6), 특별행정심판(4), 감사원(2), 약칭(1), 행정규칙 신구대조(1), 조항호목(1), 문서분석(1), chain_document_review(1)
- date-parser: 자연어 시간 표현 → YYYYMMDD 변환 (10개 패턴)
- document-analysis: 8종 문서유형 분류, 17개 리스크규칙, 금액/기간 추출, 조항 충돌 탐지
- 판례/해석례 날짜 필터 (fromDate/toDate)
- 에러 처리 통일: 40개 도구의 인라인 에러 → formatToolError 전환
- 중복 XML 파서 6개 → 공용 parseSearchXML 통합
- cli.ts 분리: cli-format.ts + cli-executor.ts + cli.ts (689줄 → 443+181+227)
- annex.ts: AnnexItem 타입 정의, any 12회 제거
- 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자만 출력)
-
조약 XML 아이템태그 대소문자 (trty→Trty), 본문 JSON 키 (BothTrtyService)
-
연계 fetchApi type 기본값 제거 (type=XML 시 500 발생)
-
api-client.ts: type 파라미터 미지정 시 생략
-
총 도구 수: 64 → 87
- HWP 구형 파서:
controls내 테이블(표) 추출 지원hwp.js의paragraph.controls[].content경로에서 테이블 구조(rows/cells) 탐색- 기존에는
paragraph.content만 탐색하여 표 형식 HWP 파싱 실패
- MCP 도구 스키마 최적화: description 압축 + apiKey 은닉
- 체인 도구 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 호환성 개선
- 구조화된 에러 포맷: [에러코드] + 도구명 + 제안
- formatToolError: ZodError 자동 감지, 구조화된 출력
- toMcpInputSchema: ZodEffects unwrap 후 JSON Schema 변환
- 총 도구 수: 57 → 64