vpsnet-mcp 2.1.0 → 2.1.1
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 +6 -0
- package/build/api-path.js +26 -0
- package/build/api.js +5 -0
- package/build/certificate-contract.js +7 -0
- package/build/file-restore-contract.js +1 -0
- package/build/index.js +240 -225
- package/build/temp-vm-contract.js +2 -0
- package/build/tool-result.js +49 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -897,6 +897,12 @@ This server needs a **management** API key. Every failure below is reported by
|
|
|
897
897
|
the tools with an `auth_problem` object containing the cause and the fix, so
|
|
898
898
|
read that rather than guessing from the HTTP status.
|
|
899
899
|
|
|
900
|
+
Tool failures carry `isError: true` while preserving the API error details and
|
|
901
|
+
recovery guidance. Unknown mutation outcomes must be checked before another
|
|
902
|
+
attempt. Service order numbers must be copied from `list_services`; ASCII
|
|
903
|
+
letters, digits, and hyphens are supported, including multi-order suffixes.
|
|
904
|
+
Malformed or oversized URL identifiers are rejected before an API request.
|
|
905
|
+
|
|
900
906
|
| What you see | What it means | Fix |
|
|
901
907
|
|---|---|---|
|
|
902
908
|
| `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. |
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Reject URL normalization and delimiter tricks before credentials or transport. */
|
|
2
|
+
export function validateApiPath(path) {
|
|
3
|
+
if (typeof path !== "string" ||
|
|
4
|
+
path.length > 8192 ||
|
|
5
|
+
!path.startsWith("/") ||
|
|
6
|
+
path.startsWith("//") ||
|
|
7
|
+
/[\\#\s\x00-\x1f\x7f]/u.test(path)) {
|
|
8
|
+
throw new Error("Invalid VPSnet API route.");
|
|
9
|
+
}
|
|
10
|
+
const pathname = path.split("?", 1)[0];
|
|
11
|
+
for (const segment of pathname.slice(1).split("/")) {
|
|
12
|
+
let decoded;
|
|
13
|
+
try {
|
|
14
|
+
decoded = decodeURIComponent(segment);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new Error("Invalid VPSnet API route encoding.");
|
|
18
|
+
}
|
|
19
|
+
if (!decoded ||
|
|
20
|
+
decoded === "." ||
|
|
21
|
+
decoded === ".." ||
|
|
22
|
+
/[/%\\?#\s\x00-\x1f\x7f]/u.test(decoded)) {
|
|
23
|
+
throw new Error("Invalid VPSnet API route segment.");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
package/build/api.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { annotateAuthFailure } from "./auth-failure.js";
|
|
2
2
|
import { createApiConfiguration } from "./api-configuration.js";
|
|
3
|
+
import { validateApiPath } from "./api-path.js";
|
|
3
4
|
const DEFAULT_API_TIMEOUT_MS = 45_000;
|
|
4
5
|
const configuration = createApiConfiguration(process.env, process.cwd());
|
|
5
6
|
const { keyFile: SIDECAR_KEY_FILE, mcpJson: PROJECT_MCP_JSON } = configuration.paths;
|
|
@@ -77,6 +78,10 @@ function resolveApiTimeoutMs() {
|
|
|
77
78
|
export async function apiRequest(method, path, body, extraHeaders,
|
|
78
79
|
// Explicitly annotate reviewed POST reads; other non-read methods fail closed.
|
|
79
80
|
semantics) {
|
|
81
|
+
if (!["GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH", "DELETE"].includes(method)) {
|
|
82
|
+
throw new Error("Invalid VPSnet API request method.");
|
|
83
|
+
}
|
|
84
|
+
validateApiPath(path);
|
|
80
85
|
const url = `${resolveApiBase()}${path}`;
|
|
81
86
|
const headers = {
|
|
82
87
|
"X-API-KEY": resolveApiKey(),
|
|
@@ -7,16 +7,19 @@ export const certificateProductIdSchema = z
|
|
|
7
7
|
.number()
|
|
8
8
|
.int()
|
|
9
9
|
.positive()
|
|
10
|
+
.max(Number.MAX_SAFE_INTEGER)
|
|
10
11
|
.describe("Published product ID returned by list_certificate_catalog");
|
|
11
12
|
export const certificateOfferIdSchema = z
|
|
12
13
|
.number()
|
|
13
14
|
.int()
|
|
14
15
|
.positive()
|
|
16
|
+
.max(Number.MAX_SAFE_INTEGER)
|
|
15
17
|
.describe("Published EUR offer ID returned by the selected catalog product");
|
|
16
18
|
export const certificateOfferGenerationSchema = z
|
|
17
19
|
.number()
|
|
18
20
|
.int()
|
|
19
21
|
.positive()
|
|
22
|
+
.max(Number.MAX_SAFE_INTEGER)
|
|
20
23
|
.describe("Exact offer generation returned by the catalog; stale generations are rejected");
|
|
21
24
|
export const certificateSubscriptionIdSchema = z
|
|
22
25
|
.string()
|
|
@@ -172,6 +175,7 @@ const freeCertificateTargetOrderIdSchema = z
|
|
|
172
175
|
.number()
|
|
173
176
|
.int()
|
|
174
177
|
.positive()
|
|
178
|
+
.max(Number.MAX_SAFE_INTEGER)
|
|
175
179
|
.nullable()
|
|
176
180
|
.optional()
|
|
177
181
|
.describe("Optional owned VPS or Cloud VPS order ID returned by service tools");
|
|
@@ -263,16 +267,19 @@ export const certificateOrderInputShape = {
|
|
|
263
267
|
.number()
|
|
264
268
|
.int()
|
|
265
269
|
.positive()
|
|
270
|
+
.max(Number.MAX_SAFE_INTEGER)
|
|
266
271
|
.describe("Owned administrator contact ID selected in the VPSnet certificate order form"),
|
|
267
272
|
technical_contact_id: z
|
|
268
273
|
.number()
|
|
269
274
|
.int()
|
|
270
275
|
.positive()
|
|
276
|
+
.max(Number.MAX_SAFE_INTEGER)
|
|
271
277
|
.describe("Owned technical contact ID selected in the VPSnet certificate order form"),
|
|
272
278
|
organization_contact_id: z
|
|
273
279
|
.number()
|
|
274
280
|
.int()
|
|
275
281
|
.positive()
|
|
282
|
+
.max(Number.MAX_SAFE_INTEGER)
|
|
276
283
|
.nullable()
|
|
277
284
|
.optional()
|
|
278
285
|
.describe("Owned organization contact ID when required for OV or EV validation"),
|