content-genie-mcp 2.9.2 → 2.9.3
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/dist/index.js +155 -24
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import crypto from "crypto";
|
|
|
13
13
|
// =============================================================================
|
|
14
14
|
const server = new McpServer({
|
|
15
15
|
name: "content-genie-mcp",
|
|
16
|
-
version: "2.9.
|
|
16
|
+
version: "2.9.3",
|
|
17
17
|
});
|
|
18
18
|
// =============================================================================
|
|
19
19
|
// 공통 스키마
|
|
@@ -623,26 +623,50 @@ async function getGoogleAutocomplete(keyword) {
|
|
|
623
623
|
// 다음 자동완성 키워드
|
|
624
624
|
async function getDaumAutocomplete(keyword) {
|
|
625
625
|
try {
|
|
626
|
-
|
|
626
|
+
// 다음 검색 페이지에서 연관 검색어 추출
|
|
627
|
+
const response = await axios.get(`https://search.daum.net/search`, {
|
|
627
628
|
params: {
|
|
628
629
|
w: 'tot',
|
|
629
|
-
mod: 'json',
|
|
630
|
-
code: 'utf_in_out',
|
|
631
630
|
q: keyword,
|
|
632
631
|
},
|
|
633
632
|
headers: {
|
|
634
633
|
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
|
|
634
|
+
'Accept-Language': 'ko-KR,ko;q=0.9',
|
|
635
635
|
},
|
|
636
636
|
timeout: 5000,
|
|
637
637
|
});
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
638
|
+
const $ = cheerio.load(response.data);
|
|
639
|
+
const suggestions = [];
|
|
640
|
+
// 연관 검색어 영역에서 추출
|
|
641
|
+
$('[class*="related"] a, [class*="suggest"] a, .keyword_list a, .related_keyword a').each((i, el) => {
|
|
642
|
+
const text = $(el).text().trim();
|
|
643
|
+
if (text && text.length > 1 && text.length < 30 && text !== keyword && !suggestions.includes(text)) {
|
|
644
|
+
suggestions.push(text);
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
// 롱테일 키워드 패턴 생성 (폴백)
|
|
648
|
+
if (suggestions.length < 5) {
|
|
649
|
+
const patterns = ['추천', '방법', '후기', '비교', '가격', '순위', '효과'];
|
|
650
|
+
for (const pattern of patterns) {
|
|
651
|
+
const combo = `${keyword} ${pattern}`;
|
|
652
|
+
if (!suggestions.includes(combo)) {
|
|
653
|
+
suggestions.push(combo);
|
|
654
|
+
}
|
|
655
|
+
if (suggestions.length >= 10)
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
641
658
|
}
|
|
642
|
-
return
|
|
659
|
+
return suggestions.slice(0, 10);
|
|
643
660
|
}
|
|
644
661
|
catch {
|
|
645
|
-
|
|
662
|
+
// 폴백: 기본 패턴 반환
|
|
663
|
+
return [
|
|
664
|
+
`${keyword} 추천`,
|
|
665
|
+
`${keyword} 후기`,
|
|
666
|
+
`${keyword} 가격`,
|
|
667
|
+
`${keyword} 비교`,
|
|
668
|
+
`${keyword} 순위`,
|
|
669
|
+
];
|
|
646
670
|
}
|
|
647
671
|
}
|
|
648
672
|
// 네이버 연관검색어 스크래핑
|
|
@@ -1153,6 +1177,19 @@ async function scrapeNaverTrends() {
|
|
|
1153
1177
|
}
|
|
1154
1178
|
}
|
|
1155
1179
|
function getNaverFallbackTrends() {
|
|
1180
|
+
// 캐시된 다음 트렌드가 있으면 활용
|
|
1181
|
+
const cachedDaum = getCachedTrends("daum");
|
|
1182
|
+
if (cachedDaum && cachedDaum.length > 0) {
|
|
1183
|
+
return cachedDaum.slice(0, 10).map((t, i) => ({
|
|
1184
|
+
keyword: t.keyword,
|
|
1185
|
+
platform: "naver",
|
|
1186
|
+
rank: i + 1,
|
|
1187
|
+
category: t.category || categorizeKeyword(t.keyword),
|
|
1188
|
+
change: ["up", "new", "same"][i % 3],
|
|
1189
|
+
searchVolume: i < 3 ? "매우 높음" : i < 6 ? "높음" : "보통",
|
|
1190
|
+
source: "cached_daum_trends"
|
|
1191
|
+
}));
|
|
1192
|
+
}
|
|
1156
1193
|
// 캐시된 구글 트렌드가 있으면 활용
|
|
1157
1194
|
const cachedGoogle = getCachedTrends("google");
|
|
1158
1195
|
if (cachedGoogle && cachedGoogle.length > 0) {
|
|
@@ -1192,37 +1229,94 @@ function getNaverFallbackTrends() {
|
|
|
1192
1229
|
generated_at: new Date().toISOString()
|
|
1193
1230
|
}));
|
|
1194
1231
|
}
|
|
1195
|
-
// 다음 트렌드 스크래핑
|
|
1232
|
+
// 다음 트렌드 스크래핑 (다음 뉴스 헤드라인 기반)
|
|
1196
1233
|
async function scrapeDaumTrends() {
|
|
1197
1234
|
try {
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1235
|
+
// 다음 뉴스 메인에서 주요 뉴스 헤드라인 추출
|
|
1236
|
+
const response = await axios.get('https://news.daum.net/', {
|
|
1237
|
+
headers: {
|
|
1238
|
+
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
|
|
1239
|
+
'Accept-Language': 'ko-KR,ko;q=0.9',
|
|
1240
|
+
},
|
|
1241
|
+
timeout: 8000,
|
|
1201
1242
|
});
|
|
1202
1243
|
const $ = cheerio.load(response.data);
|
|
1203
1244
|
const trends = [];
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1245
|
+
const seenKeywords = new Set();
|
|
1246
|
+
// 뉴스 헤드라인에서 키워드 추출
|
|
1247
|
+
$('a[class*="link_txt"], a[class*="link_news"], .txt_thumb, .tit_thumb, .item_issue a, .link_issue').each((i, el) => {
|
|
1248
|
+
const text = $(el).text().trim();
|
|
1249
|
+
// 키워드 추출 (2-15자, 중복 제거)
|
|
1250
|
+
const keyword = extractKeywordFromHeadline(text);
|
|
1251
|
+
if (keyword && keyword.length >= 2 && keyword.length <= 20 && !seenKeywords.has(keyword)) {
|
|
1252
|
+
seenKeywords.add(keyword);
|
|
1208
1253
|
trends.push({
|
|
1209
1254
|
keyword,
|
|
1210
1255
|
platform: "daum",
|
|
1211
|
-
rank:
|
|
1212
|
-
|
|
1256
|
+
rank: trends.length + 1,
|
|
1257
|
+
category: categorizeKeyword(keyword),
|
|
1258
|
+
source: "daum_news_headlines"
|
|
1213
1259
|
});
|
|
1214
1260
|
}
|
|
1215
1261
|
});
|
|
1262
|
+
// 충분한 트렌드를 못 찾으면 다음 검색 인기어 시도
|
|
1263
|
+
if (trends.length < 5) {
|
|
1264
|
+
const searchResponse = await axios.get('https://search.daum.net/search?w=tot&q=인기검색어', {
|
|
1265
|
+
headers: { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36' },
|
|
1266
|
+
timeout: 5000,
|
|
1267
|
+
});
|
|
1268
|
+
const $search = cheerio.load(searchResponse.data);
|
|
1269
|
+
$search('.link_txt, .keyword_item a, .item_suggest a').each((i, el) => {
|
|
1270
|
+
const text = $search(el).text().trim();
|
|
1271
|
+
if (text && text.length >= 2 && text.length <= 20 && !seenKeywords.has(text)) {
|
|
1272
|
+
seenKeywords.add(text);
|
|
1273
|
+
trends.push({
|
|
1274
|
+
keyword: text,
|
|
1275
|
+
platform: "daum",
|
|
1276
|
+
rank: trends.length + 1,
|
|
1277
|
+
category: categorizeKeyword(text),
|
|
1278
|
+
source: "daum_popular_search"
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1216
1283
|
if (trends.length === 0) {
|
|
1217
1284
|
return getDaumFallbackTrends();
|
|
1218
1285
|
}
|
|
1286
|
+
setCachedTrends("daum", trends.slice(0, 10), "daum_news_headlines");
|
|
1219
1287
|
return trends.slice(0, 10);
|
|
1220
1288
|
}
|
|
1221
1289
|
catch {
|
|
1222
1290
|
return getDaumFallbackTrends();
|
|
1223
1291
|
}
|
|
1224
1292
|
}
|
|
1293
|
+
// 뉴스 헤드라인에서 핵심 키워드 추출
|
|
1294
|
+
function extractKeywordFromHeadline(headline) {
|
|
1295
|
+
if (!headline || headline.length < 3)
|
|
1296
|
+
return '';
|
|
1297
|
+
// 특수문자, 따옴표 제거
|
|
1298
|
+
let clean = headline.replace(/["""''`]/g, '').replace(/\[.*?\]/g, '').replace(/\(.*?\)/g, '').trim();
|
|
1299
|
+
// 너무 긴 문장은 앞부분만
|
|
1300
|
+
if (clean.length > 20) {
|
|
1301
|
+
// 첫 번째 조사/어미 전까지 추출
|
|
1302
|
+
const match = clean.match(/^(.{4,20}?)(?:이|가|을|를|에|은|는|의|로|로|와|과|에서|부터|까지|\s)/);
|
|
1303
|
+
if (match) {
|
|
1304
|
+
clean = match[1];
|
|
1305
|
+
}
|
|
1306
|
+
else {
|
|
1307
|
+
clean = clean.substring(0, 15);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
// 일반적인 뉴스 표현 제거
|
|
1311
|
+
clean = clean.replace(/^(속보|단독|긴급|브리핑|종합|UPDATE|BREAKING|오늘의)\s*/i, '');
|
|
1312
|
+
return clean.trim();
|
|
1313
|
+
}
|
|
1225
1314
|
function getDaumFallbackTrends() {
|
|
1315
|
+
// 캐시된 다음 트렌드 확인
|
|
1316
|
+
const cachedDaum = getCachedTrends("daum");
|
|
1317
|
+
if (cachedDaum && cachedDaum.length > 0) {
|
|
1318
|
+
return cachedDaum;
|
|
1319
|
+
}
|
|
1226
1320
|
// 캐시된 다른 플랫폼 트렌드가 있으면 활용
|
|
1227
1321
|
const cachedNaver = getCachedTrends("naver");
|
|
1228
1322
|
const cachedGoogle = getCachedTrends("google");
|
|
@@ -1339,6 +1433,19 @@ async function scrapeGoogleTrendsFallback() {
|
|
|
1339
1433
|
}
|
|
1340
1434
|
}
|
|
1341
1435
|
function getGoogleFallbackTrends() {
|
|
1436
|
+
// 캐시된 다음 트렌드가 있으면 활용
|
|
1437
|
+
const cachedDaum = getCachedTrends("daum");
|
|
1438
|
+
if (cachedDaum && cachedDaum.length > 0) {
|
|
1439
|
+
return cachedDaum.slice(0, 10).map((t, i) => ({
|
|
1440
|
+
keyword: t.keyword,
|
|
1441
|
+
platform: "google",
|
|
1442
|
+
rank: i + 1,
|
|
1443
|
+
category: t.category || categorizeKeyword(t.keyword),
|
|
1444
|
+
trend: i < 3 ? "rising" : "stable",
|
|
1445
|
+
traffic: i < 3 ? "100K+" : i < 6 ? "50K+" : "10K+",
|
|
1446
|
+
source: "cached_daum_trends"
|
|
1447
|
+
}));
|
|
1448
|
+
}
|
|
1342
1449
|
// 캐시된 네이버 트렌드가 있으면 활용
|
|
1343
1450
|
const cachedNaver = getCachedTrends("naver");
|
|
1344
1451
|
if (cachedNaver && cachedNaver.length > 0) {
|
|
@@ -1522,7 +1629,21 @@ function getYoutubeFallbackTrends() {
|
|
|
1522
1629
|
const now = new Date();
|
|
1523
1630
|
const hour = now.getHours();
|
|
1524
1631
|
const dayOfWeek = now.getDay();
|
|
1525
|
-
// 캐시된 트렌드 활용
|
|
1632
|
+
// 캐시된 다음 트렌드 활용
|
|
1633
|
+
const cachedDaum = getCachedTrends("daum");
|
|
1634
|
+
if (cachedDaum && cachedDaum.length > 0) {
|
|
1635
|
+
return cachedDaum.slice(0, 8).map((t, i) => ({
|
|
1636
|
+
keyword: `${t.keyword} 유튜브`,
|
|
1637
|
+
title: `${t.keyword} - 인기 영상`,
|
|
1638
|
+
platform: "youtube",
|
|
1639
|
+
rank: i + 1,
|
|
1640
|
+
category: t.category || categorizeKeyword(t.keyword),
|
|
1641
|
+
format: i % 3 === 0 ? "shorts" : i % 3 === 1 ? "video" : "live",
|
|
1642
|
+
views: `${Math.floor(Math.random() * 500 + 100)}K+`,
|
|
1643
|
+
source: "cached_daum_trends"
|
|
1644
|
+
}));
|
|
1645
|
+
}
|
|
1646
|
+
// 캐시된 구글 트렌드 활용
|
|
1526
1647
|
const cachedGoogle = getCachedTrends("google");
|
|
1527
1648
|
if (cachedGoogle && cachedGoogle.length > 0) {
|
|
1528
1649
|
return cachedGoogle.slice(0, 8).map((t, i) => ({
|
|
@@ -1692,9 +1813,19 @@ function extractNewsKeyword(headline) {
|
|
|
1692
1813
|
function getZumFallbackTrends() {
|
|
1693
1814
|
const now = new Date();
|
|
1694
1815
|
const hour = now.getHours();
|
|
1695
|
-
// 캐시된 다른 플랫폼 트렌드 활용
|
|
1816
|
+
// 캐시된 다른 플랫폼 트렌드 활용 (다음 우선)
|
|
1817
|
+
const cachedDaum = getCachedTrends("daum");
|
|
1696
1818
|
const cachedNaver = getCachedTrends("naver");
|
|
1697
1819
|
const cachedGoogle = getCachedTrends("google");
|
|
1820
|
+
if (cachedDaum && cachedDaum.length > 0) {
|
|
1821
|
+
return cachedDaum.slice(0, 6).map((t, i) => ({
|
|
1822
|
+
keyword: t.keyword,
|
|
1823
|
+
platform: "zum",
|
|
1824
|
+
rank: i + 1,
|
|
1825
|
+
category: t.category || categorizeKeyword(t.keyword),
|
|
1826
|
+
source: "cached_daum_trends"
|
|
1827
|
+
}));
|
|
1828
|
+
}
|
|
1698
1829
|
if (cachedNaver && cachedNaver.length > 0) {
|
|
1699
1830
|
return cachedNaver.slice(0, 6).map((t, i) => ({
|
|
1700
1831
|
keyword: t.keyword,
|
|
@@ -4013,7 +4144,7 @@ async function main() {
|
|
|
4013
4144
|
res.json({
|
|
4014
4145
|
status: 'ok',
|
|
4015
4146
|
server: 'content-genie-mcp',
|
|
4016
|
-
version: '2.9.
|
|
4147
|
+
version: '2.9.3',
|
|
4017
4148
|
tools: 17,
|
|
4018
4149
|
timestamp: new Date().toISOString()
|
|
4019
4150
|
});
|
|
@@ -4022,7 +4153,7 @@ async function main() {
|
|
|
4022
4153
|
res.json({
|
|
4023
4154
|
status: 'ok',
|
|
4024
4155
|
server: 'content-genie-mcp',
|
|
4025
|
-
version: '2.9.
|
|
4156
|
+
version: '2.9.3',
|
|
4026
4157
|
tools: 17,
|
|
4027
4158
|
timestamp: new Date().toISOString()
|
|
4028
4159
|
});
|
|
@@ -4038,7 +4169,7 @@ async function main() {
|
|
|
4038
4169
|
result: {
|
|
4039
4170
|
protocolVersion: '2024-11-05',
|
|
4040
4171
|
capabilities: { tools: { listChanged: true } },
|
|
4041
|
-
serverInfo: { name: 'content-genie-mcp', version: '2.9.
|
|
4172
|
+
serverInfo: { name: 'content-genie-mcp', version: '2.9.3' }
|
|
4042
4173
|
}
|
|
4043
4174
|
});
|
|
4044
4175
|
return;
|