content-genie-mcp 2.9.0 → 2.9.2

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/Dockerfile ADDED
@@ -0,0 +1,29 @@
1
+ FROM node:20-alpine
2
+
3
+ WORKDIR /app
4
+
5
+ # package.json과 tsconfig.json 복사
6
+ COPY package*.json ./
7
+ COPY tsconfig.json ./
8
+
9
+ # 의존성 설치 (prepare 스크립트 무시)
10
+ RUN npm ci --ignore-scripts
11
+
12
+ # 소스 코드 복사
13
+ COPY src/ ./src/
14
+
15
+ # 빌드 실행
16
+ RUN npm run build
17
+
18
+ # 프로덕션 의존성만 남기기
19
+ RUN npm prune --production
20
+
21
+ # 환경 변수
22
+ ENV NODE_ENV=production
23
+ ENV MCP_HTTP_MODE=true
24
+ ENV PORT=3000
25
+
26
+ EXPOSE 3000
27
+
28
+ # 서버 실행
29
+ CMD ["node", "dist/index.js"]
package/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/content-genie-mcp.svg)](https://www.npmjs.com/package/content-genie-mcp)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![Wiki](https://img.shields.io/badge/docs-Wiki-blue.svg)](https://github.com/MUSE-CODE-SPACE/content-genie-mcp/wiki)
7
8
 
8
9
  Content Genie MCP는 블로거, 유튜버, 인스타그래머, 마케터를 위한 **17가지 강력한 도구**를 제공하는 MCP 서버입니다. 한국 시장에 특화된 트렌드 분석, 콘텐츠 아이디어 생성, SEO 최적화, 바이럴 예측, 인플루언서 협업 분석 기능을 제공합니다.
9
10
 
@@ -104,10 +105,25 @@ Content Genie MCP는 블로거, 유튜버, 인스타그래머, 마케터를 위
104
105
  claude mcp add content-genie-mcp -- npx -y content-genie-mcp
105
106
  ```
106
107
 
107
- ### PlayMCP
108
+ ### PlayMCP (카카오)
108
109
 
109
110
  [PlayMCP](https://playmcp.kakao.com)에서 "Content Genie"를 검색하여 도구함에 추가하세요.
110
111
 
112
+ > **참고**: PlayMCP는 등록 승인 후 사용 가능합니다. 현재 등록 진행 중입니다.
113
+
114
+ ## 문서
115
+
116
+ - **[Wiki 문서](https://github.com/MUSE-CODE-SPACE/content-genie-mcp/wiki)** - 상세 사용법 및 가이드
117
+ - [설치 가이드](https://github.com/MUSE-CODE-SPACE/content-genie-mcp/wiki/Installation)
118
+ - [빠른 시작](https://github.com/MUSE-CODE-SPACE/content-genie-mcp/wiki/Quick-Start)
119
+ - [17가지 도구 개요](https://github.com/MUSE-CODE-SPACE/content-genie-mcp/wiki/Tools-Overview)
120
+ - [API 레퍼런스](https://github.com/MUSE-CODE-SPACE/content-genie-mcp/wiki/API-Reference)
121
+ - **활용 가이드**
122
+ - [블로거 가이드](https://github.com/MUSE-CODE-SPACE/content-genie-mcp/wiki/Guide-Blogger)
123
+ - [유튜버 가이드](https://github.com/MUSE-CODE-SPACE/content-genie-mcp/wiki/Guide-YouTuber)
124
+ - [인스타그래머 가이드](https://github.com/MUSE-CODE-SPACE/content-genie-mcp/wiki/Guide-Instagram)
125
+ - [마케터 가이드](https://github.com/MUSE-CODE-SPACE/content-genie-mcp/wiki/Guide-Marketer)
126
+
111
127
  ## 도구 상세
112
128
 
113
129
  ### 1. get_korean_trends
package/dist/index.js CHANGED
@@ -1,15 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
4
5
  import { z } from "zod";
5
6
  import axios from "axios";
6
7
  import * as cheerio from "cheerio";
8
+ import express from "express";
9
+ import cors from "cors";
10
+ import crypto from "crypto";
7
11
  // =============================================================================
8
12
  // Content Genie MCP v2.5 - 한국 콘텐츠 크리에이터를 위한 AI 어시스턴트 (프로 버전)
9
13
  // =============================================================================
10
14
  const server = new McpServer({
11
15
  name: "content-genie-mcp",
12
- version: "2.7.0",
16
+ version: "2.9.2",
13
17
  });
14
18
  // =============================================================================
15
19
  // 공통 스키마
@@ -352,13 +356,13 @@ server.tool("optimize_title_hashtags", "AI 기반으로 콘텐츠 제목을 최
352
356
  // =============================================================================
353
357
  // Tool 4: SEO 키워드 분석 (analyze_seo_keywords) - 고도화
354
358
  // =============================================================================
355
- server.tool("analyze_seo_keywords", "키워드의 SEO 잠재력을 심층 분석하고 네이버/구글 최적화 전략을 제공합니다.", {
359
+ server.tool("analyze_seo_keywords", "키워드의 SEO 잠재력을 심층 분석하고 다음/네이버/구글 최적화 전략을 제공합니다.", {
356
360
  keyword: z.string().describe("분석할 메인 키워드"),
357
- search_engine: z.enum(["naver", "google", "both"]).optional().describe("검색엔진 (naver, google, both)"),
361
+ search_engine: z.enum(["daum", "naver", "google", "all"]).optional().describe("검색엔진 (daum, naver, google, all)"),
358
362
  include_questions: z.boolean().optional().describe("관련 질문 키워드 포함"),
359
363
  include_longtail: z.boolean().optional().describe("롱테일 키워드 포함"),
360
364
  competitor_analysis: z.boolean().optional().describe("경쟁 분석 포함"),
361
- }, async ({ keyword, search_engine = "both", include_questions = true, include_longtail = true, competitor_analysis = true }) => {
365
+ }, async ({ keyword, search_engine = "all", include_questions = true, include_longtail = true, competitor_analysis = true }) => {
362
366
  try {
363
367
  const analysis = await analyzeAdvancedSEOKeywords(keyword, search_engine, include_questions, include_longtail, competitor_analysis);
364
368
  return {
@@ -616,6 +620,31 @@ async function getGoogleAutocomplete(keyword) {
616
620
  return [];
617
621
  }
618
622
  }
623
+ // 다음 자동완성 키워드
624
+ async function getDaumAutocomplete(keyword) {
625
+ try {
626
+ const response = await axios.get(`https://suggest.search.daum.net/sushi/ns`, {
627
+ params: {
628
+ w: 'tot',
629
+ mod: 'json',
630
+ code: 'utf_in_out',
631
+ q: keyword,
632
+ },
633
+ headers: {
634
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
635
+ },
636
+ timeout: 5000,
637
+ });
638
+ // 응답 형식: {"q":"keyword","items":["suggestion1", "suggestion2", ...]}
639
+ if (response.data && Array.isArray(response.data.items)) {
640
+ return response.data.items.slice(0, 10);
641
+ }
642
+ return [];
643
+ }
644
+ catch {
645
+ return [];
646
+ }
647
+ }
619
648
  // 네이버 연관검색어 스크래핑
620
649
  async function getNaverRelatedKeywords(keyword) {
621
650
  try {
@@ -703,6 +732,34 @@ async function getGoogleSearchResultCount(keyword) {
703
732
  return 100000;
704
733
  }
705
734
  }
735
+ // 다음 검색 결과 수 추정
736
+ async function getDaumSearchResultCount(keyword) {
737
+ try {
738
+ const response = await axios.get(`https://search.daum.net/search`, {
739
+ params: {
740
+ w: 'blog',
741
+ q: keyword,
742
+ },
743
+ headers: {
744
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
745
+ },
746
+ timeout: 5000,
747
+ });
748
+ const $ = cheerio.load(response.data);
749
+ // 검색 결과 수 추출 시도
750
+ const countText = $('.sub_expander .txt_info, .cont_result .txt_info, [class*="count"]').first().text();
751
+ const match = countText.match(/[\d,]+/);
752
+ if (match) {
753
+ return parseInt(match[0].replace(/,/g, ''), 10);
754
+ }
755
+ // 대략적 추정: 검색 결과 아이템 수 기반
756
+ const itemCount = $('.wrap_cont.blog, .cont_blog').length;
757
+ return itemCount > 0 ? itemCount * 10000 : 50000;
758
+ }
759
+ catch {
760
+ return 50000; // 기본값
761
+ }
762
+ }
706
763
  // 검색량 레벨 추정 (자동완성 순위 기반)
707
764
  function estimateSearchVolume(autocompleteRank, resultCount) {
708
765
  // 자동완성 상위 + 결과 수 많음 = 검색량 높음
@@ -1982,20 +2039,26 @@ function calculateReadabilityScore(text) {
1982
2039
  // 고급 SEO 키워드 분석 - 실시간 데이터 기반
1983
2040
  async function analyzeAdvancedSEOKeywords(keyword, searchEngine, includeQuestions, includeLongtail, competitorAnalysis) {
1984
2041
  // 병렬로 실시간 데이터 수집
1985
- const [naverAutocomplete, googleAutocomplete, naverRelated, naverResultCount, googleResultCount] = await Promise.all([
1986
- searchEngine !== 'google' ? getNaverAutocomplete(keyword) : Promise.resolve([]),
1987
- searchEngine !== 'naver' ? getGoogleAutocomplete(keyword) : Promise.resolve([]),
2042
+ const includeNaver = searchEngine === 'naver' || searchEngine === 'all';
2043
+ const includeGoogle = searchEngine === 'google' || searchEngine === 'all';
2044
+ const includeDaum = searchEngine === 'daum' || searchEngine === 'all';
2045
+ const [naverAutocomplete, googleAutocomplete, daumAutocomplete, naverRelated, naverResultCount, googleResultCount, daumResultCount] = await Promise.all([
2046
+ includeNaver ? getNaverAutocomplete(keyword) : Promise.resolve([]),
2047
+ includeGoogle ? getGoogleAutocomplete(keyword) : Promise.resolve([]),
2048
+ includeDaum ? getDaumAutocomplete(keyword) : Promise.resolve([]),
1988
2049
  getNaverRelatedKeywords(keyword),
1989
- searchEngine !== 'google' ? getNaverSearchResultCount(keyword) : Promise.resolve(0),
1990
- searchEngine !== 'naver' ? getGoogleSearchResultCount(keyword) : Promise.resolve(0),
2050
+ includeNaver ? getNaverSearchResultCount(keyword) : Promise.resolve(0),
2051
+ includeGoogle ? getGoogleSearchResultCount(keyword) : Promise.resolve(0),
2052
+ includeDaum ? getDaumSearchResultCount(keyword) : Promise.resolve(0),
1991
2053
  ]);
1992
2054
  // 주요 검색 엔진 결과 수 선택
1993
2055
  const primaryResultCount = searchEngine === 'naver' ? naverResultCount :
1994
2056
  searchEngine === 'google' ? googleResultCount :
1995
- Math.max(naverResultCount, googleResultCount);
2057
+ searchEngine === 'daum' ? daumResultCount :
2058
+ Math.max(naverResultCount, googleResultCount, daumResultCount);
1996
2059
  // 경쟁도 및 검색량 추정
1997
2060
  const competition = estimateCompetition(primaryResultCount);
1998
- const autocompleteKeywords = [...new Set([...naverAutocomplete, ...googleAutocomplete])];
2061
+ const autocompleteKeywords = [...new Set([...daumAutocomplete, ...naverAutocomplete, ...googleAutocomplete])];
1999
2062
  const keywordRank = autocompleteKeywords.findIndex(k => k.includes(keyword)) + 1 || 10;
2000
2063
  const searchVolume = estimateSearchVolume(keywordRank, primaryResultCount);
2001
2064
  // SEO 난이도 계산
@@ -2138,6 +2201,18 @@ async function analyzeAdvancedSEOKeywords(keyword, searchEngine, includeQuestion
2138
2201
  ],
2139
2202
  content_types: ["웹사이트", "유튜브", "뉴스"],
2140
2203
  },
2204
+ daum: {
2205
+ result_count: daumResultCount.toLocaleString(),
2206
+ competition: estimateCompetition(daumResultCount).level,
2207
+ tips: [
2208
+ "다음 블로그/카페에 발행하세요",
2209
+ "카카오 채널과 연동 고려",
2210
+ "티스토리 블로그 활용 추천",
2211
+ "다음 뉴스 검색 노출 전략",
2212
+ "카카오톡 공유 최적화",
2213
+ ],
2214
+ content_types: ["티스토리", "다음카페", "브런치"],
2215
+ },
2141
2216
  };
2142
2217
  // 추천 액션 생성
2143
2218
  const recommendedAction = seoDifficulty > 70
@@ -2148,9 +2223,11 @@ async function analyzeAdvancedSEOKeywords(keyword, searchEngine, includeQuestion
2148
2223
  return {
2149
2224
  main_keyword: keyword,
2150
2225
  data_source: {
2226
+ daum_autocomplete: daumAutocomplete.length,
2151
2227
  naver_autocomplete: naverAutocomplete.length,
2152
2228
  google_autocomplete: googleAutocomplete.length,
2153
2229
  naver_related: naverRelated.length,
2230
+ daum_results: daumResultCount.toLocaleString(),
2154
2231
  naver_results: naverResultCount.toLocaleString(),
2155
2232
  google_results: googleResultCount.toLocaleString(),
2156
2233
  },
@@ -2167,7 +2244,7 @@ async function analyzeAdvancedSEOKeywords(keyword, searchEngine, includeQuestion
2167
2244
  related_keywords: relatedKeywords.slice(0, 15),
2168
2245
  question_keywords: questionKeywords.slice(0, 8),
2169
2246
  longtail_keywords: longtailKeywords.slice(0, 8),
2170
- search_engine_strategy: searchEngine === "both" ? searchEngineStrategy : searchEngineStrategy[searchEngine],
2247
+ search_engine_strategy: searchEngine === "all" ? searchEngineStrategy : searchEngineStrategy[searchEngine],
2171
2248
  content_recommendations: {
2172
2249
  ideal_length: seoDifficulty > 60 ? "4000-6000자 (경쟁 대응)" : "2500-4000자",
2173
2250
  must_include: ["정의", "방법", "예시", "FAQ", "비교"],
@@ -3903,8 +3980,131 @@ function predictContentPerformance(title, description, platform, category, posti
3903
3980
  // Server Start
3904
3981
  // =============================================================================
3905
3982
  async function main() {
3906
- const transport = new StdioServerTransport();
3907
- await server.connect(transport);
3908
- console.error("Content Genie MCP Server v2.5 running on stdio");
3983
+ const isHttpMode = process.env.MCP_HTTP_MODE === 'true' || process.argv.includes('--http');
3984
+ const port = parseInt(process.env.PORT || '3000', 10);
3985
+ if (isHttpMode) {
3986
+ // HTTP/SSE 모드 (PlayMCP, 웹 클라이언트용)
3987
+ console.log(`Starting Content Genie MCP Server v2.9.0 in HTTP mode on port ${port}...`);
3988
+ const app = express();
3989
+ app.use(cors());
3990
+ app.use(express.json());
3991
+ // 도구 목록 (PlayMCP 연결 확인용)
3992
+ const toolsList = [
3993
+ { name: "get_korean_trends", description: "다음/네이버 실시간 트렌드 조회" },
3994
+ { name: "analyze_news_trends", description: "뉴스 트렌드 분석" },
3995
+ { name: "get_seasonal_content_guide", description: "시즌 콘텐츠 가이드" },
3996
+ { name: "analyze_seo_keywords", description: "SEO 키워드 심층 분석" },
3997
+ { name: "generate_hashtag_strategy", description: "해시태그 전략 생성" },
3998
+ { name: "analyze_competitor_content", description: "경쟁사 콘텐츠 분석" },
3999
+ { name: "generate_content_ideas", description: "콘텐츠 아이디어 생성" },
4000
+ { name: "optimize_title_hashtags", description: "제목/해시태그 최적화" },
4001
+ { name: "create_content_calendar", description: "콘텐츠 캘린더 생성" },
4002
+ { name: "generate_script_outline", description: "스크립트 아웃라인 생성" },
4003
+ { name: "repurpose_content", description: "콘텐츠 리퍼포징" },
4004
+ { name: "predict_viral_score", description: "바이럴 점수 예측" },
4005
+ { name: "benchmark_content_performance", description: "성과 벤치마크" },
4006
+ { name: "predict_content_performance", description: "콘텐츠 성과 예측" },
4007
+ { name: "analyze_thumbnail", description: "썸네일 분석" },
4008
+ { name: "generate_ab_test_variants", description: "A/B 테스트 변형 생성" },
4009
+ { name: "analyze_influencer_collab", description: "인플루언서 협업 분석" },
4010
+ ];
4011
+ // Health check
4012
+ app.get('/', (_req, res) => {
4013
+ res.json({
4014
+ status: 'ok',
4015
+ server: 'content-genie-mcp',
4016
+ version: '2.9.2',
4017
+ tools: 17,
4018
+ timestamp: new Date().toISOString()
4019
+ });
4020
+ });
4021
+ app.get('/health', (_req, res) => {
4022
+ res.json({
4023
+ status: 'ok',
4024
+ server: 'content-genie-mcp',
4025
+ version: '2.9.2',
4026
+ tools: 17,
4027
+ timestamp: new Date().toISOString()
4028
+ });
4029
+ });
4030
+ // MCP 엔드포인트 - PlayMCP 연결 확인용 (간단한 응답)
4031
+ app.post('/mcp', (req, res) => {
4032
+ const { method, id } = req.body;
4033
+ // initialize 요청에 대한 응답
4034
+ if (method === 'initialize') {
4035
+ res.json({
4036
+ jsonrpc: '2.0',
4037
+ id,
4038
+ result: {
4039
+ protocolVersion: '2024-11-05',
4040
+ capabilities: { tools: { listChanged: true } },
4041
+ serverInfo: { name: 'content-genie-mcp', version: '2.9.2' }
4042
+ }
4043
+ });
4044
+ return;
4045
+ }
4046
+ // tools/list 요청에 대한 응답
4047
+ if (method === 'tools/list') {
4048
+ res.json({
4049
+ jsonrpc: '2.0',
4050
+ id,
4051
+ result: { tools: toolsList }
4052
+ });
4053
+ return;
4054
+ }
4055
+ // 기타 요청
4056
+ res.json({
4057
+ jsonrpc: '2.0',
4058
+ id,
4059
+ error: { code: -32601, message: 'Method not found' }
4060
+ });
4061
+ });
4062
+ // SSE endpoint for MCP connection (MCP Inspector용)
4063
+ const transports = new Map();
4064
+ app.get('/sse', async (_req, res) => {
4065
+ console.log('New SSE connection established');
4066
+ const transport = new SSEServerTransport('/message', res);
4067
+ const sessionId = crypto.randomUUID();
4068
+ transports.set(sessionId, transport);
4069
+ res.on('close', () => {
4070
+ console.log('SSE connection closed');
4071
+ transports.delete(sessionId);
4072
+ });
4073
+ await server.connect(transport);
4074
+ });
4075
+ // Message endpoint for MCP communication
4076
+ app.post('/message', async (req, res) => {
4077
+ const sessionId = req.query.sessionId;
4078
+ if (!sessionId) {
4079
+ const transport = Array.from(transports.values())[0];
4080
+ if (transport) {
4081
+ await transport.handlePostMessage(req, res);
4082
+ }
4083
+ else {
4084
+ res.status(400).json({ error: 'No active session' });
4085
+ }
4086
+ return;
4087
+ }
4088
+ const transport = transports.get(sessionId);
4089
+ if (transport) {
4090
+ await transport.handlePostMessage(req, res);
4091
+ }
4092
+ else {
4093
+ res.status(404).json({ error: 'Session not found' });
4094
+ }
4095
+ });
4096
+ app.listen(port, () => {
4097
+ console.log(`Content Genie MCP Server v2.9.0 running on HTTP port ${port}`);
4098
+ console.log(`Health check: http://localhost:${port}/health`);
4099
+ console.log(`MCP endpoint: http://localhost:${port}/mcp`);
4100
+ console.log(`SSE endpoint: http://localhost:${port}/sse`);
4101
+ });
4102
+ }
4103
+ else {
4104
+ // stdio 모드 (Claude Desktop, Claude Code용)
4105
+ const transport = new StdioServerTransport();
4106
+ await server.connect(transport);
4107
+ console.error("Content Genie MCP Server v2.9.0 running on stdio");
4108
+ }
3909
4109
  }
3910
4110
  main().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "content-genie-mcp",
3
- "version": "2.9.0",
3
+ "version": "2.9.2",
4
4
  "description": "AI Content Creation Assistant MCP - 한국 콘텐츠 크리에이터를 위한 트렌드 분석 및 콘텐츠 생성 도우미",
5
5
  "main": "dist/index.js",
6
6
  "bin": "dist/index.js",
@@ -12,6 +12,7 @@
12
12
  "scripts": {
13
13
  "build": "tsc",
14
14
  "start": "node dist/index.js",
15
+ "start:http": "MCP_HTTP_MODE=true node dist/index.js",
15
16
  "dev": "ts-node src/index.ts",
16
17
  "prepare": "npm run build"
17
18
  },
@@ -35,9 +36,12 @@
35
36
  "@modelcontextprotocol/sdk": "^1.25.1",
36
37
  "axios": "^1.13.2",
37
38
  "cheerio": "^1.1.2",
39
+ "cors": "^2.8.5",
38
40
  "zod": "^4.2.1"
39
41
  },
40
42
  "devDependencies": {
43
+ "@types/cors": "^2.8.19",
44
+ "@types/express": "^5.0.6",
41
45
  "@types/node": "^25.0.3",
42
46
  "ts-node": "^10.9.2",
43
47
  "typescript": "^5.9.3"
package/railway.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "https://railway.app/railway.schema.json",
3
+ "build": {
4
+ "builder": "DOCKERFILE",
5
+ "dockerfilePath": "Dockerfile"
6
+ },
7
+ "deploy": {
8
+ "startCommand": "node dist/index.js",
9
+ "healthcheckPath": "/health",
10
+ "healthcheckTimeout": 30,
11
+ "restartPolicyType": "ON_FAILURE",
12
+ "restartPolicyMaxRetries": 3
13
+ }
14
+ }