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.
Files changed (2) hide show
  1. package/dist/index.js +155 -24
  2. 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.2",
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
- const response = await axios.get(`https://suggest.search.daum.net/sushi/ns`, {
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
- // 응답 형식: {"q":"keyword","items":["suggestion1", "suggestion2", ...]}
639
- if (response.data && Array.isArray(response.data.items)) {
640
- return response.data.items.slice(0, 10);
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
- return [];
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
- const response = await axios.get('https://search.daum.net/search?w=tot&DA=YZR&t__nil_searchbox=btn&sug=&sugo=&sq=&o=&q=%EC%8B%A4%EC%8B%9C%EA%B0%84', {
1199
- headers: { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36' },
1200
- timeout: 5000,
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
- $('[class*="keyword"]').each((i, el) => {
1206
- const keyword = $(el).text().trim();
1207
- if (keyword && keyword.length > 1 && keyword.length < 30) {
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: i + 1,
1212
- source: "realtime_search"
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.2',
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.2',
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.2' }
4172
+ serverInfo: { name: 'content-genie-mcp', version: '2.9.3' }
4042
4173
  }
4043
4174
  });
4044
4175
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "content-genie-mcp",
3
- "version": "2.9.2",
3
+ "version": "2.9.3",
4
4
  "description": "AI Content Creation Assistant MCP - 한국 콘텐츠 크리에이터를 위한 트렌드 분석 및 콘텐츠 생성 도우미",
5
5
  "main": "dist/index.js",
6
6
  "bin": "dist/index.js",