content-genie-mcp 2.0.0 → 2.6.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.
package/dist/index.js CHANGED
@@ -5,11 +5,11 @@ import { z } from "zod";
5
5
  import axios from "axios";
6
6
  import * as cheerio from "cheerio";
7
7
  // =============================================================================
8
- // Content Genie MCP v2.0 - 한국 콘텐츠 크리에이터를 위한 AI 어시스턴트 (고도화 버전)
8
+ // Content Genie MCP v2.5 - 한국 콘텐츠 크리에이터를 위한 AI 어시스턴트 (프로 버전)
9
9
  // =============================================================================
10
10
  const server = new McpServer({
11
11
  name: "content-genie-mcp",
12
- version: "2.0.0",
12
+ version: "2.6.0",
13
13
  });
14
14
  // =============================================================================
15
15
  // 공통 스키마
@@ -19,56 +19,126 @@ const TrendCategorySchema = z.enum(["general", "news", "shopping", "entertainmen
19
19
  const ContentTypeSchema = z.enum(["blog", "youtube", "instagram", "tiktok", "newsletter", "threads", "twitter", "all"]);
20
20
  const ToneSchema = z.enum(["professional", "casual", "humorous", "educational", "inspirational", "provocative", "storytelling"]);
21
21
  // =============================================================================
22
- // 한국 기념일/이벤트 대형 DB (2025-2026)
22
+ // 한국 기념일/이벤트 메가 DB (2025-2026) - 100개 이상
23
23
  // =============================================================================
24
24
  const KOREAN_EVENTS_DB = [
25
- // 공휴일
26
- { date: "01-01", name: "새해", type: "holiday", contentIdeas: ["신년 계획", "새해 다짐", "2025 트렌드"] },
27
- { date: "01-28", name: "설날 연휴", type: "holiday", contentIdeas: ["설날 음식", "세뱃돈", "귀성길 "] },
28
- { date: "01-29", name: "설날", type: "holiday", contentIdeas: ["설날 인사말", "전통 놀이", "가족 모임"] },
29
- { date: "03-01", name: "삼일절", type: "holiday", contentIdeas: ["독립운동", "역사 콘텐츠"] },
30
- { date: "05-05", name: "어린이날", type: "holiday", contentIdeas: ["선물 추천", "가족 나들이", "키즈 콘텐츠"] },
31
- { date: "05-06", name: "대체공휴일", type: "holiday", contentIdeas: ["황금연휴", "여행지 추천"] },
32
- { date: "05-15", name: "부처님오신날", type: "holiday", contentIdeas: ["사찰 여행", "템플스테이"] },
33
- { date: "06-06", name: "현충일", type: "holiday", contentIdeas: ["호국 영웅", "추모 콘텐츠"] },
34
- { date: "08-15", name: "광복절", type: "holiday", contentIdeas: ["독립 역사", "애국 콘텐츠"] },
35
- { date: "10-03", name: "개천절", type: "holiday", contentIdeas: ["단군 신화", "한국 역사"] },
36
- { date: "10-05", name: "추석 연휴", type: "holiday", contentIdeas: ["추석 음식", "송편 만들기", "한복"] },
37
- { date: "10-06", name: "추석", type: "holiday", contentIdeas: ["가족 모임", "성묘", "보름달"] },
38
- { date: "10-09", name: "한글날", type: "holiday", contentIdeas: ["한글 사랑", "국어 콘텐츠"] },
39
- { date: "12-25", name: "크리스마스", type: "holiday", contentIdeas: ["크리스마스 선물", "연말 분위기", "캐롤"] },
40
- // 상업 이벤트
41
- { date: "02-14", name: "발렌타인데이", type: "commercial", contentIdeas: ["초콜릿 추천", "커플 데이트", "고백 "] },
42
- { date: "03-14", name: "화이트데이", type: "commercial", contentIdeas: ["사탕 선물", "답례 아이디어"] },
43
- { date: "04-14", name: "블랙데이", type: "commercial", contentIdeas: ["자장면", "솔로 위로"] },
44
- { date: "05-08", name: "어버이날", type: "commercial", contentIdeas: ["부모님 선물", "효도 여행", "카네이션"] },
45
- { date: "05-14", name: "로즈데이", type: "commercial", contentIdeas: ["장미 선물", "데이트 코스"] },
46
- { date: "05-15", name: "스승의날", type: "event", contentIdeas: ["감사 편지", "선생님 선물"] },
47
- { date: "05-21", name: "부부의날", type: "commercial", contentIdeas: ["부부 여행", "결혼 기념"] },
48
- { date: "06-14", name: "키스데이", type: "commercial", contentIdeas: ["커플 콘텐츠"] },
49
- { date: "07-14", name: "실버데이", type: "commercial", contentIdeas: ["어른 선물", "효도 콘텐츠"] },
50
- { date: "08-14", name: "그린데이", type: "commercial", contentIdeas: ["야외 데이트", "소풍"] },
51
- { date: "09-14", name: "포토데이", type: "commercial", contentIdeas: ["커플 사진", "포토존"] },
52
- { date: "10-14", name: "와인데이", type: "commercial", contentIdeas: ["와인 추천", "분위기 있는 데이트"] },
53
- { date: "11-11", name: "빼빼로데이", type: "commercial", contentIdeas: ["빼빼로 만들기", "선물 포장", "DIY"] },
54
- { date: "11-14", name: "무비데이", type: "commercial", contentIdeas: ["영화 추천", "영화관 데이트"] },
55
- { date: "12-14", name: "허그데이", type: "commercial", contentIdeas: ["겨울 데이트", "따뜻한 콘텐츠"] },
56
- // 시즌 이벤트
57
- { date: "01-15", name: "정월대보름", type: "traditional", contentIdeas: ["오곡밥", "부럼 깨기", "달맞이"] },
58
- { date: "04-05", name: "식목일", type: "event", contentIdeas: ["나무 심기", "환경 콘텐츠"] },
59
- { date: "07-17", name: "제헌절", type: "event", contentIdeas: ["헌법 이야기", "민주주의"] },
60
- { date: "10-31", name: "할로윈", type: "commercial", contentIdeas: ["코스튬", "할로윈 파티", "공포 콘텐츠"] },
61
- { date: "11-28", name: "블랙프라이데이", type: "commercial", contentIdeas: ["할인 정보", "쇼핑 리스트", "구매 가이드"] },
62
- { date: "12-31", name: "연말", type: "event", contentIdeas: ["연말 정산", "올해의 회고", "새해 계획"] },
63
- // 시험/입시
64
- { date: "03-02", name: "개학", type: "event", contentIdeas: ["새학기 준비", "학용품", "학교생활"] },
65
- { date: "06-01", name: "수능 D-180", type: "event", contentIdeas: ["수험생 응원", "공부 "] },
66
- { date: "11-14", name: "수능", type: "event", contentIdeas: ["수능 응원", "수능 후 계획", "대입 전략"] },
67
- // 쇼핑 시즌
68
- { date: "01-02", name: "신년 세일 시즌", type: "shopping", contentIdeas: ["할인 정보", "신년 쇼핑"] },
69
- { date: "06-01", name: "여름 세일 시작", type: "shopping", contentIdeas: ["여름 패션", "에어컨", "휴가 준비"] },
70
- { date: "09-01", name: "가을 신상", type: "shopping", contentIdeas: ["가을 패션", "트렌치코트", "니트"] },
71
- { date: "12-01", name: "연말 세일 시즌", type: "shopping", contentIdeas: ["선물 추천", "연말 쇼핑"] },
25
+ // ===== 공휴일 (14개) =====
26
+ { date: "01-01", name: "새해", type: "holiday", contentIdeas: ["신년 계획", "새해 다짐", "2025 트렌드"], priority: "high" },
27
+ { date: "01-27", name: "설날 연휴 시작", type: "holiday", contentIdeas: ["귀성길 ", "고속도로 정보"], priority: "high" },
28
+ { date: "01-28", name: "설날 연휴", type: "holiday", contentIdeas: ["설날 음식", "세뱃돈", "귀성길 "], priority: "high" },
29
+ { date: "01-29", name: "설날", type: "holiday", contentIdeas: ["설날 인사말", "전통 놀이", "가족 모임"], priority: "high" },
30
+ { date: "01-30", name: "설날 연휴 마지막", type: "holiday", contentIdeas: ["유턴 정보", " 연휴 마무리"], priority: "medium" },
31
+ { date: "03-01", name: "삼일절", type: "holiday", contentIdeas: ["독립운동", "역사 콘텐츠", "태극기 게양"], priority: "medium" },
32
+ { date: "05-05", name: "어린이날", type: "holiday", contentIdeas: ["선물 추천", "가족 나들이", "키즈 콘텐츠"], priority: "high" },
33
+ { date: "05-06", name: "대체공휴일", type: "holiday", contentIdeas: ["황금연휴", "여행지 추천"], priority: "medium" },
34
+ { date: "05-15", name: "부처님오신날", type: "holiday", contentIdeas: ["사찰 여행", "템플스테이", "연등 축제"], priority: "medium" },
35
+ { date: "06-06", name: "현충일", type: "holiday", contentIdeas: ["호국 영웅", "추모 콘텐츠", "국립묘지"], priority: "medium" },
36
+ { date: "08-15", name: "광복절", type: "holiday", contentIdeas: ["독립 역사", "애국 콘텐츠", "광복절 행사"], priority: "medium" },
37
+ { date: "10-03", name: "개천절", type: "holiday", contentIdeas: ["단군 신화", "한국 역사", "건국이념"], priority: "medium" },
38
+ { date: "10-05", name: "추석 연휴 시작", type: "holiday", contentIdeas: ["추석 준비", "귀성 정보"], priority: "high" },
39
+ { date: "10-06", name: "추석", type: "holiday", contentIdeas: ["가족 모임", "성묘", "보름달", "송편"], priority: "high" },
40
+ { date: "10-07", name: "추석 연휴", type: "holiday", contentIdeas: ["추석 음식", "한복", "전통놀이"], priority: "high" },
41
+ { date: "10-08", name: "추석 연휴 마지막", type: "holiday", contentIdeas: ["유턴 정보", "연휴 마무리"], priority: "medium" },
42
+ { date: "10-09", name: "한글날", type: "holiday", contentIdeas: ["한글 사랑", "세종대왕", "국어 콘텐츠"], priority: "medium" },
43
+ { date: "12-25", name: "크리스마스", type: "holiday", contentIdeas: ["크리스마스 선물", "연말 분위기", "캐롤", "트리"], priority: "high" },
44
+ // ===== 데이 시리즈 (14일 데이) (12개) =====
45
+ { date: "01-14", name: "다이어리데이", type: "commercial", contentIdeas: ["다이어리 추천", "플래너 꾸미기", "신년 계획"], priority: "low" },
46
+ { date: "02-14", name: "발렌타인데이", type: "commercial", contentIdeas: ["초콜릿 추천", "커플 데이트", "고백 팁", "DIY 초콜릿"], priority: "high" },
47
+ { date: "03-14", name: "화이트데이", type: "commercial", contentIdeas: ["사탕 선물", "답례 아이디어", "데이트 코스"], priority: "high" },
48
+ { date: "04-14", name: "블랙데이", type: "commercial", contentIdeas: ["자장면", "솔로 위로", "혼밥 맛집"], priority: "medium" },
49
+ { date: "05-14", name: "로즈데이", type: "commercial", contentIdeas: ["장미 선물", "데이트 코스", "꽃 카페"], priority: "medium" },
50
+ { date: "06-14", name: "키스데이", type: "commercial", contentIdeas: ["커플 콘텐츠", "연애 팁"], priority: "low" },
51
+ { date: "07-14", name: "실버데이", type: "commercial", contentIdeas: ["어른 선물", "효도 콘텐츠", "부모님 소개"], priority: "low" },
52
+ { date: "08-14", name: "그린데이", type: "commercial", contentIdeas: ["야외 데이트", "소풍", "숲 캠핑"], priority: "low" },
53
+ { date: "09-14", name: "포토데이", type: "commercial", contentIdeas: ["커플 사진", "포토존", "인생샷 명소"], priority: "medium" },
54
+ { date: "10-14", name: "와인데이", type: "commercial", contentIdeas: ["와인 추천", "분위기 있는 데이트", "와인바"], priority: "medium" },
55
+ { date: "11-14", name: "무비데이", type: "commercial", contentIdeas: ["영화 추천", "영화관 데이트", "넷플릭스"], priority: "medium" },
56
+ { date: "12-14", name: "허그데이", type: "commercial", contentIdeas: ["겨울 데이트", "따뜻한 콘텐츠"], priority: "low" },
57
+ // ===== 가정/효도 관련 (8개) =====
58
+ { date: "05-08", name: "어버이날", type: "commercial", contentIdeas: ["부모님 선물", "효도 여행", "카네이션", "감사 편지"], priority: "high" },
59
+ { date: "05-15", name: "스승의날", type: "event", contentIdeas: ["감사 편지", "선생님 선물", "은사 감사"], priority: "medium" },
60
+ { date: "05-21", name: "부부의날", type: "commercial", contentIdeas: ["부부 여행", "결혼 기념", "데이트"], priority: "medium" },
61
+ { date: "07-07", name: "칠석", type: "traditional", contentIdeas: ["견우직녀", "전통 이야기", " 보기"], priority: "low" },
62
+ { date: "10-02", name: "노인의날", type: "event", contentIdeas: ["효도 콘텐츠", "어르신 선물", "경로당 봉사"], priority: "low" },
63
+ { date: "11-22", name: "좋은 부부의 날", type: "commercial", contentIdeas: ["부부 선물", "결혼 기념일"], priority: "low" },
64
+ { date: "05-02", name: "근로자의 날", type: "event", contentIdeas: ["직장인 위로", "워라밸", "번아웃"], priority: "medium" },
65
+ { date: "08-08", name: "효도의 ", type: "event", contentIdeas: ["부모님 감사", "효도 선물"], priority: "low" },
66
+ // ===== 전통 절기/명절 (15개) =====
67
+ { date: "01-15", name: "정월대보름", type: "traditional", contentIdeas: ["오곡밥", "부럼 깨기", "달맞이", "쥐불놀이"], priority: "medium" },
68
+ { date: "02-04", name: "입춘", type: "traditional", contentIdeas: [" 시작", "입춘대길", "새해 다짐"], priority: "low" },
69
+ { date: "03-05", name: "경칩", type: "traditional", contentIdeas: [" 날씨", "개구리", "봄맞이"], priority: "low" },
70
+ { date: "04-05", name: "식목일", type: "event", contentIdeas: ["나무 심기", "환경 콘텐츠", "에코라이프"], priority: "medium" },
71
+ { date: "04-20", name: "곡우", type: "traditional", contentIdeas: ["봄비", "농사 시작", " 마시기"], priority: "low" },
72
+ { date: "05-05", name: "단오", type: "traditional", contentIdeas: ["창포물 머리감기", "그네뛰기", "씨름"], priority: "low" },
73
+ { date: "06-21", name: "하지", type: "traditional", contentIdeas: ["여름 시작", "가장 긴 낮", "더위 준비"], priority: "low" },
74
+ { date: "07-18", name: "초복", type: "traditional", contentIdeas: ["삼계탕", "보양식", "더위 극복"], priority: "medium" },
75
+ { date: "07-28", name: "중복", type: "traditional", contentIdeas: ["삼계탕", "보양식", "여름 건강"], priority: "medium" },
76
+ { date: "08-07", name: "말복", type: "traditional", contentIdeas: ["삼계탕", "여름 마무리", "가을 준비"], priority: "medium" },
77
+ { date: "08-23", name: "처서", type: "traditional", contentIdeas: ["더위 끝", "가을 시작", "환절기"], priority: "low" },
78
+ { date: "09-23", name: "추분", type: "traditional", contentIdeas: ["밤낮 같음", "가을 본격", "단풍"], priority: "low" },
79
+ { date: "11-07", name: "입동", type: "traditional", contentIdeas: ["겨울 시작", "김장철", "월동 준비"], priority: "medium" },
80
+ { date: "12-21", name: "동지", type: "traditional", contentIdeas: ["팥죽", "가장 긴 밤", "동지팥죽"], priority: "medium" },
81
+ { date: "07-17", name: "제헌절", type: "event", contentIdeas: ["헌법 이야기", "민주주의", "법치주의"], priority: "low" },
82
+ // ===== 글로벌/상업 이벤트 (15개) =====
83
+ { date: "10-31", name: "할로윈", type: "commercial", contentIdeas: ["코스튬", "할로윈 파티", "공포 콘텐츠", "할로윈 메이크업"], priority: "high" },
84
+ { date: "11-11", name: "빼빼로데이", type: "commercial", contentIdeas: ["빼빼로 만들기", "선물 포장", "DIY", "꾸미기"], priority: "high" },
85
+ { date: "11-29", name: "블랙프라이데이", type: "commercial", contentIdeas: ["할인 정보", "쇼핑 리스트", "구매 가이드", "직구"], priority: "high" },
86
+ { date: "12-02", name: "사이버먼데이", type: "commercial", contentIdeas: ["온라인 할인", "전자제품", "쇼핑 팁"], priority: "medium" },
87
+ { date: "12-26", name: "박싱데이", type: "commercial", contentIdeas: ["연말 세일", "크리스마스 후 쇼핑"], priority: "low" },
88
+ { date: "04-01", name: "만우절", type: "event", contentIdeas: ["장난 아이디어", "몰래카메라", "유머"], priority: "medium" },
89
+ { date: "04-22", name: "지구의날", type: "event", contentIdeas: ["환경보호", "에코라이프", "제로웨이스트"], priority: "medium" },
90
+ { date: "03-08", name: "세계 여성의 날", type: "event", contentIdeas: ["여성 리더", "젠더 평등", "워킹맘"], priority: "medium" },
91
+ { date: "03-22", name: "세계 물의 날", type: "event", contentIdeas: ["물 절약", "환경"], priority: "low" },
92
+ { date: "06-05", name: "세계 환경의 날", type: "event", contentIdeas: ["환경보호", "플라스틱 줄이기"], priority: "medium" },
93
+ { date: "09-21", name: "세계 평화의 날", type: "event", contentIdeas: ["평화", "힐링"], priority: "low" },
94
+ { date: "10-10", name: "세계 정신건강의 날", type: "event", contentIdeas: ["멘탈케어", "스트레스 관리", "마음 건강"], priority: "medium" },
95
+ { date: "11-19", name: "세계 남성의 날", type: "event", contentIdeas: ["남성 건강", "아버지"], priority: "low" },
96
+ { date: "12-01", name: "세계 에이즈의 날", type: "event", contentIdeas: ["건강 정보", "예방"], priority: "low" },
97
+ { date: "02-04", name: "세계 암의 날", type: "event", contentIdeas: ["암 예방", "건강 검진"], priority: "low" },
98
+ // ===== 학교/입시 관련 (10개) =====
99
+ { date: "03-02", name: "개학", type: "event", contentIdeas: ["새학기 준비", "학용품", "학교생활"], priority: "medium" },
100
+ { date: "06-01", name: "수능 D-180", type: "event", contentIdeas: ["수험생 응원", "공부 팁", "학습 전략"], priority: "low" },
101
+ { date: "09-01", name: "수능 D-100", type: "event", contentIdeas: ["수험생 응원", "집중 공부법"], priority: "medium" },
102
+ { date: "11-14", name: "수능", type: "event", contentIdeas: ["수능 응원", "수능 후 계획", "대입 전략"], priority: "high" },
103
+ { date: "12-15", name: "대학 원서 마감", type: "event", contentIdeas: ["대입 정보", "자소서 팁"], priority: "medium" },
104
+ { date: "02-01", name: "졸업시즌", type: "event", contentIdeas: ["졸업 선물", "졸업 사진", "졸업 축하"], priority: "medium" },
105
+ { date: "03-01", name: "입학시즌", type: "event", contentIdeas: ["입학 선물", "학교 준비", "신입생 팁"], priority: "medium" },
106
+ { date: "07-20", name: "여름방학 시작", type: "event", contentIdeas: ["방학 계획", "여름 캠프", "체험학습"], priority: "medium" },
107
+ { date: "08-20", name: "개학 준비", type: "event", contentIdeas: ["2학기 준비", "학용품 쇼핑"], priority: "low" },
108
+ { date: "12-20", name: "겨울방학 시작", type: "event", contentIdeas: ["겨울 방학", "스키", "해외여행"], priority: "medium" },
109
+ // ===== 쇼핑 시즌 (10개) =====
110
+ { date: "01-02", name: "신년 세일 시즌", type: "shopping", contentIdeas: ["할인 정보", "신년 쇼핑", "가전 세일"], priority: "high" },
111
+ { date: "04-01", name: "봄 세일 시작", type: "shopping", contentIdeas: ["봄 패션", "아우터", "봄 신상"], priority: "medium" },
112
+ { date: "06-01", name: "여름 세일 시작", type: "shopping", contentIdeas: ["여름 패션", "에어컨", "휴가 준비"], priority: "high" },
113
+ { date: "07-15", name: "여름 중간 세일", type: "shopping", contentIdeas: ["여름 할인", "시즌오프"], priority: "medium" },
114
+ { date: "09-01", name: "가을 신상", type: "shopping", contentIdeas: ["가을 패션", "트렌치코트", "니트"], priority: "high" },
115
+ { date: "10-15", name: "가을 세일", type: "shopping", contentIdeas: ["가을 할인", "겨울 준비"], priority: "medium" },
116
+ { date: "11-01", name: "프리블랙프라이데이", type: "shopping", contentIdeas: ["얼리버드 할인", "쇼핑 준비"], priority: "medium" },
117
+ { date: "12-01", name: "연말 세일 시즌", type: "shopping", contentIdeas: ["선물 추천", "연말 쇼핑", "크리스마스"], priority: "high" },
118
+ { date: "12-26", name: "연말 정리 세일", type: "shopping", contentIdeas: ["재고 정리", "추가 할인"], priority: "medium" },
119
+ { date: "08-15", name: "가전 할인대전", type: "shopping", contentIdeas: ["가전제품", "에어컨", "냉장고"], priority: "medium" },
120
+ // ===== 시즌/날씨 관련 (12개) =====
121
+ { date: "12-31", name: "연말", type: "event", contentIdeas: ["연말 정산", "올해의 회고", "새해 계획", "카운트다운"], priority: "high" },
122
+ { date: "03-20", name: "봄 시작", type: "event", contentIdeas: ["봄맞이", "봄 패션", "봄 나들이"], priority: "medium" },
123
+ { date: "04-10", name: "벚꽃 시즌", type: "event", contentIdeas: ["벚꽃 명소", "벚꽃 사진", "봄 피크닉"], priority: "high" },
124
+ { date: "05-01", name: "가정의 달 시작", type: "event", contentIdeas: ["가족 이벤트", "효도", "어린이날 준비"], priority: "medium" },
125
+ { date: "07-01", name: "여름휴가 시즌", type: "event", contentIdeas: ["휴가지 추천", "바캉스", "물놀이"], priority: "high" },
126
+ { date: "08-01", name: "휴가 피크 시즌", type: "event", contentIdeas: ["피서지", "워터파크", "해외여행"], priority: "high" },
127
+ { date: "10-20", name: "단풍 시즌", type: "event", contentIdeas: ["단풍 명소", "가을 산행", "드라이브"], priority: "medium" },
128
+ { date: "11-15", name: "김장철", type: "event", contentIdeas: ["김장 레시피", "김치 담그기", "김장 재료"], priority: "medium" },
129
+ { date: "12-15", name: "연말 파티 시즌", type: "event", contentIdeas: ["송년회", "연말 파티", "홈파티"], priority: "medium" },
130
+ { date: "06-15", name: "장마 시작", type: "event", contentIdeas: ["장마 대비", "우산", "레인부츠"], priority: "medium" },
131
+ { date: "01-10", name: "한파 시즌", type: "event", contentIdeas: ["한파 대비", "방한용품", "겨울철 건강"], priority: "medium" },
132
+ { date: "07-25", name: "폭염 시즌", type: "event", contentIdeas: ["폭염 대비", "에어컨", "열사병 예방"], priority: "medium" },
133
+ // ===== 콘텐츠 크리에이터 특화 (8개) =====
134
+ { date: "01-10", name: "연간 콘텐츠 기획", type: "event", contentIdeas: ["연간 계획", "콘텐츠 전략", "채널 목표"], priority: "medium" },
135
+ { date: "04-15", name: "유튜브 알고리즘 시즌", type: "event", contentIdeas: ["알고리즘 팁", "조회수 올리기"], priority: "low" },
136
+ { date: "06-20", name: "여름 콘텐츠 기획", type: "event", contentIdeas: ["여름 브이로그", "휴가 콘텐츠"], priority: "medium" },
137
+ { date: "09-15", name: "가을 콘텐츠 시즌", type: "event", contentIdeas: ["가을 감성", "ASMR", "독서"], priority: "medium" },
138
+ { date: "11-25", name: "연말 콘텐츠 준비", type: "event", contentIdeas: ["연말 기획", "베스트 영상", "회고"], priority: "medium" },
139
+ { date: "03-15", name: "봄 브랜드 콜라보 시즌", type: "event", contentIdeas: ["브랜드 협업", "PPL", "협찬"], priority: "low" },
140
+ { date: "10-01", name: "구독자 이벤트 시즌", type: "event", contentIdeas: ["구독자 감사", "이벤트", "선물"], priority: "low" },
141
+ { date: "12-10", name: "연말결산 콘텐츠", type: "event", contentIdeas: ["올해의 정리", "베스트 모음", "연말 특집"], priority: "medium" },
72
142
  ];
73
143
  // =============================================================================
74
144
  // Tool 1: 실시간 한국 트렌드 분석 (get_korean_trends) - 고도화
@@ -399,6 +469,35 @@ server.tool("get_seasonal_content_guide", "다가오는 시즌/이벤트에 맞
399
469
  // =============================================================================
400
470
  // Helper Functions - 고도화
401
471
  // =============================================================================
472
+ // 키워드 카테고리 자동 분류
473
+ function categorizeKeyword(keyword) {
474
+ const text = keyword.toLowerCase();
475
+ if (/ai|gpt|인공지능|기술|테크|앱|소프트웨어|코딩|개발/.test(text))
476
+ return "tech";
477
+ if (/주식|코인|비트코인|투자|금리|경제|재테크|부동산|환율/.test(text))
478
+ return "finance";
479
+ if (/운동|헬스|다이어트|건강|병원|의료|영양/.test(text))
480
+ return "health";
481
+ if (/맛집|음식|요리|레시피|카페|먹방|배달/.test(text))
482
+ return "food";
483
+ if (/여행|호텔|관광|항공|휴가|리조트/.test(text))
484
+ return "travel";
485
+ if (/드라마|영화|연예|아이돌|방송|예능|넷플릭스|kpop/.test(text))
486
+ return "entertainment";
487
+ if (/게임|롤|배그|스팀|플스|닌텐도/.test(text))
488
+ return "gaming";
489
+ if (/뷰티|화장품|스킨케어|메이크업|패션|옷/.test(text))
490
+ return "beauty";
491
+ if (/축구|야구|농구|스포츠|올림픽|월드컵/.test(text))
492
+ return "sports";
493
+ if (/교육|공부|학교|시험|자격증|취업/.test(text))
494
+ return "education";
495
+ if (/쇼핑|할인|세일|구매|가격/.test(text))
496
+ return "shopping";
497
+ if (/뉴스|정치|사회|이슈/.test(text))
498
+ return "news";
499
+ return "general";
500
+ }
402
501
  // 네이버 트렌드 스크래핑
403
502
  async function scrapeNaverTrends() {
404
503
  try {
@@ -502,38 +601,391 @@ function getDaumFallbackTrends() {
502
601
  { keyword: "날씨 정보", platform: "daum", rank: 5, category: "general" },
503
602
  ];
504
603
  }
505
- // 구글 트렌드 코리아
604
+ // 구글 트렌드 코리아 - 실제 RSS 피드 스크래핑
506
605
  async function scrapeGoogleTrendsKorea() {
507
- return [
508
- { keyword: "생성형 AI", platform: "google", rank: 1, category: "tech", trend: "rising" },
509
- { keyword: "K-POP 신곡", platform: "google", rank: 2, category: "entertainment", trend: "stable" },
510
- { keyword: "미국 주식", platform: "google", rank: 3, category: "finance", trend: "rising" },
511
- { keyword: "비트코인 전망", platform: "google", rank: 4, category: "finance", trend: "volatile" },
512
- { keyword: "건강식품 추천", platform: "google", rank: 5, category: "health", trend: "rising" },
513
- { keyword: "원격 근무", platform: "google", rank: 6, category: "work", trend: "stable" },
514
- { keyword: "전기차 비교", platform: "google", rank: 7, category: "auto", trend: "rising" },
606
+ try {
607
+ // Google Trends Daily RSS Feed for Korea
608
+ const response = await axios.get('https://trends.google.com/trending/rss?geo=KR', {
609
+ headers: {
610
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
611
+ 'Accept': 'application/rss+xml, application/xml, text/xml'
612
+ },
613
+ timeout: 8000,
614
+ });
615
+ const $ = cheerio.load(response.data, { xmlMode: true });
616
+ const trends = [];
617
+ $('item').each((i, el) => {
618
+ if (i >= 15)
619
+ return false; // 최대 15개
620
+ const title = $(el).find('title').text().trim();
621
+ const traffic = $(el).find('ht\\:approx_traffic, approx_traffic').text().trim();
622
+ const newsItem = $(el).find('ht\\:news_item_title, news_item_title').first().text().trim();
623
+ if (title) {
624
+ trends.push({
625
+ keyword: title,
626
+ platform: "google",
627
+ rank: i + 1,
628
+ category: categorizeKeyword(title),
629
+ trend: "rising",
630
+ traffic: traffic || "10K+",
631
+ related_news: newsItem || null,
632
+ source: "google_trends_rss"
633
+ });
634
+ }
635
+ });
636
+ if (trends.length > 0) {
637
+ return trends;
638
+ }
639
+ // Fallback: 실시간 검색 트렌드 페이지 스크래핑 시도
640
+ return await scrapeGoogleTrendsFallback();
641
+ }
642
+ catch (error) {
643
+ return await scrapeGoogleTrendsFallback();
644
+ }
645
+ }
646
+ // Google Trends Fallback - 트렌드 페이지 스크래핑
647
+ async function scrapeGoogleTrendsFallback() {
648
+ try {
649
+ const response = await axios.get('https://trends.google.co.kr/trends/trendingsearches/daily?geo=KR', {
650
+ headers: {
651
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
652
+ 'Accept-Language': 'ko-KR,ko;q=0.9'
653
+ },
654
+ timeout: 8000,
655
+ });
656
+ const $ = cheerio.load(response.data);
657
+ const trends = [];
658
+ // 다양한 셀렉터 시도
659
+ $('[class*="feed-item"], [class*="trending"], .title').each((i, el) => {
660
+ if (i >= 10)
661
+ return false;
662
+ const keyword = $(el).text().trim();
663
+ if (keyword && keyword.length > 1 && keyword.length < 50) {
664
+ trends.push({
665
+ keyword,
666
+ platform: "google",
667
+ rank: i + 1,
668
+ category: categorizeKeyword(keyword),
669
+ trend: "rising",
670
+ source: "google_trends_page"
671
+ });
672
+ }
673
+ });
674
+ if (trends.length > 0)
675
+ return trends;
676
+ // 최종 Fallback: 시간대별 동적 데이터
677
+ return getGoogleFallbackTrends();
678
+ }
679
+ catch {
680
+ return getGoogleFallbackTrends();
681
+ }
682
+ }
683
+ function getGoogleFallbackTrends() {
684
+ const hour = new Date().getHours();
685
+ const dayOfWeek = new Date().getDay();
686
+ const baseTrends = [
687
+ { keyword: "AI 활용법", category: "tech" },
688
+ { keyword: "ChatGPT 팁", category: "tech" },
689
+ { keyword: "주식 시세", category: "finance" },
690
+ { keyword: "비트코인", category: "finance" },
691
+ { keyword: "다이어트", category: "health" },
515
692
  ];
693
+ // 시간대별 추가 트렌드
694
+ const timeTrends = hour >= 9 && hour <= 18
695
+ ? [{ keyword: "점심 메뉴", category: "food" }, { keyword: "카페 추천", category: "food" }]
696
+ : [{ keyword: "넷플릭스 추천", category: "entertainment" }, { keyword: "게임 추천", category: "entertainment" }];
697
+ // 요일별 추가 트렌드
698
+ const dayTrends = dayOfWeek === 0 || dayOfWeek === 6
699
+ ? [{ keyword: "주말 나들이", category: "travel" }, { keyword: "브런치 맛집", category: "food" }]
700
+ : [{ keyword: "재택근무 팁", category: "work" }, { keyword: "퇴근 후 취미", category: "lifestyle" }];
701
+ return [...baseTrends, ...timeTrends, ...dayTrends].map((item, i) => ({
702
+ ...item,
703
+ platform: "google",
704
+ rank: i + 1,
705
+ trend: "stable",
706
+ source: "fallback_dynamic"
707
+ }));
516
708
  }
517
- // 유튜브 트렌드 코리아
709
+ // 유튜브 트렌드 코리아 - 실제 스크래핑
518
710
  async function scrapeYoutubeTrendsKorea() {
519
- return [
520
- { keyword: "브이로그", platform: "youtube", rank: 1, category: "lifestyle", views: "1.5M", format: "vlog" },
521
- { keyword: "먹방", platform: "youtube", rank: 2, category: "food", views: "1.2M", format: "mukbang" },
522
- { keyword: "게임 실황", platform: "youtube", rank: 3, category: "gaming", views: "980K", format: "streaming" },
523
- { keyword: "뷰티 튜토리얼", platform: "youtube", rank: 4, category: "beauty", views: "850K", format: "tutorial" },
524
- { keyword: "운동 루틴", platform: "youtube", rank: 5, category: "fitness", views: "720K", format: "how-to" },
525
- { keyword: "코딩 강의", platform: "youtube", rank: 6, category: "education", views: "650K", format: "lecture" },
526
- { keyword: "여행 영상", platform: "youtube", rank: 7, category: "travel", views: "600K", format: "cinematic" },
527
- { keyword: "숏폼 콘텐츠", platform: "youtube", rank: 8, category: "shorts", views: "2.1M", format: "shorts" },
711
+ try {
712
+ // YouTube Korea Trending 페이지
713
+ const response = await axios.get('https://www.youtube.com/feed/trending?gl=KR&hl=ko', {
714
+ headers: {
715
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
716
+ 'Accept-Language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
717
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
718
+ },
719
+ timeout: 10000,
720
+ });
721
+ const trends = [];
722
+ const html = response.data;
723
+ // YouTube 초기 데이터 JSON 추출
724
+ const ytInitialDataMatch = html.match(/var ytInitialData = ({.*?});<\/script>/s);
725
+ if (ytInitialDataMatch) {
726
+ try {
727
+ const data = JSON.parse(ytInitialDataMatch[1]);
728
+ const tabs = data?.contents?.twoColumnBrowseResultsRenderer?.tabs || [];
729
+ for (const tab of tabs) {
730
+ const contents = tab?.tabRenderer?.content?.sectionListRenderer?.contents || [];
731
+ for (const section of contents) {
732
+ const items = section?.itemSectionRenderer?.contents || [];
733
+ for (const item of items) {
734
+ const video = item?.videoRenderer;
735
+ if (video && trends.length < 15) {
736
+ const title = video?.title?.runs?.[0]?.text || video?.title?.simpleText || '';
737
+ const viewCount = video?.viewCountText?.simpleText || video?.shortViewCountText?.simpleText || '';
738
+ const channel = video?.ownerText?.runs?.[0]?.text || '';
739
+ if (title) {
740
+ trends.push({
741
+ keyword: extractKeywordFromTitle(title),
742
+ title: title,
743
+ platform: "youtube",
744
+ rank: trends.length + 1,
745
+ category: categorizeYouTubeContent(title, channel),
746
+ views: viewCount,
747
+ channel: channel,
748
+ format: detectVideoFormat(title),
749
+ source: "youtube_trending"
750
+ });
751
+ }
752
+ }
753
+ }
754
+ }
755
+ }
756
+ }
757
+ catch (parseError) {
758
+ // JSON 파싱 실패 시 fallback
759
+ }
760
+ }
761
+ // HTML 파싱 시도 (JSON 실패 시)
762
+ if (trends.length === 0) {
763
+ const $ = cheerio.load(html);
764
+ $('a#video-title').each((i, el) => {
765
+ if (i >= 15)
766
+ return false;
767
+ const title = $(el).text().trim();
768
+ if (title) {
769
+ trends.push({
770
+ keyword: extractKeywordFromTitle(title),
771
+ title: title,
772
+ platform: "youtube",
773
+ rank: i + 1,
774
+ category: categorizeYouTubeContent(title, ''),
775
+ format: detectVideoFormat(title),
776
+ source: "youtube_html"
777
+ });
778
+ }
779
+ });
780
+ }
781
+ if (trends.length > 0)
782
+ return trends;
783
+ return getYoutubeFallbackTrends();
784
+ }
785
+ catch (error) {
786
+ return getYoutubeFallbackTrends();
787
+ }
788
+ }
789
+ // YouTube 제목에서 키워드 추출
790
+ function extractKeywordFromTitle(title) {
791
+ // 대괄호, 괄호 내용 제거
792
+ let keyword = title.replace(/[\[\(【].*?[\]\)】]/g, '').trim();
793
+ // 특수문자 제거
794
+ keyword = keyword.replace(/[^\w\sㄱ-ㅎㅏ-ㅣ가-힣]/g, ' ').trim();
795
+ // 너무 길면 자르기
796
+ if (keyword.length > 30) {
797
+ keyword = keyword.substring(0, 30) + '...';
798
+ }
799
+ return keyword || title.substring(0, 30);
800
+ }
801
+ // YouTube 콘텐츠 카테고리 분류
802
+ function categorizeYouTubeContent(title, channel) {
803
+ const text = (title + ' ' + channel).toLowerCase();
804
+ if (/먹방|mukbang|음식|요리|레시피|맛집/.test(text))
805
+ return "food";
806
+ if (/게임|gaming|롤|lol|배그|minecraft/.test(text))
807
+ return "gaming";
808
+ if (/뷰티|메이크업|화장|스킨케어|뷰스타/.test(text))
809
+ return "beauty";
810
+ if (/운동|헬스|다이어트|fitness|workout/.test(text))
811
+ return "fitness";
812
+ if (/브이로그|vlog|일상/.test(text))
813
+ return "lifestyle";
814
+ if (/여행|travel|trip/.test(text))
815
+ return "travel";
816
+ if (/음악|노래|커버|music|mv/.test(text))
817
+ return "music";
818
+ if (/드라마|예능|영화|movie/.test(text))
819
+ return "entertainment";
820
+ if (/공부|강의|교육|tutorial/.test(text))
821
+ return "education";
822
+ if (/테크|리뷰|tech|unboxing/.test(text))
823
+ return "tech";
824
+ if (/뉴스|이슈|news/.test(text))
825
+ return "news";
826
+ if (/shorts|쇼츠|숏/.test(text))
827
+ return "shorts";
828
+ return "general";
829
+ }
830
+ // 영상 포맷 감지
831
+ function detectVideoFormat(title) {
832
+ const text = title.toLowerCase();
833
+ if (/shorts|쇼츠/.test(text))
834
+ return "shorts";
835
+ if (/vlog|브이로그|일상/.test(text))
836
+ return "vlog";
837
+ if (/먹방|mukbang/.test(text))
838
+ return "mukbang";
839
+ if (/asmr/.test(text))
840
+ return "asmr";
841
+ if (/리뷰|review|언박싱|unboxing/.test(text))
842
+ return "review";
843
+ if (/튜토리얼|tutorial|강의|하는 법/.test(text))
844
+ return "tutorial";
845
+ if (/live|라이브/.test(text))
846
+ return "live";
847
+ if (/mv|뮤비|music video/.test(text))
848
+ return "music_video";
849
+ return "standard";
850
+ }
851
+ function getYoutubeFallbackTrends() {
852
+ const hour = new Date().getHours();
853
+ // 시간대별 인기 카테고리
854
+ const popularCategories = hour >= 18 || hour <= 2
855
+ ? ["entertainment", "gaming", "music"] // 저녁/밤
856
+ : hour >= 6 && hour <= 9
857
+ ? ["news", "education", "lifestyle"] // 아침
858
+ : ["food", "lifestyle", "tech"]; // 낮
859
+ const trends = [
860
+ { keyword: "유튜브 인기 급상승", category: popularCategories[0], format: "trending" },
861
+ { keyword: "쇼츠 챌린지", category: "shorts", format: "shorts", views: "500K+" },
862
+ { keyword: "브이로그", category: "lifestyle", format: "vlog", views: "300K+" },
863
+ { keyword: "먹방 ASMR", category: "food", format: "mukbang", views: "400K+" },
864
+ { keyword: "게임 실황", category: "gaming", format: "streaming", views: "350K+" },
865
+ { keyword: "K-POP 커버", category: "music", format: "cover", views: "600K+" },
866
+ { keyword: "메이크업 튜토리얼", category: "beauty", format: "tutorial", views: "250K+" },
867
+ { keyword: "IT 리뷰", category: "tech", format: "review", views: "200K+" },
528
868
  ];
869
+ return trends.map((item, i) => ({
870
+ ...item,
871
+ platform: "youtube",
872
+ rank: i + 1,
873
+ source: "fallback_dynamic"
874
+ }));
529
875
  }
530
- // 줌 트렌드
876
+ // 줌 트렌드 - 실제 스크래핑
531
877
  async function scrapeZumTrends() {
532
- return [
533
- { keyword: "오늘의 뉴스", platform: "zum", rank: 1, category: "news" },
534
- { keyword: "핫이슈", platform: "zum", rank: 2, category: "general" },
535
- { keyword: "연예 화제", platform: "zum", rank: 3, category: "entertainment" },
878
+ try {
879
+ // Zum 메인 페이지 실시간 검색어
880
+ const response = await axios.get('https://zum.com/', {
881
+ headers: {
882
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
883
+ 'Accept-Language': 'ko-KR,ko;q=0.9',
884
+ },
885
+ timeout: 8000,
886
+ });
887
+ const $ = cheerio.load(response.data);
888
+ const trends = [];
889
+ // 줌 실시간 검색어 셀렉터들
890
+ const selectors = [
891
+ '.realtime_keyword_list li',
892
+ '.issue_keyword li',
893
+ '[class*="ranking"] li',
894
+ '[class*="keyword"] a',
895
+ '.hot_keyword li'
896
+ ];
897
+ for (const selector of selectors) {
898
+ if (trends.length >= 10)
899
+ break;
900
+ $(selector).each((i, el) => {
901
+ if (trends.length >= 10)
902
+ return false;
903
+ const keyword = $(el).text().trim()
904
+ .replace(/^\d+\.?\s*/, '') // 순위 번호 제거
905
+ .replace(/new|↑|↓|─/gi, '') // 변동 표시 제거
906
+ .trim();
907
+ if (keyword && keyword.length > 1 && keyword.length < 30 && !trends.find(t => t.keyword === keyword)) {
908
+ trends.push({
909
+ keyword,
910
+ platform: "zum",
911
+ rank: trends.length + 1,
912
+ category: categorizeKeyword(keyword),
913
+ source: "zum_realtime"
914
+ });
915
+ }
916
+ });
917
+ }
918
+ if (trends.length > 0)
919
+ return trends;
920
+ // Zum 뉴스 섹션 시도
921
+ return await scrapeZumNewsTrends();
922
+ }
923
+ catch (error) {
924
+ return await scrapeZumNewsTrends();
925
+ }
926
+ }
927
+ async function scrapeZumNewsTrends() {
928
+ try {
929
+ const response = await axios.get('https://news.zum.com/', {
930
+ headers: {
931
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
932
+ 'Accept-Language': 'ko-KR,ko;q=0.9',
933
+ },
934
+ timeout: 8000,
935
+ });
936
+ const $ = cheerio.load(response.data);
937
+ const trends = [];
938
+ // 뉴스 헤드라인에서 키워드 추출
939
+ $('h2, h3, .headline, .title, [class*="news_title"]').each((i, el) => {
940
+ if (trends.length >= 10)
941
+ return false;
942
+ const text = $(el).text().trim();
943
+ if (text && text.length > 5 && text.length < 50) {
944
+ const keyword = extractNewsKeyword(text);
945
+ if (keyword && !trends.find(t => t.keyword === keyword)) {
946
+ trends.push({
947
+ keyword,
948
+ platform: "zum",
949
+ rank: trends.length + 1,
950
+ category: categorizeKeyword(keyword),
951
+ source: "zum_news"
952
+ });
953
+ }
954
+ }
955
+ });
956
+ if (trends.length > 0)
957
+ return trends;
958
+ return getZumFallbackTrends();
959
+ }
960
+ catch {
961
+ return getZumFallbackTrends();
962
+ }
963
+ }
964
+ function extractNewsKeyword(headline) {
965
+ // 헤드라인에서 핵심 키워드 추출
966
+ const words = headline.split(/[\s,\.…]+/).filter(w => w.length >= 2 && w.length <= 10);
967
+ return words.slice(0, 3).join(' ') || headline.substring(0, 20);
968
+ }
969
+ function getZumFallbackTrends() {
970
+ const hour = new Date().getHours();
971
+ const baseKeywords = [
972
+ { keyword: "오늘의 뉴스", category: "news" },
973
+ { keyword: "실시간 이슈", category: "general" },
974
+ { keyword: "연예 소식", category: "entertainment" },
975
+ { keyword: "스포츠 결과", category: "sports" },
976
+ { keyword: "경제 동향", category: "finance" },
536
977
  ];
978
+ const timeBased = hour >= 7 && hour <= 9
979
+ ? [{ keyword: "아침 뉴스", category: "news" }]
980
+ : hour >= 12 && hour <= 14
981
+ ? [{ keyword: "점심 이슈", category: "general" }]
982
+ : [{ keyword: "저녁 뉴스", category: "news" }];
983
+ return [...baseKeywords, ...timeBased].map((item, i) => ({
984
+ ...item,
985
+ platform: "zum",
986
+ rank: i + 1,
987
+ source: "fallback"
988
+ }));
537
989
  }
538
990
  // 고급 트렌드 인사이트 생성
539
991
  function generateAdvancedTrendInsights(trends) {
@@ -1304,57 +1756,249 @@ function predictAdvancedViralScore(title, description, platform, hashtags, conte
1304
1756
  ].filter(Boolean).slice(0, 3),
1305
1757
  };
1306
1758
  }
1307
- // 뉴스 분석
1759
+ // 뉴스 분석 - 실제 스크래핑
1308
1760
  async function analyzeKoreanNews(category, timeRange, extractKeywords) {
1309
- // 카테고리별 뉴스 키워드 시뮬레이션
1310
- const newsData = {
1761
+ const news = [];
1762
+ const allKeywords = [];
1763
+ // 카테고리별 네이버 뉴스 섹션 URL
1764
+ const categoryUrls = {
1765
+ general: 'https://news.naver.com/',
1766
+ politics: 'https://news.naver.com/section/100',
1767
+ economy: 'https://news.naver.com/section/101',
1768
+ society: 'https://news.naver.com/section/102',
1769
+ culture: 'https://news.naver.com/section/103',
1770
+ tech: 'https://news.naver.com/section/105',
1771
+ sports: 'https://sports.news.naver.com/',
1772
+ entertainment: 'https://entertain.naver.com/home',
1773
+ };
1774
+ const url = categoryUrls[category] || categoryUrls.general;
1775
+ try {
1776
+ // 네이버 뉴스 스크래핑
1777
+ const response = await axios.get(url, {
1778
+ headers: {
1779
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
1780
+ 'Accept-Language': 'ko-KR,ko;q=0.9',
1781
+ },
1782
+ timeout: 10000,
1783
+ });
1784
+ const $ = cheerio.load(response.data);
1785
+ // 헤드라인 추출 - 다양한 셀렉터 시도
1786
+ const headlineSelectors = [
1787
+ '.cjs_t', // 네이버 뉴스 메인
1788
+ '.sa_text_title',
1789
+ 'a.news_tit',
1790
+ '.cluster_text_headline',
1791
+ '.cluster_head_topic',
1792
+ 'h2.tit',
1793
+ '.link_news',
1794
+ '[class*="headline"] a',
1795
+ '[class*="title"] a',
1796
+ ];
1797
+ for (const selector of headlineSelectors) {
1798
+ if (news.length >= 15)
1799
+ break;
1800
+ $(selector).each((i, el) => {
1801
+ if (news.length >= 15)
1802
+ return false;
1803
+ const headline = $(el).text().trim();
1804
+ const href = $(el).attr('href') || '';
1805
+ if (headline && headline.length > 10 && headline.length < 100) {
1806
+ // 중복 체크
1807
+ if (!news.find(n => n.headline === headline)) {
1808
+ const sentiment = analyzeSentiment(headline);
1809
+ news.push({
1810
+ headline,
1811
+ source: detectNewsSource(href, category),
1812
+ sentiment,
1813
+ url: href.startsWith('http') ? href : `https://news.naver.com${href}`,
1814
+ });
1815
+ // 키워드 추출
1816
+ if (extractKeywords) {
1817
+ const words = extractKeywordsFromText(headline);
1818
+ allKeywords.push(...words);
1819
+ }
1820
+ }
1821
+ }
1822
+ });
1823
+ }
1824
+ }
1825
+ catch (error) {
1826
+ // 네이버 실패 시 다음 뉴스 시도
1827
+ try {
1828
+ const daumNews = await scrapeDaumNews(category);
1829
+ news.push(...daumNews);
1830
+ if (extractKeywords) {
1831
+ daumNews.forEach(n => {
1832
+ allKeywords.push(...extractKeywordsFromText(n.headline));
1833
+ });
1834
+ }
1835
+ }
1836
+ catch {
1837
+ // 모두 실패 시 Fallback
1838
+ }
1839
+ }
1840
+ // Fallback: 뉴스가 없으면 기본 데이터
1841
+ if (news.length === 0) {
1842
+ news.push(...getNewsFallback(category));
1843
+ }
1844
+ // 키워드 빈도 계산
1845
+ const keywordFrequency = extractKeywords ? calculateKeywordFrequency(allKeywords) : [];
1846
+ // 감성 분석 요약
1847
+ const sentiments = news.map(n => n.sentiment);
1848
+ const positiveCount = sentiments.filter(s => s === 'positive').length;
1849
+ const negativeCount = sentiments.filter(s => s === 'negative').length;
1850
+ const neutralCount = sentiments.filter(s => s === 'neutral').length;
1851
+ const total = sentiments.length || 1;
1852
+ return {
1853
+ category,
1854
+ time_range: timeRange,
1855
+ analyzed_at: new Date().toISOString(),
1856
+ source: news.length > 0 && news[0].url?.includes('naver') ? 'naver_news' : 'daum_news',
1857
+ top_news: news.slice(0, 10),
1858
+ extracted_keywords: keywordFrequency.slice(0, 10),
1859
+ sentiment_summary: {
1860
+ positive: `${Math.round((positiveCount / total) * 100)}%`,
1861
+ neutral: `${Math.round((neutralCount / total) * 100)}%`,
1862
+ negative: `${Math.round((negativeCount / total) * 100)}%`,
1863
+ },
1864
+ content_opportunities: generateNewsContentOpportunities(keywordFrequency, category),
1865
+ trending_topics: news.slice(0, 5).map(n => n.headline),
1866
+ };
1867
+ }
1868
+ // 다음 뉴스 스크래핑
1869
+ async function scrapeDaumNews(category) {
1870
+ const categoryUrls = {
1871
+ general: 'https://news.daum.net/',
1872
+ politics: 'https://news.daum.net/politics',
1873
+ economy: 'https://news.daum.net/economic',
1874
+ society: 'https://news.daum.net/society',
1875
+ culture: 'https://news.daum.net/culture',
1876
+ tech: 'https://news.daum.net/digital',
1877
+ sports: 'https://sports.daum.net/',
1878
+ entertainment: 'https://entertain.daum.net/',
1879
+ };
1880
+ const url = categoryUrls[category] || categoryUrls.general;
1881
+ const response = await axios.get(url, {
1882
+ headers: {
1883
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
1884
+ 'Accept-Language': 'ko-KR,ko;q=0.9',
1885
+ },
1886
+ timeout: 8000,
1887
+ });
1888
+ const $ = cheerio.load(response.data);
1889
+ const news = [];
1890
+ $('[class*="link_txt"], .tit_g, .news_view, .txt_info').each((i, el) => {
1891
+ if (news.length >= 10)
1892
+ return false;
1893
+ const headline = $(el).text().trim();
1894
+ if (headline && headline.length > 10 && headline.length < 100) {
1895
+ if (!news.find(n => n.headline === headline)) {
1896
+ news.push({
1897
+ headline,
1898
+ source: '다음뉴스',
1899
+ sentiment: analyzeSentiment(headline),
1900
+ });
1901
+ }
1902
+ }
1903
+ });
1904
+ return news;
1905
+ }
1906
+ // 감성 분석 (간단 버전)
1907
+ function analyzeSentiment(text) {
1908
+ const positiveWords = /성공|상승|호조|기대|돌파|신기록|수상|인기|사랑|행복|좋은|최고|혁신|성장/;
1909
+ const negativeWords = /하락|위기|우려|실패|폭락|충격|논란|피해|사망|사고|비난|급락|위험|문제/;
1910
+ if (positiveWords.test(text))
1911
+ return 'positive';
1912
+ if (negativeWords.test(text))
1913
+ return 'negative';
1914
+ return 'neutral';
1915
+ }
1916
+ // 뉴스 소스 감지
1917
+ function detectNewsSource(url, category) {
1918
+ if (url.includes('sports'))
1919
+ return '스포츠';
1920
+ if (url.includes('entertain'))
1921
+ return '연예';
1922
+ const sources = {
1923
+ politics: '정치',
1924
+ economy: '경제',
1925
+ society: '사회',
1926
+ culture: '문화',
1927
+ tech: 'IT/과학',
1928
+ sports: '스포츠',
1929
+ entertainment: '연예',
1930
+ };
1931
+ return sources[category] || '종합';
1932
+ }
1933
+ // 텍스트에서 키워드 추출
1934
+ function extractKeywordsFromText(text) {
1935
+ // 특수문자 제거 후 2-6자 단어만 추출
1936
+ const words = text
1937
+ .replace(/[^\w\sㄱ-ㅎㅏ-ㅣ가-힣]/g, ' ')
1938
+ .split(/\s+/)
1939
+ .filter(w => w.length >= 2 && w.length <= 6)
1940
+ .filter(w => !/^\d+$/.test(w)); // 숫자만 있는 것 제외
1941
+ return words;
1942
+ }
1943
+ // 키워드 빈도 계산
1944
+ function calculateKeywordFrequency(words) {
1945
+ const frequency = {};
1946
+ words.forEach(word => {
1947
+ frequency[word] = (frequency[word] || 0) + 1;
1948
+ });
1949
+ return Object.entries(frequency)
1950
+ .sort((a, b) => b[1] - a[1])
1951
+ .slice(0, 15)
1952
+ .map(([keyword, freq]) => ({
1953
+ keyword,
1954
+ frequency: freq,
1955
+ trend: freq >= 5 ? '상승' : freq >= 3 ? '유지' : '일반',
1956
+ }));
1957
+ }
1958
+ // 뉴스 기반 콘텐츠 기회 생성
1959
+ function generateNewsContentOpportunities(keywords, category) {
1960
+ const opportunities = [];
1961
+ if (keywords.length > 0) {
1962
+ const topKeyword = keywords[0]?.keyword || '';
1963
+ opportunities.push(`"${topKeyword}" 관련 콘텐츠 수요 증가 - 해설/분석 콘텐츠 추천`);
1964
+ }
1965
+ const categoryOpportunities = {
1966
+ tech: ['AI/테크 트렌드 정리 콘텐츠', '신제품 리뷰 콘텐츠'],
1967
+ economy: ['재테크 팁 콘텐츠', '경제 뉴스 쉽게 풀어주기'],
1968
+ entertainment: ['K-콘텐츠 글로벌 화제', '연예 이슈 정리'],
1969
+ sports: ['경기 하이라이트', '선수 인터뷰 분석'],
1970
+ general: ['오늘의 이슈 정리', '트렌드 분석 콘텐츠'],
1971
+ };
1972
+ opportunities.push(...(categoryOpportunities[category] || categoryOpportunities.general));
1973
+ return opportunities.slice(0, 5);
1974
+ }
1975
+ // 뉴스 Fallback 데이터
1976
+ function getNewsFallback(category) {
1977
+ const now = new Date();
1978
+ const dateStr = now.toISOString().split('T')[0];
1979
+ const fallbackData = {
1311
1980
  general: [
1312
- { headline: "AI 기술 발전과 일자리 변화", source: "종합", sentiment: "neutral" },
1313
- { headline: "글로벌 경제 동향 분석", source: "경제", sentiment: "neutral" },
1314
- { headline: "2025년 트렌드 전망", source: "라이프", sentiment: "positive" },
1981
+ { headline: `${dateStr} 오늘의 주요 뉴스`, source: "종합", sentiment: "neutral" },
1982
+ { headline: "AI 기술 발전과 산업 변화", source: "종합", sentiment: "neutral" },
1983
+ { headline: "글로벌 경제 동향", source: "종합", sentiment: "neutral" },
1315
1984
  ],
1316
1985
  tech: [
1317
- { headline: "ChatGPT 신기능 출시", source: "테크", sentiment: "positive" },
1318
- { headline: "애플 신제품 발표", source: "테크", sentiment: "positive" },
1319
- { headline: "사이버 보안 위협 증가", source: "테크", sentiment: "negative" },
1986
+ { headline: "AI 서비스 업데이트 소식", source: "IT/과학", sentiment: "positive" },
1987
+ { headline: "테크 기업 신제품 발표", source: "IT/과학", sentiment: "positive" },
1988
+ { headline: "사이버 보안 동향", source: "IT/과학", sentiment: "neutral" },
1320
1989
  ],
1321
1990
  economy: [
1322
- { headline: "한국은행 금리 결정", source: "경제", sentiment: "neutral" },
1323
- { headline: "부동산 시장 동향", source: "경제", sentiment: "neutral" },
1324
- { headline: "환율 변동성 확대", source: "경제", sentiment: "negative" },
1991
+ { headline: "금융 시장 동향", source: "경제", sentiment: "neutral" },
1992
+ { headline: "부동산 시장 분석", source: "경제", sentiment: "neutral" },
1993
+ { headline: "환율 변동 현황", source: "경제", sentiment: "neutral" },
1325
1994
  ],
1326
1995
  entertainment: [
1327
1996
  { headline: "K-POP 글로벌 인기", source: "연예", sentiment: "positive" },
1328
- { headline: "넷플릭스 신작 화제", source: "연예", sentiment: "positive" },
1329
- { headline: "연예계 이슈", source: "연예", sentiment: "neutral" },
1997
+ { headline: "드라마/예능 화제작", source: "연예", sentiment: "positive" },
1998
+ { headline: "연예계 소식", source: "연예", sentiment: "neutral" },
1330
1999
  ],
1331
2000
  };
1332
- const news = newsData[category] || newsData.general;
1333
- const keywords = extractKeywords ? [
1334
- { keyword: "AI", frequency: 45, trend: "상승" },
1335
- { keyword: "경제", frequency: 38, trend: "유지" },
1336
- { keyword: "트렌드", frequency: 32, trend: "상승" },
1337
- { keyword: "투자", frequency: 28, trend: "상승" },
1338
- { keyword: "기술", frequency: 25, trend: "상승" },
1339
- ] : [];
1340
- return {
1341
- category,
1342
- time_range: timeRange,
1343
- analyzed_at: new Date().toISOString(),
1344
- top_news: news,
1345
- extracted_keywords: keywords,
1346
- sentiment_summary: {
1347
- positive: "35%",
1348
- neutral: "50%",
1349
- negative: "15%",
1350
- },
1351
- content_opportunities: [
1352
- "AI 관련 콘텐츠 수요 증가 - 입문자 가이드 추천",
1353
- "경제 불안 심리 반영 - 재테크 팁 콘텐츠",
1354
- "K-콘텐츠 글로벌 화제 - 한류 관련 콘텐츠",
1355
- ],
1356
- trending_topics: news.map(n => n.headline),
1357
- };
2001
+ return fallbackData[category] || fallbackData.general;
1358
2002
  }
1359
2003
  // 해시태그 전략 생성
1360
2004
  function generateAdvancedHashtagStrategy(topic, platform, count, includeKorean, includeEnglish) {
@@ -1600,11 +2244,635 @@ function getMonthlyFocus() {
1600
2244
  return focuses[month] || focuses[1];
1601
2245
  }
1602
2246
  // =============================================================================
2247
+ // Tool 13: 썸네일 분석 (analyze_thumbnail) - v2.5 신규
2248
+ // =============================================================================
2249
+ server.tool("analyze_thumbnail", "YouTube/Instagram 썸네일 컨셉을 분석하고 개선점을 제안합니다. 클릭률 최적화 가이드를 제공합니다.", {
2250
+ title: z.string().describe("콘텐츠 제목"),
2251
+ thumbnail_description: z.string().describe("썸네일 설명 (예: 놀란 표정의 사람, 음식 클로즈업)"),
2252
+ platform: z.enum(["youtube", "instagram", "tiktok", "blog"]).describe("플랫폼"),
2253
+ content_category: z.string().optional().describe("콘텐츠 카테고리 (예: 먹방, 뷰티, 테크)"),
2254
+ }, async ({ title, thumbnail_description, platform, content_category = "일반" }) => {
2255
+ try {
2256
+ const analysis = analyzeThumbnailConcept(title, thumbnail_description, platform, content_category);
2257
+ return {
2258
+ content: [{ type: "text", text: JSON.stringify(analysis, null, 2) }],
2259
+ };
2260
+ }
2261
+ catch (error) {
2262
+ return {
2263
+ content: [{ type: "text", text: `썸네일 분석 중 오류 발생: ${error}` }],
2264
+ isError: true,
2265
+ };
2266
+ }
2267
+ });
2268
+ // =============================================================================
2269
+ // Tool 14: 스크립트/대본 아웃라인 생성 (generate_script_outline) - v2.5 신규
2270
+ // =============================================================================
2271
+ server.tool("generate_script_outline", "유튜브, 팟캐스트, 릴스용 스크립트 아웃라인을 자동 생성합니다.", {
2272
+ topic: z.string().describe("콘텐츠 주제"),
2273
+ format: z.enum(["youtube_long", "youtube_short", "podcast", "reels", "tiktok", "live"]).describe("콘텐츠 형식"),
2274
+ duration: z.string().optional().describe("예상 길이 (예: 10분, 30초)"),
2275
+ style: z.enum(["educational", "entertainment", "storytelling", "review", "tutorial", "interview", "vlog"]).optional().describe("스타일"),
2276
+ include_hooks: z.boolean().optional().describe("오프닝 훅 포함. 기본값: true"),
2277
+ }, async ({ topic, format, duration, style = "educational", include_hooks = true }) => {
2278
+ try {
2279
+ const outline = generateScriptOutline(topic, format, duration, style, include_hooks);
2280
+ return {
2281
+ content: [{ type: "text", text: JSON.stringify(outline, null, 2) }],
2282
+ };
2283
+ }
2284
+ catch (error) {
2285
+ return {
2286
+ content: [{ type: "text", text: `스크립트 생성 중 오류 발생: ${error}` }],
2287
+ isError: true,
2288
+ };
2289
+ }
2290
+ });
2291
+ // =============================================================================
2292
+ // Tool 15: 콘텐츠 리퍼포징 (repurpose_content) - v2.5 신규
2293
+ // =============================================================================
2294
+ server.tool("repurpose_content", "하나의 콘텐츠를 여러 플랫폼용으로 변환하는 전략을 제안합니다.", {
2295
+ original_content: z.string().describe("원본 콘텐츠 (제목 또는 설명)"),
2296
+ source_platform: z.enum(["youtube", "blog", "podcast", "instagram", "newsletter"]).describe("원본 플랫폼"),
2297
+ target_platforms: z.array(z.enum(["youtube", "youtube_shorts", "instagram_post", "instagram_reels", "tiktok", "blog", "newsletter", "twitter", "threads", "linkedin"])).describe("변환할 플랫폼 목록"),
2298
+ }, async ({ original_content, source_platform, target_platforms }) => {
2299
+ try {
2300
+ const repurposed = repurposeContent(original_content, source_platform, target_platforms);
2301
+ return {
2302
+ content: [{ type: "text", text: JSON.stringify(repurposed, null, 2) }],
2303
+ };
2304
+ }
2305
+ catch (error) {
2306
+ return {
2307
+ content: [{ type: "text", text: `리퍼포징 전략 생성 중 오류 발생: ${error}` }],
2308
+ isError: true,
2309
+ };
2310
+ }
2311
+ });
2312
+ // =============================================================================
2313
+ // Tool 16: 인플루언서 콜라보 분석 (analyze_influencer_collab) - v2.5 신규
2314
+ // =============================================================================
2315
+ server.tool("analyze_influencer_collab", "인플루언서 협업 전략 및 적합도를 분석합니다. 브랜드-인플루언서 매칭 가이드를 제공합니다.", {
2316
+ brand_category: z.string().describe("브랜드/제품 카테고리"),
2317
+ target_audience: z.string().describe("타겟 오디언스"),
2318
+ budget_range: z.enum(["low", "medium", "high", "premium"]).optional().describe("예산 범위"),
2319
+ campaign_goal: z.enum(["awareness", "engagement", "conversion", "content"]).optional().describe("캠페인 목표"),
2320
+ }, async ({ brand_category, target_audience, budget_range = "medium", campaign_goal = "engagement" }) => {
2321
+ try {
2322
+ const analysis = analyzeInfluencerCollab(brand_category, target_audience, budget_range, campaign_goal);
2323
+ return {
2324
+ content: [{ type: "text", text: JSON.stringify(analysis, null, 2) }],
2325
+ };
2326
+ }
2327
+ catch (error) {
2328
+ return {
2329
+ content: [{ type: "text", text: `인플루언서 분석 중 오류 발생: ${error}` }],
2330
+ isError: true,
2331
+ };
2332
+ }
2333
+ });
2334
+ // =============================================================================
2335
+ // Tool 17: 콘텐츠 성과 예측 (predict_content_performance) - v2.5 신규
2336
+ // =============================================================================
2337
+ server.tool("predict_content_performance", "콘텐츠의 예상 성과를 AI 기반으로 예측합니다. 조회수, 참여율, 공유 가능성을 분석합니다.", {
2338
+ title: z.string().describe("콘텐츠 제목"),
2339
+ description: z.string().optional().describe("콘텐츠 설명"),
2340
+ platform: ContentTypeSchema.describe("플랫폼"),
2341
+ category: z.string().optional().describe("카테고리"),
2342
+ posting_time: z.string().optional().describe("게시 예정 시간 (예: 평일 저녁 7시)"),
2343
+ has_trending_topic: z.boolean().optional().describe("트렌딩 주제 포함 여부"),
2344
+ }, async ({ title, description = "", platform, category = "일반", posting_time, has_trending_topic = false }) => {
2345
+ try {
2346
+ const prediction = predictContentPerformance(title, description, platform, category, posting_time, has_trending_topic);
2347
+ return {
2348
+ content: [{ type: "text", text: JSON.stringify(prediction, null, 2) }],
2349
+ };
2350
+ }
2351
+ catch (error) {
2352
+ return {
2353
+ content: [{ type: "text", text: `성과 예측 중 오류 발생: ${error}` }],
2354
+ isError: true,
2355
+ };
2356
+ }
2357
+ });
2358
+ // =============================================================================
2359
+ // v2.5 신규 Helper Functions
2360
+ // =============================================================================
2361
+ // 썸네일 분석
2362
+ function analyzeThumbnailConcept(title, description, platform, category) {
2363
+ const elements = {
2364
+ face_detected: /얼굴|표정|사람|인물/.test(description),
2365
+ text_overlay: /텍스트|글자|문구/.test(description),
2366
+ bright_colors: /밝은|화려한|눈에 띄는|빨간|노란/.test(description),
2367
+ food_closeup: /음식|클로즈업|맛있/.test(description),
2368
+ before_after: /비포|애프터|전후|변화/.test(description),
2369
+ arrow_pointing: /화살표|포인팅|가리키/.test(description),
2370
+ emoji_use: /이모지|이모티콘/.test(description),
2371
+ };
2372
+ let score = 50;
2373
+ const improvements = [];
2374
+ const strengths = [];
2375
+ // 얼굴 감지 (유튜브에서 중요)
2376
+ if (elements.face_detected) {
2377
+ score += 15;
2378
+ strengths.push("얼굴/표정이 포함되어 있어 클릭률 상승 기대");
2379
+ }
2380
+ else {
2381
+ improvements.push("사람의 얼굴이나 표정을 추가하면 CTR 15-30% 상승");
2382
+ }
2383
+ // 텍스트 오버레이
2384
+ if (elements.text_overlay) {
2385
+ score += 10;
2386
+ strengths.push("텍스트 오버레이로 핵심 메시지 전달");
2387
+ }
2388
+ else {
2389
+ improvements.push("핵심 키워드 2-3개를 텍스트로 추가");
2390
+ }
2391
+ // 밝은 색상
2392
+ if (elements.bright_colors) {
2393
+ score += 8;
2394
+ strengths.push("눈에 띄는 색상 사용");
2395
+ }
2396
+ else {
2397
+ improvements.push("노란색, 빨간색 등 눈에 띄는 색상 활용");
2398
+ }
2399
+ // 플랫폼별 추가 점수
2400
+ if (platform === "youtube" && elements.face_detected)
2401
+ score += 5;
2402
+ if (platform === "instagram" && elements.bright_colors)
2403
+ score += 5;
2404
+ const platformBestPractices = {
2405
+ youtube: [
2406
+ "1280x720 이상의 해상도 사용",
2407
+ "얼굴은 프레임의 1/3 이상 차지",
2408
+ "텍스트는 3-5단어 이내",
2409
+ "대비가 강한 색상 조합",
2410
+ "호기심 자극하는 표정/포즈",
2411
+ ],
2412
+ instagram: [
2413
+ "1:1 또는 4:5 비율 권장",
2414
+ "밝고 따뜻한 톤",
2415
+ "일관된 필터/색감",
2416
+ "미니멀한 구도",
2417
+ "브랜드 컬러 활용",
2418
+ ],
2419
+ tiktok: [
2420
+ "9:16 세로 비율 필수",
2421
+ "첫 0.5초 내 시선 집중",
2422
+ "트렌디한 비주얼",
2423
+ "빠른 동작/표정",
2424
+ ],
2425
+ };
2426
+ const categoryTips = {
2427
+ 먹방: ["음식 클로즈업 + 김 오르는 장면", "먹는 표정 강조", "양 많아 보이게"],
2428
+ 뷰티: ["비포-애프터 구도", "제품 + 결과물", "깨끗한 피부 강조"],
2429
+ 테크: ["제품 + 손 포함", "스펙 텍스트 오버레이", "미래지향적 느낌"],
2430
+ 브이로그: ["자연스러운 표정", "장소가 드러나는 구도", "감성적 색감"],
2431
+ 교육: ["핵심 포인트 텍스트", "진지한 표정", "전문가 느낌"],
2432
+ };
2433
+ return {
2434
+ title,
2435
+ platform,
2436
+ category,
2437
+ thumbnail_score: Math.min(score, 100),
2438
+ grade: score >= 85 ? "A (매우 우수)" : score >= 70 ? "B (우수)" : score >= 55 ? "C (보통)" : "D (개선 필요)",
2439
+ detected_elements: elements,
2440
+ strengths,
2441
+ improvements,
2442
+ platform_best_practices: platformBestPractices[platform] || platformBestPractices.youtube,
2443
+ category_specific_tips: categoryTips[category] || ["카테고리에 맞는 시각적 요소 강조", "타겟 오디언스가 관심 가질 요소 포함"],
2444
+ color_psychology: {
2445
+ red: "긴급함, 열정 - 할인, 긴급 콘텐츠",
2446
+ yellow: "주목, 행복 - 정보성 콘텐츠",
2447
+ blue: "신뢰, 전문성 - 교육, 테크",
2448
+ green: "건강, 자연 - 웰빙, 에코",
2449
+ orange: "에너지, 창의성 - 엔터테인먼트",
2450
+ },
2451
+ ctr_prediction: {
2452
+ current: `${score >= 70 ? "높음" : score >= 50 ? "보통" : "낮음"}`,
2453
+ potential_with_improvements: "높음 (5-10% CTR 예상)",
2454
+ },
2455
+ };
2456
+ }
2457
+ // 스크립트 아웃라인 생성
2458
+ function generateScriptOutline(topic, format, duration, style, includeHooks) {
2459
+ const formatSettings = {
2460
+ youtube_long: {
2461
+ recommended_duration: "8-15분",
2462
+ sections: ["인트로", "훅", "본문1", "본문2", "본문3", "정리", "CTA", "아웃트로"],
2463
+ hook_time: "0-30초",
2464
+ },
2465
+ youtube_short: {
2466
+ recommended_duration: "30-60초",
2467
+ sections: ["훅", "핵심 포인트", "CTA"],
2468
+ hook_time: "0-3초",
2469
+ },
2470
+ podcast: {
2471
+ recommended_duration: "20-45분",
2472
+ sections: ["인트로", "주제 소개", "본론1", "본론2", "질문/토론", "정리", "아웃트로"],
2473
+ hook_time: "0-60초",
2474
+ },
2475
+ reels: {
2476
+ recommended_duration: "15-30초",
2477
+ sections: ["훅", "메인 콘텐츠", "반전/CTA"],
2478
+ hook_time: "0-1초",
2479
+ },
2480
+ tiktok: {
2481
+ recommended_duration: "15-60초",
2482
+ sections: ["훅", "스토리", "포인트", "CTA"],
2483
+ hook_time: "0-1초",
2484
+ },
2485
+ live: {
2486
+ recommended_duration: "30-60분",
2487
+ sections: ["인사", "오늘의 주제", "메인 콘텐츠", "Q&A", "마무리"],
2488
+ hook_time: "0-5분",
2489
+ },
2490
+ };
2491
+ const settings = formatSettings[format] || formatSettings.youtube_long;
2492
+ const hooks = [
2493
+ `"${topic}에 대해 이런 건 몰랐을 거예요"`,
2494
+ `"오늘 알려드릴 ${topic} 팁, 진짜 중요합니다"`,
2495
+ `"${topic} 관련해서 가장 많이 받는 질문이에요"`,
2496
+ `"이거 알고 나면 ${topic}이 완전 달라집니다"`,
2497
+ `"3년간 ${topic} 하면서 깨달은 것들"`,
2498
+ ];
2499
+ const styleGuides = {
2500
+ educational: {
2501
+ tone: "전문적이지만 친근하게",
2502
+ structure: "문제 → 해결책 → 실습",
2503
+ tips: ["전문 용어는 쉽게 풀어서 설명", "실제 예시 풍부하게", "요약 정리 포함"],
2504
+ },
2505
+ entertainment: {
2506
+ tone: "활기차고 재미있게",
2507
+ structure: "훅 → 스토리 → 반전",
2508
+ tips: ["유머 포인트 삽입", "빠른 템포", "시청자 참여 유도"],
2509
+ },
2510
+ storytelling: {
2511
+ tone: "감성적이고 몰입감 있게",
2512
+ structure: "도입 → 갈등 → 해결",
2513
+ tips: ["개인적 경험 공유", "감정선 구축", "교훈으로 마무리"],
2514
+ },
2515
+ review: {
2516
+ tone: "객관적이고 솔직하게",
2517
+ structure: "소개 → 장점 → 단점 → 총평",
2518
+ tips: ["구체적 스펙/기능 언급", "비교 대상 제시", "별점/추천도"],
2519
+ },
2520
+ tutorial: {
2521
+ tone: "차분하고 명확하게",
2522
+ structure: "개요 → 단계별 설명 → 마무리",
2523
+ tips: ["화면 보며 따라할 수 있게", "실수 포인트 미리 안내", "팁 추가"],
2524
+ },
2525
+ };
2526
+ const outline = {
2527
+ topic,
2528
+ format,
2529
+ recommended_duration: duration || settings.recommended_duration,
2530
+ style,
2531
+ style_guide: styleGuides[style] || styleGuides.educational,
2532
+ hook_examples: includeHooks ? hooks : [],
2533
+ sections: [],
2534
+ };
2535
+ settings.sections.forEach((section, index) => {
2536
+ const sectionDetail = {
2537
+ order: index + 1,
2538
+ name: section,
2539
+ estimated_time: calculateSectionTime(section, format),
2540
+ key_points: [],
2541
+ script_template: "",
2542
+ };
2543
+ if (section === "인트로") {
2544
+ sectionDetail.key_points = ["채널 소개", "오늘의 주제 예고", "시청 이유 제시"];
2545
+ sectionDetail.script_template = `안녕하세요, [채널명]입니다. 오늘은 ${topic}에 대해 이야기해볼게요.`;
2546
+ }
2547
+ else if (section === "훅") {
2548
+ sectionDetail.key_points = ["호기심 자극", "문제 제기", "결과 미리보기"];
2549
+ sectionDetail.script_template = hooks[0];
2550
+ }
2551
+ else if (section.includes("본문") || section.includes("본론")) {
2552
+ sectionDetail.key_points = [`${topic}의 핵심 포인트`, "구체적 예시", "실용적 팁"];
2553
+ sectionDetail.script_template = `[핵심 내용]에 대해 자세히 설명드릴게요...`;
2554
+ }
2555
+ else if (section === "CTA") {
2556
+ sectionDetail.key_points = ["구독/좋아요 요청", "다음 영상 예고", "댓글 유도"];
2557
+ sectionDetail.script_template = "이 영상이 도움이 되셨다면 구독과 좋아요 부탁드려요!";
2558
+ }
2559
+ else if (section === "아웃트로") {
2560
+ sectionDetail.key_points = ["핵심 요약", "감사 인사", "다음 콘텐츠 예고"];
2561
+ sectionDetail.script_template = `오늘 ${topic}에 대해 알아봤는데요, 도움이 되셨길 바랍니다.`;
2562
+ }
2563
+ outline.sections.push(sectionDetail);
2564
+ });
2565
+ outline.production_tips = {
2566
+ filming: ["조명은 자연광 또는 3점 조명", "음질이 화질보다 중요", "배경 정리"],
2567
+ editing: ["점프컷으로 템포 유지", "자막 필수", "BGM 볼륨은 음성의 10-20%"],
2568
+ thumbnail: ["제목과 연계된 이미지", "얼굴 표정 강조", "텍스트 3-5단어"],
2569
+ };
2570
+ return outline;
2571
+ }
2572
+ function calculateSectionTime(section, format) {
2573
+ const times = {
2574
+ youtube_long: {
2575
+ 인트로: "30초-1분",
2576
+ 훅: "30초",
2577
+ 본문1: "3-4분",
2578
+ 본문2: "3-4분",
2579
+ 본문3: "2-3분",
2580
+ 정리: "1분",
2581
+ CTA: "30초",
2582
+ 아웃트로: "30초",
2583
+ },
2584
+ youtube_short: { 훅: "3초", "핵심 포인트": "20-40초", CTA: "5초" },
2585
+ reels: { 훅: "1초", "메인 콘텐츠": "20초", "반전/CTA": "5초" },
2586
+ tiktok: { 훅: "1초", 스토리: "15-30초", 포인트: "10초", CTA: "3초" },
2587
+ };
2588
+ return times[format]?.[section] || "적절히 조절";
2589
+ }
2590
+ // 콘텐츠 리퍼포징
2591
+ function repurposeContent(original, source, targets) {
2592
+ const repurposingStrategies = {
2593
+ youtube_shorts: {
2594
+ approach: "핵심 하이라이트 추출",
2595
+ format: "세로 9:16",
2596
+ duration: "60초 이내",
2597
+ tips: ["가장 임팩트 있는 장면 선택", "자막 필수", "훅으로 시작"],
2598
+ },
2599
+ instagram_post: {
2600
+ approach: "핵심 포인트 카드뉴스화",
2601
+ format: "1:1 또는 4:5 캐러셀",
2602
+ tips: ["10장 이내 슬라이드", "각 슬라이드 하나의 포인트", "마지막에 CTA"],
2603
+ },
2604
+ instagram_reels: {
2605
+ approach: "15-30초 하이라이트",
2606
+ format: "세로 9:16",
2607
+ tips: ["트렌딩 오디오 활용", "빠른 컷 편집", "캡션에 풀버전 링크"],
2608
+ },
2609
+ tiktok: {
2610
+ approach: "바이럴 포인트 추출",
2611
+ format: "세로 9:16, 15-60초",
2612
+ tips: ["트렌딩 사운드 필수", "첫 1초 승부", "댓글 유도형 마무리"],
2613
+ },
2614
+ blog: {
2615
+ approach: "상세 텍스트 버전 작성",
2616
+ format: "2000-3000자 글",
2617
+ tips: ["SEO 키워드 포함", "H2/H3 구조화", "이미지 5-10개"],
2618
+ },
2619
+ newsletter: {
2620
+ approach: "핵심 인사이트 요약",
2621
+ format: "800-1200자",
2622
+ tips: ["개인적인 톤", "actionable 팁", "다음 호 예고"],
2623
+ },
2624
+ twitter: {
2625
+ approach: "핵심 문장 + 스레드",
2626
+ format: "280자 × 여러 개",
2627
+ tips: ["첫 트윗이 핵심", "숫자/통계 활용", "마지막에 원본 링크"],
2628
+ },
2629
+ threads: {
2630
+ approach: "대화형 스레드",
2631
+ format: "500자 이내 × 여러 개",
2632
+ tips: ["스토리텔링 형식", "이미지 함께", "인스타 연동"],
2633
+ },
2634
+ linkedin: {
2635
+ approach: "전문적 인사이트 버전",
2636
+ format: "1000-1500자",
2637
+ tips: ["전문성 강조", "데이터/결과 중심", "업계 해시태그"],
2638
+ },
2639
+ };
2640
+ const results = targets.map(target => ({
2641
+ platform: target,
2642
+ strategy: repurposingStrategies[target] || { approach: "플랫폼에 맞게 변환", tips: [] },
2643
+ adapted_title: adaptTitleForPlatform(original, target),
2644
+ content_adjustments: getContentAdjustments(source, target),
2645
+ estimated_effort: getEffortEstimate(source, target),
2646
+ priority: getPriorityScore(target),
2647
+ }));
2648
+ return {
2649
+ original_content: original,
2650
+ source_platform: source,
2651
+ repurposing_plan: results.sort((a, b) => b.priority - a.priority),
2652
+ workflow_tip: "고품질 원본 하나로 5-7개 플랫폼 커버 가능",
2653
+ time_saving: "평균 60-70% 시간 절약",
2654
+ recommended_order: results.map(r => r.platform),
2655
+ };
2656
+ }
2657
+ function adaptTitleForPlatform(original, platform) {
2658
+ const adaptations = {
2659
+ youtube_shorts: `${original} #shorts`,
2660
+ instagram_post: original.length > 50 ? original.substring(0, 47) + "..." : original,
2661
+ instagram_reels: `${original} 🔥`,
2662
+ tiktok: `${original} 알려줌`,
2663
+ twitter: original.length > 100 ? original.substring(0, 97) + "..." : original,
2664
+ threads: original,
2665
+ linkedin: `[인사이트] ${original}`,
2666
+ blog: `${original} - 완벽 가이드`,
2667
+ newsletter: `📧 ${original}`,
2668
+ };
2669
+ return adaptations[platform] || original;
2670
+ }
2671
+ function getContentAdjustments(source, target) {
2672
+ const adjustments = [];
2673
+ if (source === "youtube" && target.includes("short")) {
2674
+ adjustments.push("긴 영상에서 핵심 15-60초 추출");
2675
+ }
2676
+ if (target === "blog") {
2677
+ adjustments.push("영상 스크립트를 글로 확장");
2678
+ adjustments.push("스크린샷 추가");
2679
+ }
2680
+ if (target.includes("instagram")) {
2681
+ adjustments.push("비주얼 중심으로 재구성");
2682
+ adjustments.push("해시태그 20-25개 추가");
2683
+ }
2684
+ return adjustments.length > 0 ? adjustments : ["플랫폼 특성에 맞게 조정"];
2685
+ }
2686
+ function getEffortEstimate(source, target) {
2687
+ if (source === target)
2688
+ return "0분";
2689
+ if (target.includes("short") || target.includes("reels"))
2690
+ return "15-30분";
2691
+ if (target === "blog")
2692
+ return "1-2시간";
2693
+ if (target === "newsletter")
2694
+ return "30분-1시간";
2695
+ return "20-40분";
2696
+ }
2697
+ function getPriorityScore(platform) {
2698
+ const scores = {
2699
+ instagram_reels: 95,
2700
+ tiktok: 90,
2701
+ youtube_shorts: 88,
2702
+ instagram_post: 80,
2703
+ twitter: 70,
2704
+ threads: 65,
2705
+ linkedin: 60,
2706
+ blog: 75,
2707
+ newsletter: 70,
2708
+ };
2709
+ return scores[platform] || 50;
2710
+ }
2711
+ // 인플루언서 협업 분석
2712
+ function analyzeInfluencerCollab(category, audience, budget, goal) {
2713
+ const tierInfo = {
2714
+ nano: { followers: "1K-10K", engagement: "5-10%", cost: "10-50만원", pros: ["높은 참여율", "진정성", "저비용"], cons: ["도달 제한", "전문성 부족 가능"] },
2715
+ micro: { followers: "10K-50K", engagement: "3-8%", cost: "50-200만원", pros: ["좋은 참여율", "타겟 정확", "비용 효율"], cons: ["도달 중간", "협상 필요"] },
2716
+ mid: { followers: "50K-500K", engagement: "2-5%", cost: "200-1000만원", pros: ["넓은 도달", "전문성", "콘텐츠 품질"], cons: ["비용 상승", "광고 느낌"] },
2717
+ macro: { followers: "500K-1M", engagement: "1-3%", cost: "1000-3000만원", pros: ["큰 도달", "신뢰도", "브랜드 인지도"], cons: ["높은 비용", "낮은 참여율"] },
2718
+ mega: { followers: "1M+", engagement: "1-2%", cost: "3000만원+", pros: ["최대 도달", "화제성", "브랜드 이미지"], cons: ["매우 높은 비용", "진정성 의문"] },
2719
+ };
2720
+ const budgetTiers = {
2721
+ low: ["nano", "micro"],
2722
+ medium: ["micro", "mid"],
2723
+ high: ["mid", "macro"],
2724
+ premium: ["macro", "mega"],
2725
+ };
2726
+ const recommendedTiers = budgetTiers[budget] || ["micro", "mid"];
2727
+ const platformsByCategory = {
2728
+ 뷰티: ["인스타그램", "유튜브", "틱톡"],
2729
+ 패션: ["인스타그램", "유튜브"],
2730
+ 푸드: ["유튜브", "인스타그램", "블로그"],
2731
+ 테크: ["유튜브", "블로그"],
2732
+ 라이프스타일: ["인스타그램", "유튜브", "블로그"],
2733
+ 게임: ["유튜브", "트위치", "틱톡"],
2734
+ 육아: ["인스타그램", "블로그", "유튜브"],
2735
+ 여행: ["인스타그램", "유튜브", "블로그"],
2736
+ };
2737
+ const collabTypes = [
2738
+ { type: "제품 협찬", description: "제품 제공 + 솔직 리뷰", suitable_for: ["awareness", "content"] },
2739
+ { type: "유료 광고", description: "정해진 가이드라인 콘텐츠", suitable_for: ["awareness", "conversion"] },
2740
+ { type: "어필리에이트", description: "판매 수수료 기반", suitable_for: ["conversion"] },
2741
+ { type: "앰버서더", description: "장기 파트너십", suitable_for: ["awareness", "engagement"] },
2742
+ { type: "콘텐츠 공동제작", description: "함께 기획/제작", suitable_for: ["content", "engagement"] },
2743
+ ];
2744
+ return {
2745
+ brand_category: category,
2746
+ target_audience: audience,
2747
+ budget_range: budget,
2748
+ campaign_goal: goal,
2749
+ recommended_influencer_tiers: recommendedTiers.map(tier => ({
2750
+ tier,
2751
+ ...tierInfo[tier],
2752
+ })),
2753
+ recommended_platforms: platformsByCategory[category] || ["인스타그램", "유튜브"],
2754
+ suitable_collab_types: collabTypes.filter(c => c.suitable_for.includes(goal)),
2755
+ success_metrics: {
2756
+ awareness: ["도달수", "노출수", "브랜드 검색량"],
2757
+ engagement: ["좋아요", "댓글", "저장", "공유"],
2758
+ conversion: ["클릭수", "구매수", "ROAS"],
2759
+ content: ["콘텐츠 품질", "재사용 가능성"],
2760
+ }[goal] || ["도달수", "참여율"],
2761
+ negotiation_tips: [
2762
+ "명확한 KPI 설정",
2763
+ "콘텐츠 사용권 협의",
2764
+ "수정 횟수 명시",
2765
+ "게시 일정 확정",
2766
+ "성과 리포트 요청",
2767
+ ],
2768
+ red_flags: [
2769
+ "팔로워 대비 참여율 너무 낮음 (1% 미만)",
2770
+ "댓글이 대부분 이모지나 봇성",
2771
+ "최근 콘텐츠 업로드 없음",
2772
+ "브랜드 이미지와 맞지 않는 과거 콘텐츠",
2773
+ ],
2774
+ };
2775
+ }
2776
+ // 콘텐츠 성과 예측
2777
+ function predictContentPerformance(title, description, platform, category, postingTime, hasTrending) {
2778
+ let baseScore = 50;
2779
+ const factors = {};
2780
+ // 제목 분석
2781
+ const titleFactors = {
2782
+ has_numbers: /\d/.test(title),
2783
+ has_question: /\?/.test(title),
2784
+ has_emotional: /놀라운|충격|비밀|최고|완벽|필수|대박|꿀팁|진짜/.test(title),
2785
+ optimal_length: title.length >= 20 && title.length <= 60,
2786
+ has_brackets: /[\[\]【】]/.test(title),
2787
+ };
2788
+ factors.title = titleFactors;
2789
+ if (titleFactors.has_numbers)
2790
+ baseScore += 8;
2791
+ if (titleFactors.has_question)
2792
+ baseScore += 5;
2793
+ if (titleFactors.has_emotional)
2794
+ baseScore += 10;
2795
+ if (titleFactors.optimal_length)
2796
+ baseScore += 5;
2797
+ if (titleFactors.has_brackets)
2798
+ baseScore += 3;
2799
+ // 트렌딩 토픽
2800
+ if (hasTrending) {
2801
+ baseScore += 15;
2802
+ factors.trending_boost = true;
2803
+ }
2804
+ // 게시 시간
2805
+ const timeScores = {
2806
+ "평일 아침": 60,
2807
+ "평일 점심": 75,
2808
+ "평일 저녁": 90,
2809
+ "주말 오후": 85,
2810
+ "주말 저녁": 80,
2811
+ };
2812
+ if (postingTime) {
2813
+ const timeMatch = Object.keys(timeScores).find(t => postingTime.includes(t.split(" ")[1]));
2814
+ if (timeMatch) {
2815
+ baseScore += (timeScores[timeMatch] - 50) / 5;
2816
+ factors.posting_time_score = timeScores[timeMatch];
2817
+ }
2818
+ }
2819
+ // 플랫폼별 가중치
2820
+ const platformMultiplier = {
2821
+ tiktok: 1.2,
2822
+ instagram: 1.1,
2823
+ youtube: 1.0,
2824
+ blog: 0.9,
2825
+ twitter: 0.95,
2826
+ };
2827
+ baseScore *= platformMultiplier[platform] || 1;
2828
+ const finalScore = Math.min(Math.round(baseScore), 100);
2829
+ // 예상 성과
2830
+ const performanceRanges = {
2831
+ youtube: {
2832
+ high: { views: "10K-50K", engagement: "5-8%", shares: "100-500" },
2833
+ medium: { views: "1K-10K", engagement: "3-5%", shares: "20-100" },
2834
+ low: { views: "100-1K", engagement: "1-3%", shares: "5-20" },
2835
+ },
2836
+ instagram: {
2837
+ high: { reach: "5K-20K", engagement: "6-10%", saves: "50-200" },
2838
+ medium: { reach: "1K-5K", engagement: "3-6%", saves: "10-50" },
2839
+ low: { reach: "200-1K", engagement: "1-3%", saves: "2-10" },
2840
+ },
2841
+ tiktok: {
2842
+ high: { views: "50K-500K", engagement: "8-15%", shares: "500-2K" },
2843
+ medium: { views: "5K-50K", engagement: "5-8%", shares: "50-500" },
2844
+ low: { views: "500-5K", engagement: "2-5%", shares: "10-50" },
2845
+ },
2846
+ };
2847
+ const tier = finalScore >= 75 ? "high" : finalScore >= 50 ? "medium" : "low";
2848
+ const platformPerf = performanceRanges[platform] || performanceRanges.youtube;
2849
+ return {
2850
+ title,
2851
+ platform,
2852
+ category,
2853
+ performance_score: finalScore,
2854
+ grade: finalScore >= 85 ? "A (높은 성과 예상)" : finalScore >= 70 ? "B (좋은 성과 예상)" : finalScore >= 50 ? "C (보통)" : "D (개선 필요)",
2855
+ analysis_factors: factors,
2856
+ predicted_performance: platformPerf[tier],
2857
+ confidence_level: hasTrending ? "높음 (트렌딩 반영)" : "보통",
2858
+ optimization_suggestions: [
2859
+ !titleFactors.has_numbers ? "제목에 숫자 추가 (예: 5가지, TOP 10)" : null,
2860
+ !titleFactors.has_emotional ? "감정을 자극하는 단어 추가" : null,
2861
+ !titleFactors.optimal_length ? "제목 길이 20-60자 권장" : null,
2862
+ !hasTrending ? "트렌딩 키워드 연계 고려" : null,
2863
+ ].filter(Boolean),
2864
+ best_posting_windows: {
2865
+ weekday: "오전 7-9시, 점심 12-1시, 저녁 7-10시",
2866
+ weekend: "오후 2-4시, 저녁 7-9시",
2867
+ },
2868
+ };
2869
+ }
2870
+ // =============================================================================
1603
2871
  // Server Start
1604
2872
  // =============================================================================
1605
2873
  async function main() {
1606
2874
  const transport = new StdioServerTransport();
1607
2875
  await server.connect(transport);
1608
- console.error("Content Genie MCP Server v2.0 running on stdio");
2876
+ console.error("Content Genie MCP Server v2.5 running on stdio");
1609
2877
  }
1610
2878
  main().catch(console.error);