careerly-data-mcp 2.0.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 +230 -0
- package/dist/bigquery/auth.d.ts +18 -0
- package/dist/bigquery/auth.d.ts.map +1 -0
- package/dist/bigquery/auth.js +46 -0
- package/dist/bigquery/auth.js.map +1 -0
- package/dist/bigquery/client.d.ts +77 -0
- package/dist/bigquery/client.d.ts.map +1 -0
- package/dist/bigquery/client.js +495 -0
- package/dist/bigquery/client.js.map +1 -0
- package/dist/bigquery/index.d.ts +7 -0
- package/dist/bigquery/index.d.ts.map +1 -0
- package/dist/bigquery/index.js +7 -0
- package/dist/bigquery/index.js.map +1 -0
- package/dist/bigquery/types.d.ts +137 -0
- package/dist/bigquery/types.d.ts.map +1 -0
- package/dist/bigquery/types.js +40 -0
- package/dist/bigquery/types.js.map +1 -0
- package/dist/cli/setup.d.ts +30 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +859 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/ga4/auth.d.ts +29 -0
- package/dist/ga4/auth.d.ts.map +1 -0
- package/dist/ga4/auth.js +73 -0
- package/dist/ga4/auth.js.map +1 -0
- package/dist/ga4/client.d.ts +51 -0
- package/dist/ga4/client.d.ts.map +1 -0
- package/dist/ga4/client.js +223 -0
- package/dist/ga4/client.js.map +1 -0
- package/dist/ga4/index.d.ts +7 -0
- package/dist/ga4/index.d.ts.map +1 -0
- package/dist/ga4/index.js +7 -0
- package/dist/ga4/index.js.map +1 -0
- package/dist/ga4/types.d.ts +115 -0
- package/dist/ga4/types.d.ts.map +1 -0
- package/dist/ga4/types.js +63 -0
- package/dist/ga4/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +114 -0
- package/dist/index.js.map +1 -0
- package/dist/insight/renderer.d.ts +29 -0
- package/dist/insight/renderer.d.ts.map +1 -0
- package/dist/insight/renderer.js +194 -0
- package/dist/insight/renderer.js.map +1 -0
- package/dist/mcp/server.d.ts +20 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +50 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/tools/bq-ga4-events.d.ts +10 -0
- package/dist/tools/bq-ga4-events.d.ts.map +1 -0
- package/dist/tools/bq-ga4-events.js +396 -0
- package/dist/tools/bq-ga4-events.js.map +1 -0
- package/dist/tools/bq-metadata.d.ts +10 -0
- package/dist/tools/bq-metadata.d.ts.map +1 -0
- package/dist/tools/bq-metadata.js +237 -0
- package/dist/tools/bq-metadata.js.map +1 -0
- package/dist/tools/bq-query.d.ts +10 -0
- package/dist/tools/bq-query.d.ts.map +1 -0
- package/dist/tools/bq-query.js +357 -0
- package/dist/tools/bq-query.js.map +1 -0
- package/dist/tools/metadata.d.ts +11 -0
- package/dist/tools/metadata.d.ts.map +1 -0
- package/dist/tools/metadata.js +135 -0
- package/dist/tools/metadata.js.map +1 -0
- package/dist/tools/query.d.ts +10 -0
- package/dist/tools/query.d.ts.map +1 -0
- package/dist/tools/query.js +188 -0
- package/dist/tools/query.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,859 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* setup 명령어 - GUI 스타일
|
|
3
|
+
* GA4 + BigQuery 설정 + Claude Code 자동 설정
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from "fs/promises";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import ora from "ora";
|
|
10
|
+
import { input, confirm, select, checkbox } from "@inquirer/prompts";
|
|
11
|
+
import { GA4Client } from "../ga4/index.js";
|
|
12
|
+
import { BigQueryClient } from "../bigquery/index.js";
|
|
13
|
+
const CLAUDE_SETTINGS_PATH = path.join(process.env.HOME || "~", ".claude", "settings.json");
|
|
14
|
+
const CLAUDE_SETTINGS_LOCAL_PATH = path.join(process.env.HOME || "~", ".claude", "settings.local.json");
|
|
15
|
+
/**
|
|
16
|
+
* 파일 존재 여부 확인
|
|
17
|
+
*/
|
|
18
|
+
async function fileExists(filePath) {
|
|
19
|
+
try {
|
|
20
|
+
await fs.access(filePath);
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 설정 파일 읽기
|
|
29
|
+
*/
|
|
30
|
+
async function readSettings(filePath) {
|
|
31
|
+
try {
|
|
32
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
33
|
+
return JSON.parse(content);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 설정 파일 쓰기
|
|
41
|
+
*/
|
|
42
|
+
async function writeSettings(filePath, settings) {
|
|
43
|
+
const dir = path.dirname(filePath);
|
|
44
|
+
await fs.mkdir(dir, { recursive: true });
|
|
45
|
+
await fs.writeFile(filePath, JSON.stringify(settings, null, 2));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 현재 폴더에서 Service Account JSON 파일 자동 탐지
|
|
49
|
+
*/
|
|
50
|
+
async function findServiceAccountKeys() {
|
|
51
|
+
const cwd = process.cwd();
|
|
52
|
+
const results = [];
|
|
53
|
+
try {
|
|
54
|
+
const files = await fs.readdir(cwd);
|
|
55
|
+
const jsonFiles = files.filter((f) => f.endsWith(".json"));
|
|
56
|
+
for (const file of jsonFiles) {
|
|
57
|
+
const filePath = path.join(cwd, file);
|
|
58
|
+
try {
|
|
59
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
60
|
+
const json = JSON.parse(content);
|
|
61
|
+
if (json.type === "service_account") {
|
|
62
|
+
results.push({
|
|
63
|
+
path: filePath,
|
|
64
|
+
projectId: json.project_id,
|
|
65
|
+
email: json.client_email,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// JSON 파싱 실패 - 무시
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// 디렉토리 읽기 실패 - 무시
|
|
76
|
+
}
|
|
77
|
+
return results;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 헤더 출력
|
|
81
|
+
*/
|
|
82
|
+
function printHeader() {
|
|
83
|
+
console.log("");
|
|
84
|
+
console.log(chalk.cyan.bold(" Careerly Data MCP Server"));
|
|
85
|
+
console.log(chalk.gray(" GA4 + BigQuery 통합 분석"));
|
|
86
|
+
console.log(chalk.gray(" ─────────────────────────────────────"));
|
|
87
|
+
console.log("");
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* 상태 정보 출력
|
|
91
|
+
*/
|
|
92
|
+
function printStatus(config) {
|
|
93
|
+
// GA4 상태
|
|
94
|
+
const ga4Status = config.ga4Connected
|
|
95
|
+
? chalk.green("✓ connected")
|
|
96
|
+
: config.ga4PropertyId
|
|
97
|
+
? chalk.yellow("○ configured")
|
|
98
|
+
: chalk.gray("- not set");
|
|
99
|
+
// BigQuery 상태
|
|
100
|
+
const bqStatus = config.bqConnected
|
|
101
|
+
? chalk.green("✓ connected")
|
|
102
|
+
: config.bqProjectId
|
|
103
|
+
? chalk.yellow("○ configured")
|
|
104
|
+
: chalk.gray("- not set");
|
|
105
|
+
console.log(chalk.white.bold("GA4: ") + ga4Status);
|
|
106
|
+
if (config.ga4PropertyId) {
|
|
107
|
+
console.log(chalk.gray(" Property: " + config.ga4PropertyId));
|
|
108
|
+
}
|
|
109
|
+
console.log(chalk.white.bold("BigQuery: ") + bqStatus);
|
|
110
|
+
if (config.bqProjectId) {
|
|
111
|
+
console.log(chalk.gray(" Project: " + config.bqProjectId));
|
|
112
|
+
}
|
|
113
|
+
if (config.credentialsPath) {
|
|
114
|
+
const shortPath = config.credentialsPath.replace(process.env.HOME || "", "~");
|
|
115
|
+
console.log(chalk.white.bold("Credentials: ") + chalk.gray(shortPath));
|
|
116
|
+
}
|
|
117
|
+
if (config.tools) {
|
|
118
|
+
console.log(chalk.white.bold("Tools: ") + chalk.white(`${config.tools} tools`));
|
|
119
|
+
}
|
|
120
|
+
console.log("");
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* claude mcp add 명령어 실행
|
|
124
|
+
*/
|
|
125
|
+
async function runClaudeMcpAdd(config) {
|
|
126
|
+
try {
|
|
127
|
+
// 기존 설정 삭제 (있으면)
|
|
128
|
+
try {
|
|
129
|
+
execSync("claude mcp remove careerly-data -s user", {
|
|
130
|
+
stdio: "ignore",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// 없으면 무시
|
|
135
|
+
}
|
|
136
|
+
// 환경변수 구성
|
|
137
|
+
const envParts = [];
|
|
138
|
+
if (config.ga4PropertyId) {
|
|
139
|
+
envParts.push(`-e GA4_PROPERTY_ID=${config.ga4PropertyId}`);
|
|
140
|
+
}
|
|
141
|
+
if (config.bqProjectId) {
|
|
142
|
+
envParts.push(`-e BQ_PROJECT_ID=${config.bqProjectId}`);
|
|
143
|
+
}
|
|
144
|
+
if (config.bqLocation) {
|
|
145
|
+
envParts.push(`-e BQ_LOCATION=${config.bqLocation}`);
|
|
146
|
+
}
|
|
147
|
+
envParts.push(`-e GOOGLE_APPLICATION_CREDENTIALS=${config.credentialsPath}`);
|
|
148
|
+
// claude mcp add 실행
|
|
149
|
+
const cmd = [
|
|
150
|
+
"claude mcp add careerly-data",
|
|
151
|
+
"-s user",
|
|
152
|
+
...envParts,
|
|
153
|
+
"-- npx -y careerly-data-mcp serve",
|
|
154
|
+
].join(" ");
|
|
155
|
+
execSync(cmd, { stdio: "pipe" });
|
|
156
|
+
return { success: true, message: "Claude Code MCP 설정 완료" };
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
return {
|
|
160
|
+
success: false,
|
|
161
|
+
message: error instanceof Error ? error.message : String(error),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Service Account 키 파일 선택
|
|
167
|
+
*/
|
|
168
|
+
async function selectServiceAccountKey() {
|
|
169
|
+
const detectSpinner = ora("Service Account 키 파일 탐지 중...").start();
|
|
170
|
+
const foundKeys = await findServiceAccountKeys();
|
|
171
|
+
let absolutePath;
|
|
172
|
+
if (foundKeys.length > 0) {
|
|
173
|
+
detectSpinner.succeed(chalk.green(`${foundKeys.length}개의 Service Account 키 파일 발견`));
|
|
174
|
+
const choices = [
|
|
175
|
+
...foundKeys.map((key) => ({
|
|
176
|
+
name: `${path.basename(key.path)} ${chalk.gray(`(${key.projectId || "unknown"})`)}`,
|
|
177
|
+
value: key.path,
|
|
178
|
+
description: key.email,
|
|
179
|
+
})),
|
|
180
|
+
{
|
|
181
|
+
name: chalk.gray("직접 경로 입력..."),
|
|
182
|
+
value: "__manual__",
|
|
183
|
+
description: "다른 경로의 키 파일 사용",
|
|
184
|
+
},
|
|
185
|
+
];
|
|
186
|
+
const selected = await select({
|
|
187
|
+
message: "사용할 키 파일 선택",
|
|
188
|
+
choices,
|
|
189
|
+
});
|
|
190
|
+
if (selected === "__manual__") {
|
|
191
|
+
absolutePath = await inputCredentialsPath();
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
absolutePath = selected;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
detectSpinner.info(chalk.yellow("키 파일을 찾을 수 없습니다. 경로를 입력해주세요."));
|
|
199
|
+
absolutePath = await inputCredentialsPath();
|
|
200
|
+
}
|
|
201
|
+
return absolutePath;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* 자격증명 경로 입력
|
|
205
|
+
*/
|
|
206
|
+
async function inputCredentialsPath() {
|
|
207
|
+
const credentialsPath = await input({
|
|
208
|
+
message: "Service Account JSON 키 파일 경로",
|
|
209
|
+
validate: async (value) => {
|
|
210
|
+
if (!value)
|
|
211
|
+
return "경로를 입력해주세요";
|
|
212
|
+
const expandedPath = value.replace(/^~/, process.env.HOME || "");
|
|
213
|
+
const resolved = path.resolve(expandedPath);
|
|
214
|
+
if (!(await fileExists(resolved))) {
|
|
215
|
+
return `파일을 찾을 수 없습니다: ${resolved}`;
|
|
216
|
+
}
|
|
217
|
+
return true;
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
const expandedPath = credentialsPath.replace(/^~/, process.env.HOME || "");
|
|
221
|
+
return path.resolve(expandedPath);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* GA4 설정
|
|
225
|
+
*/
|
|
226
|
+
export async function runGA4Setup() {
|
|
227
|
+
printHeader();
|
|
228
|
+
console.log(chalk.cyan.bold(" GA4 Data API 설정"));
|
|
229
|
+
console.log("");
|
|
230
|
+
// 1. GA4 Property ID 입력
|
|
231
|
+
const propertyId = await input({
|
|
232
|
+
message: "GA4 Property ID (숫자만)",
|
|
233
|
+
validate: (value) => {
|
|
234
|
+
if (!value)
|
|
235
|
+
return "Property ID를 입력해주세요";
|
|
236
|
+
if (!/^\d+$/.test(value))
|
|
237
|
+
return "숫자만 입력해주세요";
|
|
238
|
+
return true;
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
// 2. Service Account 키 파일 선택
|
|
242
|
+
console.log("");
|
|
243
|
+
const absolutePath = await selectServiceAccountKey();
|
|
244
|
+
// 3. 연결 테스트
|
|
245
|
+
console.log("");
|
|
246
|
+
const spinner = ora("GA4 연결 테스트 중...").start();
|
|
247
|
+
process.env.GA4_PROPERTY_ID = propertyId;
|
|
248
|
+
process.env.GOOGLE_APPLICATION_CREDENTIALS = absolutePath;
|
|
249
|
+
let connected = false;
|
|
250
|
+
try {
|
|
251
|
+
const client = new GA4Client({ propertyId });
|
|
252
|
+
const result = await client.testConnection();
|
|
253
|
+
if (result.success) {
|
|
254
|
+
spinner.succeed(chalk.green("GA4 연결 성공!"));
|
|
255
|
+
connected = true;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
spinner.fail(chalk.red(`GA4 연결 실패: ${result.message}`));
|
|
259
|
+
const continueSetup = await confirm({
|
|
260
|
+
message: "연결에 실패했습니다. 계속 진행하시겠습니까?",
|
|
261
|
+
default: false,
|
|
262
|
+
});
|
|
263
|
+
if (!continueSetup) {
|
|
264
|
+
throw new Error("설정 취소됨");
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
if (error.message === "설정 취소됨")
|
|
270
|
+
throw error;
|
|
271
|
+
spinner.fail(chalk.red(`GA4 연결 실패: ${error instanceof Error ? error.message : error}`));
|
|
272
|
+
const continueSetup = await confirm({
|
|
273
|
+
message: "계속 진행하시겠습니까?",
|
|
274
|
+
default: false,
|
|
275
|
+
});
|
|
276
|
+
if (!continueSetup) {
|
|
277
|
+
throw new Error("설정 취소됨");
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return { propertyId, credentialsPath: absolutePath, connected };
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* BigQuery 설정
|
|
284
|
+
*/
|
|
285
|
+
export async function runBigQuerySetup() {
|
|
286
|
+
printHeader();
|
|
287
|
+
console.log(chalk.cyan.bold(" BigQuery 설정"));
|
|
288
|
+
console.log("");
|
|
289
|
+
// 1. BigQuery Project ID 입력
|
|
290
|
+
const projectId = await input({
|
|
291
|
+
message: "BigQuery Project ID",
|
|
292
|
+
validate: (value) => {
|
|
293
|
+
if (!value)
|
|
294
|
+
return "Project ID를 입력해주세요";
|
|
295
|
+
return true;
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
// 2. BigQuery Location 선택
|
|
299
|
+
const location = await select({
|
|
300
|
+
message: "BigQuery Location",
|
|
301
|
+
choices: [
|
|
302
|
+
{ name: "US (기본)", value: "US" },
|
|
303
|
+
{ name: "EU", value: "EU" },
|
|
304
|
+
{ name: "asia-northeast3 (서울)", value: "asia-northeast3" },
|
|
305
|
+
{ name: "asia-northeast1 (도쿄)", value: "asia-northeast1" },
|
|
306
|
+
{ name: "직접 입력", value: "__custom__" },
|
|
307
|
+
],
|
|
308
|
+
});
|
|
309
|
+
let finalLocation = location;
|
|
310
|
+
if (location === "__custom__") {
|
|
311
|
+
finalLocation = await input({
|
|
312
|
+
message: "Location 입력 (예: us-central1)",
|
|
313
|
+
default: "US",
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
// 3. Service Account 키 파일 선택
|
|
317
|
+
console.log("");
|
|
318
|
+
const absolutePath = await selectServiceAccountKey();
|
|
319
|
+
// 4. 연결 테스트
|
|
320
|
+
console.log("");
|
|
321
|
+
const spinner = ora("BigQuery 연결 테스트 중...").start();
|
|
322
|
+
process.env.BQ_PROJECT_ID = projectId;
|
|
323
|
+
process.env.BQ_LOCATION = finalLocation;
|
|
324
|
+
process.env.GOOGLE_APPLICATION_CREDENTIALS = absolutePath;
|
|
325
|
+
let connected = false;
|
|
326
|
+
try {
|
|
327
|
+
const client = new BigQueryClient({
|
|
328
|
+
projectId,
|
|
329
|
+
location: finalLocation,
|
|
330
|
+
});
|
|
331
|
+
const result = await client.testConnection();
|
|
332
|
+
if (result.success) {
|
|
333
|
+
spinner.succeed(chalk.green("BigQuery 연결 성공!"));
|
|
334
|
+
connected = true;
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
spinner.fail(chalk.red(`BigQuery 연결 실패: ${result.message}`));
|
|
338
|
+
const continueSetup = await confirm({
|
|
339
|
+
message: "연결에 실패했습니다. 계속 진행하시겠습니까?",
|
|
340
|
+
default: false,
|
|
341
|
+
});
|
|
342
|
+
if (!continueSetup) {
|
|
343
|
+
throw new Error("설정 취소됨");
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
if (error.message === "설정 취소됨")
|
|
349
|
+
throw error;
|
|
350
|
+
spinner.fail(chalk.red(`BigQuery 연결 실패: ${error instanceof Error ? error.message : error}`));
|
|
351
|
+
const continueSetup = await confirm({
|
|
352
|
+
message: "계속 진행하시겠습니까?",
|
|
353
|
+
default: false,
|
|
354
|
+
});
|
|
355
|
+
if (!continueSetup) {
|
|
356
|
+
throw new Error("설정 취소됨");
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
projectId,
|
|
361
|
+
location: finalLocation,
|
|
362
|
+
credentialsPath: absolutePath,
|
|
363
|
+
connected,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* 통합 설정 (GA4 + BigQuery)
|
|
368
|
+
*/
|
|
369
|
+
export async function runSetup() {
|
|
370
|
+
printHeader();
|
|
371
|
+
// 설정할 서비스 선택
|
|
372
|
+
const services = await checkbox({
|
|
373
|
+
message: "설정할 서비스 선택 (Space로 선택, Enter로 확인)",
|
|
374
|
+
choices: [
|
|
375
|
+
{ name: "GA4 Data API", value: "ga4", checked: true },
|
|
376
|
+
{ name: "BigQuery", value: "bigquery", checked: true },
|
|
377
|
+
],
|
|
378
|
+
});
|
|
379
|
+
if (services.length === 0) {
|
|
380
|
+
console.log(chalk.yellow("\n선택된 서비스가 없습니다."));
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
let ga4Config = {};
|
|
384
|
+
let bqConfig = {};
|
|
385
|
+
let credentialsPath = "";
|
|
386
|
+
// GA4 설정
|
|
387
|
+
if (services.includes("ga4")) {
|
|
388
|
+
try {
|
|
389
|
+
const result = await runGA4Setup();
|
|
390
|
+
ga4Config = { propertyId: result.propertyId, connected: result.connected };
|
|
391
|
+
credentialsPath = result.credentialsPath;
|
|
392
|
+
}
|
|
393
|
+
catch (error) {
|
|
394
|
+
if (error.message === "설정 취소됨") {
|
|
395
|
+
console.log(chalk.yellow("\nGA4 설정이 취소되었습니다."));
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// BigQuery 설정
|
|
400
|
+
if (services.includes("bigquery")) {
|
|
401
|
+
try {
|
|
402
|
+
const result = await runBigQuerySetup();
|
|
403
|
+
bqConfig = {
|
|
404
|
+
projectId: result.projectId,
|
|
405
|
+
location: result.location,
|
|
406
|
+
connected: result.connected,
|
|
407
|
+
};
|
|
408
|
+
// credentials가 아직 없으면 사용
|
|
409
|
+
if (!credentialsPath) {
|
|
410
|
+
credentialsPath = result.credentialsPath;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
catch (error) {
|
|
414
|
+
if (error.message === "설정 취소됨") {
|
|
415
|
+
console.log(chalk.yellow("\nBigQuery 설정이 취소되었습니다."));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
// 설정할 내용이 있는지 확인
|
|
420
|
+
if (!ga4Config.propertyId && !bqConfig.projectId) {
|
|
421
|
+
console.log(chalk.yellow("\n설정된 서비스가 없습니다."));
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
// Claude Code MCP 설정
|
|
425
|
+
console.log("");
|
|
426
|
+
const settingsSpinner = ora("Claude Code MCP 설정 중...").start();
|
|
427
|
+
const mcpResult = await runClaudeMcpAdd({
|
|
428
|
+
ga4PropertyId: ga4Config.propertyId,
|
|
429
|
+
bqProjectId: bqConfig.projectId,
|
|
430
|
+
bqLocation: bqConfig.location,
|
|
431
|
+
credentialsPath,
|
|
432
|
+
});
|
|
433
|
+
if (mcpResult.success) {
|
|
434
|
+
settingsSpinner.succeed(chalk.green("Claude Code MCP 서버 등록 완료"));
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
// 수동 설정으로 fallback
|
|
438
|
+
settingsSpinner.warn(chalk.yellow("claude mcp add 실패, 수동 설정으로 진행..."));
|
|
439
|
+
const manualSpinner = ora("설정 파일 직접 수정 중...").start();
|
|
440
|
+
const env = {
|
|
441
|
+
GOOGLE_APPLICATION_CREDENTIALS: credentialsPath,
|
|
442
|
+
};
|
|
443
|
+
if (ga4Config.propertyId) {
|
|
444
|
+
env.GA4_PROPERTY_ID = ga4Config.propertyId;
|
|
445
|
+
}
|
|
446
|
+
if (bqConfig.projectId) {
|
|
447
|
+
env.BQ_PROJECT_ID = bqConfig.projectId;
|
|
448
|
+
}
|
|
449
|
+
if (bqConfig.location) {
|
|
450
|
+
env.BQ_LOCATION = bqConfig.location;
|
|
451
|
+
}
|
|
452
|
+
const mcpConfig = {
|
|
453
|
+
command: "npx",
|
|
454
|
+
args: ["-y", "careerly-data-mcp", "serve"],
|
|
455
|
+
env,
|
|
456
|
+
};
|
|
457
|
+
let settingsPath = CLAUDE_SETTINGS_LOCAL_PATH;
|
|
458
|
+
if (!(await fileExists(CLAUDE_SETTINGS_LOCAL_PATH))) {
|
|
459
|
+
if (await fileExists(CLAUDE_SETTINGS_PATH)) {
|
|
460
|
+
settingsPath = CLAUDE_SETTINGS_PATH;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
try {
|
|
464
|
+
const settings = await readSettings(settingsPath);
|
|
465
|
+
settings.mcpServers = settings.mcpServers || {};
|
|
466
|
+
settings.mcpServers["careerly-data"] = mcpConfig;
|
|
467
|
+
await writeSettings(settingsPath, settings);
|
|
468
|
+
manualSpinner.succeed(chalk.green(`설정 추가됨: ${settingsPath.replace(process.env.HOME || "", "~")}`));
|
|
469
|
+
}
|
|
470
|
+
catch (error) {
|
|
471
|
+
manualSpinner.fail(chalk.red("설정 추가 실패"));
|
|
472
|
+
throw error;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
// 완료 메시지
|
|
476
|
+
console.log("");
|
|
477
|
+
console.log(chalk.green.bold(" ✓ 설정 완료!"));
|
|
478
|
+
console.log(chalk.gray(" ─────────────────────────────────────"));
|
|
479
|
+
console.log("");
|
|
480
|
+
printStatus({
|
|
481
|
+
ga4PropertyId: ga4Config.propertyId,
|
|
482
|
+
bqProjectId: bqConfig.projectId,
|
|
483
|
+
credentialsPath,
|
|
484
|
+
ga4Connected: ga4Config.connected,
|
|
485
|
+
bqConnected: bqConfig.connected,
|
|
486
|
+
tools: 7,
|
|
487
|
+
});
|
|
488
|
+
console.log(chalk.cyan(" Claude Code를 재시작하면 사용할 수 있습니다."));
|
|
489
|
+
console.log("");
|
|
490
|
+
console.log(chalk.gray(" GA4 사용 예시:"));
|
|
491
|
+
console.log(chalk.white(' "지난 7일 세션수 보여줘"'));
|
|
492
|
+
console.log(chalk.white(' "채널별 전환수 알려줘"'));
|
|
493
|
+
console.log("");
|
|
494
|
+
console.log(chalk.gray(" BigQuery 사용 예시:"));
|
|
495
|
+
console.log(chalk.white(' "BigQuery 데이터셋 목록 보여줘"'));
|
|
496
|
+
console.log(chalk.white(' "GA4 이벤트 데이터에서 page_view 조회해줘"'));
|
|
497
|
+
console.log("");
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* 인터랙티브 메뉴
|
|
501
|
+
*/
|
|
502
|
+
export async function runInteractiveMenu() {
|
|
503
|
+
printHeader();
|
|
504
|
+
// 현재 설정 상태 확인
|
|
505
|
+
let currentConfig = { ga4Connected: false, bqConnected: false };
|
|
506
|
+
// settings 파일에서 현재 설정 읽기
|
|
507
|
+
const settingsPath = (await fileExists(CLAUDE_SETTINGS_LOCAL_PATH))
|
|
508
|
+
? CLAUDE_SETTINGS_LOCAL_PATH
|
|
509
|
+
: CLAUDE_SETTINGS_PATH;
|
|
510
|
+
if (await fileExists(settingsPath)) {
|
|
511
|
+
const settings = await readSettings(settingsPath);
|
|
512
|
+
// careerly-data 또는 기존 careerly-ga4 설정 확인
|
|
513
|
+
const mcpConfig = settings.mcpServers?.["careerly-data"] ||
|
|
514
|
+
settings.mcpServers?.["careerly-ga4"];
|
|
515
|
+
if (mcpConfig?.env) {
|
|
516
|
+
currentConfig.ga4PropertyId = mcpConfig.env.GA4_PROPERTY_ID;
|
|
517
|
+
currentConfig.bqProjectId = mcpConfig.env.BQ_PROJECT_ID;
|
|
518
|
+
currentConfig.credentialsPath =
|
|
519
|
+
mcpConfig.env.GOOGLE_APPLICATION_CREDENTIALS;
|
|
520
|
+
// GA4 연결 테스트
|
|
521
|
+
if (currentConfig.ga4PropertyId && currentConfig.credentialsPath) {
|
|
522
|
+
process.env.GA4_PROPERTY_ID = currentConfig.ga4PropertyId;
|
|
523
|
+
process.env.GOOGLE_APPLICATION_CREDENTIALS = currentConfig.credentialsPath;
|
|
524
|
+
try {
|
|
525
|
+
const client = new GA4Client({
|
|
526
|
+
propertyId: currentConfig.ga4PropertyId,
|
|
527
|
+
});
|
|
528
|
+
const result = await client.testConnection();
|
|
529
|
+
currentConfig.ga4Connected = result.success;
|
|
530
|
+
}
|
|
531
|
+
catch {
|
|
532
|
+
currentConfig.ga4Connected = false;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
// BigQuery 연결 테스트
|
|
536
|
+
if (currentConfig.bqProjectId && currentConfig.credentialsPath) {
|
|
537
|
+
process.env.BQ_PROJECT_ID = currentConfig.bqProjectId;
|
|
538
|
+
process.env.GOOGLE_APPLICATION_CREDENTIALS = currentConfig.credentialsPath;
|
|
539
|
+
try {
|
|
540
|
+
const client = new BigQueryClient({
|
|
541
|
+
projectId: currentConfig.bqProjectId,
|
|
542
|
+
});
|
|
543
|
+
const result = await client.testConnection();
|
|
544
|
+
currentConfig.bqConnected = result.success;
|
|
545
|
+
}
|
|
546
|
+
catch {
|
|
547
|
+
currentConfig.bqConnected = false;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
printStatus({
|
|
553
|
+
...currentConfig,
|
|
554
|
+
tools: 7,
|
|
555
|
+
});
|
|
556
|
+
const isConfigured = currentConfig.ga4Connected || currentConfig.bqConnected;
|
|
557
|
+
// 메뉴 선택
|
|
558
|
+
const action = await select({
|
|
559
|
+
message: "선택하세요",
|
|
560
|
+
choices: [
|
|
561
|
+
{
|
|
562
|
+
name: "View tools",
|
|
563
|
+
value: "tools",
|
|
564
|
+
description: "사용 가능한 Tools 목록 보기",
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
name: isConfigured ? "Reconfigure" : "Setup",
|
|
568
|
+
value: "setup",
|
|
569
|
+
description: "GA4 + BigQuery 설정",
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
name: "Setup GA4 only",
|
|
573
|
+
value: "setup-ga4",
|
|
574
|
+
description: "GA4 Data API만 설정",
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
name: "Setup BigQuery only",
|
|
578
|
+
value: "setup-bq",
|
|
579
|
+
description: "BigQuery만 설정",
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
name: "Test connections",
|
|
583
|
+
value: "test",
|
|
584
|
+
description: "연결 상태 테스트",
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
name: isConfigured ? "Disable" : "Exit",
|
|
588
|
+
value: isConfigured ? "disable" : "exit",
|
|
589
|
+
description: isConfigured ? "MCP 서버 비활성화" : "종료",
|
|
590
|
+
},
|
|
591
|
+
],
|
|
592
|
+
});
|
|
593
|
+
switch (action) {
|
|
594
|
+
case "tools":
|
|
595
|
+
await showTools();
|
|
596
|
+
break;
|
|
597
|
+
case "setup":
|
|
598
|
+
await runSetup();
|
|
599
|
+
break;
|
|
600
|
+
case "setup-ga4":
|
|
601
|
+
await runGA4SetupOnly();
|
|
602
|
+
break;
|
|
603
|
+
case "setup-bq":
|
|
604
|
+
await runBigQuerySetupOnly();
|
|
605
|
+
break;
|
|
606
|
+
case "test":
|
|
607
|
+
await testConnections();
|
|
608
|
+
break;
|
|
609
|
+
case "disable":
|
|
610
|
+
await disableServer();
|
|
611
|
+
break;
|
|
612
|
+
case "exit":
|
|
613
|
+
console.log(chalk.gray("\n종료합니다."));
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* GA4만 설정
|
|
619
|
+
*/
|
|
620
|
+
async function runGA4SetupOnly() {
|
|
621
|
+
try {
|
|
622
|
+
const result = await runGA4Setup();
|
|
623
|
+
// Claude Code MCP 설정
|
|
624
|
+
console.log("");
|
|
625
|
+
const settingsSpinner = ora("Claude Code MCP 설정 중...").start();
|
|
626
|
+
const mcpResult = await runClaudeMcpAdd({
|
|
627
|
+
ga4PropertyId: result.propertyId,
|
|
628
|
+
credentialsPath: result.credentialsPath,
|
|
629
|
+
});
|
|
630
|
+
if (mcpResult.success) {
|
|
631
|
+
settingsSpinner.succeed(chalk.green("Claude Code MCP 서버 등록 완료"));
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
await saveSettingsManually({
|
|
635
|
+
ga4PropertyId: result.propertyId,
|
|
636
|
+
credentialsPath: result.credentialsPath,
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
console.log("");
|
|
640
|
+
console.log(chalk.green.bold(" ✓ GA4 설정 완료!"));
|
|
641
|
+
console.log(chalk.cyan(" Claude Code를 재시작하면 사용할 수 있습니다."));
|
|
642
|
+
console.log("");
|
|
643
|
+
}
|
|
644
|
+
catch (error) {
|
|
645
|
+
if (error.message === "설정 취소됨") {
|
|
646
|
+
console.log(chalk.yellow("\n설정이 취소되었습니다."));
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* BigQuery만 설정
|
|
652
|
+
*/
|
|
653
|
+
async function runBigQuerySetupOnly() {
|
|
654
|
+
try {
|
|
655
|
+
const result = await runBigQuerySetup();
|
|
656
|
+
// Claude Code MCP 설정
|
|
657
|
+
console.log("");
|
|
658
|
+
const settingsSpinner = ora("Claude Code MCP 설정 중...").start();
|
|
659
|
+
const mcpResult = await runClaudeMcpAdd({
|
|
660
|
+
bqProjectId: result.projectId,
|
|
661
|
+
bqLocation: result.location,
|
|
662
|
+
credentialsPath: result.credentialsPath,
|
|
663
|
+
});
|
|
664
|
+
if (mcpResult.success) {
|
|
665
|
+
settingsSpinner.succeed(chalk.green("Claude Code MCP 서버 등록 완료"));
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
await saveSettingsManually({
|
|
669
|
+
bqProjectId: result.projectId,
|
|
670
|
+
bqLocation: result.location,
|
|
671
|
+
credentialsPath: result.credentialsPath,
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
console.log("");
|
|
675
|
+
console.log(chalk.green.bold(" ✓ BigQuery 설정 완료!"));
|
|
676
|
+
console.log(chalk.cyan(" Claude Code를 재시작하면 사용할 수 있습니다."));
|
|
677
|
+
console.log("");
|
|
678
|
+
}
|
|
679
|
+
catch (error) {
|
|
680
|
+
if (error.message === "설정 취소됨") {
|
|
681
|
+
console.log(chalk.yellow("\n설정이 취소되었습니다."));
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* 수동 설정 저장
|
|
687
|
+
*/
|
|
688
|
+
async function saveSettingsManually(config) {
|
|
689
|
+
const manualSpinner = ora("설정 파일 직접 수정 중...").start();
|
|
690
|
+
const env = {
|
|
691
|
+
GOOGLE_APPLICATION_CREDENTIALS: config.credentialsPath,
|
|
692
|
+
};
|
|
693
|
+
if (config.ga4PropertyId) {
|
|
694
|
+
env.GA4_PROPERTY_ID = config.ga4PropertyId;
|
|
695
|
+
}
|
|
696
|
+
if (config.bqProjectId) {
|
|
697
|
+
env.BQ_PROJECT_ID = config.bqProjectId;
|
|
698
|
+
}
|
|
699
|
+
if (config.bqLocation) {
|
|
700
|
+
env.BQ_LOCATION = config.bqLocation;
|
|
701
|
+
}
|
|
702
|
+
const mcpConfig = {
|
|
703
|
+
command: "npx",
|
|
704
|
+
args: ["-y", "careerly-data-mcp", "serve"],
|
|
705
|
+
env,
|
|
706
|
+
};
|
|
707
|
+
let settingsPath = CLAUDE_SETTINGS_LOCAL_PATH;
|
|
708
|
+
if (!(await fileExists(CLAUDE_SETTINGS_LOCAL_PATH))) {
|
|
709
|
+
if (await fileExists(CLAUDE_SETTINGS_PATH)) {
|
|
710
|
+
settingsPath = CLAUDE_SETTINGS_PATH;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
try {
|
|
714
|
+
const settings = await readSettings(settingsPath);
|
|
715
|
+
settings.mcpServers = settings.mcpServers || {};
|
|
716
|
+
settings.mcpServers["careerly-data"] = mcpConfig;
|
|
717
|
+
await writeSettings(settingsPath, settings);
|
|
718
|
+
manualSpinner.succeed(chalk.green(`설정 추가됨: ${settingsPath.replace(process.env.HOME || "", "~")}`));
|
|
719
|
+
}
|
|
720
|
+
catch (error) {
|
|
721
|
+
manualSpinner.fail(chalk.red("설정 추가 실패"));
|
|
722
|
+
throw error;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Tools 목록 보기
|
|
727
|
+
*/
|
|
728
|
+
async function showTools() {
|
|
729
|
+
console.log("");
|
|
730
|
+
console.log(chalk.white.bold(" GA4 Data API Tools:"));
|
|
731
|
+
console.log("");
|
|
732
|
+
console.log(chalk.cyan(" ga4_query"));
|
|
733
|
+
console.log(chalk.gray(" GA4 데이터 조회 및 인사이트 생성"));
|
|
734
|
+
console.log(chalk.gray(" 예: 지난 7일 세션수, 채널별 전환수"));
|
|
735
|
+
console.log("");
|
|
736
|
+
console.log(chalk.cyan(" ga4_metadata"));
|
|
737
|
+
console.log(chalk.gray(" 사용 가능한 지표/차원 목록 확인"));
|
|
738
|
+
console.log("");
|
|
739
|
+
console.log(chalk.cyan(" ga4_status"));
|
|
740
|
+
console.log(chalk.gray(" GA4 연결 상태 확인"));
|
|
741
|
+
console.log("");
|
|
742
|
+
console.log(chalk.white.bold(" BigQuery Tools:"));
|
|
743
|
+
console.log("");
|
|
744
|
+
console.log(chalk.cyan(" bq_query"));
|
|
745
|
+
console.log(chalk.gray(" BigQuery 테이블 파라미터 기반 조회"));
|
|
746
|
+
console.log(chalk.gray(" 예: 특정 테이블에서 조건별 데이터 조회"));
|
|
747
|
+
console.log("");
|
|
748
|
+
console.log(chalk.cyan(" bq_ga4_events"));
|
|
749
|
+
console.log(chalk.gray(" GA4 Export 이벤트 데이터 조회"));
|
|
750
|
+
console.log(chalk.gray(" 예: page_view, purchase 이벤트 분석"));
|
|
751
|
+
console.log("");
|
|
752
|
+
console.log(chalk.cyan(" bq_metadata"));
|
|
753
|
+
console.log(chalk.gray(" 데이터셋/테이블 목록 및 스키마 조회"));
|
|
754
|
+
console.log("");
|
|
755
|
+
console.log(chalk.cyan(" bq_status"));
|
|
756
|
+
console.log(chalk.gray(" BigQuery 연결 상태 확인"));
|
|
757
|
+
console.log("");
|
|
758
|
+
await select({
|
|
759
|
+
message: "",
|
|
760
|
+
choices: [{ name: "← Back", value: "back" }],
|
|
761
|
+
});
|
|
762
|
+
await runInteractiveMenu();
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* 연결 테스트
|
|
766
|
+
*/
|
|
767
|
+
async function testConnections() {
|
|
768
|
+
console.log("");
|
|
769
|
+
// GA4 테스트
|
|
770
|
+
const ga4Spinner = ora("GA4 연결 테스트 중...").start();
|
|
771
|
+
try {
|
|
772
|
+
const client = new GA4Client();
|
|
773
|
+
const result = await client.testConnection();
|
|
774
|
+
if (result.success) {
|
|
775
|
+
ga4Spinner.succeed(chalk.green(`GA4: ${result.message}`));
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
ga4Spinner.fail(chalk.red(`GA4: ${result.message}`));
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
catch (error) {
|
|
782
|
+
ga4Spinner.fail(chalk.red(`GA4: ${error instanceof Error ? error.message : error}`));
|
|
783
|
+
}
|
|
784
|
+
// BigQuery 테스트
|
|
785
|
+
const bqSpinner = ora("BigQuery 연결 테스트 중...").start();
|
|
786
|
+
try {
|
|
787
|
+
const client = new BigQueryClient();
|
|
788
|
+
const result = await client.testConnection();
|
|
789
|
+
if (result.success) {
|
|
790
|
+
bqSpinner.succeed(chalk.green(`BigQuery: ${result.message}`));
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
bqSpinner.fail(chalk.red(`BigQuery: ${result.message}`));
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
catch (error) {
|
|
797
|
+
bqSpinner.fail(chalk.red(`BigQuery: ${error instanceof Error ? error.message : error}`));
|
|
798
|
+
}
|
|
799
|
+
console.log("");
|
|
800
|
+
await select({
|
|
801
|
+
message: "",
|
|
802
|
+
choices: [{ name: "← Back", value: "back" }],
|
|
803
|
+
});
|
|
804
|
+
await runInteractiveMenu();
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* 서버 비활성화
|
|
808
|
+
*/
|
|
809
|
+
async function disableServer() {
|
|
810
|
+
const confirmed = await confirm({
|
|
811
|
+
message: "MCP 서버를 비활성화하시겠습니까?",
|
|
812
|
+
default: false,
|
|
813
|
+
});
|
|
814
|
+
if (!confirmed) {
|
|
815
|
+
await runInteractiveMenu();
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
const spinner = ora("MCP 서버 비활성화 중...").start();
|
|
819
|
+
try {
|
|
820
|
+
// claude mcp remove 먼저 시도
|
|
821
|
+
try {
|
|
822
|
+
execSync("claude mcp remove careerly-data -s user", { stdio: "pipe" });
|
|
823
|
+
}
|
|
824
|
+
catch {
|
|
825
|
+
// 기존 이름으로도 시도
|
|
826
|
+
try {
|
|
827
|
+
execSync("claude mcp remove careerly-ga4 -s user", { stdio: "pipe" });
|
|
828
|
+
}
|
|
829
|
+
catch {
|
|
830
|
+
// 무시
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
// 수동 삭제
|
|
834
|
+
const settingsPath = (await fileExists(CLAUDE_SETTINGS_LOCAL_PATH))
|
|
835
|
+
? CLAUDE_SETTINGS_LOCAL_PATH
|
|
836
|
+
: CLAUDE_SETTINGS_PATH;
|
|
837
|
+
if (await fileExists(settingsPath)) {
|
|
838
|
+
const settings = await readSettings(settingsPath);
|
|
839
|
+
let deleted = false;
|
|
840
|
+
if (settings.mcpServers?.["careerly-data"]) {
|
|
841
|
+
delete settings.mcpServers["careerly-data"];
|
|
842
|
+
deleted = true;
|
|
843
|
+
}
|
|
844
|
+
if (settings.mcpServers?.["careerly-ga4"]) {
|
|
845
|
+
delete settings.mcpServers["careerly-ga4"];
|
|
846
|
+
deleted = true;
|
|
847
|
+
}
|
|
848
|
+
if (deleted) {
|
|
849
|
+
await writeSettings(settingsPath, settings);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
spinner.succeed(chalk.green("MCP 서버가 비활성화되었습니다."));
|
|
853
|
+
}
|
|
854
|
+
catch {
|
|
855
|
+
spinner.fail(chalk.red("비활성화 실패"));
|
|
856
|
+
}
|
|
857
|
+
console.log("");
|
|
858
|
+
}
|
|
859
|
+
//# sourceMappingURL=setup.js.map
|