helipod-mcp 0.5.9 → 0.5.10

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/http.js CHANGED
@@ -36281,6 +36281,10 @@ var period = exports_external.enum(["monthly", "3month", "6month", "yearly"]);
36281
36281
  var pointId = exports_external.string().describe("Restore point id (from list_vps_restore_points)");
36282
36282
  var backupPath = exports_external.string().max(1024).describe("Absolute path inside the backup, e.g. /etc/nginx or /var/www/index.html");
36283
36283
  var migrationId = exports_external.string().describe("Migration id (from list_vps_migrations)");
36284
+ var dryRun2 = exports_external.boolean().optional().describe("true = return the price (IDR) and the wallet balance after it; nothing is charged or changed");
36285
+ var idempotencyKey = exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real call. Generated when omitted; pass the same value when retrying so it is never charged twice");
36286
+ var DRY = "dryRun=true returns the price and walletBalanceAfter without charging or changing anything.";
36287
+ var dryQuery = (d) => ({ dryRun: d ? "true" : undefined });
36284
36288
  var vpsTools = [
36285
36289
  defineTool({
36286
36290
  name: "list_vps_plans",
@@ -36310,7 +36314,7 @@ var vpsTools = [
36310
36314
  name: "create_vps",
36311
36315
  title: "Create VPS",
36312
36316
  kind: "write",
36313
- description: "Order a server. COSTS MONEY right away (the whole billing period is charged from the wallet; refunded if provisioning fails). Show the plan price from list_vps_plans and get the user's explicit OK first. Provisioning takes a few minutes: poll get_vps.",
36317
+ description: `Order a server. COSTS MONEY right away (the whole billing period is charged from the wallet; refunded if provisioning fails). Call with dryRun=true first, show the price and the wallet after it, and get the user's explicit OK. ${DRY} autoRenew=false creates it with auto-renew off (renew it with renew_vps). Provisioning takes a few minutes: poll get_vps.`,
36314
36318
  input: {
36315
36319
  projectId: exports_external.string().uuid(),
36316
36320
  planId: exports_external.string(),
@@ -36318,9 +36322,12 @@ var vpsTools = [
36318
36322
  osType: exports_external.string().describe("From the plan's osTemplates, e.g. ubuntu"),
36319
36323
  osVersion: exports_external.string().describe("e.g. 24.04"),
36320
36324
  rootPassword: password,
36321
- billingPeriod: period.optional().describe("Default monthly")
36325
+ billingPeriod: period.optional().describe("Default monthly"),
36326
+ autoRenew: exports_external.boolean().optional().describe("false = no automatic renewal from the wallet (default true)"),
36327
+ dryRun: dryRun2,
36328
+ idempotencyKey
36322
36329
  },
36323
- run: (a, api2) => api2.api("POST", "/vps", { body: a })
36330
+ run: ({ dryRun: dryRun3, idempotencyKey: idempotencyKey2, ...body }, api2) => api2.api("POST", "/vps", { body, query: dryQuery(dryRun3), idempotencyKey: purchaseKey(dryRun3, idempotencyKey2) })
36324
36331
  }),
36325
36332
  defineTool({
36326
36333
  name: "vps_power",
@@ -36374,17 +36381,17 @@ var vpsTools = [
36374
36381
  name: "renew_vps",
36375
36382
  title: "Renew VPS",
36376
36383
  kind: "write",
36377
- description: "Extend a server's paid period. COSTS MONEY: show the price from get_vps_renewal_options and get the user's OK.",
36378
- input: { vpsId, billingPeriod: period },
36379
- run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/renew`, { body: { billingPeriod: a.billingPeriod } })
36384
+ description: `Extend a server's paid period. COSTS MONEY: call with dryRun=true first, show the price and the wallet after it, and get the user's OK. ${DRY}`,
36385
+ input: { vpsId, billingPeriod: period, dryRun: dryRun2, idempotencyKey },
36386
+ run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/renew`, { body: { billingPeriod: a.billingPeriod }, query: dryQuery(a.dryRun), idempotencyKey: purchaseKey(a.dryRun, a.idempotencyKey) })
36380
36387
  }),
36381
36388
  defineTool({
36382
36389
  name: "upgrade_vps",
36383
36390
  title: "Upgrade VPS plan",
36384
36391
  kind: "write",
36385
- description: "Move a server to a larger plan in the same family and region. COSTS MONEY (the difference for the days left) and restarts the server. planId must be one listed by get_vps_upgrade_options, which also gives the price in IDR; the server restarts during the upgrade. Servers with providerCode 2 only.",
36386
- input: { vpsId, planId: exports_external.string() },
36387
- run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/upgrade`, { body: { planId: a.planId } })
36392
+ description: `Move a server to a larger plan in the same family and region. COSTS MONEY (the difference for the days left) and restarts the server. planId must be one listed by get_vps_upgrade_options. Call with dryRun=true first and get the user's OK. ${DRY} Servers with providerCode 2 only.`,
36393
+ input: { vpsId, planId: exports_external.string(), dryRun: dryRun2, idempotencyKey },
36394
+ run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/upgrade`, { body: { planId: a.planId }, query: dryQuery(a.dryRun), idempotencyKey: purchaseKey(a.dryRun, a.idempotencyKey) })
36388
36395
  }),
36389
36396
  defineTool({
36390
36397
  name: "get_vps_upgrade_options",
@@ -36411,11 +36418,20 @@ var vpsTools = [
36411
36418
  input: { vpsId, range: exports_external.enum(["1h", "6h", "24h", "7d", "30d"]).optional() },
36412
36419
  run: (a, api2) => api2.api("GET", `/vps/${a.vpsId}/metrics`, { query: { range: a.range } })
36413
36420
  }),
36421
+ defineTool({
36422
+ name: "set_vps_auto_renew",
36423
+ title: "Set VPS auto-renew",
36424
+ kind: "write",
36425
+ idempotent: true,
36426
+ description: "Turn auto-renew on or off (sets the value, safe to repeat). With it off, the server is only renewed by renew_vps and expires otherwise.",
36427
+ input: { vpsId, autoRenew: exports_external.boolean() },
36428
+ run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/auto-renew`, { body: { autoRenew: a.autoRenew } })
36429
+ }),
36414
36430
  defineTool({
36415
36431
  name: "toggle_vps_auto_renew",
36416
36432
  title: "Toggle VPS auto-renew",
36417
36433
  kind: "write",
36418
- description: "Flip auto-renew on or off; the response gives the new value.",
36434
+ description: "Flip auto-renew to the other value; the response gives the new one. Prefer set_vps_auto_renew to reach a given state.",
36419
36435
  input: { vpsId },
36420
36436
  run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/auto-renew-toggle`)
36421
36437
  }),
@@ -36744,8 +36760,8 @@ var vpsTools = [
36744
36760
  // src/tools/domains.ts
36745
36761
  var domainId = exports_external.number().int().positive().describe("Domain id (number, from list_my_domains)");
36746
36762
  var hostId = exports_external.number().int().positive().describe("Child nameserver id (from list_child_nameservers)");
36747
- var dryRun2 = exports_external.boolean().optional().describe("true = return the exact price (IDR, VAT included) and the wallet balance after it; nothing is charged or registered");
36748
- var DRY = "dryRun=true returns the price and walletBalanceAfter without charging or changing anything.";
36763
+ var dryRun3 = exports_external.boolean().optional().describe("true = return the exact price (IDR, VAT included) and the wallet balance after it; nothing is charged or registered");
36764
+ var DRY2 = "dryRun=true returns the price and walletBalanceAfter without charging or changing anything.";
36749
36765
  var record3 = exports_external.object({
36750
36766
  name: exports_external.string().describe('"@" for the domain itself, or the part before the domain, e.g. "www" or "_dmarc.mail". A full name like "www.example.com" is accepted and stored as "www"'),
36751
36767
  type: exports_external.enum(["A", "AAAA", "MX", "MXE", "CNAME", "SPF", "TXT"]),
@@ -36821,12 +36837,12 @@ var domainTools = [
36821
36837
  name: "register_domain",
36822
36838
  title: "Register domain",
36823
36839
  kind: "write",
36824
- description: `Register a domain for 1 year. COSTS MONEY from the wallet. Check search_domain, and that the owner details are complete (check_registrant / update_registrant). ${DRY} The dry run includes WHOIS privacy when whoisProtection is true.`,
36840
+ description: `Register a domain for 1 year. COSTS MONEY from the wallet. Check search_domain, and that the owner details are complete (check_registrant / update_registrant). ${DRY2} The dry run includes WHOIS privacy when whoisProtection is true.`,
36825
36841
  input: {
36826
36842
  name: exports_external.string().describe("Full domain, e.g. tokosaya.id"),
36827
36843
  nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(),
36828
36844
  whoisProtection: exports_external.boolean().optional(),
36829
- dryRun: dryRun2
36845
+ dryRun: dryRun3
36830
36846
  },
36831
36847
  run: (a, api2) => api2.api("POST", "/domains/register", { body: { name: a.name, period: 1, nameservers: a.nameservers, whoisProtection: a.whoisProtection }, query: { dryRun: a.dryRun ? "true" : undefined } })
36832
36848
  }),
@@ -36834,16 +36850,16 @@ var domainTools = [
36834
36850
  name: "transfer_domain",
36835
36851
  title: "Transfer domain in",
36836
36852
  kind: "write",
36837
- description: `Transfer a domain from another registrar (adds 1 year). COSTS MONEY from the wallet. Needs the domain's auth code. ${DRY}`,
36838
- input: { name: exports_external.string(), authCode: exports_external.string(), nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(), dryRun: dryRun2 },
36853
+ description: `Transfer a domain from another registrar (adds 1 year). COSTS MONEY from the wallet. Needs the domain's auth code. ${DRY2}`,
36854
+ input: { name: exports_external.string(), authCode: exports_external.string(), nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(), dryRun: dryRun3 },
36839
36855
  run: (a, api2) => api2.api("POST", "/domains/transfer", { body: { name: a.name, authCode: a.authCode, period: 1, nameservers: a.nameservers }, query: { dryRun: a.dryRun ? "true" : undefined } })
36840
36856
  }),
36841
36857
  defineTool({
36842
36858
  name: "renew_domain",
36843
36859
  title: "Renew domain",
36844
36860
  kind: "write",
36845
- description: `Renew a domain for 1 year (plus WHOIS privacy when it is on). COSTS MONEY from the wallet. ${DRY}`,
36846
- input: { domainId, dryRun: dryRun2 },
36861
+ description: `Renew a domain for 1 year (plus WHOIS privacy when it is on). COSTS MONEY from the wallet. ${DRY2}`,
36862
+ input: { domainId, dryRun: dryRun3 },
36847
36863
  run: (a, api2) => api2.api("POST", `/domains/${a.domainId}/renew`, { body: { period: 1 }, query: { dryRun: a.dryRun ? "true" : undefined } })
36848
36864
  }),
36849
36865
  defineTool({
@@ -37006,7 +37022,7 @@ var message = exports_external.object({
37006
37022
  variables: exports_external.record(exports_external.string(), exports_external.string()).optional(),
37007
37023
  idempotencyKey: exports_external.string().max(128).optional()
37008
37024
  });
37009
- var dryRun3 = exports_external.boolean().optional().describe("true = return the price (IDR) and the wallet balance after it; nothing is charged or changed");
37025
+ var dryRun4 = exports_external.boolean().optional().describe("true = return the price (IDR) and the wallet balance after it; nothing is charged or changed");
37010
37026
  var idem2 = exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real purchase. Generated when omitted; pass the same value when retrying so it is never charged twice");
37011
37027
  var MONEY = "COSTS MONEY from the wallet. dryRun=true returns the charge (IDR) and walletBalanceAfter without charging or changing anything.";
37012
37028
  var planCode = exports_external.string().min(1).max(50).describe("Plan code from list_email_plans");
@@ -37042,7 +37058,7 @@ var emailTools = [
37042
37058
  title: "Buy email plan",
37043
37059
  kind: "write",
37044
37060
  description: `Start an email plan for the project (a free plan activates without charge). Works for a project with no email yet: the real call also sets email up (result.setup). ${MONEY} The dry run's mode says purchase, activate_free or already_active.`,
37045
- input: { projectId, planCode, months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional().describe("Billing cycle, default 1"), dryRun: dryRun3, idempotencyKey: idem2 },
37061
+ input: { projectId, planCode, months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional().describe("Billing cycle, default 1"), dryRun: dryRun4, idempotencyKey: idem2 },
37046
37062
  run: (a, api2) => api2.email("POST", "/plan", {
37047
37063
  project: a.projectId,
37048
37064
  body: { planCode: a.planCode, months: a.months },
@@ -37055,7 +37071,7 @@ var emailTools = [
37055
37071
  title: "Renew email plan",
37056
37072
  kind: "write",
37057
37073
  description: `Renew the project's ended or expiring email plan (409 PLAN_ACTIVE while one is still running and cannot be extended). Repeats the last plan and cycle unless planCode / months are given. ${MONEY}`,
37058
- input: { projectId, planCode: planCode.optional(), months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional(), dryRun: dryRun3, idempotencyKey: idem2 },
37074
+ input: { projectId, planCode: planCode.optional(), months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional(), dryRun: dryRun4, idempotencyKey: idem2 },
37059
37075
  run: (a, api2) => api2.email("POST", "/plan/renew", {
37060
37076
  project: a.projectId,
37061
37077
  body: { planCode: a.planCode, months: a.months },
@@ -37068,7 +37084,7 @@ var emailTools = [
37068
37084
  title: "Change email plan",
37069
37085
  kind: "write",
37070
37086
  description: `Move the active email plan to another plan. An upgrade applies now and charges the prorated difference to the same end date; a downgrade is scheduled for the next cycle (no refund). ${MONEY}`,
37071
- input: { projectId, planCode, dryRun: dryRun3, idempotencyKey: idem2 },
37087
+ input: { projectId, planCode, dryRun: dryRun4, idempotencyKey: idem2 },
37072
37088
  run: (a, api2) => api2.email("POST", "/plan/change", {
37073
37089
  project: a.projectId,
37074
37090
  body: { planCode: a.planCode },
@@ -37085,7 +37101,7 @@ var emailTools = [
37085
37101
  projectId,
37086
37102
  extraMailboxes: exports_external.number().int().min(0).max(50).describe("Total extra mailboxes wanted (0-50)"),
37087
37103
  extraStorageGb: exports_external.number().int().min(0).max(1000).describe("Total extra inbox storage in GB (0-1000)"),
37088
- dryRun: dryRun3,
37104
+ dryRun: dryRun4,
37089
37105
  idempotencyKey: idem2
37090
37106
  },
37091
37107
  run: (a, api2) => api2.email("POST", "/plan/addons", {
@@ -37655,7 +37671,7 @@ var emailTools = [
37655
37671
  // src/tools/databases.ts
37656
37672
  var databaseId = exports_external.string().uuid().describe("Database id (from list_databases)");
37657
37673
  var environmentId = exports_external.string().uuid().describe("Environment id (from list_projects). Omit for the primary (production) branch.");
37658
- var dryRun4 = exports_external.boolean().optional().describe("true = only check and show the result or price; nothing changes");
37674
+ var dryRun5 = exports_external.boolean().optional().describe("true = only check and show the result or price; nothing changes");
37659
37675
  var databaseTools = [
37660
37676
  defineTool({
37661
37677
  name: "list_database_plans",
@@ -37749,7 +37765,7 @@ var databaseTools = [
37749
37765
  minCu: exports_external.number().min(0.25).max(10).optional(),
37750
37766
  maxCu: exports_external.number().min(0.25).max(10).optional(),
37751
37767
  suspendTimeoutSeconds: exports_external.number().int().min(0).max(604800).optional().describe("0 = never suspend, or 60-604800"),
37752
- dryRun: dryRun4
37768
+ dryRun: dryRun5
37753
37769
  },
37754
37770
  run: (a, api2) => api2.api("PATCH", `/databases/${a.databaseId}/settings`, {
37755
37771
  body: { minCu: a.minCu, maxCu: a.maxCu, suspendTimeoutSeconds: a.suspendTimeoutSeconds },
@@ -37761,7 +37777,7 @@ var databaseTools = [
37761
37777
  title: "Upgrade database plan / top up",
37762
37778
  kind: "write",
37763
37779
  description: "Buy a paid plan or top up credits. COSTS MONEY from the wallet: call with dryRun first, show price and wallet balance after, and get the user's explicit OK.",
37764
- input: { databaseId, plan: exports_external.string().describe("Plan code (e.g. STARTER) or id from list_database_plans"), dryRun: dryRun4, idempotencyKey: exports_external.string().max(128).optional() },
37780
+ input: { databaseId, plan: exports_external.string().describe("Plan code (e.g. STARTER) or id from list_database_plans"), dryRun: dryRun5, idempotencyKey: exports_external.string().max(128).optional() },
37765
37781
  run: (a, api2) => api2.api("POST", `/databases/${a.databaseId}/upgrade`, { body: { plan: a.plan }, query: { dryRun: a.dryRun ? "true" : undefined }, idempotencyKey: a.idempotencyKey })
37766
37782
  }),
37767
37783
  defineTool({
@@ -38019,7 +38035,7 @@ var accountTools = [
38019
38035
  var projectId2 = exports_external.string().uuid().optional().describe("Limit to one project (a project key is always limited to its own)");
38020
38036
  var keyId = exports_external.string().uuid().optional().describe("Limit to one AI key (id from list_ai_keys)");
38021
38037
  var model = exports_external.string().max(100).optional().describe("Limit to one model id");
38022
- var dryRun5 = exports_external.boolean().optional().describe("true = only check and show the result; nothing is charged or changed");
38038
+ var dryRun6 = exports_external.boolean().optional().describe("true = only check and show the result; nothing is charged or changed");
38023
38039
  var ACCOUNT_KEY2 = "Needs a whole-account key (a key limited to one project gets 403 ACCOUNT_KEY_REQUIRED).";
38024
38040
  var imageProject = exports_external.string().uuid().optional().describe("Project the images belong to (required with a whole-account key; a project key uses its own)");
38025
38041
  var INLINE_MAX_BYTES = 1500000;
@@ -38104,7 +38120,7 @@ var aiTools = [
38104
38120
  n: exports_external.number().int().min(1).max(10).optional().describe("Images in this request (up to the model's maxN; default 1)"),
38105
38121
  images: exports_external.array(exports_external.string().url().startsWith("https://")).max(10).optional().describe("Reference images as https:// links (only models with refImages > 0)"),
38106
38122
  projectId: imageProject,
38107
- dryRun: dryRun5,
38123
+ dryRun: dryRun6,
38108
38124
  wait: exports_external.number().int().min(0).max(55).optional().describe("Seconds to wait for the result (default 50; 0 = return the id at once)"),
38109
38125
  idempotencyKey: exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real request. Generated when omitted; pass the same value when retrying so it is never charged twice")
38110
38126
  },
@@ -38154,7 +38170,7 @@ var aiTools = [
38154
38170
  input: {
38155
38171
  packId: exports_external.string().min(1).max(64).optional(),
38156
38172
  amount: exports_external.number().min(1).max(1e9).optional().describe("IDR"),
38157
- dryRun: dryRun5,
38173
+ dryRun: dryRun6,
38158
38174
  idempotencyKey: exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real top-up. Generated when omitted; pass the same value when retrying so it is never charged twice")
38159
38175
  },
38160
38176
  run: (a, api2) => {
@@ -38177,7 +38193,7 @@ var aiTools = [
38177
38193
  enabled: exports_external.boolean(),
38178
38194
  threshold: exports_external.number().min(1).optional().describe("Top up when the AI balance is below this (IDR)"),
38179
38195
  amount: exports_external.number().min(1).optional().describe("How much to top up each time (IDR)"),
38180
- dryRun: dryRun5
38196
+ dryRun: dryRun6
38181
38197
  },
38182
38198
  run: (a, api2) => api2.api("PUT", "/ai/auto-topup", {
38183
38199
  body: { enabled: a.enabled, threshold: a.threshold, amount: a.amount },
@@ -38213,7 +38229,7 @@ var generalTools = [
38213
38229
  ];
38214
38230
 
38215
38231
  // src/server.ts
38216
- var VERSION = "0.5.9";
38232
+ var VERSION = "0.5.10";
38217
38233
  var INSTRUCTIONS = `Helipod is a cloud platform (Indonesia): containers, managed Postgres databases, VPS, domains, email and an AI gateway, billed from a prepaid wallet in Rupiah.
38218
38234
  - Start with list_projects to find projects and environment ids. Account tools (get_wallet, invoices, statements, notifications, get_project_activity, support tickets) cover the account; AI tools (list_ai_models, get_ai_overview, get_ai_usage, AI keys, top-ups, generate_ai_image) cover the AI gateway.
38219
38235
  - Anything that costs money (create/deploy/scale services, resize volumes, upgrade a database plan, create/renew/upgrade VPS, VPS backups, register/renew/transfer domains, email plans and inbox add-ons, AI top-ups and auto top-up, generating images): call it with dryRun=true first (or the price lookup when it has no dry run), tell the user the cost in IDR and the wallet balance after, and wait for an explicit yes before the real call. Never treat silence or an earlier general OK as a yes.
package/dist/index.js CHANGED
@@ -35426,6 +35426,10 @@ var period = exports_external.enum(["monthly", "3month", "6month", "yearly"]);
35426
35426
  var pointId = exports_external.string().describe("Restore point id (from list_vps_restore_points)");
35427
35427
  var backupPath = exports_external.string().max(1024).describe("Absolute path inside the backup, e.g. /etc/nginx or /var/www/index.html");
35428
35428
  var migrationId = exports_external.string().describe("Migration id (from list_vps_migrations)");
35429
+ var dryRun2 = exports_external.boolean().optional().describe("true = return the price (IDR) and the wallet balance after it; nothing is charged or changed");
35430
+ var idempotencyKey = exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real call. Generated when omitted; pass the same value when retrying so it is never charged twice");
35431
+ var DRY = "dryRun=true returns the price and walletBalanceAfter without charging or changing anything.";
35432
+ var dryQuery = (d) => ({ dryRun: d ? "true" : undefined });
35429
35433
  var vpsTools = [
35430
35434
  defineTool({
35431
35435
  name: "list_vps_plans",
@@ -35455,7 +35459,7 @@ var vpsTools = [
35455
35459
  name: "create_vps",
35456
35460
  title: "Create VPS",
35457
35461
  kind: "write",
35458
- description: "Order a server. COSTS MONEY right away (the whole billing period is charged from the wallet; refunded if provisioning fails). Show the plan price from list_vps_plans and get the user's explicit OK first. Provisioning takes a few minutes: poll get_vps.",
35462
+ description: `Order a server. COSTS MONEY right away (the whole billing period is charged from the wallet; refunded if provisioning fails). Call with dryRun=true first, show the price and the wallet after it, and get the user's explicit OK. ${DRY} autoRenew=false creates it with auto-renew off (renew it with renew_vps). Provisioning takes a few minutes: poll get_vps.`,
35459
35463
  input: {
35460
35464
  projectId: exports_external.string().uuid(),
35461
35465
  planId: exports_external.string(),
@@ -35463,9 +35467,12 @@ var vpsTools = [
35463
35467
  osType: exports_external.string().describe("From the plan's osTemplates, e.g. ubuntu"),
35464
35468
  osVersion: exports_external.string().describe("e.g. 24.04"),
35465
35469
  rootPassword: password,
35466
- billingPeriod: period.optional().describe("Default monthly")
35470
+ billingPeriod: period.optional().describe("Default monthly"),
35471
+ autoRenew: exports_external.boolean().optional().describe("false = no automatic renewal from the wallet (default true)"),
35472
+ dryRun: dryRun2,
35473
+ idempotencyKey
35467
35474
  },
35468
- run: (a, api2) => api2.api("POST", "/vps", { body: a })
35475
+ run: ({ dryRun: dryRun3, idempotencyKey: idempotencyKey2, ...body }, api2) => api2.api("POST", "/vps", { body, query: dryQuery(dryRun3), idempotencyKey: purchaseKey(dryRun3, idempotencyKey2) })
35469
35476
  }),
35470
35477
  defineTool({
35471
35478
  name: "vps_power",
@@ -35519,17 +35526,17 @@ var vpsTools = [
35519
35526
  name: "renew_vps",
35520
35527
  title: "Renew VPS",
35521
35528
  kind: "write",
35522
- description: "Extend a server's paid period. COSTS MONEY: show the price from get_vps_renewal_options and get the user's OK.",
35523
- input: { vpsId, billingPeriod: period },
35524
- run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/renew`, { body: { billingPeriod: a.billingPeriod } })
35529
+ description: `Extend a server's paid period. COSTS MONEY: call with dryRun=true first, show the price and the wallet after it, and get the user's OK. ${DRY}`,
35530
+ input: { vpsId, billingPeriod: period, dryRun: dryRun2, idempotencyKey },
35531
+ run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/renew`, { body: { billingPeriod: a.billingPeriod }, query: dryQuery(a.dryRun), idempotencyKey: purchaseKey(a.dryRun, a.idempotencyKey) })
35525
35532
  }),
35526
35533
  defineTool({
35527
35534
  name: "upgrade_vps",
35528
35535
  title: "Upgrade VPS plan",
35529
35536
  kind: "write",
35530
- description: "Move a server to a larger plan in the same family and region. COSTS MONEY (the difference for the days left) and restarts the server. planId must be one listed by get_vps_upgrade_options, which also gives the price in IDR; the server restarts during the upgrade. Servers with providerCode 2 only.",
35531
- input: { vpsId, planId: exports_external.string() },
35532
- run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/upgrade`, { body: { planId: a.planId } })
35537
+ description: `Move a server to a larger plan in the same family and region. COSTS MONEY (the difference for the days left) and restarts the server. planId must be one listed by get_vps_upgrade_options. Call with dryRun=true first and get the user's OK. ${DRY} Servers with providerCode 2 only.`,
35538
+ input: { vpsId, planId: exports_external.string(), dryRun: dryRun2, idempotencyKey },
35539
+ run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/upgrade`, { body: { planId: a.planId }, query: dryQuery(a.dryRun), idempotencyKey: purchaseKey(a.dryRun, a.idempotencyKey) })
35533
35540
  }),
35534
35541
  defineTool({
35535
35542
  name: "get_vps_upgrade_options",
@@ -35556,11 +35563,20 @@ var vpsTools = [
35556
35563
  input: { vpsId, range: exports_external.enum(["1h", "6h", "24h", "7d", "30d"]).optional() },
35557
35564
  run: (a, api2) => api2.api("GET", `/vps/${a.vpsId}/metrics`, { query: { range: a.range } })
35558
35565
  }),
35566
+ defineTool({
35567
+ name: "set_vps_auto_renew",
35568
+ title: "Set VPS auto-renew",
35569
+ kind: "write",
35570
+ idempotent: true,
35571
+ description: "Turn auto-renew on or off (sets the value, safe to repeat). With it off, the server is only renewed by renew_vps and expires otherwise.",
35572
+ input: { vpsId, autoRenew: exports_external.boolean() },
35573
+ run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/auto-renew`, { body: { autoRenew: a.autoRenew } })
35574
+ }),
35559
35575
  defineTool({
35560
35576
  name: "toggle_vps_auto_renew",
35561
35577
  title: "Toggle VPS auto-renew",
35562
35578
  kind: "write",
35563
- description: "Flip auto-renew on or off; the response gives the new value.",
35579
+ description: "Flip auto-renew to the other value; the response gives the new one. Prefer set_vps_auto_renew to reach a given state.",
35564
35580
  input: { vpsId },
35565
35581
  run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/auto-renew-toggle`)
35566
35582
  }),
@@ -35889,8 +35905,8 @@ var vpsTools = [
35889
35905
  // src/tools/domains.ts
35890
35906
  var domainId = exports_external.number().int().positive().describe("Domain id (number, from list_my_domains)");
35891
35907
  var hostId = exports_external.number().int().positive().describe("Child nameserver id (from list_child_nameservers)");
35892
- var dryRun2 = exports_external.boolean().optional().describe("true = return the exact price (IDR, VAT included) and the wallet balance after it; nothing is charged or registered");
35893
- var DRY = "dryRun=true returns the price and walletBalanceAfter without charging or changing anything.";
35908
+ var dryRun3 = exports_external.boolean().optional().describe("true = return the exact price (IDR, VAT included) and the wallet balance after it; nothing is charged or registered");
35909
+ var DRY2 = "dryRun=true returns the price and walletBalanceAfter without charging or changing anything.";
35894
35910
  var record3 = exports_external.object({
35895
35911
  name: exports_external.string().describe('"@" for the domain itself, or the part before the domain, e.g. "www" or "_dmarc.mail". A full name like "www.example.com" is accepted and stored as "www"'),
35896
35912
  type: exports_external.enum(["A", "AAAA", "MX", "MXE", "CNAME", "SPF", "TXT"]),
@@ -35966,12 +35982,12 @@ var domainTools = [
35966
35982
  name: "register_domain",
35967
35983
  title: "Register domain",
35968
35984
  kind: "write",
35969
- description: `Register a domain for 1 year. COSTS MONEY from the wallet. Check search_domain, and that the owner details are complete (check_registrant / update_registrant). ${DRY} The dry run includes WHOIS privacy when whoisProtection is true.`,
35985
+ description: `Register a domain for 1 year. COSTS MONEY from the wallet. Check search_domain, and that the owner details are complete (check_registrant / update_registrant). ${DRY2} The dry run includes WHOIS privacy when whoisProtection is true.`,
35970
35986
  input: {
35971
35987
  name: exports_external.string().describe("Full domain, e.g. tokosaya.id"),
35972
35988
  nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(),
35973
35989
  whoisProtection: exports_external.boolean().optional(),
35974
- dryRun: dryRun2
35990
+ dryRun: dryRun3
35975
35991
  },
35976
35992
  run: (a, api2) => api2.api("POST", "/domains/register", { body: { name: a.name, period: 1, nameservers: a.nameservers, whoisProtection: a.whoisProtection }, query: { dryRun: a.dryRun ? "true" : undefined } })
35977
35993
  }),
@@ -35979,16 +35995,16 @@ var domainTools = [
35979
35995
  name: "transfer_domain",
35980
35996
  title: "Transfer domain in",
35981
35997
  kind: "write",
35982
- description: `Transfer a domain from another registrar (adds 1 year). COSTS MONEY from the wallet. Needs the domain's auth code. ${DRY}`,
35983
- input: { name: exports_external.string(), authCode: exports_external.string(), nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(), dryRun: dryRun2 },
35998
+ description: `Transfer a domain from another registrar (adds 1 year). COSTS MONEY from the wallet. Needs the domain's auth code. ${DRY2}`,
35999
+ input: { name: exports_external.string(), authCode: exports_external.string(), nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(), dryRun: dryRun3 },
35984
36000
  run: (a, api2) => api2.api("POST", "/domains/transfer", { body: { name: a.name, authCode: a.authCode, period: 1, nameservers: a.nameservers }, query: { dryRun: a.dryRun ? "true" : undefined } })
35985
36001
  }),
35986
36002
  defineTool({
35987
36003
  name: "renew_domain",
35988
36004
  title: "Renew domain",
35989
36005
  kind: "write",
35990
- description: `Renew a domain for 1 year (plus WHOIS privacy when it is on). COSTS MONEY from the wallet. ${DRY}`,
35991
- input: { domainId, dryRun: dryRun2 },
36006
+ description: `Renew a domain for 1 year (plus WHOIS privacy when it is on). COSTS MONEY from the wallet. ${DRY2}`,
36007
+ input: { domainId, dryRun: dryRun3 },
35992
36008
  run: (a, api2) => api2.api("POST", `/domains/${a.domainId}/renew`, { body: { period: 1 }, query: { dryRun: a.dryRun ? "true" : undefined } })
35993
36009
  }),
35994
36010
  defineTool({
@@ -36151,7 +36167,7 @@ var message = exports_external.object({
36151
36167
  variables: exports_external.record(exports_external.string(), exports_external.string()).optional(),
36152
36168
  idempotencyKey: exports_external.string().max(128).optional()
36153
36169
  });
36154
- var dryRun3 = exports_external.boolean().optional().describe("true = return the price (IDR) and the wallet balance after it; nothing is charged or changed");
36170
+ var dryRun4 = exports_external.boolean().optional().describe("true = return the price (IDR) and the wallet balance after it; nothing is charged or changed");
36155
36171
  var idem2 = exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real purchase. Generated when omitted; pass the same value when retrying so it is never charged twice");
36156
36172
  var MONEY = "COSTS MONEY from the wallet. dryRun=true returns the charge (IDR) and walletBalanceAfter without charging or changing anything.";
36157
36173
  var planCode = exports_external.string().min(1).max(50).describe("Plan code from list_email_plans");
@@ -36187,7 +36203,7 @@ var emailTools = [
36187
36203
  title: "Buy email plan",
36188
36204
  kind: "write",
36189
36205
  description: `Start an email plan for the project (a free plan activates without charge). Works for a project with no email yet: the real call also sets email up (result.setup). ${MONEY} The dry run's mode says purchase, activate_free or already_active.`,
36190
- input: { projectId, planCode, months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional().describe("Billing cycle, default 1"), dryRun: dryRun3, idempotencyKey: idem2 },
36206
+ input: { projectId, planCode, months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional().describe("Billing cycle, default 1"), dryRun: dryRun4, idempotencyKey: idem2 },
36191
36207
  run: (a, api2) => api2.email("POST", "/plan", {
36192
36208
  project: a.projectId,
36193
36209
  body: { planCode: a.planCode, months: a.months },
@@ -36200,7 +36216,7 @@ var emailTools = [
36200
36216
  title: "Renew email plan",
36201
36217
  kind: "write",
36202
36218
  description: `Renew the project's ended or expiring email plan (409 PLAN_ACTIVE while one is still running and cannot be extended). Repeats the last plan and cycle unless planCode / months are given. ${MONEY}`,
36203
- input: { projectId, planCode: planCode.optional(), months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional(), dryRun: dryRun3, idempotencyKey: idem2 },
36219
+ input: { projectId, planCode: planCode.optional(), months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional(), dryRun: dryRun4, idempotencyKey: idem2 },
36204
36220
  run: (a, api2) => api2.email("POST", "/plan/renew", {
36205
36221
  project: a.projectId,
36206
36222
  body: { planCode: a.planCode, months: a.months },
@@ -36213,7 +36229,7 @@ var emailTools = [
36213
36229
  title: "Change email plan",
36214
36230
  kind: "write",
36215
36231
  description: `Move the active email plan to another plan. An upgrade applies now and charges the prorated difference to the same end date; a downgrade is scheduled for the next cycle (no refund). ${MONEY}`,
36216
- input: { projectId, planCode, dryRun: dryRun3, idempotencyKey: idem2 },
36232
+ input: { projectId, planCode, dryRun: dryRun4, idempotencyKey: idem2 },
36217
36233
  run: (a, api2) => api2.email("POST", "/plan/change", {
36218
36234
  project: a.projectId,
36219
36235
  body: { planCode: a.planCode },
@@ -36230,7 +36246,7 @@ var emailTools = [
36230
36246
  projectId,
36231
36247
  extraMailboxes: exports_external.number().int().min(0).max(50).describe("Total extra mailboxes wanted (0-50)"),
36232
36248
  extraStorageGb: exports_external.number().int().min(0).max(1000).describe("Total extra inbox storage in GB (0-1000)"),
36233
- dryRun: dryRun3,
36249
+ dryRun: dryRun4,
36234
36250
  idempotencyKey: idem2
36235
36251
  },
36236
36252
  run: (a, api2) => api2.email("POST", "/plan/addons", {
@@ -36800,7 +36816,7 @@ var emailTools = [
36800
36816
  // src/tools/databases.ts
36801
36817
  var databaseId = exports_external.string().uuid().describe("Database id (from list_databases)");
36802
36818
  var environmentId = exports_external.string().uuid().describe("Environment id (from list_projects). Omit for the primary (production) branch.");
36803
- var dryRun4 = exports_external.boolean().optional().describe("true = only check and show the result or price; nothing changes");
36819
+ var dryRun5 = exports_external.boolean().optional().describe("true = only check and show the result or price; nothing changes");
36804
36820
  var databaseTools = [
36805
36821
  defineTool({
36806
36822
  name: "list_database_plans",
@@ -36894,7 +36910,7 @@ var databaseTools = [
36894
36910
  minCu: exports_external.number().min(0.25).max(10).optional(),
36895
36911
  maxCu: exports_external.number().min(0.25).max(10).optional(),
36896
36912
  suspendTimeoutSeconds: exports_external.number().int().min(0).max(604800).optional().describe("0 = never suspend, or 60-604800"),
36897
- dryRun: dryRun4
36913
+ dryRun: dryRun5
36898
36914
  },
36899
36915
  run: (a, api2) => api2.api("PATCH", `/databases/${a.databaseId}/settings`, {
36900
36916
  body: { minCu: a.minCu, maxCu: a.maxCu, suspendTimeoutSeconds: a.suspendTimeoutSeconds },
@@ -36906,7 +36922,7 @@ var databaseTools = [
36906
36922
  title: "Upgrade database plan / top up",
36907
36923
  kind: "write",
36908
36924
  description: "Buy a paid plan or top up credits. COSTS MONEY from the wallet: call with dryRun first, show price and wallet balance after, and get the user's explicit OK.",
36909
- input: { databaseId, plan: exports_external.string().describe("Plan code (e.g. STARTER) or id from list_database_plans"), dryRun: dryRun4, idempotencyKey: exports_external.string().max(128).optional() },
36925
+ input: { databaseId, plan: exports_external.string().describe("Plan code (e.g. STARTER) or id from list_database_plans"), dryRun: dryRun5, idempotencyKey: exports_external.string().max(128).optional() },
36910
36926
  run: (a, api2) => api2.api("POST", `/databases/${a.databaseId}/upgrade`, { body: { plan: a.plan }, query: { dryRun: a.dryRun ? "true" : undefined }, idempotencyKey: a.idempotencyKey })
36911
36927
  }),
36912
36928
  defineTool({
@@ -37164,7 +37180,7 @@ var accountTools = [
37164
37180
  var projectId2 = exports_external.string().uuid().optional().describe("Limit to one project (a project key is always limited to its own)");
37165
37181
  var keyId = exports_external.string().uuid().optional().describe("Limit to one AI key (id from list_ai_keys)");
37166
37182
  var model = exports_external.string().max(100).optional().describe("Limit to one model id");
37167
- var dryRun5 = exports_external.boolean().optional().describe("true = only check and show the result; nothing is charged or changed");
37183
+ var dryRun6 = exports_external.boolean().optional().describe("true = only check and show the result; nothing is charged or changed");
37168
37184
  var ACCOUNT_KEY2 = "Needs a whole-account key (a key limited to one project gets 403 ACCOUNT_KEY_REQUIRED).";
37169
37185
  var imageProject = exports_external.string().uuid().optional().describe("Project the images belong to (required with a whole-account key; a project key uses its own)");
37170
37186
  var INLINE_MAX_BYTES = 1500000;
@@ -37249,7 +37265,7 @@ var aiTools = [
37249
37265
  n: exports_external.number().int().min(1).max(10).optional().describe("Images in this request (up to the model's maxN; default 1)"),
37250
37266
  images: exports_external.array(exports_external.string().url().startsWith("https://")).max(10).optional().describe("Reference images as https:// links (only models with refImages > 0)"),
37251
37267
  projectId: imageProject,
37252
- dryRun: dryRun5,
37268
+ dryRun: dryRun6,
37253
37269
  wait: exports_external.number().int().min(0).max(55).optional().describe("Seconds to wait for the result (default 50; 0 = return the id at once)"),
37254
37270
  idempotencyKey: exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real request. Generated when omitted; pass the same value when retrying so it is never charged twice")
37255
37271
  },
@@ -37299,7 +37315,7 @@ var aiTools = [
37299
37315
  input: {
37300
37316
  packId: exports_external.string().min(1).max(64).optional(),
37301
37317
  amount: exports_external.number().min(1).max(1e9).optional().describe("IDR"),
37302
- dryRun: dryRun5,
37318
+ dryRun: dryRun6,
37303
37319
  idempotencyKey: exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real top-up. Generated when omitted; pass the same value when retrying so it is never charged twice")
37304
37320
  },
37305
37321
  run: (a, api2) => {
@@ -37322,7 +37338,7 @@ var aiTools = [
37322
37338
  enabled: exports_external.boolean(),
37323
37339
  threshold: exports_external.number().min(1).optional().describe("Top up when the AI balance is below this (IDR)"),
37324
37340
  amount: exports_external.number().min(1).optional().describe("How much to top up each time (IDR)"),
37325
- dryRun: dryRun5
37341
+ dryRun: dryRun6
37326
37342
  },
37327
37343
  run: (a, api2) => api2.api("PUT", "/ai/auto-topup", {
37328
37344
  body: { enabled: a.enabled, threshold: a.threshold, amount: a.amount },
@@ -37358,7 +37374,7 @@ var generalTools = [
37358
37374
  ];
37359
37375
 
37360
37376
  // src/server.ts
37361
- var VERSION = "0.5.9";
37377
+ var VERSION = "0.5.10";
37362
37378
  var INSTRUCTIONS = `Helipod is a cloud platform (Indonesia): containers, managed Postgres databases, VPS, domains, email and an AI gateway, billed from a prepaid wallet in Rupiah.
37363
37379
  - Start with list_projects to find projects and environment ids. Account tools (get_wallet, invoices, statements, notifications, get_project_activity, support tickets) cover the account; AI tools (list_ai_models, get_ai_overview, get_ai_usage, AI keys, top-ups, generate_ai_image) cover the AI gateway.
37364
37380
  - Anything that costs money (create/deploy/scale services, resize volumes, upgrade a database plan, create/renew/upgrade VPS, VPS backups, register/renew/transfer domains, email plans and inbox add-ons, AI top-ups and auto top-up, generating images): call it with dryRun=true first (or the price lookup when it has no dry run), tell the user the cost in IDR and the wallet balance after, and wait for an explicit yes before the real call. Never treat silence or an earlier general OK as a yes.
package/dist/stdio.js CHANGED
@@ -35533,6 +35533,10 @@ var period = exports_external.enum(["monthly", "3month", "6month", "yearly"]);
35533
35533
  var pointId = exports_external.string().describe("Restore point id (from list_vps_restore_points)");
35534
35534
  var backupPath = exports_external.string().max(1024).describe("Absolute path inside the backup, e.g. /etc/nginx or /var/www/index.html");
35535
35535
  var migrationId = exports_external.string().describe("Migration id (from list_vps_migrations)");
35536
+ var dryRun2 = exports_external.boolean().optional().describe("true = return the price (IDR) and the wallet balance after it; nothing is charged or changed");
35537
+ var idempotencyKey = exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real call. Generated when omitted; pass the same value when retrying so it is never charged twice");
35538
+ var DRY = "dryRun=true returns the price and walletBalanceAfter without charging or changing anything.";
35539
+ var dryQuery = (d) => ({ dryRun: d ? "true" : undefined });
35536
35540
  var vpsTools = [
35537
35541
  defineTool({
35538
35542
  name: "list_vps_plans",
@@ -35562,7 +35566,7 @@ var vpsTools = [
35562
35566
  name: "create_vps",
35563
35567
  title: "Create VPS",
35564
35568
  kind: "write",
35565
- description: "Order a server. COSTS MONEY right away (the whole billing period is charged from the wallet; refunded if provisioning fails). Show the plan price from list_vps_plans and get the user's explicit OK first. Provisioning takes a few minutes: poll get_vps.",
35569
+ description: `Order a server. COSTS MONEY right away (the whole billing period is charged from the wallet; refunded if provisioning fails). Call with dryRun=true first, show the price and the wallet after it, and get the user's explicit OK. ${DRY} autoRenew=false creates it with auto-renew off (renew it with renew_vps). Provisioning takes a few minutes: poll get_vps.`,
35566
35570
  input: {
35567
35571
  projectId: exports_external.string().uuid(),
35568
35572
  planId: exports_external.string(),
@@ -35570,9 +35574,12 @@ var vpsTools = [
35570
35574
  osType: exports_external.string().describe("From the plan's osTemplates, e.g. ubuntu"),
35571
35575
  osVersion: exports_external.string().describe("e.g. 24.04"),
35572
35576
  rootPassword: password,
35573
- billingPeriod: period.optional().describe("Default monthly")
35577
+ billingPeriod: period.optional().describe("Default monthly"),
35578
+ autoRenew: exports_external.boolean().optional().describe("false = no automatic renewal from the wallet (default true)"),
35579
+ dryRun: dryRun2,
35580
+ idempotencyKey
35574
35581
  },
35575
- run: (a, api2) => api2.api("POST", "/vps", { body: a })
35582
+ run: ({ dryRun: dryRun3, idempotencyKey: idempotencyKey2, ...body }, api2) => api2.api("POST", "/vps", { body, query: dryQuery(dryRun3), idempotencyKey: purchaseKey(dryRun3, idempotencyKey2) })
35576
35583
  }),
35577
35584
  defineTool({
35578
35585
  name: "vps_power",
@@ -35626,17 +35633,17 @@ var vpsTools = [
35626
35633
  name: "renew_vps",
35627
35634
  title: "Renew VPS",
35628
35635
  kind: "write",
35629
- description: "Extend a server's paid period. COSTS MONEY: show the price from get_vps_renewal_options and get the user's OK.",
35630
- input: { vpsId, billingPeriod: period },
35631
- run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/renew`, { body: { billingPeriod: a.billingPeriod } })
35636
+ description: `Extend a server's paid period. COSTS MONEY: call with dryRun=true first, show the price and the wallet after it, and get the user's OK. ${DRY}`,
35637
+ input: { vpsId, billingPeriod: period, dryRun: dryRun2, idempotencyKey },
35638
+ run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/renew`, { body: { billingPeriod: a.billingPeriod }, query: dryQuery(a.dryRun), idempotencyKey: purchaseKey(a.dryRun, a.idempotencyKey) })
35632
35639
  }),
35633
35640
  defineTool({
35634
35641
  name: "upgrade_vps",
35635
35642
  title: "Upgrade VPS plan",
35636
35643
  kind: "write",
35637
- description: "Move a server to a larger plan in the same family and region. COSTS MONEY (the difference for the days left) and restarts the server. planId must be one listed by get_vps_upgrade_options, which also gives the price in IDR; the server restarts during the upgrade. Servers with providerCode 2 only.",
35638
- input: { vpsId, planId: exports_external.string() },
35639
- run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/upgrade`, { body: { planId: a.planId } })
35644
+ description: `Move a server to a larger plan in the same family and region. COSTS MONEY (the difference for the days left) and restarts the server. planId must be one listed by get_vps_upgrade_options. Call with dryRun=true first and get the user's OK. ${DRY} Servers with providerCode 2 only.`,
35645
+ input: { vpsId, planId: exports_external.string(), dryRun: dryRun2, idempotencyKey },
35646
+ run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/upgrade`, { body: { planId: a.planId }, query: dryQuery(a.dryRun), idempotencyKey: purchaseKey(a.dryRun, a.idempotencyKey) })
35640
35647
  }),
35641
35648
  defineTool({
35642
35649
  name: "get_vps_upgrade_options",
@@ -35663,11 +35670,20 @@ var vpsTools = [
35663
35670
  input: { vpsId, range: exports_external.enum(["1h", "6h", "24h", "7d", "30d"]).optional() },
35664
35671
  run: (a, api2) => api2.api("GET", `/vps/${a.vpsId}/metrics`, { query: { range: a.range } })
35665
35672
  }),
35673
+ defineTool({
35674
+ name: "set_vps_auto_renew",
35675
+ title: "Set VPS auto-renew",
35676
+ kind: "write",
35677
+ idempotent: true,
35678
+ description: "Turn auto-renew on or off (sets the value, safe to repeat). With it off, the server is only renewed by renew_vps and expires otherwise.",
35679
+ input: { vpsId, autoRenew: exports_external.boolean() },
35680
+ run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/auto-renew`, { body: { autoRenew: a.autoRenew } })
35681
+ }),
35666
35682
  defineTool({
35667
35683
  name: "toggle_vps_auto_renew",
35668
35684
  title: "Toggle VPS auto-renew",
35669
35685
  kind: "write",
35670
- description: "Flip auto-renew on or off; the response gives the new value.",
35686
+ description: "Flip auto-renew to the other value; the response gives the new one. Prefer set_vps_auto_renew to reach a given state.",
35671
35687
  input: { vpsId },
35672
35688
  run: (a, api2) => api2.api("POST", `/vps/${a.vpsId}/auto-renew-toggle`)
35673
35689
  }),
@@ -35996,8 +36012,8 @@ var vpsTools = [
35996
36012
  // src/tools/domains.ts
35997
36013
  var domainId = exports_external.number().int().positive().describe("Domain id (number, from list_my_domains)");
35998
36014
  var hostId = exports_external.number().int().positive().describe("Child nameserver id (from list_child_nameservers)");
35999
- var dryRun2 = exports_external.boolean().optional().describe("true = return the exact price (IDR, VAT included) and the wallet balance after it; nothing is charged or registered");
36000
- var DRY = "dryRun=true returns the price and walletBalanceAfter without charging or changing anything.";
36015
+ var dryRun3 = exports_external.boolean().optional().describe("true = return the exact price (IDR, VAT included) and the wallet balance after it; nothing is charged or registered");
36016
+ var DRY2 = "dryRun=true returns the price and walletBalanceAfter without charging or changing anything.";
36001
36017
  var record3 = exports_external.object({
36002
36018
  name: exports_external.string().describe('"@" for the domain itself, or the part before the domain, e.g. "www" or "_dmarc.mail". A full name like "www.example.com" is accepted and stored as "www"'),
36003
36019
  type: exports_external.enum(["A", "AAAA", "MX", "MXE", "CNAME", "SPF", "TXT"]),
@@ -36073,12 +36089,12 @@ var domainTools = [
36073
36089
  name: "register_domain",
36074
36090
  title: "Register domain",
36075
36091
  kind: "write",
36076
- description: `Register a domain for 1 year. COSTS MONEY from the wallet. Check search_domain, and that the owner details are complete (check_registrant / update_registrant). ${DRY} The dry run includes WHOIS privacy when whoisProtection is true.`,
36092
+ description: `Register a domain for 1 year. COSTS MONEY from the wallet. Check search_domain, and that the owner details are complete (check_registrant / update_registrant). ${DRY2} The dry run includes WHOIS privacy when whoisProtection is true.`,
36077
36093
  input: {
36078
36094
  name: exports_external.string().describe("Full domain, e.g. tokosaya.id"),
36079
36095
  nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(),
36080
36096
  whoisProtection: exports_external.boolean().optional(),
36081
- dryRun: dryRun2
36097
+ dryRun: dryRun3
36082
36098
  },
36083
36099
  run: (a, api2) => api2.api("POST", "/domains/register", { body: { name: a.name, period: 1, nameservers: a.nameservers, whoisProtection: a.whoisProtection }, query: { dryRun: a.dryRun ? "true" : undefined } })
36084
36100
  }),
@@ -36086,16 +36102,16 @@ var domainTools = [
36086
36102
  name: "transfer_domain",
36087
36103
  title: "Transfer domain in",
36088
36104
  kind: "write",
36089
- description: `Transfer a domain from another registrar (adds 1 year). COSTS MONEY from the wallet. Needs the domain's auth code. ${DRY}`,
36090
- input: { name: exports_external.string(), authCode: exports_external.string(), nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(), dryRun: dryRun2 },
36105
+ description: `Transfer a domain from another registrar (adds 1 year). COSTS MONEY from the wallet. Needs the domain's auth code. ${DRY2}`,
36106
+ input: { name: exports_external.string(), authCode: exports_external.string(), nameservers: exports_external.array(exports_external.string()).min(2).max(5).optional(), dryRun: dryRun3 },
36091
36107
  run: (a, api2) => api2.api("POST", "/domains/transfer", { body: { name: a.name, authCode: a.authCode, period: 1, nameservers: a.nameservers }, query: { dryRun: a.dryRun ? "true" : undefined } })
36092
36108
  }),
36093
36109
  defineTool({
36094
36110
  name: "renew_domain",
36095
36111
  title: "Renew domain",
36096
36112
  kind: "write",
36097
- description: `Renew a domain for 1 year (plus WHOIS privacy when it is on). COSTS MONEY from the wallet. ${DRY}`,
36098
- input: { domainId, dryRun: dryRun2 },
36113
+ description: `Renew a domain for 1 year (plus WHOIS privacy when it is on). COSTS MONEY from the wallet. ${DRY2}`,
36114
+ input: { domainId, dryRun: dryRun3 },
36099
36115
  run: (a, api2) => api2.api("POST", `/domains/${a.domainId}/renew`, { body: { period: 1 }, query: { dryRun: a.dryRun ? "true" : undefined } })
36100
36116
  }),
36101
36117
  defineTool({
@@ -36258,7 +36274,7 @@ var message = exports_external.object({
36258
36274
  variables: exports_external.record(exports_external.string(), exports_external.string()).optional(),
36259
36275
  idempotencyKey: exports_external.string().max(128).optional()
36260
36276
  });
36261
- var dryRun3 = exports_external.boolean().optional().describe("true = return the price (IDR) and the wallet balance after it; nothing is charged or changed");
36277
+ var dryRun4 = exports_external.boolean().optional().describe("true = return the price (IDR) and the wallet balance after it; nothing is charged or changed");
36262
36278
  var idem2 = exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real purchase. Generated when omitted; pass the same value when retrying so it is never charged twice");
36263
36279
  var MONEY = "COSTS MONEY from the wallet. dryRun=true returns the charge (IDR) and walletBalanceAfter without charging or changing anything.";
36264
36280
  var planCode = exports_external.string().min(1).max(50).describe("Plan code from list_email_plans");
@@ -36294,7 +36310,7 @@ var emailTools = [
36294
36310
  title: "Buy email plan",
36295
36311
  kind: "write",
36296
36312
  description: `Start an email plan for the project (a free plan activates without charge). Works for a project with no email yet: the real call also sets email up (result.setup). ${MONEY} The dry run's mode says purchase, activate_free or already_active.`,
36297
- input: { projectId, planCode, months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional().describe("Billing cycle, default 1"), dryRun: dryRun3, idempotencyKey: idem2 },
36313
+ input: { projectId, planCode, months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional().describe("Billing cycle, default 1"), dryRun: dryRun4, idempotencyKey: idem2 },
36298
36314
  run: (a, api2) => api2.email("POST", "/plan", {
36299
36315
  project: a.projectId,
36300
36316
  body: { planCode: a.planCode, months: a.months },
@@ -36307,7 +36323,7 @@ var emailTools = [
36307
36323
  title: "Renew email plan",
36308
36324
  kind: "write",
36309
36325
  description: `Renew the project's ended or expiring email plan (409 PLAN_ACTIVE while one is still running and cannot be extended). Repeats the last plan and cycle unless planCode / months are given. ${MONEY}`,
36310
- input: { projectId, planCode: planCode.optional(), months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional(), dryRun: dryRun3, idempotencyKey: idem2 },
36326
+ input: { projectId, planCode: planCode.optional(), months: exports_external.union([exports_external.literal(1), exports_external.literal(3), exports_external.literal(6), exports_external.literal(12)]).optional(), dryRun: dryRun4, idempotencyKey: idem2 },
36311
36327
  run: (a, api2) => api2.email("POST", "/plan/renew", {
36312
36328
  project: a.projectId,
36313
36329
  body: { planCode: a.planCode, months: a.months },
@@ -36320,7 +36336,7 @@ var emailTools = [
36320
36336
  title: "Change email plan",
36321
36337
  kind: "write",
36322
36338
  description: `Move the active email plan to another plan. An upgrade applies now and charges the prorated difference to the same end date; a downgrade is scheduled for the next cycle (no refund). ${MONEY}`,
36323
- input: { projectId, planCode, dryRun: dryRun3, idempotencyKey: idem2 },
36339
+ input: { projectId, planCode, dryRun: dryRun4, idempotencyKey: idem2 },
36324
36340
  run: (a, api2) => api2.email("POST", "/plan/change", {
36325
36341
  project: a.projectId,
36326
36342
  body: { planCode: a.planCode },
@@ -36337,7 +36353,7 @@ var emailTools = [
36337
36353
  projectId,
36338
36354
  extraMailboxes: exports_external.number().int().min(0).max(50).describe("Total extra mailboxes wanted (0-50)"),
36339
36355
  extraStorageGb: exports_external.number().int().min(0).max(1000).describe("Total extra inbox storage in GB (0-1000)"),
36340
- dryRun: dryRun3,
36356
+ dryRun: dryRun4,
36341
36357
  idempotencyKey: idem2
36342
36358
  },
36343
36359
  run: (a, api2) => api2.email("POST", "/plan/addons", {
@@ -36907,7 +36923,7 @@ var emailTools = [
36907
36923
  // src/tools/databases.ts
36908
36924
  var databaseId = exports_external.string().uuid().describe("Database id (from list_databases)");
36909
36925
  var environmentId = exports_external.string().uuid().describe("Environment id (from list_projects). Omit for the primary (production) branch.");
36910
- var dryRun4 = exports_external.boolean().optional().describe("true = only check and show the result or price; nothing changes");
36926
+ var dryRun5 = exports_external.boolean().optional().describe("true = only check and show the result or price; nothing changes");
36911
36927
  var databaseTools = [
36912
36928
  defineTool({
36913
36929
  name: "list_database_plans",
@@ -37001,7 +37017,7 @@ var databaseTools = [
37001
37017
  minCu: exports_external.number().min(0.25).max(10).optional(),
37002
37018
  maxCu: exports_external.number().min(0.25).max(10).optional(),
37003
37019
  suspendTimeoutSeconds: exports_external.number().int().min(0).max(604800).optional().describe("0 = never suspend, or 60-604800"),
37004
- dryRun: dryRun4
37020
+ dryRun: dryRun5
37005
37021
  },
37006
37022
  run: (a, api2) => api2.api("PATCH", `/databases/${a.databaseId}/settings`, {
37007
37023
  body: { minCu: a.minCu, maxCu: a.maxCu, suspendTimeoutSeconds: a.suspendTimeoutSeconds },
@@ -37013,7 +37029,7 @@ var databaseTools = [
37013
37029
  title: "Upgrade database plan / top up",
37014
37030
  kind: "write",
37015
37031
  description: "Buy a paid plan or top up credits. COSTS MONEY from the wallet: call with dryRun first, show price and wallet balance after, and get the user's explicit OK.",
37016
- input: { databaseId, plan: exports_external.string().describe("Plan code (e.g. STARTER) or id from list_database_plans"), dryRun: dryRun4, idempotencyKey: exports_external.string().max(128).optional() },
37032
+ input: { databaseId, plan: exports_external.string().describe("Plan code (e.g. STARTER) or id from list_database_plans"), dryRun: dryRun5, idempotencyKey: exports_external.string().max(128).optional() },
37017
37033
  run: (a, api2) => api2.api("POST", `/databases/${a.databaseId}/upgrade`, { body: { plan: a.plan }, query: { dryRun: a.dryRun ? "true" : undefined }, idempotencyKey: a.idempotencyKey })
37018
37034
  }),
37019
37035
  defineTool({
@@ -37271,7 +37287,7 @@ var accountTools = [
37271
37287
  var projectId2 = exports_external.string().uuid().optional().describe("Limit to one project (a project key is always limited to its own)");
37272
37288
  var keyId = exports_external.string().uuid().optional().describe("Limit to one AI key (id from list_ai_keys)");
37273
37289
  var model = exports_external.string().max(100).optional().describe("Limit to one model id");
37274
- var dryRun5 = exports_external.boolean().optional().describe("true = only check and show the result; nothing is charged or changed");
37290
+ var dryRun6 = exports_external.boolean().optional().describe("true = only check and show the result; nothing is charged or changed");
37275
37291
  var ACCOUNT_KEY2 = "Needs a whole-account key (a key limited to one project gets 403 ACCOUNT_KEY_REQUIRED).";
37276
37292
  var imageProject = exports_external.string().uuid().optional().describe("Project the images belong to (required with a whole-account key; a project key uses its own)");
37277
37293
  var INLINE_MAX_BYTES = 1500000;
@@ -37356,7 +37372,7 @@ var aiTools = [
37356
37372
  n: exports_external.number().int().min(1).max(10).optional().describe("Images in this request (up to the model's maxN; default 1)"),
37357
37373
  images: exports_external.array(exports_external.string().url().startsWith("https://")).max(10).optional().describe("Reference images as https:// links (only models with refImages > 0)"),
37358
37374
  projectId: imageProject,
37359
- dryRun: dryRun5,
37375
+ dryRun: dryRun6,
37360
37376
  wait: exports_external.number().int().min(0).max(55).optional().describe("Seconds to wait for the result (default 50; 0 = return the id at once)"),
37361
37377
  idempotencyKey: exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real request. Generated when omitted; pass the same value when retrying so it is never charged twice")
37362
37378
  },
@@ -37406,7 +37422,7 @@ var aiTools = [
37406
37422
  input: {
37407
37423
  packId: exports_external.string().min(1).max(64).optional(),
37408
37424
  amount: exports_external.number().min(1).max(1e9).optional().describe("IDR"),
37409
- dryRun: dryRun5,
37425
+ dryRun: dryRun6,
37410
37426
  idempotencyKey: exports_external.string().regex(/^[A-Za-z0-9_.:-]{8,128}$/).optional().describe("Idempotency-Key for the real top-up. Generated when omitted; pass the same value when retrying so it is never charged twice")
37411
37427
  },
37412
37428
  run: (a, api2) => {
@@ -37429,7 +37445,7 @@ var aiTools = [
37429
37445
  enabled: exports_external.boolean(),
37430
37446
  threshold: exports_external.number().min(1).optional().describe("Top up when the AI balance is below this (IDR)"),
37431
37447
  amount: exports_external.number().min(1).optional().describe("How much to top up each time (IDR)"),
37432
- dryRun: dryRun5
37448
+ dryRun: dryRun6
37433
37449
  },
37434
37450
  run: (a, api2) => api2.api("PUT", "/ai/auto-topup", {
37435
37451
  body: { enabled: a.enabled, threshold: a.threshold, amount: a.amount },
@@ -37465,7 +37481,7 @@ var generalTools = [
37465
37481
  ];
37466
37482
 
37467
37483
  // src/server.ts
37468
- var VERSION = "0.5.9";
37484
+ var VERSION = "0.5.10";
37469
37485
  var INSTRUCTIONS = `Helipod is a cloud platform (Indonesia): containers, managed Postgres databases, VPS, domains, email and an AI gateway, billed from a prepaid wallet in Rupiah.
37470
37486
  - Start with list_projects to find projects and environment ids. Account tools (get_wallet, invoices, statements, notifications, get_project_activity, support tickets) cover the account; AI tools (list_ai_models, get_ai_overview, get_ai_usage, AI keys, top-ups, generate_ai_image) cover the AI gateway.
37471
37487
  - Anything that costs money (create/deploy/scale services, resize volumes, upgrade a database plan, create/renew/upgrade VPS, VPS backups, register/renew/transfer domains, email plans and inbox add-ons, AI top-ups and auto top-up, generating images): call it with dryRun=true first (or the price lookup when it has no dry run), tell the user the cost in IDR and the wallet balance after, and wait for an explicit yes before the real call. Never treat silence or an earlier general OK as a yes.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "helipod-mcp",
3
3
  "mcpName": "io.helipod/helipod",
4
- "version": "0.5.9",
4
+ "version": "0.5.10",
5
5
  "description": "MCP server for Helipod: containers, databases, VPS, domains and email from Claude Code, Codex, Cursor and other AI tools.",
6
6
  "type": "module",
7
7
  "bin": {