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,134 @@
1
+ /**
2
+ * Streamable HTTP 서버 - stateless 모드
3
+ *
4
+ * Fly.io 배포용 HTTP 엔드포인트. 매 POST 요청마다 fresh server + transport 생성.
5
+ * GET/DELETE 는 405. /health 만 GET 응답.
6
+ *
7
+ * 참고: korean-law-mcp의 server/http-server.ts 패턴
8
+ */
9
+ import express from 'express';
10
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
11
+ import { createServer } from './server.js';
12
+ const PORT = parseInt(process.env.PORT || '3000', 10);
13
+ const BODY_LIMIT = process.env.MCP_BODY_LIMIT || '200kb';
14
+ const RATE_LIMIT_RPM = parseInt(process.env.RATE_LIMIT_RPM || '60', 10);
15
+ const TRUST_PROXY_RAW = process.env.TRUST_PROXY ?? '1';
16
+ function parseTrustProxy(v) {
17
+ if (v === 'true' || v === 'all')
18
+ return true;
19
+ if (v === 'false')
20
+ return false;
21
+ if (/^\d+$/.test(v))
22
+ return parseInt(v, 10);
23
+ return v;
24
+ }
25
+ function scrub(s) {
26
+ // KOSIS API 키가 URL/에러에 노출되지 않도록 마스킹
27
+ return s.replace(/(apiKey|kosis_api_key)=[^&\s]+/gi, '$1=***');
28
+ }
29
+ async function main() {
30
+ const app = express();
31
+ app.set('trust proxy', parseTrustProxy(TRUST_PROXY_RAW));
32
+ app.use(express.json({ limit: BODY_LIMIT }));
33
+ // Rate limit: per-IP per minute
34
+ if (RATE_LIMIT_RPM > 0) {
35
+ const buckets = new Map();
36
+ app.use((req, res, next) => {
37
+ if (req.path === '/health' || req.path === '/')
38
+ return next();
39
+ const ip = req.ip || req.socket.remoteAddress || 'unknown';
40
+ const now = Date.now();
41
+ let b = buckets.get(ip);
42
+ if (!b || now >= b.resetAt) {
43
+ b = { count: 0, resetAt: now + 60_000 };
44
+ buckets.set(ip, b);
45
+ }
46
+ b.count++;
47
+ if (b.count > RATE_LIMIT_RPM) {
48
+ return res.status(429).json({
49
+ jsonrpc: '2.0',
50
+ error: { code: -32000, message: `Rate limit exceeded (${RATE_LIMIT_RPM} rpm)` },
51
+ id: null,
52
+ });
53
+ }
54
+ next();
55
+ });
56
+ }
57
+ // CORS
58
+ app.use((req, res, next) => {
59
+ res.setHeader('Access-Control-Allow-Origin', '*');
60
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
61
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, mcp-session-id');
62
+ res.setHeader('Cache-Control', 'no-store');
63
+ if (req.method === 'OPTIONS')
64
+ return res.status(200).end();
65
+ next();
66
+ });
67
+ // Health check
68
+ app.get('/health', (_req, res) => {
69
+ res.json({
70
+ status: 'ok',
71
+ name: 'korean-stats-mcp',
72
+ version: '1.3.0',
73
+ tools: 13,
74
+ });
75
+ });
76
+ app.get('/', (_req, res) => {
77
+ res.json({
78
+ name: 'korean-stats-mcp',
79
+ version: '1.3.0',
80
+ description: 'KOSIS 91 키워드 + 17 시도 + 자치구 230+ 라우팅 + 3개 체인 MCP',
81
+ endpoint: '/mcp (POST only)',
82
+ tools: 13,
83
+ docs: 'https://github.com/chrisryugj/korean-stats-mcp',
84
+ });
85
+ });
86
+ // MCP endpoint (POST only — stateless)
87
+ app.post('/mcp', async (req, res) => {
88
+ try {
89
+ const server = createServer();
90
+ const transport = new StreamableHTTPServerTransport({
91
+ sessionIdGenerator: undefined,
92
+ enableJsonResponse: true,
93
+ });
94
+ res.on('close', () => transport.close());
95
+ await server.connect(transport);
96
+ await transport.handleRequest(req, res, req.body);
97
+ }
98
+ catch (error) {
99
+ if (res.headersSent)
100
+ return;
101
+ const msg = scrub(error instanceof Error ? error.message : String(error));
102
+ res.status(500).json({
103
+ jsonrpc: '2.0',
104
+ error: { code: -32603, message: msg },
105
+ id: null,
106
+ });
107
+ }
108
+ });
109
+ // GET/DELETE on /mcp → 405
110
+ app.get('/mcp', (_req, res) => {
111
+ res.setHeader('Allow', 'POST');
112
+ res.status(405).json({
113
+ jsonrpc: '2.0',
114
+ error: { code: -32600, message: 'Stateless MCP — use POST.' },
115
+ id: null,
116
+ });
117
+ });
118
+ app.delete('/mcp', (_req, res) => {
119
+ res.setHeader('Allow', 'POST');
120
+ res.status(405).json({
121
+ jsonrpc: '2.0',
122
+ error: { code: -32600, message: 'Stateless MCP — sessions not supported.' },
123
+ id: null,
124
+ });
125
+ });
126
+ app.listen(PORT, () => {
127
+ console.log(`[korean-stats-mcp] HTTP server listening on :${PORT}`);
128
+ });
129
+ }
130
+ main().catch((err) => {
131
+ console.error('Fatal:', scrub(err instanceof Error ? err.message : String(err)));
132
+ process.exit(1);
133
+ });
134
+ //# sourceMappingURL=server-http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-http.js","sourceRoot":"","sources":["../src/server-http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Korea Stats MCP 서버
3
+ * 통계청 KOSIS OpenAPI 기반 MCP 서버
4
+ */
5
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
6
+ /**
7
+ * MCP 서버 생성 및 설정
8
+ */
9
+ export declare function createServer(): McpServer;
10
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA2CpE;;GAEG;AACH,wBAAgB,YAAY,IAAI,SAAS,CAgRxC"}
package/dist/server.js ADDED
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Korea Stats MCP 서버
3
+ * 통계청 KOSIS OpenAPI 기반 MCP 서버
4
+ */
5
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
6
+ // 도구 가져오기
7
+ import { searchStatistics, searchStatisticsSchema, getStatisticsList, getStatisticsListSchema, getStatisticsData, getStatisticsDataSchema, compareStatistics, compareStatisticsSchema, analyzeTimeSeries, analyzeTimeSeriesSchema, getTableInfo, getTableInfoSchema, quickStats, quickStatsSchema, quickTrend, quickTrendSchema, fetchKosisExcel, fetchKosisExcelSchema, chainRegionBrief, chainRegionBriefSchema, chainCompareRegions, chainCompareRegionsSchema, chainPolicyIndicator, chainPolicyIndicatorSchema, } from './tools/index.js';
8
+ // 리소스 가져오기
9
+ import { getCategoryTreeJson, getKeyIndicatorsJson } from './resources/index.js';
10
+ // 프롬프트 가져오기
11
+ import { statisticsAssistantPromptSchema, generateStatisticsAssistantPrompt, } from './prompts/index.js';
12
+ // 설정 가져오기
13
+ import { validateConfig } from './config/index.js';
14
+ /**
15
+ * MCP 서버 생성 및 설정
16
+ */
17
+ export function createServer() {
18
+ // 설정 유효성 검사
19
+ validateConfig();
20
+ const server = new McpServer({
21
+ name: 'korean-stats-mcp',
22
+ version: '1.4.0',
23
+ description: '한국 통계청 KOSIS OpenAPI 기반 MCP 서버 - 91개 키워드, 17 시도 + 자치구 230+ 라우팅, 시계열 추세, 3개 체인 도구(지역 브리핑·다지역 비교·정책 영역) — 공무원 업무 종합 통계 도우미. v1.4.0: 자치구 region 파라미터 fix, chain_compare_regions max 17 (전국 비교), chain_region_brief speech 옵션, 장래추계 데이터 isProjection 안내, list ← 추천 흡수.',
24
+ });
25
+ // ===== 도구 등록 =====
26
+ // 1. 통계 검색
27
+ server.tool(searchStatisticsSchema.name, searchStatisticsSchema.description, searchStatisticsSchema.inputSchema.shape, async (args) => {
28
+ const result = await searchStatistics(args);
29
+ return {
30
+ content: [
31
+ {
32
+ type: 'text',
33
+ text: JSON.stringify(result, null, 2),
34
+ },
35
+ ],
36
+ };
37
+ });
38
+ // 2. 통계 목록 조회
39
+ server.tool(getStatisticsListSchema.name, getStatisticsListSchema.description, getStatisticsListSchema.inputSchema.shape, async (args) => {
40
+ const result = await getStatisticsList(args);
41
+ return {
42
+ content: [
43
+ {
44
+ type: 'text',
45
+ text: JSON.stringify(result, null, 2),
46
+ },
47
+ ],
48
+ };
49
+ });
50
+ // 3. 통계 데이터 조회
51
+ server.tool(getStatisticsDataSchema.name, getStatisticsDataSchema.description, getStatisticsDataSchema.inputSchema.shape, async (args) => {
52
+ const result = await getStatisticsData(args);
53
+ return {
54
+ content: [
55
+ {
56
+ type: 'text',
57
+ text: JSON.stringify(result, null, 2),
58
+ },
59
+ ],
60
+ };
61
+ });
62
+ // 4. 통계 비교
63
+ server.tool(compareStatisticsSchema.name, compareStatisticsSchema.description, compareStatisticsSchema.inputSchema.shape, async (args) => {
64
+ const result = await compareStatistics(args);
65
+ return {
66
+ content: [
67
+ {
68
+ type: 'text',
69
+ text: JSON.stringify(result, null, 2),
70
+ },
71
+ ],
72
+ };
73
+ });
74
+ // 5. 시계열 분석
75
+ server.tool(analyzeTimeSeriesSchema.name, analyzeTimeSeriesSchema.description, analyzeTimeSeriesSchema.inputSchema.shape, async (args) => {
76
+ const result = await analyzeTimeSeries(args);
77
+ return {
78
+ content: [
79
+ {
80
+ type: 'text',
81
+ text: JSON.stringify(result, null, 2),
82
+ },
83
+ ],
84
+ };
85
+ });
86
+ // 6. 통계표 정보 조회 (경량화 — filter + sampleSize)
87
+ server.tool(getTableInfoSchema.name, getTableInfoSchema.description, getTableInfoSchema.inputSchema.shape, async (args) => {
88
+ const result = await getTableInfo(args);
89
+ return {
90
+ content: [
91
+ {
92
+ type: 'text',
93
+ text: JSON.stringify(result, null, 2),
94
+ },
95
+ ],
96
+ };
97
+ });
98
+ // 8. 빠른 통계 조회 (원스텝)
99
+ server.tool(quickStatsSchema.name, quickStatsSchema.description, quickStatsSchema.inputSchema.shape, async (args) => {
100
+ const result = await quickStats(args);
101
+ return {
102
+ content: [
103
+ {
104
+ type: 'text',
105
+ text: JSON.stringify(result, null, 2),
106
+ },
107
+ ],
108
+ };
109
+ });
110
+ // 9. 빠른 추세 분석 (시계열)
111
+ server.tool(quickTrendSchema.name, quickTrendSchema.description, quickTrendSchema.inputSchema.shape, async (args) => {
112
+ const result = await quickTrend(args);
113
+ return {
114
+ content: [
115
+ {
116
+ type: 'text',
117
+ text: JSON.stringify(result, null, 2),
118
+ },
119
+ ],
120
+ };
121
+ });
122
+ // 10. KOSIS 엑셀 파일 통계표 파싱 (OpenAPI 미지원 통계 — 자치구 기본통계 등)
123
+ server.tool(fetchKosisExcelSchema.name, fetchKosisExcelSchema.description, fetchKosisExcelSchema.inputSchema.shape, async (args) => {
124
+ const result = await fetchKosisExcel(args);
125
+ return {
126
+ content: [
127
+ {
128
+ type: 'text',
129
+ text: JSON.stringify(result, null, 2),
130
+ },
131
+ ],
132
+ };
133
+ });
134
+ // 11. 체인 — 지역 한장 종합 브리핑 (공무원 킬링 기능)
135
+ server.tool(chainRegionBriefSchema.name, chainRegionBriefSchema.description, chainRegionBriefSchema.inputSchema.shape, async (args) => {
136
+ const result = await chainRegionBrief(args);
137
+ return {
138
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
139
+ };
140
+ });
141
+ // 12. 체인 — N지역 × M지표 비교 매트릭스
142
+ server.tool(chainCompareRegionsSchema.name, chainCompareRegionsSchema.description, chainCompareRegionsSchema.inputSchema.shape, async (args) => {
143
+ const result = await chainCompareRegions(args);
144
+ return {
145
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
146
+ };
147
+ });
148
+ // 13. 체인 — 정책 영역 묶음 시계열
149
+ server.tool(chainPolicyIndicatorSchema.name, chainPolicyIndicatorSchema.description, chainPolicyIndicatorSchema.inputSchema.shape, async (args) => {
150
+ const result = await chainPolicyIndicator(args);
151
+ return {
152
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
153
+ };
154
+ });
155
+ // ===== 리소스 등록 =====
156
+ // 1. 통계 분류 체계
157
+ server.resource('category-tree', 'kosis://categories/tree', {
158
+ description: 'KOSIS 통계 분류 체계 - 주제별/기관별 분류 구조',
159
+ mimeType: 'application/json',
160
+ }, async () => ({
161
+ contents: [
162
+ {
163
+ uri: 'kosis://categories/tree',
164
+ text: getCategoryTreeJson(),
165
+ mimeType: 'application/json',
166
+ },
167
+ ],
168
+ }));
169
+ // 2. 주요 지표 목록
170
+ server.resource('key-indicators', 'kosis://indicators/list', {
171
+ description: '자주 조회되는 주요 경제사회 지표 목록',
172
+ mimeType: 'application/json',
173
+ }, async () => ({
174
+ contents: [
175
+ {
176
+ uri: 'kosis://indicators/list',
177
+ text: getKeyIndicatorsJson(),
178
+ mimeType: 'application/json',
179
+ },
180
+ ],
181
+ }));
182
+ // ===== 프롬프트 등록 =====
183
+ server.prompt(statisticsAssistantPromptSchema.name, statisticsAssistantPromptSchema.description, statisticsAssistantPromptSchema.argsSchema.shape, async (args) => {
184
+ const result = generateStatisticsAssistantPrompt(args.question);
185
+ return {
186
+ messages: result.messages.map((m) => ({
187
+ role: m.role,
188
+ content: m.content,
189
+ })),
190
+ };
191
+ });
192
+ return server;
193
+ }
194
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * 시계열 분석 도구
3
+ * 통계 데이터의 시계열 추세를 분석
4
+ */
5
+ import { z } from 'zod';
6
+ export declare const analyzeTimeSeriesSchema: {
7
+ name: string;
8
+ description: string;
9
+ inputSchema: z.ZodObject<{
10
+ orgId: z.ZodString;
11
+ tableId: z.ZodString;
12
+ objL1: z.ZodString;
13
+ objL2: z.ZodOptional<z.ZodString>;
14
+ itemId: z.ZodString;
15
+ periodType: z.ZodEnum<["Y", "M", "Q"]>;
16
+ yearCount: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
17
+ }, "strip", z.ZodTypeAny, {
18
+ orgId: string;
19
+ objL1: string;
20
+ tableId: string;
21
+ itemId: string;
22
+ periodType: "Y" | "Q" | "M";
23
+ yearCount: number;
24
+ objL2?: string | undefined;
25
+ }, {
26
+ orgId: string;
27
+ objL1: string;
28
+ tableId: string;
29
+ itemId: string;
30
+ periodType: "Y" | "Q" | "M";
31
+ objL2?: string | undefined;
32
+ yearCount?: number | undefined;
33
+ }>;
34
+ };
35
+ export type AnalyzeTimeSeriesInput = z.infer<typeof analyzeTimeSeriesSchema.inputSchema>;
36
+ interface TimeSeriesAnalysis {
37
+ trend: 'increasing' | 'decreasing' | 'stable' | 'fluctuating';
38
+ trendDescription: string;
39
+ averageGrowthRate: number;
40
+ volatility: number;
41
+ maxValue: {
42
+ period: string;
43
+ value: number;
44
+ formatted: string;
45
+ };
46
+ minValue: {
47
+ period: string;
48
+ value: number;
49
+ formatted: string;
50
+ };
51
+ recentChange: {
52
+ rate: number;
53
+ direction: 'up' | 'down' | 'stable';
54
+ formatted: string;
55
+ };
56
+ forecast?: string;
57
+ }
58
+ export declare function analyzeTimeSeries(input: AnalyzeTimeSeriesInput): Promise<{
59
+ success: boolean;
60
+ tableName?: string;
61
+ unit?: string;
62
+ analysis?: TimeSeriesAnalysis;
63
+ dataPoints: Array<{
64
+ period: string;
65
+ value: number;
66
+ }>;
67
+ interpretation: string[];
68
+ }>;
69
+ export {};
70
+ //# sourceMappingURL=analyzeTimeSeries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzeTimeSeries.d.ts","sourceRoot":"","sources":["../../src/tools/analyzeTimeSeries.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BnC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,WAAW,CAAC,CAAC;AAEzF,UAAU,kBAAkB;IAC1B,KAAK,EAAE,YAAY,GAAG,YAAY,GAAG,QAAQ,GAAG,aAAa,CAAC;IAC9D,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/D,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/D,YAAY,EAAE;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,QAAQ,CAAC;QACpC,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,UAAU,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B,CAAC,CAkLD"}
@@ -0,0 +1,204 @@
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 { analyzeTrend, formatPeriod } from '../utils/dataFormatter.js';
9
+ export const analyzeTimeSeriesSchema = {
10
+ name: 'analyze_time_series',
11
+ description: '[시계열정밀] orgId+tableId+objL1+itemId 명시한 정밀 시계열 분석. 증감/추세/성장률/변동성. 키워드만 알면 quick_trend가 즉시 응답 (자치구 fallback도 자동). 차원·항목 코드는 먼저 get_table_info로 확인.',
12
+ inputSchema: z.object({
13
+ orgId: z.string().describe('기관 ID'),
14
+ tableId: z.string().describe('통계표 ID'),
15
+ objL1: z
16
+ .string()
17
+ .describe('분류1 코드 (필수) - get_table_info로 유효한 값 조회 필요. 예: "00"(전국), "0"(계)'),
18
+ objL2: z
19
+ .string()
20
+ .optional()
21
+ .describe('분류2 코드 (선택) - 일부 테이블에서 필요. 예: 실업률 테이블의 연령계층별 "00"(계)'),
22
+ itemId: z
23
+ .string()
24
+ .describe('항목 ID (필수) - get_table_info로 유효한 값 조회 필요. 예: "T10"(출생건수)'),
25
+ periodType: z
26
+ .enum(['Y', 'M', 'Q'])
27
+ .describe('주기: Y(년), M(월), Q(분기)'),
28
+ yearCount: z
29
+ .number()
30
+ .min(2)
31
+ .max(30)
32
+ .optional()
33
+ .default(10)
34
+ .describe('분석할 기간 수 (기본: 10)'),
35
+ }),
36
+ };
37
+ export async function analyzeTimeSeries(input) {
38
+ const client = getKosisClient();
39
+ const cache = getCacheManager();
40
+ try {
41
+ // 데이터 조회
42
+ const results = await cache.getStatisticsData({
43
+ orgId: input.orgId,
44
+ tableId: input.tableId,
45
+ objL1: input.objL1,
46
+ objL2: input.objL2,
47
+ itemId: input.itemId,
48
+ periodType: input.periodType,
49
+ yearCount: input.yearCount,
50
+ }, async () => {
51
+ return client.getStatisticsData({
52
+ orgId: input.orgId,
53
+ tblId: input.tableId,
54
+ objL1: input.objL1,
55
+ objL2: input.objL2,
56
+ itmId: input.itemId,
57
+ prdSe: input.periodType,
58
+ newEstPrdCnt: input.yearCount,
59
+ });
60
+ });
61
+ if (results.length < 2) {
62
+ return {
63
+ success: true,
64
+ dataPoints: [],
65
+ interpretation: [
66
+ '분석에 필요한 충분한 데이터가 없습니다.',
67
+ `사용된 파라미터: objL1="${input.objL1}", itemId="${input.itemId}"`,
68
+ '',
69
+ '💡 해결 방법:',
70
+ '1. get_table_info로 유효한 분류/항목 코드를 확인하세요.',
71
+ '2. 예시: objL1="00"(전국) 또는 "11"(서울), itemId="T10"(출생건수)',
72
+ ],
73
+ };
74
+ }
75
+ // 데이터 정리 (시간순 정렬)
76
+ const sortedData = results
77
+ .map((r) => ({
78
+ period: r.PRD_DE,
79
+ value: parseFloat(r.DT.replace(/,/g, '')) || 0,
80
+ formatted: r.DT,
81
+ }))
82
+ .sort((a, b) => a.period.localeCompare(b.period));
83
+ const values = sortedData.map((d) => d.value);
84
+ const { trend, avgGrowthRate, volatility } = analyzeTrend(values);
85
+ // 최대/최소값 찾기
86
+ const maxIdx = values.indexOf(Math.max(...values));
87
+ const minIdx = values.indexOf(Math.min(...values));
88
+ // 최근 변화율
89
+ const lastValue = values[values.length - 1];
90
+ const prevValue = values[values.length - 2];
91
+ const recentChangeRate = prevValue !== 0 ? ((lastValue - prevValue) / Math.abs(prevValue)) * 100 : 0;
92
+ const analysis = {
93
+ trend,
94
+ trendDescription: getTrendDescription(trend),
95
+ averageGrowthRate: Math.round(avgGrowthRate * 100) / 100,
96
+ volatility: Math.round(volatility * 100) / 100,
97
+ maxValue: {
98
+ period: formatPeriod(sortedData[maxIdx].period, input.periodType),
99
+ value: sortedData[maxIdx].value,
100
+ formatted: sortedData[maxIdx].formatted,
101
+ },
102
+ minValue: {
103
+ period: formatPeriod(sortedData[minIdx].period, input.periodType),
104
+ value: sortedData[minIdx].value,
105
+ formatted: sortedData[minIdx].formatted,
106
+ },
107
+ recentChange: {
108
+ rate: Math.round(recentChangeRate * 100) / 100,
109
+ direction: recentChangeRate > 0.1 ? 'up' : recentChangeRate < -0.1 ? 'down' : 'stable',
110
+ formatted: `${recentChangeRate > 0 ? '+' : ''}${recentChangeRate.toFixed(1)}%`,
111
+ },
112
+ };
113
+ // 예측 (단순 선형 추세)
114
+ if (trend === 'increasing') {
115
+ analysis.forecast = `현재 추세가 지속된다면 향후 지속적인 증가가 예상됩니다.`;
116
+ }
117
+ else if (trend === 'decreasing') {
118
+ analysis.forecast = `현재 추세가 지속된다면 향후 지속적인 감소가 예상됩니다.`;
119
+ }
120
+ // 해석 생성
121
+ const interpretation = [];
122
+ interpretation.push(`📊 **추세**: ${analysis.trendDescription}`);
123
+ interpretation.push(`📈 **평균 성장률**: ${analysis.averageGrowthRate > 0 ? '+' : ''}${analysis.averageGrowthRate}%`);
124
+ interpretation.push(`🔝 **최고점**: ${analysis.maxValue.period} (${analysis.maxValue.formatted})`);
125
+ interpretation.push(`🔻 **최저점**: ${analysis.minValue.period} (${analysis.minValue.formatted})`);
126
+ interpretation.push(`📅 **최근 변화**: ${analysis.recentChange.formatted}`);
127
+ if (volatility > 20) {
128
+ interpretation.push(`⚠️ **주의**: 변동성이 높습니다 (${volatility.toFixed(1)}%). 데이터 해석에 주의가 필요합니다.`);
129
+ }
130
+ if (analysis.forecast) {
131
+ interpretation.push(`🔮 **전망**: ${analysis.forecast}`);
132
+ }
133
+ return {
134
+ success: true,
135
+ tableName: results[0].TBL_NM,
136
+ unit: results[0].UNIT_NM,
137
+ analysis,
138
+ dataPoints: sortedData.map((d) => ({
139
+ period: formatPeriod(d.period, input.periodType),
140
+ value: d.value,
141
+ })),
142
+ interpretation,
143
+ };
144
+ }
145
+ catch (error) {
146
+ console.error('Analysis error:', error);
147
+ const errorMessage = error instanceof Error ? error.message : String(error);
148
+ return {
149
+ success: false,
150
+ dataPoints: [],
151
+ interpretation: [
152
+ '## 시계열 분석 중 오류 발생',
153
+ '',
154
+ '### 사용된 파라미터',
155
+ `- orgId: "${input.orgId}"`,
156
+ `- tableId: "${input.tableId}"`,
157
+ `- objL1: "${input.objL1}"`,
158
+ `- itemId: "${input.itemId}"`,
159
+ `- periodType: "${input.periodType}"`,
160
+ '',
161
+ '### 오류 내용',
162
+ errorMessage,
163
+ '',
164
+ '### 해결 방법',
165
+ '1. **get_table_info 먼저 호출**하여 유효한 코드 확인:',
166
+ ' ```json',
167
+ ` { "orgId": "${input.orgId}", "tableId": "${input.tableId}" }`,
168
+ ' ```',
169
+ '',
170
+ '2. **파라미터 확인**:',
171
+ ' - objL1: 지역 코드 (예: "00"=전국, "11"=서울)',
172
+ ' - itemId: 항목 코드 (예: "T10", "T1")',
173
+ ' - ⚠️ OBJ_ID(예: "ITEM", "A")가 아닌 실제 분류값 코드 사용',
174
+ '',
175
+ '### 올바른 호출 예시',
176
+ '```json',
177
+ '{',
178
+ ` "orgId": "${input.orgId}",`,
179
+ ` "tableId": "${input.tableId}",`,
180
+ ' "objL1": "00",',
181
+ ' "itemId": "T1",',
182
+ ' "periodType": "Y",',
183
+ ' "yearCount": 10',
184
+ '}',
185
+ '```',
186
+ ],
187
+ };
188
+ }
189
+ }
190
+ function getTrendDescription(trend) {
191
+ switch (trend) {
192
+ case 'increasing':
193
+ return '지속적인 상승 추세를 보이고 있습니다.';
194
+ case 'decreasing':
195
+ return '지속적인 하락 추세를 보이고 있습니다.';
196
+ case 'stable':
197
+ return '안정적인 흐름을 유지하고 있습니다.';
198
+ case 'fluctuating':
199
+ return '변동이 큰 불안정한 흐름을 보이고 있습니다.';
200
+ default:
201
+ return '추세를 파악하기 어렵습니다.';
202
+ }
203
+ }
204
+ //# sourceMappingURL=analyzeTimeSeries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzeTimeSeries.js","sourceRoot":"","sources":["../../src/tools/analyzeTimeSeries.ts"],"names":[],"mappings":"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"}