korean-stats-mcp 1.4.0

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.
Files changed (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +301 -0
  3. package/dist/api/client.d.ts +65 -0
  4. package/dist/api/client.d.ts.map +1 -0
  5. package/dist/api/client.js +143 -0
  6. package/dist/api/client.js.map +1 -0
  7. package/dist/api/types.d.ts +157 -0
  8. package/dist/api/types.d.ts.map +1 -0
  9. package/dist/api/types.js +5 -0
  10. package/dist/api/types.js.map +1 -0
  11. package/dist/cache/index.d.ts +55 -0
  12. package/dist/cache/index.d.ts.map +1 -0
  13. package/dist/cache/index.js +102 -0
  14. package/dist/cache/index.js.map +1 -0
  15. package/dist/config/index.d.ts +46 -0
  16. package/dist/config/index.d.ts.map +1 -0
  17. package/dist/config/index.js +54 -0
  18. package/dist/config/index.js.map +1 -0
  19. package/dist/data/quickStatsParams.d.ts +76 -0
  20. package/dist/data/quickStatsParams.d.ts.map +1 -0
  21. package/dist/data/quickStatsParams.js +1344 -0
  22. package/dist/data/quickStatsParams.js.map +1 -0
  23. package/dist/index.d.ts +13 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +56 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/prompts/index.d.ts +5 -0
  28. package/dist/prompts/index.d.ts.map +1 -0
  29. package/dist/prompts/index.js +5 -0
  30. package/dist/prompts/index.js.map +1 -0
  31. package/dist/prompts/statisticsAssistant.d.ts +25 -0
  32. package/dist/prompts/statisticsAssistant.d.ts.map +1 -0
  33. package/dist/prompts/statisticsAssistant.js +43 -0
  34. package/dist/prompts/statisticsAssistant.js.map +1 -0
  35. package/dist/resources/categoryTree.d.ts +18 -0
  36. package/dist/resources/categoryTree.d.ts.map +1 -0
  37. package/dist/resources/categoryTree.js +80 -0
  38. package/dist/resources/categoryTree.js.map +1 -0
  39. package/dist/resources/index.d.ts +6 -0
  40. package/dist/resources/index.d.ts.map +1 -0
  41. package/dist/resources/index.js +6 -0
  42. package/dist/resources/index.js.map +1 -0
  43. package/dist/resources/keyIndicators.d.ts +20 -0
  44. package/dist/resources/keyIndicators.d.ts.map +1 -0
  45. package/dist/resources/keyIndicators.js +108 -0
  46. package/dist/resources/keyIndicators.js.map +1 -0
  47. package/dist/server-http.d.ts +10 -0
  48. package/dist/server-http.d.ts.map +1 -0
  49. package/dist/server-http.js +134 -0
  50. package/dist/server-http.js.map +1 -0
  51. package/dist/server.d.ts +10 -0
  52. package/dist/server.d.ts.map +1 -0
  53. package/dist/server.js +194 -0
  54. package/dist/server.js.map +1 -0
  55. package/dist/tools/analyzeTimeSeries.d.ts +70 -0
  56. package/dist/tools/analyzeTimeSeries.d.ts.map +1 -0
  57. package/dist/tools/analyzeTimeSeries.js +204 -0
  58. package/dist/tools/analyzeTimeSeries.js.map +1 -0
  59. package/dist/tools/chains.d.ts +197 -0
  60. package/dist/tools/chains.d.ts.map +1 -0
  61. package/dist/tools/chains.js +369 -0
  62. package/dist/tools/chains.js.map +1 -0
  63. package/dist/tools/compareStatistics.d.ts +62 -0
  64. package/dist/tools/compareStatistics.d.ts.map +1 -0
  65. package/dist/tools/compareStatistics.js +190 -0
  66. package/dist/tools/compareStatistics.js.map +1 -0
  67. package/dist/tools/fetchKosisExcel.d.ts +62 -0
  68. package/dist/tools/fetchKosisExcel.d.ts.map +1 -0
  69. package/dist/tools/fetchKosisExcel.js +366 -0
  70. package/dist/tools/fetchKosisExcel.js.map +1 -0
  71. package/dist/tools/getRecommendedStats.d.ts +41 -0
  72. package/dist/tools/getRecommendedStats.d.ts.map +1 -0
  73. package/dist/tools/getRecommendedStats.js +251 -0
  74. package/dist/tools/getRecommendedStats.js.map +1 -0
  75. package/dist/tools/getStatisticsData.d.ts +75 -0
  76. package/dist/tools/getStatisticsData.d.ts.map +1 -0
  77. package/dist/tools/getStatisticsData.js +305 -0
  78. package/dist/tools/getStatisticsData.js.map +1 -0
  79. package/dist/tools/getStatisticsList.d.ts +69 -0
  80. package/dist/tools/getStatisticsList.d.ts.map +1 -0
  81. package/dist/tools/getStatisticsList.js +336 -0
  82. package/dist/tools/getStatisticsList.js.map +1 -0
  83. package/dist/tools/getTableInfo.d.ts +66 -0
  84. package/dist/tools/getTableInfo.d.ts.map +1 -0
  85. package/dist/tools/getTableInfo.js +85 -0
  86. package/dist/tools/getTableInfo.js.map +1 -0
  87. package/dist/tools/index.d.ts +14 -0
  88. package/dist/tools/index.d.ts.map +1 -0
  89. package/dist/tools/index.js +19 -0
  90. package/dist/tools/index.js.map +1 -0
  91. package/dist/tools/quickStats.d.ts +71 -0
  92. package/dist/tools/quickStats.d.ts.map +1 -0
  93. package/dist/tools/quickStats.js +490 -0
  94. package/dist/tools/quickStats.js.map +1 -0
  95. package/dist/tools/quickTrend.d.ts +61 -0
  96. package/dist/tools/quickTrend.d.ts.map +1 -0
  97. package/dist/tools/quickTrend.js +328 -0
  98. package/dist/tools/quickTrend.js.map +1 -0
  99. package/dist/tools/searchStatistics.d.ts +41 -0
  100. package/dist/tools/searchStatistics.d.ts.map +1 -0
  101. package/dist/tools/searchStatistics.js +318 -0
  102. package/dist/tools/searchStatistics.js.map +1 -0
  103. package/dist/utils/dataFormatter.d.ts +40 -0
  104. package/dist/utils/dataFormatter.d.ts.map +1 -0
  105. package/dist/utils/dataFormatter.js +142 -0
  106. package/dist/utils/dataFormatter.js.map +1 -0
  107. package/dist/utils/errorHandler.d.ts +33 -0
  108. package/dist/utils/errorHandler.d.ts.map +1 -0
  109. package/dist/utils/errorHandler.js +94 -0
  110. package/dist/utils/errorHandler.js.map +1 -0
  111. package/dist/utils/metaLookup.d.ts +93 -0
  112. package/dist/utils/metaLookup.d.ts.map +1 -0
  113. package/dist/utils/metaLookup.js +170 -0
  114. package/dist/utils/metaLookup.js.map +1 -0
  115. package/dist/utils/queryParser.d.ts +45 -0
  116. package/dist/utils/queryParser.d.ts.map +1 -0
  117. package/dist/utils/queryParser.js +244 -0
  118. package/dist/utils/queryParser.js.map +1 -0
  119. package/dist/utils/regions.d.ts +70 -0
  120. package/dist/utils/regions.d.ts.map +1 -0
  121. package/dist/utils/regions.js +261 -0
  122. package/dist/utils/regions.js.map +1 -0
  123. package/package.json +60 -0
@@ -0,0 +1,190 @@
1
+ /**
2
+ * 통계 비교 도구
3
+ * 여러 지역 또는 시점의 통계를 비교
4
+ */
5
+ import { z } from 'zod';
6
+ import { getKosisClient } from '../api/client.js';
7
+ import { getCacheManager } from '../cache/index.js';
8
+ import { calculateChangeRate } from '../utils/dataFormatter.js';
9
+ export const compareStatisticsSchema = {
10
+ name: 'compare_statistics',
11
+ description: '[비교] 동일 통계표 안에서 N개 시점(period) 또는 항목(item) 비교. orgId+tableId 사전 필요. 자연어 다지역 비교(예: "서울/부산/인천 인구")는 chain_compare_regions가 더 직관적. 시점 1개 단순 조회는 quick_stats.',
12
+ inputSchema: z.object({
13
+ orgId: z.string().describe('기관 ID'),
14
+ tableId: z.string().describe('통계표 ID'),
15
+ compareType: z
16
+ .enum(['period', 'item'])
17
+ .describe('비교 유형: period(시점 비교), item(항목 비교)'),
18
+ periodType: z.enum(['Y', 'M', 'Q']).describe('주기: Y(년), M(월), Q(분기)'),
19
+ periods: z
20
+ .array(z.string())
21
+ .optional()
22
+ .describe('비교할 시점들 (예: ["2022", "2023", "2024"])'),
23
+ objL1: z.string().optional().describe('분류1 코드'),
24
+ objL2: z.string().optional().describe('분류2 코드 (일부 테이블에서 필요)'),
25
+ itemId: z.string().optional().describe('항목 ID'),
26
+ }),
27
+ };
28
+ export async function compareStatistics(input) {
29
+ const client = getKosisClient();
30
+ const cache = getCacheManager();
31
+ try {
32
+ // 데이터 조회
33
+ const results = await cache.getStatisticsData({
34
+ orgId: input.orgId,
35
+ tableId: input.tableId,
36
+ compareType: input.compareType,
37
+ periods: input.periods,
38
+ }, async () => {
39
+ if (input.compareType === 'period' && input.periods) {
40
+ // 여러 시점 데이터 조회
41
+ const allResults = [];
42
+ for (const period of input.periods) {
43
+ const data = await client.getStatisticsData({
44
+ orgId: input.orgId,
45
+ tblId: input.tableId,
46
+ objL1: input.objL1 || 'ALL',
47
+ objL2: input.objL2,
48
+ itmId: input.itemId || 'ALL',
49
+ prdSe: input.periodType,
50
+ startPrdDe: period,
51
+ endPrdDe: period,
52
+ });
53
+ allResults.push(...data);
54
+ }
55
+ return allResults;
56
+ }
57
+ else {
58
+ // 단일 시점, 여러 항목 조회
59
+ return client.getStatisticsData({
60
+ orgId: input.orgId,
61
+ tblId: input.tableId,
62
+ objL1: input.objL1 || 'ALL',
63
+ objL2: input.objL2,
64
+ itmId: 'ALL',
65
+ prdSe: input.periodType,
66
+ newEstPrdCnt: 1,
67
+ });
68
+ }
69
+ });
70
+ if (results.length === 0) {
71
+ return {
72
+ success: true,
73
+ compareType: input.compareType,
74
+ items: [],
75
+ summary: '비교할 데이터가 없습니다.',
76
+ insights: [],
77
+ };
78
+ }
79
+ // 비교 항목 생성 (모든 관련 정보 포함)
80
+ const items = results.map((r) => {
81
+ const value = parseFloat(r.DT.replace(/,/g, '')) || 0;
82
+ const region = r.C1_NM || undefined;
83
+ const itemName = r.ITM_NM || undefined;
84
+ const period = r.PRD_DE || undefined;
85
+ const unit = r.UNIT_NM || undefined;
86
+ // name은 비교 타입에 따라 주요 식별자로 설정
87
+ // 하지만 모든 정보를 별도 필드로 제공
88
+ let name;
89
+ if (input.compareType === 'period') {
90
+ name = period || 'N/A';
91
+ }
92
+ else {
93
+ // item 비교 시: 지역명과 항목명 조합
94
+ name = [region, itemName].filter(Boolean).join(' - ') || 'N/A';
95
+ }
96
+ return {
97
+ name,
98
+ region,
99
+ itemName,
100
+ period,
101
+ value,
102
+ formattedValue: r.DT,
103
+ unit,
104
+ };
105
+ });
106
+ // 순위 부여
107
+ const sortedItems = [...items].sort((a, b) => b.value - a.value);
108
+ sortedItems.forEach((item, index) => {
109
+ const original = items.find((i) => i.name === item.name);
110
+ if (original) {
111
+ original.rank = index + 1;
112
+ }
113
+ });
114
+ // 변화율 계산 (시점 비교인 경우)
115
+ if (input.compareType === 'period' && items.length > 1) {
116
+ for (let i = 1; i < items.length; i++) {
117
+ items[i].change = calculateChangeRate(items[i].value, items[i - 1].value);
118
+ }
119
+ }
120
+ // 요약 생성
121
+ const maxItem = sortedItems[0];
122
+ const minItem = sortedItems[sortedItems.length - 1];
123
+ const summary = input.compareType === 'period'
124
+ ? `${items[0].name}부터 ${items[items.length - 1].name}까지의 변화를 비교했습니다.`
125
+ : `총 ${items.length}개 항목 중 "${maxItem.name}"이(가) 가장 높고, "${minItem.name}"이(가) 가장 낮습니다.`;
126
+ // 인사이트 생성
127
+ const insights = [];
128
+ if (input.compareType === 'period') {
129
+ const firstValue = items[0].value;
130
+ const lastValue = items[items.length - 1].value;
131
+ const totalChange = calculateChangeRate(lastValue, firstValue);
132
+ insights.push(`전체 기간 동안 ${totalChange.direction === 'up' ? '증가' : totalChange.direction === 'down' ? '감소' : '변동 없음'} (${totalChange.formatted})`);
133
+ const maxChange = items
134
+ .filter((i) => i.change)
135
+ .sort((a, b) => Math.abs(b.change.rate) - Math.abs(a.change.rate))[0];
136
+ if (maxChange?.change) {
137
+ insights.push(`가장 큰 변화: ${maxChange.name} (${maxChange.change.formatted})`);
138
+ }
139
+ }
140
+ else {
141
+ const diff = ((maxItem.value - minItem.value) / minItem.value * 100).toFixed(1);
142
+ insights.push(`최대-최소 차이: ${diff}%`);
143
+ }
144
+ return {
145
+ success: true,
146
+ compareType: input.compareType,
147
+ items,
148
+ summary,
149
+ insights,
150
+ };
151
+ }
152
+ catch (error) {
153
+ console.error('Compare error:', error);
154
+ const errorMessage = error instanceof Error ? error.message : String(error);
155
+ return {
156
+ success: false,
157
+ compareType: input.compareType,
158
+ items: [],
159
+ summary: '비교 중 오류가 발생했습니다.',
160
+ insights: [
161
+ '## 오류 상세 정보',
162
+ '',
163
+ `### 오류 내용: ${errorMessage}`,
164
+ '',
165
+ '### 사용된 파라미터',
166
+ `- orgId: "${input.orgId}"`,
167
+ `- tableId: "${input.tableId}"`,
168
+ `- compareType: "${input.compareType}"`,
169
+ `- objL1: "${input.objL1 || '미지정'}"`,
170
+ `- itemId: "${input.itemId || '미지정'}"`,
171
+ input.periods ? `- periods: ${JSON.stringify(input.periods)}` : '',
172
+ '',
173
+ '### 해결 방법',
174
+ '1. **get_table_info 먼저 호출**하여 유효한 코드 확인:',
175
+ ' ```json',
176
+ ` { "orgId": "${input.orgId}", "tableId": "${input.tableId}" }`,
177
+ ' ```',
178
+ '',
179
+ '2. **다중 지역 비교 시 형식**:',
180
+ ' - 개별 호출 후 결과 비교 (권장)',
181
+ ' - 예: 서울과 부산을 각각 조회하여 비교',
182
+ '',
183
+ '3. **시점 비교 시 형식**:',
184
+ ' - compareType: "period"',
185
+ ' - periods: ["2020", "2021", "2022", "2023"]',
186
+ ].filter(Boolean),
187
+ };
188
+ }
189
+ }
190
+ //# sourceMappingURL=compareStatistics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compareStatistics.js","sourceRoot":"","sources":["../../src/tools/compareStatistics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * KOSIS 파일 통계표(엑셀) 다운로드 + 파싱 도구
3
+ *
4
+ * OpenAPI 미지원 통계 (자치구 기본통계 등)는 KOSIS 사이트에 엑셀 파일로만 제공된다.
5
+ * 광진구 기본통계 케이스:
6
+ * orgId=505, tblId=DT_505001_FILE2024, file_sn=1~14 (Ⅱ.토지·기후 ~ XV.공공행정·사법)
7
+ *
8
+ * 다운로드 3단계 (KOSIS nsibsHtmlSvc 내부 흐름):
9
+ * 1) GET fileStblView.do?in_org_id=...&in_tbl_id=... → JSESSIONID 쿠키 + 파일 목록 HTML
10
+ * 2) POST fileItmDownload.do (org_id, tbl_id, file_sn) → {dwldFilePath, dwldFileNm} JSON
11
+ * 3) POST dwldServerFile.do (file_path, file_name echo)→ xlsx 바이너리
12
+ *
13
+ * 파싱: kordoc.parse() 가 XLSX → 마크다운 변환 (시트별 표)
14
+ */
15
+ import { z } from 'zod';
16
+ export declare const fetchKosisExcelSchema: {
17
+ name: string;
18
+ description: string;
19
+ inputSchema: z.ZodObject<{
20
+ districtName: z.ZodOptional<z.ZodString>;
21
+ year: z.ZodOptional<z.ZodNumber>;
22
+ orgId: z.ZodOptional<z.ZodString>;
23
+ tblId: z.ZodOptional<z.ZodString>;
24
+ fileSn: z.ZodOptional<z.ZodNumber>;
25
+ listOnly: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
26
+ }, "strip", z.ZodTypeAny, {
27
+ listOnly: boolean;
28
+ orgId?: string | undefined;
29
+ tblId?: string | undefined;
30
+ year?: number | undefined;
31
+ districtName?: string | undefined;
32
+ fileSn?: number | undefined;
33
+ }, {
34
+ orgId?: string | undefined;
35
+ tblId?: string | undefined;
36
+ year?: number | undefined;
37
+ districtName?: string | undefined;
38
+ fileSn?: number | undefined;
39
+ listOnly?: boolean | undefined;
40
+ }>;
41
+ };
42
+ export type FetchKosisExcelInput = z.infer<typeof fetchKosisExcelSchema.inputSchema>;
43
+ interface FileItem {
44
+ file_sn: number;
45
+ file_nm: string;
46
+ }
47
+ interface ExcelFetchResult {
48
+ success: boolean;
49
+ orgId: string;
50
+ tblId: string;
51
+ files?: FileItem[];
52
+ fileSn?: number;
53
+ fileName?: string;
54
+ markdown?: string;
55
+ fileType?: string;
56
+ byteSize?: number;
57
+ warnings?: string[];
58
+ error?: string;
59
+ }
60
+ export declare function fetchKosisExcel(input: FetchKosisExcelInput): Promise<ExcelFetchResult>;
61
+ export {};
62
+ //# sourceMappingURL=fetchKosisExcel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetchKosisExcel.d.ts","sourceRoot":"","sources":["../../src/tools/fetchKosisExcel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgBxB,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;CA+CjC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,WAAW,CAAC,CAAC;AAErF,UAAU,QAAQ;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,gBAAgB;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAkKD,wBAAsB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyJ5F"}
@@ -0,0 +1,366 @@
1
+ /**
2
+ * KOSIS 파일 통계표(엑셀) 다운로드 + 파싱 도구
3
+ *
4
+ * OpenAPI 미지원 통계 (자치구 기본통계 등)는 KOSIS 사이트에 엑셀 파일로만 제공된다.
5
+ * 광진구 기본통계 케이스:
6
+ * orgId=505, tblId=DT_505001_FILE2024, file_sn=1~14 (Ⅱ.토지·기후 ~ XV.공공행정·사법)
7
+ *
8
+ * 다운로드 3단계 (KOSIS nsibsHtmlSvc 내부 흐름):
9
+ * 1) GET fileStblView.do?in_org_id=...&in_tbl_id=... → JSESSIONID 쿠키 + 파일 목록 HTML
10
+ * 2) POST fileItmDownload.do (org_id, tbl_id, file_sn) → {dwldFilePath, dwldFileNm} JSON
11
+ * 3) POST dwldServerFile.do (file_path, file_name echo)→ xlsx 바이너리
12
+ *
13
+ * 파싱: kordoc.parse() 가 XLSX → 마크다운 변환 (시트별 표)
14
+ */
15
+ import { z } from 'zod';
16
+ // kordoc은 optionalDependencies — 원격 배포(Vercel)에서는 함수 사이즈 한도(250MB)
17
+ // 때문에 제외됩니다. 로컬 설치에서만 동적으로 로드합니다.
18
+ async function loadKordoc() {
19
+ try {
20
+ const mod = await import('kordoc');
21
+ return mod.parse;
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ import { resolveDistrictFileTable } from '../utils/regions.js';
28
+ const KOSIS_HOST = 'https://stat.kosis.kr';
29
+ const KOSIS_BASE = `${KOSIS_HOST}/nsibsHtmlSvc/fileView/FileStbl`;
30
+ export const fetchKosisExcelSchema = {
31
+ name: 'fetch_kosis_excel',
32
+ description: `[엑셀파싱] KOSIS 사이트의 파일 통계표(엑셀로만 제공)를 다운로드 + 파싱한 마크다운 반환. quick_stats / quick_trend가 광역시도까지만 지원하므로 자치구별 정밀 데이터는 이 도구로.
33
+
34
+ ⚠️ 자치구 file 통계표 제공은 광역시도마다 차이:
35
+ • **확인된 제공**: 서울 자치구 일부 (광진구 orgId=505, 강남구 orgId=523 등) — DT_{orgId}001_FILE{year} 형식
36
+ • **미제공 확인**: 부산 해운대구(539), 대구 수성구(556) 등 비-서울 다수 자치구 — 404 발생
37
+ • 비-서울 자치구는 file 통계표 대신 \`get_statistics_data\` 로 OpenAPI 경로 사용 권장:
38
+ - Path A: 광역시도 기본통계 (orgId=202/203, regionName="해운대구"/"수성구")
39
+ - Path B: 자치구 단독 OpenAPI (orgId=539, tblId=DT_53902_B001003 등)
40
+ - Path C: 통계청 e-지방지표 (orgId=101, DT_1YL*)
41
+
42
+ 언제 사용:
43
+ • OpenAPI(get_statistics_data) 가 "통계표 없음" 에러를 내고, 해당 자치구가 서울 자치구일 때
44
+ • 「작성중지통계」 카테고리 등 파일 형태로만 제공되는 통계
45
+
46
+ 사용 흐름:
47
+ 1. listOnly=true 로 파일 목록 먼저 조회 → file_sn(1~N) + 파일명(Ⅲ.인구 등) 확인
48
+ 2. file_sn 지정해서 다시 호출 → 해당 파일을 마크다운으로 반환
49
+
50
+ 광진구 기본통계 예시:
51
+ { orgId: "505", tblId: "DT_505001_FILE2024", listOnly: true } // 14개 파일 목록
52
+ { orgId: "505", tblId: "DT_505001_FILE2024", fileSn: 3 } // Ⅲ.인구 파일 파싱
53
+
54
+ 자치구 이름으로 자동 도출 (서울 자치구만 안정적):
55
+ { districtName: "광진구", listOnly: true } // orgId/tblId 자동 도출
56
+ { districtName: "강남구", fileSn: 3, year: 2024 }`,
57
+ inputSchema: z.object({
58
+ districtName: z
59
+ .string()
60
+ .optional()
61
+ .describe('자치구·시·군 이름 (예: "광진구", "강남구"). 주어지면 orgId/tblId 자동 도출. 단독 사용 시 orgId/tblId 생략 가능.'),
62
+ year: z.number().int().optional().describe('기준 연도 (예: 2024). districtName 사용시 적용. 기본은 현재 연도-1.'),
63
+ orgId: z.string().optional().describe('파일 통계표 기관 ID (예: "505" = 서울특별시 광진구). districtName 있으면 무시.'),
64
+ tblId: z.string().optional().describe('파일 통계표 ID (예: "DT_505001_FILE2024"). districtName 있으면 무시.'),
65
+ fileSn: z
66
+ .number()
67
+ .int()
68
+ .min(1)
69
+ .optional()
70
+ .describe('파일 일련번호 (1부터). listOnly=true 면 무시.'),
71
+ listOnly: z
72
+ .boolean()
73
+ .optional()
74
+ .default(false)
75
+ .describe('true면 파일 목록만 조회. 어느 file_sn을 받을지 결정할 때 먼저 호출.'),
76
+ }),
77
+ };
78
+ /**
79
+ * Set-Cookie 헤더에서 "JSESSIONID=...; WMONID=...; ..." 형태로 추출
80
+ * Node 20+ fetch는 getSetCookie() 지원. 폴백으로 set-cookie 헤더 단일 split.
81
+ */
82
+ function extractCookies(res) {
83
+ const sc = res.headers.getSetCookie?.()
84
+ ?? (res.headers.get('set-cookie')?.split(/,(?=\s*[A-Za-z0-9_-]+=)/) ?? []);
85
+ return sc
86
+ .map((c) => c.split(';')[0].trim())
87
+ .filter(Boolean)
88
+ .join('; ');
89
+ }
90
+ /** 1단계: 파일 통계표 페이지 GET → 쿠키 + HTML */
91
+ async function fetchTableView(orgId, tblId) {
92
+ const url = `${KOSIS_BASE}/fileStblView.do?in_org_id=${encodeURIComponent(orgId)}&in_tbl_id=${encodeURIComponent(tblId)}`;
93
+ const res = await fetch(url, {
94
+ headers: {
95
+ 'User-Agent': 'Mozilla/5.0 korea-stats-mcp',
96
+ 'Accept': 'text/html',
97
+ },
98
+ });
99
+ if (!res.ok)
100
+ throw new Error(`fileStblView.do HTTP ${res.status}`);
101
+ return { cookie: extractCookies(res), html: await res.text() };
102
+ }
103
+ /**
104
+ * HTML에서 파일 목록 추출
105
+ * KOSIS는 `fn_fileitm_download('N')` 호출과 그 근처(보통 <li>의 텍스트)에 파일명을 둔다.
106
+ *
107
+ * 예시 구조:
108
+ * <li>
109
+ * <span class="...">Ⅲ.인구</span>
110
+ * <a href="javascript:file_prev_obj.fn_fileitm_download('3');">...</a>
111
+ * </li>
112
+ */
113
+ function parseFileList(html) {
114
+ const out = [];
115
+ const seen = new Set();
116
+ // 패턴 A: <li> 단위로 파일명 + sn 한 번에 묶기
117
+ const liRx = /<li\b[^>]*>([\s\S]*?)<\/li>/g;
118
+ let m;
119
+ while ((m = liRx.exec(html)) !== null) {
120
+ const block = m[1];
121
+ const snMatch = block.match(/fn_fileitm_download\s*\(\s*['"]?(\d+)['"]?\s*\)/);
122
+ if (!snMatch)
123
+ continue;
124
+ const sn = parseInt(snMatch[1], 10);
125
+ if (seen.has(sn))
126
+ continue;
127
+ // 파일명 = li 안의 텍스트에서 태그·script 제거 후 가장 긴 의미있는 줄
128
+ const text = block
129
+ .replace(/<script[\s\S]*?<\/script>/g, '')
130
+ .replace(/<[^>]+>/g, ' ')
131
+ .replace(/&nbsp;/g, ' ')
132
+ .replace(/&lt;/g, '<')
133
+ .replace(/&gt;/g, '>')
134
+ .replace(/&quot;/g, '"')
135
+ .replace(/&#39;/g, "'")
136
+ .replace(/&amp;/g, '&')
137
+ .replace(/\s+/g, ' ')
138
+ .trim();
139
+ if (!text)
140
+ continue;
141
+ seen.add(sn);
142
+ out.push({ file_sn: sn, file_nm: text.length > 80 ? text.slice(0, 80) + '…' : text });
143
+ }
144
+ // 패턴 B: <li>로 못 잡았으면 함수 호출만이라도 추출
145
+ if (out.length === 0) {
146
+ const rx = /fn_fileitm_download\s*\(\s*['"]?(\d+)['"]?\s*\)/g;
147
+ while ((m = rx.exec(html)) !== null) {
148
+ const sn = parseInt(m[1], 10);
149
+ if (seen.has(sn))
150
+ continue;
151
+ seen.add(sn);
152
+ out.push({ file_sn: sn, file_nm: `file_sn=${sn}` });
153
+ }
154
+ }
155
+ out.sort((a, b) => a.file_sn - b.file_sn);
156
+ return out;
157
+ }
158
+ /** 2단계: fileItmDownload.do POST → {dwldFilePath, dwldFileNm} */
159
+ async function fetchDownloadInfo(orgId, tblId, fileSn, cookie) {
160
+ const url = `${KOSIS_BASE}/fileItmDownload.do`;
161
+ const body = new URLSearchParams({
162
+ vw_cd: 'NULL',
163
+ list_id: 'NULL',
164
+ org_id: orgId,
165
+ tbl_id: tblId,
166
+ file_svc: '',
167
+ file_sn: String(fileSn),
168
+ conn_path: '',
169
+ });
170
+ const res = await fetch(url, {
171
+ method: 'POST',
172
+ headers: {
173
+ 'User-Agent': 'Mozilla/5.0 korea-stats-mcp',
174
+ 'Cookie': cookie,
175
+ 'Content-Type': 'application/x-www-form-urlencoded',
176
+ 'Accept': 'application/json',
177
+ 'X-Requested-With': 'XMLHttpRequest',
178
+ },
179
+ body: body.toString(),
180
+ });
181
+ if (!res.ok)
182
+ throw new Error(`fileItmDownload.do HTTP ${res.status}`);
183
+ const json = (await res.json());
184
+ // 응답 형식: { resultMap: { dwldFilePath, dwldFileNm, dwldFileSize }, baseinfo, success }
185
+ // 일부 응답은 최상위에 직접 둘 수 있으므로 양쪽 모두 확인
186
+ const path = json.resultMap?.dwldFilePath ?? json.dwldFilePath;
187
+ const name = json.resultMap?.dwldFileNm ?? json.dwldFileNm;
188
+ const size = json.resultMap?.dwldFileSize ?? json.dwldFileSize;
189
+ if (!path || !name) {
190
+ throw new Error(`fileItmDownload.do 응답 누락: ${JSON.stringify(json).slice(0, 300)}`);
191
+ }
192
+ return { dwldFilePath: path, dwldFileNm: name, dwldFileSize: size };
193
+ }
194
+ /** 3단계: dwldServerFile.do POST → 실제 xlsx 바이너리 */
195
+ async function downloadFile(orgId, tblId, fileSn, info, cookie) {
196
+ const url = `${KOSIS_BASE}/dwldServerFile.do`;
197
+ const body = new URLSearchParams({
198
+ org_id: orgId,
199
+ tbl_id: tblId,
200
+ file_sn: String(fileSn),
201
+ img_yn: '',
202
+ fileSvc: '',
203
+ file_path: info.dwldFilePath,
204
+ file_name: info.dwldFileNm,
205
+ });
206
+ const res = await fetch(url, {
207
+ method: 'POST',
208
+ headers: {
209
+ 'User-Agent': 'Mozilla/5.0 korea-stats-mcp',
210
+ 'Cookie': cookie,
211
+ 'Content-Type': 'application/x-www-form-urlencoded',
212
+ 'Accept': 'application/octet-stream,*/*',
213
+ },
214
+ body: body.toString(),
215
+ });
216
+ if (!res.ok)
217
+ throw new Error(`dwldServerFile.do HTTP ${res.status}`);
218
+ return res.arrayBuffer();
219
+ }
220
+ export async function fetchKosisExcel(input) {
221
+ try {
222
+ // districtName 우선 — 자동 도출
223
+ let orgId = input.orgId;
224
+ let tblId = input.tblId;
225
+ let tblIdCandidates = tblId ? [tblId] : [];
226
+ if (input.districtName) {
227
+ const resolved = await resolveDistrictFileTable(input.districtName, input.year);
228
+ if (!resolved) {
229
+ return {
230
+ success: false,
231
+ orgId: orgId ?? '',
232
+ tblId: tblId ?? '',
233
+ error: `자치구 "${input.districtName}" 의 파일통계표를 도출할 수 없습니다. 광역시도 매핑(DISTRICT_TO_PROVINCE)에 있는지 확인하세요.`,
234
+ };
235
+ }
236
+ orgId = resolved.orgId;
237
+ tblId = resolved.tblId;
238
+ tblIdCandidates = resolved.tblIdCandidates;
239
+ }
240
+ if (!orgId || !tblId) {
241
+ return {
242
+ success: false,
243
+ orgId: orgId ?? '',
244
+ tblId: tblId ?? '',
245
+ error: 'orgId·tblId 또는 districtName 중 하나는 반드시 지정해야 합니다.',
246
+ };
247
+ }
248
+ // 1단계 — 페이지 GET (쿠키 + 파일 목록). year 후보 순회 (currentYear-1 → -2 → -3).
249
+ let cookie;
250
+ let html;
251
+ let pickedTblId = tblId;
252
+ const tried = [];
253
+ for (const candidate of tblIdCandidates) {
254
+ tried.push(candidate);
255
+ try {
256
+ const res = await fetchTableView(orgId, candidate);
257
+ cookie = res.cookie;
258
+ html = res.html;
259
+ pickedTblId = candidate;
260
+ break;
261
+ }
262
+ catch (e) {
263
+ const msg = e instanceof Error ? e.message : String(e);
264
+ if (msg.includes('HTTP 404'))
265
+ continue;
266
+ throw e;
267
+ }
268
+ }
269
+ tblId = pickedTblId;
270
+ if (!cookie || !html) {
271
+ // 모든 후보 404 — 해당 자치구는 KOSIS file 통계표 미제공
272
+ const dn = input.districtName ?? '';
273
+ return {
274
+ success: false,
275
+ orgId,
276
+ tblId,
277
+ error: `KOSIS 파일 통계표 미제공: ${dn ? `자치구 "${dn}"` : `orgId=${orgId}`} 에 해당하는 파일 통계표가 KOSIS 사이트에 없습니다.
278
+
279
+ 시도한 tblId: ${tried.join(', ')}
280
+
281
+ 대안 (OpenAPI 경로) — get_statistics_data 사용:
282
+ • Path A — 광역시도 기본통계: 해당 광역시도(예: 부산=202, 대구=203)의 구·군별 표 (DT_202, DT_B40001 등)에 regionName 지정
283
+ • Path B — 자치구 단독 OpenAPI: orgId=${orgId} tblId 시리즈 (DT_${orgId}xx_<chapter><nnnnnn> 형식)를 search_statistics 로 검색
284
+ • Path C — 통계청 e-지방지표 (orgId=101, DT_1YL*) 에 regionName 지정
285
+
286
+ 자치구별 file 통계표 제공 확정 자치구: 서울 25개 자치구 일부 (광진구 505, 강남구 523 등)`,
287
+ };
288
+ }
289
+ const files = parseFileList(html);
290
+ if (input.listOnly || !input.fileSn) {
291
+ return {
292
+ success: true,
293
+ orgId,
294
+ tblId,
295
+ files,
296
+ warnings: files.length === 0 ? ['파일 목록 파싱 실패 — file_sn=1부터 직접 시도해보세요.'] : undefined,
297
+ };
298
+ }
299
+ const fileMeta = files.find((f) => f.file_sn === input.fileSn);
300
+ // 2단계 — 다운로드 정보
301
+ const info = await fetchDownloadInfo(orgId, tblId, input.fileSn, cookie);
302
+ // 3단계 — 실제 파일
303
+ const ab = await downloadFile(orgId, tblId, input.fileSn, info, cookie);
304
+ // 매직바이트 확인 (xlsx = PK\x03\x04)
305
+ const head = new Uint8Array(ab.slice(0, 4));
306
+ if (!(head[0] === 0x50 && head[1] === 0x4b)) {
307
+ const decoder = new TextDecoder('utf-8', { fatal: false });
308
+ const snippet = decoder.decode(ab.slice(0, 500));
309
+ return {
310
+ success: false,
311
+ orgId,
312
+ tblId,
313
+ fileSn: input.fileSn,
314
+ fileName: fileMeta?.file_nm ?? info.dwldFileNm,
315
+ byteSize: ab.byteLength,
316
+ error: `XLSX가 아닌 응답 (${ab.byteLength}B): ${snippet.slice(0, 300)}`,
317
+ };
318
+ }
319
+ // kordoc 파싱 (XLSX → 마크다운). 원격 배포에선 미설치라 안내 후 종료.
320
+ const parse = await loadKordoc();
321
+ if (!parse) {
322
+ return {
323
+ success: false,
324
+ orgId,
325
+ tblId,
326
+ fileSn: input.fileSn,
327
+ fileName: fileMeta?.file_nm ?? info.dwldFileNm,
328
+ byteSize: ab.byteLength,
329
+ error: 'kordoc 모듈이 설치돼 있지 않습니다. 자치구 .xlsx 파일 파싱은 로컬 설치(pnpm install)에서만 지원됩니다. 원격 MCP에서는 search_statistics / get_statistics_data 사용.',
330
+ };
331
+ }
332
+ const result = await parse(ab);
333
+ if (!result.success) {
334
+ return {
335
+ success: false,
336
+ orgId,
337
+ tblId,
338
+ fileSn: input.fileSn,
339
+ fileName: fileMeta?.file_nm ?? info.dwldFileNm,
340
+ byteSize: ab.byteLength,
341
+ error: `kordoc 파싱 실패: ${result.error ?? '알 수 없음'} (code=${result.code ?? '?'})`,
342
+ };
343
+ }
344
+ return {
345
+ success: true,
346
+ orgId,
347
+ tblId,
348
+ fileSn: input.fileSn,
349
+ fileName: fileMeta?.file_nm ?? info.dwldFileNm,
350
+ fileType: result.fileType,
351
+ byteSize: ab.byteLength,
352
+ markdown: result.markdown,
353
+ warnings: result.warnings?.map((w) => w.message),
354
+ };
355
+ }
356
+ catch (e) {
357
+ return {
358
+ success: false,
359
+ orgId: input.orgId ?? '',
360
+ tblId: input.tblId ?? '',
361
+ fileSn: input.fileSn,
362
+ error: e instanceof Error ? e.message : String(e),
363
+ };
364
+ }
365
+ }
366
+ //# sourceMappingURL=fetchKosisExcel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetchKosisExcel.js","sourceRoot":"","sources":["../../src/tools/fetchKosisExcel.ts"],"names":[],"mappings":"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"}