connectbase-client 0.10.2 → 0.10.4

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/cli.js CHANGED
@@ -369,7 +369,7 @@ ${colors.cyan}Connect Base \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654${colors.r
369
369
  return;
370
370
  }
371
371
  }
372
- log(`${colors.dim}Secret Key (cb_sk_): \uCF58\uC194 > \uD504\uB85C\uD544 > MCP Key \uD0ED\uC5D0\uC11C \uBC1C\uAE09${colors.reset}
372
+ log(`${colors.dim}Secret Key (cb_sk_): \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uBC1C\uAE09${colors.reset}
373
373
  `);
374
374
  const secretKey = await promptSecret(`${colors.blue}?${colors.reset} Secret Key: `);
375
375
  if (!secretKey) {
@@ -378,7 +378,7 @@ ${colors.cyan}Connect Base \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654${colors.r
378
378
  }
379
379
  if (secretKey.startsWith("cb_pk_")) {
380
380
  error("Public Key\uAC00 \uC544\uB2CC Secret Key(cb_sk_)\uB97C \uC785\uB825\uD558\uC138\uC694");
381
- info("Secret Key\uB294 \uCF58\uC194 > \uD504\uB85C\uD544 > MCP Key \uD0ED\uC5D0\uC11C \uC0DD\uC131\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
381
+ info("Secret Key\uB294 \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uC0DD\uC131\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
382
382
  process.exit(1);
383
383
  }
384
384
  if (!secretKey.startsWith("cb_sk_")) {
@@ -712,21 +712,12 @@ function updateRootClaudeMd(claudeMdPath) {
712
712
  ## ConnectBase SDK
713
713
 
714
714
  \uC774 \uD504\uB85C\uC81D\uD2B8\uB294 **ConnectBase**\uB97C \uBC31\uC5D4\uB4DC\uB85C \uC0AC\uC6A9\uD569\uB2C8\uB2E4.
715
- \uC0AC\uC6A9\uC790\uB294 **\uBE44\uAC1C\uBC1C\uC790**\uC785\uB2C8\uB2E4. \uC26C\uC6B4 \uB9D0\uB85C \uC124\uBA85\uD558\uACE0, \uBC14\uB85C \uC2E4\uD589 \uAC00\uB2A5\uD55C \uC644\uC131\uB41C \uCF54\uB4DC\uB97C \uC81C\uACF5\uD558\uC138\uC694.
716
715
 
717
716
  ### AI \uD544\uB3C5 \u2014 \uCF54\uB529 \uC804 \uBC18\uB4DC\uC2DC \uC218\uD589
718
717
 
719
- 1. \`.claude/docs/project-rules.md\` \uD30C\uC77C\uC744 **Read tool\uB85C \uC77D\uC73C\uC138\uC694** (\uAE30\uB2A5 \uB9E4\uD551\uD45C, \uBCF4\uC548 \uADDC\uCE59 \uD3EC\uD568)
720
- 2. \uC0AC\uC6A9\uC790 \uC694\uCCAD\uC5D0 \uB9DE\uB294 \uAE30\uB2A5\uC744 \uB9E4\uD551\uD45C\uC5D0\uC11C \uCC3E\uC73C\uC138\uC694
721
- 3. \`search_sdk_docs("\uD0A4\uC6CC\uB4DC")\`\uB85C \uAD6C\uD604 \uBC29\uBC95\uC744 \uAC80\uC0C9\uD558\uC138\uC694
722
- 4. \uC644\uC131\uB41C \uCF54\uB4DC\uB97C \uC791\uC131\uD558\uC138\uC694 (\uC2A4\uB2C8\uD3AB X, \uBC14\uB85C \uC2E4\uD589 \uAC00\uB2A5\uD55C \uC804\uCCB4 \uCF54\uB4DC)
723
-
724
- ### \uBE44\uAC1C\uBC1C\uC790 \uC751\uB300 \uC6D0\uCE59
725
-
726
- - \uC804\uBB38 \uC6A9\uC5B4 \uB300\uC2E0 \uC26C\uC6B4 \uB9D0 \uC0AC\uC6A9 ("API" \u2192 "\uAE30\uB2A5", "DB" \u2192 "\uC800\uC7A5\uC18C")
727
- - \uC5D0\uB7EC \uBC1C\uC0DD \uC2DC \uC6D0\uC778\uACFC \uD574\uACB0\uCC45\uC744 \uD55C \uC904\uB85C \uC124\uBA85
728
- - \uCF54\uB4DC \uC791\uC131 \uC804 "\uC774\uB807\uAC8C \uB9CC\uB4E4\uAC8C\uC694~" \uD55C \uC904 \uC694\uC57D \uBA3C\uC800
729
- - \uBAA8\uB974\uB294 \uAC8C \uC788\uC73C\uBA74 \uB9CC\uB4E4\uAE30 \uC804\uC5D0 \uBA3C\uC800 \uC9C8\uBB38
718
+ 1. \`.claude/docs/project-rules.md\`\uB97C **Read tool\uB85C \uC77D\uC73C\uC138\uC694** (\uAE30\uB2A5 \uB9E4\uD551\uD45C, \uBCF4\uC548 \uADDC\uCE59, \uC5D0\uB7EC \uAC00\uC774\uB4DC)
719
+ 2. \`search_sdk_docs("\uD0A4\uC6CC\uB4DC")\`\uB85C \uAD6C\uD604 \uBC29\uBC95\uC744 \uAC80\uC0C9\uD558\uC138\uC694
720
+ 3. \uAC80\uC0C9 \uACB0\uACFC\uC758 \uCF54\uB4DC \uD328\uD134\uC744 \uB530\uB77C \uAD6C\uD604\uD558\uC138\uC694
730
721
  ${endMarker}`;
731
722
  if (fs.existsSync(claudeMdPath)) {
732
723
  let content = fs.readFileSync(claudeMdPath, "utf-8");
@@ -735,7 +726,7 @@ ${endMarker}`;
735
726
  if (startIdx !== -1 && endIdx !== -1) {
736
727
  content = content.substring(0, startIdx) + sdkBlock + content.substring(endIdx + endMarker.length);
737
728
  } else {
738
- content = content.trimEnd() + "\n\n" + sdkBlock + "\n";
729
+ content = sdkBlock + "\n\n" + content.trimStart();
739
730
  }
740
731
  fs.writeFileSync(claudeMdPath, content);
741
732
  info("CLAUDE.md\uC5D0 ConnectBase \uCC38\uC870 \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC");
@@ -782,10 +773,20 @@ ${subBlock}
782
773
  }
783
774
  success(`\uC11C\uBE0C \uD328\uD0A4\uC9C0 CLAUDE.md \uC0DD\uC131 \uC644\uB8CC: ${subClaudeMdPath}`);
784
775
  }
785
- async function setupClaudeCode(apiKey, secretKey, projectRoot) {
786
- const root = projectRoot || getProjectRoot();
776
+ async function setupMcp(secretKey) {
777
+ const gitRoot = getGitRoot();
778
+ const root = gitRoot || process.cwd();
779
+ const monorepo = detectMonorepo(root);
780
+ if (monorepo.type !== "none") {
781
+ info(`\uBAA8\uB178\uB808\uD3EC \uAC10\uC9C0: ${monorepo.type} (\uB8E8\uD2B8: ${root})`);
782
+ if (monorepo.isSubPackage) {
783
+ info(`MCP \uC124\uC815\uC740 \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8(${root})\uC5D0 \uC0DD\uC131\uB429\uB2C8\uB2E4`);
784
+ }
785
+ }
787
786
  const mcpConfigPath = path.join(root, ".mcp.json");
788
- await downloadDocs(apiKey, void 0, root);
787
+ if (!secretKey) {
788
+ secretKey = await prompt(`${colors.blue}?${colors.reset} Secret Key (cb_sk_...): `);
789
+ }
789
790
  const mcpEntry = {
790
791
  type: "http",
791
792
  url: "https://mcp.connectbase.world/mcp",
@@ -809,14 +810,21 @@ async function setupClaudeCode(apiKey, secretKey, projectRoot) {
809
810
  }
810
811
  mcpConfig.mcpServers["connect-base"] = mcpEntry;
811
812
  fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + "\n");
812
- success(".mcp.json \uC0DD\uC131 \uC644\uB8CC");
813
+ success(`${mcpConfigPath} \uC0DD\uC131 \uC644\uB8CC`);
813
814
  addToGitignore(".mcp.json", root);
814
- if (secretKey) {
815
- success("MCP \uC11C\uBC84 \uC790\uB3D9 \uC124\uC815 \uC644\uB8CC (Secret Key \uC801\uC6A9\uB428)");
815
+ if (secretKey && secretKey !== "YOUR_SECRET_KEY_HERE") {
816
+ success("MCP \uC11C\uBC84 \uC124\uC815 \uC644\uB8CC (Secret Key \uC801\uC6A9\uB428)");
816
817
  } else {
817
818
  warn("MCP \uC11C\uBC84\uB294 Secret Key (cb_sk_)\uB9CC \uD5C8\uC6A9\uD569\uB2C8\uB2E4.");
818
819
  info(".mcp.json \uD30C\uC77C\uC758 YOUR_SECRET_KEY_HERE\uB97C Secret Key\uB85C \uAD50\uCCB4\uD558\uC138\uC694.");
819
820
  }
821
+ log(`
822
+ ${colors.dim}Claude Code\uC5D0\uC11C ConnectBase MCP \uB3C4\uAD6C\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.${colors.reset}`);
823
+ }
824
+ async function setupClaudeCode(apiKey, secretKey, projectRoot) {
825
+ const root = projectRoot || getProjectRoot();
826
+ await downloadDocs(apiKey, void 0, root);
827
+ await setupMcp(secretKey);
820
828
  }
821
829
  function createWsTextFrame(payload) {
822
830
  const data = Buffer.from(payload, "utf-8");
@@ -932,15 +940,89 @@ function getTunnelServerUrl(baseUrl) {
932
940
  }
933
941
  return baseUrl.replace(/:\d+/, ":8090");
934
942
  }
943
+ async function resolveAppForTunnel(apiKey, baseUrl, appIdOption) {
944
+ if (appIdOption) {
945
+ return appIdOption;
946
+ }
947
+ let apps = [];
948
+ try {
949
+ info("\uC571 \uBAA9\uB85D \uC870\uD68C \uC911...");
950
+ const appsRes = await makeRequest(
951
+ `${baseUrl}/v1/public/cli/apps`,
952
+ "GET",
953
+ { "X-API-Key": apiKey }
954
+ );
955
+ if (appsRes.status === 401) {
956
+ error("Secret Key\uAC00 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uCF58\uC194\uC5D0\uC11C \uD0A4\uB97C \uD655\uC778\uD558\uC138\uC694");
957
+ process.exit(1);
958
+ }
959
+ if (appsRes.status !== 200) {
960
+ throw new Error(`HTTP ${appsRes.status}`);
961
+ }
962
+ const appsData = appsRes.data;
963
+ apps = appsData.apps || [];
964
+ } catch (err) {
965
+ error(`\uC571 \uBAA9\uB85D \uC870\uD68C \uC2E4\uD328: ${err instanceof Error ? err.message : err}`);
966
+ process.exit(1);
967
+ }
968
+ if (apps.length === 1) {
969
+ success(`\uC571 \uC790\uB3D9 \uC120\uD0DD: ${apps[0].name}`);
970
+ return apps[0].id;
971
+ }
972
+ if (apps.length > 0) {
973
+ log(`
974
+ ${colors.dim}\uB0B4 \uC571 \uBAA9\uB85D:${colors.reset}`);
975
+ apps.forEach((a, i) => {
976
+ const date = a.created_at ? a.created_at.substring(0, 10) : "";
977
+ log(` ${colors.cyan}${i + 1}${colors.reset}) ${a.name} ${colors.dim}(${date})${colors.reset}`);
978
+ });
979
+ }
980
+ log(` ${colors.cyan}0${colors.reset}) \uC0C8 \uC571 \uB9CC\uB4E4\uAE30`);
981
+ const choice = await prompt(`
982
+ ${colors.blue}?${colors.reset} \uC571 \uC120\uD0DD (\uBC88\uD638): `);
983
+ const num = parseInt(choice, 10);
984
+ if (num > 0 && num <= apps.length) {
985
+ success(`\uC120\uD0DD\uB428: ${apps[num - 1].name}`);
986
+ return apps[num - 1].id;
987
+ }
988
+ const projectName = path.basename(process.cwd());
989
+ const appName = await prompt(`${colors.blue}?${colors.reset} \uC571 \uC774\uB984 (${projectName}): `) || projectName;
990
+ info("\uC571 \uC0DD\uC131 \uC911...");
991
+ const createRes = await makeRequest(
992
+ `${baseUrl}/v1/public/cli/apps`,
993
+ "POST",
994
+ { "X-API-Key": apiKey },
995
+ JSON.stringify({ name: appName })
996
+ );
997
+ if (createRes.status === 402) {
998
+ error("\uC571 \uC0DD\uC131 \uD55C\uB3C4 \uCD08\uACFC. \uD50C\uB79C \uC5C5\uADF8\uB808\uC774\uB4DC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4");
999
+ process.exit(1);
1000
+ }
1001
+ if (createRes.status !== 201) {
1002
+ const data = createRes.data;
1003
+ error(`\uC571 \uC0DD\uC131 \uC2E4\uD328: ${data?.error || `HTTP ${createRes.status}`}`);
1004
+ process.exit(1);
1005
+ }
1006
+ const createData = createRes.data;
1007
+ success(`\uC571 \uC0DD\uC131 \uC644\uB8CC: ${createData.app_name}`);
1008
+ return createData.app_id;
1009
+ }
935
1010
  async function startTunnel(port, config, tunnelOpts) {
936
1011
  if (!config.apiKey) {
937
- error("API Key\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. -k \uC635\uC158 \uB610\uB294 CONNECTBASE_API_KEY \uD658\uACBD\uBCC0\uC218\uB97C \uC124\uC815\uD558\uC138\uC694");
1012
+ error("Secret Key\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. -k \uC635\uC158 \uB610\uB294 CONNECTBASE_API_KEY \uD658\uACBD\uBCC0\uC218\uB97C \uC124\uC815\uD558\uC138\uC694");
1013
+ info("Secret Key\uB294 \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uC0DD\uC131\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
1014
+ process.exit(1);
1015
+ }
1016
+ if (!config.apiKey.startsWith("cb_sk_")) {
1017
+ error("\uD130\uB110\uC740 \uC720\uC800 Secret Key(cb_sk_)\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. Public Key(cb_pk_)\uB294 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
1018
+ info("Secret Key\uB294 \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uC0DD\uC131\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
938
1019
  process.exit(1);
939
1020
  }
1021
+ const appId = await resolveAppForTunnel(config.apiKey, config.baseUrl, tunnelOpts?.appId);
940
1022
  const tunnelServerUrl = getTunnelServerUrl(config.baseUrl);
941
1023
  const parsedUrl = new URL(tunnelServerUrl);
942
1024
  const isHttps = parsedUrl.protocol === "https:";
943
- let wsPath = `/v1/tunnel/connect?api_key=${encodeURIComponent(config.apiKey)}`;
1025
+ let wsPath = `/v1/tunnel/connect?app_id=${encodeURIComponent(appId)}`;
944
1026
  if (tunnelOpts?.timeout) {
945
1027
  wsPath += `&timeout=${tunnelOpts.timeout}`;
946
1028
  }
@@ -986,7 +1068,8 @@ ${colors.cyan}ConnectBase Tunnel${colors.reset}`);
986
1068
  "Upgrade": "websocket",
987
1069
  "Connection": "Upgrade",
988
1070
  "Sec-WebSocket-Key": wsKey,
989
- "Sec-WebSocket-Version": "13"
1071
+ "Sec-WebSocket-Version": "13",
1072
+ "Authorization": `Bearer ${config.apiKey}`
990
1073
  }
991
1074
  };
992
1075
  const req = lib.request(reqOptions);
@@ -1112,29 +1195,78 @@ ${colors.dim}Ctrl+C\uB85C \uC885\uB8CC${colors.reset}
1112
1195
  headers: localHeaders
1113
1196
  };
1114
1197
  const localReq = http.request(reqOptions, (res) => {
1115
- const chunks = [];
1116
- res.on("data", (chunk) => chunks.push(chunk));
1117
- res.on("end", () => {
1118
- const body = Buffer.concat(chunks);
1119
- const responseHeaders = {};
1120
- for (const [key, value] of Object.entries(res.headers)) {
1121
- if (value) responseHeaders[key] = Array.isArray(value) ? value.join(", ") : value;
1122
- }
1123
- const response = {
1124
- type: "http_response",
1125
- request_id: requestId,
1126
- status: res.statusCode || 200,
1127
- headers: responseHeaders,
1128
- body: body.length > 0 ? body.toString("base64") : ""
1129
- };
1198
+ const responseHeaders = {};
1199
+ for (const [key, value] of Object.entries(res.headers)) {
1200
+ if (value) responseHeaders[key] = Array.isArray(value) ? value.join(", ") : value;
1201
+ }
1202
+ const contentType = (responseHeaders["content-type"] || "").toLowerCase();
1203
+ const transferEncoding = (responseHeaders["transfer-encoding"] || "").toLowerCase();
1204
+ const isStreaming = contentType.includes("text/event-stream") || transferEncoding.includes("chunked");
1205
+ if (isStreaming) {
1130
1206
  try {
1131
- sock.write(createWsTextFrame(JSON.stringify(response)));
1132
- const methodColor = method === "GET" ? colors.green : method === "POST" ? colors.blue : colors.yellow;
1133
- log(`${colors.dim}${(/* @__PURE__ */ new Date()).toLocaleTimeString()}${colors.reset} ${methodColor}${method}${colors.reset} ${reqPath} \u2192 ${res.statusCode}`);
1207
+ sock.write(createWsTextFrame(JSON.stringify({
1208
+ type: "http_response_start",
1209
+ request_id: requestId,
1210
+ status: res.statusCode || 200,
1211
+ headers: responseHeaders
1212
+ })));
1134
1213
  } catch {
1135
- warn(`\uC751\uB2F5 \uC804\uC1A1 \uC2E4\uD328: ${requestId}`);
1214
+ warn(`\uC2A4\uD2B8\uB9AC\uBC0D \uC2DC\uC791 \uC804\uC1A1 \uC2E4\uD328: ${requestId}`);
1215
+ return;
1136
1216
  }
1137
- });
1217
+ const methodColor = method === "GET" ? colors.green : method === "POST" ? colors.blue : colors.yellow;
1218
+ log(`${colors.dim}${(/* @__PURE__ */ new Date()).toLocaleTimeString()}${colors.reset} ${methodColor}${method}${colors.reset} ${reqPath} \u2192 ${res.statusCode} ${colors.cyan}[stream]${colors.reset}`);
1219
+ res.on("data", (chunk) => {
1220
+ try {
1221
+ sock.write(createWsTextFrame(JSON.stringify({
1222
+ type: "http_response_chunk",
1223
+ request_id: requestId,
1224
+ data: chunk.toString("base64")
1225
+ })));
1226
+ } catch {
1227
+ warn(`\uC2A4\uD2B8\uB9AC\uBC0D \uCCAD\uD06C \uC804\uC1A1 \uC2E4\uD328: ${requestId}`);
1228
+ }
1229
+ });
1230
+ res.on("end", () => {
1231
+ try {
1232
+ sock.write(createWsTextFrame(JSON.stringify({
1233
+ type: "http_response_end",
1234
+ request_id: requestId
1235
+ })));
1236
+ } catch {
1237
+ }
1238
+ });
1239
+ res.on("error", (err) => {
1240
+ try {
1241
+ sock.write(createWsTextFrame(JSON.stringify({
1242
+ type: "http_response_error",
1243
+ request_id: requestId,
1244
+ error: err.message
1245
+ })));
1246
+ } catch {
1247
+ }
1248
+ });
1249
+ } else {
1250
+ const chunks = [];
1251
+ res.on("data", (chunk) => chunks.push(chunk));
1252
+ res.on("end", () => {
1253
+ const body = Buffer.concat(chunks);
1254
+ const response = {
1255
+ type: "http_response",
1256
+ request_id: requestId,
1257
+ status: res.statusCode || 200,
1258
+ headers: responseHeaders,
1259
+ body: body.length > 0 ? body.toString("base64") : ""
1260
+ };
1261
+ try {
1262
+ sock.write(createWsTextFrame(JSON.stringify(response)));
1263
+ const methodColor = method === "GET" ? colors.green : method === "POST" ? colors.blue : colors.yellow;
1264
+ log(`${colors.dim}${(/* @__PURE__ */ new Date()).toLocaleTimeString()}${colors.reset} ${methodColor}${method}${colors.reset} ${reqPath} \u2192 ${res.statusCode}`);
1265
+ } catch {
1266
+ warn(`\uC751\uB2F5 \uC804\uC1A1 \uC2E4\uD328: ${requestId}`);
1267
+ }
1268
+ });
1269
+ }
1138
1270
  });
1139
1271
  localReq.on("error", (err) => {
1140
1272
  const response = {
@@ -1168,7 +1300,8 @@ ${colors.yellow}\uC0AC\uC6A9\uBC95:${colors.reset}
1168
1300
 
1169
1301
  ${colors.yellow}\uBA85\uB839\uC5B4:${colors.reset}
1170
1302
  init \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654 (\uC571 \uC0DD\uC131, MCP \uC124\uC815, SDK \uBB38\uC11C \uB2E4\uC6B4\uB85C\uB4DC)
1171
- docs SDK \uBB38\uC11C \uB2E4\uC6B4\uB85C\uB4DC/\uC5C5\uB370\uC774\uD2B8 (\uC790\uB3D9\uC73C\uB85C git root\uC5D0 \uC0DD\uC131)
1303
+ docs SDK \uBB38\uC11C \uB2E4\uC6B4\uB85C\uB4DC/\uC5C5\uB370\uC774\uD2B8 (\uBAA8\uB178\uB808\uD3EC \uC790\uB3D9 \uAC10\uC9C0)
1304
+ mcp MCP \uC11C\uBC84 \uC124\uC815 (.mcp.json \uC0DD\uC131/\uC5C5\uB370\uC774\uD2B8, \uBAA8\uB178\uB808\uD3EC \uC790\uB3D9 \uAC10\uC9C0)
1172
1305
  deploy <directory> \uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0\uC5D0 \uD30C\uC77C \uBC30\uD3EC (--dev: Dev \uD658\uACBD)
1173
1306
  tunnel <port> \uB85C\uCEEC \uC11C\uBE44\uC2A4\uB97C \uC778\uD130\uB137\uC5D0 \uB178\uCD9C
1174
1307
 
@@ -1186,7 +1319,7 @@ ${colors.yellow}\uC635\uC158:${colors.reset}
1186
1319
  -t, --timeout <sec> \uD130\uB110 \uC694\uCCAD \uD0C0\uC784\uC544\uC6C3 (\uCD08, tunnel \uC804\uC6A9)
1187
1320
  --max-body <MB> \uD130\uB110 \uCD5C\uB300 \uBC14\uB514 \uD06C\uAE30 (MB, tunnel \uC804\uC6A9)
1188
1321
  -d, --dev Dev \uD658\uACBD\uC5D0 \uBC30\uD3EC (deploy \uC804\uC6A9)
1189
- (docs\uB294 \uC790\uB3D9\uC73C\uB85C git root\uB97C \uAC10\uC9C0\uD558\uC5EC \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC5D0 \uC0DD\uC131)
1322
+ (docs, mcp\uB294 \uBAA8\uB178\uB808\uD3EC\uB97C \uC790\uB3D9 \uAC10\uC9C0\uD558\uC5EC \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC5D0 \uC0DD\uC131)
1190
1323
  -h, --help \uB3C4\uC6C0\uB9D0 \uD45C\uC2DC
1191
1324
  -v, --version \uBC84\uC804 \uD45C\uC2DC
1192
1325
 
@@ -1197,7 +1330,10 @@ ${colors.yellow}\uBE60\uB978 \uC2DC\uC791:${colors.reset}
1197
1330
  ${colors.dim}# 2. SDK \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8${colors.reset}
1198
1331
  npx connectbase docs
1199
1332
 
1200
- ${colors.dim}# 3. Prod \uBC30\uD3EC${colors.reset}
1333
+ ${colors.dim}# 3. MCP \uC11C\uBC84 \uC124\uC815${colors.reset}
1334
+ npx connectbase mcp
1335
+
1336
+ ${colors.dim}# 4. Prod \uBC30\uD3EC${colors.reset}
1201
1337
  npm run deploy
1202
1338
 
1203
1339
  ${colors.dim}# 4. Dev \uD658\uACBD \uBC30\uD3EC (\uB0B4\uBD80 QA\uC6A9)${colors.reset}
@@ -1240,6 +1376,8 @@ function parseArgs(args) {
1240
1376
  result.options.timeout = args[++i];
1241
1377
  } else if (arg === "--max-body") {
1242
1378
  result.options.maxBody = args[++i];
1379
+ } else if (arg === "-a" || arg === "--app") {
1380
+ result.options.appId = args[++i];
1243
1381
  } else if (arg === "-d" || arg === "--dev") {
1244
1382
  result.options.dev = "true";
1245
1383
  } else if (arg === "-h" || arg === "--help") {
@@ -1285,6 +1423,8 @@ async function main() {
1285
1423
  }
1286
1424
  }
1287
1425
  await downloadDocs(docsApiKey);
1426
+ } else if (parsed.command === "mcp") {
1427
+ await setupMcp();
1288
1428
  } else if (parsed.command === "deploy") {
1289
1429
  const directory = parsed.args[0] || fileConfig.deployDir || ".";
1290
1430
  if (!config.apiKey) {
@@ -1317,6 +1457,9 @@ async function main() {
1317
1457
  const m = parseInt(parsed.options.maxBody, 10);
1318
1458
  if (!isNaN(m) && m > 0) tunnelOpts.maxBody = m;
1319
1459
  }
1460
+ if (parsed.options.appId) {
1461
+ tunnelOpts.appId = parsed.options.appId;
1462
+ }
1320
1463
  await startTunnel(port, config, tunnelOpts);
1321
1464
  } else {
1322
1465
  error(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839\uC5B4: ${parsed.command}`);