careerly-data-mcp 2.1.1 → 2.2.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Careerly Data MCP Server
2
2
 
3
- Claude Code에서 **자연어로 GA4 BigQuery 데이터를 분석**하는 MCP(Model Context Protocol) 서버입니다.
3
+ Claude Code에서 **자연어로 GA4, BigQuery, Search Console 데이터를 분석**하는 MCP(Model Context Protocol) 서버입니다.
4
4
 
5
5
  ## 왜 MCP를 쓰나요? (vs API 직접 호출)
6
6
 
@@ -16,6 +16,7 @@ BigQuery 분석하고 싶다 → SQL 작성 → UNNEST 문법 검색 → 쿼리
16
16
  ```
17
17
  "지난 7일 세션수 보여줘" → 끝!
18
18
  "page_view → post_detail_view 전환율 분석해줘" → 끝!
19
+ "검색어별 클릭수 보여줘" → 끝!
19
20
  ```
20
21
 
21
22
  ### 핵심 장점
@@ -58,7 +59,8 @@ npx careerly-data-mcp setup
58
59
  1. [Google Cloud Console](https://console.cloud.google.com/)에서 프로젝트 생성
59
60
  2. **Google Analytics Data API** 활성화
60
61
  3. **BigQuery API** 활성화
61
- 4. **서비스 계정** 생성 후 JSON 키 다운로드
62
+ 4. **Search Console API** 활성화
63
+ 5. **서비스 계정** 생성 후 JSON 키 다운로드
62
64
 
63
65
  ### 2. GA4 권한 설정
64
66
 
@@ -69,6 +71,11 @@ npx careerly-data-mcp setup
69
71
 
70
72
  - IAM & Admin > 서비스 계정에 **BigQuery 데이터 뷰어** 역할 추가
71
73
 
74
+ ### 4. Search Console 권한 설정 (선택)
75
+
76
+ - [Search Console](https://search.google.com/search-console) > 설정 > 사용자 및 권한
77
+ - 서비스 계정 이메일 추가 (전체 권한 또는 제한된 권한)
78
+
72
79
  ---
73
80
 
74
81
  ## 제공 도구 (Tools)
@@ -90,7 +97,7 @@ npx careerly-data-mcp setup
90
97
  | `bq_status` | 연결 상태 확인 | 연결 문제 진단 |
91
98
  | `bq_ga4_events` | GA4 Export 이벤트 조회 | event_params 자동 추출 |
92
99
 
93
- ### BigQuery 고급 도구 (v2.1.0 신규)
100
+ ### BigQuery 고급 도구 (v2.1.0)
94
101
 
95
102
  | Tool | 설명 | 언제 쓰나요? |
96
103
  |------|------|------------|
@@ -99,6 +106,14 @@ npx careerly-data-mcp setup
99
106
  | `bq_user_journey` | 사용자 여정 분석 | 특정 사용자가 어떤 행동을 했는지 |
100
107
  | `bq_funnel` | 퍼널 분석 | 단계별 전환율 자동 계산 |
101
108
 
109
+ ### Google Search Console 도구 (v2.2.0 신규)
110
+
111
+ | Tool | 설명 | 언제 쓰나요? |
112
+ |------|------|------------|
113
+ | `gsc_query` | 검색 분석 데이터 조회 | 검색어/페이지별 클릭, 노출, CTR, 순위 |
114
+ | `gsc_status` | 연결 상태 확인 | 연결 문제 진단 |
115
+ | `gsc_sites` | 등록된 사이트 목록 | 접근 가능한 사이트 확인 |
116
+
102
117
  ---
103
118
 
104
119
  ## 사용 예시
@@ -123,6 +138,15 @@ Claude Code에서 자연어로 질문하세요:
123
138
  "디바이스별 전환율 비교해줘"
124
139
  ```
125
140
 
141
+ ### Search Console 질의 (v2.2.0)
142
+
143
+ ```
144
+ "최근 28일 검색어별 클릭수 보여줘"
145
+ "클릭률이 낮은 페이지 찾아줘"
146
+ "모바일 vs 데스크톱 검색 성과 비교해줘"
147
+ "순위가 높은데 CTR이 낮은 키워드 분석해줘"
148
+ ```
149
+
126
150
  ### 실제 결과 예시 (퍼널 분석)
127
151
 
128
152
  ```
@@ -142,6 +166,28 @@ page_view → post_create_start 전환율: 2.3% | 1,215 → 28명
142
166
 
143
167
  ---
144
168
 
169
+ ## v2.2.0 주요 업데이트 (최신)
170
+
171
+ ### Google Search Console 통합
172
+
173
+ - **3개 새 도구 추가**: `gsc_query`, `gsc_status`, `gsc_sites`
174
+ - 검색어/페이지별 클릭수, 노출수, CTR, 평균 순위 분석
175
+ - 국가별, 디바이스별 필터링 지원
176
+ - 16개월 이전 데이터까지 조회 가능
177
+
178
+ ### 새 환경변수
179
+
180
+ | 변수 | 설명 |
181
+ |------|------|
182
+ | `GSC_SITE_URL` | 기본 사이트 URL (예: `https://example.com/` 또는 `sc-domain:example.com`) |
183
+
184
+ ### GSC 권한 설정
185
+
186
+ 1. Search Console > 설정 > 사용자 및 권한
187
+ 2. Service Account 이메일 추가 (전체 권한 또는 제한된 권한)
188
+
189
+ ---
190
+
145
191
  ## v2.1.0 주요 업데이트
146
192
 
147
193
  ### 버그 수정
@@ -226,6 +272,7 @@ claude mcp add careerly-ga4 \
226
272
  -s user \
227
273
  -e GA4_PROPERTY_ID=123456789 \
228
274
  -e BQ_PROJECT_ID=your-project-id \
275
+ -e GSC_SITE_URL=https://example.com/ \
229
276
  -e GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json \
230
277
  -- npx -y careerly-data-mcp serve
231
278
  ```
@@ -243,6 +290,7 @@ claude mcp add careerly-ga4 \
243
290
  "env": {
244
291
  "GA4_PROPERTY_ID": "123456789",
245
292
  "BQ_PROJECT_ID": "your-project-id",
293
+ "GSC_SITE_URL": "https://example.com/",
246
294
  "GOOGLE_APPLICATION_CREDENTIALS": "/path/to/key.json"
247
295
  }
248
296
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * setup 명령어 - GUI 스타일
3
- * GA4 + BigQuery 설정 + Claude Code 자동 설정
3
+ * GA4 + BigQuery + Search Console 설정 + Claude Code 자동 설정
4
4
  */
5
5
  /**
6
6
  * GA4 설정
@@ -20,9 +20,17 @@ export declare function runBigQuerySetup(): Promise<{
20
20
  connected: boolean;
21
21
  }>;
22
22
  /**
23
- * 통합 설정 (GA4 + BigQuery)
23
+ * 통합 설정 (GA4 + BigQuery + GSC)
24
24
  */
25
25
  export declare function runSetup(): Promise<void>;
26
+ /**
27
+ * Search Console 설정
28
+ */
29
+ export declare function runGSCSetup(existingCredentialsPath?: string): Promise<{
30
+ siteUrl: string;
31
+ credentialsPath: string;
32
+ connected: boolean;
33
+ }>;
26
34
  /**
27
35
  * 인터랙티브 메뉴
28
36
  */
@@ -1 +1 @@
1
- {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/cli/setup.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAqSH;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC,CAgED;AAED;;GAEG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC,CA4FD;AAED;;GAEG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAoJ9C;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAmIxD"}
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/cli/setup.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAwTH;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC,CAgED;AAED;;GAEG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC,CA4FD;AAED;;GAEG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAgL9C;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,uBAAuB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC,CA2ID;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CA4JxD"}
package/dist/cli/setup.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * setup 명령어 - GUI 스타일
3
- * GA4 + BigQuery 설정 + Claude Code 자동 설정
3
+ * GA4 + BigQuery + Search Console 설정 + Claude Code 자동 설정
4
4
  */
5
5
  import * as fs from "fs/promises";
6
6
  import * as path from "path";
@@ -10,6 +10,7 @@ import ora from "ora";
10
10
  import { input, confirm, select, checkbox } from "@inquirer/prompts";
11
11
  import { GA4Client } from "../ga4/index.js";
12
12
  import { BigQueryClient } from "../bigquery/index.js";
13
+ import { GSCClient } from "../gsc/index.js";
13
14
  const CLAUDE_SETTINGS_PATH = path.join(process.env.HOME || "~", ".claude", "settings.json");
14
15
  const CLAUDE_SETTINGS_LOCAL_PATH = path.join(process.env.HOME || "~", ".claude", "settings.local.json");
15
16
  /**
@@ -82,7 +83,7 @@ async function findServiceAccountKeys() {
82
83
  function printHeader() {
83
84
  console.log("");
84
85
  console.log(chalk.cyan.bold(" Careerly Data MCP Server"));
85
- console.log(chalk.gray(" GA4 + BigQuery 통합 분석"));
86
+ console.log(chalk.gray(" GA4 + BigQuery + Search Console 통합 분석"));
86
87
  console.log(chalk.gray(" ─────────────────────────────────────"));
87
88
  console.log("");
88
89
  }
@@ -102,6 +103,12 @@ function printStatus(config) {
102
103
  : config.bqProjectId
103
104
  ? chalk.yellow("○ configured")
104
105
  : chalk.gray("- not set");
106
+ // GSC 상태
107
+ const gscStatus = config.gscConnected
108
+ ? chalk.green("✓ connected")
109
+ : config.gscSiteUrl
110
+ ? chalk.yellow("○ configured")
111
+ : chalk.gray("- not set");
105
112
  console.log(chalk.white.bold("GA4: ") + ga4Status);
106
113
  if (config.ga4PropertyId) {
107
114
  console.log(chalk.gray(" Property: " + config.ga4PropertyId));
@@ -110,6 +117,10 @@ function printStatus(config) {
110
117
  if (config.bqProjectId) {
111
118
  console.log(chalk.gray(" Project: " + config.bqProjectId));
112
119
  }
120
+ console.log(chalk.white.bold("GSC: ") + gscStatus);
121
+ if (config.gscSiteUrl) {
122
+ console.log(chalk.gray(" Site: " + config.gscSiteUrl));
123
+ }
113
124
  if (config.credentialsPath) {
114
125
  const shortPath = config.credentialsPath.replace(process.env.HOME || "", "~");
115
126
  console.log(chalk.white.bold("Credentials: ") + chalk.gray(shortPath));
@@ -144,6 +155,9 @@ async function runClaudeMcpAdd(config) {
144
155
  if (config.bqLocation) {
145
156
  envParts.push(`-e BQ_LOCATION=${config.bqLocation}`);
146
157
  }
158
+ if (config.gscSiteUrl) {
159
+ envParts.push(`-e GSC_SITE_URL=${config.gscSiteUrl}`);
160
+ }
147
161
  envParts.push(`-e GOOGLE_APPLICATION_CREDENTIALS=${config.credentialsPath}`);
148
162
  // claude mcp add 실행
149
163
  const cmd = [
@@ -364,7 +378,7 @@ export async function runBigQuerySetup() {
364
378
  };
365
379
  }
366
380
  /**
367
- * 통합 설정 (GA4 + BigQuery)
381
+ * 통합 설정 (GA4 + BigQuery + GSC)
368
382
  */
369
383
  export async function runSetup() {
370
384
  printHeader();
@@ -374,6 +388,7 @@ export async function runSetup() {
374
388
  choices: [
375
389
  { name: "GA4 Data API", value: "ga4", checked: true },
376
390
  { name: "BigQuery", value: "bigquery", checked: true },
391
+ { name: "Search Console", value: "gsc", checked: true },
377
392
  ],
378
393
  });
379
394
  if (services.length === 0) {
@@ -382,6 +397,7 @@ export async function runSetup() {
382
397
  }
383
398
  let ga4Config = {};
384
399
  let bqConfig = {};
400
+ let gscConfig = {};
385
401
  let credentialsPath = "";
386
402
  // GA4 설정
387
403
  if (services.includes("ga4")) {
@@ -416,8 +432,24 @@ export async function runSetup() {
416
432
  }
417
433
  }
418
434
  }
435
+ // Search Console 설정
436
+ if (services.includes("gsc")) {
437
+ try {
438
+ const result = await runGSCSetup(credentialsPath);
439
+ gscConfig = { siteUrl: result.siteUrl, connected: result.connected };
440
+ // credentials가 아직 없으면 사용
441
+ if (!credentialsPath) {
442
+ credentialsPath = result.credentialsPath;
443
+ }
444
+ }
445
+ catch (error) {
446
+ if (error.message === "설정 취소됨") {
447
+ console.log(chalk.yellow("\nSearch Console 설정이 취소되었습니다."));
448
+ }
449
+ }
450
+ }
419
451
  // 설정할 내용이 있는지 확인
420
- if (!ga4Config.propertyId && !bqConfig.projectId) {
452
+ if (!ga4Config.propertyId && !bqConfig.projectId && !gscConfig.siteUrl) {
421
453
  console.log(chalk.yellow("\n설정된 서비스가 없습니다."));
422
454
  return;
423
455
  }
@@ -428,6 +460,7 @@ export async function runSetup() {
428
460
  ga4PropertyId: ga4Config.propertyId,
429
461
  bqProjectId: bqConfig.projectId,
430
462
  bqLocation: bqConfig.location,
463
+ gscSiteUrl: gscConfig.siteUrl,
431
464
  credentialsPath,
432
465
  });
433
466
  if (mcpResult.success) {
@@ -449,6 +482,9 @@ export async function runSetup() {
449
482
  if (bqConfig.location) {
450
483
  env.BQ_LOCATION = bqConfig.location;
451
484
  }
485
+ if (gscConfig.siteUrl) {
486
+ env.GSC_SITE_URL = gscConfig.siteUrl;
487
+ }
452
488
  const mcpConfig = {
453
489
  command: "npx",
454
490
  args: ["-y", "careerly-data-mcp", "serve"],
@@ -480,10 +516,12 @@ export async function runSetup() {
480
516
  printStatus({
481
517
  ga4PropertyId: ga4Config.propertyId,
482
518
  bqProjectId: bqConfig.projectId,
519
+ gscSiteUrl: gscConfig.siteUrl,
483
520
  credentialsPath,
484
521
  ga4Connected: ga4Config.connected,
485
522
  bqConnected: bqConfig.connected,
486
- tools: 7,
523
+ gscConnected: gscConfig.connected,
524
+ tools: 14,
487
525
  });
488
526
  console.log(chalk.cyan(" Claude Code를 재시작하면 사용할 수 있습니다."));
489
527
  console.log("");
@@ -495,6 +533,144 @@ export async function runSetup() {
495
533
  console.log(chalk.white(' "BigQuery 데이터셋 목록 보여줘"'));
496
534
  console.log(chalk.white(' "GA4 이벤트 데이터에서 page_view 조회해줘"'));
497
535
  console.log("");
536
+ console.log(chalk.gray(" Search Console 사용 예시:"));
537
+ console.log(chalk.white(' "검색어별 클릭수 보여줘"'));
538
+ console.log(chalk.white(' "페이지별 순위 분석해줘"'));
539
+ console.log("");
540
+ }
541
+ /**
542
+ * Search Console 설정
543
+ */
544
+ export async function runGSCSetup(existingCredentialsPath) {
545
+ printHeader();
546
+ console.log(chalk.cyan.bold(" Search Console 설정"));
547
+ console.log("");
548
+ // 1. Service Account 키 파일 선택 (이미 있으면 재사용 제안)
549
+ let absolutePath;
550
+ if (existingCredentialsPath) {
551
+ const reuseCredentials = await confirm({
552
+ message: `기존 키 파일 사용? (${path.basename(existingCredentialsPath)})`,
553
+ default: true,
554
+ });
555
+ if (reuseCredentials) {
556
+ absolutePath = existingCredentialsPath;
557
+ }
558
+ else {
559
+ console.log("");
560
+ absolutePath = await selectServiceAccountKey();
561
+ }
562
+ }
563
+ else {
564
+ absolutePath = await selectServiceAccountKey();
565
+ }
566
+ // 2. 연결 테스트 및 사이트 목록 조회
567
+ console.log("");
568
+ const spinner = ora("Search Console 연결 테스트 중...").start();
569
+ process.env.GOOGLE_APPLICATION_CREDENTIALS = absolutePath;
570
+ let siteUrl = "";
571
+ let connected = false;
572
+ try {
573
+ const client = new GSCClient();
574
+ const result = await client.testConnection();
575
+ if (result.success && result.sites && result.sites.length > 0) {
576
+ spinner.succeed(chalk.green("Search Console 연결 성공!"));
577
+ connected = true;
578
+ // 사이트 선택
579
+ console.log("");
580
+ const choices = [
581
+ ...result.sites.map((site) => ({
582
+ name: site,
583
+ value: site,
584
+ })),
585
+ {
586
+ name: chalk.gray("직접 입력..."),
587
+ value: "__manual__",
588
+ },
589
+ ];
590
+ const selected = await select({
591
+ message: "분석할 사이트 선택",
592
+ choices,
593
+ });
594
+ if (selected === "__manual__") {
595
+ siteUrl = await input({
596
+ message: "Site URL (예: https://example.com/ 또는 sc-domain:example.com)",
597
+ validate: (value) => {
598
+ if (!value)
599
+ return "Site URL을 입력해주세요";
600
+ if (!value.startsWith("http") && !value.startsWith("sc-domain:")) {
601
+ return "https:// 또는 sc-domain: 형식이어야 합니다";
602
+ }
603
+ return true;
604
+ },
605
+ });
606
+ }
607
+ else {
608
+ siteUrl = selected;
609
+ }
610
+ }
611
+ else if (result.success) {
612
+ spinner.warn(chalk.yellow("연결됐지만 접근 가능한 사이트가 없습니다."));
613
+ // 수동 입력
614
+ siteUrl = await input({
615
+ message: "Site URL (예: https://example.com/ 또는 sc-domain:example.com)",
616
+ validate: (value) => {
617
+ if (!value)
618
+ return "Site URL을 입력해주세요";
619
+ if (!value.startsWith("http") && !value.startsWith("sc-domain:")) {
620
+ return "https:// 또는 sc-domain: 형식이어야 합니다";
621
+ }
622
+ return true;
623
+ },
624
+ });
625
+ }
626
+ else {
627
+ spinner.fail(chalk.red(`Search Console 연결 실패: ${result.message}`));
628
+ const continueSetup = await confirm({
629
+ message: "연결에 실패했습니다. 계속 진행하시겠습니까?",
630
+ default: false,
631
+ });
632
+ if (!continueSetup) {
633
+ throw new Error("설정 취소됨");
634
+ }
635
+ // 수동 입력
636
+ siteUrl = await input({
637
+ message: "Site URL (예: https://example.com/ 또는 sc-domain:example.com)",
638
+ validate: (value) => {
639
+ if (!value)
640
+ return "Site URL을 입력해주세요";
641
+ if (!value.startsWith("http") && !value.startsWith("sc-domain:")) {
642
+ return "https:// 또는 sc-domain: 형식이어야 합니다";
643
+ }
644
+ return true;
645
+ },
646
+ });
647
+ }
648
+ }
649
+ catch (error) {
650
+ if (error.message === "설정 취소됨")
651
+ throw error;
652
+ spinner.fail(chalk.red(`Search Console 연결 실패: ${error instanceof Error ? error.message : error}`));
653
+ const continueSetup = await confirm({
654
+ message: "계속 진행하시겠습니까?",
655
+ default: false,
656
+ });
657
+ if (!continueSetup) {
658
+ throw new Error("설정 취소됨");
659
+ }
660
+ // 수동 입력
661
+ siteUrl = await input({
662
+ message: "Site URL (예: https://example.com/ 또는 sc-domain:example.com)",
663
+ validate: (value) => {
664
+ if (!value)
665
+ return "Site URL을 입력해주세요";
666
+ if (!value.startsWith("http") && !value.startsWith("sc-domain:")) {
667
+ return "https:// 또는 sc-domain: 형식이어야 합니다";
668
+ }
669
+ return true;
670
+ },
671
+ });
672
+ }
673
+ return { siteUrl, credentialsPath: absolutePath, connected };
498
674
  }
499
675
  /**
500
676
  * 인터랙티브 메뉴
@@ -502,7 +678,7 @@ export async function runSetup() {
502
678
  export async function runInteractiveMenu() {
503
679
  printHeader();
504
680
  // 현재 설정 상태 확인
505
- let currentConfig = { ga4Connected: false, bqConnected: false };
681
+ let currentConfig = { ga4Connected: false, bqConnected: false, gscConnected: false };
506
682
  // settings 파일에서 현재 설정 읽기
507
683
  const settingsPath = (await fileExists(CLAUDE_SETTINGS_LOCAL_PATH))
508
684
  ? CLAUDE_SETTINGS_LOCAL_PATH
@@ -515,6 +691,7 @@ export async function runInteractiveMenu() {
515
691
  if (mcpConfig?.env) {
516
692
  currentConfig.ga4PropertyId = mcpConfig.env.GA4_PROPERTY_ID;
517
693
  currentConfig.bqProjectId = mcpConfig.env.BQ_PROJECT_ID;
694
+ currentConfig.gscSiteUrl = mcpConfig.env.GSC_SITE_URL;
518
695
  currentConfig.credentialsPath =
519
696
  mcpConfig.env.GOOGLE_APPLICATION_CREDENTIALS;
520
697
  // GA4 연결 테스트
@@ -547,13 +724,26 @@ export async function runInteractiveMenu() {
547
724
  currentConfig.bqConnected = false;
548
725
  }
549
726
  }
727
+ // GSC 연결 테스트
728
+ if (currentConfig.gscSiteUrl && currentConfig.credentialsPath) {
729
+ process.env.GSC_SITE_URL = currentConfig.gscSiteUrl;
730
+ process.env.GOOGLE_APPLICATION_CREDENTIALS = currentConfig.credentialsPath;
731
+ try {
732
+ const client = new GSCClient();
733
+ const result = await client.testConnection();
734
+ currentConfig.gscConnected = result.success;
735
+ }
736
+ catch {
737
+ currentConfig.gscConnected = false;
738
+ }
739
+ }
550
740
  }
551
741
  }
552
742
  printStatus({
553
743
  ...currentConfig,
554
- tools: 7,
744
+ tools: 14,
555
745
  });
556
- const isConfigured = currentConfig.ga4Connected || currentConfig.bqConnected;
746
+ const isConfigured = currentConfig.ga4Connected || currentConfig.bqConnected || currentConfig.gscConnected;
557
747
  // 메뉴 선택
558
748
  const action = await select({
559
749
  message: "선택하세요",
@@ -566,7 +756,7 @@ export async function runInteractiveMenu() {
566
756
  {
567
757
  name: isConfigured ? "Reconfigure" : "Setup",
568
758
  value: "setup",
569
- description: "GA4 + BigQuery 설정",
759
+ description: "GA4 + BigQuery + Search Console 설정",
570
760
  },
571
761
  {
572
762
  name: "Setup GA4 only",
@@ -578,6 +768,11 @@ export async function runInteractiveMenu() {
578
768
  value: "setup-bq",
579
769
  description: "BigQuery만 설정",
580
770
  },
771
+ {
772
+ name: "Setup Search Console only",
773
+ value: "setup-gsc",
774
+ description: "Search Console만 설정",
775
+ },
581
776
  {
582
777
  name: "Test connections",
583
778
  value: "test",
@@ -603,6 +798,9 @@ export async function runInteractiveMenu() {
603
798
  case "setup-bq":
604
799
  await runBigQuerySetupOnly();
605
800
  break;
801
+ case "setup-gsc":
802
+ await runGSCSetupOnly();
803
+ break;
606
804
  case "test":
607
805
  await testConnections();
608
806
  break;
@@ -614,6 +812,39 @@ export async function runInteractiveMenu() {
614
812
  break;
615
813
  }
616
814
  }
815
+ /**
816
+ * Search Console만 설정
817
+ */
818
+ async function runGSCSetupOnly() {
819
+ try {
820
+ const result = await runGSCSetup();
821
+ // Claude Code MCP 설정
822
+ console.log("");
823
+ const settingsSpinner = ora("Claude Code MCP 설정 중...").start();
824
+ const mcpResult = await runClaudeMcpAdd({
825
+ gscSiteUrl: result.siteUrl,
826
+ credentialsPath: result.credentialsPath,
827
+ });
828
+ if (mcpResult.success) {
829
+ settingsSpinner.succeed(chalk.green("Claude Code MCP 서버 등록 완료"));
830
+ }
831
+ else {
832
+ await saveSettingsManually({
833
+ gscSiteUrl: result.siteUrl,
834
+ credentialsPath: result.credentialsPath,
835
+ });
836
+ }
837
+ console.log("");
838
+ console.log(chalk.green.bold(" ✓ Search Console 설정 완료!"));
839
+ console.log(chalk.cyan(" Claude Code를 재시작하면 사용할 수 있습니다."));
840
+ console.log("");
841
+ }
842
+ catch (error) {
843
+ if (error.message === "설정 취소됨") {
844
+ console.log(chalk.yellow("\n설정이 취소되었습니다."));
845
+ }
846
+ }
847
+ }
617
848
  /**
618
849
  * GA4만 설정
619
850
  */
@@ -699,6 +930,9 @@ async function saveSettingsManually(config) {
699
930
  if (config.bqLocation) {
700
931
  env.BQ_LOCATION = config.bqLocation;
701
932
  }
933
+ if (config.gscSiteUrl) {
934
+ env.GSC_SITE_URL = config.gscSiteUrl;
935
+ }
702
936
  const mcpConfig = {
703
937
  command: "npx",
704
938
  args: ["-y", "careerly-data-mcp", "serve"],
@@ -727,11 +961,10 @@ async function saveSettingsManually(config) {
727
961
  */
728
962
  async function showTools() {
729
963
  console.log("");
730
- console.log(chalk.white.bold(" GA4 Data API Tools:"));
964
+ console.log(chalk.white.bold(" GA4 Data API Tools (3):"));
731
965
  console.log("");
732
966
  console.log(chalk.cyan(" ga4_query"));
733
967
  console.log(chalk.gray(" GA4 데이터 조회 및 인사이트 생성"));
734
- console.log(chalk.gray(" 예: 지난 7일 세션수, 채널별 전환수"));
735
968
  console.log("");
736
969
  console.log(chalk.cyan(" ga4_metadata"));
737
970
  console.log(chalk.gray(" 사용 가능한 지표/차원 목록 확인"));
@@ -739,15 +972,13 @@ async function showTools() {
739
972
  console.log(chalk.cyan(" ga4_status"));
740
973
  console.log(chalk.gray(" GA4 연결 상태 확인"));
741
974
  console.log("");
742
- console.log(chalk.white.bold(" BigQuery Tools:"));
975
+ console.log(chalk.white.bold(" BigQuery Tools (8):"));
743
976
  console.log("");
744
977
  console.log(chalk.cyan(" bq_query"));
745
978
  console.log(chalk.gray(" BigQuery 테이블 파라미터 기반 조회"));
746
- console.log(chalk.gray(" 예: 특정 테이블에서 조건별 데이터 조회"));
747
979
  console.log("");
748
980
  console.log(chalk.cyan(" bq_ga4_events"));
749
981
  console.log(chalk.gray(" GA4 Export 이벤트 데이터 조회"));
750
- console.log(chalk.gray(" 예: page_view, purchase 이벤트 분석"));
751
982
  console.log("");
752
983
  console.log(chalk.cyan(" bq_metadata"));
753
984
  console.log(chalk.gray(" 데이터셋/테이블 목록 및 스키마 조회"));
@@ -755,6 +986,29 @@ async function showTools() {
755
986
  console.log(chalk.cyan(" bq_status"));
756
987
  console.log(chalk.gray(" BigQuery 연결 상태 확인"));
757
988
  console.log("");
989
+ console.log(chalk.cyan(" bq_raw_sql"));
990
+ console.log(chalk.gray(" SQL 직접 실행 (SELECT/WITH만 허용)"));
991
+ console.log("");
992
+ console.log(chalk.cyan(" bq_event_params"));
993
+ console.log(chalk.gray(" event_params 쉽게 추출"));
994
+ console.log("");
995
+ console.log(chalk.cyan(" bq_user_journey"));
996
+ console.log(chalk.gray(" 사용자별 이벤트 시퀀스 분석"));
997
+ console.log("");
998
+ console.log(chalk.cyan(" bq_funnel"));
999
+ console.log(chalk.gray(" 전환 퍼널 자동 계산"));
1000
+ console.log("");
1001
+ console.log(chalk.white.bold(" Search Console Tools (3):"));
1002
+ console.log("");
1003
+ console.log(chalk.cyan(" gsc_query"));
1004
+ console.log(chalk.gray(" 검색어/페이지별 클릭, 노출, CTR, 순위 분석"));
1005
+ console.log("");
1006
+ console.log(chalk.cyan(" gsc_sites"));
1007
+ console.log(chalk.gray(" 접근 가능한 사이트 목록"));
1008
+ console.log("");
1009
+ console.log(chalk.cyan(" gsc_status"));
1010
+ console.log(chalk.gray(" Search Console 연결 상태 확인"));
1011
+ console.log("");
758
1012
  await select({
759
1013
  message: "",
760
1014
  choices: [{ name: "← Back", value: "back" }],
@@ -796,6 +1050,21 @@ async function testConnections() {
796
1050
  catch (error) {
797
1051
  bqSpinner.fail(chalk.red(`BigQuery: ${error instanceof Error ? error.message : error}`));
798
1052
  }
1053
+ // Search Console 테스트
1054
+ const gscSpinner = ora("Search Console 연결 테스트 중...").start();
1055
+ try {
1056
+ const client = new GSCClient();
1057
+ const result = await client.testConnection();
1058
+ if (result.success) {
1059
+ gscSpinner.succeed(chalk.green(`Search Console: ${result.message}`));
1060
+ }
1061
+ else {
1062
+ gscSpinner.fail(chalk.red(`Search Console: ${result.message}`));
1063
+ }
1064
+ }
1065
+ catch (error) {
1066
+ gscSpinner.fail(chalk.red(`Search Console: ${error instanceof Error ? error.message : error}`));
1067
+ }
799
1068
  console.log("");
800
1069
  await select({
801
1070
  message: "",