daiso 1.0.9 → 1.0.10
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/README.md +17 -8
- package/dist/api/healthCheckDefinitions.js +18 -0
- package/dist/api/healthChecks.js +11 -2
- package/dist/cli/commands/convenience.js +2 -2
- package/dist/services/cgv/client.js +7 -4
- package/dist/services/emart24/tools/searchProducts.js +37 -19
- package/dist/services/places/client.js +29 -8
- package/dist/services/seveneleven/tools/searchProducts.js +36 -17
- package/package.json +19 -16
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
# Daiso MCP 및 Skill
|
|
9
9
|
|
|
10
|
-
다이소(제품/매장/재고),
|
|
10
|
+
다이소(제품/매장/재고), 상품 가격 비교, 주변 음식점/카페, 주유소/유가, 개발자 요청 제출, 롯데마트(매장/상품), GS25(매장/상품/재고), 세븐일레븐(상품/매장/재고/인기검색어/카탈로그), CU(매장/재고), 이마트24(매장/상품/재고), 올리브영(매장/재고), 메가박스(지점/영화/시간표/좌석), 롯데시네마(지점/영화/좌석), CGV(극장/영화/시간표) 조회 기능을 MCP, CLI, Codex Skill로 AI에 연결합니다.
|
|
11
11
|
|
|
12
12
|
<br>
|
|
13
13
|
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
**ClawHub:** [clawhub.ai/hmmhmmhm/daiso-cli](https://clawhub.ai/hmmhmmhm/daiso-cli)
|
|
17
17
|
|
|
18
|
-
한국 로컬
|
|
18
|
+
한국 로컬 리테일, 생활 정보, 영화관 조회를 MCP, CLI, Codex Skill로 연결하는 도구입니다.
|
|
19
|
+
사용자는 별도 API 키를 준비하지 않고 바로 사용할 수 있습니다.
|
|
19
20
|
|
|
20
21
|
<br>
|
|
21
22
|
|
|
@@ -35,10 +36,15 @@
|
|
|
35
36
|
<td>네이버 지역 검색</td>
|
|
36
37
|
<td>음식점, 카페, 디저트, 주변 장소</td>
|
|
37
38
|
</tr>
|
|
39
|
+
<tr>
|
|
40
|
+
<td>교통</td>
|
|
41
|
+
<td>오피넷</td>
|
|
42
|
+
<td>전국 평균 유가, 최저가 주유소, 위치 기반 주유소, 주유소 상세정보</td>
|
|
43
|
+
</tr>
|
|
38
44
|
<tr>
|
|
39
45
|
<td>비교</td>
|
|
40
46
|
<td>다이소, GS25, 세븐일레븐, 이마트24</td>
|
|
41
|
-
<td
|
|
47
|
+
<td>같은 상품의 판매처별 가격 후보 비교</td>
|
|
42
48
|
</tr>
|
|
43
49
|
<tr>
|
|
44
50
|
<td>운영</td>
|
|
@@ -75,11 +81,11 @@
|
|
|
75
81
|
**[실시간 서비스 상태 보기](https://aka-page.betteruptime.com/)**
|
|
76
82
|
|
|
77
83
|
<!-- WORKERS_INVOCATIONS_CHART:START -->
|
|
78
|
-
<h3>Cloudflare Workers 호출량 (2026-
|
|
84
|
+
<h3>Cloudflare Workers 호출량 (2026-06-14 ~ 2026-07-13, 30일)</h3>
|
|
79
85
|
|
|
80
|
-
<img src="./assets/analytics/workers-invocations.png?v=2026-
|
|
86
|
+
<img src="./assets/analytics/workers-invocations.png?v=2026-07-13T16:10:36.883Z" alt="Cloudflare Workers 호출량 그래프 (2026-06-14 ~ 2026-07-13)" width="100%">
|
|
81
87
|
|
|
82
|
-
<sub>기준 워커: <code>daiso-mcp</code> · 마지막 갱신: 2026-
|
|
88
|
+
<sub>기준 워커: <code>daiso-mcp</code> · 마지막 갱신: 2026-07-14 01:10 KST</sub>
|
|
83
89
|
|
|
84
90
|
<!-- WORKERS_INVOCATIONS_CHART:END -->
|
|
85
91
|
|
|
@@ -127,6 +133,8 @@ GS25 mcp로 강남 근처 매장과 오감자 재고 알려줘
|
|
|
127
133
|
세븐일레븐 mcp로 인기 검색어와 카탈로그 요약 알려줘
|
|
128
134
|
강남역 근처 카페 찾아줘
|
|
129
135
|
성수동 근처 브런치 음식점 찾아줘
|
|
136
|
+
강남역 근처 제일 싼 주유소 찾아줘
|
|
137
|
+
오늘 전국 평균 휘발유 가격 알려줘
|
|
130
138
|
메가박스 mcp로 강남점 영화와 잔여 좌석 알려줘
|
|
131
139
|
롯데시네마 mcp로 월드타워 근처 지점과 상영 영화 알려줘
|
|
132
140
|
롯데시네마 mcp로 월드타워 잔여 좌석 알려줘
|
|
@@ -137,6 +145,7 @@ CGV mcp로 강남 상영 영화와 시간표 알려줘
|
|
|
137
145
|
|
|
138
146
|
- 가격 비교: `콜라 어디가 싸?` → `compare_products`
|
|
139
147
|
- 주변 장소: `강남역 근처 카페 찾아줘` → `places_search_nearby`
|
|
148
|
+
- 주유소/유가: `강남역 근처 제일 싼 주유소 찾아줘` → `opinet_search_stations_around`
|
|
140
149
|
- 브랜드 명시 재고: `다이소 핫식스 재고 찾아줘` → 먼저 다이소에서 검색 후 결과 없을 때만 대안 제안
|
|
141
150
|
- 편의점 재고: `GS25 강남 오감자 재고` → 상품 후보 확인 후 재고 조회
|
|
142
151
|
- 영화 시간표: `오늘 강남 CGV 시간표` → KST 오늘 날짜로 극장 검색 후 시간표 조회
|
|
@@ -330,9 +339,9 @@ MCP 도구 응답은 각 서비스의 원본 필드를 유지하면서, 클라
|
|
|
330
339
|
- `standard.theaters`: 영화관 목록을 `code`, `name`, `address`, `distanceMeters`, `raw` 형태로 정규화합니다.
|
|
331
340
|
- `standard.places`: 음식점/카페 같은 장소 목록을 `name`, `address`, `raw` 중심으로 정규화합니다.
|
|
332
341
|
|
|
333
|
-
###
|
|
342
|
+
### 통합 상품 가격 비교
|
|
334
343
|
|
|
335
|
-
|
|
344
|
+
여러 리테일/편의점 상품 검색 결과를 묶어 같은 상품의 판매처별 가격 후보를 비교합니다.
|
|
336
345
|
|
|
337
346
|
```bash
|
|
338
347
|
npx daiso compare 콜라 --limit 3 --json
|
|
@@ -6,6 +6,19 @@ export const GS25_CLOUDFRONT_403_PATTERNS = [
|
|
|
6
6
|
'The request could not be satisfied',
|
|
7
7
|
'403 ERROR',
|
|
8
8
|
];
|
|
9
|
+
export const EMART24_UPSTREAM_403_PATTERNS = [
|
|
10
|
+
'403 Forbidden',
|
|
11
|
+
'<title>403 Forbidden</title>',
|
|
12
|
+
];
|
|
13
|
+
export const CU_UPSTREAM_BLOCK_PATTERNS = [
|
|
14
|
+
'400 Bad Request',
|
|
15
|
+
'Request Blocked',
|
|
16
|
+
];
|
|
17
|
+
export const SEVENELEVEN_UPSTREAM_403_PATTERNS = [
|
|
18
|
+
'403 Forbidden',
|
|
19
|
+
'_Incapsula_Resource',
|
|
20
|
+
'NOINDEX, NOFOLLOW',
|
|
21
|
+
];
|
|
9
22
|
export const HEALTH_CHECKS = [
|
|
10
23
|
{
|
|
11
24
|
id: 'cli.contract',
|
|
@@ -41,6 +54,7 @@ export const HEALTH_CHECKS = [
|
|
|
41
54
|
path: '/api/emart24/products?keyword=%EC%BB%A4%ED%94%BC&pageSize=1',
|
|
42
55
|
collectionKey: 'products',
|
|
43
56
|
requiredFields: ['pluCd', 'goodsName', 'itemName', 'name'],
|
|
57
|
+
degradedFailurePatterns: EMART24_UPSTREAM_403_PATTERNS,
|
|
44
58
|
},
|
|
45
59
|
{
|
|
46
60
|
id: 'gs25.products',
|
|
@@ -69,6 +83,7 @@ export const HEALTH_CHECKS = [
|
|
|
69
83
|
path: '/api/seveneleven/products?query=%EC%BB%A4%ED%94%BC&size=1',
|
|
70
84
|
collectionKey: 'products',
|
|
71
85
|
requiredFields: ['itemCode', 'itemName', 'productNo', 'name'],
|
|
86
|
+
degradedFailurePatterns: SEVENELEVEN_UPSTREAM_403_PATTERNS,
|
|
72
87
|
},
|
|
73
88
|
{
|
|
74
89
|
id: 'lottemart.products',
|
|
@@ -124,6 +139,7 @@ export const HEALTH_CHECKS = [
|
|
|
124
139
|
path: '/api/cu/inventory?keyword=%EC%BB%A4%ED%94%BC&size=1&storeLimit=0&storeCheck=false',
|
|
125
140
|
collectionKey: 'inventoryItems',
|
|
126
141
|
requiredFields: ['itemCode', 'itemName', 'name'],
|
|
142
|
+
degradedFailurePatterns: CU_UPSTREAM_BLOCK_PATTERNS,
|
|
127
143
|
},
|
|
128
144
|
{
|
|
129
145
|
id: 'emart24.inventory',
|
|
@@ -133,6 +149,7 @@ export const HEALTH_CHECKS = [
|
|
|
133
149
|
path: '/api/emart24/inventory?keyword=%EC%BB%A4%ED%94%BC&storeKeyword=%EA%B0%95%EB%82%A8&limit=1',
|
|
134
150
|
collectionKey: 'inventoryItems',
|
|
135
151
|
requiredFields: ['pluCd', 'goodsName', 'itemName', 'name'],
|
|
152
|
+
degradedFailurePatterns: EMART24_UPSTREAM_403_PATTERNS,
|
|
136
153
|
},
|
|
137
154
|
{
|
|
138
155
|
id: 'gs25.inventory',
|
|
@@ -156,6 +173,7 @@ export const HEALTH_CHECKS = [
|
|
|
156
173
|
path: '/api/seveneleven/inventory?keyword=%EC%BB%A4%ED%94%BC&storeKeyword=%EA%B0%95%EB%82%A8&size=1',
|
|
157
174
|
collectionKey: 'inventoryItems',
|
|
158
175
|
requiredFields: ['itemCode', 'itemName', 'productNo', 'name'],
|
|
176
|
+
degradedFailurePatterns: SEVENELEVEN_UPSTREAM_403_PATTERNS,
|
|
159
177
|
},
|
|
160
178
|
{
|
|
161
179
|
id: 'oliveyoung.inventory',
|
package/dist/api/healthChecks.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 개별 서비스 헬스 체크 실행기
|
|
3
3
|
*/
|
|
4
|
-
import { GS25_CLOUDFRONT_403_PATTERNS, HEALTH_CHECKS } from './healthCheckDefinitions.js';
|
|
4
|
+
import { EMART24_UPSTREAM_403_PATTERNS, GS25_CLOUDFRONT_403_PATTERNS, HEALTH_CHECKS, SEVENELEVEN_UPSTREAM_403_PATTERNS, } from './healthCheckDefinitions.js';
|
|
5
5
|
import { hasRequiredRepresentativeFields, toCount, toFirstName } from './healthCheckShape.js';
|
|
6
6
|
const HEALTH_CHECK_CACHE_TTL_MS = 300_000;
|
|
7
7
|
const DEFAULT_HEALTH_CHECK_TIMEOUT_MS = 7000;
|
|
@@ -71,7 +71,16 @@ function shouldDegradeFailedResponse(check, message) {
|
|
|
71
71
|
return check.degradedFailurePatterns?.some((pattern) => message.includes(pattern)) ?? false;
|
|
72
72
|
}
|
|
73
73
|
function shouldDegradeCliContractPath(path, message) {
|
|
74
|
-
|
|
74
|
+
if (path.startsWith('/api/gs25/')) {
|
|
75
|
+
return GS25_CLOUDFRONT_403_PATTERNS.some((pattern) => message.includes(pattern));
|
|
76
|
+
}
|
|
77
|
+
if (path.startsWith('/api/emart24/')) {
|
|
78
|
+
return EMART24_UPSTREAM_403_PATTERNS.some((pattern) => message.includes(pattern));
|
|
79
|
+
}
|
|
80
|
+
if (path.startsWith('/api/seveneleven/')) {
|
|
81
|
+
return SEVENELEVEN_UPSTREAM_403_PATTERNS.some((pattern) => message.includes(pattern));
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
75
84
|
}
|
|
76
85
|
function resolveCheckTimeoutMs(check, timeoutMs) {
|
|
77
86
|
if (check.timeoutMs === undefined) {
|
|
@@ -229,7 +229,7 @@ export async function handleSevenElevenProducts(options, deps) {
|
|
|
229
229
|
deps,
|
|
230
230
|
path: '/api/seveneleven/products',
|
|
231
231
|
configure: (parsed) => {
|
|
232
|
-
const query = parsed.positionals[0];
|
|
232
|
+
const query = parsed.positionals[0] || parsed.options.query;
|
|
233
233
|
if (!query) {
|
|
234
234
|
return 'seveneleven-products 명령은 검색어가 필요합니다. 예: daiso seveneleven-products 삼각김밥';
|
|
235
235
|
}
|
|
@@ -245,7 +245,7 @@ export async function handleSevenElevenStores(options, deps) {
|
|
|
245
245
|
deps,
|
|
246
246
|
path: '/api/seveneleven/stores',
|
|
247
247
|
configure: (parsed) => {
|
|
248
|
-
const keyword = parsed.positionals[0];
|
|
248
|
+
const keyword = parsed.positionals[0] || parsed.options.keyword;
|
|
249
249
|
if (!keyword) {
|
|
250
250
|
return 'seveneleven-stores 명령은 검색어가 필요합니다. 예: daiso seveneleven-stores 안산 중앙역';
|
|
251
251
|
}
|
|
@@ -6,6 +6,9 @@ import { CGV_API } from './api.js';
|
|
|
6
6
|
import { requestCgv } from './transport.js';
|
|
7
7
|
const DEFAULT_THEATER_CODE = '0056';
|
|
8
8
|
const MAX_FALLBACK_THEATERS = 5;
|
|
9
|
+
function asArray(value) {
|
|
10
|
+
return Array.isArray(value) ? value : [];
|
|
11
|
+
}
|
|
9
12
|
async function resolveTheaterCode(playDate, theaterCode, params) {
|
|
10
13
|
if (theaterCode) {
|
|
11
14
|
return theaterCode;
|
|
@@ -25,7 +28,7 @@ async function fetchMoviesByTheaterCode(playDate, theaterCode, params) {
|
|
|
25
28
|
scnYmd: playDate,
|
|
26
29
|
});
|
|
27
30
|
const response = await requestCgv(CGV_API.MOVIE_LIST_PATH, searchParams, params.timeout, params.zyteApiKey);
|
|
28
|
-
return (response.data
|
|
31
|
+
return asArray(response.data)
|
|
29
32
|
.filter((item) => item.movNo && item.movNm)
|
|
30
33
|
.map((item) => ({
|
|
31
34
|
movieCode: item.movNo,
|
|
@@ -42,7 +45,7 @@ async function fetchTimetableByMovieCode(playDate, theaterCode, movieCode, param
|
|
|
42
45
|
rtctlScopCd: CGV_API.TIMETABLE_SCOPE_CODE,
|
|
43
46
|
});
|
|
44
47
|
const response = await requestCgv(CGV_API.TIMETABLE_PATH, searchParams, params.timeout, params.zyteApiKey);
|
|
45
|
-
return (response.data
|
|
48
|
+
return asArray(response.data)
|
|
46
49
|
.filter((item) => item.siteNo && item.movNo && item.scnYmd)
|
|
47
50
|
.map((item) => ({
|
|
48
51
|
scheduleId: `${item.scnYmd}${item.siteNo}${item.scnSseq || ''}`,
|
|
@@ -65,7 +68,7 @@ async function fetchTimetableBySite(playDate, theaterCode, params) {
|
|
|
65
68
|
rtctlScopCd: CGV_API.TIMETABLE_SITE_SCOPE_CODE,
|
|
66
69
|
});
|
|
67
70
|
const response = await requestCgv(CGV_API.TIMETABLE_BY_SITE_PATH, searchParams, params.timeout, params.zyteApiKey);
|
|
68
|
-
return (response.data
|
|
71
|
+
return asArray(response.data)
|
|
69
72
|
.filter((item) => item.siteNo && item.scnYmd)
|
|
70
73
|
.map((item) => ({
|
|
71
74
|
scheduleId: `${item.scnYmd}${item.siteNo}${item.scnSseq || ''}`,
|
|
@@ -85,7 +88,7 @@ export async function fetchCgvTheaters(params) {
|
|
|
85
88
|
coCd: CGV_API.COMPANY_CODE,
|
|
86
89
|
});
|
|
87
90
|
const response = await requestCgv(CGV_API.THEATER_LIST_PATH, searchParams, params.timeout, params.zyteApiKey);
|
|
88
|
-
const list = (response.data
|
|
91
|
+
const list = asArray(response.data).flatMap((region) => asArray(region.siteList).map((site) => ({
|
|
89
92
|
theaterCode: site.siteNo || '',
|
|
90
93
|
theaterName: site.siteNm || '',
|
|
91
94
|
regionCode: region.regnGrpCd || undefined,
|
|
@@ -14,25 +14,41 @@ async function searchProducts(args) {
|
|
|
14
14
|
if (!keyword || keyword.trim().length === 0) {
|
|
15
15
|
throw new Error('상품 검색어(keyword)를 입력해주세요.');
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
17
|
+
try {
|
|
18
|
+
const result = await searchEmart24Products({
|
|
19
|
+
keyword,
|
|
20
|
+
page,
|
|
21
|
+
pageSize,
|
|
22
|
+
sortType,
|
|
23
|
+
saleProductYn,
|
|
24
|
+
}, {
|
|
25
|
+
timeout: timeoutMs,
|
|
26
|
+
});
|
|
27
|
+
return buildTextResponse({
|
|
28
|
+
keyword,
|
|
29
|
+
page,
|
|
30
|
+
pageSize,
|
|
31
|
+
sortType,
|
|
32
|
+
saleProductYn,
|
|
33
|
+
totalCount: result.totalCount,
|
|
34
|
+
count: result.products.length,
|
|
35
|
+
products: result.products,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
return buildTextResponse({
|
|
40
|
+
keyword,
|
|
41
|
+
page,
|
|
42
|
+
pageSize,
|
|
43
|
+
sortType,
|
|
44
|
+
saleProductYn,
|
|
45
|
+
totalCount: 0,
|
|
46
|
+
count: 0,
|
|
47
|
+
products: [],
|
|
48
|
+
status: 'degraded',
|
|
49
|
+
message: error instanceof Error ? error.message : '이마트24 상품 검색에 실패했습니다.',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
36
52
|
}
|
|
37
53
|
const searchProductsOutputSchema = {
|
|
38
54
|
keyword: z.string().describe('검색어'),
|
|
@@ -43,6 +59,8 @@ const searchProductsOutputSchema = {
|
|
|
43
59
|
totalCount: z.number().describe('검색된 전체 상품 수'),
|
|
44
60
|
count: z.number().describe('반환된 상품 수'),
|
|
45
61
|
products: z.array(z.unknown()).describe('이마트24 상품 검색 결과'),
|
|
62
|
+
status: z.string().optional().describe('외부 API 차단 등으로 결과가 제한된 경우 degraded'),
|
|
63
|
+
message: z.string().optional().describe('degraded 상태의 원인 메시지'),
|
|
46
64
|
};
|
|
47
65
|
export function createSearchProductsTool() {
|
|
48
66
|
return {
|
|
@@ -5,22 +5,43 @@ const CATEGORY_KEYWORDS = {
|
|
|
5
5
|
dessert: '디저트',
|
|
6
6
|
all: '',
|
|
7
7
|
};
|
|
8
|
+
const HTML_ENTITY_REPLACEMENTS = {
|
|
9
|
+
'&': '&',
|
|
10
|
+
'"': '"',
|
|
11
|
+
''': "'",
|
|
12
|
+
''': "'",
|
|
13
|
+
};
|
|
8
14
|
function getProcessEnvValue(name) {
|
|
9
15
|
/* c8 ignore next -- Node 테스트와 CLI 런타임은 process를 제공하며, Worker에는 API에서 키를 주입합니다. */
|
|
10
16
|
return typeof process !== 'undefined' ? process.env[name] : undefined;
|
|
11
17
|
}
|
|
18
|
+
function stripHtmlTags(value) {
|
|
19
|
+
let result = '';
|
|
20
|
+
let insideTag = false;
|
|
21
|
+
for (const character of value) {
|
|
22
|
+
if (character === '<') {
|
|
23
|
+
insideTag = true;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (character === '>' && insideTag) {
|
|
27
|
+
insideTag = false;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (!insideTag) {
|
|
31
|
+
result += character;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
12
36
|
function cleanHtml(value) {
|
|
13
37
|
if (!value) {
|
|
14
38
|
return '';
|
|
15
39
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
.replace(/"/g, '"')
|
|
22
|
-
.replace(/'/g, "'")
|
|
23
|
-
.trim();
|
|
40
|
+
let result = stripHtmlTags(value);
|
|
41
|
+
for (const [entity, replacement] of Object.entries(HTML_ENTITY_REPLACEMENTS)) {
|
|
42
|
+
result = result.split(entity).join(replacement);
|
|
43
|
+
}
|
|
44
|
+
return result.trim();
|
|
24
45
|
}
|
|
25
46
|
function toCoordinate(value) {
|
|
26
47
|
if (!value) {
|
|
@@ -14,23 +14,40 @@ async function searchProducts(args) {
|
|
|
14
14
|
if (!query || query.trim().length === 0) {
|
|
15
15
|
throw new Error('상품 검색어(query)를 입력해주세요.');
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
17
|
+
try {
|
|
18
|
+
const result = await searchSevenElevenProductsWithVariants(query, {
|
|
19
|
+
page,
|
|
20
|
+
size,
|
|
21
|
+
sort,
|
|
22
|
+
timeout: timeoutMs,
|
|
23
|
+
});
|
|
24
|
+
return buildTextResponse({
|
|
25
|
+
query: result.query,
|
|
26
|
+
page,
|
|
27
|
+
size,
|
|
28
|
+
sort,
|
|
29
|
+
totalCount: result.totalCount,
|
|
30
|
+
count: result.products.length,
|
|
31
|
+
collectionIds: result.collectionIds,
|
|
32
|
+
appliedQueries: result.appliedQueries,
|
|
33
|
+
products: result.products,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
return buildTextResponse({
|
|
38
|
+
query,
|
|
39
|
+
page,
|
|
40
|
+
size,
|
|
41
|
+
sort,
|
|
42
|
+
totalCount: 0,
|
|
43
|
+
count: 0,
|
|
44
|
+
collectionIds: [],
|
|
45
|
+
appliedQueries: [query],
|
|
46
|
+
products: [],
|
|
47
|
+
status: 'degraded',
|
|
48
|
+
message: error instanceof Error ? error.message : '세븐일레븐 상품 검색에 실패했습니다.',
|
|
49
|
+
});
|
|
50
|
+
}
|
|
34
51
|
}
|
|
35
52
|
const searchProductsOutputSchema = {
|
|
36
53
|
query: z.string().describe('적용된 검색어'),
|
|
@@ -42,6 +59,8 @@ const searchProductsOutputSchema = {
|
|
|
42
59
|
collectionIds: z.array(z.string()).describe('검색 컬렉션 ID 목록'),
|
|
43
60
|
appliedQueries: z.array(z.string()).describe('실제로 시도한 검색어 목록'),
|
|
44
61
|
products: z.array(z.unknown()).describe('세븐일레븐 상품 검색 결과'),
|
|
62
|
+
status: z.string().optional().describe('외부 API 차단 등으로 결과가 제한된 경우 degraded'),
|
|
63
|
+
message: z.string().optional().describe('degraded 상태의 원인 메시지'),
|
|
45
64
|
};
|
|
46
65
|
export function createSearchProductsTool() {
|
|
47
66
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "daiso",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"description": "Korean retail and cinema MCP server, CLI, and Codex Skill for Daiso, convenience stores, marts, Olive Young, and theaters",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -79,34 +79,37 @@
|
|
|
79
79
|
"node": ">=20"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
|
-
"@biomejs/biome": "^2.
|
|
83
|
-
"@cloudflare/workers-types": "^4.
|
|
82
|
+
"@biomejs/biome": "^2.5.2",
|
|
83
|
+
"@cloudflare/workers-types": "^4.20260702.1",
|
|
84
84
|
"@eslint/js": "^10.0.1",
|
|
85
|
-
"@types/node": "^
|
|
85
|
+
"@types/node": "^26.1.0",
|
|
86
86
|
"@vitest/coverage-v8": "^4.1.7",
|
|
87
|
-
"eslint": "^10.
|
|
87
|
+
"eslint": "^10.6.0",
|
|
88
88
|
"eslint-config-prettier": "^10.1.8",
|
|
89
|
-
"globals": "^17.
|
|
89
|
+
"globals": "^17.7.0",
|
|
90
90
|
"jiti": "^2.7.0",
|
|
91
|
-
"playwright": "^1.
|
|
92
|
-
"prettier": "^3.
|
|
93
|
-
"sharp": "^0.
|
|
94
|
-
"tsx": "^4.
|
|
91
|
+
"playwright": "^1.61.1",
|
|
92
|
+
"prettier": "^3.9.4",
|
|
93
|
+
"sharp": "^0.35.3",
|
|
94
|
+
"tsx": "^4.23.0",
|
|
95
95
|
"typescript": "^6.0.3",
|
|
96
|
-
"typescript-eslint": "^8.
|
|
96
|
+
"typescript-eslint": "^8.62.1",
|
|
97
97
|
"vitest": "^4.1.7",
|
|
98
|
-
"wrangler": "^4.
|
|
98
|
+
"wrangler": "^4.107.0"
|
|
99
99
|
},
|
|
100
100
|
"overrides": {
|
|
101
101
|
"flatted": "3.4.2",
|
|
102
|
-
"undici": "7.
|
|
102
|
+
"undici": "7.28.0",
|
|
103
103
|
"brace-expansion": "5.0.6",
|
|
104
|
-
"ws": "8.
|
|
104
|
+
"ws": "8.21.0",
|
|
105
|
+
"tmp": "0.2.7",
|
|
106
|
+
"markdown-it": "14.2.0",
|
|
107
|
+
"esbuild": "0.28.1"
|
|
105
108
|
},
|
|
106
109
|
"dependencies": {
|
|
107
110
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
108
|
-
"hono": "^4.12.
|
|
109
|
-
"proj4": "^2.20.
|
|
111
|
+
"hono": "^4.12.27",
|
|
112
|
+
"proj4": "^2.20.9",
|
|
110
113
|
"workers-mcp": "^0.0.13"
|
|
111
114
|
}
|
|
112
115
|
}
|