vpsnet-mcp 2.3.16 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## Features
6
6
 
7
- - **280+ tools** covering VPSNet service management, managed applications, free and paid TLS certificates, Automatic SSL subscriptions, DNS, domains, billing, API keys, and account operations
7
+ - **<!-- tool-count -->371<!-- /tool-count --> tools** (local package; the hosted connector offers a subset) covering VPSNet service management, networking (private networks, server firewall, port forwarding, public addresses, load balancers, VPN), managed applications, free and paid TLS certificates, Automatic SSL subscriptions, DNS, domains, billing, API keys, and account operations
8
8
  - Account & profile management
9
9
  - VPS lifecycle (start, stop, restart, reinstall OS)
10
10
  - Plan changes (free upgrades/downgrades; KVM/Firecracker disks cannot shrink)
@@ -20,7 +20,7 @@
20
20
 
21
21
  ## Remote OAuth connector
22
22
 
23
- The remote profile also includes scoped DNS, domain and certificate management, public SSH keys, server recovery and backup browsing. It uses the same customer APIs as the panel; permissions are enforced by the backend on every request.
23
+ The remote profile also includes scoped DNS, domain and certificate management, public SSH keys, backup browsing and, while VPSnet offers it, server rescue. It uses the same customer APIs as the panel; permissions are enforced by the backend on every request.
24
24
 
25
25
  The remote connector uses `https://mcp.vpsnet.com/mcp` and customer consent through
26
26
  VPSnet. See [Remote AI connections](docs/REMOTE_MCP.md) for connection setup,
@@ -307,7 +307,7 @@ This MCP server manages your VPS infrastructure through the VPSnet.com API, incl
307
307
 
308
308
  Most AI coding environments (Claude Code, Cline, Cursor, Codex) have built-in terminal access, so the AI can SSH into your VPS immediately after deploying a key — no extra tools needed.
309
309
 
310
- For environments without native SSH access, pair this with [mcp-server-ssh](https://github.com/bacarrdy/mcp-server-ssh) for direct server connectivity via MCP tools.
310
+ For environments without native SSH access, pair this with [mcp-server-ssh](https://github.com/bacarrdy/mcp-ssh-server) for direct server connectivity via MCP tools.
311
311
 
312
312
  **Combined config:**
313
313
 
@@ -628,8 +628,13 @@ Every request is sent with the `X-API-KEY` header (`VPSNET_API_KEY`).
628
628
  - **Granular scopes only narrow a key.** A key with an empty scope list keeps
629
629
  everything its access level allows; listing scopes (for example `dns:read`,
630
630
  `services:manage`) restricts it. `X:manage` includes `X:read`. Networking is the
631
- exception: `networking:read` / `networking:manage` / `networking:order` are never
632
- implied by `full` or `read` and must be granted explicitly.
631
+ exception: `networking:read` / `networking:manage` / `networking:order`,
632
+ `files:read` / `files:manage` (service file manager) and the App Cloud permissions
633
+ `projects:*` / `projectdb:*` are never implied by `full` or `read` and must be added
634
+ to the key explicitly. A key created
635
+ without a permission list (for example an older `full` key) reaches no network
636
+ function (private networks, the server firewall, port forwarding, public addresses,
637
+ load balancers, VPN) until `networking:read` or `networking:manage` is added to it.
633
638
  - **Paid operations are a separate gate.** A tool marked `Paid` or `Quote` in the
634
639
  tables below needs a `full`-access key, `paid_operations_enabled`, the named paid
635
640
  scope (`vps:order`, `vds:order`, `ds:order`, `fc:order`, `domains:order`,
@@ -639,9 +644,11 @@ Every request is sent with the `X-API-KEY` header (`VPSNET_API_KEY`).
639
644
  an uncertain attempt, never a new one). Orders run quote, then confirm with the
640
645
  returned `quoteToken`; disclose the exact EUR total to the user before confirming.
641
646
  Use `list_api_keys` to see your key's scopes, paid scopes, caps and rate limit.
642
- - **Rate limits.** Requests are rate-limited per key (`rate_limit` in
643
- `list_api_keys`, requests per minute). On HTTP `429` back off and retry after
644
- `retry_after` seconds instead of retrying immediately.
647
+ - **Rate limits.** Each key has one request budget: 300 requests per 60 seconds by
648
+ default, and some DNS and domain operations have lower limits. On HTTP `429` wait
649
+ the `retry_after` seconds of the result (the API also sends `Retry-After`;
650
+ `X-RateLimit-Limit` / `-Remaining` show the budget) instead of retrying immediately. (The `rate_limit` value shown by
651
+ `list_api_keys` is stored with the key but is not what the limiter applies.)
645
652
  - API keys can never create, change or revoke API keys, and never receive admin
646
653
  access.
647
654
 
@@ -707,6 +714,8 @@ everything else.
707
714
  | `confirm_application_compose_adoption` | Confirm the exact source takeover after explicit approval | `applications:manage` | free |
708
715
  | `install_application` | Queue a confirmed, version-pinned managed installation | `applications:manage` | free |
709
716
  | `configure_application_access` | Queue a confirmed platform-hostname, private, public-IP, managed-HTTPS, or customer-managed external-HTTPS access change | `applications:manage` | free |
717
+ | `get_application_server_routes` | Read customer-hostname server routes, fallback, revision, per-route state and DNS status | `applications:read` | free |
718
+ | `configure_application_server_routes` | Replace the complete confirmed set of customer-hostname routes to local services (and optional fallback web server) with automatic certificates | `applications:manage` | free |
710
719
  | `configure_application_resource_thresholds` | Replace confirmed non-enforcing application resource thresholds and reached/recovered email preference | `applications:manage` | free |
711
720
  | `manage_application` | Compatibility tool for older clients; use the individual actions below | `applications:manage` | free |
712
721
  | `reconcile_application` | Reconcile an application with its declared configuration | `applications:manage` | free |
@@ -741,12 +750,14 @@ everything else.
741
750
  | `get_rdns` | Get current rDNS records | `services:read` | free |
742
751
  | `change_rdns` | Change reverse DNS (PTR) record | `services:manage` | free |
743
752
  | `clear_rdns` | Clear a PTR override and restore the automatic value | `services:manage` | free |
744
- | `flush_iptables` | Flush iptables rules (useful when locked out) | `services:manage` | free |
753
+ | `flush_iptables` | Flush the iptables rules inside the server (useful when locked out by them); the server's public firewall is separate | `services:manage` | free |
745
754
  | `get_title` | Get the current service display title | `services:read` | free |
746
755
  | `change_title` | Change service display title | `services:manage` | free |
747
756
  | `toggle_ipv6` | Enable or disable IPv6 | `services:manage` | free |
748
757
  | `toggle_extra_settings` | Toggle ppp, fuse, tuntap, or nfs | `services:manage` | free |
749
758
  | `deploy_ssh_key` | Deploy an SSH key to a VPS; remote OAuth also requires `vm:exec` | `services:manage` | free |
759
+ | `list_service_ssh_keys` | List which account SSH keys are on a Cloud VPS or Firecracker VPS; local package only | `services:read` | free |
760
+ | `remove_service_ssh_key` | Remove one account SSH key from a server's root authorized_keys; local package only | `services:manage` | free |
750
761
 
751
762
  ### OS Reinstall
752
763
  | Tool | Description | Scope | Paid |
@@ -799,6 +810,28 @@ everything else.
799
810
  | `browse_restore_files` | Queue a backup directory listing job without restoring files | `services:read` | free |
800
811
  | `get_restore_file_browse` | Poll a queued backup directory listing (free, read-only) | `services:read` | free |
801
812
 
813
+ ### Service files
814
+ Firecracker servers need the updated guest agent and Cloud VPS (KVM) the VDS file-manager switch; `files:read` / `files:manage` are explicit grants (an empty permission list does not include them).
815
+
816
+ | Tool | Description | Scope | Paid |
817
+ |------|-------------|-------|------|
818
+ | `get_service_file_capabilities` | Check whether the file manager can serve a server now, with the verbatim unavailable reason, limits and transfer mode | `files:read` | free |
819
+ | `list_service_files` | List one directory page inside a running server | `files:read` | free |
820
+ | `stat_service_file` | Describe one entry, optionally with its SHA-256 | `files:read` | free |
821
+ | `read_service_file` | View a text file (up to 1 MiB) or an image (up to 4 MiB), or read a 1 MiB window of any file | `files:read` | free |
822
+ | `search_service_files` | Find entries by name (substring or glob) below a directory | `files:read` | free |
823
+ | `get_service_file_download_url` | Mint a one-file download URL bound to the caller's address | `files:read` | free |
824
+ | `download_service_file` | Download a server file to a local path on the MCP machine, size-checked, with its SHA-256 | `files:read` | free |
825
+ | `get_service_file_operation` | Poll a file operation that answered 202 (slow guest, recursive delete, extraction) | `files:read` | free |
826
+ | `create_service_directory` | Create a directory, optionally with missing parents | `files:manage` | free |
827
+ | `move_service_file` | Rename or move one entry (confirmation required) | `files:manage` | free |
828
+ | `delete_service_files` | **Destructive:** permanently delete files or directories (confirmation required) | `files:manage` | free |
829
+ | `write_service_file` | Write a small file (up to 1 MiB) with compare-and-swap | `files:manage` | free |
830
+ | `upload_service_file` | Upload a local file or base64 content in one call: chunked, resumable transfer, atomic commit and SHA-256 check | `files:manage` | free |
831
+ | `list_service_file_uploads` | List the service's open uploads | `files:manage` | free |
832
+ | `abort_service_file_upload` | Abort one open upload and remove its staging file | `files:manage` | free |
833
+ | `extract_service_file_archive` | Firecracker: extract a zip/tar/tar.gz/.gz archive into a new directory | `files:manage` | free |
834
+
802
835
  ### SSH Keys
803
836
  | Tool | Description | Scope | Paid |
804
837
  |------|-------------|-------|------|
@@ -929,7 +962,7 @@ short-lived quote token.
929
962
  | `list_service_dns_options` | List owned zones and service addresses available for DNS attachment | `dns:read` | free |
930
963
  | `attach_service_dns_record` | Point an owned-zone name at one service address | `dns:write` | free |
931
964
  | `list_dns_zones` | List forward DNS zones owned by the account | `dns:read` | free |
932
- | `create_dns_zone` | Create a native or secondary zone awaiting ownership TXT verification | `dns:write` | free |
965
+ | `create_dns_zone` | Create a zone; qualifying ownership may restore and publish it. Hosted: native only; local stdio: native or secondary | `dns:write` | free |
933
966
  | `get_dns_zone` | Get desired records with IDs and a recoverable ownership TXT value | `dns:read` | free |
934
967
  | `reapply_dns_zone` | Retry publication of a published DNS zone whose apply_state is failed. | `dns:write` | free |
935
968
  | `get_dns_zone_diagnostics` | Inspect delegation, SOA, DNSSEC, and record hygiene | `dns:read` | free |
@@ -966,7 +999,7 @@ short-lived quote token.
966
999
  ### Service Rescue
967
1000
  | Tool | Description | Scope | Paid |
968
1001
  |------|-------------|-------|------|
969
- | `get_service_rescue` | Get rescue capability and current durable rescue session | `services:read` | free |
1002
+ | `get_service_rescue` | Get rescue capability and current durable rescue session; rescue is offered only while VPSnet has it switched on | `services:read` | free |
970
1003
  | `enter_service_rescue` | Restart an eligible service into an advertised rescue image | `services:rescue` | free |
971
1004
  | `exit_service_rescue` | Restore the exact pre-rescue boot configuration and state | `services:rescue` | free |
972
1005
 
@@ -1032,7 +1065,8 @@ resource. DNS changes are polled through the network's `settings.dns_change`.
1032
1065
  New private/public load balancers, VPNs and public addresses use an explicit
1033
1066
  quote → common checkout → order-status flow. Quote tools always send
1034
1067
  `prepare_after_payment=true`: no provisioning happens before payment.
1035
- Show the quote's exact EUR `gross_amount`, obtain approval, and pass the same
1068
+ Choose `period_months` only from the live catalog's offered terms (omission is one month).
1069
+ Show the quoted term and exact EUR `gross_amount`, obtain approval, and pass the same
1036
1070
  `idempotencyKey`, `quote_id` and `quoteToken` to `order_network_product`.
1037
1071
  API keys additionally need full access, enabled paid operations,
1038
1072
  `networking:order`, and spend limits. Read `get_network_payment_methods` and
@@ -1060,12 +1094,69 @@ not an alternative to checkout.
1060
1094
  Device configuration pickup is a one-time **management** action, even though
1061
1095
  its HTTP method is GET. Generate the reveal key pair outside MCP, submit only
1062
1096
  the X25519 public key, save the returned sealed ciphertext, and decrypt it
1063
- outside the conversation. Neither private keys nor plaintext WireGuard
1064
- configuration are requested or decrypted by these tools. Never automatically
1097
+ outside the conversation. Neither private keys, plaintext WireGuard configuration nor IPsec PSKs are
1098
+ requested or decrypted by these tools. Router recipes retain a literal PSK
1099
+ placeholder; fill it only in the customer's external configuration workflow. Never automatically
1065
1100
  repeat a pickup after an uncertain response.
1066
1101
 
1067
- The current backend has no customer network rename, existing-port fixed-IP
1068
- edit, appliance retry, or native site-to-site API in this release. Fixed DHCP
1102
+ Devices support WireGuard or the operating system's built-in IKEv2 client when
1103
+ `capabilities.device_ikev2` is available. Connected locations support WireGuard
1104
+ or IPsec IKEv2; read `capabilities.site_ipsec` and `ipsec_profiles` before choosing
1105
+ site protocol, vendor or cipher mode. A listed tool does not prove an older
1106
+ gateway image supports that protocol. Read router recipes with
1107
+ `get_network_vpn_peer_recipe`; don't use a different vendor profile than the
1108
+ one used to create the peer unless the returned API guidance supports it.
1109
+ For IPsec, connected means an established SA and the handshake timestamp is a
1110
+ CHILD-SA rekey; WireGuard uses its recent-handshake status.
1111
+
1112
+ Device `grant_scope=network` authorizes eligible current and future servers in
1113
+ the attached private network, still subject to network security policy. Obtain
1114
+ that broader authorization explicitly. Selected-server access uses
1115
+ `grant_scope=servers`; replacing grants without a scope also selects servers,
1116
+ so inspect and preserve the intended scope when editing an existing device.
1117
+
1118
+ Site health webhooks use `list_network_vpn_site_health_webhooks` with
1119
+ `networking:read` and `create_network_vpn_site_health_webhook` or
1120
+ `delete_network_vpn_site_health_webhook` with `networking:manage`. The list
1121
+ shows only the public destination host. Creation needs a caller-generated
1122
+ X25519 public reveal key and returns the signing secret only in a sealed
1123
+ envelope for external decryption. The subscription and removal are immediate
1124
+ resources, with no operation ID or idempotency key. After an uncertain create,
1125
+ list the site's subscriptions before deciding whether to create another.
1126
+
1127
+ NAT port forwarding publishes TCP or UDP ports of a private network's NAT public address on
1128
+ servers of that network. Read `get_network_port_forwards` first (rules, the router generation, per-rule
1129
+ exposure and eligible targets); `set_network_port_forwards` replaces the complete rule set at that
1130
+ generation, and an empty list withdraws every rule. A target is a Firecracker server of the network: one
1131
+ without a public IPv4 of its own, or the private interface of one that has its own public IPv4 (its replies
1132
+ go back through the network; the target's `reply_route` says whether that is in place). Cloud VPS servers
1133
+ are not targets. Security groups still decide what a server accepts.
1134
+ Poll the rules resource until `applied_generation` equals `generation`.
1135
+
1136
+ The firewall of a server's own public interface (VPS and Cloud VPS, IPv4 and IPv6 incoming allow
1137
+ rules) is read with `get_service_public_firewall` and replaced as a whole with
1138
+ `set_service_public_firewall` at the read `generation`; `apply_service_public_firewall` sends the
1139
+ saved rules to the node again. Keep the management port (SSH, or RDP on Windows) open for the user's
1140
+ own address and ask the user for that address: `client_address` in the answer is only the address
1141
+ the API saw for the request. Over the hosted connector, network changes act only on servers selected for the connection (`service_not_selected` otherwise).
1142
+
1143
+ `set_public_address_outgoing_source` makes an additional address attached to a VPS's public interface
1144
+ the source of the server's new outgoing IPv4 connections (or switches back to the server's own IP);
1145
+ `recheck_public_address_outgoing_source` asks a server again after an address left it.
1146
+ Reverse DNS of an additional address, a floating binding, a VPN's or a public load balancer's
1147
+ address uses `get_public_ip_rdns`, `set_public_ip_rdns` and `reset_public_ip_rdns` with a `target`
1148
+ field; a server's own primary address keeps `get_rdns` / `change_rdns`.
1149
+ `rebuild_network_load_balancer` rebuilds a public load balancer's proxies one at a time.
1150
+
1151
+ An owned public address can also go directly onto the public interface of a server that has its own
1152
+ public IPv4 (`attach_public_address_to_server_interface`), only while `get_networking_capabilities`
1153
+ reports `public_address_server_public` as true; the tool reads that first and refuses without a change
1154
+ otherwise. Pick the server from `list_public_address_server_targets`, then follow the attachment on
1155
+ `get_network_public_address` (`target.kind=server_public`,
1156
+ `carrier.node_state`, `carrier.guest_state`, `carrier.instruction`). When the server agent is off the
1157
+ instruction is what the customer runs inside the server; `check_network_public_address_reach` asks again.
1158
+
1159
+ This release exposes no customer network rename or existing-port fixed-IP edit. Fixed DHCP
1069
1160
  addresses are selected at attach; removing/recreating an attachment requires
1070
1161
  separate approval and is not represented as an in-place edit. Legacy unpaid
1071
1162
  LB/VPN create routes, legacy stock-before-payment purchases and enabling
@@ -1092,13 +1183,18 @@ feature gate.
1092
1183
  | `set_private_network_names` | Replace the complete internal DNS zone label and alias list | `networking:manage` | free |
1093
1184
  | `list_private_network_ports` | Read owned network ports, their fixed DHCP addresses, generations and attachment states | `networking:read` | free |
1094
1185
  | `list_service_private_ports` | Read private ports and order attachment recovery state for one owned service order number. | `networking:read` | free |
1186
+ | `get_service_public_firewall` | Read the firewall of a server's public interface (VPS or Cloud VPS): rules, generation and state | `networking:read` | free |
1187
+ | `set_service_public_firewall` | Switch the server's public firewall on or off and replace all its incoming rules at the read generation | `networking:manage` | free |
1188
+ | `apply_service_public_firewall` | Send the saved firewall rules to the server's node again | `networking:manage` | free |
1095
1189
  | `list_network_attachment_candidates` | Read attachable services and each attachment_mode | `networking:read` | free |
1096
1190
  | `attach_private_network_port` | Attach an owned service; omit ipv4 for automatic allocation or pass a fixed usable address | `networking:manage` | free |
1097
1191
  | `detach_private_network_port` | Detach the owned server port | `networking:manage` | free |
1098
1192
  | `retry_private_network_port` | Retry the failed current port operation with the PORT generation and any required restart approval. | `networking:manage` | free |
1099
- | `get_network_vpn_summary` | Read the network's VPN summary, including truthful absence | `networking:read` | free |
1193
+ | `get_network_vpn_summary` | Read the network's VPN summary, including truthful absence; connected locations are managed on the gateway | `networking:read` | free |
1100
1194
  | `get_network_routed_egress` | Read this network's NAT/internet access, public address and qualification state. | `networking:read` | free |
1101
1195
  | `set_network_routed_egress` | Request the currently supported NAT transition for an owned network; the backend may refuse mode changes | `networking:manage` | free |
1196
+ | `get_network_port_forwards` | Read the NAT port forwarding rules of an owned private network: applied rules (protocol, external_port on the network's NAT public address, the target server's private_port_id and its internal_port), the router generation to send to set_network_port_forwards (not the network generation), state (empty, pending, active, failed), per-rule exposure (open_to_internet is true when the server's security groups admit that port from every IPv4 address), targets with eligible and unavailable_reason_code, capability (available, limit, protocols), last_change (a refused change with its nat_port_forward_refused_* reason_code) and suspended (rules kept while the network has no NAT address; they are on no router). | `networking:read` | free |
1197
+ | `set_network_port_forwards` | Replace the COMPLETE NAT port forwarding rule set of an owned network (rules: [] withdraws all of them) at the router generation read from get_network_port_forwards. | `networking:manage` | free |
1102
1198
  | `get_security_group_capabilities` | Read security-group availability and whether the network default policy can be changed. | `networking:read` | free |
1103
1199
  | `list_network_security_groups` | List groups in the owned project | `networking:read` | free |
1104
1200
  | `create_network_security_group` | Create a named allow-rule set | `networking:manage` | free |
@@ -1122,18 +1218,27 @@ feature gate.
1122
1218
  | `remove_network_load_balancer_member` | Remove a member from rotation using the load balancer generation. | `networking:manage` | free |
1123
1219
  | `add_network_load_balancer_listener` | Add a listener and optional initial members using currently offered protocol capabilities | `networking:manage` | free |
1124
1220
  | `remove_network_load_balancer_listener` | Remove a listener and its members; deleting the last listener is refused. | `networking:manage` | free |
1221
+ | `rebuild_network_load_balancer` | Rebuild every proxy of a public load balancer, one at a time, keeping its address and listeners | `networking:manage` | free |
1125
1222
  | `list_network_vpn_gateways` | List owned VPN gateways, their device peers and safe status | `networking:read` | free |
1126
- | `get_network_vpn_gateway` | Read one owned VPN gateway, current generation, device peers, grant candidates and attachment capability. | `networking:read` | free |
1223
+ | `get_network_vpn_gateway` | Read one owned VPN gateway, protocol-specific peer status, IPsec capabilities/profiles, device grant scopes and site options | `networking:read` | free |
1127
1224
  | `delete_network_vpn_gateway` | Request teardown of an owned VPN gateway; all devices lose connectivity | `networking:manage` | free |
1128
1225
  | `attach_network_vpn_gateway` | Attach or move an account VPN to an owned network only when standalone attachment is advertised | `networking:manage` | free |
1129
1226
  | `detach_network_vpn_gateway` | Detach an account VPN from its network when supported; it keeps its paid period and devices but recreates the gateway | `networking:manage` | free |
1130
1227
  | `set_vpn_server_access` | Choose accept or groups_only for the VPN's access to attached servers when this capability is available | `networking:manage` | free |
1131
- | `create_network_vpn_peer` | Add a device with an externally generated one-time PUBLIC reveal key | `networking:manage` | free |
1228
+ | `create_network_vpn_peer` | Add a device, or a connected location (site-to-site office router), with an externally generated one-time PUBLIC reveal key | `networking:manage` | free |
1132
1229
  | `take_network_vpn_peer_config` | Consume the device's sealed configuration ONCE | `networking:manage` | free |
1133
1230
  | `revoke_network_vpn_peer` | Revoke one device using the VPN GATEWAY generation | `networking:manage` | free |
1134
- | `set_network_vpn_peer_grants` | Replace the device's complete reachable-server list when standalone VPN is available | `networking:manage` | free |
1231
+ | `set_network_vpn_peer_grants` | Replace a device or site's complete individual-server or network-wide access choice | `networking:manage` | free |
1135
1232
  | `get_network_vpn_egress_options` | Read supported VPN Internet exits and qualification | `networking:read` | free |
1136
1233
  | `set_network_vpn_peer_egress` | Set a device's private-only, inherited NAT, or advertised chosen exit policy | `networking:manage` | free |
1234
+ | `get_network_vpn_peer_recipe` | Read public IPsec router instructions with a PSK placeholder; no secret retrieval | `networking:read` | free |
1235
+ | `get_network_vpn_peer_history` | Read a device's or connected location's traffic and handshake history (24 h raw, 7 d, 30 d, 13 months) | `networking:read` | free |
1236
+ | `set_network_vpn_site_options` | Switch hub-and-spoke between the locations connected to a VPN | `networking:manage` | free |
1237
+ | `set_network_vpn_site_alert` | Switch the office-down e-mail of one connected location | `networking:manage` | free |
1238
+ | `list_network_vpn_site_health_webhooks` | List a site's signed health subscriptions and last delivery outcomes; URLs show public hosts only | `networking:read` | free |
1239
+ | `create_network_vpn_site_health_webhook` | Create a signed site health subscription with an external X25519 reveal public key; returns sealed signing material | `networking:manage` | free |
1240
+ | `delete_network_vpn_site_health_webhook` | Remove a site's subscription and cancel pending deliveries | `networking:manage` | free |
1241
+ | `set_network_site_route_mode` | Choose DHCP routes or an internal router for a network's routes to connected locations | `networking:manage` | free |
1137
1242
  | `list_network_products` | Read current paid products, live tier names, prices and purchase capability; public proxy load balancer 3/5-node offerings must come from this catalog. | `networking:read` | free |
1138
1243
  | `list_network_product_periods` | Read owned product periods, renewal availability and lifecycle facts. | `networking:read` | free |
1139
1244
  | `quote_network_product` | Create the network product's exact paid quote for COMMON CHECKOUT | `networking:manage` | Quote: `networking:order` |
@@ -1152,6 +1257,14 @@ feature gate.
1152
1257
  | `quote_network_public_address` | Quote a new paid public address for common checkout; no charge | `networking:manage` | Quote: `networking:order` |
1153
1258
  | `attach_public_address_to_network` | Attach or move an already-paid address to an owned network's NAT | `networking:manage` | free |
1154
1259
  | `attach_public_address_to_server` | Attach or move an owned paid public address to an eligible zero-public-IP server's private port | `networking:manage` | free |
1260
+ | `list_public_address_server_targets` | Search and page the account's servers as targets for attach_public_address_to_server_interface. | `networking:read` | free |
1261
+ | `attach_public_address_to_server_interface` | Attach an owned paid public address directly to the public interface of a server that has its own public IPv4 (service_id from list_public_address_server_targets with eligible true). | `networking:manage` | free |
1262
+ | `check_network_public_address_reach` | Ask again whether an owned public address attached to a server's public interface is reachable from the internet, and ask the server's own step again (for example after the server agent was started). | `networking:manage` | free |
1263
+ | `set_public_address_outgoing_source` | Use an additional address on a VPS's public interface for the server's new outgoing IPv4 connections, or switch back | `networking:manage` | free |
1264
+ | `recheck_public_address_outgoing_source` | Ask a server again to put its outgoing source back on its own IP | `networking:manage` | free |
1265
+ | `get_public_ip_rdns` | Read the reverse DNS of an additional, floating, VPN or load-balancer address (`target`) | `networking:read` | free |
1266
+ | `set_public_ip_rdns` | Set the reverse DNS hostname of such an address at its revision | `networking:manage` | free |
1267
+ | `reset_public_ip_rdns` | Remove the custom reverse DNS of such an address | `networking:manage` | free |
1155
1268
  | `detach_network_public_address` | Detach an owned public address from its current target; it stays owned for the paid period | `networking:manage` | free |
1156
1269
  | `quote_public_address_renewal` | Quote the address's next month without charging; use the returned quote and this key for confirmation. | `networking:manage` | Quote: `networking:order` |
1157
1270
  | `renew_network_public_address` | Pay the approved address renewal from balance with its exact quote ID/token and original key. | `networking:manage` | Paid: `networking:order` |
@@ -1159,9 +1272,32 @@ feature gate.
1159
1272
  | `get_network_public_binding` | Read one existing owned public binding's generation, state and assigned address | `networking:read` | free |
1160
1273
  | `bind_network_public_ip` | Bind an already reserved floating public binding to an owned private port when supported | `networking:manage` | free |
1161
1274
  | `move_network_public_ip` | Move a prepared floating public binding to another owned private port; confirm interruption to the old target. | `networking:manage` | free |
1162
- | `configure_network_public_forwarding` | Configure an existing reserved forwarding binding's TCP/UDP listeners | `networking:manage` | free |
1163
1275
  | `delete_network_public_binding` | Request deletion of an existing binding using its generation | `networking:manage` | free |
1164
1276
 
1277
+ ### VPN gateways and sites (more)
1278
+ | Tool | Description | Scope | Paid |
1279
+ |------|-------------|-------|------|
1280
+ | `restart_network_vpn_gateway` | Restart the owned VPN VM in place. | `networking:manage` | free |
1281
+ | `rebuild_network_vpn_gateway` | Rebuild the owned VPN VM with its existing address and devices. | `networking:manage` | free |
1282
+ | `set_network_vpn_internet_exit` | Choose none, the VPN's own IPv4, or its attached network NAT as Internet exit. | `networking:manage` | free |
1283
+ | `take_network_vpn_site_line_config` | Consume the second IPsec line's sealed key exactly once with line=2. | `networking:manage` | free |
1284
+ | `rotate_network_vpn_ipsec_key` | Rotate an IPsec site's pre-shared key. | `networking:manage` | free |
1285
+ | `save_network_vpn_router_setup` | Save nonsecret backend-catalogue router version and local interface/zone fields for an IPsec site, optionally line 2. | `networking:manage` | free |
1286
+ | `set_network_vpn_site_bgp` | Enable or change BGP over an eligible IPsec site using the gateway generation. | `networking:manage` | free |
1287
+ | `add_network_vpn_site_line` | Add line 2 to an IPsec site using the gateway generation and an external X25519 public reveal key. | `networking:manage` | free |
1288
+ | `set_network_vpn_site_main_line` | Choose line 1 or 2 as the main IPsec line using the gateway generation; optional DPD applies to both. | `networking:manage` | free |
1289
+ | `remove_network_vpn_site_second_line` | Remove line 2 using the gateway generation. | `networking:manage` | free |
1290
+ | `start_network_vpn_tunnel_test` | Start a backend tunnel test for a site or supported WireGuard peer; optional line selects 1 or 2. | `networking:manage` | free |
1291
+ | `get_network_vpn_tunnel_test` | Read one owned tunnel test by its returned ID; while running, poll no faster than the backend's one-second guidance. | `networking:read` | free |
1292
+ | `set_network_vpn_site_reach` | Set this site's load-balancer/floating-IP reach choice, or null to follow its VPN default. | `networking:manage` | free |
1293
+
1294
+ | `rename_network_vpn_gateway` | Rename an owned VPN's customer label. | `networking:manage` | free |
1295
+ | `update_network_vpn_gateway_image` | Apply the backend-qualified reviewed VPN image in place. | `networking:manage` | free |
1296
+ | `check_network_vpn_public_access` | Preview the complete VPN public-source lists without applying them. | `networking:read` | free |
1297
+ | `set_network_vpn_public_access` | Replace the complete WireGuard/IPsec public-source lists at the VPN gateway generation. | `networking:manage` | free |
1298
+ | `set_network_vpn_ipsec_tuning` | Replace an IPsec site's advanced tunnel choices using the gateway generation. | `networking:manage` | free |
1299
+ | `quote_network_vpn_traffic_pack` | Quote a traffic pack for this owned VPN using an offered tier from get_network_vpn_gateway traffic. | `networking:manage` | Quote: `networking:order` |
1300
+
1165
1301
  <!-- tool-reference:end -->
1166
1302
 
1167
1303
  ## Worked examples
@@ -1239,12 +1375,19 @@ at the location given by `zone.verification.next_step`, then call
1239
1375
 
1240
1376
  ## Getting an API key
1241
1377
 
1242
- 1. Log in at [vpsnet.com](https://www.vpsnet.com)
1243
- 2. Go to **Account** > **API Keys**
1244
- 3. Click **Create API Key**
1245
- 4. Give it a name and copy the key
1378
+ 1. Log in at [vpsnet.com](https://www.vpsnet.com) and open **Account** > **API Keys**:
1379
+ <https://www.vpsnet.com/en/management/account/api-keys>
1380
+ 2. Click **Create API Key**
1381
+ 3. Give it a name, choose the access level and, for network tools, add `networking:read`
1382
+ or `networking:manage` (they are never included by default)
1383
+ 4. Copy the key
1384
+
1385
+ The key is shown only once — store it securely. The full API description is
1386
+ <https://www.vpsnet.com/openapi-v1.yml>.
1246
1387
 
1247
- The key is shown only once — store it securely.
1388
+ To use ChatGPT, Claude, Gemini or Grok without an API key, connect the hosted
1389
+ connector instead; connections are created and managed in **Account** > **AI connections**:
1390
+ <https://www.vpsnet.com/en/management/account/ai-connections>.
1248
1391
 
1249
1392
  ## Troubleshooting
1250
1393
 
@@ -1297,18 +1440,18 @@ Malformed or oversized URL identifiers are rejected before an API request.
1297
1440
  |---|---|---|
1298
1441
  | `aiScopedApiKeyCannotManageAccount` (403) | The key is an **AI-scoped key**. Those are issued only for VPSnet AI assistant inference and are deliberately refused on the entire account API. Granting scopes cannot change this — the restriction is on the key type. | Create a separate key with scope `full` (or `read` for GET-only use) and use that here. Keep the AI-scoped key for inference. |
1299
1442
  | `apiKeyRejected` (401) | The key failed authentication outright. The API returns one identical 401 for every cause, so it is one of: unknown, revoked, expired, malformed, or your source IP is not on the key's IP allowlist. | Check the key is active and copied whole, and that any IP allowlist includes the address this server calls from. |
1300
- | `readOnlyApiKeyCannotWrite` (401) | A read-scoped key was used for a write; read keys are limited to GET. | Use a full-scope key, or stay on read-only tools. |
1301
- | `apiKeyScopeMissing` (403) | The key authenticated but lacks the granular scope named in `requiredScope`. | Add that scope in Account > API Keys. |
1443
+ | `readOnlyApiKeyCannotWrite` (403) | A read-scoped key was used for a write; read keys are limited to GET. | Use a full-scope key, or stay on read-only tools. |
1444
+ | `apiKeyScopeMissing` (403) | The key authenticated but lacks the granular scope named in `requiredScope`. | Add that scope in Account > API Keys (https://www.vpsnet.com/en/management/account/api-keys). |
1302
1445
  | `apiKeyForbidden` (403) | The endpoint refuses API keys entirely. API keys never grant admin access. | Use the panel with a signed-in session. |
1303
1446
 
1304
1447
  - Verify the key is correct (starts with `vpsnet_` followed by 43 characters)
1305
1448
  - Keys are shown only once at creation — if lost, create a new one
1306
- - Check that the key hasn't expired (Account > API Keys)
1449
+ - Check that the key hasn't expired (Account > API Keys: https://www.vpsnet.com/en/management/account/api-keys)
1307
1450
 
1308
1451
  ### `deploy_ssh_key` succeeded but SSH still fails
1309
1452
 
1310
1453
  - All VPSnet.com servers use `root` as the SSH username
1311
- - Key deployment is async — wait 15-30 seconds after deploying before attempting SSH
1454
+ - Key deployment is asynchronous: follow the returned noty UUID with `wait_for_event` until it reports completed before attempting SSH
1312
1455
 
1313
1456
  ### `fetch is not defined` or unexpected errors
1314
1457
 
package/build/api.js CHANGED
@@ -166,6 +166,50 @@ semantics) {
166
166
  // real cause instead of an unactionable "Unauthorized".
167
167
  return { status: res.status, data: authorizationContext() ? data : annotateAuthFailure(res.status, data) };
168
168
  }
169
+ /**
170
+ * Send raw bytes (an upload chunk) to an account API route with the caller's
171
+ * credentials. Same base, path, credential, redirect and timeout rules as
172
+ * apiRequest; only the request body differs.
173
+ */
174
+ export async function apiRawRequest(method, path, bytes, extraHeaders = {}) {
175
+ validateApiPath(path);
176
+ const url = `${resolveApiBase()}${path}`;
177
+ const headers = {
178
+ Accept: "application/json",
179
+ "Content-Type": "application/octet-stream",
180
+ ...extraHeaders,
181
+ ...apiAuthorization(resolveApiKey),
182
+ };
183
+ const timeoutMs = resolveApiTimeoutMs();
184
+ const controller = new AbortController();
185
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
186
+ let res;
187
+ let text;
188
+ try {
189
+ res = await fetch(url, { method, headers, body: bytes, redirect: "error", signal: controller.signal });
190
+ text = await res.text();
191
+ }
192
+ catch (error) {
193
+ if (error instanceof Error && error.name === "AbortError") {
194
+ throw new Error(`VPSnet API request timed out after ${timeoutMs}ms: ${method} ${path}`);
195
+ }
196
+ throw error;
197
+ }
198
+ finally {
199
+ clearTimeout(timeout);
200
+ }
201
+ let data;
202
+ try {
203
+ data = text ? JSON.parse(text) : null;
204
+ }
205
+ catch {
206
+ return { status: res.status, data: res.ok ? { success: false, error: "The VPSnet API answered with a non-JSON body." }
207
+ : describeNonApiResponse(res.status, res.headers.get("content-type"), text, new URL(url).host, false) };
208
+ }
209
+ if (res.ok === false)
210
+ data = withHttpStatus(res.status, data, res.headers.get("retry-after"));
211
+ return { status: res.status, data: authorizationContext() ? data : annotateAuthFailure(res.status, data) };
212
+ }
169
213
  /**
170
214
  * Structured tool error for a non-2xx answer whose body is not JSON.
171
215
  */
@@ -1,3 +1,4 @@
1
+ import { unboundedInteger } from './tool-schema.js';
1
2
  import { z } from "zod";
2
3
  export const applicationLogTailLinesSchema = z
3
4
  .number()
@@ -32,7 +33,7 @@ export const applicationActionSchema = z
32
33
  .describe("Supported lifecycle action. Update and rollback apply the exact backend-advertised immutable release. Selective data restore is a separate revision-bound operation.");
33
34
  export const applicationActionIdSchema = z
34
35
  .string()
35
- .uuid()
36
+ .guid()
36
37
  .describe("Exact action UUID from the current installation latest_action.id");
37
38
  export const applicationExpectedVersionSchema = z
38
39
  .string()
@@ -82,7 +83,7 @@ const externalHttpsUrlSchema = z.string().max(500).refine((value) => {
82
83
  * app-additional-addresses-design-1005 section 2.3.
83
84
  */
84
85
  export const applicationAdditionalAddressSchema = z.object({
85
- zone_id: z.number().int().min(1),
86
+ zone_id: unboundedInteger().min(1),
86
87
  name: z
87
88
  .string()
88
89
  .max(190)
@@ -97,7 +98,7 @@ export const applicationSingleAccessSchema = z.discriminatedUnion("mode", [
97
98
  z.object({ mode: z.literal("external_https"), url: externalHttpsUrlSchema }).strict(),
98
99
  z.object({
99
100
  mode: z.literal("managed_https"),
100
- zone_id: z.number().int().positive(),
101
+ zone_id: unboundedInteger().positive(),
101
102
  subdomain: z
102
103
  .string()
103
104
  .max(190)
@@ -192,11 +193,11 @@ export function applicationResourceThresholdRequestBody(input) {
192
193
  }
193
194
  export const applicationDataRestorePointIdSchema = z
194
195
  .string()
195
- .uuid()
196
+ .guid()
196
197
  .describe("Opaque eligible backup-point UUID returned by list_application_restore_points");
197
198
  export const applicationDataRestoreIdSchema = z
198
199
  .string()
199
- .uuid()
200
+ .guid()
200
201
  .describe("Opaque selective restore UUID returned by restore_application_data");
201
202
  export const applicationDataRestoreQuoteTokenSchema = z
202
203
  .string()
@@ -17,7 +17,7 @@ function message(data) {
17
17
  const raw = record(data).message;
18
18
  return typeof raw === "string" ? raw : "";
19
19
  }
20
- const MANAGEMENT_KEY_FIX = "Create a management API key in the VPSnet panel under Account > API Keys "
20
+ const MANAGEMENT_KEY_FIX = "Create a management API key in the VPSnet panel under Account > API Keys (https://www.vpsnet.com/en/management/account/api-keys) "
21
21
  + "(scope 'full', or 'read' if you only need GET reads) and set it as "
22
22
  + "VPSNET_API_KEY. A key is 'vpsnet_' followed by 43 characters.";
23
23
  /**
@@ -74,8 +74,8 @@ export function explainAuthFailure(status, data) {
74
74
  ? `The API key authenticated successfully but is missing the '${required}' scope.`
75
75
  : "The API key authenticated successfully but is missing a scope this endpoint requires.",
76
76
  fix: required
77
- ? `Add the '${required}' scope to the key in Account > API Keys, or use a key that already has it.`
78
- : "Grant the endpoint's scope to the key in Account > API Keys.",
77
+ ? `Add the '${required}' scope to the key in Account > API Keys (https://www.vpsnet.com/en/management/account/api-keys), or use a key that already has it.`
78
+ : "Grant the endpoint's scope to the key in Account > API Keys (https://www.vpsnet.com/en/management/account/api-keys).",
79
79
  };
80
80
  }
81
81
  if (payload.apiKeyForbidden === true) {
@@ -105,7 +105,7 @@ export function explainAuthFailure(status, data) {
105
105
  + "not exist, was revoked, has expired, is malformed, or your source "
106
106
  + "IP is not on the key's IP allowlist. Note this is NOT the "
107
107
  + "AI-scoped-key case, which returns 403 with its own message.",
108
- fix: "Check the key in Account > API Keys: confirm it is active, unexpired, "
108
+ fix: "Check the key in Account > API Keys (https://www.vpsnet.com/en/management/account/api-keys): confirm it is active, unexpired, "
109
109
  + "copied whole, and that any IP allowlist on it includes the address "
110
110
  + "this server calls from. " + MANAGEMENT_KEY_FIX,
111
111
  };
@@ -113,7 +113,7 @@ export function explainAuthFailure(status, data) {
113
113
  return {
114
114
  code: "apiKeyForbiddenUnclassified",
115
115
  reason: "The account API refused this request for the authenticated key.",
116
- fix: "Check the key's scopes and access settings in Account > API Keys.",
116
+ fix: "Check the key's scopes and access settings in Account > API Keys (https://www.vpsnet.com/en/management/account/api-keys).",
117
117
  };
118
118
  }
119
119
  /**
@@ -1,11 +1,11 @@
1
1
  import { z } from "zod";
2
2
  export const customProjectIdSchema = z
3
3
  .string()
4
- .uuid()
4
+ .guid()
5
5
  .describe("Customer-owned recipe project UUID returned by list_application_recipes");
6
6
  export const composeAdoptionIdSchema = z
7
7
  .string()
8
- .uuid()
8
+ .guid()
9
9
  .describe("Opaque Compose adoption UUID returned by the prepare operation");
10
10
  export const composeProjectLabelSchema = z
11
11
  .string()
@@ -45,7 +45,7 @@ export const customProjectSecretNamesSchema = z
45
45
  .default([])
46
46
  .describe("Secret variable names only; definitions and exports never contain values");
47
47
  export const customProjectRegistryCredentialIdsSchema = z
48
- .array(z.string().uuid())
48
+ .array(z.string().guid())
49
49
  .max(8)
50
50
  .refine((ids) => new Set(ids).size === ids.length, {
51
51
  message: "Registry credential IDs must be unique",
@@ -0,0 +1,11 @@
1
+ /** Describes both creation paths in DnsZonesController::store, including restoration. */
2
+ export function createDnsZoneDescription(hosted = false) {
3
+ return [
4
+ hosted
5
+ ? "Create a native forward DNS zone in the connected VPSnet account. Secondary zones and their TSIG credentials must be configured in the VPSnet control panel; this hosted tool does not accept authentication secrets."
6
+ : "Create a native or secondary forward DNS zone in the authenticated VPSnet account. Secondary zones transfer records from public primary DNS servers and require TSIG.",
7
+ "Normally creates pending_verification. With qualifying current ownership proof, the API may instead restore a recently deleted zone and queue immediate publication. Publication can delete stale zone candidates for the same name, including their DNS records and DDNS tokens. Existing live zones are refused; this does not register a domain or change registrar nameservers.",
8
+ "Read the returned zone.status and apply_state: queued publication is not proof of live DNS service. When verification is needed, verificationRecord contains the public ownership TXT challenge (_vpsnet-dns.<zone>). Follow zone.verification.next_step and warnings for its location, then call verify_dns_zone. Keep nameservers already delegated to VPSnet. A previous-provider or parent-zone location may be required.",
9
+ "If the TXT value is missing, first read get_dns_zone.zone.verification.record.value; reissue_dns_zone_verification rotates it only when unrecoverable or requested by the customer. Requires dns:write.",
10
+ ].join(" ");
11
+ }
@@ -22,7 +22,7 @@ export const fileBrowsePointIdSchema = z
22
22
  .describe("Backup point ID returned by list_restore_file_points");
23
23
  export const fileBrowseIdSchema = z
24
24
  .string()
25
- .uuid()
25
+ .guid()
26
26
  .describe("Browse UUID returned by browse_restore_files");
27
27
  export const fileBrowseDirectoryEntryIdSchema = z
28
28
  .string()