monacloud-mcp 0.1.2 → 0.2.1
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/README.md +35 -20
- package/STATUS.md +18 -10
- package/dist/clients.d.ts +7 -0
- package/dist/clients.js +17 -4
- package/dist/config.js +1 -1
- package/dist/index.js +1 -1
- package/dist/server.js +127 -55
- package/dist/templates.js +2 -2
- package/docs/ai-agent.md +26 -11
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# monacloud-mcp
|
|
2
2
|
|
|
3
|
-
`monacloud-mcp` là MCP hợp nhất của MONA Cloud: cài một lần, đăng nhập một MONA Pass và dùng chung ví VND để quản lý
|
|
3
|
+
`monacloud-mcp` là MCP hợp nhất của MONA Cloud: cài một lần, đăng nhập một MONA Pass và dùng chung ví VND để quản lý tài khoản, chạy app/VPS, tích hợp MONA Pay và đọc catalog MONA Agent ngay trong Claude Code, Codex hoặc Cursor.
|
|
4
4
|
|
|
5
5
|
Human chỉ cần đăng ký, nạp tiền và cung cấp OTP/KYC khi bắt buộc. Các bước tạo tài nguyên, đọc trạng thái, cấu hình webhook, test và deploy được thiết kế để AI agent làm qua MCP.
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ Human chỉ cần đăng ký, nạp tiền và cung cấp OTP/KYC khi bắt bu
|
|
|
8
8
|
|
|
9
9
|
- Node.js 20 trở lên.
|
|
10
10
|
- Một MONA Pass có quyền dùng client `monacloud-mcp`.
|
|
11
|
-
- MONA Pass, Billing,
|
|
11
|
+
- MONA Pass, Billing, compute MONA Cloud và MONA Pay đã được cấu hình theo môi trường triển khai.
|
|
12
12
|
|
|
13
13
|
## Cài và đăng nhập
|
|
14
14
|
|
|
@@ -69,7 +69,7 @@ Tên tool dùng `snake_case` để giữ tương thích với prompt MONA Pay c
|
|
|
69
69
|
| `cloud_ledger` | Ledger nạp/trừ/hoàn tiền có cursor |
|
|
70
70
|
| `cloud_topup(amount)` | Tạo yêu cầu nạp, trả `qr_data_url` và hướng dẫn VietQR |
|
|
71
71
|
| `cloud_usage(period)` | Usage theo tháng, có thể lọc sản phẩm |
|
|
72
|
-
| `cloud_services` | Gom
|
|
72
|
+
| `cloud_services` | Gom VPS/database, VA và webhook MONA Pay |
|
|
73
73
|
| `cloud_budget_set` / `cloud_budget_get` | Đặt và đọc budget theo product/project/token |
|
|
74
74
|
| `cloud_token_limit` | Đặt spend limit cho PAT/token |
|
|
75
75
|
| `cloud_open_console` | Trả URL console chung |
|
|
@@ -86,36 +86,50 @@ Tên tool dùng `snake_case` để giữ tương thích với prompt MONA Pay c
|
|
|
86
86
|
|
|
87
87
|
Các tên cũ như `monapay_create_qr`, `monapay_link_bank_start`, `monapay_create_webhook` được giữ nguyên. `monapay_link` là adapter chuyển tiếp: đổi MONA Pass thành `client_id/client_secret` rồi cache kín ở `~/.config/monacloud/links.json`. Adapter này sẽ được bỏ khi MONA Pay nhận JWT MONA Pass trực tiếp.
|
|
88
88
|
|
|
89
|
-
###
|
|
89
|
+
### Compute MONA Cloud
|
|
90
90
|
|
|
91
91
|
| Tool | Công dụng |
|
|
92
92
|
|---|---|
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
93
|
+
| `cloud_link` | Xác nhận direct MONA Pass; chỉ đổi sang `vc_live_*` nếu upstream còn ở chế độ cũ |
|
|
94
|
+
| `cloud_vps_create` | Tạo LXC theo `package_slug` hoặc CPU/RAM/đĩa |
|
|
95
|
+
| `cloud_db_create` | Tạo MongoDB, PostgreSQL hoặc MySQL |
|
|
96
|
+
| `cloud_job_status` | Poll job tới trạng thái cuối, có timeout |
|
|
97
|
+
| `cloud_services_list` | Liệt kê VPS/database |
|
|
98
|
+
| `cloud_service_start` / `cloud_service_stop` / `cloud_service_rebuild` | Quản lý vòng đời service |
|
|
99
|
+
| `cloud_prices` / `cloud_packages` | Đơn giá và gói cấu hình |
|
|
100
|
+
| `cloud_agent_deploy` | Stub có cấu trúc cho runtime MONA Agent wave sau |
|
|
101
101
|
|
|
102
|
-
`
|
|
102
|
+
`cloud_service_stop` không bị chặn bởi số dư để người dùng luôn có thể hạn chế chi phí. Những lệnh tạo/start/rebuild thật đọc `GET /v1/balance` trước khi gọi compute MONA Cloud.
|
|
103
|
+
|
|
104
|
+
## Thử 0đ bằng sandbox
|
|
105
|
+
|
|
106
|
+
Truyền `sandbox: true` cho `cloud_vps_create`, `cloud_db_create`, `cloud_service_start`, `cloud_service_rebuild` hoặc `cloud_agent_deploy` để thử luồng mà không cần số dư ví. Có thể bật mặc định cho cả MCP process bằng `MONACLOUD_SANDBOX=1`; các alias `vibecloud_*` có cùng hành vi.
|
|
107
|
+
|
|
108
|
+
Ở chế độ này MCP bỏ qua `GET /v1/balance`, tự gửi `X-Vibecloud-Sandbox: 1` đến compute API và bảo đảm response có `sandbox: true`. Ví dụ:
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
cloud_vps_create({ app_name: "shop-demo", package_slug: "standard-2", sandbox: true })
|
|
112
|
+
cloud_job_status({ job_id: "...", sandbox: true })
|
|
113
|
+
cloud_services_list({ sandbox: true })
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`cloud_job_status` tự tìm job sandbox theo ID mà không cần header. `cloud_services_list({ sandbox: true })` dùng khả năng `include_sandbox` của API để gộp service thật và sandbox. Sandbox không tạo hạ tầng thật, không trừ tiền và có thể bị dọn theo TTL của compute API. `cloud_agent_deploy` vẫn là stub cho đến khi runtime MONA Agent được phát hành, nhưng response sandbox được đánh dấu nhất quán.
|
|
103
117
|
|
|
104
118
|
### MONA Agent
|
|
105
119
|
|
|
106
120
|
- `agent_templates_list`: ưu tiên catalog local tại `~/monacloud/templates`, sau đó URL cấu hình, cuối cùng catalog wave 1 tích hợp.
|
|
107
121
|
- `agent_templates_get(template)`: trả các file `README.md`, `AGENTS.md`, `tools.json`, `deploy.md`, `CHECKLIST.md` và skill text.
|
|
108
|
-
- `agent_deploy(template)`: dùng cùng slot runtime với `
|
|
122
|
+
- `agent_deploy(template)`: dùng cùng slot runtime với `cloud_agent_deploy`; hiện trả `agent_runtime_pending` theo yêu cầu stub của wave này.
|
|
109
123
|
|
|
110
124
|
## Resource và prompt
|
|
111
125
|
|
|
112
126
|
- `monacloud://llms`: mô tả máy đọc của toàn stack MONA Cloud.
|
|
113
|
-
- `monacloud://status`: health tổng hợp MONA Pass, Billing,
|
|
127
|
+
- `monacloud://status`: health tổng hợp MONA Pass, Billing, compute MONA Cloud và MONA Pay.
|
|
114
128
|
- Prompt `dung-app-ban-hang-monacloud`: chuỗi VPS → database → VA/QR → webhook → deploy, chỉ dừng để hỏi nạp tiền hoặc OTP bắt buộc.
|
|
115
129
|
|
|
116
130
|
## Spend guard và lỗi cho AI
|
|
117
131
|
|
|
118
|
-
Trước lệnh
|
|
132
|
+
Trước lệnh compute MONA Cloud thật có thể phát sinh tiền, MCP đọc ví chung. Sandbox bỏ qua bước này vì chi phí 0đ. HTTP `402 insufficient_funds` và `402 budget_exceeded` được chuẩn hoá thành text JSON:
|
|
119
133
|
|
|
120
134
|
```json
|
|
121
135
|
{
|
|
@@ -136,9 +150,9 @@ Người dùng nói:
|
|
|
136
150
|
|
|
137
151
|
Agent thực hiện:
|
|
138
152
|
|
|
139
|
-
1. `cloud_whoami` → `cloud_balance` → `
|
|
153
|
+
1. `cloud_whoami` → `cloud_balance` → `cloud_packages`.
|
|
140
154
|
2. Nếu ví thiếu: `cloud_topup(200000)`, đưa QR cho người dùng và chờ xác nhận.
|
|
141
|
-
3. `
|
|
155
|
+
3. `cloud_vps_create` + `cloud_db_create`, sau đó `cloud_job_status` cho từng job.
|
|
142
156
|
4. `monapay_link` nếu adapter chuyển tiếp chưa có credential; `monapay_whoami` để kiểm tra VA.
|
|
143
157
|
5. Nếu chưa có VA: bắt đầu nối ACB, hỏi người dùng OTP đúng hai điểm bắt buộc, không tự đoán.
|
|
144
158
|
6. Agent viết endpoint webhook HMAC/idempotent, gọi `monapay_create_webhook`, `monapay_test_webhook`, `monapay_webhook_logs`.
|
|
@@ -153,16 +167,17 @@ Chi tiết dành riêng cho agent: [`docs/ai-agent.md`](docs/ai-agent.md).
|
|
|
153
167
|
| `MONACLOUD_ISSUER` | `https://pass.monacloud.vn/realms/mona` | OIDC issuer |
|
|
154
168
|
| `MONACLOUD_BILLING_URL` | `https://billing.monacloud.vn` | Billing/ví API |
|
|
155
169
|
| `MONAPAY_API` | `https://api.monapay.vn` | MONA Pay API |
|
|
156
|
-
| `
|
|
170
|
+
| `MONACLOUD_API` | `https://api.monacloud.vn` | Compute API của MONA Cloud |
|
|
157
171
|
| `MONACLOUD_CONSOLE_URL` | `https://monacloud.vn/console` | URL trả cho bước human |
|
|
158
172
|
| `MONACLOUD_TOKEN` | — | PAT/access token ưu tiên token store |
|
|
173
|
+
| `MONACLOUD_SANDBOX` | — | Đặt `1` để mọi lệnh compute hỗ trợ sandbox chạy thử 0đ, không cần ví |
|
|
159
174
|
| `MONACLOUD_CONFIG_DIR` | `~/.config/monacloud` | Token và link cache |
|
|
160
175
|
| `MONACLOUD_TEMPLATES_DIR` | `~/monacloud/templates` | Catalog agent local |
|
|
161
176
|
| `MONACLOUD_TEMPLATES_URL` | — | Catalog JSON từ xa, chỉ dùng khi local không có |
|
|
162
177
|
| `MONAPAY_LINK_PATH` | `/api/v1/client/oauth/mona-id/link` | Endpoint adapter chuyển tiếp |
|
|
163
178
|
| `VIBECLOUD_LINK_PATH` | `/api/auth/monaid/link` | Endpoint adapter cũ, chỉ gọi khi direct MONA Pass bị từ chối |
|
|
164
179
|
|
|
165
|
-
`MONAPAY_CLIENT_ID` + `MONAPAY_CLIENT_SECRET` và `VIBECLOUD_API_TOKEN` chỉ là đường tương thích trong giai đoạn migrate. `logout` xoá cả token store lẫn linked credential cache; luồng đích là một MONA Pass.
|
|
180
|
+
`VIBECLOUD_API`/`VIBECLOUD_API_URL`, `MONAPAY_CLIENT_ID` + `MONAPAY_CLIENT_SECRET` và `VIBECLOUD_API_TOKEN` chỉ là đường tương thích trong giai đoạn migrate. `logout` xoá cả token store lẫn linked credential cache; luồng đích là một MONA Pass.
|
|
166
181
|
|
|
167
182
|
## Phát triển offline
|
|
168
183
|
|
package/STATUS.md
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
# STATUS — monacloud-mcp 0.1
|
|
1
|
+
# STATUS — monacloud-mcp 0.2.1
|
|
2
2
|
|
|
3
|
-
Updated: 2026-09-
|
|
3
|
+
Updated: 2026-09-04 (Asia/Ho_Chi_Minh)
|
|
4
4
|
|
|
5
5
|
## Hoàn thành
|
|
6
6
|
|
|
7
|
-
- [x] Package Node 20 + TypeScript, ESM, stdio MCP; bin `monacloud-mcp`, version `0.1
|
|
7
|
+
- [x] Package Node 20 + TypeScript, ESM, stdio MCP; bin `monacloud-mcp`, version `0.2.1`.
|
|
8
8
|
- [x] OAuth device flow client `monacloud-mcp`, scope `offline_access`, token store private `0600`, refresh tự động; CLI `login`, `logout`, `whoami`.
|
|
9
9
|
- [x] 10 tool `cloud_*`: identity, balance, ledger, topup VietQR, usage, services aggregation, budget, token limit và console URL.
|
|
10
10
|
- [x] Import package `monapay-mcp` và re-export toàn bộ tool, không copy code. Dependency offline tại lần verify cuối là 0.5.5/47 tool (superset 24 tool yêu cầu); cộng `monapay_link` chuyển tiếp.
|
|
11
|
-
- [x] 11 tool `
|
|
12
|
-
- [x]
|
|
13
|
-
- [x]
|
|
11
|
+
- [x] 11 tool compute tên mới `cloud_*`: link, VPS, database, job poll, list, start/stop/rebuild, prices, packages và agent-deploy stub.
|
|
12
|
+
- [x] 11 alias kỹ thuật `vibecloud_*` vẫn gọi được cho client cũ, không xuất hiện trong README/hướng dẫn agent.
|
|
13
|
+
- [x] 3 tool `agent_*`: list/get catalog local/remote/built-in và deploy qua cùng stub MONA Cloud.
|
|
14
|
+
- [x] Spend guard đọc billing balance trước create/start/rebuild thật; `sandbox: true` hoặc `MONACLOUD_SANDBOX=1` bỏ qua Billing, gửi header compute và đánh dấu response. Stop luôn được phép.
|
|
14
15
|
- [x] 2 resource đúng brief: `monacloud://llms`, `monacloud://status`.
|
|
15
16
|
- [x] 1 prompt `dung-app-ban-hang-monacloud`: VPS → DB → VA/QR → webhook → deploy.
|
|
16
17
|
- [x] Không log token; token và linked credential được lưu bằng atomic write, directory `0700`, file `0600`.
|
|
@@ -18,7 +19,7 @@ Updated: 2026-09-03 (Asia/Ho_Chi_Minh)
|
|
|
18
19
|
- [x] `docs/ai-agent.md` mô tả zero-dashboard flow, OTP boundaries, idempotency, sandbox, deploy và definition of done.
|
|
19
20
|
- [x] Test offline: stdio process thật + `tools/list` + `cloud_whoami` token giả; spend guard; 402; MONA Pay re-export; catalog/path validation.
|
|
20
21
|
|
|
21
|
-
Tổng tool với dependency được chuẩn bị trong handoff tại lần verify cuối: **
|
|
22
|
+
Tổng tool với dependency được chuẩn bị trong handoff tại lần verify cuối: **83** (gồm 11 alias tương thích).
|
|
22
23
|
|
|
23
24
|
## Gate đã chạy
|
|
24
25
|
|
|
@@ -27,7 +28,7 @@ $ node_modules/.bin/tsc -p tsconfig.json
|
|
|
27
28
|
exit 0, không có diagnostic
|
|
28
29
|
|
|
29
30
|
$ node --test
|
|
30
|
-
tests
|
|
31
|
+
tests 13, pass 13, fail 0
|
|
31
32
|
```
|
|
32
33
|
|
|
33
34
|
Không chạy `npm install`; toàn bộ build/test dùng `node_modules` có sẵn và không gọi Internet.
|
|
@@ -35,12 +36,19 @@ Không chạy `npm install`; toàn bộ build/test dùng `node_modules` có sẵ
|
|
|
35
36
|
## Boundary chuyển tiếp đã ghi rõ
|
|
36
37
|
|
|
37
38
|
- `monapay_link` mặc định gọi `/api/v1/client/oauth/mona-id/link`; đổi bằng `MONAPAY_LINK_PATH` nếu upstream chốt path khác. Adapter cache client credential và sẽ bị bỏ khi MONA Pay nhận JWT trực tiếp.
|
|
38
|
-
- `
|
|
39
|
-
- `
|
|
39
|
+
- `cloud_link` thử Bearer MONA Pass trực tiếp trước; chỉ khi upstream trả 401/403 mới gọi adapter `/api/auth/monaid/link` (đổi bằng `VIBECLOUD_LINK_PATH`). Alias kỹ thuật cũ vẫn hoạt động.
|
|
40
|
+
- `cloud_agent_deploy` và `agent_deploy` là stub được brief yêu cầu tới khi runtime MONA Agent được phát hành; response không báo thành công giả.
|
|
41
|
+
- Compute API mặc định là `https://api.monacloud.vn`; `MONACLOUD_API` là env chính, `VIBECLOUD_API`/`VIBECLOUD_API_URL` vẫn được nhận để tương thích.
|
|
40
42
|
- Chưa chạy smoke production vì phiên này offline và không được cấp token/endpoint production. Unit/integration local đã phủ wire contract chính.
|
|
41
43
|
|
|
42
44
|
CODEX DONE
|
|
43
45
|
|
|
46
|
+
## Đổi tên compute VibeCloud → MONA Cloud (04/09)
|
|
47
|
+
|
|
48
|
+
- Tên người đọc và 11 tool chính đã chuyển sang MONA Cloud / `cloud_*`; VibeCloud chỉ còn trong alias và định danh kỹ thuật tương thích.
|
|
49
|
+
- Resource `monacloud://llms`, prompt dựng app, README và `docs/ai-agent.md` chỉ hướng dẫn tên mới.
|
|
50
|
+
- Package, MCP server và binary version đồng bộ `0.2.1`; build và test được ghi ở gate mới bên trên.
|
|
51
|
+
|
|
44
52
|
## QC Claude (03/09 tối) — ZERO-DASHBOARD TEST LOCAL: PASS
|
|
45
53
|
- Build tsc sạch · `node --test` 7/7 · stdio thật: **72 tool** (cloud 10 · monapay 48 · vibecloud 11 · agent 3), 2 resource, 1 prompt.
|
|
46
54
|
- E2E thật với Keycloak local (`MONACLOUD_ISSUER=http://127.0.0.1:8180/realms/mona`) + billing (`MONACLOUD_BILLING_URL=http://127.0.0.1:8191`): `monacloud-mcp login` → mã thiết bị → đăng nhập + màn "Cấp quyền" của MONA ID → `token.json` 0600 → `cloud_whoami` (userinfo thật) → `cloud_balance` 0đ (ví JIT) → `cloud_ledger` → `cloud_budget_get`. **Một token, không chạm dashboard.** *(Log 03/09 giữ tên cũ; tên hiện tại: MONA Pass.)*
|
package/dist/clients.d.ts
CHANGED
|
@@ -20,11 +20,18 @@ export declare class CloudClients {
|
|
|
20
20
|
budgetGet(): Promise<unknown>;
|
|
21
21
|
tokenLimit(spendLimitVnd: number, period: string, tokenId?: string): Promise<unknown>;
|
|
22
22
|
spendGuard(): Promise<JsonObject>;
|
|
23
|
+
sandboxEnabled(requested?: boolean): boolean;
|
|
23
24
|
private vibecloudToken;
|
|
24
25
|
vibecloud<T = unknown>(path: string, options?: {
|
|
25
26
|
method?: string;
|
|
26
27
|
body?: unknown;
|
|
28
|
+
query?: Record<string, string | number | undefined>;
|
|
29
|
+
headers?: Record<string, string>;
|
|
27
30
|
}): Promise<T>;
|
|
31
|
+
guardedVibecloud(path: string, options?: {
|
|
32
|
+
method?: string;
|
|
33
|
+
body?: unknown;
|
|
34
|
+
}, requestedSandbox?: boolean): Promise<unknown>;
|
|
28
35
|
vibecloudLink(): Promise<JsonObject>;
|
|
29
36
|
monapayLink(): Promise<JsonObject>;
|
|
30
37
|
prices(): Promise<unknown>;
|
package/dist/clients.js
CHANGED
|
@@ -69,6 +69,9 @@ export class CloudClients {
|
|
|
69
69
|
}
|
|
70
70
|
return current;
|
|
71
71
|
}
|
|
72
|
+
sandboxEnabled(requested = false) {
|
|
73
|
+
return requested || this.auth.env.MONACLOUD_SANDBOX === '1';
|
|
74
|
+
}
|
|
72
75
|
async vibecloudToken() {
|
|
73
76
|
if (this.auth.env.VIBECLOUD_API_TOKEN)
|
|
74
77
|
return this.auth.env.VIBECLOUD_API_TOKEN;
|
|
@@ -86,9 +89,19 @@ export class CloudClients {
|
|
|
86
89
|
fetchImpl: this.fetchImpl,
|
|
87
90
|
});
|
|
88
91
|
}
|
|
92
|
+
async guardedVibecloud(path, options = {}, requestedSandbox = false) {
|
|
93
|
+
const sandbox = this.sandboxEnabled(requestedSandbox);
|
|
94
|
+
if (!sandbox)
|
|
95
|
+
await this.spendGuard();
|
|
96
|
+
const result = await this.vibecloud(path, {
|
|
97
|
+
...options,
|
|
98
|
+
...(sandbox ? { headers: { 'X-Vibecloud-Sandbox': '1' } } : {}),
|
|
99
|
+
});
|
|
100
|
+
return sandbox ? { ...asObject(result), sandbox: true } : result;
|
|
101
|
+
}
|
|
89
102
|
async vibecloudLink() {
|
|
90
103
|
if (this.auth.env.VIBECLOUD_API_TOKEN) {
|
|
91
|
-
return { linked: true, product: 'vibecloud', mode: 'legacy_env', next_step: 'Dùng các tool
|
|
104
|
+
return { linked: true, product: 'vibecloud', mode: 'legacy_env', next_step: 'Dùng các tool cloud_*.' };
|
|
92
105
|
}
|
|
93
106
|
const monaIdToken = await this.auth.accessToken();
|
|
94
107
|
try {
|
|
@@ -100,7 +113,7 @@ export class CloudClients {
|
|
|
100
113
|
linked: true,
|
|
101
114
|
product: 'vibecloud',
|
|
102
115
|
mode: 'direct_mona_id',
|
|
103
|
-
next_step: '
|
|
116
|
+
next_step: 'MONA Cloud đã nhận MONA Pass trực tiếp; không cần cache vc_live token.',
|
|
104
117
|
};
|
|
105
118
|
}
|
|
106
119
|
catch (error) {
|
|
@@ -117,7 +130,7 @@ export class CloudClients {
|
|
|
117
130
|
const data = asObject(unwrapData(value));
|
|
118
131
|
const token = pickString([data, root], ['api_token', 'access_token', 'token', 'vibecloud_token']);
|
|
119
132
|
if (!token) {
|
|
120
|
-
throw new CloudError('invalid_link_response', '
|
|
133
|
+
throw new CloudError('invalid_link_response', 'MONA Cloud không trả automation token sau khi liên kết.', 'Kiểm tra endpoint chuyển tiếp MONA Pass của MONA Cloud rồi gọi lại cloud_link.');
|
|
121
134
|
}
|
|
122
135
|
const expiresIn = Number(data.expires_in ?? root.expires_in);
|
|
123
136
|
const links = await readLinks(this.config);
|
|
@@ -127,7 +140,7 @@ export class CloudClients {
|
|
|
127
140
|
linked_at: new Date().toISOString(),
|
|
128
141
|
};
|
|
129
142
|
await writeLinks(this.config, links);
|
|
130
|
-
return { linked: true, product: 'vibecloud', next_step: 'Dùng các tool
|
|
143
|
+
return { linked: true, product: 'vibecloud', next_step: 'Dùng các tool cloud_* bằng cùng MONA Pass.' };
|
|
131
144
|
}
|
|
132
145
|
async monapayLink() {
|
|
133
146
|
const value = await requestJson(`${this.config.monapayApi}${this.config.monapayLinkPath}`, {
|
package/dist/config.js
CHANGED
|
@@ -8,7 +8,7 @@ export function readConfig(env = process.env) {
|
|
|
8
8
|
issuer: cleanBaseUrl(env.MONACLOUD_ISSUER || 'https://pass.monacloud.vn/realms/mona'),
|
|
9
9
|
billingUrl: cleanBaseUrl(env.MONACLOUD_BILLING_URL || 'https://billing.monacloud.vn'),
|
|
10
10
|
monapayApi: cleanBaseUrl(env.MONAPAY_API || env.MONAPAY_BASE_URL || 'https://api.monapay.vn'),
|
|
11
|
-
vibecloudApi: cleanBaseUrl(env.VIBECLOUD_API || env.VIBECLOUD_API_URL || 'https://api.
|
|
11
|
+
vibecloudApi: cleanBaseUrl(env.MONACLOUD_API || env.VIBECLOUD_API || env.VIBECLOUD_API_URL || 'https://api.monacloud.vn'),
|
|
12
12
|
consoleUrl: cleanBaseUrl(env.MONACLOUD_CONSOLE_URL || 'https://monacloud.vn/console'),
|
|
13
13
|
clientId: env.MONACLOUD_CLIENT_ID || 'monacloud-mcp',
|
|
14
14
|
scope: env.MONACLOUD_SCOPE || 'openid profile email product billing-api offline_access',
|
package/dist/index.js
CHANGED
package/dist/server.js
CHANGED
|
@@ -8,8 +8,10 @@ import { errorResult, runTool, textResult, toAgentError } from './errors.js';
|
|
|
8
8
|
import { requestJson, unwrapData } from './http.js';
|
|
9
9
|
import { createMonaPayClient, importedMonaPayTools, normalizeImportedResult } from './monapay.js';
|
|
10
10
|
import { TemplateCatalog } from './templates.js';
|
|
11
|
-
const ENTITY = 'MONA Cloud là hệ công cụ cho vibecoder Việt Nam: một MONA Pass, một ví VND và một MCP để
|
|
12
|
-
const TERMINAL_JOBS = new Set(['succeeded', 'failed', 'cancelled']);
|
|
11
|
+
const ENTITY = 'MONA Cloud là hệ công cụ cho vibecoder Việt Nam: một MONA Pass, một ví VND và một MCP để chạy app/VPS, thu tiền MONA Pay và dùng các sản phẩm MONA.';
|
|
12
|
+
const TERMINAL_JOBS = new Set(['done', 'succeeded', 'failed', 'cancelled']);
|
|
13
|
+
const SANDBOX_DESCRIPTION = 'sandbox=true: thử 0đ, không cần ví';
|
|
14
|
+
const sandboxSchema = z.boolean().optional().describe(SANDBOX_DESCRIPTION);
|
|
13
15
|
const asObject = (value) => (value && typeof value === 'object' ? value : {});
|
|
14
16
|
function collection(value) {
|
|
15
17
|
const data = unwrapData(value);
|
|
@@ -29,6 +31,7 @@ const sizingSchema = {
|
|
|
29
31
|
cpu: z.number().int().min(1).max(16).optional(),
|
|
30
32
|
ram_gb: z.number().int().min(1).max(64).optional(),
|
|
31
33
|
disk_gb: z.number().int().min(10).max(1000).optional(),
|
|
34
|
+
sandbox: sandboxSchema,
|
|
32
35
|
};
|
|
33
36
|
const provisionSchema = z.object(sizingSchema).superRefine((value, context) => {
|
|
34
37
|
const dimensions = [value.cpu, value.ram_gb, value.disk_gb];
|
|
@@ -41,28 +44,34 @@ const provisionSchema = z.object(sizingSchema).superRefine((value, context) => {
|
|
|
41
44
|
context.addIssue({ code: 'custom', message: 'Cần package_slug hoặc đủ cpu, ram_gb và disk_gb.' });
|
|
42
45
|
}
|
|
43
46
|
});
|
|
44
|
-
function agentRuntimeStub(template) {
|
|
47
|
+
function agentRuntimeStub(template, sandbox = false) {
|
|
45
48
|
return {
|
|
46
49
|
status: 'not_available',
|
|
47
50
|
code: 'agent_runtime_pending',
|
|
48
51
|
template,
|
|
49
|
-
message: 'Runtime MONA Agent trên
|
|
50
|
-
next_step: 'Dùng agent_templates_get để lấy nội dung template và triển khai thủ công, hoặc thử lại khi
|
|
52
|
+
message: 'Runtime MONA Agent trên MONA Cloud chưa được phát hành; tool này là slot wave kế tiếp theo brief.',
|
|
53
|
+
next_step: 'Dùng agent_templates_get để lấy nội dung template và triển khai thủ công, hoặc thử lại khi MONA Cloud bật runtime agent.',
|
|
54
|
+
...(sandbox ? { sandbox: true } : {}),
|
|
51
55
|
};
|
|
52
56
|
}
|
|
53
|
-
async function pollJob(clients, jobId, wait, intervalSeconds, timeoutSeconds) {
|
|
57
|
+
async function pollJob(clients, jobId, wait, intervalSeconds, timeoutSeconds, sandbox) {
|
|
54
58
|
let result = await clients.vibecloud(`/api/jobs/${encodeURIComponent(jobId)}`);
|
|
55
59
|
if (!wait)
|
|
56
|
-
return result;
|
|
60
|
+
return sandbox ? { ...asObject(result), sandbox: true } : result;
|
|
57
61
|
const deadline = Date.now() + timeoutSeconds * 1000;
|
|
58
62
|
while (!TERMINAL_JOBS.has(String(asObject(result).status || '')) && Date.now() < deadline) {
|
|
59
63
|
await delay(intervalSeconds * 1000);
|
|
60
64
|
result = await clients.vibecloud(`/api/jobs/${encodeURIComponent(jobId)}`);
|
|
61
65
|
}
|
|
62
66
|
if (!TERMINAL_JOBS.has(String(asObject(result).status || ''))) {
|
|
63
|
-
return {
|
|
67
|
+
return {
|
|
68
|
+
...asObject(result),
|
|
69
|
+
...(sandbox ? { sandbox: true } : {}),
|
|
70
|
+
polling: 'timeout',
|
|
71
|
+
next_step: `Gọi lại cloud_job_status với job_id=${jobId}.`,
|
|
72
|
+
};
|
|
64
73
|
}
|
|
65
|
-
return result;
|
|
74
|
+
return sandbox ? { ...asObject(result), sandbox: true } : result;
|
|
66
75
|
}
|
|
67
76
|
async function providerHealth(name, url, fetchImpl) {
|
|
68
77
|
const started = Date.now();
|
|
@@ -81,7 +90,7 @@ export function createServer(dependencies = {}) {
|
|
|
81
90
|
const auth = new AuthManager(config, env, fetchImpl);
|
|
82
91
|
const clients = new CloudClients(config, auth, fetchImpl);
|
|
83
92
|
const catalog = new TemplateCatalog(config, fetchImpl);
|
|
84
|
-
const server = new McpServer({ name: 'monacloud-mcp', version: '0.1
|
|
93
|
+
const server = new McpServer({ name: 'monacloud-mcp', version: '0.2.1' }, { instructions: `${ENTITY}\nDùng cloud_* cho tài khoản, ví và hạ tầng; monapay_* cho thu tiền; agent_* cho catalog. Không bao giờ yêu cầu mật khẩu sản phẩm. Chỉ dừng hỏi người dùng khi cần nạp tiền, OTP ngân hàng hoặc KYC bắt buộc.` });
|
|
85
94
|
server.registerTool('cloud_whoami', {
|
|
86
95
|
title: 'Tài khoản MONA Cloud',
|
|
87
96
|
description: 'Xác minh MONA Pass hiện tại. / Return the current MONA Pass profile.',
|
|
@@ -119,7 +128,7 @@ export function createServer(dependencies = {}) {
|
|
|
119
128
|
}, ({ period, product }) => runTool(() => clients.usage(period, product)));
|
|
120
129
|
server.registerTool('cloud_services', {
|
|
121
130
|
title: 'Mọi dịch vụ đang chạy',
|
|
122
|
-
description: 'Gom
|
|
131
|
+
description: 'Gom VPS/database MONA Cloud cùng tài khoản ảo và webhook MONA Pay.',
|
|
123
132
|
}, () => runTool(async () => {
|
|
124
133
|
const [vibecloud, monapay] = await Promise.all([
|
|
125
134
|
clients.vibecloud('/api/services')
|
|
@@ -177,66 +186,129 @@ export function createServer(dependencies = {}) {
|
|
|
177
186
|
title: 'Liên kết MONA Pay chuyển tiếp',
|
|
178
187
|
description: 'Tạm đổi MONA Pass thành client credential MONA Pay và cache cục bộ; bỏ khi MONA Pay nhận JWT trực tiếp.',
|
|
179
188
|
}, () => runTool(() => clients.monapayLink()));
|
|
189
|
+
server.registerTool('cloud_link', {
|
|
190
|
+
title: 'Liên kết MONA Cloud chuyển tiếp',
|
|
191
|
+
description: 'Xác nhận compute MONA Cloud nhận MONA Pass trực tiếp; chỉ đổi thành vc_live token khi upstream còn ở chế độ cũ.',
|
|
192
|
+
}, () => runTool(() => clients.vibecloudLink()));
|
|
180
193
|
server.registerTool('vibecloud_link', {
|
|
181
|
-
title: '
|
|
182
|
-
description: '
|
|
194
|
+
title: 'Alias cũ của cloud_link',
|
|
195
|
+
description: 'Alias tương thích; dùng cloud_link cho tích hợp mới.',
|
|
183
196
|
}, () => runTool(() => clients.vibecloudLink()));
|
|
197
|
+
server.registerTool('cloud_vps_create', {
|
|
198
|
+
title: 'Tạo VPS MONA Cloud',
|
|
199
|
+
description: `Kiểm tra ví chung rồi tạo LXC VPS; trả job_id để poll. ${SANDBOX_DESCRIPTION}.`,
|
|
200
|
+
inputSchema: provisionSchema,
|
|
201
|
+
}, (body) => runTool(async () => {
|
|
202
|
+
const { sandbox, ...payload } = body;
|
|
203
|
+
return clients.guardedVibecloud('/api/lxc', { method: 'POST', body: payload }, sandbox);
|
|
204
|
+
}));
|
|
184
205
|
server.registerTool('vibecloud_create_vps', {
|
|
185
|
-
title: '
|
|
186
|
-
description:
|
|
206
|
+
title: 'Alias cũ của cloud_vps_create',
|
|
207
|
+
description: `Alias tương thích; dùng cloud_vps_create cho tích hợp mới. ${SANDBOX_DESCRIPTION}.`,
|
|
187
208
|
inputSchema: provisionSchema,
|
|
188
209
|
}, (body) => runTool(async () => {
|
|
189
|
-
|
|
190
|
-
return clients.
|
|
210
|
+
const { sandbox, ...payload } = body;
|
|
211
|
+
return clients.guardedVibecloud('/api/lxc', { method: 'POST', body: payload }, sandbox);
|
|
212
|
+
}));
|
|
213
|
+
const databaseSchema = provisionSchema.extend({ engine: z.enum(['mongodb', 'postgresql', 'mysql']).default('mongodb') });
|
|
214
|
+
server.registerTool('cloud_db_create', {
|
|
215
|
+
title: 'Tạo database MONA Cloud',
|
|
216
|
+
description: `Kiểm tra ví chung rồi tạo MongoDB/PostgreSQL/MySQL; trả job_id để poll. ${SANDBOX_DESCRIPTION}.`,
|
|
217
|
+
inputSchema: databaseSchema,
|
|
218
|
+
}, (body) => runTool(async () => {
|
|
219
|
+
const { sandbox, ...payload } = body;
|
|
220
|
+
return clients.guardedVibecloud('/api/databases', { method: 'POST', body: payload }, sandbox);
|
|
191
221
|
}));
|
|
192
222
|
server.registerTool('vibecloud_create_database', {
|
|
193
|
-
title: '
|
|
194
|
-
description:
|
|
195
|
-
inputSchema:
|
|
223
|
+
title: 'Alias cũ của cloud_db_create',
|
|
224
|
+
description: `Alias tương thích; dùng cloud_db_create cho tích hợp mới. ${SANDBOX_DESCRIPTION}.`,
|
|
225
|
+
inputSchema: databaseSchema,
|
|
196
226
|
}, (body) => runTool(async () => {
|
|
197
|
-
|
|
198
|
-
return clients.
|
|
227
|
+
const { sandbox, ...payload } = body;
|
|
228
|
+
return clients.guardedVibecloud('/api/databases', { method: 'POST', body: payload }, sandbox);
|
|
199
229
|
}));
|
|
230
|
+
const jobStatusSchema = {
|
|
231
|
+
job_id: z.string().min(1),
|
|
232
|
+
wait: z.boolean().default(true),
|
|
233
|
+
interval_sec: z.number().int().min(1).max(30).default(3),
|
|
234
|
+
timeout_sec: z.number().int().min(1).max(600).default(180),
|
|
235
|
+
sandbox: sandboxSchema,
|
|
236
|
+
};
|
|
237
|
+
server.registerTool('cloud_job_status', {
|
|
238
|
+
title: 'Theo dõi job MONA Cloud',
|
|
239
|
+
description: 'Đọc job thật hoặc sandbox theo ID; API tự nhận diện sandbox nên không cần header.',
|
|
240
|
+
inputSchema: jobStatusSchema,
|
|
241
|
+
}, ({ job_id, wait, interval_sec, timeout_sec, sandbox }) => runTool(() => pollJob(clients, job_id, wait, interval_sec, timeout_sec, clients.sandboxEnabled(sandbox))));
|
|
200
242
|
server.registerTool('vibecloud_job_status', {
|
|
201
|
-
title: '
|
|
202
|
-
description: '
|
|
203
|
-
inputSchema:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
},
|
|
209
|
-
}, ({
|
|
243
|
+
title: 'Alias cũ của cloud_job_status',
|
|
244
|
+
description: 'Alias tương thích; dùng cloud_job_status cho tích hợp mới. Job sandbox được tự nhận diện, không cần header.',
|
|
245
|
+
inputSchema: jobStatusSchema,
|
|
246
|
+
}, ({ job_id, wait, interval_sec, timeout_sec, sandbox }) => runTool(() => pollJob(clients, job_id, wait, interval_sec, timeout_sec, clients.sandboxEnabled(sandbox))));
|
|
247
|
+
server.registerTool('cloud_services_list', {
|
|
248
|
+
title: 'Danh sách service MONA Cloud',
|
|
249
|
+
description: 'Liệt kê VPS/database; sandbox=true gộp cả service thử 0đ bằng API, không cần header.',
|
|
250
|
+
inputSchema: { sandbox: sandboxSchema },
|
|
251
|
+
}, ({ sandbox }) => runTool(() => clients.vibecloud('/api/services', {
|
|
252
|
+
query: clients.sandboxEnabled(sandbox) ? { include_sandbox: 1 } : undefined,
|
|
253
|
+
})));
|
|
210
254
|
server.registerTool('vibecloud_list_services', {
|
|
211
|
-
title: '
|
|
212
|
-
description: '
|
|
213
|
-
|
|
255
|
+
title: 'Alias cũ của cloud_services_list',
|
|
256
|
+
description: 'Alias tương thích; dùng cloud_services_list cho tích hợp mới. sandbox=true gộp service thử 0đ, không cần header.',
|
|
257
|
+
inputSchema: { sandbox: sandboxSchema },
|
|
258
|
+
}, ({ sandbox }) => runTool(() => clients.vibecloud('/api/services', {
|
|
259
|
+
query: clients.sandboxEnabled(sandbox) ? { include_sandbox: 1 } : undefined,
|
|
260
|
+
})));
|
|
214
261
|
for (const action of ['start', 'stop', 'rebuild']) {
|
|
215
|
-
|
|
216
|
-
|
|
262
|
+
const name = `cloud_service_${action}`;
|
|
263
|
+
server.registerTool(name, {
|
|
264
|
+
title: `${action} service MONA Cloud`,
|
|
217
265
|
description: action === 'stop'
|
|
218
|
-
? 'Dừng VPS/database
|
|
219
|
-
: `${action} VPS/database
|
|
220
|
-
inputSchema: { service_id: z.string().min(1) },
|
|
221
|
-
}, ({ service_id }) => runTool(async () => {
|
|
266
|
+
? 'Dừng VPS/database MONA Cloud; luôn cho phép dừng để người dùng hạn chế chi phí.'
|
|
267
|
+
: `${action} VPS/database MONA Cloud. Lệnh có thể phát sinh chi phí và kiểm tra ví trước. ${SANDBOX_DESCRIPTION}.`,
|
|
268
|
+
inputSchema: { service_id: z.string().min(1), sandbox: sandboxSchema },
|
|
269
|
+
}, ({ service_id, sandbox }) => runTool(async () => {
|
|
270
|
+
const path = `/api/services/${encodeURIComponent(service_id)}/${action}`;
|
|
222
271
|
if (action !== 'stop')
|
|
223
|
-
|
|
224
|
-
return clients.vibecloud(
|
|
272
|
+
return clients.guardedVibecloud(path, { method: 'POST' }, sandbox);
|
|
273
|
+
return clients.vibecloud(path, { method: 'POST' });
|
|
274
|
+
}));
|
|
275
|
+
server.registerTool(`vibecloud_${action}`, {
|
|
276
|
+
title: `Alias cũ của ${name}`,
|
|
277
|
+
description: `Alias tương thích; dùng ${name} cho tích hợp mới.${action === 'stop' ? '' : ` ${SANDBOX_DESCRIPTION}.`}`,
|
|
278
|
+
inputSchema: { service_id: z.string().min(1), sandbox: sandboxSchema },
|
|
279
|
+
}, ({ service_id, sandbox }) => runTool(async () => {
|
|
280
|
+
const path = `/api/services/${encodeURIComponent(service_id)}/${action}`;
|
|
281
|
+
if (action !== 'stop')
|
|
282
|
+
return clients.guardedVibecloud(path, { method: 'POST' }, sandbox);
|
|
283
|
+
return clients.vibecloud(path, { method: 'POST' });
|
|
225
284
|
}));
|
|
226
285
|
}
|
|
227
|
-
server.registerTool('
|
|
228
|
-
title: 'Bảng giá
|
|
286
|
+
server.registerTool('cloud_prices', {
|
|
287
|
+
title: 'Bảng giá MONA Cloud',
|
|
229
288
|
description: 'Đọc đơn giá giờ hiện hành.',
|
|
230
289
|
}, () => runTool(() => clients.prices()));
|
|
231
|
-
server.registerTool('
|
|
232
|
-
title: '
|
|
290
|
+
server.registerTool('vibecloud_prices', {
|
|
291
|
+
title: 'Alias cũ của cloud_prices',
|
|
292
|
+
description: 'Alias tương thích; dùng cloud_prices cho tích hợp mới.',
|
|
293
|
+
}, () => runTool(() => clients.prices()));
|
|
294
|
+
server.registerTool('cloud_packages', {
|
|
295
|
+
title: 'Gói cấu hình MONA Cloud',
|
|
233
296
|
description: 'Liệt kê package_slug và cấu hình CPU/RAM/đĩa.',
|
|
234
297
|
}, () => runTool(() => clients.packages()));
|
|
298
|
+
server.registerTool('vibecloud_packages', {
|
|
299
|
+
title: 'Alias cũ của cloud_packages',
|
|
300
|
+
description: 'Alias tương thích; dùng cloud_packages cho tích hợp mới.',
|
|
301
|
+
}, () => runTool(() => clients.packages()));
|
|
302
|
+
server.registerTool('cloud_agent_deploy', {
|
|
303
|
+
title: 'Deploy MONA Agent trên MONA Cloud',
|
|
304
|
+
description: `Slot runtime agent wave kế tiếp; hiện trả trạng thái stub rõ ràng. ${SANDBOX_DESCRIPTION}.`,
|
|
305
|
+
inputSchema: { template: z.string().regex(/^[a-z0-9][a-z0-9-]{0,79}$/), sandbox: sandboxSchema },
|
|
306
|
+
}, ({ template, sandbox }) => runTool(() => agentRuntimeStub(template, clients.sandboxEnabled(sandbox))));
|
|
235
307
|
server.registerTool('vibecloud_agent_deploy', {
|
|
236
|
-
title: '
|
|
237
|
-
description:
|
|
238
|
-
inputSchema: { template: z.string().regex(/^[a-z0-9][a-z0-9-]{0,79}$/) },
|
|
239
|
-
}, ({ template }) => runTool(() => agentRuntimeStub(template)));
|
|
308
|
+
title: 'Alias cũ của cloud_agent_deploy',
|
|
309
|
+
description: `Alias tương thích; dùng cloud_agent_deploy cho tích hợp mới. ${SANDBOX_DESCRIPTION}.`,
|
|
310
|
+
inputSchema: { template: z.string().regex(/^[a-z0-9][a-z0-9-]{0,79}$/), sandbox: sandboxSchema },
|
|
311
|
+
}, ({ template, sandbox }) => runTool(() => agentRuntimeStub(template, clients.sandboxEnabled(sandbox))));
|
|
240
312
|
server.registerTool('agent_templates_list', {
|
|
241
313
|
title: 'Catalog MONA Agent',
|
|
242
314
|
description: 'Đọc template từ thư mục local, URL catalog hoặc catalog wave 1 tích hợp.',
|
|
@@ -248,7 +320,7 @@ export function createServer(dependencies = {}) {
|
|
|
248
320
|
}, ({ template }) => runTool(() => catalog.get(template)));
|
|
249
321
|
server.registerTool('agent_deploy', {
|
|
250
322
|
title: 'Dùng ngay MONA Agent template',
|
|
251
|
-
description: 'Gọi cùng runtime với
|
|
323
|
+
description: 'Gọi cùng runtime với cloud_agent_deploy.',
|
|
252
324
|
inputSchema: { template: z.string().regex(/^[a-z0-9][a-z0-9-]{0,79}$/) },
|
|
253
325
|
}, ({ template }) => runTool(() => agentRuntimeStub(template)));
|
|
254
326
|
const imported = importedMonaPayTools(config, env, fetchImpl);
|
|
@@ -279,17 +351,17 @@ export function createServer(dependencies = {}) {
|
|
|
279
351
|
}, async (uri) => ({ contents: [{
|
|
280
352
|
uri: uri.href,
|
|
281
353
|
mimeType: 'text/plain',
|
|
282
|
-
text: `${ENTITY}\n\nHuman chỉ đăng ký MONA Pass, nạp tiền và cung cấp OTP/KYC bắt buộc. AI dùng MCP làm phần còn lại.\n\n- cloud_*: tài khoản, ví, ledger, usage, budget,
|
|
354
|
+
text: `${ENTITY}\n\nHuman chỉ đăng ký MONA Pass, nạp tiền và cung cấp OTP/KYC bắt buộc. AI dùng MCP làm phần còn lại.\n\n- cloud_*: tài khoản, ví, ledger, usage, budget, VPS, database, job, vòng đời service và giá/gói.\n- monapay_*: nối ngân hàng, checkout/QR, giao dịch, webhook và email.\n- agent_*: catalog và deploy template.\n\nMONA Cloud: https://monacloud.vn\nCompute API: https://api.monacloud.vn\nMONA Pay: https://monapay.vn\n`,
|
|
283
355
|
}] }));
|
|
284
356
|
server.registerResource('monacloud-status', 'monacloud://status', {
|
|
285
357
|
title: 'Trạng thái hệ MONA Cloud',
|
|
286
|
-
description: 'Health tổng hợp của MONA Pass, billing,
|
|
358
|
+
description: 'Health tổng hợp của MONA Pass, billing, compute MONA Cloud và MONA Pay.',
|
|
287
359
|
mimeType: 'application/json',
|
|
288
360
|
}, async (uri) => {
|
|
289
361
|
const members = await Promise.all([
|
|
290
362
|
providerHealth('mona-id', `${config.issuer}/.well-known/openid-configuration`, fetchImpl),
|
|
291
363
|
providerHealth('billing', `${config.billingUrl}/v1/healthz`, fetchImpl),
|
|
292
|
-
providerHealth('
|
|
364
|
+
providerHealth('monacloud-compute', `${config.vibecloudApi}/api/prices`, fetchImpl),
|
|
293
365
|
providerHealth('monapay', `${config.monapayApi}/health`, fetchImpl),
|
|
294
366
|
]);
|
|
295
367
|
return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify({ checked_at: new Date().toISOString(), members }, null, 2) }] };
|
|
@@ -305,7 +377,7 @@ export function createServer(dependencies = {}) {
|
|
|
305
377
|
role: 'user',
|
|
306
378
|
content: {
|
|
307
379
|
type: 'text',
|
|
308
|
-
text: `Dựng app bán hàng ${app_name || 'của tôi'} bằng ${framework || 'stack phù hợp'} trên MONA Cloud theo đúng thứ tự:\n1. Gọi cloud_whoami, cloud_balance,
|
|
380
|
+
text: `Dựng app bán hàng ${app_name || 'của tôi'} bằng ${framework || 'stack phù hợp'} trên MONA Cloud theo đúng thứ tự:\n1. Gọi cloud_whoami, cloud_balance, cloud_packages và cloud_prices. Nếu thiếu tiền, gọi cloud_topup rồi dừng để người dùng quét VietQR.\n2. Gọi cloud_vps_create và cloud_db_create; poll từng job bằng cloud_job_status tới succeeded.\n3. Gọi monapay_link nếu MONA Pay còn ở lớp chuyển tiếp. Gọi monapay_whoami; nếu chưa có VA, nối ngân hàng bằng chuỗi monapay_link_bank_start → HỎI OTP → verify → notification_register → HỎI OTP lần 2 → verify. Không tự đoán OTP.\n4. Viết endpoint webhook có HMAC và idempotency theo transaction_code; đăng ký bằng monapay_create_webhook, bắn monapay_test_webhook và đọc monapay_webhook_logs.\n5. Tích hợp monapay_create_checkout hoặc monapay_create_qr vào app, chỉ giao hàng sau CHECKOUT_PAID.\n6. Deploy code lên VPS vừa tạo, kiểm tra health và báo URL/credential cần lưu. Không yêu cầu người dùng mở dashboard ngoài bước nạp tiền/OTP bắt buộc.`,
|
|
309
381
|
},
|
|
310
382
|
}] }));
|
|
311
383
|
return server;
|
package/dist/templates.js
CHANGED
|
@@ -7,7 +7,7 @@ const BUILTIN = [
|
|
|
7
7
|
{ slug: 'sales-chot-don', name: 'Sales chốt đơn', description: 'Tư vấn, báo giá, tạo QR MONA Pay và xác nhận tiền vào.', products: ['MONA Pay', 'MONA AI'] },
|
|
8
8
|
{ slug: 'ke-toan-hddt', name: 'Kế toán HĐĐT', description: 'Đọc tiền vào, phát hành hoá đơn điện tử và nhắc công nợ.', products: ['MONA Pay', 'monahddt', 'MONA Mail'] },
|
|
9
9
|
{ slug: 'content-seo', name: 'Content SEO', description: 'Viết và đăng bài theo voice thương hiệu, có gate QC.', products: ['MONA AI'] },
|
|
10
|
-
{ slug: 'noi-bo-kin', name: 'Nội bộ kín', description: 'Trợ lý đọc tài liệu công ty, dữ liệu không rời server.', products: ['
|
|
10
|
+
{ slug: 'noi-bo-kin', name: 'Nội bộ kín', description: 'Trợ lý đọc tài liệu công ty, dữ liệu không rời server.', products: ['MONA Cloud', 'MONA Base', 'Ollama'] },
|
|
11
11
|
{ slug: 'tro-giang-academy', name: 'Trợ giảng Academy', description: 'Trợ giảng cho academy của giảng viên.', products: ['mona.academy', 'MONA AI'] },
|
|
12
12
|
];
|
|
13
13
|
const allowedSlug = /^[a-z0-9][a-z0-9-]{0,79}$/;
|
|
@@ -111,7 +111,7 @@ export class TemplateCatalog {
|
|
|
111
111
|
...template,
|
|
112
112
|
source: 'built-in-wave-1-catalog',
|
|
113
113
|
status: 'catalog_only',
|
|
114
|
-
next_step: 'Catalog runtime chưa được phát hành; dùng
|
|
114
|
+
next_step: 'Catalog runtime chưa được phát hành; dùng cloud_agent_deploy để kiểm tra trạng thái triển khai.',
|
|
115
115
|
};
|
|
116
116
|
}
|
|
117
117
|
}
|
package/docs/ai-agent.md
CHANGED
|
@@ -5,7 +5,7 @@ Tài liệu này dành cho Claude, Codex, Cursor và agent dùng Model Context P
|
|
|
5
5
|
## Quy tắc vận hành
|
|
6
6
|
|
|
7
7
|
1. Bắt đầu bằng `cloud_whoami`. Nếu nhận `login_required`, yêu cầu người dùng chạy `monacloud-mcp login`; không hỏi username hoặc password trong chat.
|
|
8
|
-
2. Trước khi provision, đọc `cloud_balance`, `
|
|
8
|
+
2. Trước khi provision thật, đọc `cloud_balance`, `cloud_packages` và `cloud_prices`. Với `sandbox: true`, không cần đọc hoặc nạp ví.
|
|
9
9
|
3. Khi cần nạp, gọi `cloud_topup`; đưa nguyên `qr_data_url`, số tiền, nội dung và hạn thanh toán cho người dùng. Chỉ tiếp tục sau khi `cloud_balance` phản ánh tiền vào.
|
|
10
10
|
4. Với OTP ngân hàng, dừng đúng sau `monapay_link_bank_start` và `monapay_notification_register`. Hỏi người dùng mã vừa nhận; không suy đoán, brute-force hoặc ghi OTP vào source/log.
|
|
11
11
|
5. Mọi endpoint webhook phải xác minh HMAC, chống replay theo timestamp và idempotent theo `transaction_code`.
|
|
@@ -26,10 +26,10 @@ Không in hoặc copy token vào source, issue, log, output tool hay nội dung
|
|
|
26
26
|
|
|
27
27
|
### Adapter chuyển tiếp
|
|
28
28
|
|
|
29
|
-
MONA Pay và
|
|
29
|
+
MONA Pay và compute MONA Cloud có thể chưa nhận JWT MONA Pass trực tiếp tại thời điểm wave 1:
|
|
30
30
|
|
|
31
31
|
- gọi `monapay_link` một lần để nhận/cache `client_id/client_secret` MONA Pay;
|
|
32
|
-
- gọi `
|
|
32
|
+
- gọi `cloud_link` để xác nhận direct MONA Pass; tool chỉ nhận/cache `vc_live_*` khi upstream từ chối JWT và còn bật adapter cũ;
|
|
33
33
|
- linked credential nằm ở `~/.config/monacloud/links.json`, mode `0600`;
|
|
34
34
|
- không gọi link lại ở mỗi request;
|
|
35
35
|
- khi upstream nhận JWT trực tiếp, bỏ adapter mà không đổi tên tool nghiệp vụ.
|
|
@@ -43,8 +43,8 @@ Nếu tài khoản MONA Pay cũ cần OTP email để link, trả quyền điề
|
|
|
43
43
|
```text
|
|
44
44
|
cloud_whoami
|
|
45
45
|
cloud_balance
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
cloud_packages
|
|
47
|
+
cloud_prices
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
Chọn `package_slug` nếu có thể. Chỉ dùng custom sizing khi cần; không gửi đồng thời `package_slug` và `cpu/ram_gb/disk_gb`.
|
|
@@ -60,14 +60,14 @@ Giữ cùng idempotency key khi retry cùng một yêu cầu nạp.
|
|
|
60
60
|
### 2. Tạo compute và database
|
|
61
61
|
|
|
62
62
|
```text
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
cloud_vps_create({ app_name: "shop-demo", package_slug: "standard-2" })
|
|
64
|
+
cloud_db_create({ app_name: "shop-demo", engine: "postgresql", package_slug: "standard-2" })
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
Mỗi response là job. Dùng:
|
|
68
68
|
|
|
69
69
|
```text
|
|
70
|
-
|
|
70
|
+
cloud_job_status({ job_id: "...", wait: true, interval_sec: 3, timeout_sec: 180 })
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
Chỉ dùng `result`/credential khi trạng thái `succeeded`. Với `failed`, đọc `error`; sửa nguyên nhân rồi mới retry. Không đưa database password vào commit hoặc response công khai.
|
|
@@ -130,7 +130,7 @@ Deploy source lên VPS từ credential của job, đưa database connection stri
|
|
|
130
130
|
|
|
131
131
|
## Spend guard
|
|
132
132
|
|
|
133
|
-
`
|
|
133
|
+
`cloud_vps_create`, `cloud_db_create`, `cloud_service_start` và `cloud_service_rebuild` đọc `GET /v1/balance` trước khi gọi sản phẩm thật. Upstream vẫn là nguồn quyết định cuối và có thể trả:
|
|
134
134
|
|
|
135
135
|
```json
|
|
136
136
|
{
|
|
@@ -140,13 +140,28 @@ Deploy source lên VPS từ credential của job, đưa database connection stri
|
|
|
140
140
|
}
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
-
hoặc `budget_exceeded`. Khi gặp hai code này, không retry. Gọi `cloud_topup`, `cloud_budget_get` hoặc yêu cầu user tăng budget. `
|
|
143
|
+
hoặc `budget_exceeded`. Khi gặp hai code này, không retry. Gọi `cloud_topup`, `cloud_budget_get` hoặc yêu cầu user tăng budget. `cloud_service_stop` luôn được phép vì chặn stop có thể làm tăng rủi ro chi phí.
|
|
144
|
+
|
|
145
|
+
## Thử 0đ bằng sandbox
|
|
146
|
+
|
|
147
|
+
Thêm `sandbox: true` khi gọi `cloud_vps_create`, `cloud_db_create`, `cloud_service_start`, `cloud_service_rebuild` hoặc `cloud_agent_deploy`. Có thể đặt `MONACLOUD_SANDBOX=1` để bật cho toàn bộ MCP process. Alias `vibecloud_*` tương ứng hoạt động giống hệt.
|
|
148
|
+
|
|
149
|
+
```text
|
|
150
|
+
cloud_vps_create({ app_name: "shop-demo", package_slug: "standard-2", sandbox: true })
|
|
151
|
+
cloud_db_create({ app_name: "shop-demo", engine: "postgresql", package_slug: "standard-2", sandbox: true })
|
|
152
|
+
cloud_job_status({ job_id: "...", sandbox: true })
|
|
153
|
+
cloud_services_list({ sandbox: true })
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Với lệnh tạo/start/rebuild sandbox, MCP không gọi Billing, tự gửi `X-Vibecloud-Sandbox: 1`, và response có `sandbox: true`. `cloud_job_status` tra job theo ID mà không cần header; trạng thái `done` là terminal hợp lệ của sandbox. `cloud_services_list({ sandbox: true })` dùng `include_sandbox=1`, cũng không cần header, để gộp service thật và sandbox.
|
|
157
|
+
|
|
158
|
+
Sandbox không gọi Proxmox, không cấp IP thật, không trừ tiền và có TTL do compute API quản lý. Chỉ chuyển sang lệnh thật khi người dùng yêu cầu rõ ràng. `cloud_agent_deploy` hiện vẫn trả `agent_runtime_pending`; sandbox chỉ đánh dấu response nhất quán, không giả vờ đã deploy.
|
|
144
159
|
|
|
145
160
|
## Catalog MONA Agent
|
|
146
161
|
|
|
147
162
|
`agent_templates_list` trả nguồn catalog và danh sách slug. `agent_templates_get` trả toàn bộ file text cần để agent build local. Không tự nối path từ input ngoài; chỉ dùng slug đã trả.
|
|
148
163
|
|
|
149
|
-
`agent_deploy` và `
|
|
164
|
+
`agent_deploy` và `cloud_agent_deploy` hiện là stub có chủ đích, trả `agent_runtime_pending`; không giả vờ đã provision. Khi nhận code này, có thể đọc template và triển khai thủ công hoặc báo rõ runtime hosted chưa được bật.
|
|
150
165
|
|
|
151
166
|
## Resource hữu ích
|
|
152
167
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "monacloud-mcp",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "Unified MCP server for MONA Cloud, MONA Pay
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Unified MCP server for MONA Cloud, MONA Pay and MONA Agent.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|