@nomadamas/k-skill 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/k-skill.js +0 -0
- package/package.json +1 -1
- package/skills/fine-dust-location/instruction.md +1 -1
- package/skills/fine-dust-location/scripts/fine_dust.py +549 -0
- package/skills/fine-dust-location/skill.json +7 -1
- package/skills/gangnamunni-clinic-search/instruction.md +16 -8
- package/skills/hankookilbo-news/instruction.md +143 -0
- package/skills/hankookilbo-news/skill.json +8 -0
- package/skills/k-skill-setup/instruction.md +1 -1
- package/skills/k-skill-setup/scripts/check-setup.sh +29 -0
- package/skills/k-skill-setup/skill.json +7 -1
- package/skills/kosis-stats/instruction.md +24 -3
- package/skills/kosis-stats/references/kosis-openapi-guide.md +5 -1
- package/skills/kosis-stats/scripts/run_kosis_stats.py +1 -1
- package/skills/ktx-booking/instruction.md +15 -15
- package/skills/ktx-booking/scripts/ktx_booking.py +1316 -0
- package/skills/ktx-booking/skill.json +7 -1
- package/skills/naver-blog-research/scripts/naver_search.py +6 -1
- package/src/assemble.js +1 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# 한국일보 뉴스 조회
|
|
2
|
+
|
|
3
|
+
## What this skill does
|
|
4
|
+
|
|
5
|
+
한국일보가 운영하는 공식 원격 MCP 서버를 직접 호출해 기사 메타데이터를 조회한다.
|
|
6
|
+
|
|
7
|
+
- 엔드포인트: `https://mcp.hankookilbo.com/mcp` (Streamable HTTP MCP, 인증 불필요)
|
|
8
|
+
- 공식 MCP Registry 등재명: `com.hankookilbo.mcp/hankookilbo-mcp`
|
|
9
|
+
- 도구 10종 — 편집 헤드라인, 많이 본, 꼼꼼히 본, 시간대 추천, 최신, 섹션별 편집 추천, 주제 검색, 섹션 목록, 오늘의 운세, MBTI 운세
|
|
10
|
+
- 반환값은 기사 메타데이터다: 기사 ID, 제목, 원문 URL, 썸네일 URL, 발행시각, 기사 유형, 원문 접근성, 짧은 발췌(`excerpt`)
|
|
11
|
+
- 기사 본문 전문은 반환하지 않는다
|
|
12
|
+
|
|
13
|
+
서버가 무상태라 `initialize` 핸드셰이크와 세션 ID 없이 단일 POST 로 `tools/call` 이 동작한다. MCP SDK 를 설치하지 않고 `curl` 로 호출한다.
|
|
14
|
+
|
|
15
|
+
## When to use
|
|
16
|
+
|
|
17
|
+
- "한국일보 헤드라인 보여줘"
|
|
18
|
+
- "한국일보에서 많이 본 기사"
|
|
19
|
+
- "오늘 한국일보 정치면 추천 기사"
|
|
20
|
+
- "한국일보에 이 사건 기사 있어?"
|
|
21
|
+
- "오늘의 운세", "MBTI 운세" (한국일보 지면 기준)
|
|
22
|
+
- 한국일보가 무엇을 머리기사로 올렸는지, 즉 편집 판단 자체가 필요할 때
|
|
23
|
+
|
|
24
|
+
## When not to use
|
|
25
|
+
|
|
26
|
+
- 언론사를 가리지 않는 일반 뉴스 키워드 검색 — 이 서버는 한국일보 기사만 반환한다
|
|
27
|
+
- 기사 본문 전문이 필요할 때 — 이 스킬은 메타데이터만 반환한다. 본문은 `item.url` 원문에서만 볼 수 있다
|
|
28
|
+
- 한국경제(한경)나 다른 언론사 기사 — 이 서버는 종합일간지 한국일보 전용이다
|
|
29
|
+
- 로그인 뒤에만 보이는 기사의 본문 — `item.view_type` 이 `LoginWall` 이면 원문 열람에 로그인이 필요하다
|
|
30
|
+
- 주식·환율 시세, 실시간 데이터 — 이 서버는 기사만 다룬다
|
|
31
|
+
|
|
32
|
+
## Endpoint contract
|
|
33
|
+
|
|
34
|
+
- POST 만 쓴다. GET·PUT·DELETE·OPTIONS 는 405 다.
|
|
35
|
+
- `Accept` 헤더에 `application/json` 과 `text/event-stream` 을 **둘 다** 넣는다. MCP Streamable HTTP 규격이 클라이언트에 요구하는 사항이고, 서버 응답이 나중에 SSE 로 바뀌어도 깨지지 않는다. `Accept` 를 아예 빼면 406 이다.
|
|
36
|
+
- `initialize`, `notifications/initialized`, `Mcp-Session-Id` 는 필요 없다.
|
|
37
|
+
- `User-Agent` 를 `k-skill-hankookilbo/1.0` 으로 보낸다. 한국일보 쪽에서 k-skill 경유 트래픽을 분리 관측한다.
|
|
38
|
+
|
|
39
|
+
기본 호출:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
curl -fsS --max-time 35 https://mcp.hankookilbo.com/mcp \
|
|
43
|
+
-H 'Content-Type: application/json' \
|
|
44
|
+
-H 'Accept: application/json, text/event-stream' \
|
|
45
|
+
-H 'User-Agent: k-skill-hankookilbo/1.0' \
|
|
46
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_top_headlines","arguments":{}}}'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
기사 목록은 `result.structuredContent.items` 에 들어온다. `result.content[0].text` 는 사람이 읽는 요약본이다. 목록을 정리할 때는 `structuredContent` 를 쓴다.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
curl -fsS --max-time 35 https://mcp.hankookilbo.com/mcp \
|
|
53
|
+
-H 'Content-Type: application/json' \
|
|
54
|
+
-H 'Accept: application/json, text/event-stream' \
|
|
55
|
+
-H 'User-Agent: k-skill-hankookilbo/1.0' \
|
|
56
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_popular_news","arguments":{"section_cd":"economy","page_size":5}}}' \
|
|
57
|
+
| python3 -c 'import json,sys
|
|
58
|
+
for i in json.load(sys.stdin)["result"]["structuredContent"]["items"]:
|
|
59
|
+
print(i["published_at"], "|", i["title"], "|", i["url"])'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
MCP 클라이언트에 직접 등록해도 된다. 이 경로에서는 서버가 보내는 instructions 도 함께 전달된다.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
claude mcp add --transport http hankookilbo https://mcp.hankookilbo.com/mcp
|
|
66
|
+
codex mcp add hankookilbo --url https://mcp.hankookilbo.com/mcp
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 도구 선택
|
|
70
|
+
|
|
71
|
+
| 사용자 발화 | 도구 | 인자 |
|
|
72
|
+
| --- | --- | --- |
|
|
73
|
+
| "헤드라인", "오늘 주요 뉴스", "제일 중요한 뉴스" | `list_top_headlines` | 없음 |
|
|
74
|
+
| "많이 본", "인기 뉴스", "지금 뜨는" | `list_popular_news` | `section_cd`, `page_size`, `exclude_section_cd` (전부 선택) |
|
|
75
|
+
| "꼼꼼히 본", "정독할 만한" | `list_most_read_news` | 없음 |
|
|
76
|
+
| "지금 볼만한", "출근길에 볼 뉴스" | `list_timely_news` | 없음 |
|
|
77
|
+
| "최신 기사", "방금 올라온" | `list_latest_news` | `page_num`, `page_size` (선택) |
|
|
78
|
+
| 섹션별 편집 추천 | `list_recommended_articles` | `section_cd` (필수), `page_size` |
|
|
79
|
+
| 주제·인물·사건 검색, "왜 화제야" | `search_news` | `query` (필수), `limit` |
|
|
80
|
+
| 섹션 코드 확인 | `list_sections` | 없음 |
|
|
81
|
+
| "오늘의 운세", "띠별 운세" | `get_daily_horoscope` | `date` (선택) |
|
|
82
|
+
| "MBTI 운세" | `get_mbti_horoscope` | `date` (선택) |
|
|
83
|
+
|
|
84
|
+
`section_cd` 값:
|
|
85
|
+
|
|
86
|
+
- `politics` 정치
|
|
87
|
+
- `economy` 경제
|
|
88
|
+
- `society` 사회
|
|
89
|
+
- `world` 국제
|
|
90
|
+
- `culture` 문화
|
|
91
|
+
- `sports` 스포츠
|
|
92
|
+
- `life` 라이프
|
|
93
|
+
- `people` 사람
|
|
94
|
+
- `local` 지역
|
|
95
|
+
- `opinion` 오피니언
|
|
96
|
+
|
|
97
|
+
값을 추측하지 말고 불확실하면 `list_sections` 로 확인한다.
|
|
98
|
+
|
|
99
|
+
세 가지 목록이 서로 다르다.
|
|
100
|
+
|
|
101
|
+
- `list_top_headlines` — 편집부가 비중 있게 배치한 머리기사
|
|
102
|
+
- `list_popular_news` — 조회수 기준 인기 순위. "많이 본"은 이쪽이다
|
|
103
|
+
- `list_recommended_articles` — 섹션별 편집 추천. 순위 값을 갖지 않는다
|
|
104
|
+
|
|
105
|
+
## Workflow
|
|
106
|
+
|
|
107
|
+
1. 요청을 위 표로 분류한다. 섹션이 필요한데 불확실하면 `list_sections` 를 먼저 호출한다.
|
|
108
|
+
2. `curl` 로 해당 도구를 호출한다.
|
|
109
|
+
3. `result.structuredContent.items` 상위 3~5건을 제목·발행시각·원문 링크로 정리한다.
|
|
110
|
+
4. `search_news` 는 응답이 30초에 가까울 수 있다. `--max-time 35` 를 주고, 타임아웃되면 재시도하지 말고 질의를 좁혀 다시 묻는다.
|
|
111
|
+
|
|
112
|
+
## Response style
|
|
113
|
+
|
|
114
|
+
- **기사 제목은 `item.title` 원문을 그대로 인용한다.** 요약·의역·말줄임·기호 변경·맞춤법 교정을 거치면 실제 보도 제목과 달라진다. `[제목](url)` 형태가 원문 표기와 가장 일치한다.
|
|
115
|
+
- `item.excerpt` 는 기사 도입부 일부이고 기사 전체 요약이 아니다. 발췌에 없는 해석·평가·배경·결말을 이 데이터로 단정하지 않는다.
|
|
116
|
+
- 본문 전문은 `item.url` 에서만 확인할 수 있다. 본문 내용을 재구성하지 않고 링크를 제시한다.
|
|
117
|
+
- `item.view_type` 이 `LoginWall` 이면 원문 열람에 로그인이 필요하다고 함께 알린다.
|
|
118
|
+
- `items` 가 비어 있지 않다는 것은 그 조건의 기사가 존재한다는 뜻이다. 목록을 임의로 누락시키지 않는다.
|
|
119
|
+
- 운세 도구는 운세 내용을 반환하지 않는다. 제목·발행일·원문 링크만 전달하고 운세 내용은 원문 링크로 넘긴다.
|
|
120
|
+
- 원문 URL 에는 서버가 `?did=mcp` 유입 파라미터를 붙여 돌려준다. 링크를 제시할 때 이 파라미터를 지우지 않는다.
|
|
121
|
+
|
|
122
|
+
## Failure modes
|
|
123
|
+
|
|
124
|
+
- `406 Not Acceptable` — `Accept` 헤더가 없다. `application/json, text/event-stream` 을 채워 재시도한다.
|
|
125
|
+
- `405 Method Not Allowed` — POST 가 아닌 메서드를 썼다. POST 로 바꾼다.
|
|
126
|
+
- `result.isError: true` — 도구 인자가 잘못됐다. `section_cd` 오타, `query` 누락을 확인한다.
|
|
127
|
+
- `504` — `search_news` 가 제한 시간을 넘겼다. 재시도 루프를 만들지 말고 질의를 좁힌다.
|
|
128
|
+
- 빈 `items` — 해당 조건의 기사가 없다. 섹션·질의를 바꿔 다시 시도하거나 없다고 답한다.
|
|
129
|
+
- 연결 실패 — 한국일보 측 서버 장애다. 재시도 루프를 만들지 않고 현재 조회 불가임을 분명히 말한다.
|
|
130
|
+
|
|
131
|
+
## Privacy
|
|
132
|
+
|
|
133
|
+
- 인증·로그인·개인화가 없는 공개 조회 전용이다. 시크릿을 요구하지 않는다.
|
|
134
|
+
- 기사 본문을 요청하거나 저장하지 않는다.
|
|
135
|
+
- 검색어와 결과를 영구 저장하지 않는다.
|
|
136
|
+
|
|
137
|
+
## Done when
|
|
138
|
+
|
|
139
|
+
- 요청 유형에 맞는 도구를 골랐다.
|
|
140
|
+
- `structuredContent.items` 기준으로 상위 후보를 제목·발행시각·원문 링크로 정리했다.
|
|
141
|
+
- 제목을 원문 그대로 인용하고 `[제목](url)` 링크를 제시했다.
|
|
142
|
+
- 본문이 필요한 요청이면 본문은 원문 링크에서만 볼 수 있다고 안내했다.
|
|
143
|
+
- 실패 시 재시도 루프 없이 원인을 알렸다.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hankookilbo-news",
|
|
3
|
+
"description": "한국일보 공식 원격 MCP 서버(mcp.hankookilbo.com)를 인증 없이 직접 호출해 편집 헤드라인·많이 본·꼼꼼히 본·최신 기사·섹션별 편집 추천·주제 검색·오늘의 운세를 기사 메타데이터(제목·발행시각·원문 링크) 중심으로 조회한다.",
|
|
4
|
+
"profiles": [
|
|
5
|
+
"lookup"
|
|
6
|
+
],
|
|
7
|
+
"frontmatter": "name: hankookilbo-news\ndescription: 한국일보 공식 원격 MCP 서버(mcp.hankookilbo.com)를 인증 없이 직접 호출해 편집 헤드라인·많이 본·꼼꼼히 본·최신 기사·섹션별 편집 추천·주제 검색·오늘의 운세를 기사 메타데이터(제목·발행시각·원문 링크) 중심으로 조회한다.\nlicense: MIT\nmetadata:\n category: information\n locale: ko-KR\n phase: v1"
|
|
8
|
+
}
|
|
@@ -167,7 +167,7 @@ else
|
|
|
167
167
|
fi
|
|
168
168
|
```
|
|
169
169
|
|
|
170
|
-
repo 전체를 clone받은 경우에는 같은 검증을 `
|
|
170
|
+
repo 전체를 clone받은 경우에는 같은 검증을 `npx -y @nomadamas/k-skill@0 exec k-skill-setup scripts/check-setup.sh --` 로 실행해도 된다.
|
|
171
171
|
|
|
172
172
|
### 3. Offer scheduled update checks
|
|
173
173
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
secrets_file="${1:-$HOME/.config/k-skill/secrets.env}"
|
|
5
|
+
|
|
6
|
+
missing=0
|
|
7
|
+
|
|
8
|
+
if [[ ! -f "$secrets_file" ]]; then
|
|
9
|
+
echo "missing secrets file: $secrets_file"
|
|
10
|
+
missing=1
|
|
11
|
+
else
|
|
12
|
+
perms=$(stat -f '%Lp' "$secrets_file" 2>/dev/null || stat -c '%a' "$secrets_file" 2>/dev/null)
|
|
13
|
+
if [[ "$perms" != "600" ]]; then
|
|
14
|
+
echo "insecure permissions on $secrets_file: $perms (expected 600)"
|
|
15
|
+
missing=1
|
|
16
|
+
fi
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
if [[ "$missing" -ne 0 ]]; then
|
|
20
|
+
cat <<EOF
|
|
21
|
+
next steps:
|
|
22
|
+
1. create ~/.config/k-skill/secrets.env with your credentials
|
|
23
|
+
2. chmod 0600 ~/.config/k-skill/secrets.env
|
|
24
|
+
3. run this check again
|
|
25
|
+
EOF
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
echo "k-skill setup looks usable"
|
|
@@ -7,5 +7,11 @@
|
|
|
7
7
|
"browser",
|
|
8
8
|
"operations"
|
|
9
9
|
],
|
|
10
|
-
"frontmatter": "name: k-skill-setup\ndescription: After installing the full k-skill bundle, configure and verify the shared cross-platform setup, then optionally wire update checks and GitHub starring with explicit user consent.\nlicense: MIT\nmetadata:\n category: setup\n locale: ko-KR\n phase: v1"
|
|
10
|
+
"frontmatter": "name: k-skill-setup\ndescription: After installing the full k-skill bundle, configure and verify the shared cross-platform setup, then optionally wire update checks and GitHub starring with explicit user consent.\nlicense: MIT\nmetadata:\n category: setup\n locale: ko-KR\n phase: v1",
|
|
11
|
+
"bundle": [
|
|
12
|
+
{
|
|
13
|
+
"from": "scripts/check-setup.sh",
|
|
14
|
+
"to": "scripts/check-setup.sh"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
11
17
|
}
|
|
@@ -132,12 +132,19 @@ npx -y @nomadamas/k-skill@0 exec kosis-stats scripts/run_kosis_stats.py -- searc
|
|
|
132
132
|
|
|
133
133
|
### 3. Inspect the table meta before fetching data
|
|
134
134
|
|
|
135
|
-
데이터를 받기 전에
|
|
135
|
+
데이터를 받기 전에 분류/단위를 확인한다.
|
|
136
136
|
|
|
137
137
|
```bash
|
|
138
138
|
npx -y @nomadamas/k-skill@0 exec kosis-stats scripts/run_kosis_stats.py -- meta --table-id DT_1JC1501 --text
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
+
**수록주기는 `meta` 로 확정되지 않는다.** `meta --meta-type TBL` 은 표 명칭(국/영문) 위주라 `--prd-se` 에 넣을 주기를 돌려주지 않고, `search` 응답의 `PRD_SE` 는 `--prd-se` 와 코드 체계가 달라(예: `DT_1IN0001` → `PRD_SE=A` 이지만 실제 조회는 `F`) 그대로 쓰면 안 된다. `STRT_PRD_DE`~`END_PRD_DE` 도 수록 범위일 뿐 간격을 알려주지 않는다. 조사주기는 `explain` 으로 확인하거나 §4 의 프로브로 확정한다.
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
npx -y @nomadamas/k-skill@0 exec kosis-stats scripts/run_kosis_stats.py -- explain \
|
|
145
|
+
--org-id 101 --table-id DT_1IN0001 --meta-itm All --text
|
|
146
|
+
```
|
|
147
|
+
|
|
141
148
|
### 4. Fetch a small bounded slice first
|
|
142
149
|
|
|
143
150
|
`--prd-se`, `--start`, `--end`, `--obj-l` 으로 범위를 좁혀 작은 슬라이스를 먼저 조회한다.
|
|
@@ -148,6 +155,16 @@ npx -y @nomadamas/k-skill@0 exec kosis-stats scripts/run_kosis_stats.py -- data
|
|
|
148
155
|
--obj-l 1=ALL --json
|
|
149
156
|
```
|
|
150
157
|
|
|
158
|
+
**`--prd-se` 를 먼저 확정한다.** 주기를 틀리면 다른 파라미터가 전부 맞아도 KOSIS는 코드 `30`(결과 없음)만 돌려주며, 메시지로는 원인을 알 수 없다. `explain`(§3)으로 조사주기를 확인하거나, 좁은 기간·최소 분류로 `Y` → `F` → `IR` → `M`/`Q`/`S` 순으로 프로브해 데이터가 나오는 코드를 찾은 뒤 본 조회를 한다. 총조사·인구주택총조사처럼 5년 간격인 표는 연 단위로 보여도 `F`(다년)다.
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
for p in Y F IR; do
|
|
162
|
+
npx -y @nomadamas/k-skill@0 exec kosis-stats scripts/run_kosis_stats.py -- data \
|
|
163
|
+
--org-id 101 --table-id DT_1IN0001 --prd-se "$p" \
|
|
164
|
+
--start 2010 --end 2010 --itm-id ALL --obj-l 1=00 --obj-l 2=00 --text
|
|
165
|
+
done
|
|
166
|
+
```
|
|
167
|
+
|
|
151
168
|
표마다 필수 분류 차원 수가 다르다. **default `--obj-l 1=ALL` 만으로는 부족한 표가 많다.** KOSIS가 코드 `20` (필수요청변수값 누락 objL)을 돌려주면, `meta --table-id <ID> --meta-type ITM --json` 으로 ITM 안에 들어 있는 `OBJ_ID`(분류 차원)와 코드를 확인한 뒤 `--obj-l 1=<코드> --obj-l 2=<코드>` 형태로 필요한 차원을 모두 지정한다. (많은 표가 OBJ 메타는 비어 있고 분류가 ITM 안에 들어 있음.)
|
|
152
169
|
|
|
153
170
|
40,000셀을 초과하면 KOSIS는 에러 코드 `31` 또는 `41` 을 반환한다. 기간을 좁히거나(예: 5년→1년) 분류 필터의 ALL 을 특정 코드로 바꿔(예: `--obj-l 1=11` 서울만) 호출을 분할한다. 그래도 부족하면 사용자별 통계자료(`userStatsId`)를 등록해 `bigdata` 서브커맨드를 사용한다.
|
|
@@ -171,7 +188,7 @@ npx -y @nomadamas/k-skill@0 exec kosis-stats scripts/run_kosis_stats.py -- bigda
|
|
|
171
188
|
## Done when
|
|
172
189
|
|
|
173
190
|
- 사용자 질문에 대응하는 통계표 ID(`org_id`/`tbl_id`)가 명확하다.
|
|
174
|
-
- 메타데이터를 1회 이상 조회해
|
|
191
|
+
- 메타데이터를 1회 이상 조회해 분류·단위를 확인했고, `--prd-se` 는 실제 데이터가 나오는 코드로 확정했다.
|
|
175
192
|
- 작은 슬라이스부터 단계적으로 데이터를 받았다.
|
|
176
193
|
- 결과에 출처(table id, 기간, 단위, endpoint)를 명시했다.
|
|
177
194
|
- 한도 초과 시 분할 또는 `bigdata` 안내로 처리했다.
|
|
@@ -182,7 +199,10 @@ npx -y @nomadamas/k-skill@0 exec kosis-stats scripts/run_kosis_stats.py -- bigda
|
|
|
182
199
|
- KOSIS 에러 코드 `10`/`11`: 인증키 누락/만료 → 키 점검. `bigdata` 에서 `11` 이 나오면 `userStatsId` 가 본인 KOSIS 계정에 등록된 것이 아닐 가능성이 크다.
|
|
183
200
|
- 코드 `20`: 필수 분류 누락 → `meta --meta-type OBJ` (또는 비어 있으면 `ITM`) 으로 필요한 차원 수와 코드를 확인하고 `--obj-l 1=... --obj-l 2=...` 모두 지정 후 재시도
|
|
184
201
|
- 코드 `21`: 잘못된 요청 변수 → `org_id`/`tbl_id`/기간 형식 재확인. tblId 의심 시 `search` 로 정확한 ID 다시 찾기
|
|
185
|
-
- 코드 `30`: 결과
|
|
202
|
+
- 코드 `30`: 결과 없음. 어느 서브커맨드에서 났는지로 원인이 갈린다.
|
|
203
|
+
- **`data` 에서 30 → 먼저 `--prd-se` 오지정을 의심한다.** 표의 실제 수록주기와 다른 코드를 주면 `--start`/`--end`/`--obj-l` 이 전부 맞아도 항상 30 이다. 연 단위처럼 보이는 표라도 총조사·센서스류는 다년주기 `F`(2·3·4·5·10년) 또는 부정기 `IR` 인 경우가 많다. `explain` 으로 조사주기를 확인하거나 `Y` → `F` → `IR` → `M`/`Q`/`S` 순으로 프로브해 확정한다 (§4 참고).
|
|
204
|
+
- `search` 에서 30 → 키워드를 더 짧게 또는 다른 표현으로 바꾸거나 기간/분류 완화
|
|
205
|
+
- **`meta` 에서 30** → 표가 해당 메타 타입을 지원하지 않는 경우이므로 다른 `--meta-type` 시도
|
|
186
206
|
- 코드 `31`/`41`: 한도 초과 → 기간 좁히기, 분류 ALL 을 특정 코드로 바꾸기, 또는 `bigdata` 사용
|
|
187
207
|
- 코드 `40`: 분당 1,000건 호출 한도 → 잠시 대기
|
|
188
208
|
- 코드 `50`: KOSIS 서버 오류 → 1~2초 후 재시도
|
|
@@ -194,6 +214,7 @@ npx -y @nomadamas/k-skill@0 exec kosis-stats scripts/run_kosis_stats.py -- bigda
|
|
|
194
214
|
|
|
195
215
|
- 코드 20 회복: `data --table-id DT_1J22001 --prd-se M --start 202401 --end 202401` → 코드 20 → `meta --table-id DT_1J22001 --meta-type ITM --json` 으로 차원 확인 → `data ... --obj-l 1=T10 --obj-l 2=0` 재호출 → 성공
|
|
196
216
|
- 코드 31 회복: `data --table-id DT_1B26001 --prd-se Y --start 2020 --end 2024 --obj-l 1=ALL --obj-l 2=ALL --obj-l 3=ALL` → 코드 31 → `... --start 2024 --end 2024 --obj-l 1=11 --obj-l 2=ALL --obj-l 3=ALL` (서울만) 재호출 → 성공
|
|
217
|
+
- 코드 30 회복(수록주기 오지정): `data --org-id 101 --table-id DT_1IN0001 --prd-se Y --start 1990 --end 2010 --itm-id ALL --obj-l 1=00 --obj-l 2=00` → 코드 30 → 연도를 2015·2010·2005·2000·1995·1990 으로 바꿔 재시도해도 전부 코드 30, `--prd-se IR` 도 코드 30 → `--prd-se F --start 1925 --end 2010` 으로 재호출 → 성공(수록 시점 18개, 72셀). `DT_1IN0001`(총조사인구 총괄)은 연 단위 표처럼 보이지만 5년 간격 총조사라 `F` 로만 조회된다. 이 표는 `meta --meta-type TBL` 에도 주기가 없고 `search` 응답 `PRD_SE` 는 `A` 로 나와 둘 다 `--prd-se` 값으로 쓸 수 없었다.
|
|
197
218
|
|
|
198
219
|
## Maintainer review notes
|
|
199
220
|
|
|
@@ -54,10 +54,12 @@ GET https://kosis.kr/openapi/statisticsSearch.do
|
|
|
54
54
|
&searchNm=인구&resultCount=20&startCount=1
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
응답 필드(주요): `ORG_ID`, `ORG_NM`, `TBL_ID`, `TBL_NM`, `STAT_ID`, `STAT_NM`, `VW_CD`, `MT_ATITLE`, `STRT_PRD_DE`, `END_PRD_DE`, `LINK_URL`.
|
|
57
|
+
응답 필드(주요): `ORG_ID`, `ORG_NM`, `TBL_ID`, `TBL_NM`, `STAT_ID`, `STAT_NM`, `VW_CD`, `MT_ATITLE`, `PRD_SE`, `STRT_PRD_DE`, `END_PRD_DE`, `LINK_URL`.
|
|
58
58
|
|
|
59
59
|
데이터 조회는 `ORG_ID` + `TBL_ID` 조합을 다음 단계에서 사용한다.
|
|
60
60
|
|
|
61
|
+
⚠️ **`search` 의 `PRD_SE` 를 §3.3 의 `prdSe` 요청 파라미터로 그대로 쓰지 말 것.** 두 필드는 코드 체계가 다르다 — `DT_1IN0001`(총조사인구 총괄)은 `search` 가 `PRD_SE=A` 를 돌려주지만 실제 데이터 조회는 `prdSe=F` 로만 성공하고 `A`·`Y`·`IR` 은 모두 코드 `30`(결과 없음)이다. `STRT_PRD_DE`~`END_PRD_DE` 도 수록 범위일 뿐 간격(주기)을 알려주지 않는다. 요청용 주기는 `statisticsExplData.do`(helper 의 `explain` 서브커맨드) 의 조사주기 항목에서 확인하거나, §3.3 표의 코드를 좁은 슬라이스로 프로브해 확정한다.
|
|
62
|
+
|
|
61
63
|
### 3.2 통계표 메타데이터 (`statisticsData.do?method=getMeta`)
|
|
62
64
|
|
|
63
65
|
통계표의 분류·항목·단위·국문/영문명 등을 조회한다.
|
|
@@ -97,6 +99,8 @@ GET https://kosis.kr/openapi/Param/statisticsParameterData.do
|
|
|
97
99
|
| `F` | 다년(2,3,4,5,10년) | `YYYY` |
|
|
98
100
|
| `IR` | 부정기 | `YYYY` 또는 `YYYYMMDD` |
|
|
99
101
|
|
|
102
|
+
`prdSe` 를 틀리면 `startPrdDe`/`endPrdDe`/`objL*` 이 전부 맞아도 응답은 항상 코드 `30`(결과 없음)이고, 메시지만으로는 주기 문제인지 구분되지 않는다. 5년 간격 총조사처럼 연 단위로 보이는 표가 `F`(다년)인 경우가 흔하므로, `statisticsExplData.do` 로 조사주기를 확인하거나 최소 슬라이스로 `Y` → `F` → `IR` → `M`/`Q`/`S` 순으로 프로브해 확정한 뒤 본 조회를 한다.
|
|
103
|
+
|
|
100
104
|
분류 파라미터는 `objL1` ~ `objL8` (필요한 만큼만), 항목은 `itmId` (`ALL` 또는 특정 ID).
|
|
101
105
|
|
|
102
106
|
응답 셀 필드: `PRD_DE` (기간), `ITM_NM` (항목), `UNIT_NM` (단위), `DT` (값), `C1_NM`~`C8_NM` (분류명).
|
|
@@ -58,7 +58,7 @@ ERROR_CODE_HINTS: dict[str, str] = {
|
|
|
58
58
|
"11": "인증키가 만료되었거나 해당 endpoint에서 무효입니다. https://kosis.kr/openapi/ 에서 갱신하거나, bigdata는 본인이 등록한 userStatsId 인지 확인하세요.",
|
|
59
59
|
"20": "필수 요청 변수가 누락되었습니다. `meta --table-id <ID> --meta-type ITM --json` 으로 ITM 안에 들어 있는 OBJ_ID(분류 차원)와 코드를 확인하세요(많은 표가 OBJ 메타는 비어 있고 분류가 ITM 안에 들어 있음). 그 뒤 `--obj-l 1=<코드> --obj-l 2=<코드>` 형태로 필요한 차원을 모두 지정해 재호출하세요. 별도 OBJ 메타가 있는 표는 `--meta-type OBJ` 로도 확인 가능합니다.",
|
|
60
60
|
"21": "잘못된 요청 변수입니다. orgId/tblId/기간 형식을 재확인하세요. tblId가 의심되면 `search --query <키워드>` 로 정확한 ID를 다시 찾으세요.",
|
|
61
|
-
"30": "조회 결과가 없습니다. 키워드를 더 짧게(예: '1인 가구' → '가구') 또는 다른 표현으로 재검색하거나, 기간/분류 필터를 완화하세요. meta 호출에서 이 에러가 나면 해당 메타 타입을 표가 지원하지 않는 경우이므로 다른 `--meta-type` 을 시도하세요.",
|
|
61
|
+
"30": "조회 결과가 없습니다. data 호출이면 먼저 `--prd-se` 오지정을 의심하세요 — 표의 실제 수록주기와 다르면 기간/분류가 맞아도 항상 이 에러가 납니다. 연 단위로 보이는 총조사·센서스류는 다년주기 `F` 또는 부정기 `IR` 인 경우가 많으니 `explain --org-id <ORG> --table-id <TBL>` 로 조사주기를 확인하거나 좁은 슬라이스로 `Y` → `F` → `IR` → `M`/`Q`/`S` 순으로 프로브하세요(`search` 응답의 `PRD_SE` 는 코드 체계가 달라 그대로 쓸 수 없습니다). search 호출이면 키워드를 더 짧게(예: '1인 가구' → '가구') 또는 다른 표현으로 재검색하거나, 기간/분류 필터를 완화하세요. meta 호출에서 이 에러가 나면 해당 메타 타입을 표가 지원하지 않는 경우이므로 다른 `--meta-type` 을 시도하세요.",
|
|
62
62
|
"31": "조회 결과가 한도(40,000셀)를 초과했습니다. 기간을 좁히거나(예: 5년→1년) 분류 필터의 ALL 을 특정 코드로 바꾸세요(예: `--obj-l 1=ALL` → `--obj-l 1=11` 서울만). 그래도 부족하면 `bigdata` 서브커맨드 + 사전 등록한 userStatsId 를 사용하세요.",
|
|
63
63
|
"40": "분당 호출 한도(1,000건)를 초과했습니다. 잠시 대기 후 재시도하거나 호출 간 sleep 을 두세요.",
|
|
64
64
|
"41": "1회 호출 ROW 한도를 초과했습니다. 기간이나 분류를 좁혀 쿼리를 분할하세요.",
|
|
@@ -73,13 +73,13 @@ python3 -m pip install korail2-ncard pycryptodome
|
|
|
73
73
|
항상 helper 를 통해 조회한다.
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
|
-
|
|
76
|
+
npx -y @nomadamas/k-skill@0 exec ktx-booking scripts/ktx_booking.py -- search 서울 부산 20260328 090000 --limit 5
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
기본 `--train-type` 은 `ktx` 다. ITX-청춘(예: 남춘천↔용산)·ITX-새마을·무궁화호처럼 KTX 외 노선을 잡으려면 `--train-type` 으로 지정한다.
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
|
-
|
|
82
|
+
npx -y @nomadamas/k-skill@0 exec ktx-booking scripts/ktx_booking.py -- search 남춘천 용산 20260503 150000 --train-type itx-cheongchun
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
선택지: `ktx`, `itx-saemaeul`, `mugunghwa`, `nuriro`, `tonggeun`, `itx-cheongchun`, `airport`, `all`.
|
|
@@ -106,19 +106,19 @@ python3 scripts/ktx_booking.py search 남춘천 용산 20260503 150000 --train-t
|
|
|
106
106
|
기본 상세 좌석 조회:
|
|
107
107
|
|
|
108
108
|
```bash
|
|
109
|
-
|
|
109
|
+
npx -y @nomadamas/k-skill@0 exec ktx-booking scripts/ktx_booking.py -- seats 서울 부산 20260328 090000 --train-id <train_id>
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
일반실/특실은 `--room` 으로 나눈다.
|
|
113
113
|
|
|
114
114
|
```bash
|
|
115
|
-
|
|
115
|
+
npx -y @nomadamas/k-skill@0 exec ktx-booking scripts/ktx_booking.py -- seats 서울 부산 20260328 090000 --train-id <train_id> --room special
|
|
116
116
|
```
|
|
117
117
|
|
|
118
118
|
남은 좌석번호만 보고 싶으면 `--available-only` 를 쓴다.
|
|
119
119
|
|
|
120
120
|
```bash
|
|
121
|
-
|
|
121
|
+
npx -y @nomadamas/k-skill@0 exec ktx-booking scripts/ktx_booking.py -- seats 서울 부산 20260328 090000 --train-id <train_id> --available-only
|
|
122
122
|
```
|
|
123
123
|
|
|
124
124
|
특정 호차를 지정하지 않으면 `seats` 는 **5호차를 최우선**으로 탐색한다. 5호차가 없으면 5호차와의 거리가 가까운 호차 순으로, 같은 거리에서는 낮은 호차 번호 순으로 탐색한다(예: 1~8호차 편성은 `5, 4, 6, 3, 7, 2, 8, 1`, 1~4호차 편성은 `4, 3, 2, 1`). 일반 KTX, KTX-산천(분류 코드 07·10), KTX-청룡 모두 같은 규칙을 적용한다. 각 호차 안의 좌석은 콘센트 힌트가 있는 좌석(`direct`, `adjacent`)을 먼저, 같은 조건에서는 순방향 좌석을 먼저 보여준다.
|
|
@@ -126,19 +126,19 @@ python3 scripts/ktx_booking.py seats 서울 부산 20260328 090000 --train-id <t
|
|
|
126
126
|
특정 호차만 확인하려면 `--car-no` 를 쓴다.
|
|
127
127
|
|
|
128
128
|
```bash
|
|
129
|
-
|
|
129
|
+
npx -y @nomadamas/k-skill@0 exec ktx-booking scripts/ktx_booking.py -- seats 서울 부산 20260328 090000 --train-id <train_id> --car-no 5 --available-only
|
|
130
130
|
```
|
|
131
131
|
|
|
132
132
|
콘센트 꿀팁 자리부터 확인하려면 `--power-only` 를 붙인다. 응답의 `power_outlet` 은 `direct`, `adjacent`, `none` 중 하나다.
|
|
133
133
|
|
|
134
134
|
```bash
|
|
135
|
-
|
|
135
|
+
npx -y @nomadamas/k-skill@0 exec ktx-booking scripts/ktx_booking.py -- seats 서울 부산 20260328 090000 --train-id <train_id> --available-only --power-only
|
|
136
136
|
```
|
|
137
137
|
|
|
138
138
|
`seats` 도 `search` 와 같은 `--train-type` 을 넘겨야 한다. ITX-청춘 등 KTX 외 열차를 조회했다면 상세 좌석 조회에도 같은 값을 사용한다.
|
|
139
139
|
|
|
140
140
|
```bash
|
|
141
|
-
|
|
141
|
+
npx -y @nomadamas/k-skill@0 exec ktx-booking scripts/ktx_booking.py -- seats 남춘천 용산 20260503 150000 \
|
|
142
142
|
--train-id <train_id> \
|
|
143
143
|
--train-type itx-cheongchun \
|
|
144
144
|
--available-only
|
|
@@ -159,13 +159,13 @@ python3 scripts/ktx_booking.py seats 남춘천 용산 20260503 150000 \
|
|
|
159
159
|
조회 결과의 `train_id` 를 고른 뒤에만 예약한다. 이 값은 helper 가 열차 번호/운행일/시각/역 코드를 묶어 만든 stable selector 이므로, 재조회 시 같은 열차가 아직 있으면 그대로 잡고 없으면 실패한다.
|
|
160
160
|
|
|
161
161
|
```bash
|
|
162
|
-
|
|
162
|
+
npx -y @nomadamas/k-skill@0 exec ktx-booking scripts/ktx_booking.py -- reserve 서울 부산 20260328 090000 --train-id <train_id> --seat-option general-first
|
|
163
163
|
```
|
|
164
164
|
|
|
165
165
|
ITX 등 KTX 외 노선을 search 단계에서 골랐다면 reserve 에도 똑같이 `--train-type` 을 넘긴다.
|
|
166
166
|
|
|
167
167
|
```bash
|
|
168
|
-
|
|
168
|
+
npx -y @nomadamas/k-skill@0 exec ktx-booking scripts/ktx_booking.py -- reserve 남춘천 용산 20260503 150000 --train-id <train_id> --train-type itx-cheongchun --seat-option general-first
|
|
169
169
|
```
|
|
170
170
|
|
|
171
171
|
응답에는 예약번호, 운임, 구입기한이 포함된다. 이 시점에 **좌석 확보는 완료되었다**고 안내한다. generic fallback에서는 결제를 handoff하고, 돌쇠에서는 아래 결제 단계로 계속 진행한다.
|
|
@@ -176,19 +176,19 @@ python3 scripts/ktx_booking.py reserve 남춘천 용산 20260503 150000 --train-
|
|
|
176
176
|
N카드 할인을 적용하려면 먼저 보유 N카드 목록을 조회해 카드 번호를 확인한다.
|
|
177
177
|
|
|
178
178
|
```bash
|
|
179
|
-
|
|
179
|
+
npx -y @nomadamas/k-skill@0 exec ktx-booking scripts/ktx_booking.py -- ncard-list
|
|
180
180
|
```
|
|
181
181
|
|
|
182
182
|
N카드로 할인 열차를 조회한다 (`--ncard-index` 는 `ncard-list` 결과의 순번). `ncard-list` 는 로그/셸 노출을 줄이기 위해 카드 번호를 마스킹해 출력한다.
|
|
183
183
|
|
|
184
184
|
```bash
|
|
185
|
-
|
|
185
|
+
npx -y @nomadamas/k-skill@0 exec ktx-booking scripts/ktx_booking.py -- ncard-search 대전 서울 20260512 100000 --ncard-index 1 --train-type ktx
|
|
186
186
|
```
|
|
187
187
|
|
|
188
188
|
응답의 `train_id` 를 복사해 `reserve` 에 같은 `--ncard-index` 를 붙여 예약한다.
|
|
189
189
|
|
|
190
190
|
```bash
|
|
191
|
-
|
|
191
|
+
npx -y @nomadamas/k-skill@0 exec ktx-booking scripts/ktx_booking.py -- reserve 대전 서울 20260512 100000 \
|
|
192
192
|
--train-id <train_id> \
|
|
193
193
|
--ncard-index 1
|
|
194
194
|
```
|
|
@@ -213,13 +213,13 @@ N카드 기능은 `korail2-ncard` 패키지가 필요하다. 없으면 해당
|
|
|
213
213
|
취소는 대상 예약을 다시 조회해 식별한 뒤에만 진행한다.
|
|
214
214
|
|
|
215
215
|
```bash
|
|
216
|
-
|
|
216
|
+
npx -y @nomadamas/k-skill@0 exec ktx-booking scripts/ktx_booking.py -- reservations
|
|
217
217
|
```
|
|
218
218
|
|
|
219
219
|
취소 실행 직전에 `clarify`로 예약번호, 열차, 날짜·시각, 승객, 환불/위약금 정보를 확인하고 승인받는다.
|
|
220
220
|
|
|
221
221
|
```bash
|
|
222
|
-
|
|
222
|
+
npx -y @nomadamas/k-skill@0 exec ktx-booking scripts/ktx_booking.py -- cancel <reservation_id>
|
|
223
223
|
```
|
|
224
224
|
|
|
225
225
|
## Done when
|