vpsnet-mcp 2.3.9 → 2.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -262,7 +262,9 @@ one. `list_restore_file_points`, `browse_restore_files` and
262
262
  charge the account, never overwrite the disk, and never put a file back on the
263
263
  server. Initially supported on Firecracker VPS.
264
264
 
265
- Browsing is asynchronous. `browse_restore_files` returns a browse id in a
265
+ Browsing is asynchronous. `browse_restore_files` creates a persisted job and
266
+ queues worker activity, so its MCP annotation is `readOnlyHint: false` even
267
+ though the backup and running server are unchanged. It returns a browse id in a
266
268
  pending state; poll `get_restore_file_browse` until `state` is `succeeded`.
267
269
  Entries exist only in that state — a `failed` browse carries an `errorCode` and
268
270
  no listing, and must not be presented as an empty directory.
@@ -626,12 +628,14 @@ everything else.
626
628
  | `get_application_events` | Get bounded customer-safe installation events | `applications:read` | free |
627
629
  | `get_application_health` | Run and poll a fresh container-health inspection | `applications:read` | free |
628
630
  | `get_application_logs` | Run and poll a size-bounded recent-log inspection | `applications:read` | free |
631
+ | `get_application_inspection` | Poll the exact persisted health or logs inspection returned by get_application_health or get_application_logs when it remains pending. | `applications:read` | free |
629
632
  | `list_application_restore_points` | List eligible nightly points for the exact application revision | `applications:manage` | Needs paid scope: `applications:restore` |
630
633
  | `quote_application_data_restore` | Freeze the exact selective-restore balance charge without payment | `applications:manage` | Quote: `applications:restore` |
631
634
  | `restore_application_data` | Pay and queue confirmed selective replacement of declared application data | `applications:manage` | Paid: `applications:restore` |
632
635
  | `get_application_data_restore` | Poll one tenant-bound selective data restore | `applications:read` | free |
633
636
  | `list_application_registry_credentials` | List non-secret private registry credential metadata | `applications:read` | free |
634
637
  | `validate_application_recipe` | Validate customer Compose against the target worker policy | `applications:manage` | free |
638
+ | `get_application_recipe_validation` | Poll the exact persisted customer recipe validation returned by validate_application_recipe or a pending recipe creation/revision. | `applications:read` | free |
635
639
  | `list_application_recipes` | List customer-owned immutable recipe projects | `applications:read` | free |
636
640
  | `list_application_recipe_revisions` | List immutable revisions for a customer recipe | `applications:read` | free |
637
641
  | `create_application_recipe` | Validate and save a new customer recipe without installing it | `applications:manage` | free |
@@ -639,6 +643,7 @@ everything else.
639
643
  | `export_application_recipe` | Export only a customer-owned recipe without secret values | `applications:read` | free |
640
644
  | `install_application_recipe` | Install an exact validated customer recipe revision | `applications:manage` | free |
641
645
  | `discover_service_containers` | Discover bounded read-only container metadata | `applications:read` | free |
646
+ | `get_service_container_discovery` | Poll the exact persisted service container discovery returned by discover_service_containers when it remains pending. | `applications:read` | free |
642
647
  | `prepare_application_compose_adoption` | Prepare and poll a scrubbed candidate for one discovered Compose project | `applications:manage` | free |
643
648
  | `get_application_compose_adoption` | Poll one tenant-bound Compose adoption candidate | `applications:read` | free |
644
649
  | `confirm_application_compose_adoption` | Confirm the exact source takeover after explicit approval | `applications:manage` | free |
@@ -653,6 +658,7 @@ everything else.
653
658
  | `stop_application` | Stop an application after confirming interruption | `applications:manage` | free |
654
659
  | `uninstall_application` | Delete an application after explicit data-loss approval | `applications:manage` | free |
655
660
  | `update_application` | Apply the eligible immutable update with exact version preconditions | `applications:manage` | free |
661
+ | `rollback_application` | Queue the exact reverse transition advertised as type=rollback by get_application_installation. | `applications:manage` | free |
656
662
  | `cancel_application_action` | Cancel the exact latest queued action only while the backend advertises it as cancellable | `applications:manage` | free |
657
663
 
658
664
  ### Service Actions
@@ -669,8 +675,10 @@ everything else.
669
675
  | Tool | Description | Scope | Paid |
670
676
  |------|-------------|-------|------|
671
677
  | `get_hostname` | Get current and automatic service hostname state | `services:read` | free |
678
+ | `get_guest_nameservers` | Get the server's saved guest DNS resolver configuration: nsData.type is own (VPSnet defaults) or custom, with ns1-ns4 resolver IP addresses. | `services:read` | free |
672
679
  | `change_hostname` | Change VPS hostname | `services:manage` | free |
673
680
  | `reset_hostname` | Restore the VPSnet-managed automatic hostname | `services:manage` | free |
681
+ | `set_guest_nameservers` | Change DNS resolvers inside a supported Linux server. | `services:manage` | free |
674
682
  | `change_root_password` | Change VPS root password | `services:manage` | free |
675
683
  | `get_rdns` | Get current rDNS records | `services:read` | free |
676
684
  | `change_rdns` | Change reverse DNS (PTR) record | `services:manage` | free |
@@ -712,14 +720,16 @@ everything else.
712
720
  |------|-------------|-------|------|
713
721
  | `get_order_plans` | Get available plans for an explicitly selected service product | none | free |
714
722
  | `get_order_options` | Get configurable options for a plan | none | free |
715
- | `order_service` | Order a new VPS (optionally attached to one of your private networks with `privateNetworkId`) | none | Paid: `vps:order`, `vds:order`, `ds:order` or `fc:order` (by product) |
723
+ | `order_service` | Quote a new VPS, then confirm the exact total and period after customer approval; optionally attach a private network with `privateNetworkId` | none | Paid: `vps:order`, `vds:order`, `ds:order` or `fc:order` (by product) |
716
724
 
717
725
  ### Backups
718
726
  | Tool | Description | Scope | Paid |
719
727
  |------|-------------|-------|------|
720
728
  | `get_backup_status` | Get backup status and configuration | `services:read` | free |
729
+ | `get_firecracker_backup_status` | Get nightly off-node backup status for a Firecracker VPS, including running count and the latest retained backup's consistency provenance. | `services:read` | free |
721
730
  | `get_backup_history` | Get backup history | `services:read` | free |
722
- | `create_backup` | Create a new backup (paid) | `services:manage` | Paid |
731
+ | `get_firecracker_backup_history` | Get retained nightly Firecracker VPS backup history, including failed/running attempts and consistency provenance. | `services:read` | free |
732
+ | `create_backup` | Retrieve historical Container VPS backup content with explicit payment consent and a stable request key | `services:manage` | Paid |
723
733
 
724
734
  ### Restore
725
735
  | Tool | Description | Scope | Paid |
@@ -728,7 +738,7 @@ everything else.
728
738
  | `list_restore_points` | List whole-service restore points | `services:read` | free |
729
739
  | `request_restore` | **Paid and destructive:** restore the whole service disk from a point | `services:read` | Paid: `services:restore` |
730
740
  | `list_restore_file_points` | List browsable backup points and whether folder search is available (free) | `services:read` | free |
731
- | `browse_restore_files` | Queue a listing of one directory inside a backup (free, read-only) | `services:read` | free |
741
+ | `browse_restore_files` | Queue a backup directory listing job without restoring files | `services:read` | free |
732
742
  | `get_restore_file_browse` | Poll a queued backup directory listing (free, read-only) | `services:read` | free |
733
743
 
734
744
  ### SSH Keys
@@ -861,20 +871,21 @@ short-lived quote token.
861
871
  | `list_service_dns_options` | List owned zones and service addresses available for DNS attachment | `dns:read` | free |
862
872
  | `attach_service_dns_record` | Point an owned-zone name at one service address | `dns:write` | free |
863
873
  | `list_dns_zones` | List forward DNS zones owned by the account | `dns:read` | free |
864
- | `create_dns_zone` | Create a native or secondary forward DNS zone | `dns:write` | free |
865
- | `get_dns_zone` | Get one DNS zone and its desired records | `dns:read` | free |
874
+ | `create_dns_zone` | Create a native or secondary zone awaiting ownership TXT verification | `dns:write` | free |
875
+ | `get_dns_zone` | Get desired records with IDs and a recoverable ownership TXT value | `dns:read` | free |
876
+ | `reapply_dns_zone` | Retry publication of a published DNS zone whose apply_state is failed. | `dns:write` | free |
866
877
  | `get_dns_zone_diagnostics` | Inspect delegation, SOA, DNSSEC, and record hygiene | `dns:read` | free |
867
878
  | `export_dns_zone` | Export a native zone as a BIND-style zone file | `dns:read` | free |
868
879
  | `import_dns_zone` | Import bounded BIND-style records into a native zone | `dns:write` | free |
869
880
  | `list_dns_templates` | List backend-defined DNS record templates | `dns:read` | free |
870
881
  | `apply_dns_template` | Preview or apply one DNS record template | `dns:write` | free |
871
882
  | `delete_dns_zone` | Delete a forward DNS zone | `dns:write` | free |
872
- | `verify_dns_zone` | Verify ownership (TXT publicly or at the previous provider) and publish a pending zone | `dns:write` | free |
873
- | `reissue_dns_zone_verification` | Replace a lost ownership TXT value for a pending zone | `dns:write` | free |
883
+ | `verify_dns_zone` | Verify ownership and publish a pending or verification-expired zone | `dns:write` | free |
884
+ | `reissue_dns_zone_verification` | Rotate ownership TXT for a pending or verification-expired zone after checking its recoverable value | `dns:write` | free |
874
885
  | `get_dnssec` | Get DNSSEC state and public DNSKEY/DS material | `dns:read` | free |
875
886
  | `set_dnssec` | Enable or safely disable DNSSEC signing | `dnssec:manage` | free |
876
- | `upsert_dns_record` | Create or replace a forward DNS desired-state record | `dns:write` | free |
877
- | `update_dns_record` | Update one existing non-system DNS record | `dns:write` | free |
887
+ | `upsert_dns_record` | Add a name/type/content tuple; an identical tuple updates TTL/comment and re-enables the record | `dns:write` | free |
888
+ | `update_dns_record` | Replace one existing non-system DNS record's content by exact record ID | `dns:write` | free |
878
889
  | `delete_dns_record` | Delete one forward DNS desired-state record | `dns:write` | free |
879
890
  | `get_dns_service_status` | Get managed DNS cluster status and published endpoints | `dns:read` | free |
880
891
  | `get_dns_zone_history` | Get recent zone and record change history | `dns:read` | free |
@@ -905,6 +916,7 @@ short-lived quote token.
905
916
  | Tool | Description | Scope | Paid |
906
917
  |------|-------------|-------|------|
907
918
  | `list_functions` | List usage-billed Firecracker Functions | `services:read` | free |
919
+ | `list_function_history` | Read paginated account-wide Firecracker Function invocation history. | `services:read` | free |
908
920
  | `get_function` | Get one function, including integrity state and protected values when readable | `services:read` | free |
909
921
  | `create_function` | Create a Firecracker Function | `services:manage` | free |
910
922
  | `update_function` | Update a function; unreadable protected values require explicit replacement approval | `services:manage` | free |
@@ -1106,10 +1118,13 @@ Each example is a request to your assistant and the tools it calls.
1106
1118
  (OS, period, resource IDs) and `list_ssh_keys` for the key ID.
1107
1119
  2. `order_service` with `plan`, `os`, `period`, `resources`, `sshKey` (or
1108
1120
  `rootPassword`, not both), `payment: { payment: 1, successUrl: '', cancelUrl: '' }`
1109
- and a stable `idempotencyKey`. With an API key it first requests the server
1110
- quote and then confirms with the returned `quoteToken` in one call, so the key
1111
- needs `vps:order` (or `vds:order`, `ds:order`, `fc:order`) and spend caps.
1112
- 3. Poll `get_service` for the returned order number; payment accepted is not
1121
+ and a stable `idempotencyKey`. The first call returns a quote without charging.
1122
+ The API key needs `vps:order` (or `vds:order`, `ds:order`, `fc:order`) and spend caps.
1123
+ 3. Show the exact gross EUR total and billing period. After the customer's approval,
1124
+ repeat the unchanged order and key with the returned `quoteToken`,
1125
+ `expected_total_eur` and `acknowledge_charge: true`. Inspect the original attempt
1126
+ in the panel if its outcome is unknown; do not create another key to retry it.
1127
+ 4. Poll `get_service` for the returned order number; payment accepted is not
1113
1128
  server ready.
1114
1129
 
1115
1130
  ### Networking (quote, then order)
@@ -1129,13 +1144,25 @@ Each example is a request to your assistant and the tools it calls.
1129
1144
 
1130
1145
  > Point www.example.com at 203.0.113.10 with a 5 minute TTL.
1131
1146
 
1132
- 1. `list_dns_zones` to find the `zone_id` (needs `dns:read`).
1133
- 2. `upsert_dns_record` with `zone_id`, `name: "www"`, `type: "A"`,
1134
- `content: "203.0.113.10"`, `ttl: 300` (needs `dns:write`). It creates the
1135
- record or replaces the existing one.
1147
+ 1. `list_dns_zones` to find the `zone_id`, then `get_dns_zone` to inspect the
1148
+ existing records and their IDs (needs `dns:read`).
1149
+ 2. To add an A value, call `upsert_dns_record` with `zone_id`, `name: "www"`,
1150
+ `type: "A"`, `content: "203.0.113.10"`, `ttl: 300` (needs `dns:write`).
1151
+ Different content adds another record and keeps existing values. An identical
1152
+ name/type/content tuple updates its TTL/comment and re-enables it if disabled.
1153
+ To replace an existing value, use `update_dns_record` with its exact
1154
+ `record_id` and the desired name, type, content and TTL.
1136
1155
  3. `get_dns_zone` to read the desired records back, or `get_dns_zone_history` to
1137
1156
  see the change.
1138
1157
 
1158
+ For a waiting zone whose ownership TXT value was lost, first read
1159
+ `get_dns_zone.zone.verification.record.value`. Use
1160
+ `reissue_dns_zone_verification` only if that value is null/unrecoverable or the
1161
+ customer requests rotation. It supports both `pending_verification` and
1162
+ `verification_expired`; the previous value stops being accepted. Update the TXT
1163
+ at the location given by `zone.verification.next_step`, then call
1164
+ `verify_dns_zone`. Reissuing alone does not verify the zone or reset its status.
1165
+
1139
1166
  ### Order a paid certificate
1140
1167
 
1141
1168
  > Buy a one-year DV certificate for example.com; I generated the CSR myself.
@@ -27,8 +27,9 @@ export const applicationActionSchema = z
27
27
  "stop",
28
28
  "uninstall",
29
29
  "update",
30
+ "rollback",
30
31
  ])
31
- .describe("Supported lifecycle action. Update applies the backend-selected newer immutable release. Selective data restore is a separate revision-bound operation.");
32
+ .describe("Supported lifecycle action. Update and rollback apply the exact backend-advertised immutable release. Selective data restore is a separate revision-bound operation.");
32
33
  export const applicationActionIdSchema = z
33
34
  .string()
34
35
  .uuid()
@@ -37,7 +38,7 @@ export const applicationExpectedVersionSchema = z
37
38
  .string()
38
39
  .max(96)
39
40
  .regex(/^[\x21-\x7E]{1,96}$/)
40
- .describe("Exact version copied from the current advertised update capability; this is an execution precondition, not a caller-selected target");
41
+ .describe("Exact version copied from the advertised update or rollback capability; this is an execution precondition, not a caller-selected target");
41
42
  const externalHttpsUrlSchema = z.string().max(500).refine((value) => {
42
43
  const addressMatch = /^https:\/\/[^/?#]+(\/[^?#]*)?$/i.exec(value);
43
44
  if (value !== value.trim() ||
@@ -239,9 +240,9 @@ export function applicationInstallRequestBody(params) {
239
240
  };
240
241
  }
241
242
  export function applicationLifecycleRequestBody(action, acknowledgeDataLoss, expectedUpdate) {
242
- if (action === "update") {
243
+ if (action === "update" || action === "rollback") {
243
244
  if (!expectedUpdate?.blueprintVersion || !expectedUpdate.upstreamVersion) {
244
- throw new Error("An exact advertised update release is required.");
245
+ throw new Error(`An exact advertised ${action} release is required.`);
245
246
  }
246
247
  return {
247
248
  action,
@@ -459,6 +460,9 @@ export function safeApplicationDataRestorePointsPayload(status, data) {
459
460
  return {
460
461
  success: true,
461
462
  points,
463
+ incompatible_points: boundedInteger(payload.incompatiblePoints, Number.MAX_SAFE_INTEGER),
464
+ failed_captures: boundedInteger(payload.failedCaptures, Number.MAX_SAFE_INTEGER),
465
+ last_failed_capture_at: timestamp(payload.lastFailedCaptureAt),
462
466
  quote,
463
467
  active_restore: payload.activeRestore === null
464
468
  ? null
@@ -729,6 +729,7 @@ function safeAcmeSubscriptionRenewal(value) {
729
729
  const renewalAt = text(source.renewal_at, 64);
730
730
  const state = [
731
731
  "awaiting_payment",
732
+ "processing",
732
733
  "prepaid",
733
734
  "settled",
734
735
  "refund_pending",
@@ -781,6 +782,7 @@ function safeAcmeSubscriptionAction(value) {
781
782
  "awaiting_payment",
782
783
  "queued",
783
784
  "submitting",
785
+ "processing",
784
786
  "reconciling",
785
787
  "reconciliation_required",
786
788
  "succeeded",
@@ -1203,6 +1205,15 @@ function safeFreeTimelineEvent(value) {
1203
1205
  }
1204
1206
  return { event, actor: source.actor, created_at: createdAt };
1205
1207
  }
1208
+ // Mirrors FreeCertificateFailureCode::all(); never pass stored/internal CA prose.
1209
+ const freeCertificateFailureReasons = new Set([
1210
+ "dns_validation_failed", "rate_limited", "validation_unauthorized",
1211
+ "validation_record_mismatch", "caa_forbids_issuance", "hostname_rejected",
1212
+ "validation_connection_failed", "csr_rejected", "authority_unavailable",
1213
+ "authority_protocol_error", "request_malformed", "authority_account_problem",
1214
+ "dns_name_not_found", "dns_propagation_pending", "authority_credentials_changed",
1215
+ "issuance_failed",
1216
+ ]);
1206
1217
  function safeFreeRequest(value) {
1207
1218
  const source = record(value);
1208
1219
  const id = freeCertificateRequestIdSchema.safeParse(source.id);
@@ -1219,6 +1230,11 @@ function safeFreeRequest(value) {
1219
1230
  const authorityName = source.certificate_authority === null
1220
1231
  ? null
1221
1232
  : text(source.certificate_authority, 64);
1233
+ const failureReason = source.failure_reason == null
1234
+ ? null
1235
+ : typeof source.failure_reason === "string" && freeCertificateFailureReasons.has(source.failure_reason)
1236
+ ? source.failure_reason
1237
+ : "issuance_failed";
1222
1238
  const deliveryOrderId = nullableInteger(source.delivery_order_id);
1223
1239
  const timeline = source.timeline === undefined
1224
1240
  ? undefined
@@ -1271,6 +1287,8 @@ function safeFreeRequest(value) {
1271
1287
  not_before: timestamp(source.not_before),
1272
1288
  not_after: timestamp(source.not_after),
1273
1289
  renewal_due_at: timestamp(source.renewal_due_at),
1290
+ failure_reason: failureReason,
1291
+ failure_at: failureReason === null ? null : timestamp(source.failure_at),
1274
1292
  certificate_authority_id: authorityId !== null && authorityId.success ? authorityId.data : null,
1275
1293
  certificate_authority: authorityName,
1276
1294
  download_available: source.download_available,
@@ -262,6 +262,12 @@ export function safeCustomProjectInstallPayload(status, data, portalPath) {
262
262
  },
263
263
  };
264
264
  }
265
+ const CUSTOM_PROJECT_VALIDATION_ERROR_CODES = new Set([
266
+ "applicationCustomProjectValidationFailed",
267
+ "applicationCustomProjectValidationDispatchFailed",
268
+ "applicationCustomProjectValidationExpired",
269
+ "applicationCustomProjectValidationTimedOut",
270
+ ]);
265
271
  export function safeCustomProjectValidationPayload(status, data) {
266
272
  const payload = record(data);
267
273
  const validation = record(payload.validation);
@@ -273,7 +279,10 @@ export function safeCustomProjectValidationPayload(status, data) {
273
279
  };
274
280
  }
275
281
  const valid = validation.valid === true;
276
- const resolvedCompose = valid && typeof validation.resolved_compose_yaml === "string"
282
+ // Keep the caller's resolved definition while the authoritative worker is
283
+ // pending, so a later GET can complete this exact validation without a new POST.
284
+ const pending = validation.state === "queued" || validation.state === "awaiting_reply";
285
+ const resolvedCompose = (valid || pending) && typeof validation.resolved_compose_yaml === "string"
277
286
  ? validation.resolved_compose_yaml.slice(0, 262144)
278
287
  : null;
279
288
  const resolutions = valid && Array.isArray(validation.image_resolutions)
@@ -295,6 +304,12 @@ export function safeCustomProjectValidationPayload(status, data) {
295
304
  validation: {
296
305
  id: boundedString(validation.id, 64),
297
306
  state: boundedString(validation.state, 32),
307
+ compose_digest: typeof validation.compose_digest === "string"
308
+ && /^[a-f0-9]{64}$/.test(validation.compose_digest)
309
+ ? validation.compose_digest : null,
310
+ error_code: typeof validation.error_code === "string"
311
+ && CUSTOM_PROJECT_VALIDATION_ERROR_CODES.has(validation.error_code)
312
+ ? validation.error_code : null,
298
313
  valid: typeof validation.valid === "boolean" ? validation.valid : null,
299
314
  errors: Array.isArray(validation.errors)
300
315
  ? validation.errors