diff --git a/doc/mcp-tools.md b/doc/mcp-tools.md index fabbadab..4285db0a 100644 --- a/doc/mcp-tools.md +++ b/doc/mcp-tools.md @@ -2292,7 +2292,7 @@ Agent 域统一写入口。支持创建、更新和删除远端 Agent。 **云函数**: `DescribeFunctions`、`CreateFunction`、`UpdateFunctionCode`、`DeleteFunction` **数据库**: `CreateMySQLInstance`、`DescribeMySQLInstances`、`DestroyMySQLInstance` -销毁环境时,常见做法是至少带上 `EnvId` 和 `BypassCheck: true`,如果环境已经处于隔离期再按文档补 `IsForce: true`。 +销毁环境时,`EnvId` 必须传环境的 canonical full `EnvId`,不要传别名或其他简写;如果对话里只有别名,先用 `envQuery(action=list, alias=..., aliasExact=true)` 解析。按官方接口,普通销毁可先只传 `EnvId`;仅在需要绕过资源检查时再补 `BypassCheck: true`,环境已处于隔离期且要彻底删除时再补 `IsForce: true`。 #### 参数 @@ -2313,7 +2313,7 @@ Agent 域统一写入口。支持创建、更新和删除远端 Agent。 { name: "params", type: "object", - description: `Action 对应的参数对象,键名需与官方 API 定义一致。某些 Action 需要携带 EnvId 等信息;如不确定参数结构,请先查官方文档。tcb 示例:\`{ "service": "tcb", "action": "DestroyEnv", "params": { "EnvId": "env-xxx", "BypassCheck": true } }\`,如果环境已经处于隔离期,可再补 \`IsForce: true\`;更新环境别名则可用 \`{ "service": "tcb", "action": "ModifyEnv", "params": { "EnvId": "env-xxx", "Alias": "demo" } }\`。若你的场景是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请优先使用 OpenAPI / Swagger 或 searchKnowledgeBase(mode="openapi"),而不是优先使用 callCloudApi。`, + description: `Action 对应的参数对象,键名需与官方 API 定义一致。某些 Action 需要携带 EnvId 等信息;如不确定参数结构,请先查官方文档。tcb 示例:\`{ "service": "tcb", "action": "DestroyEnv", "params": { "EnvId": "env-xxx" } }\`。\`DestroyEnv\` 的 \`EnvId\` 必须是 canonical full \`EnvId\`,不要传环境别名或其他简写;如果对话里只有别名,先用 \`envQuery(action=list, alias=..., aliasExact=true)\` 解析。仅在需要绕过资源检查时再补 \`BypassCheck: true\`,仅在隔离期环境彻底删除时再补 \`IsForce: true\`;更新环境别名则可用 \`{ "service": "tcb", "action": "ModifyEnv", "params": { "EnvId": "env-xxx", "Alias": "demo" } }\`。若你的场景是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请优先使用 OpenAPI / Swagger 或 searchKnowledgeBase(mode="openapi"),而不是优先使用 callCloudApi。`, } ]} /> diff --git a/mcp/src/generated/tcb-action-index.ts b/mcp/src/generated/tcb-action-index.ts index 22d08ea2..52c3cb0e 100644 --- a/mcp/src/generated/tcb-action-index.ts +++ b/mcp/src/generated/tcb-action-index.ts @@ -50,11 +50,11 @@ export const TCB_ACTION_INDEX_MAP: Record = { ], "exampleParams": { "Alias": "cloudbase", - "AutoVoucher": "true", + "AutoVoucher": true, "Channel": "qc_console", "EnvId": "my-env-i3jkguiejls", "PackageId": "baas_personal", - "Period": "1", + "Period": 1, "RenewFlag": "NOTIFY_AND_MANUAL_RENEW", "Resources": "[\"flexdb\"]", "Source": "qcloud", @@ -220,7 +220,7 @@ export const TCB_ACTION_INDEX_MAP: Record = { "requiredKeys": [], "exampleParams": { "EnvId": "env-xxyyzzaa", - "NeedOrderInfo": "true", + "NeedOrderInfo": true, "ResourceId": "weda.xxxxdad", "WxAppId": "wxasdkfjdkjfk" }, @@ -299,7 +299,7 @@ export const TCB_ACTION_INDEX_MAP: Record = { "EndTime": "2019-04-04 19:00:00", "EnvId": "lotestapi100004", "MetricName": "DbSizepkg", - "Period": "300", + "Period": 300, "ResourceID": "ibot-agent1", "StartTime": "2019-04-02 09:00:00", "SubresourceID": "deepseek", @@ -430,8 +430,8 @@ export const TCB_ACTION_INDEX_MAP: Record = { ], "exampleParams": { "EnvId": "lowcode-abc", - "MgoLimit": "10", - "MgoOffset": "0", + "MgoLimit": 10, + "MgoOffset": 0, "MongoConnector": "无", "TableNames": "[\"table_name\"]", "Tag": "tnt-abc" @@ -460,8 +460,8 @@ export const TCB_ACTION_INDEX_MAP: Record = { "EnvId": "test_envId", "Name": "zhang", "NickName": "张", - "PageNo": "1", - "PageSize": "10", + "PageNo": 1, + "PageSize": 10, "Phone": "13900139000" }, "paramsType": "/**\n * 查询tcb用户列表\n */\ntype DescribeUserListParams = {\n /**\n * 邮箱,模糊查询\n */\n \"Email\"?: string;\n /**\n * 环境id\n */\n \"EnvId\": string;\n /**\n * 用户名,模糊查询\n */\n \"Name\"?: string;\n /**\n * 用户昵称,模糊查询\n */\n \"NickName\"?: string;\n /**\n * 页码,从1开始,默认1\n */\n \"PageNo\"?: number;\n /**\n * 每页数量,默认20,最大100\n */\n \"PageSize\"?: number;\n /**\n * 手机号,模糊查询\n */\n \"Phone\"?: string;\n};" @@ -482,11 +482,11 @@ export const TCB_ACTION_INDEX_MAP: Record = { "EnvId" ], "exampleParams": { - "BypassCheck": "false", - "BypassLimit": "false", - "DeleteDirectly": "false", + "BypassCheck": false, + "BypassLimit": false, + "DeleteDirectly": false, "EnvId": "yourenvid-2fb346", - "IsForce": "false" + "IsForce": false }, "paramsType": "/**\n * 销毁环境\n */\ntype DestroyEnvParams = {\n /**\n * 是否绕过资源检查,资源包等额外资源,默认为false,如果为true,则不检查资源是否有数据,直接删除。\n */\n \"BypassCheck\"?: boolean;\n /**\n * 销毁消耗用户删除配额 默认为false-占用配额 true-不占用配额\n */\n \"BypassLimit\"?: boolean;\n /**\n * 是否自动删除环境。(仅在IsForce=false时,且仅对预付费环境有效)\n */\n \"DeleteDirectly\"?: boolean;\n /**\n * 环境Id\n */\n \"EnvId\": string;\n /**\n * 针对预付费 删除隔离中的环境时要传true 正常环境直接跳过隔离期删除\n */\n \"IsForce\"?: boolean;\n};" }, @@ -557,11 +557,11 @@ export const TCB_ACTION_INDEX_MAP: Record = { "exampleParams": { "EnvId": "lowcode-abc", "Filters": "[\"HIDDEN\"]", - "MgoLimit": "10", - "MgoOffset": "0", + "MgoLimit": 10, + "MgoOffset": 0, "MongoConnector": "无", "SearchValue": "prefix", - "ShowHidden": "false", + "ShowHidden": false, "Tag": "tag-123" }, "paramsType": "/**\n * 查询文档型数据库所有表\n */\ntype ListTablesParams = {\n /**\n * 云开发环境ID\n */\n \"EnvId\"?: string;\n /**\n * 过滤标签数组,用于过滤表名,可选值如:HIDDEN、WEDA、WEDA_SYSTEM\n */\n \"Filters\"?: (string)[];\n /**\n * 每页返回数量(0-1000)\n */\n \"MgoLimit\": number;\n /**\n * 分页偏移量\n */\n \"MgoOffset\"?: number;\n /**\n * MongoDB连接器配置\n */\n \"MongoConnector\"?: {\n /**\n * MongoDB数据库名\n */\n \"DatabaseName\"?: string;\n /**\n * 连接器实例ID\n */\n \"InstanceId\"?: string;\n };\n /**\n * 模糊搜索查询值\n */\n \"SearchValue\"?: string;\n /**\n * 是否展示隐藏表\n */\n \"ShowHidden\"?: boolean;\n /**\n * FlexDB实例ID\n */\n \"Tag\"?: string;\n};" @@ -580,7 +580,7 @@ export const TCB_ACTION_INDEX_MAP: Record = { ], "exampleParams": { "EnvId": "env-xxxxxx", - "Period": "10" + "Period": 10 }, "paramsType": "/**\n * 修改日志主题\n */\ntype ModifyClsTopicParams = {\n /**\n * 环境ID\n */\n \"EnvId\": string;\n /**\n * 日志生命周期,单位天,可取值范围1~3600,取值为3640时代表永久保存\n */\n \"Period\"?: number;\n};" }, @@ -651,7 +651,7 @@ export const TCB_ACTION_INDEX_MAP: Record = { "PackageId" ], "exampleParams": { - "AutoVoucher": "true", + "AutoVoucher": true, "EnvId": "cloudbase-8grqda2hfc2f62bb", "PackageId": "baas_pf_standard" }, @@ -680,7 +680,7 @@ export const TCB_ACTION_INDEX_MAP: Record = { "CollectionName": "my-table", "EnvId": "myenv-c849jgkdldcmsd", "Rule": "{ \"read\": true, \"write\": \"doc._openid == auth.openid\" }", - "SyncModel": "false", + "SyncModel": false, "WxAppId": "wxg89jkjdkf034kgjlsdk" }, "paramsType": "/**\n * 设置数据库安全规则\n */\ntype ModifySafeRuleParams = {\n /**\n * 权限标签。包含以下取值:\n * - READONLY:所有用户可读,仅创建者和管理员可写\n * - PRIVATE:仅创建者及管理员可读写\n * - ADMINWRITE:所有用户可读,仅管理员可写\n * - ADMINONLY:仅管理员可读写\n * - CUSTOM:自定义安全规则\n */\n \"AclTag\": string;\n /**\n * 集合名称\n */\n \"CollectionName\": string;\n /**\n * 环境ID\n */\n \"EnvId\": string;\n /**\n * 安全规则内容。\n * 当 AclTag=CUSTOM 时,此参数必填。\n * 详情参考:[文档型数据库安全规则](https://docs.cloudbase.net/database/security-rules)\n */\n \"Rule\"?: string;\n /**\n * 是否同步数据模型,默认同步\n */\n \"SyncModel\"?: boolean;\n /**\n * 微信 AppId,微信必传\n */\n \"WxAppId\"?: string;\n};" @@ -760,9 +760,9 @@ export const TCB_ACTION_INDEX_MAP: Record = { "EnvId" ], "exampleParams": { - "AutoVoucher": "true", + "AutoVoucher": true, "EnvId": "cloudbase-8grqda2hfc2f62bb", - "Period": "1" + "Period": 1 }, "paramsType": "/**\n * 续费云开发环境\n */\ntype RenewEnvParams = {\n /**\n * 是否自动选择代金券支付。\n */\n \"AutoVoucher\"?: boolean;\n /**\n * 环境ID\n */\n \"EnvId\": string;\n /**\n * 续费周期,单位:月。\n * 默认值为 1,即续费1个月。\n */\n \"Period\"?: number;\n};" }, diff --git a/mcp/src/tools/capi.test.ts b/mcp/src/tools/capi.test.ts index 68985d8e..a07ae3dd 100644 --- a/mcp/src/tools/capi.test.ts +++ b/mcp/src/tools/capi.test.ts @@ -27,6 +27,19 @@ describe("buildCapiErrorMessage", () => { expect(message).toContain("/**"); }); + it("adds destroy env specific guidance for invalid parameter values", () => { + const message = buildCapiErrorMessage( + "tcb", + "DestroyEnv", + new Error("400 invalid parameter value"), + ); + + expect(message).toContain("canonical full `EnvId`"); + expect(message).toContain("aliasExact=true"); + expect(message).toContain("{ \"EnvId\": \"env-xxx\" }"); + expect(message).toContain("`IsForce: true`"); + }); + it("does not inject tcb action suggestions for non-tcb services", () => { const message = buildCapiErrorMessage( "scf", diff --git a/mcp/src/tools/capi.ts b/mcp/src/tools/capi.ts index fa3325b1..41598542 100644 --- a/mcp/src/tools/capi.ts +++ b/mcp/src/tools/capi.ts @@ -155,6 +155,11 @@ export function buildCapiErrorMessage(service: AllowedService, action: string, e suggestions.push("2. 数值参数是否在允许范围内"); suggestions.push("3. 枚举值是否使用了正确的取值(区分大小写)"); suggestions.push("4. 必填参数是否有值"); + if (service === "tcb" && tcbEntry?.action === "DestroyEnv") { + suggestions.push("`DestroyEnv` 的 `EnvId` 必须是环境的 canonical full `EnvId`,不要传环境别名、昵称、当前登录态标识,或带 `/` 的复合值。"); + suggestions.push("如果对话里只有环境别名,先用 `envQuery(action=\"list\", alias=..., aliasExact=true)` 解析出准确 `EnvId`。"); + suggestions.push("按官方接口,普通销毁可先只传 `{ \"EnvId\": \"env-xxx\" }`;仅在确实需要绕过资源检查时再补 `BypassCheck: true`,仅在隔离期环境彻底删除时再补 `IsForce: true`。"); + } if (service === "tcb" && tcbEntry) { const paramsTypeHint = formatTcbParamsTypeHint(tcbEntry.action); if (paramsTypeHint) { @@ -205,7 +210,7 @@ export function registerCapiTools(server: ExtendedMcpServer) { **云函数**: \`DescribeFunctions\`、\`CreateFunction\`、\`UpdateFunctionCode\`、\`DeleteFunction\` **数据库**: \`CreateMySQLInstance\`、\`DescribeMySQLInstances\`、\`DestroyMySQLInstance\` -销毁环境时,常见做法是至少带上 \`EnvId\` 和 \`BypassCheck: true\`,如果环境已经处于隔离期再按文档补 \`IsForce: true\`。`, +销毁环境时,\`EnvId\` 必须传环境的 canonical full \`EnvId\`,不要传别名或其他简写;如果对话里只有别名,先用 \`envQuery(action=list, alias=..., aliasExact=true)\` 解析。按官方接口,普通销毁可先只传 \`EnvId\`;仅在需要绕过资源检查时再补 \`BypassCheck: true\`,环境已处于隔离期且要彻底删除时再补 \`IsForce: true\`。`, inputSchema: { service: z .enum(ALLOWED_SERVICES) @@ -220,7 +225,7 @@ export function registerCapiTools(server: ExtendedMcpServer) { .record(z.any()) .optional() .describe( - "Action 对应的参数对象,键名需与官方 API 定义一致。某些 Action 需要携带 EnvId 等信息;如不确定参数结构,请先查官方文档。tcb 示例:`{ \"service\": \"tcb\", \"action\": \"DestroyEnv\", \"params\": { \"EnvId\": \"env-xxx\", \"BypassCheck\": true } }`,如果环境已经处于隔离期,可再补 `IsForce: true`;更新环境别名则可用 `{ \"service\": \"tcb\", \"action\": \"ModifyEnv\", \"params\": { \"EnvId\": \"env-xxx\", \"Alias\": \"demo\" } }`。若你的场景是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请优先使用 OpenAPI / Swagger 或 searchKnowledgeBase(mode=\"openapi\"),而不是优先使用 callCloudApi。", + "Action 对应的参数对象,键名需与官方 API 定义一致。某些 Action 需要携带 EnvId 等信息;如不确定参数结构,请先查官方文档。tcb 示例:`{ \"service\": \"tcb\", \"action\": \"DestroyEnv\", \"params\": { \"EnvId\": \"env-xxx\" } }`。`DestroyEnv` 的 `EnvId` 必须是 canonical full `EnvId`,不要传环境别名或其他简写;如果对话里只有别名,先用 `envQuery(action=list, alias=..., aliasExact=true)` 解析。仅在需要绕过资源检查时再补 `BypassCheck: true`,仅在隔离期环境彻底删除时再补 `IsForce: true`;更新环境别名则可用 `{ \"service\": \"tcb\", \"action\": \"ModifyEnv\", \"params\": { \"EnvId\": \"env-xxx\", \"Alias\": \"demo\" } }`。若你的场景是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请优先使用 OpenAPI / Swagger 或 searchKnowledgeBase(mode=\"openapi\"),而不是优先使用 callCloudApi。", ), }, annotations: { diff --git a/scripts/generate-tcb-action-index.mjs b/scripts/generate-tcb-action-index.mjs index 04bf7278..86863c3a 100644 --- a/scripts/generate-tcb-action-index.mjs +++ b/scripts/generate-tcb-action-index.mjs @@ -51,6 +51,54 @@ function cloneExample(value) { return JSON.parse(JSON.stringify(value)); } +/** + * Normalize example values to match their schema types. + * This fixes issues where the OpenAPI YAML has string values for boolean fields + * (e.g., 'false' instead of false), which can cause 400 errors when agents use them. + */ +function normalizeExampleValue(value, schema) { + if (value === null || value === undefined) { + return value; + } + + // Handle boolean type normalization + if (schema?.type === "boolean" && typeof value === "string") { + const lowerValue = value.toLowerCase(); + if (lowerValue === "true") { + return true; + } + if (lowerValue === "false") { + return false; + } + } + + // Handle integer/number type normalization + if ((schema?.type === "integer" || schema?.type === "number") && typeof value === "string") { + const parsed = Number(value); + if (!Number.isNaN(parsed)) { + return schema.type === "integer" ? Math.floor(parsed) : parsed; + } + } + + // Handle array type normalization + if (schema?.type === "array" && Array.isArray(value)) { + return value.map((item) => normalizeExampleValue(item, schema.items)); + } + + // Handle object type normalization + if ((schema?.type === "object" || schema?.properties) && typeof value === "object" && value !== null) { + const properties = schema.properties ?? {}; + const normalized = {}; + for (const [key, val] of Object.entries(value)) { + const propSchema = properties[key]; + normalized[key] = normalizeExampleValue(val, propSchema); + } + return normalized; + } + + return value; +} + function mergeSchemaNodes(base, incoming) { if (!incoming || typeof incoming !== "object") { return base; @@ -185,7 +233,7 @@ function buildSchemaResolver(components) { required: Array.isArray(schema.required) ? [...schema.required].sort() : undefined, - example: cloneExample(schema.example), + example: normalizeExampleValue(cloneExample(schema.example), schema), }; if (schema.properties && typeof schema.properties === "object") { diff --git a/scripts/tools.json b/scripts/tools.json index 25de23b4..ad6f3a37 100644 --- a/scripts/tools.json +++ b/scripts/tools.json @@ -2463,7 +2463,7 @@ }, { "name": "callCloudApi", - "description": "通用的云 API 调用工具,主要用于 CloudBase / 腾讯云管控面与依赖资源相关 API 调用。调用前请先确认 service、Action 与 Param,避免猜测 Action 名称。如果你的目标是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请不要优先使用 callCloudApi,而应优先查看对应 OpenAPI / Swagger。现有 OpenAPI / Swagger 能力不是通用的管控面 Action 集合;管控面 API 请优先参考 CloudBase API 概览 https://cloud.tencent.com/document/product/876/34809 与云开发依赖资源接口指引 https://cloud.tencent.com/document/product/876/34808。对于 tcb service,常用 Action 分类如下:\n\n**环境管理**: `CreateEnv`、`ModifyEnv`、`DescribeEnvs`、`DestroyEnv`\n**用户管理**: `CreateUser`、`ModifyUser`、`DescribeUserList`、`DeleteUsers`\n**认证配置**: `EditAuthConfig`、`DescribeAuthDomains`\n**云函数**: `DescribeFunctions`、`CreateFunction`、`UpdateFunctionCode`、`DeleteFunction`\n**数据库**: `CreateMySQLInstance`、`DescribeMySQLInstances`、`DestroyMySQLInstance`\n\n销毁环境时,常见做法是至少带上 `EnvId` 和 `BypassCheck: true`,如果环境已经处于隔离期再按文档补 `IsForce: true`。", + "description": "通用的云 API 调用工具,主要用于 CloudBase / 腾讯云管控面与依赖资源相关 API 调用。调用前请先确认 service、Action 与 Param,避免猜测 Action 名称。如果你的目标是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请不要优先使用 callCloudApi,而应优先查看对应 OpenAPI / Swagger。现有 OpenAPI / Swagger 能力不是通用的管控面 Action 集合;管控面 API 请优先参考 CloudBase API 概览 https://cloud.tencent.com/document/product/876/34809 与云开发依赖资源接口指引 https://cloud.tencent.com/document/product/876/34808。对于 tcb service,常用 Action 分类如下:\n\n**环境管理**: `CreateEnv`、`ModifyEnv`、`DescribeEnvs`、`DestroyEnv`\n**用户管理**: `CreateUser`、`ModifyUser`、`DescribeUserList`、`DeleteUsers`\n**认证配置**: `EditAuthConfig`、`DescribeAuthDomains`\n**云函数**: `DescribeFunctions`、`CreateFunction`、`UpdateFunctionCode`、`DeleteFunction`\n**数据库**: `CreateMySQLInstance`、`DescribeMySQLInstances`、`DestroyMySQLInstance`\n\n销毁环境时,`EnvId` 必须传环境的 canonical full `EnvId`,不要传别名或其他简写;如果对话里只有别名,先用 `envQuery(action=list, alias=..., aliasExact=true)` 解析。按官方接口,普通销毁可先只传 `EnvId`;仅在需要绕过资源检查时再补 `BypassCheck: true`,环境已处于隔离期且要彻底删除时再补 `IsForce: true`。", "inputSchema": { "type": "object", "properties": { @@ -2488,7 +2488,7 @@ "params": { "type": "object", "additionalProperties": {}, - "description": "Action 对应的参数对象,键名需与官方 API 定义一致。某些 Action 需要携带 EnvId 等信息;如不确定参数结构,请先查官方文档。tcb 示例:`{ \"service\": \"tcb\", \"action\": \"DestroyEnv\", \"params\": { \"EnvId\": \"env-xxx\", \"BypassCheck\": true } }`,如果环境已经处于隔离期,可再补 `IsForce: true`;更新环境别名则可用 `{ \"service\": \"tcb\", \"action\": \"ModifyEnv\", \"params\": { \"EnvId\": \"env-xxx\", \"Alias\": \"demo\" } }`。若你的场景是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请优先使用 OpenAPI / Swagger 或 searchKnowledgeBase(mode=\"openapi\"),而不是优先使用 callCloudApi。" + "description": "Action 对应的参数对象,键名需与官方 API 定义一致。某些 Action 需要携带 EnvId 等信息;如不确定参数结构,请先查官方文档。tcb 示例:`{ \"service\": \"tcb\", \"action\": \"DestroyEnv\", \"params\": { \"EnvId\": \"env-xxx\" } }`。`DestroyEnv` 的 `EnvId` 必须是 canonical full `EnvId`,不要传环境别名或其他简写;如果对话里只有别名,先用 `envQuery(action=list, alias=..., aliasExact=true)` 解析。仅在需要绕过资源检查时再补 `BypassCheck: true`,仅在隔离期环境彻底删除时再补 `IsForce: true`;更新环境别名则可用 `{ \"service\": \"tcb\", \"action\": \"ModifyEnv\", \"params\": { \"EnvId\": \"env-xxx\", \"Alias\": \"demo\" } }`。若你的场景是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请优先使用 OpenAPI / Swagger 或 searchKnowledgeBase(mode=\"openapi\"),而不是优先使用 callCloudApi。" } }, "required": [