instruction.md
# 외교부 해외안전·여행경보 조회
## What this skill does
외교부 `국가·지역별 여행경보 목록 조회(0404 대륙정보)` API를
`k-skill-proxy`의 좁은 read-only route로 조회한다. 국가명 또는 ISO 2자리
코드로 공식 여행경보 단계, 지역 유형, 경보 내용, 작성일과 공식 지도 URL을
확인한다.
이 skill은 자체적인 안전/위험 점수나 여행 허가 판단을 만들지 않는다.
응답된 외교부 공식 경보와 원문 링크를 사실 그대로 요약한다.
## Inputs
- `country_iso_alp2`: ISO 2자리 국가코드(예: `RU`)
- `country_nm`: 한글 국가명(예: `러시아`)
- `page`: 기본 `1`
- `perPage`: 기본 `10`, 최대 `100`
국가명과 ISO 코드는 동시에 주지 않는다.
## Workflow
```bash
BASE="${KSKILL_PROXY_BASE_URL:-https://k-skill-proxy.nomadamas.org}"
curl -fsS --get "$BASE/v1/mofa-travel-safety/travel-alerts" \
--data-urlencode "country_iso_alp2=RU" \
--data-urlencode "perPage=10"
```
응답 `items`의 `country_nm`, `country_iso_alp2`, `alarm_lvl`,
`region_ty`, `remark`, `written_dt`, `dang_map_download_url`을 원문 기준으로
요약한다. `alarm_lvl` 숫자의 의미는 답변에서 임의로 재분류하지 말고
외교부 원문/0404 설명을 함께 제시한다.
## Access path and credentials
일반 사용자는 hosted `k-skill-proxy`를 사용하므로 data.go.kr 키가 필요
없다. proxy 운영자는 승인된 `DATA_GO_KR_API_KEY`를 서버 runtime env에
보관한다. 키는 URL, 로그, 저장소, 사용자 응답에 노출하지 않는다.
공식 API:
<https://www.data.go.kr/data/15095500/openapi.do>
실제 upstream:
`https://apis.data.go.kr/1262000/TravelAlarmService0404/getTravelAlarm0404List`
## Fallback and failure modes
- 먼저 proxy route를 사용하고, 실패하면 공식 data.go.kr API 문서와 0404
원문 링크만 안내한다. 임의의 안전 판정을 대신하지 않는다.
- `400 bad_request`: 잘못된 ISO 코드, 국가명, 페이지 값
- `503 upstream_not_configured`: proxy runtime에 data.go.kr 키 없음
- `502 upstream_error`: 외교부/data.go.kr 인증·quota·상류 장애
- `upstream_invalid_response`: JSON 대신 XML/HTML 오류 응답
- `items: []`: 해당 조건에 맞는 국가/지역 기록 없음
## Done when
- 국가명 또는 ISO 코드와 조회 시각을 답변에 적었다.
- `alarm_lvl`, `region_ty`, `remark`를 공식 필드명과 함께 제시했다.
- 원문 API 문서와 0404 공식 링크를 함께 제공했다.
scripts/run_mofa_travel_safety.py
#!/usr/bin/env python3
"""Read MOFA 0404 travel alerts through the hosted proxy."""
import argparse
import json
import os
import sys
import urllib.error
import urllib.parse
import urllib.request
DEFAULT_PROXY = "https://k-skill-proxy.nomadamas.org"
def main(argv=None):
parser = argparse.ArgumentParser(description="외교부 국가별 여행경보 조회")
parser.add_argument("--country-iso")
parser.add_argument("--country-name")
parser.add_argument("--page", type=int, default=1)
parser.add_argument("--per-page", type=int, default=10)
parser.add_argument("--text", action="store_true")
parser.add_argument("--dry-run", action="store_true")
parser.add_argument("--proxy-base-url", default=os.getenv("KSKILL_PROXY_BASE_URL", DEFAULT_PROXY))
args = parser.parse_args(argv)
if args.country_iso and args.country_name:
print("[error] use either --country-iso or --country-name", file=sys.stderr)
return 2
if not 1 <= args.page or not 1 <= args.per_page <= 100:
print("[error] invalid page or per-page", file=sys.stderr)
return 2
params = {"page": args.page, "perPage": args.per_page}
if args.country_iso:
if len(args.country_iso) != 2 or not args.country_iso.isalpha():
print("[error] --country-iso must be two letters", file=sys.stderr)
return 2
params["country_iso_alp2"] = args.country_iso.upper()
if args.country_name:
params["country_nm"] = args.country_name
url = f"{args.proxy_base_url.rstrip('/')}/v1/mofa-travel-safety/travel-alerts?{urllib.parse.urlencode(params)}"
if args.dry_run:
print(json.dumps({"url": url, "query": params}, ensure_ascii=False, indent=2))
return 0
try:
with urllib.request.urlopen(urllib.request.Request(url, headers={"accept": "application/json", "user-agent": "k-skill/mofa-travel-safety"}), timeout=30) as response:
payload = json.loads(response.read())
except (OSError, ValueError, urllib.error.HTTPError) as error:
print(f"[error] MOFA request failed: {error}", file=sys.stderr)
return 3
if args.text:
for item in payload.get("items", []):
print(f"{item.get('country_nm')} ({item.get('country_iso_alp2')}): level={item.get('alarm_lvl')} region={item.get('region_ty')}")
else:
print(json.dumps(payload, ensure_ascii=False, indent=2))
return 0
if __name__ == "__main__":
raise SystemExit(main())
skill.json
{
"name": "mofa-travel-safety",
"description": "외교부 0404 국가·지역별 여행경보 Open API를 k-skill-proxy 경유로 조회해 국가별 공식 경보 단계와 원문 정보를 제공한다. 조회 전용.",
"profiles": [
"proxy",
"lookup"
],
"frontmatter": "name: mofa-travel-safety\ndescription: 외교부 0404 국가·지역별 여행경보 Open API를 k-skill-proxy 경유로 조회해 국가별 공식 경보 단계와 원문 정보를 제공한다. 조회 전용.\nlicense: MIT\nmetadata:\n category: travel\n locale: ko-KR\n phase: v1"
}
SKILL.md
---
name: mofa-travel-safety
description: 외교부 0404 국가·지역별 여행경보 Open API를 k-skill-proxy 경유로 조회해 국가별 공식 경보 단계와 원문 정보를 제공한다. 조회 전용.
license: MIT
metadata:
category: travel
locale: ko-KR
phase: v1
---
# mofa-travel-safety
<!-- k-skill:cli-stub — generated by scripts/generate-skill-stubs.js; edit skill.json / instruction.md instead -->
## Get the full instructions (required first step)
Run this and follow its output as the primary instructions for this skill:
```bash
npx -y @nomadamas/k-skill@0 instruct mofa-travel-safety
```
The CLI detects the current runtime (Dolshoi vault/CloakBrowser vs generic) and prints only the applicable instructions, always up to date. Helper files bundled with the CLI are listed by:
```bash
npx -y @nomadamas/k-skill@0 files mofa-travel-safety
```
Keep the CLI and every coding-agent skill install current (including Vercel Agent Skills) with:
```bash
npx -y @nomadamas/k-skill@0 update
```
If `npx` is unavailable, install Node.js 18+ or follow https://github.com/NomaDamas/k-skill#readme, or read the source instructions at https://github.com/NomaDamas/k-skill/blob/main/mofa-travel-safety/instruction.md.
## Hard rules even without the CLI
- Never execute payment, message/email delivery, final submission, cancellation, or public posting without the user's explicit approval immediately beforehand.
- Never ask for, print, or store plaintext credentials in chat, files, or shell arguments.
- Never bypass legal, physical-presence, CAPTCHA, identity-proofing, or electronic-signature boundaries.