safemap-uv-index-mcp
by hlucent
README.md
> ⚠️ **이 프로젝트는 2026-08-23 리뉴얼되어 [korea-living-weather-index-mcp](https://github.com/hlucent/korea-living-weather-index-mcp)로 통합되었습니다.**
> 자외선지수(get_uv_index, get_uv_forecast)와 지역코드 검색 기능이 모두 새 저장소로 이관되었으며, 대기정체지수 기능도 추가되었습니다.
> 이 저장소는 참고용으로 보관되며, fly.io 배포는 중단되었습니다. 앞으로는 위 신규 저장소를 사용하세요.
# safemap-uv-index-mcp
행정안전부 생활안전지도(safemap.go.kr)에서 제공하는 **자외선지수 정보조회 서비스**
(기상청 생활기상지수 중 자외선지수를 시군구 단위로 정리한 자료)를 조회하는 MCP 서버.
## 제공 데이터
단일 데이터셋 구성이며, 별도의 Dataset Registry 표는 필요하지 않다.
- 데이터셋명(화면 표시): 자외선지수
- 제공기관: 기상청 (관리부서: 기상융합서비스과)
- 제공 플랫폼: 행정안전부 생활안전지도(safemap.go.kr)
- 원 API: 자외선지수 정보조회 서비스 (IF_0113)
## 툴
### `get_uv_index`
시도/시군구 기준으로 자외선지수를 조회한다.
**파라미터**
| 이름 | 타입 | 필수 | 설명 |
|---|---|---|---|
| sido | string | 선택 | 시도명으로 필터링 (예: "서울", "경기"). 클라이언트 사이드 부분 일치 |
| sigungu | string | 선택 | 시군구명으로 필터링 (예: "강남구"). 클라이언트 사이드 부분 일치 |
| page_no | int | 선택 (기본 1) | 지역 필터 없이 전체 페이징 조회 시 사용 |
| num_of_rows | int | 선택 (기본 300) | 한 번에 가져올 결과 수 (전국 총 269건이므로 300이면 1페이지에 전체 포함) |
**반환 필드**
| 필드 | 설명 |
|---|---|
| ctprvn_nm | 시도명 (예: "서울", "경남", "강원특별자치도") |
| signgu_nm | 시군구명. 시도 전체를 대표하는 항목은 빈 문자열("")로 옴 |
| emd_nm | 읍면동명. 실측 결과 **항상 빈 문자열**로 확인됨 |
| ulvry_index | 자외선지수 값. 정수(int)로 반환 (실측 결과 0~9 범위의 순수 숫자로 확인됨, 등급 문자열 아님) |
| occrrnc_dt | 발생일시. 실측 포맷: `YYYYMMDDHH` (10자리, 예: "2026072106" = 2026-07-21 06시) |
## 알려진 제약사항 (실측 완료)
원 명세서가 엑셀/XML 샘플이 아닌 화면 캡처 2장 + 자바 샘플코드로만 제공되어 실측이
필요했던 항목들의 실제 확인 결과는 다음과 같다 (2026-08-22 실측):
- **returnType 기본값**: `returnType`을 생략해도 실제로 JSON이 반환됨(안내 문구
"Default: JSON"이 맞음). 다만 안전하게 매 요청에 `returnType=json`을 명시적으로 포함한다.
- **지역 필터 파라미터**: `sidoNm` 등 지역 필터 파라미터를 시도해봤으나 서버가 무시하고
전체 데이터를 그대로 반환함 — **API 자체 지역 필터는 지원하지 않음**. 이 서버는
전국 데이터를 전체 조회한 뒤 `sido`/`sigungu` 파라미터로 클라이언트 사이드
필터링을 수행한다.
- **자외선지수(ulvry_index)**: 실측 결과 `"0"`, `"1"`, `"2"`, `"7"`, `"9"` 등 순수
숫자 문자열로 확인됨 ("낮음/보통/높음" 같은 등급 문자열 아님). 서버는 이를 정수로
안전 변환하여 반환한다.
- **발생일시(occrrnc_dt) 포맷**: `YYYYMMDDHH` (시(hour) 단위까지, 분 없음). 예:
`"2026072106"`.
- **totalCount / 전국 시군구 수**: `numOfRows=1000, pageNo=1`로 호출 시 `totalCount=269`,
실제 반환 건수도 269건으로 일치. 시도 17개 전체가 포함되며, 각 시도별로 "시군구 전체"를
대표하는 항목(signgu_nm="")과 개별 시군구 항목이 함께 옴.
- **에러코드**: 정상 응답은 `resultCode="00"`, `resultMsg="NORMAL_SERVICE"`. 잘못된
서비스키로 호출 시 `resultCode="30"`, `resultMsg="SERVICE_KEY_IS_NOT_REGISTERED_ERROR"`
(HTTP 상태코드는 500으로 확인됨).
- **읍면동명(emd_nm)**: 실측 범위 내에서는 항상 빈 문자열로 확인됨.
- **엔드포인트**: `http://safemap.go.kr/openapi2/IF_0113`으로 호출 시 `https://www.safemap.go.kr/...`로
리다이렉트됨(자동 https 전환, httpx `follow_redirects=True`로 처리).
## 설치 및 실행
```bash
pip install -r requirements.txt
cp .env.example .env # SAFEMAP_API_KEY 값 입력
python server.py
```
## 환경변수
| 변수명 | 설명 |
|---|---|
| SAFEMAP_API_KEY | 행정안전부 생활안전지도(safemap.go.kr)에서 발급받은 인증키 |
| PORT | 서버 포트 (기본 8080) |
## 배포 (fly.io)
```bash
fly launch --no-deploy
fly secrets set SAFEMAP_API_KEY=발급받은키
flyctl deploy
```
배포 후 Claude.ai 커넥터 연결 시 반드시 `/mcp` 경로를 붙인다:
```
https://safemap-uv-index-mcp.fly.dev/mcp
```
## Rate Limit
API 키 인증 없이 URL만으로 커넥터 연결이 가능한 공개 서버이므로, IP 기준
슬라이딩 윈도우 방식의 rate limit이 적용된다:
- 60초 이내 30회 초과 시 429 (`Fly-Client-IP` 헤더 우선, 없으면 `X-Forwarded-For` 첫 값 사용)
- CORS preflight(OPTIONS) 요청은 카운트에서 제외
## 라이선스
MIT License. 데이터 자체의 저작권은 공공누리(행정안전부 생활안전지도) 이용조건을 따른다.
## 출처
행정안전부 생활안전지도(safemap.go.kr), 기상청(기상융합서비스과) 제공
「자외선지수 정보조회 서비스(IF_0113)」
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues