monacloud-mcp 0.10.10 → 0.10.12

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/dist/base.js CHANGED
@@ -2,6 +2,7 @@ import { z } from 'zod';
2
2
  import { CloudError, runTool } from './errors.js';
3
3
  import { pollJob, TERMINAL_JOBS } from './jobs.js';
4
4
  import { unwrapData } from './http.js';
5
+ import { completeToolMetadata } from './monapay.js';
5
6
  const object = (value) => value && typeof value === 'object' ? value : {};
6
7
  const id = z.string().trim().min(1).max(255);
7
8
  const sandbox = z.boolean().optional().describe('Thử 0đ, chỉ ước tính và không tạo hạ tầng thật / Sandbox estimate only');
@@ -45,7 +46,10 @@ async function createBase(clients, body) {
45
46
  export function registerBaseTools(server, clients) {
46
47
  const register = (name, description, schema, handler) => {
47
48
  for (const toolName of [name, name.replace(/^cloud_/, 'vibecloud_')]) {
49
+ const metadata = completeToolMetadata(toolName, undefined, undefined);
48
50
  server.registerTool(toolName, {
51
+ title: metadata.title,
52
+ annotations: metadata.annotations,
49
53
  description: toolName === name ? `${description} ${baseDescription}` : `Alias tương thích của ${name}. / Compatibility alias. ${description} ${baseDescription}`,
50
54
  inputSchema: schema,
51
55
  }, (args) => runTool(() => handler(args)));
package/dist/compute.js CHANGED
@@ -4,6 +4,7 @@ import { unwrapData } from './http.js';
4
4
  import { finishAppJob } from './jobs.js';
5
5
  import { detectProject } from './local.js';
6
6
  import { localAppTools } from './local-app.js';
7
+ import { completeToolMetadata } from './monapay.js';
7
8
  export const APP_FLOW = 'AI làm 99%: cloud_app_detect(local_dir) offline → cloud_app_host_list, cloud_prices/cloud_packages/cloud_plan_list và cloud_balance → cloud_app_create(local_dir, sandbox=true) nếu chưa có app host để ước tính chi phí giờ/gói → hỏi human một lần duyệt chi phí nếu chưa được duyệt → cloud_app_create(local_dir, sandbox=false) → poll tới done/succeeded → kiểm và trả URL. Cần DB/Supabase? cloud_base_create (beta) — chung account/ví MONA Cloud, khớp app deploy. Sau khi app có URL, CHỦ ĐỘNG hỏi tên miền riêng: human đã có domain → cloud_app_domain_add (CNAME); chưa có → cloud_domain_search gợi 3–5 đuôi (.vn trước nếu khách Việt) kèm giá VND, hỏi xác nhận chính tả + duyệt tiền → cloud_domain_buy (đã login) hoặc cloud_domain_reserve (chưa login: QR + claim_url) → cloud_domain_attach trỏ DNS + SSL. Human đăng ký MONA Pass bằng device flow một lần; hết credit 20k thì AI gọi cloud_topup và in QR (qr_ascii) ngay trong terminal, human chỉ quét bằng app ngân hàng; AI làm các bước còn lại. App git cloud_app_create(repo_url) đã live; không gọi agent_deploy cho deploy dự án. / Detect locally, estimate, obtain cost approval once, upload and deploy, create an optional Base, return URL, then attach an optional domain.';
8
9
  const sandbox = z.boolean().optional().describe('Thử 0đ, không tạo hạ tầng thật / Sandbox, no charge');
9
10
  const id = z.string().trim().min(1).max(255);
@@ -49,7 +50,10 @@ export function registerComputeTools(server, clients) {
49
50
  // Every new compute tool keeps a mechanical legacy alias, using the same schema and handler.
50
51
  const register = (name, description, schema, handler) => {
51
52
  for (const toolName of [name, name.replace(/^cloud_/, 'vibecloud_')]) {
53
+ const metadata = completeToolMetadata(toolName, undefined, undefined);
52
54
  server.registerTool(toolName, {
55
+ title: metadata.title,
56
+ annotations: metadata.annotations,
53
57
  description: toolName === name ? description : `Alias tương thích của ${name}. / Compatibility alias. ${description}`,
54
58
  inputSchema: schema,
55
59
  }, (args) => runTool(() => handler(args)));
package/dist/domains.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { runTool } from './errors.js';
3
+ import { toolAnnotationsForName } from './monapay.js';
3
4
  const domainName = z.string().trim().toLowerCase()
4
5
  .min(3).max(253)
5
6
  .describe('Tên miền đầy đủ (vd: example.vn) hoặc chỉ tên chưa có đuôi (vd: example).');
@@ -24,6 +25,7 @@ const registrantSchema = z.object({
24
25
  }).strict();
25
26
  export function registerDomainTools(server, clients) {
26
27
  server.registerTool('cloud_domain_search', {
28
+ annotations: toolAnnotationsForName('cloud_domain_search'),
27
29
  title: 'Tìm kiếm + báo giá tên miền',
28
30
  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
31
  inputSchema: z.object({
@@ -48,12 +50,79 @@ export function registerDomainTools(server, clients) {
48
50
  }
49
51
  return results;
50
52
  }));
53
+ server.registerTool('cloud_domain_tld_groups', {
54
+ annotations: toolAnnotationsForName('cloud_domain_tld_groups'),
55
+ title: 'Danh mục đuôi tên miền theo ngành',
56
+ description: 'Liệt kê các nhóm ngành của đuôi tên miền (Ăn uống, Công nghệ & AI, Quốc gia, Việt Nam...) kèm số lượng đuôi và khoảng giá VND mỗi nhóm. Gọi tool này TRƯỚC cloud_domain_tlds để biết slug ngành cần lọc. Không cần đăng nhập.',
57
+ inputSchema: z.object({}).strict(),
58
+ }, () => runTool(() => clients.vibecloudGuestOk('/api/domains/tlds/groups')));
59
+ server.registerTool('cloud_domain_tlds', {
60
+ annotations: toolAnnotationsForName('cloud_domain_tlds'),
61
+ title: 'Bảng giá đuôi tên miền',
62
+ description: 'Bảng giá đầy đủ các đuôi tên miền MONA Domain bán được, giá VND đã gồm VAT — giá năm đầu (price_vnd, đã tính khuyến mãi nếu registry đang chạy) và giá gia hạn mỗi năm (renew_vnd). Lọc theo ngành (group, lấy slug từ cloud_domain_tld_groups), theo từ khoá (q), hoặc theo ngân sách (max_price_vnd). Dùng khi người dùng hỏi "có những đuôi nào", "đuôi nào rẻ", "đuôi nào hợp ngành X". Đuôi .vn có needs_verification=true nghĩa là cần hồ sơ xác thực chủ thể. Không cần đăng nhập.',
63
+ inputSchema: z.object({
64
+ scope: z.enum(['popular', 'all']).optional()
65
+ .describe('popular = nhóm đuôi thông dụng (mặc định), all = toàn bộ danh sách.'),
66
+ group: z.string().max(40).optional()
67
+ .describe('Slug ngành từ cloud_domain_tld_groups (vd: "an-uong", "cong-nghe", "viet-nam").'),
68
+ q: z.string().max(40).optional().describe('Tìm trong tên đuôi và nhãn tiếng Việt (vd: "shop").'),
69
+ max_price_vnd: z.number().int().min(0).optional().describe('Chỉ lấy đuôi có giá năm đầu không quá số này.'),
70
+ sort: z.enum(['popular', 'price', 'tld']).optional().describe('Thứ tự sắp xếp. Mặc định popular.'),
71
+ limit: z.number().int().min(1).max(1000).optional().describe('Số dòng tối đa. Mặc định 200.'),
72
+ offset: z.number().int().min(0).optional().describe('Bỏ qua bao nhiêu dòng đầu, để lật trang.'),
73
+ }).strict(),
74
+ }, ({ scope, group, q, max_price_vnd, sort, limit, offset }) => runTool(() => {
75
+ const params = new URLSearchParams();
76
+ if (scope)
77
+ params.set('scope', scope);
78
+ if (group)
79
+ params.set('group', group);
80
+ if (q)
81
+ params.set('q', q);
82
+ if (max_price_vnd !== undefined)
83
+ params.set('max_price_vnd', String(max_price_vnd));
84
+ if (sort)
85
+ params.set('sort', sort);
86
+ if (limit !== undefined)
87
+ params.set('limit', String(limit));
88
+ if (offset !== undefined)
89
+ params.set('offset', String(offset));
90
+ const query = params.toString();
91
+ return clients.vibecloudGuestOk(`/api/domains/tlds${query ? `?${query}` : ''}`);
92
+ }));
93
+ server.registerTool('cloud_domain_suggest', {
94
+ annotations: toolAnnotationsForName('cloud_domain_suggest'),
95
+ title: 'Gợi ý tên miền còn trống',
96
+ description: 'Gợi ý những tên miền còn trống quanh một từ khoá, kèm giá VND đã gồm VAT. Từ khoá nhận tiếng Việt có dấu, tool tự bỏ dấu thành nhãn hợp lệ. Truyền industry (slug từ cloud_domain_tld_groups) để ưu tiên đuôi hợp ngành — vd quán cà phê sẽ ra .cafe và .coffee trước. Mọi kết quả đều đã tra trạng thái thật tại nhà đăng ký. Dùng khi tên người dùng muốn đã có người lấy, hoặc khi họ chưa nghĩ ra tên. Sau khi người dùng chọn thì gọi cloud_domain_buy (hoặc cloud_domain_reserve nếu chưa đăng nhập). Không cần đăng nhập.',
97
+ inputSchema: z.object({
98
+ keyword: z.string().min(1).max(63)
99
+ .describe('Tên muốn đặt hoặc từ khoá mô tả sản phẩm (vd: "Cà Phê Sạch", "myapp").'),
100
+ industry: z.string().max(40).optional()
101
+ .describe('Slug ngành từ cloud_domain_tld_groups để ưu tiên đuôi hợp ngành.'),
102
+ limit: z.number().int().min(1).max(30).optional().describe('Số gợi ý trả về. Mặc định 12.'),
103
+ max_price_vnd: z.number().int().min(0).optional().describe('Chặn trần giá năm đầu.'),
104
+ include_taken: z.boolean().optional().describe('true = giữ lại cả tên đã có người đăng ký.'),
105
+ }).strict(),
106
+ }, ({ keyword, industry, limit, max_price_vnd, include_taken }) => runTool(() => {
107
+ const params = new URLSearchParams({ keyword });
108
+ if (industry)
109
+ params.set('industry', industry);
110
+ if (limit !== undefined)
111
+ params.set('limit', String(limit));
112
+ if (max_price_vnd !== undefined)
113
+ params.set('max_price_vnd', String(max_price_vnd));
114
+ if (include_taken)
115
+ params.set('include_taken', 'true');
116
+ return clients.vibecloudGuestOk(`/api/domains/tlds/suggest?${params}`);
117
+ }));
51
118
  server.registerTool('cloud_domain_registrant_get', {
119
+ annotations: toolAnnotationsForName('cloud_domain_registrant_get'),
52
120
  title: 'Xem thông tin chủ thể đăng ký tên miền',
53
121
  description: 'Lấy thông tin chủ thể (registrant) đã lưu. Cần dữ liệu này để mua tên miền.',
54
122
  inputSchema: z.object({}).strict(),
55
123
  }, () => runTool(() => clients.vibecloud('/api/domains/registrant')));
56
124
  server.registerTool('cloud_domain_registrant_set', {
125
+ annotations: toolAnnotationsForName('cloud_domain_registrant_set'),
57
126
  title: 'Cập nhật thông tin chủ thể đăng ký tên miền',
58
127
  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 đó.',
59
128
  inputSchema: registrantSchema,
@@ -62,6 +131,7 @@ export function registerDomainTools(server, clients) {
62
131
  body: args,
63
132
  })));
64
133
  server.registerTool('cloud_domain_buy', {
134
+ annotations: toolAnnotationsForName('cloud_domain_buy'),
65
135
  title: 'Mua tên miền',
66
136
  description: [
67
137
  'Mua tên miền và trừ ví VND — làm trọn trong phiên; người dùng chỉ quét QR và xác nhận, không phải đăng ký trước hay tự làm trên web. Trước khi gọi:',
@@ -93,6 +163,7 @@ export function registerDomainTools(server, clients) {
93
163
  }));
94
164
  // --- Guest → reserve → claim (spec monadomain §12): mua TRƯỚC khi có tài khoản ---------------
95
165
  server.registerTool('cloud_domain_reserve', {
166
+ annotations: toolAnnotationsForName('cloud_domain_reserve'),
96
167
  title: 'Giữ chỗ tên miền + QR trả tiền (không cần tài khoản)',
97
168
  description: [
98
169
  '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.',
@@ -119,6 +190,7 @@ export function registerDomainTools(server, clients) {
119
190
  ...(clients.sandboxEnabled(sandbox === true) ? { headers: { 'X-Vibecloud-Sandbox': '1' } } : {}),
120
191
  })));
121
192
  server.registerTool('cloud_domain_reserve_status', {
193
+ annotations: toolAnnotationsForName('cloud_domain_reserve_status'),
122
194
  title: 'Trạng thái giữ chỗ tên miền',
123
195
  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ế.',
124
196
  inputSchema: z.object({
@@ -127,6 +199,7 @@ export function registerDomainTools(server, clients) {
127
199
  }).strict(),
128
200
  }, ({ reservation_id, claim_token }) => runTool(() => clients.vibecloudGuestOk(`/api/domains/reserve/${encodeURIComponent(reservation_id)}`, { query: claim_token ? { t: claim_token } : undefined, guestToken: claim_token })));
129
201
  server.registerTool('cloud_domain_claim', {
202
+ annotations: toolAnnotationsForName('cloud_domain_claim'),
130
203
  title: 'Nhận reservation về tài khoản + mua (claim = đăng ký = trả tiền)',
131
204
  description: [
132
205
  '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.',
@@ -141,6 +214,7 @@ export function registerDomainTools(server, clients) {
141
214
  }).strict(),
142
215
  }, ({ reservation_id, ...body }) => runTool(() => clients.vibecloud(`/api/domains/reserve/${encodeURIComponent(reservation_id)}/claim`, { method: 'POST', body })));
143
216
  server.registerTool('cloud_domain_reserve_release', {
217
+ annotations: toolAnnotationsForName('cloud_domain_reserve_release'),
144
218
  title: 'Nhả chỗ tên miền đã giữ',
145
219
  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.',
146
220
  inputSchema: z.object({
@@ -149,6 +223,7 @@ export function registerDomainTools(server, clients) {
149
223
  }).strict(),
150
224
  }, ({ 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 })));
151
225
  server.registerTool('cloud_domain_list', {
226
+ annotations: toolAnnotationsForName('cloud_domain_list'),
152
227
  title: 'Danh sách tên miền đã import/mua',
153
228
  description: 'Liệt kê tên miền đã đưa vào MONA Cloud (import + mua). sandbox=true → xem domain sandbox.',
154
229
  inputSchema: z.object({
@@ -159,6 +234,7 @@ export function registerDomainTools(server, clients) {
159
234
  return clients.vibecloud('/api/domains', { headers });
160
235
  }));
161
236
  server.registerTool('cloud_domain_verify_start', {
237
+ annotations: toolAnnotationsForName('cloud_domain_verify_start'),
162
238
  title: 'Bắt đầu nộp hồ sơ .vn',
163
239
  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).',
164
240
  inputSchema: z.object({
@@ -168,6 +244,7 @@ export function registerDomainTools(server, clients) {
168
244
  method: 'POST',
169
245
  })));
170
246
  server.registerTool('cloud_domain_verify_status', {
247
+ annotations: toolAnnotationsForName('cloud_domain_verify_status'),
171
248
  title: 'Trạng thái hồ sơ .vn',
172
249
  description: 'Kiểm tra trạng thái duyệt hồ sơ đăng ký .vn (profile_status từ MONA Host).',
173
250
  inputSchema: z.object({
@@ -175,6 +252,7 @@ export function registerDomainTools(server, clients) {
175
252
  }).strict(),
176
253
  }, ({ id }) => runTool(() => clients.vibecloud(`/api/domains/${encodeURIComponent(id)}/verify`)));
177
254
  server.registerTool('cloud_domain_health', {
255
+ annotations: toolAnnotationsForName('cloud_domain_health'),
178
256
  title: 'Kiểm tra sức khoẻ tên miền',
179
257
  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.',
180
258
  inputSchema: z.object({
@@ -182,6 +260,7 @@ export function registerDomainTools(server, clients) {
182
260
  }).strict(),
183
261
  }, ({ id }) => runTool(() => clients.vibecloud(`/api/domains/${encodeURIComponent(id)}/health`)));
184
262
  server.registerTool('cloud_domain_renew', {
263
+ annotations: toolAnnotationsForName('cloud_domain_renew'),
185
264
  title: 'Gia hạn tên miền (trừ ví VND)',
186
265
  description: [
187
266
  'Gia hạn tên miền đã mua qua MONA Cloud. LUÔN gọi dry_run=true trước để lấy price_vnd (đã VAT), HỎI người dùng duyệt số tiền, rồi gọi lại dry_run=false — lúc đó trừ ví VND và gia hạn thật ở registrar.',
@@ -194,6 +273,7 @@ export function registerDomainTools(server, clients) {
194
273
  }).strict(),
195
274
  }, ({ id, billing_cycle, dry_run }) => runTool(() => clients.guardedVibecloud(`/api/domains/${encodeURIComponent(id)}/renew`, { method: 'POST', body: { billing_cycle, dry_run: dry_run !== false } }, false)));
196
275
  server.registerTool('cloud_domain_wait', {
276
+ annotations: toolAnnotationsForName('cloud_domain_wait'),
197
277
  title: 'Chờ tên miền active',
198
278
  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ý.',
199
279
  inputSchema: z.object({
@@ -205,6 +285,7 @@ export function registerDomainTools(server, clients) {
205
285
  return clients.vibecloud(`/api/domains/${encodeURIComponent(id)}/wait${params}`);
206
286
  }));
207
287
  server.registerTool('cloud_domain_webhook_set', {
288
+ annotations: toolAnnotationsForName('cloud_domain_webhook_set'),
208
289
  title: 'Đăng ký webhook tên miền',
209
290
  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.',
210
291
  inputSchema: z.object({
@@ -216,6 +297,7 @@ export function registerDomainTools(server, clients) {
216
297
  body: { url, secret },
217
298
  })));
218
299
  server.registerTool('cloud_domain_attach', {
300
+ annotations: toolAnnotationsForName('cloud_domain_attach'),
219
301
  title: 'Gắn tên miền vào ứng dụng',
220
302
  description: [
221
303
  '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.',
@@ -242,26 +324,31 @@ export function registerDomainTools(server, clients) {
242
324
  priority: z.number().int().min(0).max(65535).optional().describe('Ưu tiên (MX/SRV).'),
243
325
  };
244
326
  server.registerTool('cloud_domain_dns_list', {
327
+ annotations: toolAnnotationsForName('cloud_domain_dns_list'),
245
328
  title: 'Xem bản ghi DNS của tên miền',
246
329
  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.',
247
330
  inputSchema: z.object({ domain_id: objectId }).strict(),
248
331
  }, ({ domain_id }) => runTool(() => clients.vibecloud(`/api/domains/${encodeURIComponent(domain_id)}/records`)));
249
332
  server.registerTool('cloud_domain_dns_add', {
333
+ annotations: toolAnnotationsForName('cloud_domain_dns_add'),
250
334
  title: 'Thêm bản ghi DNS',
251
335
  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.',
252
336
  inputSchema: z.object({ domain_id: objectId, ...recordSchema }).strict(),
253
337
  }, ({ domain_id, ...record }) => runTool(() => clients.vibecloud(`/api/domains/${encodeURIComponent(domain_id)}/records`, { method: 'POST', body: record })));
254
338
  server.registerTool('cloud_domain_dns_update', {
339
+ annotations: toolAnnotationsForName('cloud_domain_dns_update'),
255
340
  title: 'Sửa bản ghi DNS',
256
341
  description: 'Sửa 1 bản ghi DNS theo record_id (lấy từ cloud_domain_dns_list).',
257
342
  inputSchema: z.object({ domain_id: objectId, record_id: z.string().min(1).max(128), ...recordSchema }).strict(),
258
343
  }, ({ domain_id, record_id, ...record }) => runTool(() => clients.vibecloud(`/api/domains/${encodeURIComponent(domain_id)}/records/${encodeURIComponent(record_id)}`, { method: 'PUT', body: record })));
259
344
  server.registerTool('cloud_domain_dns_delete', {
345
+ annotations: toolAnnotationsForName('cloud_domain_dns_delete'),
260
346
  title: 'Xoá bản ghi DNS',
261
347
  description: 'Xoá 1 bản ghi DNS theo record_id.',
262
348
  inputSchema: z.object({ domain_id: objectId, record_id: z.string().min(1).max(128) }).strict(),
263
349
  }, ({ domain_id, record_id }) => runTool(() => clients.vibecloud(`/api/domains/${encodeURIComponent(domain_id)}/records/${encodeURIComponent(record_id)}`, { method: 'DELETE' })));
264
350
  server.registerTool('cloud_domain_ns_set', {
351
+ annotations: toolAnnotationsForName('cloud_domain_ns_set'),
265
352
  title: 'Đổi nameserver (NS) của tên miền',
266
353
  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.',
267
354
  inputSchema: z.object({
package/dist/mail.js CHANGED
@@ -2,6 +2,7 @@ import { randomUUID } from 'node:crypto';
2
2
  import { domainToASCII } from 'node:url';
3
3
  import { z } from 'zod';
4
4
  import { runTool } from './errors.js';
5
+ import { toolAnnotationsForName } from './monapay.js';
5
6
  const id = z.string().trim().min(1).max(255);
6
7
  const email = z.string().max(320).regex(/^[^\s@<>,;]+@[^\s@<>,;]+\.[^\s@<>,;]+$/, 'Cần địa chỉ email hợp lệ.');
7
8
  const mailbox = z.union([
@@ -32,21 +33,25 @@ function idempotencyHeaders(key) {
32
33
  }
33
34
  export function registerMailTools(server, clients, config) {
34
35
  server.registerTool('mail_account', {
36
+ annotations: toolAnnotationsForName('mail_account'),
35
37
  title: 'Tài khoản MONA Mail',
36
38
  description: `Khi bắt đầu tích hợp email, đọc tài khoản, quota và bước kế tiếp. / Use first to read account and quota at ${config.monamailApi}.`,
37
39
  inputSchema: empty,
38
40
  }, () => runTool(() => clients.mail('/v1/account')));
39
41
  server.registerTool('mail_plans', {
42
+ annotations: toolAnnotationsForName('mail_plans'),
40
43
  title: 'Gói MONA Mail',
41
44
  description: 'Khi chọn gói gửi mail, đọc giá và quota hiện hành. / Use to compare current email plans.',
42
45
  inputSchema: empty,
43
46
  }, () => runTool(() => clients.mail('/v1/plans')));
44
47
  server.registerTool('mail_plan_set', {
48
+ annotations: toolAnnotationsForName('mail_plan_set'),
45
49
  title: 'Đổi gói MONA Mail',
46
50
  description: 'Khi cần đổi quota, chọn gói; gói trả phí trừ ví VND, thiếu tiền gọi cloud_topup. / Use to change the email plan.',
47
51
  inputSchema: z.object({ plan: z.enum(['free', 'khoi-nghiep', 'kinh-doanh', 'doanh-nghiep']) }).strict(),
48
52
  }, ({ plan }) => runTool(() => clients.mail('/v1/account/plan', { method: 'PUT', body: { plan } })));
49
53
  server.registerTool('mail_send', {
54
+ annotations: toolAnnotationsForName('mail_send'),
50
55
  title: 'Gửi email giao dịch',
51
56
  description: 'Khi gửi OTP hoặc thông báo, dùng domain đã verify; onboarding@monamail.vn chỉ gửi tới email chủ. sandbox=true thử 0đ, không gửi ra Internet. / Use to send transactional email or test in sandbox.',
52
57
  inputSchema: z.object({
@@ -79,11 +84,13 @@ export function registerMailTools(server, clients, config) {
79
84
  return sandbox ? { ...result, sandbox: true } : result;
80
85
  }));
81
86
  server.registerTool('mail_status', {
87
+ annotations: toolAnnotationsForName('mail_status'),
82
88
  title: 'Trạng thái email',
83
89
  description: 'Khi cần xác nhận thư đã giao, đọc trạng thái, events và sandbox_preview. / Use to inspect an email after sending.',
84
90
  inputSchema: z.object({ email_id: id }).strict(),
85
91
  }, ({ email_id }) => runTool(() => clients.mail(`/v1/emails/${encodeURIComponent(email_id)}`)));
86
92
  server.registerTool('mail_list', {
93
+ annotations: toolAnnotationsForName('mail_list'),
87
94
  title: 'Danh sách email',
88
95
  description: 'Khi tra lịch sử gửi, lọc theo trạng thái, người nhận hoặc thời gian. / Use to search sent email history.',
89
96
  inputSchema: z.object({
@@ -94,6 +101,7 @@ export function registerMailTools(server, clients, config) {
94
101
  }).strict(),
95
102
  }, (query) => runTool(() => clients.mail('/v1/emails', { query })));
96
103
  server.registerTool('mail_domain_add', {
104
+ annotations: toolAnnotationsForName('mail_domain_add'),
97
105
  title: 'Thêm domain gửi email',
98
106
  description: 'Khi gửi bằng domain của app, thêm domain. Trả record DNS; nếu người dùng dùng Cloudflare có thể gọi mail_domain_cloudflare với token của họ (không lưu). / Use to register a sender domain and get DNS records.',
99
107
  inputSchema: z.object({
@@ -107,6 +115,7 @@ export function registerMailTools(server, clients, config) {
107
115
  instructions: 'Thêm các records vào DNS rồi gọi mail_domain_verify. Nếu dùng Cloudflare, gọi mail_domain_cloudflare với token người dùng cung cấp; token chỉ dùng một lần, không lưu. DKIM đúng là đủ để verify; SPF/DMARC là cảnh báo. Nếu đã có SPF, thêm include:_spf.monamail.vn vào record hiện có.',
108
116
  })));
109
117
  server.registerTool('mail_domain_verify', {
118
+ annotations: toolAnnotationsForName('mail_domain_verify'),
110
119
  title: 'Xác minh DNS domain',
111
120
  description: 'Khi đã thêm DNS, kiểm DKIM và trạng thái domain. / Use after adding DNS records to verify the sender domain.',
112
121
  inputSchema: z.object({ domain_id: id, ...idempotency }).strict(),
@@ -114,6 +123,7 @@ export function registerMailTools(server, clients, config) {
114
123
  method: 'POST', headers: idempotencyHeaders(idempotency_key),
115
124
  })));
116
125
  server.registerTool('mail_domain_cloudflare', {
126
+ annotations: toolAnnotationsForName('mail_domain_cloudflare'),
117
127
  title: 'Thêm DNS qua Cloudflare',
118
128
  description: 'Khi người dùng cung cấp token Cloudflare, thêm DNS rồi verify domain. Token dùng một lần, không lưu, không log. / Use a user-provided Cloudflare token to configure DNS and verify.',
119
129
  inputSchema: z.object({ domain_id: id, api_token: z.string().trim().min(1).max(4096), ...idempotency }).strict(),
@@ -121,11 +131,13 @@ export function registerMailTools(server, clients, config) {
121
131
  method: 'POST', headers: idempotencyHeaders(idempotency_key), body: { api_token },
122
132
  })));
123
133
  server.registerTool('mail_domains_list', {
134
+ annotations: toolAnnotationsForName('mail_domains_list'),
124
135
  title: 'Domain MONA Mail',
125
136
  description: 'Khi chọn địa chỉ gửi, xem domain và trạng thái xác minh. / Use to find verified sender domains.',
126
137
  inputSchema: empty,
127
138
  }, () => runTool(() => clients.mail('/v1/domains')));
128
139
  server.registerTool('mail_api_key_create', {
140
+ annotations: toolAnnotationsForName('mail_api_key_create'),
129
141
  title: 'Tạo API key MONA Mail',
130
142
  description: 'Khi tích hợp SDK vào app, tạo key live hoặc test. Key chỉ trả một lần; ghi vào .env của app dưới tên MONAMAIL_API_KEY, không cần in ra chat. / Use to create an app key; store the one-time secret in .env.',
131
143
  inputSchema: z.object({ name: id, mode: z.enum(['live', 'test']), ...idempotency }).strict(),
@@ -133,16 +145,19 @@ export function registerMailTools(server, clients, config) {
133
145
  method: 'POST', headers: idempotencyHeaders(idempotency_key), body,
134
146
  })));
135
147
  server.registerTool('mail_api_keys_list', {
148
+ annotations: toolAnnotationsForName('mail_api_keys_list'),
136
149
  title: 'Danh sách API key',
137
150
  description: 'Khi kiểm tra key của app, đọc prefix và trạng thái; không trả secret. / Use to inspect existing API key metadata.',
138
151
  inputSchema: empty,
139
152
  }, () => runTool(() => clients.mail('/v1/api-keys')));
140
153
  server.registerTool('mail_api_key_revoke', {
154
+ annotations: toolAnnotationsForName('mail_api_key_revoke'),
141
155
  title: 'Thu hồi API key',
142
156
  description: 'Khi key không còn dùng hoặc bị lộ, thu hồi bằng key_id. / Use to revoke an unused or compromised API key.',
143
157
  inputSchema: z.object({ key_id: id }).strict(),
144
158
  }, ({ key_id }) => runTool(() => clients.mail(`/v1/api-keys/${encodeURIComponent(key_id)}`, { method: 'DELETE' })));
145
159
  server.registerTool('mail_webhook_create', {
160
+ annotations: toolAnnotationsForName('mail_webhook_create'),
146
161
  title: 'Tạo webhook email',
147
162
  description: 'Khi app cần nhận sự kiện gửi hoặc bounce, đăng ký HTTPS webhook; lưu secret một lần vào .env, không log. / Use to subscribe an app to email events.',
148
163
  inputSchema: z.object({ url: httpsUrl, events: z.array(events).min(1).max(8), ...idempotency }).strict(),
@@ -150,11 +165,13 @@ export function registerMailTools(server, clients, config) {
150
165
  method: 'POST', headers: idempotencyHeaders(idempotency_key), body,
151
166
  })));
152
167
  server.registerTool('mail_webhooks_list', {
168
+ annotations: toolAnnotationsForName('mail_webhooks_list'),
153
169
  title: 'Danh sách webhook email',
154
170
  description: 'Khi kiểm tra cấu hình sự kiện của app, liệt kê webhook. / Use to inspect registered email webhooks.',
155
171
  inputSchema: empty,
156
172
  }, () => runTool(() => clients.mail('/v1/webhooks')));
157
173
  server.registerTool('mail_webhook_test', {
174
+ annotations: toolAnnotationsForName('mail_webhook_test'),
158
175
  title: 'Thử webhook email',
159
176
  description: 'Khi đã có endpoint, gửi mẫu email.delivered để kiểm tra HTTP response. / Use to test webhook delivery to an app.',
160
177
  inputSchema: z.object({ webhook_id: id, ...idempotency }).strict(),
@@ -162,16 +179,19 @@ export function registerMailTools(server, clients, config) {
162
179
  method: 'POST', headers: idempotencyHeaders(idempotency_key),
163
180
  })));
164
181
  server.registerTool('mail_suppressions_list', {
182
+ annotations: toolAnnotationsForName('mail_suppressions_list'),
165
183
  title: 'Địa chỉ ngừng gửi',
166
184
  description: 'Khi thư bị suppressed, xem địa chỉ và lý do ngừng gửi. / Use to diagnose suppressed recipients.',
167
185
  inputSchema: empty,
168
186
  }, () => runTool(() => clients.mail('/v1/suppressions')));
169
187
  server.registerTool('mail_suppression_remove', {
188
+ annotations: toolAnnotationsForName('mail_suppression_remove'),
170
189
  title: 'Gỡ suppression tài khoản',
171
190
  description: 'Khi đã xử lý nguyên nhân chặn, gỡ suppression của tài khoản; lớp toàn hệ không gỡ được. / Use to remove an account-level suppression.',
172
191
  inputSchema: z.object({ email }).strict(),
173
192
  }, ({ email }) => runTool(() => clients.mail(`/v1/suppressions/${encodeURIComponent(email)}`, { method: 'DELETE' })));
174
193
  server.registerTool('mail_template_create', {
194
+ annotations: toolAnnotationsForName('mail_template_create'),
175
195
  title: 'Tạo mẫu email',
176
196
  description: 'Khi app dùng lại nội dung mail, tạo template với biến {{ten_bien}}. / Use to create a reusable email template.',
177
197
  inputSchema: z.object({ name: id, subject, html: z.string().min(1), text: z.string().min(1).optional(), ...idempotency }).strict(),
@@ -179,12 +199,14 @@ export function registerMailTools(server, clients, config) {
179
199
  method: 'POST', headers: idempotencyHeaders(idempotency_key), body,
180
200
  })));
181
201
  server.registerTool('mail_stats', {
202
+ annotations: toolAnnotationsForName('mail_stats'),
182
203
  title: 'Thống kê gửi email',
183
204
  description: 'Khi đánh giá khả năng giao thư, đọc tỷ lệ delivered và bounce theo thời gian. / Use to review email delivery statistics.',
184
205
  inputSchema: z.object({ from: dateOrTimestamp.optional(), to: dateOrTimestamp.optional() }).strict().refine((value) => !value.from || !value.to || Date.parse(value.from) <= Date.parse(value.to), 'Thời điểm from phải trước hoặc bằng to.'),
185
206
  }, (query) => runTool(() => clients.mail('/v1/stats', { query })));
186
207
  // ── Hộp thư AI agent (nhận + đọc + trả lời) ─────────────────────────────
187
208
  server.registerTool('mail_inbox_create', {
209
+ annotations: toolAnnotationsForName('mail_inbox_create'),
188
210
  title: 'Tạo hộp thư agent',
189
211
  description: 'Khi cần một địa chỉ email cho agent trực (đọc thư, trả lời), tạo hộp. Bỏ domain để dùng miền agent.monamail.vn (nhận ngay); truyền domain đã verify để có địa chỉ brand. / Use to create an inbox an AI agent can read and reply from.',
190
212
  inputSchema: z.object({ agent_id: agentId, domain: domain.optional() }).strict(),
@@ -192,21 +214,25 @@ export function registerMailTools(server, clients, config) {
192
214
  method: 'POST', body: domain ? { agent_id, domain } : { agent_id },
193
215
  })));
194
216
  server.registerTool('mail_inbox_list', {
217
+ annotations: toolAnnotationsForName('mail_inbox_list'),
195
218
  title: 'Danh sách hộp agent',
196
219
  description: 'Khi cần biết agent đang trực những hộp nào, liệt kê hộp còn hoạt động. / Use to list active agent inboxes.',
197
220
  inputSchema: empty,
198
221
  }, () => runTool(() => clients.mail('/v1/inboxes')));
199
222
  server.registerTool('mail_inbox_get', {
223
+ annotations: toolAnnotationsForName('mail_inbox_get'),
200
224
  title: 'Xem hộp agent',
201
225
  description: 'Khi cần địa chỉ và trạng thái của một hộp, đọc chi tiết. / Use to read one inbox.',
202
226
  inputSchema: z.object({ inbox_id: id }).strict(),
203
227
  }, ({ inbox_id }) => runTool(() => clients.mail(`/v1/inboxes/${encodeURIComponent(inbox_id)}`)));
204
228
  server.registerTool('mail_inbox_delete', {
229
+ annotations: toolAnnotationsForName('mail_inbox_delete'),
205
230
  title: 'Xóa hộp agent',
206
231
  description: 'Khi hộp không còn dùng, xóa mềm; thư cũ còn đọc tới hết retention. / Use to soft-delete an inbox.',
207
232
  inputSchema: z.object({ inbox_id: id }).strict(),
208
233
  }, ({ inbox_id }) => runTool(() => clients.mail(`/v1/inboxes/${encodeURIComponent(inbox_id)}`, { method: 'DELETE' })));
209
234
  server.registerTool('mail_inbox_messages', {
235
+ annotations: toolAnnotationsForName('mail_inbox_messages'),
210
236
  title: 'Thư trong hộp agent',
211
237
  description: 'Khi trực hộp, liệt kê thư nhận; lọc seen=false để lấy thư chưa xử lý, phân trang bằng cursor. / Use to list messages an agent needs to handle.',
212
238
  inputSchema: z.object({
@@ -220,17 +246,20 @@ export function registerMailTools(server, clients, config) {
220
246
  query: { limit, cursor, since, ...(seen === undefined ? {} : { seen: String(seen) }) },
221
247
  })));
222
248
  server.registerTool('mail_inbox_message', {
249
+ annotations: toolAnnotationsForName('mail_inbox_message'),
223
250
  title: 'Đọc thư trong hộp',
224
251
  description: 'Khi cần nội dung đầy đủ, header và đính kèm của một thư, đọc chi tiết; lần đọc đầu đánh dấu đã xem. / Use to read a full inbox message before replying.',
225
252
  inputSchema: z.object({ inbox_id: id, message_id: id }).strict(),
226
253
  }, ({ inbox_id, message_id }) => runTool(() => clients.mail(`/v1/inboxes/${encodeURIComponent(inbox_id)}/messages/${encodeURIComponent(message_id)}`)));
227
254
  server.registerTool('mail_inbox_reply', {
255
+ annotations: toolAnnotationsForName('mail_inbox_reply'),
228
256
  title: 'Trả lời thư trong hộp',
229
257
  description: 'Khi agent trả lời khách, gửi đúng thread (In-Reply-To, References) từ địa chỉ hộp; cần text hoặc html. Việc khó rút lại (hứa giá, chuyển tiền) thì báo người, đừng tự gửi. / Use to reply to an inbox message in-thread.',
230
258
  inputSchema: z.object({ inbox_id: id, message_id: id, text: z.string().min(1).optional(), html: z.string().min(1).optional() })
231
259
  .strict().refine((value) => value.text || value.html, 'Cần text hoặc html để trả lời.'),
232
260
  }, ({ inbox_id, message_id, ...body }) => runTool(() => clients.mail(`/v1/inboxes/${encodeURIComponent(inbox_id)}/messages/${encodeURIComponent(message_id)}/reply`, { method: 'POST', body })));
233
261
  server.registerTool('mail_inbox_wait', {
262
+ annotations: toolAnnotationsForName('mail_inbox_wait'),
234
263
  title: 'Chờ thư trong hộp',
235
264
  description: 'Khi cần chờ thư tới (ví dụ mã OTP hoặc thư khớp mẫu), chờ tối đa timeout giây; có thư khớp trả full nội dung + extracted_code, hết giờ trả 204. Gọi lại nếu cần chờ lâu hơn. / Use to wait for an OTP or a matching message.',
236
265
  inputSchema: z.object({ inbox_id: id, match: inboxMatch, timeout: z.number().int().min(0).max(300).default(120) }).strict(),
@@ -238,6 +267,7 @@ export function registerMailTools(server, clients, config) {
238
267
  query: { match, timeout },
239
268
  })));
240
269
  server.registerTool('mail_inbox_update', {
270
+ annotations: toolAnnotationsForName('mail_inbox_update'),
241
271
  title: 'Cấu hình hộp agent',
242
272
  description: 'Khi cần đặt cách hộp xử lý thư: mode redirect (tự forward thư về email chủ) | store (chỉ lưu) | ai (để AI trực), địa chỉ forward_to, chat Telegram nhận báo, hoặc bật/tắt hộp. / Use to configure an inbox: redirect-forward email, Telegram chat, mode, active.',
243
273
  inputSchema: z.object({
@@ -249,6 +279,7 @@ export function registerMailTools(server, clients, config) {
249
279
  }).strict().refine((v) => Object.keys(v).length > 1, 'Cần ít nhất một trường để cập nhật.'),
250
280
  }, ({ inbox_id, ...body }) => runTool(() => clients.mail(`/v1/inboxes/${encodeURIComponent(inbox_id)}`, { method: 'PATCH', body })));
251
281
  server.registerTool('mail_inbox_batch', {
282
+ annotations: toolAnnotationsForName('mail_inbox_batch'),
252
283
  title: 'Tạo nhiều hộp agent',
253
284
  description: 'Khi cần tạo nhiều địa chỉ một lần (ví dụ mỗi nhân viên một hộp), tạo hàng loạt trong quota gói; lỗi từng hộp không hủy cả batch. / Use to create many inboxes at once.',
254
285
  inputSchema: z.object({
package/dist/monapay.d.ts CHANGED
@@ -10,6 +10,18 @@ type ImportedTool = {
10
10
  handler: (...args: unknown[]) => unknown;
11
11
  enabled: boolean;
12
12
  };
13
+ export type ToolAnnotations = {
14
+ readOnlyHint: boolean;
15
+ destructiveHint: boolean;
16
+ idempotentHint: boolean;
17
+ openWorldHint: boolean;
18
+ };
19
+ /** Mechanical metadata shared by native tools and tools imported from monapay-mcp. */
20
+ export declare function toolAnnotationsForName(name: string): ToolAnnotations;
21
+ export declare function completeToolMetadata(name: string, title: unknown, annotations: unknown): {
22
+ title: string;
23
+ annotations: ToolAnnotations;
24
+ };
13
25
  export declare function createMonaPayClient(config: Config, env?: NodeJS.ProcessEnv, fetchImpl?: typeof fetch): MonaPayClient;
14
26
  export declare function importedMonaPayTools(config: Config, env?: NodeJS.ProcessEnv, fetchImpl?: typeof fetch): Record<string, ImportedTool>;
15
27
  export declare function normalizeImportedResult(result: unknown): Promise<unknown>;
package/dist/monapay.js CHANGED
@@ -2,6 +2,47 @@ import { readFileSync } from 'node:fs';
2
2
  import { createServer as createMonaPayServer } from 'monapay-mcp';
3
3
  import { MonaPayClient } from 'monapay-mcp/dist/client.js';
4
4
  import { CloudError, errorResult } from './errors.js';
5
+ // cloud_subscription_update có thể huỷ gia hạn / dừng máy (cancel_action=stop) → xếp destructive để agent hỏi lại trước khi gọi.
6
+ const DESTRUCTIVE = /(?:^|_)(?:delete|remove|cancel|release|rotate|stop|destroy|reset|revoke|suspend)(?:_|$)|^cloud_subscription_update$/;
7
+ const READ_ONLY = /(?:^|_)(?:list|get|status|search|balance|ledger|health|whoami|usage|services|quote|detect|wait|logs|stats|templates|tlds|prices|packages|suggest|groups)(?:_|$)/;
8
+ const IDEMPOTENT_WRITE = /(?:^|_)(?:set|update|attach)(?:_|$)/;
9
+ /** Mechanical metadata shared by native tools and tools imported from monapay-mcp. */
10
+ export function toolAnnotationsForName(name) {
11
+ const canonical = name.replace(/^vibecloud_/, 'cloud_');
12
+ const destructiveHint = DESTRUCTIVE.test(canonical);
13
+ const readOnlyHint = !destructiveHint && (READ_ONLY.test(canonical)
14
+ || canonical === 'cloud_open_console'
15
+ || canonical === 'mail_account'
16
+ || canonical === 'mail_plans'
17
+ || canonical === 'cloud_invoice_pdf'
18
+ || canonical === 'cloud_base_credentials'
19
+ || canonical === 'mail_inbox_message'
20
+ || canonical === 'mail_inbox_messages'
21
+ || canonical === 'monapay_me'
22
+ || canonical === 'monapay_verify_signature'
23
+ || canonical === 'monapay_generate_webhook_snippet'
24
+ || canonical === 'monapay_quickstart');
25
+ const idempotentHint = readOnlyHint
26
+ || (destructiveHint && !/(?:^|_)(?:rotate|reset)(?:_|$)/.test(canonical))
27
+ || (!destructiveHint && IDEMPOTENT_WRITE.test(canonical));
28
+ return {
29
+ readOnlyHint,
30
+ destructiveHint,
31
+ idempotentHint,
32
+ openWorldHint: canonical !== 'cloud_app_detect',
33
+ };
34
+ }
35
+ export function completeToolMetadata(name, title, annotations) {
36
+ const existing = annotations && typeof annotations === 'object'
37
+ ? annotations
38
+ : {};
39
+ return {
40
+ title: typeof title === 'string' && title.trim()
41
+ ? title
42
+ : `Công cụ MONA ${name.replace(/^(?:cloud|vibecloud|monapay|mail|agent)_/, '').replaceAll('_', ' ')}`,
43
+ annotations: { ...toolAnnotationsForName(name), ...existing },
44
+ };
45
+ }
5
46
  function linkedCredentials(config) {
6
47
  try {
7
48
  const parsed = JSON.parse(readFileSync(config.linksFile, 'utf8'));
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ import { createServer as createHttpServer } from "node:http";
3
+ import { createRemoteServer } from "./remote.js";
4
+ const bind = process.env.MCP_REMOTE_BIND ?? "127.0.0.1";
5
+ const port = Number.parseInt(process.env.MCP_REMOTE_PORT ?? "8765", 10);
6
+ const publicUrl = process.env.MCP_REMOTE_PUBLIC_URL ?? "https://mcp.monacloud.vn";
7
+ const issuer = process.env.MONACLOUD_ISSUER ?? "https://pass.monacloud.vn/realms/mona";
8
+ const initialAccessToken = process.env.MONA_PASS_INITIAL_ACCESS_TOKEN;
9
+ if (!initialAccessToken)
10
+ throw new Error("MONA_PASS_INITIAL_ACCESS_TOKEN is required");
11
+ if (!Number.isInteger(port) || port < 1 || port > 65_535)
12
+ throw new Error("MCP_REMOTE_PORT is invalid");
13
+ const dataDir = process.env.MCP_REMOTE_DATA_DIR ?? "./data";
14
+ const remote = await createRemoteServer({ publicUrl, issuer, initialAccessToken, dataDir });
15
+ const httpServer = createHttpServer(remote.app);
16
+ httpServer.listen(port, bind, () => {
17
+ console.log(JSON.stringify({ event: "listening", bind, port }));
18
+ });
19
+ let stopping = false;
20
+ async function shutdown() {
21
+ if (stopping)
22
+ return;
23
+ stopping = true;
24
+ await remote.close();
25
+ await new Promise((resolve, reject) => {
26
+ httpServer.close((error) => error ? reject(error) : resolve());
27
+ });
28
+ }
29
+ process.on("SIGTERM", () => void shutdown());
30
+ process.on("SIGINT", () => void shutdown());
@@ -0,0 +1,17 @@
1
+ export interface VerifiedAccessToken {
2
+ token: string;
3
+ clientId: string;
4
+ scopes: string[];
5
+ expiresAt: number;
6
+ extra: {
7
+ sub: string;
8
+ };
9
+ }
10
+ export interface JwtVerifierOptions {
11
+ issuer: string;
12
+ fetch?: typeof globalThis.fetch;
13
+ jwksTtlMs?: number;
14
+ isRegisteredClient?: (clientId: string) => boolean | Promise<boolean>;
15
+ now?: () => number;
16
+ }
17
+ export declare function createAccessTokenVerifier(options: JwtVerifierOptions): (token: string) => Promise<VerifiedAccessToken>;