为企业团队封装专属 CloudBase MCP
一句话定义:给团队发一份 MCP 配置,员工在自己的 AI IDE 里就能部署和管理 CloudBase 应用——不用腾讯云账号、看不到别人的数据、删不掉生产资源。
预计耗时:1 小时 | 难度:进阶
更简单的路:如果只是想先用起来,直接看「先看:多数团队不需要封装」——一句提示词接入,不用封装。本文其余内容是要管控之后的路线。
适用与不适用
适用:内部研发平台统一入口、对外 SaaS 让客户生成应用、按人按项目隔离环境、需要审计调用记录。
不适用:个人开发者自用(直接配官方 MCP);只想让模型「通常不做」某事(写 System Prompt 就够);客户要求零明文凭证(走自定义授权码,本篇用 API Key);国际站(端点和账号体系不同)。
先看:三层解法,按需取用
企业接入的「管控」不是只有封装一条路,是三个层面的解法,约束强度递进。定位先说清:第 1 层不改任何代码,配专家和 skill 就够;当专家 / skill 的软约束满足不了管控要求(拦不住误删越权、要凭证生命周期、要审计),需要做一个企业专属的 MCP——这就进入第 2 / 3 层,是涉及 AI 参与开发的工作(本文教的就是怎么做)。第 2 与第 3 层的差别只在部署位置:跑员工机器,还是跑企业服务器。
第 1 层 · 定制专家 / System Prompt / Skill:不改任何代码,通过提示词和 skill 给 AI 注入企业规范——创建云函数强制带 VPC 配置、统一资源命名,甚至把授权 Helper 打包进专家实现零明文授权。约束在提示词层(软,可能被绕过),成本最低。做法见企业级 Agent 平台集成最佳实践。
第 2 层 · 封装本地 MCP(本文):协议层硬管控——工具裁剪、危险动作拦截、凭证按人下发与审计。跑在员工机器(stdio)。
第 3 层 · 封装远端 MCP:wrapper 作为远端服务统一运营,员工零安装。代码与第 2 层同源,部署流程已按远端形态设计(源码走 COS 通道,不依赖本地文件),差异点见文末「远端形态的差异」。
接入本身不分层:第 1 层的门槛就是一句提示词——员工只拿环境 ID 和 API Key,在任意 AI 工具的对话里发:
帮我把 CloudBase 接好,按下面做:
1. 打开 https://docs.cloudbase.net/skill.md,按说明完成接入。
2. 接入完成后告诉我,并建议最相关的下一步。
我的环境 id 是 env-xxxxx,apikey 是 ak-xxxxx
AI 会自己打开官方接入说明(skill.md)完成配置,员工不需要懂 MCP 配置格式。
三层对照:
| 第 1 层 · 定制专家 | 第 2 层 · 本地 MCP(本文) | 第 3 层 · 远端 MCP | |
|---|---|---|---|
| 约束在哪 | 提示词层(软,可能被绕过) | 协议层(硬,绕不过) | 协议层 + 服务端统一运营 |
| 工具面 | 官方全量,含 capi 透传 | 按企业策略裁剪 | 同左 |
| 危险动作 | 提示词规范行为 | 协议层拦截 | 协议层拦截 |
| 凭证 | 可打包 Helper 零明文授权 | 平台按人下发、可吊销、有审计 | 服务端持有,员工零凭证 |
| 部署位置 | 员工配置里装专家 / skill | 员工机器(stdio) | 企业服务器(HTTP 端点) |
不想自己评估选哪层? 把官方的 AI 集成提示词发给你的 AI 编程助手(Cursor、Copilot、企业自建 Agent 均可),它会读文档、向你澄清架构与合规要求、给出选型建议并协助落地:
我想在当前项目中集成腾讯云开发(CloudBase)的企业级 Agent 平台能力。
请先阅读以下参考文档:
1. CloudBase Skill 规范:https://docs.cloudbase.net/skill.md
2. 企业级平台集成最佳实践:https://docs.cloudbase.net/solutions/cloudbase-platform-edition/enterprise-integration
如你的环境已安装 CloudBase 的 **cloud-api-operations** skill,优先加载它,并阅读其 `references/recipes/enterprise-mcp-wrapper.md`(封装企业专属 MCP 的完整实操);未安装则按上述文档操作。
结合文档,请先向我了解我们目前的系统架构、鉴权方式及合规要求,与我澄清并评估最合适的集成方案(三层:定制专家 / System Prompt、封装本地 MCP、封装远端 MCP)。方案确认后,再一步步指导或协助我完成落地实施。实施中涉及云 API 的接口名、参数与行为,以文档与实测为准,不从记忆推断。
判断标准一句话:先能用,再软约束,最后硬管控——提示词层拦不住的(误删、越权、凭证外泄),才需要走到封装。
整体结构
两个面:员工日常操作全在对话里(部署、预览、查状态、数据库);发布动作只在流水线配置里出现,由维护者配一次、git push 自动触发,员工不接触 CLI。
流水线用 CloudBase 自带的发布流水线(代码推上去自动构建部署),本文的「流水线」都指它。团队已有自有 CI(GitHub Actions、Jenkins 等)也能接入,配置方式本文不展开。
wrapper 同一套代码有两种部署形态:本地 stdio(上图,跑在员工机器)或远端 HTTP 服务(跑在企业服务器,员工配置里连 URL 而不是 npx)。第四步的部署流程对两种形态通用——这就是源码统一走 COS 通道的原因。
第一步:给每人一个环境,下发配置
用 CreateEnv 按人(或按项目)开环境,维护 userId → envId 映射。两条隔离原则:一人一个环境(凭证出事只影响一个人)、一个应用一个环境(多应用混用会集合混杂、函数重名硬覆盖、静态托管互相冲掉,也没法按应用计量)。然后给每人下发这份配置:
{
"mcpServers": {
"cloudbase": {
"command": "npx",
"args": ["-y", "@your-org/cloudbase-wrapper"],
"env": {
"CLOUDBASE_ENV_ID": "env-xxxxx",
"CLOUDBASE_API_KEY": "ak-xxxxx"
}
}
}
}
配置里的 @your-org/cloudbase-wrapper 是占位符——它就是第二步做好的 wrapper 发布成的包名,换成你们自己的。没做过 npm 发布的话看第二步末尾「把 wrapper 发出去」。
Key 是环境级凭证(泄露只影响一个人的环境),用 CreateApiKey 创建,可配白名单权限(比如给前端团队只开「静态托管-写」)。三条纪律:一人一 Key、离职即删、定期轮换(单环境最多 5 个,滚着换不断服)。
交付给 AI 的凭证形态只有一种:环境 ID + API Key。API Key 是长期根凭证,MCP 内部换 30 分钟 STS 临时凭证出网、过期自动重换;STS 三件套是换证产物,不能单独当交付物——只交 STS 的话,过期后没有根凭证可重换,整条链路瘫痪。
wrapper 部署为远端服务时,这份配置换成 URL 连接("url": "https://mcp.your-org.com/..." 加鉴权头),凭证改由服务端持有、不下发到员工机器——下发内容和形态按企业安全基线选,后面的步骤不变。
第二步:写 wrapper
一个文件搞定。官方 MCP 跑在同进程,in-memory 传输对接,外层做三件事:裁工具、拦危险动作、验凭证。
import { createRequire } from 'node:module';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
// 必须走 CJS 入口:ESM 入口有未声明的内部依赖
const require = createRequire(import.meta.url);
const { createCloudBaseMcpServer } = require('@cloudbase/cloudbase-mcp');
// 插件组合是平台方策略,写死在 wrapper 里,员工不配置。按需增减:
const PLUGINS = 'env,database,functions,storage,hosting,gateway,cloudrun,apps'.split(',');
// 不传时官方默认全开,含能透传任意云 API 的 capi——企业场景务必排除它
const upstream = await createCloudBaseMcpServer({
name: 'cloudbase-mcp', cloudMode: false,
pluginsEnabled: PLUGINS,
cloudBaseOptions: { envId: process.env.CLOUDBASE_ENV_ID },
});
const [ct, st] = InMemoryTransport.createLinkedPair();
await upstream.connect(st);
const client = new Client({ name: 'wrapper', version: '1.0.0' }, { capabilities: {} });
await client.connect(ct);
// 危险动作:官方二次确认清单的删除类,按语义前缀兜底(delete/destroy/remove...)
const isDenied = (tool, action) =>
/^(delete|destroy|remove|clear|truncate|drop)/i.test(action ?? '');
const wrapper = new Server({ name: 'cloudbase-wrapper', version: '1.0.0' }, { capabilities: { tools: {} } });
wrapper.setRequestHandler(ListToolsRequestSchema, async () => {
const { tools } = await client.listTools();
const visible = [];
for (const tool of tools) {
// 被禁动作从参数 enum 里剪掉,模型在参数定义里就看不见
const p = tool.inputSchema?.properties?.action;
if (p?.enum) {
p.enum = p.enum.filter((a) => !isDenied(tool.name, a));
if (p.enum.length === 0) continue;
}
visible.push(tool);
}
return { tools: visible };
});
wrapper.setRequestHandler(CallToolRequestSchema, async (req) => {
const { name, arguments: args = {} } = req.params;
if (isDenied(name, args.action)) {
return { content: [{ type: 'text', text: `${name}.${args.action} 被平台策略禁止,重试无效。` }], isError: true };
}
console.error('[audit]', JSON.stringify({ ts: Date.now(), env: process.env.CLOUDBASE_ENV_ID, call: `${name}.${args.action ?? ''}` }));
return client.callTool(req.params);
});
// 凭证自检:只有 single_env + env 来源才放行,防止静默回落到账号级全局登录态
const res = await client.callTool({ name: 'auth', arguments: { action: 'status' } });
const text = (res.content ?? []).map((c) => c.text ?? '').join('\n');
const scope = /credential_scope["'=:\s]+(\w+)/i.exec(text)?.[1];
if (scope && scope !== 'single_env') {
console.error(`[cbw] 凭证作用域异常(${scope}),API Key 未生效,拒绝服务`);
process.exit(1);
}
await wrapper.connect(new StdioServerTransport());
console.error('[cbw] ready');
三条不能省:
- 日志全走
console.error。 stdio 是按行分隔的 JSON-RPC,stdout 多一个字符客户端就崩 pluginsEnabled必须显式传。 不传默认全开,包括capi——它能调任意云 API,等于绕过整个白名单- 过滤和拦截都要。 只裁列表,模型会猜工具名硬调;只拦调用,模型看得见调不动会反复重试
把 wrapper 发出去
第二步这份代码就是一个标准的 npm 包:配上 package.json(name 写成你们的包名,如 @your-org/cloudbase-wrapper),npm publish 发出去,第一步配置里的 npx -y 包名 就能自动下载执行——员工机器上不需要预先安装任何东西。
没有公共 npm 源的企业,两个替代:发到企业内部源(如制品库、verdaccio,员工配置多一行 .npmrc 指向内部源),或直接从内部 git 仓库安装(npx git+ssh://...,免发布)。发版由平台方统一做,建议版本号固定(如 包名@1.2.0),升级由平台推新版本,避免 npx 每次拉到不一样的版本。
第三步:验一次凭证
API Key 有个隐蔽行为:解析条件是「Key 与 CLOUDBASE_ENV_ID 同时在环境变量」,且换证失败会被静默吞掉、回落到机器上可能存在的全局登录态——wrapper 会带着账号级权限继续跑而不自知。
所以第二步代码里的自检不能删:credential_scope=single_env 且 credential_source=env 才算 Key 生效。调试时可用环境变量临时跳过自检做协议层验证,生产默认开启。
好消息是 API Key 本身不是每次调用的凭证——MCP 先换 30 分钟临时密钥再出网,Key 只在换证时用一次。
第四步:部署
部署是同一个动作,只有触发方式不同:开发时在对话里说「部署」,模型走 MCP 工具;正式发布时同一份配置进 git,由 CloudBase 自带的发布流水线执行。两个触发产出的是同一个 CloudApp 的连续版本。
本节代码均为调用 MCP 工具的伪代码示意(通过 CloudBase MCP 或企业 wrapper MCP),重点是参数与流程,不是可直接运行的 JS。
源码统一走 COS 通道:
getUploadUrl拿预签名地址 → PUT 源码 zip →deployApp带source: { type: 'zip', cosTimestamp }触发构建(Type枚举只有git/zip,二阶段上传时repo留空,平台按时间戳定位刚上传的包)。三个例子同一模式。这套流程不依赖任何本地文件——调用方在哪里(本地 stdio 或远端 HTTP)都能执行,wrapper 被部署为远端 MCP 时同样成立。本地形态另有filePath快捷方式(MCP 直接读本机目录打包上传),不进示例。
按场景对号入座:
| 你的应用 | 走哪条路径 | 用到的工具 |
|---|---|---|
| 纯静态 / 标准框架 | 一次上传 + 一次 deployApp | manageApps |
| http 函数(自定义镜像后端) | 上传 + 构建镜像 → 挂成函数 | manageApps + manageFunctions |
| 前后端一体 | 上传 + 一次 deployApp:serviceList + routes | manageApps |
例 1:纯静态 / 标准框架
// 1. 拿预签名上传地址
const { uploadUrl, uploadHeaders, unixTimestamp } = await manageApps({
action: 'getUploadUrl',
serviceName: 'crm-portal',
});
// 2. PUT 源码 zip 到 uploadUrl(带上 uploadHeaders)
// 3. 触发部署:云端拉包构建
await manageApps({
action: 'deployApp',
serviceName: 'crm-portal', // 域名:crm-portal-<envId>.webapps.tcloudbase.com
source: { type: 'zip', cosTimestamp: unixTimestamp }, // 即第 1 步返回的 unixTimestamp
framework: 'react',
installCmd: 'npm ci',
buildCmd: 'npm run build',
buildPath: 'dist', // 相对项目根的产物目录
});
// 4. 轮询状态
await queryApps({ action: 'getAppVersion', serviceName: 'crm-portal', buildId });
// 5. 失败看日志
await queryApps({ action: 'getBuildLog', serviceName: 'crm-portal', buildId });
参数速查:buildPath 是相对项目根的产物目录;非标准构建不用换工具,installCmd / buildCmd 可换成任意命令(如 pnpm --filter app build)。
例 2:http 函数(自定义镜像后端)
构建和部署是两段:构建容器里没有部署命令——构建的终点是把镜像推上仓库,部署发生在容器外的第二次工具调用。
// 0. 上传源码(同例 1:getUploadUrl → PUT zip;custom 场景 getUploadUrl 也带 deployType: 'custom')
// Stage A:构建镜像并推送(manageApps custom,容器内)
await manageApps({
action: 'deployApp',
serviceName: 'order-api',
deployType: 'custom',
source: { type: 'zip', cosTimestamp: unixTimestamp }, // 同例 1:getUploadUrl 返回的 unixTimestamp
// IMAGE = TCR 实例地址/命名空间/服务名,企业自定;tag 必须用 $CLOUDBASE_VERSION_NAME
customSteps: [
{ name: 'build', command: 'docker build -t $IMAGE:$CLOUDBASE_VERSION_NAME .' },
{ name: 'push', command: 'docker push $IMAGE:$CLOUDBASE_VERSION_NAME' },
],
});
// 登录凭证不用传:平台内置注入 $API_SECRET_ID / $API_SECRET_KEY / $API_TOKEN,
// 签 TCR CreateInstanceToken 换临时 token 后 docker login --password-stdin
// Stage B:把镜像挂成 http 函数(容器外)
await manageFunctions({
action: 'createFunction',
func: {
name: 'order-api',
runtime: 'CustomImage',
imageConfig: { imageUri: '$IMAGE:$CLOUDBASE_VERSION_NAME' },
},
});
// 后续迭代用 updateFunctionCode + imageConfig,不用重建函数
要点:容器监听 9000 端口并自己起 HTTP 服务;镜像 ≤ 500MB(冷启动);imageUri 必须带 tag,不能 :latest。
secrets 什么时候用:第三方凭证(私有 npm 源 token、内部制品库账号)才需要传。注意容器内引用名带前缀——传 { name: 'NPM_TOKEN', value: '...' },步骤里引用的是 $SECRET_NPM_TOKEN,不是 $NPM_TOKEN;引用了没传的变量不会报参数错误,跑到那一步才以空值失败。
例 3:前后端一体(一键部署)
deployApp 默认是单服务(例 1 / 例 2),serviceList 把它扩展为多服务:一个应用名下挂多个服务,routes 把路径映射到各服务。一次调用,所有服务进同一个版本历史:
// 0. 上传源码(同例 2:getUploadUrl 带 deployType: 'custom' → PUT zip)
await manageApps({
action: 'deployApp',
serviceName: 'crm-fullstack', // 一个应用名,前后端同属一个版本序列
deployType: 'custom',
source: { type: 'zip', cosTimestamp: unixTimestamp }, // 同例 1:getUploadUrl 返回的 unixTimestamp
serviceList: [ // 不传 = 单服务(例 1 / 例 2)
{ serviceType: 'static-hosting', serviceName: 'web',
buildContext: { path: 'frontend', outPut: 'dist' }, // 该服务的构建目录与产物目录
command: { installCmd: 'npm ci', buildCmd: 'npm run build' } },
{ serviceType: 'http-function', serviceName: 'api',
buildContext: { path: 'backend' } },
],
routes: [ // 不传 = 走默认路由
{ source: '/api', serviceType: 'http-function', serviceName: 'api' },
{ source: '/', serviceType: 'static-hosting', serviceName: 'web' },
],
});
// 轮询与日志同例 1(getAppVersion / getBuildLog)
字段对照:serviceType 枚举 static-hosting / http-function / function;routes[].source 是路径(如 /api),targetPath 暂不支持。
正式发布:单服务应用把配置写进 cloudbaserc.json 进仓库,发布流水线自动执行部署;多服务(serviceList)目前没有声明式对应字段,流水线里用同样参数调 manager SDK 或 CLI 脚本。部署配置都可以让 AI 在对话里组装,进 git 走评审。
给 AI 的知识提示:模型默认不会声明式发布,也写不出 cloudbaserc.json。让 AI 自助配流水线,需要给它两份知识:配置规范(v2.1)和声明式部署——skill 的「声明式部署最小知识」即最小集。
版本语义
同一个 serviceName 重复部署是新增版本不是覆盖,对话触发的试部署和流水线触发的正式发布互不踩,版本历史连续。前端资源用 app 不用 hosting:CloudApp 一个 serviceName 一个独立子域名,hosting 是共享环境域名靠子路径区分,容易互相覆盖。
第五步:验证
用标准 MCP 客户端(假凭证 + 跳过自检)验证协议层,用真凭证验证一次作用域:
initialize握手成功(stdout 没被日志污染)tools/list与插件组合一致- 拦截表动作全部返回
isError: true auth(action="status")返回credential_scope=single_env——看到account说明回落到了全局登录态,停下排查- 真环境部署一次,确认域名可访问
贯穿所有步骤的一条硬要求:接口名、参数、默认值、行为,一律以文档与实测为准,不从记忆推断。拿不准的内容先查API 索引与官方文档,推断值必须标注并验证后再用——这条对 AI 同样生效,把它写进给 AI 的指令里。
远端形态的差异
wrapper 代码同源:工厂创建、插件裁剪、危险动作拦截、审计逻辑全部复用,变的只有四处:
| 差异点 | 本地 | 远端 | 经验 |
|---|---|---|---|
| 传输 | StdioServerTransport | StreamableHTTPServerTransport,短连接(无状态)模式:每请求独立处理,无需会话保持 | 每请求新建一对 server 实例与传输——SDK 硬约束:一个实例同时只能绑一个 transport,并发复用会报「Already connected to a transport」;短连接下每请求新建即可,天然满足 |
| 工具面 | 自己裁(pluginsEnabled + 过滤) | 开 cloudMode: true,官方自动不注册依赖本地文件的工具(声明式部署、本地上传),部分动作受限 | 远端工具黑名单不用自己维护,官方内置 |
| 员工 → wrapper | 无鉴权(本机进程) | 企业 SSO / OAuth 网关 | 网关层鉴权后置换凭证,员工全程零凭证 |
| wrapper → CloudBase | API Key(env 注入,MCP 内部换临时凭证) | OAuth 2.1(浏览器完成登录、免密钥)或静态凭证 header(X-TencentCloud-SecretId / X-TencentCloud-SecretKey,两个 header 位,无 STS Token 位置) | OAuth 适合交互式客户端,header 适合服务端到服务端 |
启用方式:createCloudBaseMcpServer({ cloudMode: true, ... }),传输换 HTTP,其余逻辑不变。
真要做时的起步清单(现有材料能支撑的部分):
- 骨架:第二步的 wrapper 代码原样保留,
StdioServerTransport换成StreamableHTTPServerTransport(短连接模式),每请求新建一对传输与实例即可 - 工具面:加
cloudMode: true,官方自动收窄;自己的tools/list过滤逻辑保留做企业级裁剪 - 鉴权:先定员工 → wrapper 的通道(企业 SSO / OAuth 网关),再定 wrapper → CloudBase 的通道(OAuth 2.1 或静态 header)
- 验证参照:官方托管的远端 MCP 端点(
tcb-api.cloud.tencent.com/mcp/v1)是行为对照物——它的工具面、鉴权流、错误语义就是远端形态的既定事实
短连接模式下不需要会话管理——没有连接生命周期、断线重连、会话粘性这些问题,多实例横向扩容也没有状态负担。只有当需要服务端长连接会话(如 SSE 流式推送)时才引入会话层,届时再补实现。
进阶
- 危险动作的完整拦截表:官方二次确认清单可整体作为黑名单(函数删除、集合删除、MySQL 销毁、托管删除等),见连接方式 · 危险操作
- 更细的权限:API Key 白名单模式(能力 × 读写)+ wrapper 工具过滤双层配合;其他凭证方式(授权码、STS)见其他凭证方式
- 预留能力:
deployType有http-function预留值,开放后函数型部署可走同一套 CloudApp 管理