monacloud-mcp 0.8.1 → 0.10.0
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 +19 -1
- package/dist/clients.d.ts +12 -0
- package/dist/clients.js +23 -0
- package/dist/domains.d.ts +3 -0
- package/dist/domains.js +273 -0
- package/dist/server.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Human đăng ký MONA Pass một lần, duyệt chi phí, thêm DNS khi cần, q
|
|
|
6
6
|
|
|
7
7
|
## 0.8.1 — luật nạp ví đứng đầu instructions
|
|
8
8
|
|
|
9
|
-
Codex chỉ chắc chắn đọc 512 ký tự đầu của `instructions` MCP, nên luật "ví thiếu → AI tự gọi `cloud_topup`, in QR, không bảo mở console" được đưa lên đầu; mô tả hệ và APP_FLOW đặt sau. Kiểm thử 17/09: Claude Code (sonnet) và Gemini (Antigravity CLI) tự gọi `cloud_topup` và in QR khi người dùng nói "ví hết tiền, nạp 50k"; Codex `exec`
|
|
9
|
+
Codex chỉ chắc chắn đọc 512 ký tự đầu của `instructions` MCP, nên luật "ví thiếu → AI tự gọi `cloud_topup`, in QR, không bảo mở console" được đưa lên đầu; mô tả hệ và APP_FLOW đặt sau. Kiểm thử 17/09: Claude Code (sonnet) và Gemini (Antigravity CLI) tự gọi `cloud_topup` và in QR khi người dùng nói "ví hết tiền, nạp 50k"; Codex chạy headless (`codex exec`) mặc định chặn mọi lời gọi MCP theo approval policy; thêm `--approve-for-me` thì Codex cũng tự gọi `cloud_topup` và in QR (kiểm 17/09). Codex tương tác thì duyệt lời gọi tool như bình thường.
|
|
10
10
|
|
|
11
11
|
## 0.8.0 — Nạp ví ngay trong terminal
|
|
12
12
|
|
|
@@ -150,6 +150,24 @@ Mỗi tool có alias `vibecloud_base_*` cùng schema và handler. Cần DB/Supab
|
|
|
150
150
|
|
|
151
151
|
`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.
|
|
152
152
|
|
|
153
|
+
### MONA Domain — tên miền cho app (monadomain.vn)
|
|
154
|
+
|
|
155
|
+
AI tra tên, báo giá VND đã VAT, hỏi chủ thể, mua .vn/.com bằng ví MONA Cloud rồi trỏ DNS + SSL vào app. Tra giá **không cần đăng nhập**; người dùng chưa có tài khoản vẫn mua được trong phiên bằng đường giữ chỗ → quét QR → bấm claim (đăng nhập MONA Pass 1 bước = tạo ví = mua).
|
|
156
|
+
|
|
157
|
+
| Tool | Công dụng |
|
|
158
|
+
|---|---|
|
|
159
|
+
| `cloud_domain_search` | Còn trống + giá theo TLD; **guest gọi được** |
|
|
160
|
+
| `cloud_domain_reserve` | Giữ chỗ 30' không trừ tiền; hỏi email + sđt (+ opt-in ưu đãi); trả QR đúng giá + `claim_url` + `claim_token`/`guest_token` |
|
|
161
|
+
| `cloud_domain_reserve_status` | reserved / paid / claimed / expired + `next_step` |
|
|
162
|
+
| `cloud_domain_claim` | Đăng nhập Pass → nhận reservation, kéo tiền về ví, mua ngay (idempotent) |
|
|
163
|
+
| `cloud_domain_reserve_release` | Nhả chỗ khi chưa có tiền vào |
|
|
164
|
+
| `cloud_domain_registrant_get` / `cloud_domain_registrant_set` | Chủ thể đăng ký (.vn cá nhân cần CCCD, tổ chức cần MST) |
|
|
165
|
+
| `cloud_domain_buy` | Mua thẳng bằng ví khi đã đăng nhập; 402 → `cloud_topup` in QR |
|
|
166
|
+
| `cloud_domain_verify_start` / `cloud_domain_verify_status` / `cloud_domain_wait` | Hồ sơ .vn + chờ active |
|
|
167
|
+
| `cloud_domain_dns_*` / `cloud_domain_ns_set` / `cloud_domain_attach` / `cloud_domain_health` | DNS, NS, gắn app, sức khoẻ |
|
|
168
|
+
|
|
169
|
+
Giữ chỗ chỉ khoá tên trong hệ MONA Cloud (`hold_scope=monacloud`), không giữ ở registry — tên đẹp thì trả sớm. Không gọi registrar tới khi tiền vào và có tài khoản nhận. Prompt `mua-ten-mien-monacloud(keyword?, app_id?)` dẫn trọn luồng.
|
|
170
|
+
|
|
153
171
|
### MONA Mail
|
|
154
172
|
|
|
155
173
|
MONA Mail là dịch vụ gửi email giao dịch cho phần mềm và AI agent của người Việt: một API, trả VND, không cần thẻ, thuộc nhóm MONA Cloud của The MONA Group.
|
package/dist/clients.d.ts
CHANGED
|
@@ -44,6 +44,18 @@ export declare class CloudClients {
|
|
|
44
44
|
headers?: Record<string, string>;
|
|
45
45
|
timeoutMs?: number;
|
|
46
46
|
}): Promise<T>;
|
|
47
|
+
/**
|
|
48
|
+
* Gọi endpoint MONA Cloud mở cho guest (spec monadomain §12: search/reserve/status không cần Pass).
|
|
49
|
+
* Có token trên máy thì vẫn gửi (để backend ghi lead + gắn chủ); chưa login thì đi ẩn danh
|
|
50
|
+
* hoặc dùng guest_token của reservation — KHÔNG ném login_required.
|
|
51
|
+
*/
|
|
52
|
+
vibecloudGuestOk<T = unknown>(path: string, options?: {
|
|
53
|
+
method?: string;
|
|
54
|
+
body?: unknown;
|
|
55
|
+
query?: Record<string, string | number | undefined>;
|
|
56
|
+
headers?: Record<string, string>;
|
|
57
|
+
guestToken?: string;
|
|
58
|
+
}): Promise<T>;
|
|
47
59
|
guardedVibecloud(path: string, options?: {
|
|
48
60
|
method?: string;
|
|
49
61
|
body?: unknown;
|
package/dist/clients.js
CHANGED
|
@@ -222,6 +222,29 @@ export class CloudClients {
|
|
|
222
222
|
fetchImpl: this.fetchImpl,
|
|
223
223
|
});
|
|
224
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Gọi endpoint MONA Cloud mở cho guest (spec monadomain §12: search/reserve/status không cần Pass).
|
|
227
|
+
* Có token trên máy thì vẫn gửi (để backend ghi lead + gắn chủ); chưa login thì đi ẩn danh
|
|
228
|
+
* hoặc dùng guest_token của reservation — KHÔNG ném login_required.
|
|
229
|
+
*/
|
|
230
|
+
async vibecloudGuestOk(path, options = {}) {
|
|
231
|
+
const { guestToken, ...rest } = options;
|
|
232
|
+
let token;
|
|
233
|
+
try {
|
|
234
|
+
token = await this.vibecloudToken();
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
if (!(error instanceof CloudError) || error.code !== 'login_required')
|
|
238
|
+
throw error;
|
|
239
|
+
token = guestToken;
|
|
240
|
+
}
|
|
241
|
+
return requestJson(`${this.config.vibecloudApi}${path}`, {
|
|
242
|
+
timeoutMs: 30_000,
|
|
243
|
+
...rest,
|
|
244
|
+
token,
|
|
245
|
+
fetchImpl: this.fetchImpl,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
225
248
|
async guardedVibecloud(path, options = {}, requestedSandbox = false, requiredVnd) {
|
|
226
249
|
const sandbox = this.sandboxEnabled(requestedSandbox);
|
|
227
250
|
if (!sandbox)
|
package/dist/domains.js
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { runTool } from './errors.js';
|
|
3
|
+
const domainName = z.string().trim().toLowerCase()
|
|
4
|
+
.min(3).max(253)
|
|
5
|
+
.describe('Tên miền đầy đủ (vd: example.vn) hoặc chỉ tên chưa có đuôi (vd: example).');
|
|
6
|
+
const objectId = z.string().regex(/^[a-f0-9]{24}$/i, 'Cần ID 24 ký tự hex.');
|
|
7
|
+
const registrantSchema = z.object({
|
|
8
|
+
owner_type: z.enum(['individual', 'organization']).optional()
|
|
9
|
+
.describe('individual = cá nhân (mặc định), organization = tổ chức/doanh nghiệp.'),
|
|
10
|
+
fullname: z.string().min(2).max(200).describe('Họ và tên đầy đủ (hoặc tên doanh nghiệp nếu organization).'),
|
|
11
|
+
email: z.string().email().describe('Email liên hệ.'),
|
|
12
|
+
phone: z.string().min(8).max(20).describe('Số điện thoại.'),
|
|
13
|
+
address: z.string().min(5).max(500).describe('Địa chỉ liên hệ.'),
|
|
14
|
+
province: z.string().max(100).optional().describe('Tỉnh/thành phố.'),
|
|
15
|
+
ward: z.string().max(100).optional().describe('Phường/xã.'),
|
|
16
|
+
country: z.string().max(10).optional().describe('Mã quốc gia, mặc định VN.'),
|
|
17
|
+
cccd: z.string().min(12).max(12).optional()
|
|
18
|
+
.describe('Số CCCD (12 số) — bắt buộc khi đăng ký tên miền .vn cá nhân.'),
|
|
19
|
+
dob: z.string().max(20).optional().describe('Ngày sinh định dạng DD/MM/YYYY — cá nhân .vn.'),
|
|
20
|
+
gender: z.enum(['male', 'female', '']).optional().describe('Giới tính — cá nhân .vn.'),
|
|
21
|
+
org_name: z.string().max(300).optional().describe('Tên tổ chức/doanh nghiệp — bắt buộc khi organization.'),
|
|
22
|
+
tax_code: z.string().max(20).optional().describe('Mã số thuế — bắt buộc khi organization.'),
|
|
23
|
+
representative: z.string().max(200).optional().describe('Họ tên người đại diện — bắt buộc khi organization.'),
|
|
24
|
+
}).strict();
|
|
25
|
+
export function registerDomainTools(server, clients) {
|
|
26
|
+
server.registerTool('cloud_domain_search', {
|
|
27
|
+
title: 'Tìm kiếm + báo giá tên miền',
|
|
28
|
+
description: 'Kiểm tra tên miền còn trống và xem giá mua (VND, đã VAT). KHÔNG cần đăng nhập MONA Pass — gọi được ngay cả khi người dùng chưa có tài khoản; dùng trước khi mua/giữ chỗ để biết available/price.',
|
|
29
|
+
inputSchema: z.object({
|
|
30
|
+
q: z.string().min(1).max(253).describe('Tên miền hoặc từ khoá (vd: "myapp" hoặc "myapp.vn").'),
|
|
31
|
+
tlds: z.string().max(200).optional().describe('Đuôi muốn tìm, phân cách phẩy (vd: "vn,com"). Mặc định: vn,com.'),
|
|
32
|
+
years: z.number().int().min(1).max(10).optional().describe('Số năm đăng ký. Mặc định 1.'),
|
|
33
|
+
}).strict(),
|
|
34
|
+
}, ({ q, tlds, years }) => runTool(async () => {
|
|
35
|
+
const params = new URLSearchParams({ q });
|
|
36
|
+
if (tlds)
|
|
37
|
+
params.set('tlds', tlds);
|
|
38
|
+
if (years)
|
|
39
|
+
params.set('years', String(years));
|
|
40
|
+
return clients.vibecloudGuestOk(`/api/domains/search?${params}`);
|
|
41
|
+
}));
|
|
42
|
+
server.registerTool('cloud_domain_registrant_get', {
|
|
43
|
+
title: 'Xem thông tin chủ thể đăng ký tên miền',
|
|
44
|
+
description: 'Lấy thông tin chủ thể (registrant) đã lưu. Cần dữ liệu này để mua tên miền.',
|
|
45
|
+
inputSchema: z.object({}).strict(),
|
|
46
|
+
}, () => runTool(() => clients.vibecloud('/api/domains/registrant')));
|
|
47
|
+
server.registerTool('cloud_domain_registrant_set', {
|
|
48
|
+
title: 'Cập nhật thông tin chủ thể đăng ký tên miền',
|
|
49
|
+
description: 'Lưu thông tin chủ thể để đăng ký tên miền. HỎI NGƯỜI DÙNG cung cấp NGAY TRONG PHIÊN: họ tên, email, điện thoại, địa chỉ. Tên miền .vn cá nhân cần thêm CCCD 12 số + ngày sinh (DD/MM/YYYY) + giới tính; .vn tổ chức cần org_name + tax_code (MST) + representative. Lưu 1 lần, tái dùng cho các domain sau. Không tự bịa dữ liệu — thiếu trường nào thì hỏi đúng trường đó.',
|
|
50
|
+
inputSchema: registrantSchema,
|
|
51
|
+
}, (args) => runTool(() => clients.vibecloud('/api/domains/registrant', {
|
|
52
|
+
method: 'PUT',
|
|
53
|
+
body: args,
|
|
54
|
+
})));
|
|
55
|
+
server.registerTool('cloud_domain_buy', {
|
|
56
|
+
title: 'Mua tên miền',
|
|
57
|
+
description: [
|
|
58
|
+
'Mua tên miền và trừ ví VND — làm trọn trong phiên, không bảo người dùng mở web. Trước khi gọi:',
|
|
59
|
+
'1. cloud_domain_search để xem giá + còn trống.',
|
|
60
|
+
'2. Hỏi người dùng xác nhận chính tả tên miền (vd: "Đăng ký example.vn nhé?") → spelling_confirmed. WHOIS không sửa được sau khi mua.',
|
|
61
|
+
'3. Hỏi duyệt chi phí (giá từ search).',
|
|
62
|
+
'4. cloud_domain_registrant_set để lưu thông tin chủ thể (hỏi người dùng ngay trong phiên); .vn cần CCCD/MST.',
|
|
63
|
+
'Khi gọi mà trả 402 insufficient_balance → gọi cloud_topup, IN NGUYÊN KHỐI qr_ascii (QR VietQR) cho người dùng quét bằng app ngân hàng NGAY trong terminal, chờ cloud_topup_status=paid rồi gọi lại cloud_domain_buy. Không bảo người dùng mở console để nạp.',
|
|
64
|
+
'Kết quả có suggested_next → gợi ý người dùng deploy app lên MONA Cloud (cloud_app_create) và gắn domain (cloud_domain_attach).',
|
|
65
|
+
'.vn sau khi mua ở trạng thái pending_verification → dùng cloud_domain_verify_start (link eKYC/bản khai) rồi cloud_domain_wait.',
|
|
66
|
+
'sandbox=true: giả lập 0đ, không mua thật.',
|
|
67
|
+
].join(' '),
|
|
68
|
+
inputSchema: z.object({
|
|
69
|
+
name: domainName,
|
|
70
|
+
years: z.number().int().min(1).max(10).optional().describe('Số năm đăng ký. Mặc định 1.'),
|
|
71
|
+
spelling_confirmed: z.boolean()
|
|
72
|
+
.describe('true = người dùng đã xác nhận chính tả tên miền; false = chặn, không mua.'),
|
|
73
|
+
registrant_id: objectId.optional().describe('ID registrant (lấy từ cloud_domain_registrant_get). Để trống → dùng registrant mặc định.'),
|
|
74
|
+
sandbox: z.boolean().optional().describe('sandbox=true: mô phỏng 0đ, không gọi MONA Host.'),
|
|
75
|
+
}).strict(),
|
|
76
|
+
}, ({ name, years, spelling_confirmed, registrant_id, sandbox }) => runTool(async () => {
|
|
77
|
+
const body = { name, spelling_confirmed };
|
|
78
|
+
if (years)
|
|
79
|
+
body.years = years;
|
|
80
|
+
if (registrant_id)
|
|
81
|
+
body.registrant_id = registrant_id;
|
|
82
|
+
return clients.guardedVibecloud('/api/domains', { method: 'POST', body }, sandbox === true);
|
|
83
|
+
}));
|
|
84
|
+
// --- Guest → reserve → claim (spec monadomain §12): mua TRƯỚC khi có tài khoản ---------------
|
|
85
|
+
server.registerTool('cloud_domain_reserve', {
|
|
86
|
+
title: 'Giữ chỗ tên miền + QR trả tiền (không cần tài khoản)',
|
|
87
|
+
description: [
|
|
88
|
+
'Giữ chỗ tên miền 30 phút cho người dùng CHƯA có MONA Pass (hoặc chưa login trên máy này) — không trừ tiền, không đăng ký thật, chỉ khoá tên trong hệ MONA Cloud.',
|
|
89
|
+
'Dùng khi người dùng muốn mua ngay trong phiên mà không muốn "đi đăng ký" trước: bạn đã dựng app, chọn tên, chỉ cần người bấm.',
|
|
90
|
+
'Bắt buộc hỏi người dùng: tên miền (xác nhận chính tả → spelling_confirmed), email, số điện thoại (kênh nhận link claim + hoá đơn). Hỏi thêm 1 câu nhẹ: "nhận thông báo ưu đãi MONA Cloud không?" → marketing_consent; không tick thì để false.',
|
|
91
|
+
'Nếu người dùng đã đưa đủ thông tin chủ thể (.vn cần CCCD/MST) thì gửi luôn ở registrant — lúc claim khỏi hỏi lại.',
|
|
92
|
+
'Kết quả: payment.qr_url (QR VietQR đúng số tiền) + claim_url + claim_token + guest_token (giữ lại để gọi cloud_domain_reserve_status). Đưa QR cho người dùng quét NGAY; tiền vào → hệ giữ cho reservation này. Sau đó người dùng mở claim_url: đăng nhập/đăng ký MONA Pass (Google/GitHub/email, 1 bước) → hệ tự lấy tiền + mua + tạo ví. Nếu người dùng ĐÃ có Pass trên máy này thì gọi thẳng cloud_domain_claim.',
|
|
93
|
+
'Giữ chỗ chỉ trong hệ MONA Cloud (hold_scope=monacloud) — người ngoài vẫn có thể đăng ký tên đó ở registry khác trong 30 phút; nói rõ điều này nếu tên đẹp.',
|
|
94
|
+
].join(' '),
|
|
95
|
+
inputSchema: z.object({
|
|
96
|
+
name: domainName,
|
|
97
|
+
years: z.number().int().min(1).max(10).optional().describe('Số năm đăng ký. Mặc định 1.'),
|
|
98
|
+
email: z.string().email().describe('Email người dùng — nhận claim_url + hoá đơn.'),
|
|
99
|
+
phone: z.string().min(8).max(20).describe('Số điện thoại người dùng.'),
|
|
100
|
+
spelling_confirmed: z.boolean().describe('true = người dùng đã xác nhận chính tả tên miền.'),
|
|
101
|
+
marketing_consent: z.boolean().optional().describe('true CHỈ KHI người dùng đồng ý nhận thông báo ưu đãi MONA Cloud. Mặc định false.'),
|
|
102
|
+
registrant: registrantSchema.optional().describe('Thông tin chủ thể nếu đã hỏi được đủ trong phiên (tuỳ chọn).'),
|
|
103
|
+
context: z.string().max(500).optional().describe('Ngữ cảnh ngắn (vd: "Cursor dựng shop Next.js") — giúp MONA hỗ trợ đúng.'),
|
|
104
|
+
sandbox: z.boolean().optional().describe('sandbox=true: giả lập, không tạo QR thật.'),
|
|
105
|
+
}).strict(),
|
|
106
|
+
}, ({ sandbox, ...body }) => runTool(() => clients.vibecloudGuestOk('/api/domains/reserve', {
|
|
107
|
+
method: 'POST',
|
|
108
|
+
body,
|
|
109
|
+
...(clients.sandboxEnabled(sandbox === true) ? { headers: { 'X-Vibecloud-Sandbox': '1' } } : {}),
|
|
110
|
+
})));
|
|
111
|
+
server.registerTool('cloud_domain_reserve_status', {
|
|
112
|
+
title: 'Trạng thái giữ chỗ tên miền',
|
|
113
|
+
description: 'Xem reservation đã nhận tiền chưa / đã claim chưa / còn hạn không. Guest truyền claim_token (hoặc guest_token) nhận từ cloud_domain_reserve; chủ reservation đã login thì không cần. Đọc next_step trong kết quả để biết bước kế.',
|
|
114
|
+
inputSchema: z.object({
|
|
115
|
+
reservation_id: objectId.describe('ID reservation từ cloud_domain_reserve.'),
|
|
116
|
+
claim_token: z.string().min(16).max(128).optional().describe('claim_token hoặc guest_token từ cloud_domain_reserve (bắt buộc khi chưa login).'),
|
|
117
|
+
}).strict(),
|
|
118
|
+
}, ({ reservation_id, claim_token }) => runTool(() => clients.vibecloudGuestOk(`/api/domains/reserve/${encodeURIComponent(reservation_id)}`, { query: claim_token ? { t: claim_token } : undefined, guestToken: claim_token })));
|
|
119
|
+
server.registerTool('cloud_domain_claim', {
|
|
120
|
+
title: 'Nhận reservation về tài khoản + mua (claim = đăng ký = trả tiền)',
|
|
121
|
+
description: [
|
|
122
|
+
'Gắn reservation vào MONA Pass đang đăng nhập, kéo tiền đã chuyển (nếu có) về ví rồi MUA ngay. Cần đăng nhập (monacloud-mcp login) — lần đầu đăng nhập MONA Cloud tự tạo hồ sơ + ví, không có form đăng ký riêng.',
|
|
123
|
+
'Idempotent: gọi lại khi trả 402 (ví chưa đủ → cloud_topup hoặc chờ tiền QR vào) hoặc 422 registrant_required (→ cloud_domain_registrant_set rồi claim lại; tiền vẫn nằm trong ví).',
|
|
124
|
+
'Kết quả như cloud_domain_buy (order id, status, suggested_next).',
|
|
125
|
+
].join(' '),
|
|
126
|
+
inputSchema: z.object({
|
|
127
|
+
reservation_id: objectId.describe('ID reservation từ cloud_domain_reserve.'),
|
|
128
|
+
claim_token: z.string().min(16).max(128).describe('claim_token từ cloud_domain_reserve (hoặc tham số t trong claim_url).'),
|
|
129
|
+
registrant_id: objectId.optional().describe('ID registrant muốn dùng (mặc định: registrant của user hoặc từ reservation).'),
|
|
130
|
+
marketing_consent: z.boolean().optional().describe('true nếu người dùng đồng ý nhận ưu đãi lúc này.'),
|
|
131
|
+
}).strict(),
|
|
132
|
+
}, ({ reservation_id, ...body }) => runTool(() => clients.vibecloud(`/api/domains/reserve/${encodeURIComponent(reservation_id)}/claim`, { method: 'POST', body })));
|
|
133
|
+
server.registerTool('cloud_domain_reserve_release', {
|
|
134
|
+
title: 'Nhả chỗ tên miền đã giữ',
|
|
135
|
+
description: 'Huỷ reservation chưa nhận tiền (người dùng đổi ý / chọn tên khác). Đã có tiền vào thì không huỷ được — dùng cloud_domain_claim.',
|
|
136
|
+
inputSchema: z.object({
|
|
137
|
+
reservation_id: objectId,
|
|
138
|
+
claim_token: z.string().min(16).max(128).optional().describe('Bắt buộc khi chưa login.'),
|
|
139
|
+
}).strict(),
|
|
140
|
+
}, ({ reservation_id, claim_token }) => runTool(() => clients.vibecloudGuestOk(`/api/domains/reserve/${encodeURIComponent(reservation_id)}`, { method: 'DELETE', query: claim_token ? { t: claim_token } : undefined, guestToken: claim_token })));
|
|
141
|
+
server.registerTool('cloud_domain_list', {
|
|
142
|
+
title: 'Danh sách tên miền đã import/mua',
|
|
143
|
+
description: 'Liệt kê tên miền đã đưa vào MONA Cloud (import + mua). sandbox=true → xem domain sandbox.',
|
|
144
|
+
inputSchema: z.object({
|
|
145
|
+
sandbox: z.boolean().optional().describe('true → hiện domain sandbox.'),
|
|
146
|
+
}).strict(),
|
|
147
|
+
}, ({ sandbox }) => runTool(async () => {
|
|
148
|
+
const headers = sandbox ? { 'X-Vibecloud-Sandbox': '1' } : undefined;
|
|
149
|
+
return clients.vibecloud('/api/domains', { headers });
|
|
150
|
+
}));
|
|
151
|
+
server.registerTool('cloud_domain_verify_start', {
|
|
152
|
+
title: 'Bắt đầu nộp hồ sơ .vn',
|
|
153
|
+
description: 'Sinh bản khai đã điền sẵn và link upload hồ sơ .vn. Dùng sau khi mua tên miền .vn (status=pending_verification).',
|
|
154
|
+
inputSchema: z.object({
|
|
155
|
+
id: objectId.describe('Order ID trả về khi cloud_domain_buy.'),
|
|
156
|
+
}).strict(),
|
|
157
|
+
}, ({ id }) => runTool(() => clients.vibecloud(`/api/domains/${encodeURIComponent(id)}/verify`, {
|
|
158
|
+
method: 'POST',
|
|
159
|
+
})));
|
|
160
|
+
server.registerTool('cloud_domain_verify_status', {
|
|
161
|
+
title: 'Trạng thái hồ sơ .vn',
|
|
162
|
+
description: 'Kiểm tra trạng thái duyệt hồ sơ đăng ký .vn (profile_status từ MONA Host).',
|
|
163
|
+
inputSchema: z.object({
|
|
164
|
+
id: objectId.describe('Order ID của tên miền .vn.'),
|
|
165
|
+
}).strict(),
|
|
166
|
+
}, ({ id }) => runTool(() => clients.vibecloud(`/api/domains/${encodeURIComponent(id)}/verify`)));
|
|
167
|
+
server.registerTool('cloud_domain_health', {
|
|
168
|
+
title: 'Kiểm tra sức khoẻ tên miền',
|
|
169
|
+
description: 'Kiểm tra hạn đăng ký, trạng thái hồ sơ, NS, SSL và cảnh báo tên miền đã mua.',
|
|
170
|
+
inputSchema: z.object({
|
|
171
|
+
id: objectId.describe('Order ID tên miền cần kiểm tra.'),
|
|
172
|
+
}).strict(),
|
|
173
|
+
}, ({ id }) => runTool(() => clients.vibecloud(`/api/domains/${encodeURIComponent(id)}/health`)));
|
|
174
|
+
server.registerTool('cloud_domain_wait', {
|
|
175
|
+
title: 'Chờ tên miền active',
|
|
176
|
+
description: 'Long-poll cho đến khi tên miền chuyển sang active/failed (mặc định timeout=60s). Dùng sau cloud_domain_buy để chờ MONA Host xử lý.',
|
|
177
|
+
inputSchema: z.object({
|
|
178
|
+
id: objectId.describe('Order ID cần chờ.'),
|
|
179
|
+
timeout: z.number().int().min(5).max(300).optional().describe('Timeout giây. Mặc định 60.'),
|
|
180
|
+
}).strict(),
|
|
181
|
+
}, ({ id, timeout }) => runTool(async () => {
|
|
182
|
+
const params = timeout ? `?timeout=${timeout}` : '';
|
|
183
|
+
return clients.vibecloud(`/api/domains/${encodeURIComponent(id)}/wait${params}`);
|
|
184
|
+
}));
|
|
185
|
+
server.registerTool('cloud_domain_webhook_set', {
|
|
186
|
+
title: 'Đăng ký webhook tên miền',
|
|
187
|
+
description: 'Đăng ký URL nhận sự kiện domain.status_changed (ký HMAC). Mỗi user 1 webhook; gọi lại để cập nhật.',
|
|
188
|
+
inputSchema: z.object({
|
|
189
|
+
url: z.string().url().refine((v) => v.startsWith('https://'), 'Cần URL HTTPS.'),
|
|
190
|
+
secret: z.string().min(8).max(256).describe('Chuỗi bí mật để xác minh chữ ký HMAC.'),
|
|
191
|
+
}).strict(),
|
|
192
|
+
}, ({ url, secret }) => runTool(() => clients.vibecloud('/api/domains/webhook', {
|
|
193
|
+
method: 'PUT',
|
|
194
|
+
body: { url, secret },
|
|
195
|
+
})));
|
|
196
|
+
server.registerTool('cloud_domain_attach', {
|
|
197
|
+
title: 'Gắn tên miền vào ứng dụng',
|
|
198
|
+
description: [
|
|
199
|
+
'Gắn tên miền vào app đã deploy. Tên miền .vn chưa active vẫn gọi được — attach đặt trước, server tự hoàn tất sau khi hồ sơ được duyệt và gửi webhook domain.status_changed.',
|
|
200
|
+
'Dùng cloud_app_list để lấy app_id; domain_id là ID của domain row (từ cloud_domain_list).',
|
|
201
|
+
].join(' '),
|
|
202
|
+
inputSchema: z.object({
|
|
203
|
+
domain_id: objectId.describe('Domain ID (từ cloud_domain_list).'),
|
|
204
|
+
app_id: objectId.describe('App ID cần gắn vào.'),
|
|
205
|
+
sandbox: z.boolean().optional(),
|
|
206
|
+
}).strict(),
|
|
207
|
+
}, ({ domain_id, app_id, sandbox }) => runTool(async () => {
|
|
208
|
+
const headers = sandbox ? { 'X-Vibecloud-Sandbox': '1' } : undefined;
|
|
209
|
+
return clients.vibecloud(`/api/domains/${encodeURIComponent(domain_id)}/attach`, {
|
|
210
|
+
method: 'POST',
|
|
211
|
+
body: { app_id },
|
|
212
|
+
headers,
|
|
213
|
+
});
|
|
214
|
+
}));
|
|
215
|
+
const recordSchema = {
|
|
216
|
+
type: z.enum(['A', 'AAAA', 'CNAME', 'MX', 'TXT', 'SRV', 'NS']).describe('Loại bản ghi.'),
|
|
217
|
+
name: z.string().min(1).max(253).describe('Tên bản ghi ("@" cho gốc, "www", "api"...).'),
|
|
218
|
+
data: z.string().min(1).max(2048).describe('Giá trị (IP cho A, hostname cho CNAME, nội dung cho TXT...).'),
|
|
219
|
+
ttl: z.number().int().min(60).max(86400).optional().describe('TTL giây (mặc định để trống).'),
|
|
220
|
+
priority: z.number().int().min(0).max(65535).optional().describe('Ưu tiên (MX/SRV).'),
|
|
221
|
+
};
|
|
222
|
+
server.registerTool('cloud_domain_dns_list', {
|
|
223
|
+
title: 'Xem bản ghi DNS của tên miền',
|
|
224
|
+
description: 'Liệt kê bản ghi DNS (A/CNAME/MX/TXT...) của tên miền đăng ký tại MONA Cloud. domain_id lấy từ cloud_domain_list.',
|
|
225
|
+
inputSchema: z.object({ domain_id: objectId }).strict(),
|
|
226
|
+
}, ({ domain_id }) => runTool(() => clients.vibecloud(`/api/domains/${encodeURIComponent(domain_id)}/records`)));
|
|
227
|
+
server.registerTool('cloud_domain_dns_add', {
|
|
228
|
+
title: 'Thêm bản ghi DNS',
|
|
229
|
+
description: 'Thêm 1 bản ghi DNS. Vd trỏ web: type=A, name=@, data=<IP>. Trỏ www: type=CNAME, name=www, data=<domain>. AI làm trọn, không cần mở dashboard.',
|
|
230
|
+
inputSchema: z.object({ domain_id: objectId, ...recordSchema }).strict(),
|
|
231
|
+
}, ({ domain_id, ...record }) => runTool(() => clients.vibecloud(`/api/domains/${encodeURIComponent(domain_id)}/records`, { method: 'POST', body: record })));
|
|
232
|
+
server.registerTool('cloud_domain_dns_update', {
|
|
233
|
+
title: 'Sửa bản ghi DNS',
|
|
234
|
+
description: 'Sửa 1 bản ghi DNS theo record_id (lấy từ cloud_domain_dns_list).',
|
|
235
|
+
inputSchema: z.object({ domain_id: objectId, record_id: z.string().min(1).max(128), ...recordSchema }).strict(),
|
|
236
|
+
}, ({ domain_id, record_id, ...record }) => runTool(() => clients.vibecloud(`/api/domains/${encodeURIComponent(domain_id)}/records/${encodeURIComponent(record_id)}`, { method: 'PUT', body: record })));
|
|
237
|
+
server.registerTool('cloud_domain_dns_delete', {
|
|
238
|
+
title: 'Xoá bản ghi DNS',
|
|
239
|
+
description: 'Xoá 1 bản ghi DNS theo record_id.',
|
|
240
|
+
inputSchema: z.object({ domain_id: objectId, record_id: z.string().min(1).max(128) }).strict(),
|
|
241
|
+
}, ({ domain_id, record_id }) => runTool(() => clients.vibecloud(`/api/domains/${encodeURIComponent(domain_id)}/records/${encodeURIComponent(record_id)}`, { method: 'DELETE' })));
|
|
242
|
+
server.registerTool('cloud_domain_ns_set', {
|
|
243
|
+
title: 'Đổi nameserver (NS) của tên miền',
|
|
244
|
+
description: 'Đổi NS cho tên miền (≥2). Vd giữ DNS ở MONA: ns1.mona.host, ns2.mona.host. Hoặc chuyển sang Cloudflare/nhà khác. AI làm hoàn toàn.',
|
|
245
|
+
inputSchema: z.object({
|
|
246
|
+
domain_id: objectId,
|
|
247
|
+
ns_list: z.array(z.string().min(3).max(253)).min(2).max(6).describe('Danh sách hostname NS, tối thiểu 2.'),
|
|
248
|
+
}).strict(),
|
|
249
|
+
}, ({ domain_id, ns_list }) => runTool(() => clients.vibecloud(`/api/domains/${encodeURIComponent(domain_id)}/ns`, { method: 'PATCH', body: { ns_list } })));
|
|
250
|
+
server.registerPrompt('mua-ten-mien-monacloud', {
|
|
251
|
+
title: 'Mua tên miền cho app — làm trọn trong phiên (gợi ý, hỏi info, bắn QR, mua)',
|
|
252
|
+
description: 'AI tự tra + báo giá + hỏi thông tin chủ thể + nạp ví bằng QR trong terminal + mua + xác thực .vn, không bảo người dùng mở web.',
|
|
253
|
+
argsSchema: {
|
|
254
|
+
keyword: z.string().optional(),
|
|
255
|
+
app_id: z.string().optional(),
|
|
256
|
+
},
|
|
257
|
+
}, ({ keyword, app_id }) => ({ messages: [{
|
|
258
|
+
role: 'user',
|
|
259
|
+
content: {
|
|
260
|
+
type: 'text',
|
|
261
|
+
text: `Mua giúp tôi tên miền${keyword ? ` quanh "${keyword}"` : ''} bằng MONA Cloud, làm trọn ngay trong phiên này, đừng bảo tôi mở web:
|
|
262
|
+
1. cloud_domain_search để tìm tên còn trống + báo giá VND (ưu tiên .vn và .com; nói rõ giá đã gồm VAT).
|
|
263
|
+
2. Hỏi tôi chọn tên nào và XÁC NHẬN CHÍNH TẢ (WHOIS không sửa được sau khi mua).
|
|
264
|
+
3. cloud_domain_registrant_set: HỎI TÔI thông tin chủ thể ngay trong phiên — họ tên, email, điện thoại, địa chỉ; nếu là .vn cá nhân hỏi thêm CCCD 12 số + ngày sinh + giới tính (tổ chức: tên công ty + MST + người đại diện). Đừng tự bịa.
|
|
265
|
+
4. Kiểm ví bằng cloud_balance. Nếu thiếu tiền, gọi cloud_topup rồi IN NGUYÊN KHỐI qr_ascii (QR VietQR) cho tôi quét bằng app ngân hàng ngay trong terminal; chờ cloud_topup_status=paid.
|
|
266
|
+
5. cloud_domain_buy (spelling_confirmed=true) — trừ ví, mua thật.
|
|
267
|
+
⚠️ Nếu tôi CHƯA có tài khoản MONA Pass / tool trả login_required: KHÔNG bảo tôi đi đăng ký trước. Dùng cloud_domain_reserve (hỏi email + sđt, hỏi 1 câu "nhận ưu đãi MONA Cloud không?") → đưa tôi QR trong payment để quét trả tiền ngay + claim_url để tôi bấm đăng nhập 1 bước (Google/GitHub/email) → hệ tự tạo ví + mua. Theo dõi bằng cloud_domain_reserve_status; nếu tôi đã login trên máy này thì gọi cloud_domain_claim.
|
|
268
|
+
6. Nếu là .vn: cloud_domain_verify_start đưa tôi link eKYC/bản khai (chụp CCCD + chân dung / ký), rồi cloud_domain_wait tới khi active.
|
|
269
|
+
7. ${app_id ? `Gắn vào app ${app_id} bằng cloud_domain_attach` : 'Gợi ý tôi deploy app lên MONA Cloud (cloud_app_create) rồi gắn domain bằng cloud_domain_attach'} — trỏ DNS + SSL tự động.
|
|
270
|
+
Trả VND, không cần thẻ quốc tế, không mở dashboard.`,
|
|
271
|
+
},
|
|
272
|
+
}] }));
|
|
273
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -11,6 +11,7 @@ import { registerMailTools } from './mail.js';
|
|
|
11
11
|
import { APP_FLOW, createVps, registerComputeTools } from './compute.js';
|
|
12
12
|
import { pollJob } from './jobs.js';
|
|
13
13
|
import { registerBaseTools } from './base.js';
|
|
14
|
+
import { registerDomainTools } from './domains.js';
|
|
14
15
|
import { presentTopup } from './qr.js';
|
|
15
16
|
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. Base beta thay Supabase bằng cloud_base_*, chung account/ví MONA Cloud và khớp app deploy. Dùng mail_* để gửi email giao dịch (MONA Mail). MONA Mail là dịch vụ gửi email giao dịch cho phần mềm và AI agent của người Việt: một API, trả VND, không cần thẻ, thuộc nhóm MONA Cloud của The MONA Group.';
|
|
16
17
|
const SANDBOX_DESCRIPTION = 'sandbox=true: thử 0đ, không cần ví';
|
|
@@ -317,6 +318,7 @@ export function createServer(dependencies = {}) {
|
|
|
317
318
|
registerComputeTools(server, clients);
|
|
318
319
|
registerBaseTools(server, clients);
|
|
319
320
|
registerMailTools(server, clients, config);
|
|
321
|
+
registerDomainTools(server, clients);
|
|
320
322
|
server.registerTool('agent_templates_list', {
|
|
321
323
|
title: 'Catalog MONA Agent',
|
|
322
324
|
description: 'Đọc template từ thư mục local, URL catalog hoặc catalog wave 1 tích hợp. Deploy dự án: cloud_app_detect rồi cloud_app_create(local_dir), sandbox trước nếu chưa có host.',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "monacloud-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "MCP server for MONA Cloud — deploy apps (Vercel alternative), Supabase-compatible Postgres/Auth/Storage bases (Supabase alternative), VND wallet, MONA Pay & Mail — for AI agents (Claude, Cursor, Codex) in Vietnam.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://monacloud.vn",
|