@cloudbase/cloudbase-mcp
Version:
CloudBase MCP Server — operate Tencent CloudBase (database, auth, functions, storage, hosting) from AI coding tools via the Model Context Protocol. Part of CloudBase AI Toolkit.
1,202 lines (1,198 loc) • 1.04 MB
JavaScript
import * as __WEBPACK_EXTERNAL_MODULE_lockfile__ from "lockfile";
import * as __WEBPACK_EXTERNAL_MODULE_open__ from "open";
import * as __WEBPACK_EXTERNAL_MODULE__cloudbase_toolbox_034e5bd0__ from "@cloudbase/toolbox";
import * as __WEBPACK_EXTERNAL_MODULE__modelcontextprotocol_sdk_types_js_70eb1363__ from "@modelcontextprotocol/sdk/types.js";
import * as __WEBPACK_EXTERNAL_MODULE__modelcontextprotocol_sdk_server_stdio_js_25848778__ from "@modelcontextprotocol/sdk/server/stdio.js";
import * as __WEBPACK_EXTERNAL_MODULE_path__ from "path";
import * as __WEBPACK_EXTERNAL_MODULE_express__ from "express";
import * as __WEBPACK_EXTERNAL_MODULE_adm_zip_aa3527d5__ from "adm-zip";
import * as __WEBPACK_EXTERNAL_MODULE_zod__ from "zod";
import * as __WEBPACK_EXTERNAL_MODULE_winston_daily_rotate_file_69928d76__ from "winston-daily-rotate-file";
import * as __WEBPACK_EXTERNAL_MODULE_http__ from "http";
import * as __WEBPACK_EXTERNAL_MODULE_https__ from "https";
import * as __WEBPACK_EXTERNAL_MODULE_net__ from "net";
import * as __WEBPACK_EXTERNAL_MODULE_fs__ from "fs";
import * as __WEBPACK_EXTERNAL_MODULE__modelcontextprotocol_sdk_server_mcp_js_45c326f0__ from "@modelcontextprotocol/sdk/server/mcp.js";
import * as __WEBPACK_EXTERNAL_MODULE_child_process__ from "child_process";
import * as __WEBPACK_EXTERNAL_MODULE_ws__ from "ws";
import * as __WEBPACK_EXTERNAL_MODULE__cloudbase_manager_node_6a52ea40__ from "@cloudbase/manager-node";
import * as __WEBPACK_EXTERNAL_MODULE_url__ from "url";
import * as __WEBPACK_EXTERNAL_MODULE__cloudbase_cals_lib_cjs_utils_mermaid_datasource_mermaid_json_transform_04ea0f14__ from "@cloudbase/cals/lib/cjs/utils/mermaid-datasource/mermaid-json-transform";
import * as __WEBPACK_EXTERNAL_MODULE_dns__ from "dns";
import * as __WEBPACK_EXTERNAL_MODULE_crypto__ from "crypto";
import * as __WEBPACK_EXTERNAL_MODULE_fs_promises_f8dae9d1__ from "fs/promises";
import * as __WEBPACK_EXTERNAL_MODULE_os__ from "os";
import * as __WEBPACK_EXTERNAL_MODULE_winston__ from "winston";
/******/ var __webpack_modules__ = ({
/***/ 622:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.DEFAULT_CREATE_ENV_RESOURCES = exports.CREATE_ENV_RESOURCE_VALUES = void 0;
exports.resolveCreateEnvResources = resolveCreateEnvResources;
exports.simplifyEnvList = simplifyEnvList;
exports.isUsableNoSqlDatabaseEntry = isUsableNoSqlDatabaseEntry;
exports.registerEnvTools = registerEnvTools;
const toolbox_1 = __webpack_require__(2090);
const zod_1 = __webpack_require__(2971);
const auth_js_1 = __webpack_require__(7291);
const cloudbase_manager_js_1 = __webpack_require__(3431);
const logger_js_1 = __webpack_require__(3039);
const tool_result_js_1 = __webpack_require__(9835);
const env_setup_js_1 = __webpack_require__(6978);
/**
* Resources accepted by CreateEnv. Matches Cloud API / Manager SDK contract.
* `postgresql` enables CloudBase PostgreSQL (PG mode) when supported by the package.
*/
exports.CREATE_ENV_RESOURCE_VALUES = [
"flexdb",
"storage",
"function",
"postgresql",
];
/** Default Resources when callers omit the field (schema promises "all four"). */
exports.DEFAULT_CREATE_ENV_RESOURCES = [
...exports.CREATE_ENV_RESOURCE_VALUES,
];
function resolveCreateEnvResources(resources) {
if (Array.isArray(resources) && resources.length > 0) {
return [...resources];
}
return [...exports.DEFAULT_CREATE_ENV_RESOURCES];
}
/**
* Simplify environment list data by keeping only essential fields for AI assistant
* This reduces token consumption when returning environment lists via MCP tools
* @param envList - Full environment list from API
* @returns Simplified environment list with only essential fields
*/
function simplifyEnvList(envList) {
if (!Array.isArray(envList)) {
return envList;
}
return envList.map((env) => {
// Only keep essential fields that are useful for AI assistant
const simplified = {};
if (env.EnvId !== undefined)
simplified.EnvId = env.EnvId;
if (env.Alias !== undefined)
simplified.Alias = env.Alias;
if (env.Status !== undefined)
simplified.Status = env.Status;
if (env.EnvType !== undefined)
simplified.EnvType = env.EnvType;
if (env.Region !== undefined)
simplified.Region = env.Region;
if (env.PackageId !== undefined)
simplified.PackageId = env.PackageId;
if (env.PackageName !== undefined)
simplified.PackageName = env.PackageName;
if (env.IsDefault !== undefined)
simplified.IsDefault = env.IsDefault;
return simplified;
});
}
const DEFAULT_ENV_CANDIDATE_LIMIT = 20;
const DEFAULT_ENV_FIELDS = [
"EnvId",
"Alias",
"Status",
"EnvType",
"Region",
"PackageId",
"PackageName",
"IsDefault",
];
function selectEnvFields(env, fields) {
const selectedFields = fields && fields.length > 0 ? fields : DEFAULT_ENV_FIELDS;
const simplified = {};
for (const field of selectedFields) {
if (env[field] !== undefined) {
simplified[field] = env[field];
}
}
return simplified;
}
function filterEnvList(envList, filters) {
const alias = filters.alias?.trim().toLowerCase();
const aliasExact = filters.aliasExact === true;
const envId = filters.envId?.trim().toLowerCase();
return envList.filter((env) => {
const normalizedAlias = String(env.Alias ?? "").toLowerCase();
const matchesAlias = alias
? aliasExact
? normalizedAlias === alias
: normalizedAlias.includes(alias)
: true;
const matchesEnvId = envId
? String(env.EnvId ?? "").toLowerCase() === envId
: true;
return matchesAlias && matchesEnvId;
});
}
function paginateEnvList(envList, offset, limit) {
const safeOffset = Math.max(0, Math.floor(offset ?? 0));
const safeLimit = limit === undefined ? undefined : Math.max(1, Math.floor(limit));
const items = safeLimit === undefined
? envList.slice(safeOffset)
: envList.slice(safeOffset, safeOffset + safeLimit);
return {
total: envList.length,
offset: safeOffset,
limit: safeLimit ?? envList.length,
items,
};
}
function buildEnvCandidatePayload(envCandidates, limit = DEFAULT_ENV_CANDIDATE_LIMIT) {
const env_candidates = envCandidates.slice(0, limit);
return {
env_candidates,
env_candidates_summary: {
total: envCandidates.length,
returned: env_candidates.length,
truncated: envCandidates.length > env_candidates.length,
},
};
}
function buildLocalDevDomainHint() {
return {
format: "host:port",
useActualOrigin: true,
requiredValue: "当前浏览器实际访问 origin 对应的 host:port",
deriveFrom: ["浏览器地址栏中的当前 origin", "本地 dev server 实际启动输出"],
note: "如果你的前端运行在自定义域名或本地开发端口上,请把当前浏览器实际访问地址对应的 host:port 加入安全域名。不要依赖一组固定默认端口,也不要假设已有 localhost/127.0.0.1 条目已经覆盖当前运行端口。",
};
}
function summarizeConfiguredLocalDevEntries(domains) {
const localEntries = domains
.map((domain) => String(domain?.Domain ?? "").trim())
.filter((domain) => domain.startsWith("127.0.0.1:") || domain.startsWith("localhost:"));
return {
hasAnyConfiguredLocalEntry: localEntries.length > 0,
configuredEntries: localEntries,
};
}
function simplifyEnvDomains(domains) {
if (!Array.isArray(domains)) {
return domains;
}
return domains.map((domain) => {
if (!domain || typeof domain !== "object") {
return domain;
}
const source = domain;
return {
...(source.Id !== undefined ? { Id: source.Id } : {}),
...(source.Domain !== undefined ? { Domain: source.Domain } : {}),
...(source.CreateTime !== undefined ? { CreateTime: source.CreateTime } : {}),
...(source.UpdateTime !== undefined ? { UpdateTime: source.UpdateTime } : {}),
...(source.Status !== undefined ? { Status: source.Status } : {}),
...(source.Type !== undefined ? { Type: source.Type } : {}),
};
});
}
function buildEnvDomainManagementResult(params) {
const { action, domains, result } = params;
const rawResult = result && typeof result === "object" && !Array.isArray(result)
? result
: { result };
if (action === "create") {
return {
...rawResult,
ok: true,
code: "DOMAIN_UPDATE_PENDING",
operation: action,
targetDomains: domains,
asyncState: "PENDING",
message: '安全域名已提交添加请求。该变更通常需要数分钟传播(平台侧);请每 10 秒轮询 queryEnv(action="domains") 直到 Status 为 ENABLE,勿一次 sleep 满 10 分钟。',
propagation: {
requiresPolling: true,
pollTool: "queryEnv",
pollAction: "domains",
pollIntervalSuggestionSeconds: 10,
timeoutSuggestionSeconds: 300,
successCondition: '目标域名出现在 queryEnv(action="domains") 返回中,且 Status 为 ENABLE。',
},
next_step: {
tool: "queryEnv",
action: "domains",
suggested_args: {
action: "domains",
},
},
};
}
return {
...rawResult,
ok: true,
code: "DOMAIN_DELETE_PENDING",
operation: action,
targetDomains: domains,
asyncState: "PENDING",
message: '安全域名已提交删除请求。该变更通常需要数分钟传播;请每 10 秒轮询 queryEnv(action="domains") 直到目标域名不再出现,勿一次 sleep 满数分钟。',
propagation: {
requiresPolling: true,
pollTool: "queryEnv",
pollAction: "domains",
pollIntervalSuggestionSeconds: 10,
timeoutSuggestionSeconds: 300,
successCondition: '目标域名不再出现在 queryEnv(action="domains") 返回中。',
},
next_step: {
tool: "queryEnv",
action: "domains",
suggested_args: {
action: "domains",
},
},
};
}
function formatDeviceAuthHint(deviceAuthInfo) {
if (!deviceAuthInfo) {
return "";
}
const verificationUriComplete = (0, auth_js_1.buildVerificationUriComplete)(deviceAuthInfo);
const lines = [
"",
"### Device Flow 授权信息",
`- user_code: ${deviceAuthInfo.user_code}`,
];
if (deviceAuthInfo.verification_uri) {
lines.push(`- verification_uri: ${deviceAuthInfo.verification_uri}`);
}
if (verificationUriComplete) {
lines.push(`- verification_uri_complete: ${verificationUriComplete}`);
}
lines.push(`- expires_in: ${deviceAuthInfo.expires_in}s`);
lines.push("", "请优先向用户展示完整的 `verification_uri_complete`,不要截断或改写 URL。");
return lines.join("\n");
}
function emitDeviceAuthNotice(server, deviceAuthInfo) {
// Temporarily disabled: avoid sending logging notifications for device auth
}
async function fetchAvailableEnvCandidates(cloudBaseOptions, server) {
try {
return await (0, cloudbase_manager_js_1.listAvailableEnvCandidates)({
cloudBaseOptions,
});
}
catch {
return [];
}
}
const CODEBUDDY_AUTH_ACTIONS = ["status", "set_env", "login_by_api_key"];
const DEFAULT_AUTH_ACTIONS = [
"status",
"start_auth",
"set_env",
"logout",
"get_temp_credentials",
"login_by_api_key",
];
function maskSensitiveValue(value) {
if (value.length <= 4) {
return "*".repeat(value.length);
}
return `${value.slice(0, 2)}******${value.slice(-2)}`;
}
function isTemporaryCredentialLoginState(loginState) {
const refreshToken = normalizeOptionalToolString(loginState.refreshToken);
const token = normalizeOptionalToolString(loginState.token);
const accessTokenExpired = typeof loginState.accessTokenExpired === "number" ||
typeof loginState.accessTokenExpired === "string";
return Boolean(token && (refreshToken || accessTokenExpired));
}
function getCurrentIde(server) {
return server.ide || process.env.INTEGRATION_IDE || "";
}
function isCodeBuddyIde(server) {
return getCurrentIde(server) === "CodeBuddy";
}
function getSupportedAuthActions(server) {
return isCodeBuddyIde(server) ? CODEBUDDY_AUTH_ACTIONS : DEFAULT_AUTH_ACTIONS;
}
function buildAuthRequiredNextStep(server) {
if (isCodeBuddyIde(server)) {
return (0, tool_result_js_1.buildAuthNextStep)("status", {
suggestedArgs: { action: "status" },
});
}
return (0, tool_result_js_1.buildAuthNextStep)("start_auth", {
suggestedArgs: { action: "start_auth", authMode: "device" },
});
}
function buildSetEnvNextStep(envCandidates) {
const singleEnvId = envCandidates.length === 1 ? envCandidates[0].envId : undefined;
return (0, tool_result_js_1.buildAuthNextStep)("set_env", {
requiredParams: singleEnvId ? undefined : ["envId"],
suggestedArgs: singleEnvId
? { action: "set_env", envId: singleEnvId }
: { action: "set_env" },
});
}
function dedupeActions(actions) {
return actions.filter((action, index) => actions.indexOf(action) === index);
}
function buildAuthEnvSetupFailure(params) {
return {
reason: params.reason,
error_code: params.errorCode,
message: params.message,
help_url: params.helpUrl,
need_real_name_auth: params.needRealNameAuth,
need_cam_auth: params.needCamAuth,
};
}
function buildAuthEnvSetupPayload(preparation) {
return {
current_env_id: preparation.currentEnvId,
env_status: preparation.envStatus,
env_setup_status: preparation.envSetupStatus,
env_setup_actions: preparation.envSetupActions,
...(preparation.envSetupFailure
? {
env_setup_failure: preparation.envSetupFailure,
}
: {}),
...buildEnvCandidatePayload(preparation.envCandidates),
};
}
async function prepareAuthEnvironment(params) {
const { server, cloudBaseOptions, loginState } = params;
const currentEnvId = (0, cloudbase_manager_js_1.getCachedEnvId)() ||
process.env.CLOUDBASE_ENV_ID ||
(typeof loginState?.envId === "string" && loginState.envId.length > 0
? loginState.envId
: null);
if (currentEnvId) {
return {
currentEnvId,
envStatus: "READY",
envCandidates: [],
envSetupStatus: "NOT_NEEDED",
envSetupActions: [],
message: `当前已登录,环境: ${currentEnvId}`,
nextStep: (0, tool_result_js_1.buildAuthNextStep)("status", {
suggestedArgs: { action: "status" },
}),
};
}
const envSetupActions = ["list_envs"];
const envCandidates = await fetchAvailableEnvCandidates(cloudBaseOptions, server);
if (envCandidates.length === 1) {
const singleEnvId = envCandidates[0].envId;
await cloudbase_manager_js_1.envManager.setEnvId(singleEnvId);
return {
currentEnvId: singleEnvId,
envStatus: "READY",
envCandidates,
envSetupStatus: "AUTO_BOUND",
envSetupActions: dedupeActions(envSetupActions),
message: `当前已登录,已自动绑定唯一环境: ${singleEnvId}`,
nextStep: (0, tool_result_js_1.buildAuthNextStep)("status", {
suggestedArgs: { action: "status" },
}),
};
}
if (envCandidates.length > 1) {
return {
currentEnvId: null,
envStatus: "MULTIPLE",
envCandidates,
envSetupStatus: "SELECTION_REQUIRED",
envSetupActions: dedupeActions(envSetupActions),
message: "当前已登录,但存在多个可用环境,请先选择环境。",
nextStep: buildSetEnvNextStep(envCandidates),
};
}
let setupContext = {};
const manager = await (0, cloudbase_manager_js_1.getCloudBaseManager)({
requireEnvId: false,
cloudBaseOptions: cloudBaseOptions
? {
...cloudBaseOptions,
envId: undefined,
}
: undefined,
mcpServer: server,
});
setupContext = await (0, env_setup_js_1.checkAndInitTcbService)(manager, setupContext);
if (setupContext.checkTcbServiceAttempted) {
envSetupActions.push("check_tcb_service");
}
if (setupContext.initTcbAttempted) {
envSetupActions.push("init_tcb");
}
if (setupContext.initTcbError || !setupContext.tcbServiceInitialized) {
const failure = setupContext.initTcbError
? buildAuthEnvSetupFailure({
reason: "tcb_init_failed",
errorCode: setupContext.initTcbError.code || "TCB_INIT_FAILED",
message: setupContext.initTcbError.message,
helpUrl: setupContext.initTcbError.helpUrl,
needRealNameAuth: setupContext.initTcbError.needRealNameAuth,
needCamAuth: setupContext.initTcbError.needCamAuth,
})
: buildAuthEnvSetupFailure({
reason: "tcb_init_failed",
errorCode: "TCB_INIT_FAILED",
message: "CloudBase 服务初始化失败,请稍后重试。",
helpUrl: "https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp",
});
return {
currentEnvId: null,
envStatus: "NONE",
envCandidates: [],
envSetupStatus: "ACTION_REQUIRED",
envSetupActions: dedupeActions(envSetupActions),
envSetupFailure: failure,
message: failure.message,
nextStep: (0, tool_result_js_1.buildAuthNextStep)("status", {
suggestedArgs: { action: "status" },
}),
};
}
const createResult = await (0, env_setup_js_1.checkAndCreateFreeEnv)(manager, setupContext);
setupContext = createResult.context;
if (setupContext.promotionalActivitiesChecked) {
envSetupActions.push("check_promotional_activity");
}
if (setupContext.createFreeEnvAttempted) {
envSetupActions.push("create_free_env");
}
// Surface the user-facing notice emitted at checkAndCreateFreeEnv entry.
// Informational only — no confirm required. Prepend to the final message so
// the user knows an automatic free-env creation was attempted.
const userNotice = createResult.userNotice?.trim() || "";
if (createResult.success && createResult.envId) {
await cloudbase_manager_js_1.envManager.setEnvId(createResult.envId);
const successMessage = `当前已登录,已自动创建并绑定环境: ${createResult.envId}`;
return {
currentEnvId: createResult.envId,
envStatus: "READY",
envCandidates: [],
envSetupStatus: "AUTO_CREATED",
envSetupActions: dedupeActions(envSetupActions),
message: userNotice ? `${userNotice}\n${successMessage}` : successMessage,
nextStep: (0, tool_result_js_1.buildAuthNextStep)("status", {
suggestedArgs: { action: "status" },
}),
};
}
const createFailure = setupContext.createEnvError
? buildAuthEnvSetupFailure({
reason: "env_creation_failed",
errorCode: setupContext.createEnvError.code || "ENV_CREATION_FAILED",
message: setupContext.createEnvError.message,
helpUrl: setupContext.createEnvError.helpUrl,
})
: buildAuthEnvSetupFailure({
reason: "env_creation_failed",
errorCode: "ENV_CREATION_FAILED",
message: "环境创建失败,请稍后重试或手动创建环境。",
helpUrl: "https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp",
});
const failureMessage = userNotice
? `${userNotice}\n${createFailure.message}`
: createFailure.message;
return {
currentEnvId: null,
envStatus: "NONE",
envCandidates: [],
envSetupStatus: "ACTION_REQUIRED",
envSetupActions: dedupeActions(envSetupActions),
envSetupFailure: createFailure,
message: failureMessage,
nextStep: (0, tool_result_js_1.buildAuthNextStep)("status", {
suggestedArgs: { action: "status" },
}),
};
}
function buildEnvQueryListResult(params) {
const envList = Array.isArray(params.result?.EnvList) ? params.result.EnvList : [];
const shouldRestrictToCurrentEnv = params.hasEnvId && !params.filters.alias && !params.filters.envId;
const baseList = shouldRestrictToCurrentEnv
? envList.filter((env) => env.EnvId === params.cloudBaseOptions?.envId)
: envList;
const filteredList = filterEnvList(baseList, {
alias: params.filters.alias,
aliasExact: params.filters.aliasExact,
envId: params.filters.envId,
});
const paginated = paginateEnvList(filteredList, params.filters.offset, params.filters.limit);
const exactEnvIdSummaryHint = params.filters.envId
? {
tool: "queryEnv",
action: "info",
reason: "action=list with envId only returns a concise summary. Use action=info to fetch detailed environment information such as full resource metadata and additional environment details.",
}
: undefined;
return {
EnvList: paginated.items.map((env) => selectEnvFields(env, params.filters.fields)),
TotalCount: paginated.total,
Offset: paginated.offset,
Limit: paginated.limit,
HasMore: paginated.offset + paginated.items.length < paginated.total,
AppliedFilters: {
alias: params.filters.alias ?? null,
aliasExact: params.filters.aliasExact ?? null,
envId: params.filters.envId ?? null,
fields: params.filters.fields ?? [...DEFAULT_ENV_FIELDS],
currentEnvOnly: shouldRestrictToCurrentEnv,
},
...(exactEnvIdSummaryHint
? {
RecommendedNextAction: exactEnvIdSummaryHint,
}
: {}),
};
}
async function enrichEnvInfoWithBilling(params) {
const targetEnvId = params.envId ||
params.result?.EnvInfo?.EnvId;
if (!targetEnvId || !params.result?.EnvInfo) {
return params.result;
}
try {
const billingResult = await params.manager.commonService("tcb", "2018-06-08").call({
Action: "DescribeBillingInfo",
Param: {
EnvId: targetEnvId,
},
});
(0, cloudbase_manager_js_1.logCloudBaseResult)(params.logger, billingResult);
const billingList = billingResult?.EnvBillingInfoList ||
billingResult?.Response?.EnvBillingInfoList ||
billingResult?.Data?.EnvBillingInfoList ||
[];
const matchedBillingInfo = Array.isArray(billingList)
? billingList.find((item) => item?.EnvId === targetEnvId) ?? billingList[0]
: undefined;
if (!matchedBillingInfo) {
return params.result;
}
return {
...params.result,
EnvInfo: {
...params.result.EnvInfo,
BillingInfo: matchedBillingInfo,
},
};
}
catch (billingError) {
(0, logger_js_1.debug)("DescribeBillingInfo enrichment failed, continuing without billing info", {
error: billingError,
envId: targetEnvId,
});
return params.result;
}
}
/**
* Derive a RuntimeMode hint from the EnvInfo payload so the AI agent can
* immediately tell which CloudBase data backends are actually present in
* this environment.
*
* Reality model (revised after observing a real PG environment): a single
* CloudBase environment can have any combination of:
* - PostgreSQL (CloudBase PG / pgstore): signaled by `EnvInfo.PostgreSQL[]`
* non-empty and/or `EnvInfo.Meta` containing `postgresql=enable`.
* - NoSQL document database (flexdb): signaled ONLY by a usable entry in
* `EnvInfo.Databases[]`. The usable signal is a non-empty `InstanceId`
* (FlexDB Tag, typically `tnt-...` — same value `getDatabaseInstanceId`
* / ListTables `Tag` consume) with `Status` missing or `RUNNING`.
* Cloud storage (`EnvInfo.Storages[]` / CreateEnv `storage`) is unrelated
* and must never flip `RuntimeBackends.nosql`.
* - MySQL: signaled by a non-empty `EnvInfo.MysqlInstances[]` (or similar
* field, name varies). In a pure PG environment this is absent.
*
* In particular: a CloudBase PG environment commonly STILL has flexdb
* provisioned in parallel (`Databases[].InstanceId = tnt-...`). NoSQL is
* therefore "co-present" — its collection APIs and NoSQL `securityRule`s
* remain valid for collections that already live there. The thing that is
* NOT a substitute for the new PG surface is using NoSQL collections to
* model a brand-new business table that the task explicitly puts in PG.
*
* What this enrichment does:
* - Adds `EnvInfo.RuntimeMode = "postgresql" | "nosql"` based on whether
* PG is present. This is the recommended primary backend for new
* business data when set to "postgresql".
* - Adds `EnvInfo.RuntimeBackends`, a structured snapshot of which
* backends are actually available (postgresql / nosql / mysql), so the
* agent does not have to re-read `Databases`/`PostgreSQL`.
* - Adds `EnvInfo.RuntimeModeHints` summarizing which API/tool to prefer
* for new code, including an explicit `MysqlNotAvailable` line when
* MySQL is absent — that one IS a hard "do not use" signal.
*/
/** Exported for unit tests / live-fixture checks against FlexDB InstanceId. */
function isUsableNoSqlDatabaseEntry(db) {
if (!db || typeof db !== "object") {
return false;
}
const entry = db;
const instanceId = typeof entry.InstanceId === "string" ? entry.InstanceId.trim() : "";
// Empty InstanceId means no flexdb tenant — same failure mode as
// getDatabaseInstanceId() throwing "无法获取数据库实例ID".
if (!instanceId) {
return false;
}
// Some API versions omit Status; when present, only RUNNING counts as usable.
if (entry.Status != null && String(entry.Status).toUpperCase() !== "RUNNING") {
return false;
}
return true;
}
async function enrichEnvInfoWithRuntimeMode(result, manager) {
const envInfo = result?.EnvInfo;
if (!envInfo || typeof envInfo !== "object") {
return result;
}
const pgList = Array.isArray(envInfo.PostgreSQL) ? envInfo.PostgreSQL : [];
const metaList = Array.isArray(envInfo.Meta) ? envInfo.Meta : [];
const metaPostgresEnabled = metaList.some((item) => item &&
typeof item.Key === "string" &&
/^postgre[_]?sql$/i.test(item.Key) &&
String(item.Value).toLowerCase() === "enable");
const hasPostgresql = pgList.length > 0 || metaPostgresEnabled;
// NoSQL = flexdb tenant id on Databases[].InstanceId (typically tnt-...).
// Aligns with getDatabaseInstanceId() / ListTables Tag. Storage is unrelated.
const databasesList = Array.isArray(envInfo.Databases)
? envInfo.Databases
: [];
const hasNoSql = databasesList.some(isUsableNoSqlDatabaseEntry);
// MySQL field name has been seen as MysqlInstances / MySQLInstances /
// MySQL across API versions; check any of them.
const mysqlList = (() => {
for (const k of ["MysqlInstances", "MySQLInstances", "MySQL"]) {
const v = envInfo[k];
if (Array.isArray(v) && v.length > 0)
return v;
}
return [];
})();
let hasMysql = mysqlList.length > 0;
// Fallback: if MySQL not detected from DescribeEnvInfo fields, probe via
// DescribeMySQLClusterDetail (dedicated MySQL API). This covers cases where
// the SDK strips MySQL fields from the DescribeEnvInfo response.
if (!hasMysql && manager?.commonService) {
try {
const probeResult = await manager
.commonService("tcb", "2018-06-08")
.call({
Action: "DescribeMySQLClusterDetail",
Param: { EnvId: envInfo.EnvId },
});
const clusterId = probeResult?.DbClusterId ||
probeResult?.Response?.DbClusterId ||
probeResult?.Data?.DbClusterId;
if (clusterId) {
hasMysql = true;
}
}
catch {
// Probe failure means MySQL is not provisioned — keep hasMysql = false
}
}
// Primary mode: prefer PG when it is provisioned, otherwise fall back to
// legacy NoSQL labeling. This drives "what skill to read first / what API
// to reach for when starting new business code".
const runtimeMode = hasPostgresql
? "postgresql"
: "nosql";
const hints = hasPostgresql
? {
PrimaryBackend: "PostgreSQL (CloudBase PG) is provisioned — prefer it for any NEW business data the task introduces.",
BusinessDataAPI: "For NEW business data on PG: use CloudBase JS SDK v3 `app.rdb()` (Supabase-style chained query). Existing NoSQL collections in this env keep working through `app.database()`; do not migrate them unless the task asks.",
Permissions: "PG table permissions use Row-Level Security. Run `ALTER TABLE ... ENABLE ROW LEVEL SECURITY` and `CREATE POLICY` via `managePgDatabase(action=\"execute\", confirm=true)`. `managePermissions(resourceType=\"noSqlDatabase\", securityRule=...)` only governs NoSQL collection rules and has NO effect on PG tables — keep using it for the NoSQL collections that already exist here.",
Storage: "PG-mode browser uploads should use `app.storage.from().upload(<bucket>/<key>, file)` against an explicitly-created `pgstore` bucket (same model as Supabase Storage; the v3 SDK does not auto-create one). `EnvInfo.Storages[]` here is the legacy NoSQL bucket — it is still usable for the legacy `app.uploadFile()` flow but is NOT a valid pgstore target.",
CoexistingNoSQL: hasNoSql
? "This env also has a usable NoSQL (flexdb) InstanceId in EnvInfo.Databases[] (FlexDB Tag, typically tnt-...). Existing collections and `managePermissions(resourceType=\"noSqlDatabase\")` rules remain valid for that data."
: "No usable NoSQL (flexdb) InstanceId in EnvInfo.Databases[] — do not assume app.database() / NoSQL MCP tools work. Cloud storage does not imply flexdb.",
MysqlNotAvailable: hasMysql
? "MySQL instance(s) detected — see EnvInfo.MysqlInstances."
: "No MySQL instance is provisioned for this env. Do NOT use `manageMysqlDatabase` / `queryMysqlDatabase` (those are MySQL-specific) and do NOT read the `relational-database-mcp-cloudbase` skill — that family targets MySQL, not CloudBase PG.",
RecommendedSkills: "Read `postgresql-development-cloudbase` first for new PG code. `cloudbase-document-database-web-sdk` is still applicable for existing NoSQL collections in this env. Skip `relational-database-mcp-cloudbase` (MySQL-only) entirely.",
}
: {
PrimaryBackend: "PostgreSQL is NOT provisioned in this env — this is a legacy NoSQL CloudBase backend.",
BusinessDataAPI: "Use `app.database()` collections via `@cloudbase/js-sdk` for browser business data. Do not switch to `app.rdb()` here — PG is not available.",
Permissions: "Use `managePermissions(resourceType=\"noSqlDatabase\", securityRule=...)` for collection rules. PG-only RLS guidance (e.g. `auth.uid()` SQL policies) does not apply here.",
Storage: "Browser uploads use `app.uploadFile()` against the bucket exposed in `EnvInfo.Storages[].Bucket`.",
MysqlNotAvailable: hasMysql
? "MySQL instance(s) detected — see EnvInfo.MysqlInstances."
: "No MySQL instance in this env. `manageMysqlDatabase` / `queryMysqlDatabase` and the `relational-database-mcp-cloudbase` skill are not applicable.",
RecommendedSkills: "Read `cloudbase-document-database-web-sdk` (and `cloud-storage-web` for uploads). `postgresql-development-cloudbase` and `relational-database-mcp-cloudbase` are not applicable to this env.",
};
return {
...result,
EnvInfo: {
...envInfo,
RuntimeMode: runtimeMode,
RuntimeBackends: {
postgresql: hasPostgresql,
nosql: hasNoSql,
mysql: hasMysql,
},
RuntimeModeHints: hints,
},
};
}
/**
* 补充 SDK getEnvInfo() 遗漏的字段。
*
* @cloudbase/manager-node 的 getEnvInfo() 从 DescribeEnvInfo CAPI 响应的
* EnvBaseInfo 中手写白名单映射时,漏掉了 PostgreSQL、Meta、StaticStorages 等字段。
* 这导致 enrichEnvInfoWithRuntimeMode 无法正确判断环境是否支持 PostgreSQL。
*
* 此函数通过 commonService 额外调用 DescribeEnvInfo CAPI,从原始响应中提取
* 缺失字段补到 EnvInfo 上。
*/
async function enrichEnvInfoWithMissingFields(manager, result, envId) {
const envInfo = result?.EnvInfo;
if (!envInfo || typeof envInfo !== "object") {
return result;
}
// 如果 PostgreSQL 字段已存在且非空,说明 SDK 已经透传了,不需要补充
if (Array.isArray(envInfo.PostgreSQL) &&
envInfo.PostgreSQL.length > 0 &&
Array.isArray(envInfo.Meta)) {
return result;
}
try {
const capiResult = await manager.commonService("tcb", "2018-06-08").call({
Action: "DescribeEnvInfo",
Param: { EnvId: envId },
});
const envBaseInfo = capiResult?.EnvInfo?.EnvBaseInfo ||
capiResult?.Response?.EnvInfo?.EnvBaseInfo;
if (!envBaseInfo || typeof envBaseInfo !== "object") {
return result;
}
return {
...result,
EnvInfo: {
...envInfo,
// 补充 PostgreSQL 字段(SDK 白名单映射遗漏)
...(Array.isArray(envBaseInfo.PostgreSQL) && {
PostgreSQL: envBaseInfo.PostgreSQL,
}),
// 补充 Meta 字段(SDK 白名单映射遗漏)
...(Array.isArray(envBaseInfo.Meta) && {
Meta: envBaseInfo.Meta,
}),
// 补充 StaticStorages 字段(SDK 白名单映射遗漏)
...(Array.isArray(envBaseInfo.StaticStorages) && {
StaticStorages: envBaseInfo.StaticStorages,
}),
},
};
}
catch {
// CAPI 调用失败不影响已有数据,静默忽略
return result;
}
}
function normalizeOptionalToolString(value) {
return typeof value === "string" && value.trim().length > 0
? value.trim()
: undefined;
}
/**
* Build enhanced error message for queryEnv tool errors
* Provides actionable guidance based on error patterns
*/
function buildEnvQueryErrorMessage(error, action) {
const baseMessage = error instanceof Error ? error.message : String(error);
// Check for common error patterns and provide specific guidance
const hasInvalidParameterError = /400|invalid parameter|invalid argument|parameter value/i.test(baseMessage);
const hasAuthError = /未登录|auth required|unauthorized|authentication|credential|token|secret/i.test(baseMessage);
const hasNetworkError = /ECONNRESET|socket hang up|ETIMEDOUT|ENOTFOUND|timeout/i.test(baseMessage);
const hasPermissionError = /permission|denied|forbidden|无权|拒绝/i.test(baseMessage);
const hasEnvNotFoundError = /env|environment|环境.*不存在|not found/i.test(baseMessage);
const suggestions = [];
if (hasInvalidParameterError) {
suggestions.push("参数错误:可能是认证信息无效或已过期,请尝试以下步骤:");
suggestions.push("1. 先调用 auth(action=\"status\") 检查当前登录状态");
suggestions.push("2. 如果未登录,调用 auth(action=\"start_auth\", authMode=\"device\") 完成登录");
suggestions.push("3. 登录完成后再次调用 queryEnv(action=\"list\")");
}
if (hasAuthError) {
suggestions.push("认证错误:当前未登录或认证已过期。");
suggestions.push("建议先执行 auth(action=\"status\") 查看状态,然后按提示完成登录。");
}
if (hasPermissionError) {
suggestions.push("权限错误:当前账号可能没有访问该资源的权限。");
suggestions.push("请确认:1) 已选择正确的环境 2) 账号有对应权限");
}
if (hasEnvNotFoundError) {
suggestions.push("环境错误:指定的环境不存在或无法访问。");
suggestions.push("请使用 queryEnv(action=\"list\") 查看可用的环境列表。");
}
if (hasNetworkError) {
suggestions.push("网络错误:请检查网络连接,稍后重试。");
}
// If no specific pattern matched, provide general guidance
if (suggestions.length === 0) {
suggestions.push("查询环境信息时出错,建议:");
suggestions.push("1. 先调用 auth(action=\"status\") 确认登录状态");
suggestions.push("2. 如未登录,执行 auth(action=\"start_auth\") 完成认证");
suggestions.push("3. 确认环境 ID 正确且可访问");
}
return `[queryEnv/${action}] 调用失败: ${baseMessage}\n\n解决建议:\n${suggestions.join("\n")}`;
}
function normalizeOptionalToolBoolean(value) {
return typeof value === "boolean" ? value : undefined;
}
/**
* 解析用于询价的 region。与 cloudbase-manager.ts 一致:
* cloudBaseOptions.region → TCB_REGION → 'ap-shanghai'。
*/
function resolvePricingRegion(cloudBaseOptions) {
return ((typeof cloudBaseOptions?.region === "string" && cloudBaseOptions.region) ||
process.env.TCB_REGION ||
"ap-shanghai");
}
/**
* 从 describeBaasPackageList 查询某个 packageId 的人类可读套餐名。
* 失败时返回 undefined,调用方降级为只显示 packageId。
*/
async function fetchPackageTitle(manager, packageId) {
try {
const result = await manager.env.describeBaasPackageList({
TargetAction: "new",
Source: "qcloud",
});
const packageList = result?.PackageList || [];
const matched = packageList.find((item) => item?.BillTags === packageId || item?.PackageName === packageId);
if (matched) {
return (matched.PackageTitle || matched.PackageName || matched.BillTags);
}
return undefined;
}
catch (error) {
(0, logger_js_1.debug)("fetchPackageTitle failed", { packageId, error });
return undefined;
}
}
/**
* 查询环境的当前计费/套餐信息(PackageName、PackageId、Region、到期时间、PayMode)。
* 失败时返回 undefined。
*/
async function fetchEnvBillingSummary(manager, envId) {
try {
// describeBillingInfo 返回 EnvBillingInfoList;从中挑出 envId 对应项
const billingResult = await manager.env.describeBillingInfo({ EnvId: envId });
const billingList = billingResult?.EnvBillingInfoList ||
billingResult?.Response?.EnvBillingInfoList ||
billingResult?.Data?.EnvBillingInfoList ||
[];
const matched = Array.isArray(billingList)
? billingList.find((item) => item?.EnvId === envId) ?? billingList[0]
: undefined;
if (!matched) {
return undefined;
}
return {
packageName: matched.PackageName,
packageId: matched.PackageId,
region: matched.Region,
expireTime: matched.ExpireTime,
payMode: matched.PayMode,
isAutoRenew: matched.IsAutoRenew,
};
}
catch (error) {
(0, logger_js_1.debug)("fetchEnvBillingSummary failed", { envId, error });
return undefined;
}
}
/**
* 询价新购价格。失败时返回 { error }。
*/
async function calculateCreatePrice(manager, params) {
try {
const priceResult = await manager.env.calculatePackageCreatePrice({
packageId: params.packageId,
region: params.region,
period: params.period,
});
return { priceResult };
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
(0, logger_js_1.debug)("calculatePackageCreatePrice failed", { params, error });
return { error: message };
}
}
/**
* 询价续费价格。失败时返回 { error }。
*/
async function calculateRenewPrice(manager, envId, period) {
try {
const priceResult = await manager.env.calculatePackageRenewPrice({
envId,
period,
});
return { priceResult };
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
(0, logger_js_1.debug)("calculatePackageRenewPrice failed", { envId, period, error });
return { error: message };
}
}
/**
* 询价变配价格(含退款)。失败时返回 { error }。
*/
async function calculateModifyPrice(manager, envId, newPackageId) {
try {
const priceResult = await manager.env.calculatePackageModifyPrice({
envId,
newPackageId,
});
return { priceResult };
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
(0, logger_js_1.debug)("calculatePackageModifyPrice failed", { envId, newPackageId, error });
return { error: message };
}
}
/**
* 把 PriceResult 格式化为人类可读的价格摘要。
* 返回 { summary, detail }:summary 是一行短语,detail 是多行明细。
*/
function formatPriceSection(priceResult) {
if (!priceResult || typeof priceResult !== "object") {
return { summary: "询价返回为空", detail: "" };
}
const currency = priceResult.Currency === "USD" ? "$" : "¥";
const realTotalCost = Number(priceResult.RealTotalCost);
const totalCost = Number(priceResult.TotalCost);
const unitPrice = Number(priceResult.Price);
const timeSpan = priceResult.TimeSpan;
const TimeUnit = priceResult.TimeUnit || "";
const refund = priceResult.Refund ? Number(priceResult.Refund) : undefined;
const lines = [];
let summary = "";
if (Number.isFinite(realTotalCost) && realTotalCost > 0) {
summary = `预计实付 ${currency}${realTotalCost}`;
lines.push(`- 实付总价: ${currency}${realTotalCost}`);
}
else if (Number.isFinite(totalCost) && totalCost > 0) {
summary = `预计费用 ${currency}${totalCost}`;
lines.push(`- 总价: ${currency}${totalCost}`);
}
if (Number.isFinite(totalCost) && totalCost > 0 && totalCost !== realTotalCost) {
lines.push(`- 原价: ${currency}${totalCost}`);
}
if (Number.isFinite(unitPrice) && unitPrice > 0) {
lines.push(`- 单价: ${currency}${unitPrice}`);
}
if (timeSpan !== undefined && TimeUnit) {
lines.push(`- 时长: ${timeSpan} ${TimeUnit}`);
}
if (refund !== undefined && Number.isFinite(refund) && refund > 0) {
lines.push(`- 退款: ${currency}${refund}(变配降级时退回差价)`);
if (summary) {
summary += `,退款 ${currency}${refund}`;
}
else {
summary = `退款 ${currency}${refund}`;
}
}
if (priceResult.Formula && typeof priceResult.Formula === "string") {
lines.push(`- 计价公式: ${priceResult.Formula}`);
}
return {
summary: summary || "询价返回缺少总价字段",
detail: lines.join("\n"),
};
}
/**
* 构建释放方式说明。manageEnv 当前未提供 destroy action,
* 指向控制台让用户手动销毁。
*/
function buildReleaseMethodHint() {
return {
method: "手动销毁",
consoleUrl: "https://console.cloud.tencent.com/tcb",
note: '环境创建后可随时销毁以停止计费。当前 manageEnv 未提供 destroy action,请前往控制台手动销毁,或调用 manageEnv(action="listPackages") 查看其他套餐。',
};
}
/**
* manageEnv 涉及计费的官方文档链接。来源:腾讯云开发 CloudBase 公开文档。
* 任何 confirm 消息展示这些链接前不需要再次校验 URL 真实性。
*/
const MANAGE_ENV_DOC_LINKS = {
// 包年包月套餐说明(个人版 / 标准版 / 企业版 / 企业高级版)
package: "https://cloud.tencent.com/document/product/876/39093",
// 计费能力项说明
billingItems: "https://cloud.tencent.com/document/product/876/120713",
// 资源点价格文档
resourcePointPrice: "https://cloud.tencent.com/document/product/876/127357",
// 预付费计费与到期释放(费用中心通用)
prepayExpiry: "https://cloud.tencent.com/document/product/555/9618",
};
/**
* 把文档链接渲染为多行 markdown 列表文本。
* filter 不传时全部展示。
*/
function renderDocLinksBlock(filter) {
const labels = {
package: "包年包月套餐说明",
billingItems: "计费能力项说明",
resourcePointPrice: "资源点价格文档",
prepayExpiry: "预付费计费与到期释放(费用中心通用)",
};
const entries = (filter ?? Object.keys(MANAGE_ENV_DOC_LINKS))
.map((k) => `- [${labels[k]}](${MANAGE_ENV_DOC_LINKS[k]})`)
.join("\n");
return `参考文档:\n${entries}`;
}
/**
* 资源清单详细描述(对应控制台购买页"资源清单"段)。
*/
function buildResourceListText() {
return "资源清单:\n- 云开发环境 ×1(含 云数据库 / 云函数 / 云存储 / 静态托管 / 身份认证 等基础资源)";
}
/**
* 计费项披露(对应控制台购买页"计费项"段)。
* 此处为静态披露,调用方在 confirm 消息中拼接即可。
*/
function buildBillingItemsText() {
return "计费项:\n- 数据库容量/调用 · 云函数调用/资源/流量 · 存储读写/CDN · 网关/认证/API 调用 · QPS 超限按量 · 日志";
}
/**
* 计费方式披露(对应控制台购买页"计费方式"段)。
* 针对用户传入的 packageId 判断套餐类型:
* - 含 free/activity/trial/试用 等关键字视为免费体验版
* - 其余视为付费套餐
*/
function buildBillingModeText(packageId) {
const id = (packageId || "").toLowerCase();
const isFree = id.includes("free") ||
id.includes("activity") ||
id.includes("trial") ||
id.includes("试用") ||
id.includes("体验");
if (isFree) {
return ("计费方式:\n- 免费体验版(每月赠送约 3000 资源点 ≈ 3 元,0 元开通)\n" +
"- 付费套餐:个人版 / 标准版 / 企业版 / 企业高级版");
}
return ("计费方式:\n- 付费套餐:个人版 / 标准版 / 企业版 / 企业高级版\n" +
"- 免费体验版通过 auth 工具自动创建;本次 manageEnv(create) 不会创建免费版");
}
/**
* 资源释放方式详细说明(对应控制台购买页"资源释放方式"段)。
* - 免费版:1 个月有效期 + 免费续期 + 停服(保留数据) + 1~7天回收站 + 释放(数据不可恢复)
* - 付费版:到期未续费 → 停服(保留数据) + 1~7天可回收站找回 + 释放(数据不可恢复)
* 任何时候都可在控制台主动销毁、关闭按量、退订加购资源。
*/
function buildReleaseMethodDetailText(packageId) {
const id = (packageId || "").toLowerCase();
const isFree = id.includes("free") ||
id.includes("activity") ||
id.includes("trial") ||
id.includes("试用") ||
id.includes("体验");
const lines = ["资源释放方式:\n- 可随时在控制台销毁环境、关闭按量、退订加购资源"];
if (isFree) {
lines.push("- 免费体验版:有效期 1 个月,到期可免费续期 1 个月;未续期则停服(保留数据)→1~7天回收站→释放(数据不可恢复)");
}
else {
lines.push("- 付费套餐到期未续费:停服(保留数据)→1~7天可回收站找回→释放(数据不可恢复)");
lines.push("- 主动销毁:随时生效;销毁前请确保已迁移或备份数据");
}
return lines.join("\n");
}
/**
* 预计费用披露段:把询价结果 + 周期说明 + 超额按量说明组合成人类可读文本。
*/
function buildPricingDisclosureText(packageId, priceSection, priceError, period) {
const id = (packageId || "").toLowerCase();
const isFree = id.includes("free") ||
id.includes("activity") ||
id.includes("trial") ||
id.includes("试用") ||
id.includes("体验");
const lines = ["预计费用:"];
if (isFree) {
lines.push(`- 免费体验版:本次开通 0 元;超免费额度后需升级为付费套餐(免费版不支持开按量)`);
lines.push("- 实际单价以资源点价格文档为准(3000 资源点 ≈ 3 元)");
}
else {
lines.push(`- 付费套餐:${priceSection?.summary ?? (priceError ? `⚠️ 询价失败(${priceError}),请前往控制台确认` : "询价返回为空")}`);
if (priceSection?.detail) {
lines.push(priceSection.detail);
}
if (priceError && priceSection) {
lines.push(`- ⚠️ 部分明细缺失:${priceError}`);
}
lines.push(`- 计费周期:约 ${period} 个月(包年包月),到期可续费或变配`);
lines.push("- 超额可另开按量(次日结算),详细计费项以官方文档为准");
}
return lines.join("\n");
}
function resolveToolAuthOptions(server, overrides) {
return (0, auth_js_1.resolveAuthOptions)({
...overrides,
serverAuthOptions: server.authOptions,
});
}
function registerEnvTools(server) {
// 获取 cloudBaseOptions,如果没有则为 undefined
const cloudBaseOptions = server.cloudBaseOptions;
// Default: env-scoped tools need a bound envId (domains / domain management use SDK this.envId).
// manageEnv create/listPackages are account-level — use requireEnvId: false there only.
const getManager = (options) => (0, cloudbase_manager_js_1.getCloudBaseManager)({
cloudBaseOptions,
requireEnvId: options?.requireEnvId ?? true,
mcpServer: server,
});
const getManagerForEnvQuery = (targetEnvId, requireEnvId = true) => (0, cloudbase_manager_js_1.getCloudBaseManager)({
cloudBaseOptions: targetEnvId && targetEnvId !== cloudBaseOptions?.envId
? {
...cloudBaseOptions,
envId: targetEnvId,
}
: cloudBaseOptions,
requireEnvId,
mcpServer: server,
});
const hasEnvId = typeof cloudBaseOptions?.envId === 'string' && cloudBaseOptions?.envId.length > 0;
const supportedAuthActions = getSupportedAuthActions(server);
const authActionEnum = [...supportedAuthActions];
// auth - CloudBase (云开发) 开发阶段登录与环境绑定
// 微信 IDE 使用票据认证,不需要登录工具
if (server.ide !== 'wxide') {
server.registerTool?.("auth", {
title: "CloudBase 开发阶段登录与环境",
description: "CloudBase(腾讯云开发)开发阶段登录与环境绑定。登录后即可访问云资源;环境(env)是云函数、数据库、静态托管等资源的隔离单元,绑定环境后其他 MCP 工具才能操作该环境。支持:查询状态、发起登录、API Key登录、绑定环境(set_env)、退出登录。",
inputSchema: {
action: zod_1.z
.enum(authActionEnum)
.optional()
.describe("动作:status=查询状态,start_auth=发起登录,login_by_api_key=API Key登录,set_env=绑定环境(传envId),logout=退出登录"),
...(supportedAuthActions.includes("start_auth")
? {
authMode: zod_1.z
.enum(["device", "web"])
.optional()
.describe("认证模式:device=设备码授权,web=浏览器回调授权"),
oauthEndpoint: zod_1.z
.string()
.optional()
.describe("高级可选:自定义 device-code 登录 endpoint。配置后 oauthCustom 默认按 true 处理"),
clientId: zod_1.z
.string()
.optional()
.describe("高级可选:自定义 device-code 登录 client_id,不传则使用默认值"),
oauthCustom: zod_1.z
.boolean()
.optional()
.describe("高级可选:自定义 endpoint 返回格式开关。未配置 endpoint 时默认 false;配置 endpoint 后默认 true,且不能设为 false"),
}
: {}),
envId: zod_1.z
.string()
.optional()
.describe("环境ID(CloudBase 环境唯一标识),绑定后工具将操作该环境。action=set_env 时必填"),