korean-patent-mcp
특허 출원번호나 기술 설명을 넣으면, 그 권리가 지금 살아있는지를 판정해서 돌려주는 MCP 서버.
KIPRIS Plus 기반. 도구는 3개다. 늘리지 않는다.
npx korean-patent-mcp@latest setup

도구 3개의 실제 실행 결과입니다. 재현한 화면이 아니라 KIPRIS·등록원부에서 받은 값입니다.
왜 필요한가
LLM에게 특허를 물으면 세 가지 방식으로 틀린다.
| 거짓말 | 이 서버가 잡는 법 |
|---|
| 없는 특허번호를 지어낸다 | exists: false |
| 실재하는 번호에 엉뚱한 명칭을 붙인다 | title_match: "mismatch" |
| 20년 전에 소멸한 특허를 유효하다고 쓴다 | alive: false |
세 번째가 제일 위험하다. 번호도 실재하고 명칭도 맞는데, 그 권리는 이미 죽어 있다.
검색만 하는 도구는 이걸 절대 못 잡는다. 그래서 이 서버는 검색 결과에도 alive를 강제로 붙인다.
이렇게 물어보면 된다
설치하고 나면 클라이언트에서 그냥 한국어로 물으면 된다.
"10-2019-0123456 이 특허 아직 살아있어?"
"이 IR 자료에 적힌 특허번호들 진짜인지 전부 확인해줘" (문서를 그대로 붙여넣기)
"무선 충전 코일 정렬 관련 국내 특허 중에 아직 유효한 것만 찾아줘"
도구 3개
1. rights_alive(number)
번호 하나 → 생사 판정. 이 서버의 존재 이유.
{
"alive": false,
"status": "소멸(연차료 불납)",
"stage": "소멸",
"number": "10-2000-0012345",
"title": "카세트 테이프 권취 장치",
"holder": "다라산업",
"expiry": "2020-05-10",
"expiry_estimated": true,
"raw_status": "소멸(연차료 불납)",
"basis": "상태 문자열 \"소멸(연차료 불납)\"에서 소멸 신호 검출",
"warnings": ["holder는 출원인 기준입니다. 등록 후 권리가 양도되었으면 현재 권리자와 다를 수 있습니다."],
"latest_event": { "date": "2020-08-01", "description": "연차료 불납에 의한 소멸" },
"checked_at": "2026-09-04",
"source_url": "https://www.kipris.or.kr/khome/search/searchResult.do?tab=patent&query=10-2000-0012345"
}
alive만 보지 말고 stage도 보라. alive: false에는 정반대 두 가지가 섞여 있다.
| stage | alive | 뜻 |
|---|
등록유효 | true | 지금 행사 가능한 권리 |
소멸 | false | 있었는데 죽었다. 등록원부를 붙이면 연차료 불납인지 존속기간 만료인지까지 갈린다 |
출원종료 | false | 등록 못 하고 끝났다 (거절·취하·포기) |
출원계속 | false | 아직 안 태어났다. 심사 중이라 장래 등록될 수 있다 |
불명 | false | 상태값을 해석 못 했다. raw_status를 직접 보라 |
FTO 관점에서 소멸은 안전 신호이고 출원계속은 위험 신호다. 둘 다 alive: false지만 의미가 반대다.
2. verify_citations(text)
LLM 답변을 통째로 넣으면 인용된 특허번호를 전부 검증한다.
{
"all_clear": false,
"total_found": 3,
"checked": 3,
"citations": [
{
"number": "10-2019-0123456",
"cited_as": "10-2019-0123456",
"exists": true,
"claimed_title": "인공지능 기반 신약 후보물질 탐색 방법",
"actual_title": "무선 충전 장치 및 그 제어 방법",
"title_match": "mismatch",
"title_similarity": 0.04,
"alive": true,
"verdict": "title_mismatch",
"note": "명칭 불일치. 인용: \"인공지능 기반 신약 후보물질 탐색 방법\" / 실제: \"무선 충전 장치 및 그 제어 방법\""
}
],
"warnings": ["명칭 불일치 1건: 10-2019-0123456 — 번호는 실재하나 다른 발명입니다."],
"summary": "인용 3건 중 1건에 문제가 있습니다."
}
verdict는 ok / dead / title_mismatch / not_found / pending / unknown 중 하나다.
3. search_ip(query, ...)
검색. 단, 모든 결과에 alive가 붙는다. 죽은 권리는 display: "dimmed"로 표시되고 뒤로 정렬된다.
{
"query": "무선 충전 코일 정렬",
"total_matched": 412,
"inspected": 20,
"alive_in_inspected": 7,
"dead_in_inspected": 13,
"coverage_warning": "전체 412건 중 20건만 판정했습니다. ...",
"results": [
{ "alive": true, "display": "normal", "status": "등록유효 (만료예정 2039-03-14)", "number": "10-2019-0123456", "…": "…" },
{ "alive": false, "display": "dimmed", "status": "소멸(존속기간 만료)", "number": "10-2001-0009999", "…": "…" }
],
"notes": ["생사 판정은 검색 결과의 등록상태 문자열에 근거합니다. 특정 건을 근거로 삼기 전에 그 번호로 rights_alive를 호출해 확정하세요."]
}
alive_only: true를 주면 살아있는 것만 나온다.
집계는 inspected 기준이지 total_matched 기준이 아니다. 전체를 다 보지 못했으면
coverage_warning이 채워진다. alive_in_inspected: 0을 "그런 유효 특허가 없다"로
읽으면 안 된다 — 21,060건 중 10건만 본 결과일 수 있다.
설치
1. KIPRIS 서비스 키 발급 (무료, 승인 대기 있음)
KIPRIS Plus에서 직접 신청한다. 공공데이터포털(data.go.kr)이 아니다.
https://plus.kipris.or.kr → 회원가입 → 서비스 신청 → "특허·실용 공개·등록공보"
(국내 IP데이터 > 공보 > 특허·실용, REST)
발급된 ServiceKey 를 KIPRIS_SERVICE_KEY 에 넣는다.
무료 이용자 구간이 있다. 초당 호출 제한은 무료 50회 / 유료 75회다.
공공데이터포털에서 찾지 마세요. 포털의 KIPRISPlus 항목들은 "활용신청"이 아니라
"제공처 바로가기" 로 KIPRIS Plus를 가리키기만 합니다. 예전에 있던
data.go.kr/data/15058788 항목은 현재 폐지(404)됐습니다.
포털에서 직접 신청 가능한 건 별개 API인 등록원부 실시간 조회(15124946) 쪽입니다.
다른 KIPRIS 서비스 키로는 동작하지 않습니다. KIPRIS에는 등록사항·해외특허·심판 등
서비스가 여러 개 있고 신청은 각각 따로 승인됩니다. 이 서버는 patUtiModInfoSearchSevice
하나만 호출하므로 위 서비스를 신청해야 합니다.
엉뚱한 서비스 키를 넣으면 모든 호출이 SERVICE KEY IS NOT REGISTERED 로 실패합니다.
호출 한도보다 호출량을 먼저 신경 쓰세요. verify_citations가 번호 20개를 검증하면
그것만으로 20회입니다. 서버는 같은 번호를 프로세스 내에서 1시간 캐시하고
(KIPRIS_CACHE_TTL로 조절), 동시 요청을 3개로 묶어 초당 제한에 여유를 둡니다.
2. 설치
npx korean-patent-mcp@latest setup
키를 프롬프트로 받아 실제 호출로 검증한 뒤 선택한 클라이언트 설정 파일에 등록한다.
Claude Desktop / Claude Code / Cursor / VS Code / Windsurf / Gemini CLI / Zed 지원.
설치 전에 MCP 클라이언트를 완전히 종료하세요(Cmd+Q).
Claude Desktop은 claude_desktop_config.json 을 자기 preferences 저장소로도 씁니다.
켜진 채로 고치면 앱이 종료할 때 덮어써서 방금 추가한 서버가 조용히 사라집니다.
setup 이 실행 중인 클라이언트를 감지해 막아주지만, 수동 설정 시에는 직접 확인하세요.
최초 실행은 30초쯤 걸립니다. npx 가 패키지를 받는 시간입니다(2회차부터 1초 내외).
그 사이 "연결 실패"로 보이면 클라이언트를 한 번 더 재시작하세요.
매번 빠르게 뜨길 원하면 npm i -g korean-patent-mcp 후 command 를
korean-patent-mcp 로 바꾸면 됩니다.
수동 설정
{
"mcpServers": {
"korean-patent": {
"command": "npx",
"args": ["-y", "korean-patent-mcp@latest"],
"env": { "KIPRIS_SERVICE_KEY": "여기에-키" }
}
}
}
키를 어디에 둘 것인가
셋 중 하나. 위에서부터 권장한다.
1) MCP 클라이언트에서 쓸 때 — setup이 알아서 넣는다
npx korean-patent-mcp@latest setup
클라이언트 설정 파일의 env 블록에 기록하고 파일 권한을 600으로 조인다.
2) 여러 곳에서 공용으로 — 홈 설정 파일
mkdir -p ~/.config/korean-patent-mcp
printf 'KIPRIS_SERVICE_KEY=발급받은키\n' > ~/.config/korean-patent-mcp/.env
chmod 600 ~/.config/korean-patent-mcp/.env
MCP 클라이언트는 서버를 임의의 작업 디렉토리에서 띄우므로, 프로젝트 .env보다
이 경로가 안정적이다. 서버가 시작할 때 자동으로 읽는다.
3) 이 저장소에서 개발할 때 — 프로젝트 .env
cp .env.example .env && chmod 600 .env
.gitignore에 이미 들어 있어 커밋되지 않는다.
우선순위는 실제 환경변수 > 프로젝트 .env > 홈 .env 다.
일회성으로 다른 키를 쓰려면 그냥 앞에 붙이면 된다:
KIPRIS_SERVICE_KEY='다른키' npx korean-patent-mcp probe
~/.zshrc에 export 하는 방법도 되지만 권하지 않는다.
셸을 띄우는 모든 프로세스에 키가 노출되고, dotfiles를 저장소에 올리는 사람이 많다.
키를 이슈·PR·채팅에 붙여넣지 마세요. 노출됐으면 KIPRIS Plus 마이페이지에서 재발급하세요.
| 환경변수 | 기본값 | 설명 |
|---|
KIPRIS_SERVICE_KEY | (필수) | KIPRIS Plus ServiceKey. 위 3가지 방법 중 하나로 설정 |
KIPRIS_CACHE_TTL | 3600 | 응답 캐시 TTL(초) |
DATA_GO_KR_SERVICE_KEY | (선택) | 등록원부 API 키 — 연차료·확정 만료일 |
LEDGER_ENDPOINT | (선택) | 등록원부 오퍼레이션 URL (활용가이드 PDF 참조) |
KIPRIS_BASE_URL | KIPRIS Plus 공식 | 엔드포인트 오버라이드 |
선택: 등록원부 연동 — 추정을 확정으로
기본 상태에서 rights_alive의 만료일은 출원일 + 법정 존속기간으로 계산한 추정치이고,
연차료 납부 여부는 확인되지 않는다. 등록원부 API를 붙이면 둘 다 확정된다.
| 기본 | 등록원부 연동 |
|---|
expiry | 추정 (expiry_estimated: true) | 확정 (연장등록 반영) |
holder | 출원인 | 실제 등록권자 (양도 반영) |
annual_fee | null | { paid_year, paid_until } |
KIPRIS Plus와 별개 API다. 키도 신청도 따로 한다.
- https://www.data.go.kr/data/15124946/openapi.do 에서 활용신청 (자동승인)
DATA_GO_KR_SERVICE_KEY = 일반 인증키(Decoding)
이게 전부다. 엔드포인트는 기본값이 들어 있고, 번호의 권리구분을 보고
특허는 getPatentRegisterHistory로, 실용신안은 getUtilityModelHistory로 자동으로 나뉜다.
설정하면 이렇게 바뀐다:
{
"expiry": "2034-11-26",
"expiry_estimated": false,
"holder": "삼성전자주식회사",
"annual_fee": { "paid_year": 6, "last_paid_date": "2026-03-27", "payment_count": 4 },
"sources": ["KIPRIS 서지상세", "등록원부"],
"warnings": []
}
warnings가 빈 배열인 것에 주목. 추정치도 미확인 항목도 없으니 붙일 경고가 없다.
npx korean-patent-mcp probe --ledger 10-2245822
설정하지 않아도 서버는 그대로 동작한다. 등록원부는 부가정보라,
호출이 실패해도 생사 판정은 KIPRIS 서지정보만으로 나간다. 어떤 소스를 봤는지는
응답의 sources 필드에 적힌다.
probe — 데이터를 믿어도 되는지 먼저 확인
이 서버 전체는 두 가지 전제 위에 서 있다. 하나라도 무너지면 코드가 아무리 좋아도 거짓말을 하게 된다.
- 소멸·포기된 권리가 응답에서 명확히 구분되는가
- 상태 갱신이 며칠이나 지연되는가
이걸 눈으로 확인하지 말고 명령 하나로 판정한다.
KIPRIS_SERVICE_KEY='...' npx korean-patent-mcp probe
npx korean-patent-mcp probe 10-2019-0123456 10-1234567
npx korean-patent-mcp probe --raw 10-2019-0123456
probe가 하는 일:
- 응답에 판정용 필드(
registerStatus, finalDisposal, applicationDate …)가 실제로 있는지 확인
- 관측된 상태값 분포를 뽑아, 등록 계열과 소멸 계열이 서로 다른 값으로 갈리는지 판정
- 판정 규칙이 모르는 상태값을 잡아내 (→
src/lib/status.ts에 추가하면 된다)
- 법적상태 이력 날짜로 갱신 지연을 가늠
- 마지막에 "이 기획은 성립한다 / 확인포인트 1 미통과" 를 한 줄로 결론
판정 규칙
src/lib/status.ts 하나에 모여 있다. 나머지는 전부 배관이다.
- "등록"이라는 글자만 보고 살아있다고 하지 않는다. 죽음 어휘(소멸·무효·취소·말소·연차료 불납)를 먼저 본다. 등록된 뒤 소멸한 권리가 압도적으로 많고, 그게 정확히 사람들이 틀리는 지점이다.
- 등록 상태라도 존속기간을 다시 계산한다. KIPRIS 상태가 "등록"에 머물러 있어도 만료일이 지났으면 그 권리는 죽었다. 특허 = 출원일 + 20년, 실용신안 = 출원일 + 10년.
- 모르면 모른다고 한다. 모든 응답에
basis(판정 근거)와 warnings가 붙는다. 해석 못 한 상태값은 stage: "불명" + alive: false다 — 모르는 걸 살아있다고 하지 않는다.
KIPRIS 문서가 명시한 registerStatus 값은 공개 · 등록 · 거절 · 무효 · 소멸 · 취하 · 포기 7개다.
7개 모두 규칙이 커버하며(kipris-fixture.test.ts에서 고정),
이 중 alive: true가 되는 값은 등록 하나뿐이다.
한계 — 읽고 쓰세요
expiry는 대개 추정치다. expiry_estimated: true면 출원일 + 법정 존속기간으로 계산한 값이다. 존속기간 연장등록(의약품·농약)이 있으면 실제 만료일은 더 뒤다.
- 연차료 납부 여부는 확정하지 못한다. 등록 상태로 나와도 최근 연차료를 안 냈으면 곧 소멸한다. KIPRIS 상태 반영에는 지연이 있다.
holder는 출원인 기준이다. 등록 후 권리가 양도됐으면 현재 권리자와 다르다. 정확한 권리자는 등록원부를 봐야 한다.
- 거래·소송·실시 판단에 이 서버의 출력을 그대로 쓰지 마세요. 스크리닝 도구다. 최종 확인은 등록원부와 변리사다.
search_ip의 생사 판정은 검색 응답의 상태 문자열에만 근거한다. 특정 건을 근거로 삼기 전에 그 번호로 rights_alive를 다시 부르세요.
안 하는 것 (의도한 부재)
fto_screen — 침해 가능성 판단. 유사도 로직이 필요하고, 틀렸을 때 손해가 크다.
- 상표·디자인 — 특허·실용신안만.
30-·40- 번호는 명시적으로 거절한다.
- 해외 특허 — 국내만.
- 도구 10개 — 도구가 늘면 LLM이 어느 걸 부를지 헷갈리고, 그 순간 "생사 판정"이라는 유일한 약속이 흐려진다.
개발
npm install
npm test
npm run typecheck
npm run build
src/
├── index.ts 진입점 (stdio / setup / probe)
├── tool-registry.ts 도구 3개 등록 — 여기가 늘어나면 안 된다
├── setup.ts 대화형 설치 (키 검증 포함)
├── probe.ts 응답 진단 — 기획 폐기 여부 판정
├── lib/
│ ├── status.ts ★ 생사 판정 엔진. 이 프로젝트의 실체
│ ├── number.ts 번호 파싱·정규화·텍스트 추출
│ ├── kipris-client.ts KIPRIS Plus HTTP (재시도·캐시·오류 해석)
│ ├── ledger-client.ts 등록원부 실시간 조회 (선택 — 연차료·확정 만료일)
│ ├── title-match.ts 인용 명칭 ↔ 실제 명칭 대조
│ ├── xml.ts 의존성 없는 XML 추출
│ ├── cache.ts TTL 캐시 (호출량 방어)
│ └── errors.ts 오류 → 도구 응답
└── tools/
├── rights-alive.ts
├── verify-citations.ts
└── search-ip.ts
상태값을 하나 더 알게 됐다면 src/lib/status.ts의 DEAD_PATTERNS / PENDING_PATTERNS에 추가하고 src/lib/status.test.ts에 케이스를 하나 넣으면 된다. probe가 모르는 상태값을 알려준다.
배포
main에 푸시하면 CI가 Node 20.19/22/24에서 typecheck·test·build·패키지 검증을 돌린다.
릴리스를 만들면 npm에 자동 배포된다:
npm version patch && git push --follow-tags
gh release create "v$(node -p "require('./package.json').version")" --generate-notes
publish.yml이 릴리스 태그와 package.json 버전 일치를 강제한다.
로컬 버전과 배포 버전이 어긋나는 사고를 구조적으로 막는 게이트다.
인증은 Trusted Publishing(OIDC) 을 쓴다. NPM_TOKEN 시크릿이 없다 —
npm granular token은 최대 90일이라 분기마다 만료되고 그때마다 배포가 조용히 깨진다.
OIDC는 GitHub가 워크플로 실행마다 발급하는 단기 증명이라 갱신할 것도, 유출될 장기 비밀도 없다.
최초 1회만 npmjs.com > 패키지 > Settings > Trusted Publisher 에 등록한다:
| 항목 | 값 |
|---|
| Repository | didrod205/korean-patent-mcp |
| Workflow | publish.yml |
등록 전까지는 로컬에서 npm publish 하면 된다 (prepublishOnly가 빌드와
verify:package를 자동으로 돌려 빌드-버전 불일치를 막는다).
문제가 생기면
서버가 안 붙습니다
클라이언트 로그를 먼저 보세요. Claude Desktop은
~/Library/Logs/Claude/mcp*.log 에 원인이 그대로 찍힙니다.
| 로그에 보이는 것 | 원인 | 해결 |
|---|
notarget No matching version found | 방금 배포된 버전을 @latest 가 가리키는데 레지스트리 전파가 끝나지 않음 | 몇 분 뒤 클라이언트를 다시 시작 |
| 아무 로그도 없음 | 클라이언트가 설정을 아직 안 읽음 | 완전 종료(Cmd+Q) 후 재시작 |
Server disconnected 만 반복 | 최초 실행 다운로드(30초) 중 타임아웃 | 재시작하면 캐시가 있어 1초 내에 뜸 |
KIPRIS_SERVICE_KEY 가 없습니다 | 설정의 env 가 비었음 | 아래 "설정이 사라졌습니다" 참조 |
설정이 사라졌습니다
mcpServers 에 넣었는데 없어졌다면, 클라이언트가 켜진 상태에서 파일을 고쳤을 가능성이 큽니다.
Claude Desktop은 claude_desktop_config.json 을 자기 preferences 저장소로도 써서,
앱이 종료할 때 메모리 상태로 덮어씁니다. 완전히 종료한 뒤 다시 설정하세요.
setup 은 실행 중인 클라이언트를 감지해 이 상황을 막습니다.
매번 최신을 받는 게 부담스럽다면
args 의 @latest 를 빼면 캐시된 버전을 쓰므로 전파 지연에 걸리지 않습니다.
버전을 고정하고 싶으면 korean-patent-mcp@1.3.3 처럼 명시하세요.
데이터 출처
특허·실용신안 서지정보와 등록상태는 KIPRIS Plus(지식재산처 / 한국특허정보원)에서,
존속기간 만료일·연차 납부 이력·등록권자는 등록원부 실시간 정보 조회 서비스
(지식재산처, 공공데이터포털 15124946)에서 조회합니다.
API 인증키는 각자 발급받아야 하며, 발급받은 본인만 사용할 수 있습니다.
이 소프트웨어는 인증키를 수집하거나 전송하지 않습니다.
법적 효력이 필요한 판단에는 반드시 등록원부 원본을 확인하세요.
이 도구는 조회 결과를 가공·요약하며, 상태 반영에는 지연이 있을 수 있습니다.
라이선스
MIT
제3자 구현 참조 및 데이터 출처 고지는 NOTICE를 참조하세요.
Made by 양경찬 @E:LAB STUDIO