Skip to content

Commit b575600

Browse files
authored
Merge pull request #505 from TencentCloudBase/feature/update-skills-and-configs
feat(tooling): ✨ expand manager-node 5.1 auth and workflow support
2 parents b516755 + 4eb1fd3 commit b575600

16 files changed

Lines changed: 4736 additions & 427 deletions

config/source/skills/auth-tool/SKILL.md

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,25 @@ Prefer MCP: `queryAppAuth(action="getStaticDomain")` — use `cdnDomain` / `stat
375375
}
376376
```
377377

378-
### 8. Client Configuration Boundary
378+
### 8. Provider Lifecycle Boundary
379+
380+
Use provider lifecycle APIs when the identity source itself needs to be created, updated, or removed.
381+
382+
Preferred MCP tool path:
383+
384+
- `queryAppAuth(action="listProviders")`
385+
- `queryAppAuth(action="getProvider")`
386+
- `manageAppAuth(action="addProvider")`
387+
- `manageAppAuth(action="updateProvider")`
388+
- `manageAppAuth(action="deleteProvider")`
389+
390+
Guidance:
391+
392+
- Use `addProvider` when the provider record does not exist yet and you need to create it with `providerType`, optional `providerId`, `displayName`, and `config`.
393+
- Use `updateProvider` when the provider already exists and only its configuration or enablement state needs to change.
394+
- Use `deleteProvider` when the provider must be removed entirely instead of only disabling it.
395+
396+
### 9. Client Configuration Boundary
379397

380398
Use client APIs for client metadata and token/session settings. Do not use them as a replacement for login strategy or provider management.
381399

@@ -410,14 +428,20 @@ Both tools should default to the current selected environment's default client.
410428
}
411429
```
412430

413-
### 9. Get Publishable Key
431+
### 10. Publishable Key and API Key Boundary
414432

415433
Preferred MCP tool path:
416434

417435
- `queryAppAuth(action="getPublishableKey")`
418436
- `manageAppAuth(action="ensurePublishableKey")`
437+
- `queryAppAuth(action="listApiKeys")`
438+
- `manageAppAuth(action="createApiKey")`
439+
- `manageAppAuth(action="deleteApiKey")`
440+
441+
Use the shortcut pair `getPublishableKey` / `ensurePublishableKey` for the most common frontend-readiness flow.
442+
Use the generic API key lifecycle actions when you need inventory, pagination, non-publishable keys, or explicit deletion.
419443

420-
**Query existing key**:
444+
**Query existing publishable key**:
421445
```js
422446
{
423447
"params": { "EnvId": `env`, "KeyType": "publish_key", "PageNumber": 1, "PageSize": 10 },
@@ -427,7 +451,18 @@ Preferred MCP tool path:
427451
```
428452
`queryAppAuth(action="getPublishableKey")` should always force `KeyType="publish_key"` and return a short payload with `publishableKey`, `keyId`, `keyName`, `expireAt`, and `createdAt`.
429453

430-
**Ensure key exists**:
454+
**List API keys**:
455+
```json
456+
{
457+
"action": "listApiKeys",
458+
"keyType": "api_key",
459+
"pageNumber": 1,
460+
"pageSize": 20
461+
}
462+
```
463+
Use `listApiKeys` for a general key inventory view. It supports optional `keyType`, `pageNumber`, and `pageSize`.
464+
465+
**Ensure publishable key exists**:
431466
```js
432467
{
433468
"params": { "EnvId": `env`, "KeyType": "publish_key" },
@@ -437,9 +472,29 @@ Preferred MCP tool path:
437472
```
438473
`manageAppAuth(action="ensurePublishableKey")` should first query the existing `publish_key`; if one already exists, return it directly; otherwise create it and return the new key. This keeps the MCP interface short and avoids requiring the model to reason about `KeyType` or whether a key already exists.
439474

475+
**Create a generic API key**:
476+
```json
477+
{
478+
"action": "createApiKey",
479+
"keyType": "api_key",
480+
"keyName": "server-prod",
481+
"expireIn": 86400
482+
}
483+
```
484+
`createApiKey` defaults to `publish_key` when `keyType` is omitted, but it can also create `api_key` for generic service-side access.
485+
486+
**Delete an API key**:
487+
```json
488+
{
489+
"action": "deleteApiKey",
490+
"keyId": "api-key-id"
491+
}
492+
```
493+
Use `deleteApiKey` only when you intentionally want to revoke that key token.
494+
440495
If creation fails, direct user to: "https://tcb.cloud.tencent.com/dev?envId=`env`#/env/apikey"
441496

442-
### 10. Custom Login Keys
497+
### 11. Custom Login Keys
443498

444499
Preferred MCP tool path: `manageAppAuth(action="createCustomLoginKeys")`
445500

doc/mcp-tools.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -793,18 +793,24 @@ API名:storage API介绍:Storage API - 云存储 HTTP API
793793
---
794794

795795
### `manageAppAuth`
796-
应用侧认证配置写入口。用于修改登录方式、provider、client 配置,确保 publishable key 和创建自定义登录密钥
796+
应用侧认证配置写入口。用于修改登录方式、provider、client 配置,以及创建或删除 API key、自定义登录密钥
797797

798798
#### 参数
799799

800800
<table>
801801
<thead><tr><th>参数名</th><th>类型</th><th>必填</th><th>说明</th></tr></thead>
802802
<tbody>
803-
<tr><td><code>action</code></td><td>string</td><td>是</td><td>可填写的值: "patchLoginStrategy", "updateProvider", "updateClientConfig", "ensurePublishableKey", "createCustomLoginKeys"</td></tr>
803+
<tr><td><code>action</code></td><td>string</td><td>是</td><td>可填写的值: "patchLoginStrategy", "addProvider", "updateProvider", "deleteProvider", "updateClientConfig", "ensurePublishableKey", "createApiKey", "deleteApiKey", "createCustomLoginKeys"</td></tr>
804804
<tr><td><code>patch</code></td><td>object</td><td></td><td>patchLoginStrategy 使用的简化登录策略 patch,如 &#123; usernamePassword: true &#125;</td></tr>
805-
<tr><td><code>providerId</code></td><td>string</td><td></td><td>provider 标识,如 email、google</td></tr>
805+
<tr><td><code>providerId</code></td><td>string</td><td></td><td>provider 标识,如 email、google;addProvider 时也可作为自定义 provider Id</td></tr>
806+
<tr><td><code>providerType</code></td><td>string</td><td></td><td>addProvider 时的 provider 协议类型,如 OAUTH、OIDC、EMAIL</td></tr>
807+
<tr><td><code>displayName</code></td><td>string or object</td><td></td><td>addProvider 时的展示名称,可传字符串或多语言对象</td></tr>
806808
<tr><td><code>clientId</code></td><td>string</td><td></td><td>updateClientConfig 时的客户端 Id;省略时默认使用当前环境 ID</td></tr>
807809
<tr><td><code>config</code></td><td>object</td><td></td><td>provider / client 的配置对象</td></tr>
810+
<tr><td><code>keyType</code></td><td>string</td><td></td><td>createApiKey 时的 API key 类型,默认 "publish_key"</td></tr>
811+
<tr><td><code>keyName</code></td><td>string</td><td></td><td>createApiKey 时的 API key 名称</td></tr>
812+
<tr><td><code>expireIn</code></td><td>number</td><td></td><td>createApiKey 时的有效期,单位秒;0 表示不过期</td></tr>
813+
<tr><td><code>keyId</code></td><td>string</td><td></td><td>deleteApiKey 时的 API key 唯一标识</td></tr>
808814
</tbody>
809815
</table>
810816

mcp/package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mcp/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
"dependencies": {
6666
"@cloudbase/cals": "^1.2.23",
6767
"@cloudbase/functions-framework": "^1.14.0",
68-
"@cloudbase/manager-node": "5.0.0",
68+
"@cloudbase/manager-node": "^5.1.0",
6969
"@cloudbase/mcp": "^1.0.0-beta.25",
7070
"@cloudbase/toolbox": "^0.7.19",
7171
"@modelcontextprotocol/sdk": "1.13.1",

mcp/src/cloudbase-manager.test.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,121 @@ describe("cloudbase manager auth gate", () => {
121121
await expect(getEnvId()).resolves.toBe("env-1");
122122
});
123123

124+
it("should resolve actual env region when envId is provided without region", async () => {
125+
process.env.TCB_REGION = "ap-shanghai";
126+
mockPeekLoginState.mockResolvedValue({
127+
secretId: "sid",
128+
secretKey: "skey",
129+
token: "token",
130+
});
131+
mockCommonServiceCall.mockResolvedValue({
132+
EnvList: [
133+
{
134+
EnvId: "env-explicit",
135+
Alias: "prod",
136+
Region: "ap-guangzhou",
137+
},
138+
],
139+
});
140+
141+
const { getCloudBaseManager } = await import("./cloudbase-manager.js");
142+
143+
await expect(
144+
getCloudBaseManager({
145+
cloudBaseOptions: {
146+
envId: "env-explicit",
147+
},
148+
}),
149+
).resolves.toMatchObject({
150+
commonService: expect.any(Function),
151+
env: expect.any(Object),
152+
});
153+
154+
expect(mockCloudBaseCtor).toHaveBeenLastCalledWith(
155+
expect.objectContaining({
156+
secretId: "sid",
157+
secretKey: "skey",
158+
token: "token",
159+
envId: "env-explicit",
160+
region: "ap-guangzhou",
161+
}),
162+
);
163+
expect(mockCommonServiceCall).toHaveBeenCalledWith(
164+
expect.objectContaining({
165+
Action: "DescribeEnvs",
166+
}),
167+
);
168+
});
169+
170+
it("should honor explicit region without resolving env candidates", async () => {
171+
mockPeekLoginState.mockResolvedValue({
172+
secretId: "sid",
173+
secretKey: "skey",
174+
token: "token",
175+
});
176+
177+
const { getCloudBaseManager } = await import("./cloudbase-manager.js");
178+
179+
await expect(
180+
getCloudBaseManager({
181+
cloudBaseOptions: {
182+
envId: "env-explicit",
183+
region: "ap-guangzhou",
184+
},
185+
}),
186+
).resolves.toMatchObject({
187+
commonService: expect.any(Function),
188+
env: expect.any(Object),
189+
});
190+
191+
expect(mockCloudBaseCtor).toHaveBeenLastCalledWith(
192+
expect.objectContaining({
193+
secretId: "sid",
194+
secretKey: "skey",
195+
token: "token",
196+
envId: "env-explicit",
197+
region: "ap-guangzhou",
198+
}),
199+
);
200+
expect(mockCommonServiceCall).not.toHaveBeenCalled();
201+
});
202+
203+
it("should prefer actual env region over fallback region when login envId is already known", async () => {
204+
process.env.TCB_REGION = "ap-shanghai";
205+
mockPeekLoginState.mockResolvedValue({
206+
secretId: "sid",
207+
secretKey: "skey",
208+
token: "token",
209+
envId: "env-guangzhou",
210+
});
211+
mockCommonServiceCall.mockResolvedValue({
212+
EnvList: [
213+
{
214+
EnvId: "env-guangzhou",
215+
Alias: "prod",
216+
Region: "ap-guangzhou",
217+
},
218+
],
219+
});
220+
221+
const { getCloudBaseManager } = await import("./cloudbase-manager.js");
222+
223+
await expect(getCloudBaseManager()).resolves.toMatchObject({
224+
commonService: expect.any(Function),
225+
env: expect.any(Object),
226+
});
227+
228+
expect(mockCloudBaseCtor).toHaveBeenLastCalledWith(
229+
expect.objectContaining({
230+
secretId: "sid",
231+
secretKey: "skey",
232+
token: "token",
233+
envId: "env-guangzhou",
234+
region: "ap-guangzhou",
235+
}),
236+
);
237+
});
238+
124239
it("should fail fast with ENV_REQUIRED when login exists but multiple envs", async () => {
125240
mockPeekLoginState.mockResolvedValue({
126241
secretId: "sid",

0 commit comments

Comments
 (0)