@runpod/mcp-server 4.2.0 → 4.4.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/dist/stdio.mjs CHANGED
@@ -1,22 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  authedGraphqlBase,
4
+ hostedMcpBase,
4
5
  publicGraphqlBase,
5
6
  sdkBase,
6
7
  serverlessBase
7
- } from "./chunk-DDX42UTT.mjs";
8
+ } from "./chunk-4GF3CMSH.mjs";
8
9
 
9
10
  // src/stdio.ts
10
11
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
12
 
12
13
  // src/server.ts
13
14
  var SERVER_NAME = "Runpod API Server";
14
- var SERVER_VERSION = true ? "4.2.0" : "dev";
15
+ var SERVER_VERSION = true ? "4.4.0" : "dev";
15
16
 
16
17
  // src/specgen/server.ts
17
18
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
18
19
  import {
19
- CallToolRequestSchema,
20
+ CallToolRequestSchema as CallToolRequestSchema2,
20
21
  ListResourcesRequestSchema,
21
22
  ListResourceTemplatesRequestSchema,
22
23
  ListToolsRequestSchema,
@@ -221,10 +222,13 @@ function validateArguments(inputSchema, args) {
221
222
  async function dispatchGeneratedTool(client, tool, args) {
222
223
  const rejected = validateArguments(tool.inputSchema, args);
223
224
  if (rejected) return rejected;
225
+ const required = new Set(tool.inputSchema.required ?? []);
224
226
  const pathParams = {};
225
227
  const query = {};
226
228
  for (const param of tool.params) {
227
229
  if (args[param.name] === void 0) continue;
230
+ if (param.location === "query" && !required.has(param.name) && isEmptyValue(args[param.name]))
231
+ continue;
228
232
  if (param.location === "path") {
229
233
  pathParams[param.name] = args[param.name];
230
234
  continue;
@@ -246,6 +250,11 @@ async function dispatchGeneratedTool(client, tool, args) {
246
250
  };
247
251
  }
248
252
  }
253
+ if (tool.hasBody && requestBody !== void 0) {
254
+ const properties = tool.inputSchema.properties ?? {};
255
+ const defs = tool.inputSchema.$defs ?? {};
256
+ requestBody = coerceScalars(requestBody, properties.body, defs);
257
+ }
249
258
  const verbs = client;
250
259
  const { data, error, response } = await verbs[tool.method](tool.path, {
251
260
  params: { path: pathParams, query },
@@ -258,6 +267,79 @@ async function dispatchGeneratedTool(client, tool, args) {
258
267
  payload: data ?? { status: response.status }
259
268
  };
260
269
  }
270
+ function isEmptyValue(value) {
271
+ return value === null || value === "" || Array.isArray(value) && !value.length;
272
+ }
273
+ function asSchema(value) {
274
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
275
+ }
276
+ function resolveSchema(schema, defs, depth = 0) {
277
+ const node = asSchema(schema);
278
+ if (!node || depth > 16) return node;
279
+ const ref = node.$ref;
280
+ if (typeof ref === "string" && ref.startsWith("#/$defs/"))
281
+ return resolveSchema(defs[ref.slice("#/$defs/".length)], defs, depth + 1);
282
+ return node;
283
+ }
284
+ function branches(schema, defs, depth = 0) {
285
+ const node = resolveSchema(schema, defs);
286
+ if (!node || depth > 16) return [];
287
+ const out = [node];
288
+ for (const key of ["allOf", "anyOf", "oneOf"]) {
289
+ const list = node[key];
290
+ if (Array.isArray(list))
291
+ for (const sub of list) out.push(...branches(sub, defs, depth + 1));
292
+ }
293
+ return out;
294
+ }
295
+ function types(schemas) {
296
+ const out = /* @__PURE__ */ new Set();
297
+ for (const s of schemas) {
298
+ if (typeof s.type === "string") out.add(s.type);
299
+ else if (Array.isArray(s.type)) {
300
+ for (const t of s.type) if (typeof t === "string") out.add(t);
301
+ }
302
+ }
303
+ return out;
304
+ }
305
+ var NUMERIC = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
306
+ function coerceScalars(value, schema, defs, depth = 0) {
307
+ if (depth > 32) return value;
308
+ const applicable = branches(schema, defs);
309
+ if (!applicable.length) return value;
310
+ const allowed = types(applicable);
311
+ if (typeof value === "string") {
312
+ if (allowed.has("string")) return value;
313
+ if ((allowed.has("integer") || allowed.has("number")) && NUMERIC.test(value)) {
314
+ const n = Number(value);
315
+ if (allowed.has("number") || Number.isInteger(n)) return n;
316
+ }
317
+ if (allowed.has("boolean") && (value === "true" || value === "false"))
318
+ return value === "true";
319
+ return value;
320
+ }
321
+ if (Array.isArray(value)) {
322
+ const items = applicable.map((s) => s.items).filter(Boolean);
323
+ if (!items.length) return value;
324
+ return value.map(
325
+ (item) => coerceScalars(item, { anyOf: items }, defs, depth + 1)
326
+ );
327
+ }
328
+ const object = asSchema(value);
329
+ if (!object) return value;
330
+ const out = {};
331
+ for (const [key, child] of Object.entries(object)) {
332
+ const childSchemas = [];
333
+ for (const s of applicable) {
334
+ const props = asSchema(s.properties);
335
+ if (props && key in props) childSchemas.push(props[key]);
336
+ else if (asSchema(s.additionalProperties))
337
+ childSchemas.push(s.additionalProperties);
338
+ }
339
+ out[key] = childSchemas.length ? coerceScalars(child, { anyOf: childSchemas }, defs, depth + 1) : child;
340
+ }
341
+ return out;
342
+ }
261
343
 
262
344
  // src/specgen/generated/tools.gen.ts
263
345
  var generatedTools = [
@@ -275,6 +357,7 @@ var generatedTools = [
275
357
  idempotentHint: false,
276
358
  openWorldHint: true
277
359
  },
360
+ schemaOnDemand: true,
278
361
  inputSchema: {
279
362
  type: "object",
280
363
  properties: {
@@ -286,11 +369,10 @@ var generatedTools = [
286
369
  $defs: {
287
370
  BaseContainerConfig: {
288
371
  type: "object",
289
- description: "Container configuration universal to every containerized resource. Compose ContainerConfig instead unless the resource cannot support a direct registry credential (clusters \u2014 there the registry credential arrives via a pod template, see CreateClusterRequest.templateId).\n",
290
372
  properties: {
291
373
  args: {
292
374
  type: "string",
293
- description: 'The container\'s command, as a single raw string. This is the field `entrypoint` and `cmd` encode into, exposed in its stored form.\n\nTwo shapes are accepted. A bare shell string is treated as CMD and split into arguments, which is what the console\'s "Container start command" field writes. A JSON object of the form `{"entrypoint":[...],"cmd":[...]}` sets either or both explicitly.\n\nResponses always return both representations: `args` exactly as stored, plus the deconstructed `entrypoint` and `cmd`. Supplying `args` together with `entrypoint` or `cmd` is allowed only when they describe the same command, so a read-modify-write client can send back everything it received. Send `""` to clear, omit to leave unchanged.\n',
375
+ description: 'The container\'s command as one raw string: a bare shell string is treated as CMD and split into arguments; a JSON object `{"entrypoint":[...],"cmd":[...]}` sets either or both. Send `""` to clear; omit to leave unchanged.',
294
376
  examples: [
295
377
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
296
378
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -302,7 +384,7 @@ var generatedTools = [
302
384
  items: {
303
385
  type: "string"
304
386
  },
305
- description: "Container CMD in exec form. When the image defines an ENTRYPOINT, this is the argument list passed to it. Encoded into the `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.\n",
387
+ description: "Container CMD in exec form: when the image defines an ENTRYPOINT, the argument list passed to it. Encoded into `args`; supply both only when they describe the same command. Send `[]` to clear; omit to leave unchanged.",
306
388
  examples: [
307
389
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
308
390
  ]
@@ -312,7 +394,7 @@ var generatedTools = [
312
394
  items: {
313
395
  type: "string"
314
396
  },
315
- description: "Container ENTRYPOINT in exec form, overriding the image's own. Encoded into `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.\n",
397
+ description: "Container ENTRYPOINT in exec form, overriding the image's own. Encoded into `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.",
316
398
  examples: [["/bin/bash", "-c"]]
317
399
  },
318
400
  disk: {
@@ -348,6 +430,21 @@ var generatedTools = [
348
430
  }
349
431
  }
350
432
  },
433
+ BaseMounts: {
434
+ type: "object",
435
+ properties: {
436
+ persistent: {
437
+ $ref: "#/$defs/PersistentMount"
438
+ },
439
+ network: {
440
+ type: "array",
441
+ maxItems: 1,
442
+ items: {
443
+ $ref: "#/$defs/NetworkMount"
444
+ }
445
+ }
446
+ }
447
+ },
351
448
  ClusterCompute: {
352
449
  type: "object",
353
450
  additionalProperties: false,
@@ -375,6 +472,15 @@ var generatedTools = [
375
472
  }
376
473
  }
377
474
  },
475
+ ClusterMounts: {
476
+ description: "Storage mounts for a cluster create: `persistent` or `network`, not both. Clusters cannot mount global volumes, so `global` is rejected.",
477
+ allOf: [
478
+ {
479
+ $ref: "#/$defs/BaseMounts"
480
+ }
481
+ ],
482
+ unevaluatedProperties: false
483
+ },
378
484
  ClusterType: {
379
485
  type: "string",
380
486
  description: "Cluster type. TRAINING is the generic distributed-training cluster; SLURM provisions a managed Slurm controller/compute topology; RAY provisions a managed Ray head/worker topology; APPLICATION is a general multi-node application cluster.",
@@ -389,7 +495,7 @@ var generatedTools = [
389
495
  {
390
496
  type: "object",
391
497
  required: ["name", "type", "compute"],
392
- description: "Request body for creating a cluster. `compute` defines the\nhomogeneous pod shape; the container configuration (image, env, ports,\n\u2026) applies to every pod. Pass `templateId` to provision every member\npod from a pod template instead of inline container fields \u2014 the\ntemplate also supplies the container registry credential, the only\nprivate-image path for clusters (a bare `registry` property on this\nbody is rejected).\n",
498
+ description: "Request body for creating a cluster. `compute` defines the homogeneous pod shape; the container configuration (image, env, ports, \u2026) applies to every pod.",
393
499
  properties: {
394
500
  compute: {
395
501
  $ref: "#/$defs/ClusterCompute"
@@ -405,7 +511,7 @@ var generatedTools = [
405
511
  templateId: {
406
512
  type: "string",
407
513
  minLength: 1,
408
- description: "ID of a pod template to provision every member pod from. The\ntemplate supplies the container settings (image, args, disk,\nenv, ports) and the container registry credential for private\nimages \u2014 the only private-image path for clusters. Mutually\nexclusive with `image`, `args`, `entrypoint`, `cmd`, `disk`,\n`env`, `ports`, and `mounts` (rejected with 400). The cluster\nretains the link: the `template` response field is set. Must be\na non-serverless pod template accessible to the caller.\n",
514
+ description: "ID of a pod template to provision every member pod from: it supplies the container settings (image, args, disk, env, ports) and the registry credential for private images \u2014 the only private-image path for clusters. Mutually exclusive with the inline container fields (`image`, `args`, `entrypoint`, `cmd`, `disk`, `env`, `ports`, `mounts`). Must be a non-serverless template you can access.",
409
515
  examples: ["30zmvf89kd"]
410
516
  },
411
517
  dataCenterIds: {
@@ -413,11 +519,11 @@ var generatedTools = [
413
519
  items: {
414
520
  type: "string"
415
521
  },
416
- description: "Preferred data centers for placement. Omit or pass an empty\narray to let the scheduler choose. A cluster is always placed\nwithin a single data center.\n",
522
+ description: "Preferred data centers for placement. Omit or pass an empty array to let the scheduler choose. A cluster is always placed within a single data center.",
417
523
  example: ["US-TX-3"]
418
524
  },
419
525
  mounts: {
420
- $ref: "#/$defs/Mounts"
526
+ $ref: "#/$defs/ClusterMounts"
421
527
  },
422
528
  startJupyter: {
423
529
  type: "boolean",
@@ -434,28 +540,11 @@ var generatedTools = [
434
540
  ],
435
541
  unevaluatedProperties: false
436
542
  },
437
- Mounts: {
438
- type: "object",
439
- additionalProperties: false,
440
- description: "Storage mounts attached to a pod. At-most-one of `persistent` or\n`network` may be set today (mutually exclusive, enforced at the\nhandler with 400 if both are present). The `network` field is an\narray for forward compatibility with eventual multi-network-volume\nsupport, but `maxItems` is 1 today.\n\nPATCH semantics:\n- Omitting `mounts` or sending `{}` leaves the existing mount\n unchanged.\n- An explicit `network: []` is rejected with 400 (clearing mounts\n is not supported).\n- Mount kind is fixed at create \u2014 a PATCH that introduces a kind\n not present at create (persistent on a network pod, network on\n a persistent pod, or any mount on a previously-mountless pod)\n is rejected with 400.\n- The `volumeId` of a network mount is immutable; a PATCH that\n names a different `volumeId` is rejected with 400.\n- Partial mounts are not supported \u2014 every mount entry must\n include the full schema (`size` + `path` for persistent,\n `volumeId` + `path` for network). Missing required fields \u2192 422.\n",
441
- properties: {
442
- persistent: {
443
- $ref: "#/$defs/PersistentMount"
444
- },
445
- network: {
446
- type: "array",
447
- maxItems: 1,
448
- items: {
449
- $ref: "#/$defs/NetworkMount"
450
- }
451
- }
452
- }
453
- },
454
543
  NetworkMount: {
455
544
  type: "object",
456
545
  required: ["volumeId", "path"],
457
546
  additionalProperties: false,
458
- description: "Reference to a NetworkVolume. Custom paths are honored at runtime on\nboth GPU and CPU pods. The underlying `volumeId` is immutable\npost-create; the mount `path` may be changed via PATCH.\n",
547
+ description: "Reference to a NetworkVolume. Custom paths are honored at runtime on both GPU and CPU pods. The underlying `volumeId` is immutable post-create; the mount `path` may be changed via PATCH.",
459
548
  properties: {
460
549
  volumeId: {
461
550
  type: "string",
@@ -473,7 +562,7 @@ var generatedTools = [
473
562
  type: "object",
474
563
  required: ["size", "path"],
475
564
  additionalProperties: false,
476
- description: "Host-local persistent storage. Pinned to the pod's host machine \u2014 data\ndoes not survive a host failure. Disallowed on CPU pods. Mutually\nexclusive with NetworkMount. Deprecated: prefer NetworkMount for any\ndata you cannot recreate.\n",
565
+ description: "Host-local persistent storage. Pinned to the pod's host machine \u2014 data does not survive a host failure. Disallowed on CPU pods. Mutually exclusive with NetworkMount. Deprecated: prefer NetworkMount for any data you cannot recreate.",
477
566
  properties: {
478
567
  size: {
479
568
  type: "integer",
@@ -550,6 +639,7 @@ var generatedTools = [
550
639
  idempotentHint: false,
551
640
  openWorldHint: true
552
641
  },
642
+ schemaOnDemand: true,
553
643
  inputSchema: {
554
644
  type: "object",
555
645
  properties: {
@@ -561,11 +651,10 @@ var generatedTools = [
561
651
  $defs: {
562
652
  BaseContainerConfig: {
563
653
  type: "object",
564
- description: "Container configuration universal to every containerized resource. Compose ContainerConfig instead unless the resource cannot support a direct registry credential (clusters \u2014 there the registry credential arrives via a pod template, see CreateClusterRequest.templateId).\n",
565
654
  properties: {
566
655
  args: {
567
656
  type: "string",
568
- description: 'The container\'s command, as a single raw string. This is the field `entrypoint` and `cmd` encode into, exposed in its stored form.\n\nTwo shapes are accepted. A bare shell string is treated as CMD and split into arguments, which is what the console\'s "Container start command" field writes. A JSON object of the form `{"entrypoint":[...],"cmd":[...]}` sets either or both explicitly.\n\nResponses always return both representations: `args` exactly as stored, plus the deconstructed `entrypoint` and `cmd`. Supplying `args` together with `entrypoint` or `cmd` is allowed only when they describe the same command, so a read-modify-write client can send back everything it received. Send `""` to clear, omit to leave unchanged.\n',
657
+ description: 'The container\'s command as one raw string: a bare shell string is treated as CMD and split into arguments; a JSON object `{"entrypoint":[...],"cmd":[...]}` sets either or both. Send `""` to clear; omit to leave unchanged.',
569
658
  examples: [
570
659
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
571
660
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -577,7 +666,7 @@ var generatedTools = [
577
666
  items: {
578
667
  type: "string"
579
668
  },
580
- description: "Container CMD in exec form. When the image defines an ENTRYPOINT, this is the argument list passed to it. Encoded into the `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.\n",
669
+ description: "Container CMD in exec form: when the image defines an ENTRYPOINT, the argument list passed to it. Encoded into `args`; supply both only when they describe the same command. Send `[]` to clear; omit to leave unchanged.",
581
670
  examples: [
582
671
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
583
672
  ]
@@ -587,7 +676,7 @@ var generatedTools = [
587
676
  items: {
588
677
  type: "string"
589
678
  },
590
- description: "Container ENTRYPOINT in exec form, overriding the image's own. Encoded into `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.\n",
679
+ description: "Container ENTRYPOINT in exec form, overriding the image's own. Encoded into `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.",
591
680
  examples: [["/bin/bash", "-c"]]
592
681
  },
593
682
  disk: {
@@ -647,7 +736,7 @@ var generatedTools = [
647
736
  pools: {
648
737
  type: "array",
649
738
  minItems: 1,
650
- description: 'Serverless GPU pool IDs (as returned by `GET /v2/catalog/gpus` in\n`pool`). Workers are placed on whichever listed pool has capacity.\nNarrow a pool down to specific cards with `excludedTypes`.\n\nOn `PATCH`, `pools` and `excludedTypes` are one selection and are\nreplaced together, so sending `pools` by itself **clears the\nexclusions**. Two cases:\n\n- **Changing pools, keeping exclusions** \u2014 send both fields in one\n request: `{"gpu": {"pools": ["ADA_24"], "excludedTypes":\n ["NVIDIA L40"]}}`. `GET` the endpoint first to read the current\n `excludedTypes` and resend the ones that still apply to the new\n pools; an exclusion naming a type outside `pools` is a 400.\n- **Changing only `count` or a CUDA constraint** \u2014 omit `pools`:\n `{"gpu": {"minCudaVersion": "12.4"}}`. The pool list and the\n exclusions are both left exactly as they are.\n\n`excludedTypes` documents the full rule.\n',
739
+ description: "Serverless GPU pool IDs (the `pool` field of `list-gpu-types`). Workers are placed on whichever listed pool has capacity; narrow to specific cards with `excludedTypes`. On update, `pools` and `excludedTypes` are replaced together, so sending `pools` alone clears the exclusions.",
651
740
  items: {
652
741
  type: "string"
653
742
  },
@@ -656,7 +745,7 @@ var generatedTools = [
656
745
  excludedTypes: {
657
746
  type: "array",
658
747
  uniqueItems: true,
659
- description: 'GPU **type** IDs to subtract from the selected pools \u2014 the `id`\nfield of `GET /v2/catalog/gpus`, the same identifiers pods take in\n`gpu.id`. Workers run on every type in `pools` except these. Omit to\nuse the whole pool.\n\nPools stay the unit of selection; types are the unit of\nsubtraction. There is no inclusive allowlist: a card later added to\none of your pools becomes eligible, which is the honest reading of\n"this pool, minus these".\n\nTied to `pools`, because the two together are one selection:\nsupplying `pools` replaces that selection wholesale, so a `PATCH`\nsending `pools` **without `excludedTypes`** **clears** them \u2014\nrestate them to keep them. A `PATCH` that omits `pools` leaves both\nthe pools and the exclusions untouched, so changing only a CUDA\nconstraint cannot widen a pinned endpoint.\n\nRejected with 400 if a value is not a GPU type in one of `pools`;\nupstream accepts unrecognized exclusions silently, so a typo would\notherwise produce a filter that does nothing. Surrounding whitespace\nis trimmed, so `" NVIDIA L40"` and `"NVIDIA L40"` mean the same card.\n',
748
+ description: "GPU type IDs to subtract from the selected pools \u2014 the `id` field of `list-gpu-types`, the same identifiers pods take in `gpu.id`. Workers run on every type in `pools` except these; omit to use the whole pool. A value outside `pools` is rejected. On update, restate them whenever you send `pools`, or they are cleared.",
660
749
  items: {
661
750
  type: "string",
662
751
  pattern: "^\\s*[^-\\s]"
@@ -673,7 +762,6 @@ var generatedTools = [
673
762
  }
674
763
  },
675
764
  ContainerConfig: {
676
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
677
765
  allOf: [
678
766
  {
679
767
  $ref: "#/$defs/BaseContainerConfig"
@@ -699,7 +787,7 @@ var generatedTools = [
699
787
  unevaluatedProperties: false
700
788
  },
701
789
  CreateEndpointGpuConfig: {
702
- description: "GPU request for an endpoint create. Carries the CUDA constraints, which\nlive here rather than at the body's top level so they are\nunrepresentable on a CPU endpoint.\n",
790
+ description: "GPU request for an endpoint create. Carries the CUDA constraints, which live here rather than at the body's top level so they are unrepresentable on a CPU endpoint.",
703
791
  allOf: [
704
792
  {
705
793
  $ref: "#/$defs/BaseEndpointGpuConfig"
@@ -714,13 +802,13 @@ var generatedTools = [
714
802
  type: "string",
715
803
  pattern: "^\\d+\\.\\d+$"
716
804
  },
717
- description: "Acceptable CUDA versions for worker placement, as\n`major.minor`. Omit to accept any version (or inherit the\ntemplate's constraint when creating from `templateId`).\nMatching is exact \u2014 discover valid values per GPU type via\n`GET /v2/catalog/gpus?include=AVAILABILITY&product=SERVERLESS`\n(`cudaVersions`).\n\nA non-empty set is mutually exclusive with minCudaVersion (400\nif both are sent). An explicit `[]` states no constraint, so it\nmay accompany a floor.\n",
805
+ description: "Acceptable CUDA versions for worker placement, as `major.minor`. Omit to accept any version (or inherit the template's constraint when creating from `templateId`).",
718
806
  examples: [["12.8", "12.6"]]
719
807
  },
720
808
  minCudaVersion: {
721
809
  type: "string",
722
810
  pattern: "^\\d+\\.\\d+$",
723
- description: "Lowest acceptable CUDA version for worker placement, as\n`major.minor`, compared numerically rather than as a decimal \u2014\nso 12.11 is above 12.2. Use this for an open-ended floor and\nallowedCudaVersions for an exact set.\n\nMutually exclusive with a non-empty allowedCudaVersions (400 if\nboth are sent); an explicit `[]` there states no constraint and\nmay accompany this floor.\n",
811
+ description: "Lowest acceptable CUDA version for worker placement, as `major.minor`, compared numerically rather than as a decimal \u2014 so 12.11 is above 12.2. Use this for an open-ended floor and allowedCudaVersions for an exact set.",
724
812
  examples: ["12.1"]
725
813
  }
726
814
  }
@@ -764,13 +852,13 @@ var generatedTools = [
764
852
  $ref: "#/$defs/EndpointType"
765
853
  }
766
854
  ],
767
- description: "Request-routing model. Required \u2014 it determines the valid scaler\nand request URLs, so it must be chosen explicitly on every create.\n"
855
+ description: "Request-routing model. Required \u2014 it determines the valid scaler and request URLs, so it must be chosen explicitly on every create."
768
856
  },
769
857
  cpu: {
770
858
  type: "array",
771
859
  minItems: 1,
772
860
  uniqueItems: true,
773
- description: "Eligible CPU configurations for each worker. Memory is derived from the\nselected flavor's catalog RAM multiplier. Exact duplicate configurations\nare rejected; the same flavor may be listed at different vCPU counts.\n",
861
+ description: "Eligible CPU configurations for each worker. Memory is derived from the selected flavor's catalog RAM multiplier. Exact duplicate configurations are rejected; the same flavor may be listed at different vCPU counts.",
774
862
  items: {
775
863
  $ref: "#/$defs/CreateCpuConfig"
776
864
  }
@@ -794,12 +882,17 @@ var generatedTools = [
794
882
  type: "array",
795
883
  items: {
796
884
  type: "string"
797
- }
885
+ },
886
+ description: "Network volume IDs.",
887
+ deprecated: true
888
+ },
889
+ mounts: {
890
+ $ref: "#/$defs/EndpointMountsInput"
798
891
  },
799
892
  templateId: {
800
893
  type: "string",
801
894
  minLength: 1,
802
- description: "ID of a serverless template to base this endpoint on. The\ntemplate is resolved at create time into the same container\nsettings you could otherwise spread into this body (image,\nargs, disk, ports, env, registry); explicit body fields\noverride the template's, except `env`, which is merged per\nkey with body values winning. The template's\nallowedCudaVersions seeds `gpu.allowedCudaVersions` when the\nbody omits it \u2014 but only for a GPU create, since a CPU endpoint\nhas no gpu block to seed into, and not when the body sets\n`gpu.minCudaVersion`, since seeding a set beside a floor would\nmanufacture the mutual-exclusion 400 from a valid request. Its\npod-specific startSsh/startJupyter flags are\nignored. Later template edits do not affect the endpoint.\nThe template may be one of your own or a public catalog\ntemplate \u2014 see `GET /v2/catalog/templates` (unknown or\ninaccessible ID \u2192 404) \u2014 and must be a serverless template\n(\u2192 422).\n",
895
+ description: "ID of a serverless template to base this endpoint on (must be serverless). Its container settings (image, args, disk, ports, env, registry) fill in what this body omits; body fields win, and `env` merges per key with body winning. Its allowedCudaVersions seeds `gpu.allowedCudaVersions` when the body omits it; pod-only flags are ignored. Copied once: later edits do not apply.",
803
896
  examples: ["30zmvf89kd"]
804
897
  },
805
898
  timeout: {
@@ -847,8 +940,42 @@ var generatedTools = [
847
940
  }
848
941
  }
849
942
  },
943
+ EndpointMount: {
944
+ type: "object",
945
+ required: ["volumeId"],
946
+ additionalProperties: false,
947
+ description: "A volume mounted on every worker, always at `/runpod-volume`. Custom paths are not supported yet.",
948
+ properties: {
949
+ volumeId: {
950
+ type: "string",
951
+ description: "ID of a NetworkVolume (under `network`) or GlobalVolume (under `global`).",
952
+ examples: ["vol_abc"]
953
+ }
954
+ }
955
+ },
956
+ EndpointMountsInput: {
957
+ type: "object",
958
+ additionalProperties: false,
959
+ description: "Volumes to mount on the endpoint's workers. A `network` or `global` list that is present replaces the endpoint's current list of that kind; an empty list detaches it, and an omitted list is left unchanged.",
960
+ properties: {
961
+ network: {
962
+ type: "array",
963
+ description: "Network volumes, at most one per data center.",
964
+ items: {
965
+ $ref: "#/$defs/EndpointMount"
966
+ }
967
+ },
968
+ global: {
969
+ type: "array",
970
+ maxItems: 1,
971
+ items: {
972
+ $ref: "#/$defs/EndpointMount"
973
+ }
974
+ }
975
+ }
976
+ },
850
977
  EndpointScaling: {
851
- description: "Autoscaling signal \u2014 a discriminated union on `type`: `QUEUE_DELAY`\n(queue-based endpoints only) or `REQUEST_COUNT`. The scaler is chosen\nindependently of the endpoint's routing `type` and can be switched on\nupdate.\n",
978
+ description: "Autoscaling signal \u2014 a discriminated union on `type`: `QUEUE_DELAY` (queue-based endpoints only) or `REQUEST_COUNT`. The scaler is chosen independently of the endpoint's routing `type` and can be switched on update.",
852
979
  oneOf: [
853
980
  {
854
981
  $ref: "#/$defs/QueueDelayScaling"
@@ -867,7 +994,7 @@ var generatedTools = [
867
994
  },
868
995
  EndpointType: {
869
996
  type: "string",
870
- description: "Request-routing semantics for a modern serverless endpoint.\n- `QUEUE` \u2014 submit asynchronous or synchronous jobs through the managed queue.\n- `LOAD_BALANCER` \u2014 send requests directly to worker-defined HTTP paths.\n Configure via `env`: `PORT` (server port, default 80), `PORT_HEALTH`\n (health-check port, default 80), and `HEALTH_CHECK_PATH` (path the\n load balancer polls for worker health, default `/ping`).\n",
997
+ description: "Request-routing semantics for a modern serverless endpoint. `QUEUE` \u2014 submit asynchronous or synchronous jobs through the managed queue. `LOAD_BALANCER` \u2014 send requests directly to worker-defined HTTP paths.",
871
998
  "x-enum-varnames": ["EndpointTypeQueue", "EndpointTypeLoadBalancer"],
872
999
  enum: ["QUEUE", "LOAD_BALANCER"]
873
1000
  },
@@ -891,14 +1018,14 @@ var generatedTools = [
891
1018
  type: "integer",
892
1019
  minimum: 1,
893
1020
  maximum: 3600,
894
- description: "Seconds before idle workers scale down. Not applicable to queue-based\nendpoints scaling on `requestCount` \u2014 rejected on create/update and\nomitted from responses for that combination.\n",
1021
+ description: "Seconds before idle workers scale down. Not applicable to queue-based endpoints scaling on `requestCount` \u2014 rejected on create/update and omitted from responses for that combination.",
895
1022
  examples: [5]
896
1023
  }
897
1024
  }
898
1025
  },
899
1026
  FlashBoot: {
900
1027
  type: "string",
901
- description: "FlashBoot cold-start acceleration mode.\n- `OFF` \u2014 disabled\n- `FLASHBOOT` \u2014 enabled\n- `PRIORITY_FLASHBOOT` \u2014 enabled with priority capacity\n",
1028
+ description: "FlashBoot cold-start acceleration mode. `OFF` \u2014 disabled. `FLASHBOOT` \u2014 enabled. `PRIORITY_FLASHBOOT` \u2014 enabled with priority capacity",
902
1029
  enum: ["OFF", "FLASHBOOT", "PRIORITY_FLASHBOOT"]
903
1030
  },
904
1031
  QueueDelayScaling: {
@@ -924,7 +1051,7 @@ var generatedTools = [
924
1051
  RequestCountScaling: {
925
1052
  type: "object",
926
1053
  additionalProperties: false,
927
- description: "Scale on concurrent in-flight requests per worker. Required for\nload-balancing endpoints; also selectable for queue-based.\n",
1054
+ description: "Scale on concurrent in-flight requests per worker. Required for load-balancing endpoints; also selectable for queue-based.",
928
1055
  required: ["type", "requestCount"],
929
1056
  properties: {
930
1057
  type: {
@@ -943,6 +1070,47 @@ var generatedTools = [
943
1070
  }
944
1071
  }
945
1072
  },
1073
+ {
1074
+ name: "create-global-volume",
1075
+ operationId: "createGlobalVolume",
1076
+ description: "Create a global volume. Provisions a new global volume \u2014 storage that mounts into workloads in any data center. Only a `name` is required; storage is provisioned for you. Accounts have a cap on how many global volumes they can hold; creating one past it fails with `400`. Returns `201` with the created global volume, including its assigned `id`.",
1077
+ method: "POST",
1078
+ path: "/v2/global-volumes",
1079
+ params: [],
1080
+ hasBody: true,
1081
+ annotations: {
1082
+ readOnlyHint: false,
1083
+ destructiveHint: false,
1084
+ idempotentHint: false,
1085
+ openWorldHint: true
1086
+ },
1087
+ inputSchema: {
1088
+ type: "object",
1089
+ properties: {
1090
+ body: {
1091
+ $ref: "#/$defs/CreateGlobalVolumeRequest"
1092
+ }
1093
+ },
1094
+ required: ["body"],
1095
+ $defs: {
1096
+ CreateGlobalVolumeRequest: {
1097
+ type: "object",
1098
+ additionalProperties: false,
1099
+ required: ["name"],
1100
+ properties: {
1101
+ name: {
1102
+ type: "string",
1103
+ minLength: 1,
1104
+ maxLength: 191,
1105
+ pattern: "[^\\p{Z}\\t-\\r\x85]",
1106
+ description: "Human-readable name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed.",
1107
+ examples: ["training-data"]
1108
+ }
1109
+ }
1110
+ }
1111
+ }
1112
+ }
1113
+ },
946
1114
  {
947
1115
  name: "create-network-volume",
948
1116
  operationId: "createNetworkVolume",
@@ -996,7 +1164,7 @@ var generatedTools = [
996
1164
  $ref: "#/$defs/VolumeType"
997
1165
  }
998
1166
  ],
999
- description: "Storage tier for the volume. Optional. When omitted, the volume is\nprovisioned using the requested data center's default (primary)\nstorage tier. HIGH_PERFORMANCE provisions a high-performance (HPS)\nvolume; STANDARD provisions a standard volume. A volume's tier is\nimmutable after creation.\n"
1167
+ description: "Storage tier for the volume. Optional. When omitted, the volume is provisioned using the requested data center's default (primary) storage tier."
1000
1168
  }
1001
1169
  }
1002
1170
  },
@@ -1022,6 +1190,7 @@ var generatedTools = [
1022
1190
  idempotentHint: false,
1023
1191
  openWorldHint: true
1024
1192
  },
1193
+ schemaOnDemand: true,
1025
1194
  inputSchema: {
1026
1195
  type: "object",
1027
1196
  properties: {
@@ -1033,11 +1202,10 @@ var generatedTools = [
1033
1202
  $defs: {
1034
1203
  BaseContainerConfig: {
1035
1204
  type: "object",
1036
- description: "Container configuration universal to every containerized resource. Compose ContainerConfig instead unless the resource cannot support a direct registry credential (clusters \u2014 there the registry credential arrives via a pod template, see CreateClusterRequest.templateId).\n",
1037
1205
  properties: {
1038
1206
  args: {
1039
1207
  type: "string",
1040
- description: 'The container\'s command, as a single raw string. This is the field `entrypoint` and `cmd` encode into, exposed in its stored form.\n\nTwo shapes are accepted. A bare shell string is treated as CMD and split into arguments, which is what the console\'s "Container start command" field writes. A JSON object of the form `{"entrypoint":[...],"cmd":[...]}` sets either or both explicitly.\n\nResponses always return both representations: `args` exactly as stored, plus the deconstructed `entrypoint` and `cmd`. Supplying `args` together with `entrypoint` or `cmd` is allowed only when they describe the same command, so a read-modify-write client can send back everything it received. Send `""` to clear, omit to leave unchanged.\n',
1208
+ description: 'The container\'s command as one raw string: a bare shell string is treated as CMD and split into arguments; a JSON object `{"entrypoint":[...],"cmd":[...]}` sets either or both. Send `""` to clear; omit to leave unchanged.',
1041
1209
  examples: [
1042
1210
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
1043
1211
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -1049,7 +1217,7 @@ var generatedTools = [
1049
1217
  items: {
1050
1218
  type: "string"
1051
1219
  },
1052
- description: "Container CMD in exec form. When the image defines an ENTRYPOINT, this is the argument list passed to it. Encoded into the `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.\n",
1220
+ description: "Container CMD in exec form: when the image defines an ENTRYPOINT, the argument list passed to it. Encoded into `args`; supply both only when they describe the same command. Send `[]` to clear; omit to leave unchanged.",
1053
1221
  examples: [
1054
1222
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
1055
1223
  ]
@@ -1059,7 +1227,7 @@ var generatedTools = [
1059
1227
  items: {
1060
1228
  type: "string"
1061
1229
  },
1062
- description: "Container ENTRYPOINT in exec form, overriding the image's own. Encoded into `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.\n",
1230
+ description: "Container ENTRYPOINT in exec form, overriding the image's own. Encoded into `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.",
1063
1231
  examples: [["/bin/bash", "-c"]]
1064
1232
  },
1065
1233
  disk: {
@@ -1131,13 +1299,27 @@ var generatedTools = [
1131
1299
  }
1132
1300
  }
1133
1301
  },
1302
+ BaseMounts: {
1303
+ type: "object",
1304
+ properties: {
1305
+ persistent: {
1306
+ $ref: "#/$defs/PersistentMount"
1307
+ },
1308
+ network: {
1309
+ type: "array",
1310
+ maxItems: 1,
1311
+ items: {
1312
+ $ref: "#/$defs/NetworkMount"
1313
+ }
1314
+ }
1315
+ }
1316
+ },
1134
1317
  Cloud: {
1135
1318
  type: "string",
1136
- description: "Cloud tier.\n- `SECURE` \u2014 Runpod-owned datacenter hardware\n- `COMMUNITY` \u2014 community-hosted hardware\n",
1319
+ description: "Cloud tier. `SECURE` \u2014 Runpod-owned datacenter hardware. `COMMUNITY` \u2014 community-hosted hardware",
1137
1320
  enum: ["SECURE", "COMMUNITY"]
1138
1321
  },
1139
1322
  ContainerConfig: {
1140
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
1141
1323
  allOf: [
1142
1324
  {
1143
1325
  $ref: "#/$defs/BaseContainerConfig"
@@ -1163,7 +1345,7 @@ var generatedTools = [
1163
1345
  unevaluatedProperties: false
1164
1346
  },
1165
1347
  CreateGpuConfig: {
1166
- description: "GPU request for a pod create. Carries the CUDA host constraints, which\nlive here rather than at the body's top level so they are\nunrepresentable on a CPU pod.\n",
1348
+ description: "GPU request for a pod create. Carries the CUDA host constraints, which live here rather than at the body's top level so they are unrepresentable on a CPU pod.",
1167
1349
  allOf: [
1168
1350
  {
1169
1351
  $ref: "#/$defs/BaseGpuConfig"
@@ -1177,13 +1359,13 @@ var generatedTools = [
1177
1359
  type: "string",
1178
1360
  pattern: "^\\d+\\.\\d+$"
1179
1361
  },
1180
- description: "Acceptable CUDA versions for the host machine, as `major.minor`.\nOmit to accept any version. Matching is exact, so a version no\nmachine reports yields a capacity error rather than a fallback \u2014\ndiscover valid values per GPU type via\n`GET /v2/catalog/gpus?include=AVAILABILITY&product=POD`\n(`cudaVersions`).\n\nA non-empty set is mutually exclusive with minCudaVersion (400\nif both are sent). An explicit `[]` states no constraint, so it\nmay accompany a floor.\n",
1362
+ description: "Acceptable CUDA versions for the host machine, as `major.minor`. Omit to accept any version.",
1181
1363
  examples: [["12.8", "12.6"]]
1182
1364
  },
1183
1365
  minCudaVersion: {
1184
1366
  type: "string",
1185
1367
  pattern: "^\\d+\\.\\d+$",
1186
- description: "Lowest acceptable CUDA version for the host machine, as\n`major.minor`, compared numerically rather than as a decimal \u2014\nso 12.11 is above 12.2. Use this for an open-ended floor and\nallowedCudaVersions for an exact set.\n\nMutually exclusive with a non-empty allowedCudaVersions (400 if\nboth are sent); an explicit `[]` there states no constraint and\nmay accompany this floor.\n",
1368
+ description: "Lowest acceptable CUDA version for the host machine, as `major.minor`, compared numerically rather than as a decimal \u2014 so 12.11 is above 12.2. Use this for an open-ended floor and allowedCudaVersions for an exact set.",
1187
1369
  examples: ["12.1"]
1188
1370
  },
1189
1371
  minRamPerGpu: {
@@ -1211,7 +1393,7 @@ var generatedTools = [
1211
1393
  {
1212
1394
  type: "object",
1213
1395
  required: ["name"],
1214
- description: "Request body for creating a pod. Exactly one of `gpu` or `cpu`\nmust be set \u2014 enforced at the handler layer. For CPU pods, memory\nis derived by the API from the selected flavor's RAM multiplier;\nclients provide only CPU flavor and vCPU count. CPU pods support\ncontainer disk and network volumes only; `mounts.persistent` is\ninvalid when `cpu` is set.\n\n`image` is required unless `templateId` is set.\n",
1396
+ description: "Request body for creating a pod. Exactly one of `gpu` or `cpu` must be set \u2014 enforced at the handler layer.",
1215
1397
  properties: {
1216
1398
  name: {
1217
1399
  type: "string",
@@ -1235,13 +1417,13 @@ var generatedTools = [
1235
1417
  items: {
1236
1418
  type: "string"
1237
1419
  },
1238
- description: "Preferred data centers for placement. Omit or pass an empty\narray to let the scheduler choose.\n",
1420
+ description: "Preferred data centers for placement. Omit or pass an empty array to let the scheduler choose.",
1239
1421
  examples: [["US-TX-3"]]
1240
1422
  },
1241
1423
  globalNetworking: {
1242
1424
  type: "boolean",
1243
1425
  default: false,
1244
- description: "Enable global networking, giving the pod a private IP reachable across data centers. Requires an NVIDIA GPU and a global-networking-enabled data center (both enforced upstream). See `GET /v2/catalog/datacenters` (`globalNetwork`) for eligible data centers.",
1426
+ description: "Enable global networking, giving the pod a private IP reachable across data centers. Requires an NVIDIA GPU and a global-networking-enabled data center (both enforced upstream).",
1245
1427
  examples: [false]
1246
1428
  },
1247
1429
  gpu: {
@@ -1253,19 +1435,19 @@ var generatedTools = [
1253
1435
  startJupyter: {
1254
1436
  type: "boolean",
1255
1437
  default: false,
1256
- description: "Create-time flag telling the provisioner to start JupyterLab:\ninjects a generated `JUPYTER_PASSWORD` environment variable,\nunless the request already sets one. Only images that honor\nthe convention start Jupyter from it (Runpod official images\ndo); expose `8888/http` in `ports` to reach it.\n\nNot part of the pod's readable config \u2014 never returned by\nGET and not changeable by PATCH.\n",
1438
+ description: "Create-time flag: starts JupyterLab by injecting a generated `JUPYTER_PASSWORD` environment variable, unless the request sets one. Expose `8888/http` in `ports` to reach it.",
1257
1439
  examples: [true]
1258
1440
  },
1259
1441
  startSsh: {
1260
1442
  type: "boolean",
1261
1443
  default: false,
1262
- description: "Create-time flag telling the provisioner to set up SSH\naccess: injects a `PUBLIC_KEY` environment variable carrying\nyour account's registered SSH public keys, unless the request\nalready sets one. **Requires registered keys** (`PUT\n/v2/account/ssh-keys`) \u2014 with none registered the flag does\nnothing and the pod has no SSH access. Only images that honor\nthe convention start sshd from it (all Runpod official images\ndo). Connect using the pod's `ssh` block; the `ssh.direct`\nvariant additionally needs a `22/tcp` entry in `ports`.\n\nNot part of the pod's readable config \u2014 never returned by\nGET and not changeable by PATCH.\n",
1444
+ description: "Create-time flag: sets up SSH access by injecting a `PUBLIC_KEY` environment variable with your account's registered SSH keys (`update-ssh-keys`), unless the request sets one. With no registered keys it does nothing and the pod has no SSH access. Direct SSH also needs a `22/tcp` entry in `ports`.",
1263
1445
  examples: [true]
1264
1446
  },
1265
1447
  templateId: {
1266
1448
  type: "string",
1267
1449
  minLength: 1,
1268
- description: "ID of a pod template to base this pod on. The template is\nresolved at create time into the same container settings you\ncould otherwise spread into this body (image, args, disk,\nports, env, registry, persistent mount, startSsh,\nstartJupyter, allowedCudaVersions); explicit body fields\noverride the template's, except `env`, which is merged per\nkey with body values winning. Sending either CUDA field\n(`gpu.allowedCudaVersions` or `gpu.minCudaVersion`) replaces\nthe template's CUDA constraint entirely, and CPU pods ignore\nit (like the persistent mount). The template is a one-time\nsource of settings: later template edits do not affect the\npod, and the created pod does not retain a link to the\ntemplate (`template` stays null). The template may be one\nof your own or a public catalog template \u2014 see\n`GET /v2/catalog/templates` (unknown or inaccessible ID \u2192\n404) \u2014 and must not be a serverless template (\u2192 422). CPU\npods do not inherit a template's persistent mount.\n",
1450
+ description: "ID of a pod template to base this pod on (not a serverless one). Its container settings (image, args, disk, ports, env, registry, persistent mount) fill in what this body omits; body fields win, and `env` merges per key with body winning. A CUDA field in `gpu` replaces the template's CUDA constraint; CPU pods ignore its mount. Copied once: later template edits do not affect the pod.",
1269
1451
  examples: ["30zmvf89kd"]
1270
1452
  }
1271
1453
  }
@@ -1281,28 +1463,51 @@ var generatedTools = [
1281
1463
  required: ["image"]
1282
1464
  }
1283
1465
  },
1284
- Mounts: {
1466
+ GlobalMount: {
1285
1467
  type: "object",
1468
+ required: ["volumeId"],
1286
1469
  additionalProperties: false,
1287
- description: "Storage mounts attached to a pod. At-most-one of `persistent` or\n`network` may be set today (mutually exclusive, enforced at the\nhandler with 400 if both are present). The `network` field is an\narray for forward compatibility with eventual multi-network-volume\nsupport, but `maxItems` is 1 today.\n\nPATCH semantics:\n- Omitting `mounts` or sending `{}` leaves the existing mount\n unchanged.\n- An explicit `network: []` is rejected with 400 (clearing mounts\n is not supported).\n- Mount kind is fixed at create \u2014 a PATCH that introduces a kind\n not present at create (persistent on a network pod, network on\n a persistent pod, or any mount on a previously-mountless pod)\n is rejected with 400.\n- The `volumeId` of a network mount is immutable; a PATCH that\n names a different `volumeId` is rejected with 400.\n- Partial mounts are not supported \u2014 every mount entry must\n include the full schema (`size` + `path` for persistent,\n `volumeId` + `path` for network). Missing required fields \u2192 422.\n",
1470
+ description: "Reference to a GlobalVolume. Usage-billed, so it has no size. The underlying `volumeId` is immutable post-create; the mount `path` may be changed via PATCH.",
1288
1471
  properties: {
1289
- persistent: {
1290
- $ref: "#/$defs/PersistentMount"
1472
+ volumeId: {
1473
+ type: "string",
1474
+ description: "ID of an existing GlobalVolume.",
1475
+ examples: ["gsb_abc"]
1291
1476
  },
1292
- network: {
1293
- type: "array",
1294
- maxItems: 1,
1295
- items: {
1296
- $ref: "#/$defs/NetworkMount"
1297
- }
1477
+ path: {
1478
+ type: "string",
1479
+ description: "Mount path inside the container. Optional on create, where a default is chosen for you; a PATCH that omits it leaves the path unchanged.",
1480
+ examples: ["/data"]
1298
1481
  }
1299
1482
  }
1300
1483
  },
1484
+ Mounts: {
1485
+ description: "Storage mounts for the pod: `persistent` (size + path) excludes both `network` and `global`; `network` (volumeId + path) and `global` (volumeId, optional path) take one entry each and can be combined. Every entry needs its full shape. On update, omit the field to leave the mounts as they are; the mount kind and a volume's volumeId cannot change, and mounts cannot be cleared.",
1486
+ allOf: [
1487
+ {
1488
+ $ref: "#/$defs/BaseMounts"
1489
+ },
1490
+ {
1491
+ type: "object",
1492
+ properties: {
1493
+ global: {
1494
+ type: "array",
1495
+ maxItems: 1,
1496
+ description: "At most one global volume, on GPU and CPU pods, with no data center constraint. Combinable with `network` but not with `persistent` (rejected): upstream drops a persistent disk whenever a volume is attached.",
1497
+ items: {
1498
+ $ref: "#/$defs/GlobalMount"
1499
+ }
1500
+ }
1501
+ }
1502
+ }
1503
+ ],
1504
+ unevaluatedProperties: false
1505
+ },
1301
1506
  NetworkMount: {
1302
1507
  type: "object",
1303
1508
  required: ["volumeId", "path"],
1304
1509
  additionalProperties: false,
1305
- description: "Reference to a NetworkVolume. Custom paths are honored at runtime on\nboth GPU and CPU pods. The underlying `volumeId` is immutable\npost-create; the mount `path` may be changed via PATCH.\n",
1510
+ description: "Reference to a NetworkVolume. Custom paths are honored at runtime on both GPU and CPU pods. The underlying `volumeId` is immutable post-create; the mount `path` may be changed via PATCH.",
1306
1511
  properties: {
1307
1512
  volumeId: {
1308
1513
  type: "string",
@@ -1320,7 +1525,7 @@ var generatedTools = [
1320
1525
  type: "object",
1321
1526
  required: ["size", "path"],
1322
1527
  additionalProperties: false,
1323
- description: "Host-local persistent storage. Pinned to the pod's host machine \u2014 data\ndoes not survive a host failure. Disallowed on CPU pods. Mutually\nexclusive with NetworkMount. Deprecated: prefer NetworkMount for any\ndata you cannot recreate.\n",
1528
+ description: "Host-local persistent storage. Pinned to the pod's host machine \u2014 data does not survive a host failure. Disallowed on CPU pods. Mutually exclusive with NetworkMount. Deprecated: prefer NetworkMount for any data you cannot recreate.",
1324
1529
  properties: {
1325
1530
  size: {
1326
1531
  type: "integer",
@@ -1403,6 +1608,7 @@ var generatedTools = [
1403
1608
  idempotentHint: false,
1404
1609
  openWorldHint: true
1405
1610
  },
1611
+ schemaOnDemand: true,
1406
1612
  inputSchema: {
1407
1613
  type: "object",
1408
1614
  properties: {
@@ -1422,7 +1628,7 @@ var generatedTools = [
1422
1628
  minLength: 1,
1423
1629
  maxLength: 191,
1424
1630
  pattern: "^[a-zA-Z_][a-zA-Z0-9_.\\-/]*$",
1425
- description: "Unique name for the secret \u2014 referenced from environment variables as `{{ RUNPOD_SECRET_<name> }}`; immutable after creation. Maximum 191 characters, must start with a letter or underscore, and may contain letters, digits, and `_.-/`. Names beginning with the reserved prefix `RUNPOD` are rejected (case-insensitive).",
1631
+ description: "Unique name for the secret \u2014 referenced from environment variables as `{{ RUNPOD_SECRET_<name> }}`; immutable after creation. Maximum 191 characters, must start with a letter or underscore, and may contain letters, digits, and `_.-/`.",
1426
1632
  examples: ["hf-token"]
1427
1633
  },
1428
1634
  value: {
@@ -1456,6 +1662,7 @@ var generatedTools = [
1456
1662
  idempotentHint: false,
1457
1663
  openWorldHint: true
1458
1664
  },
1665
+ schemaOnDemand: true,
1459
1666
  inputSchema: {
1460
1667
  type: "object",
1461
1668
  properties: {
@@ -1467,11 +1674,10 @@ var generatedTools = [
1467
1674
  $defs: {
1468
1675
  BaseContainerConfig: {
1469
1676
  type: "object",
1470
- description: "Container configuration universal to every containerized resource. Compose ContainerConfig instead unless the resource cannot support a direct registry credential (clusters \u2014 there the registry credential arrives via a pod template, see CreateClusterRequest.templateId).\n",
1471
1677
  properties: {
1472
1678
  args: {
1473
1679
  type: "string",
1474
- description: 'The container\'s command, as a single raw string. This is the field `entrypoint` and `cmd` encode into, exposed in its stored form.\n\nTwo shapes are accepted. A bare shell string is treated as CMD and split into arguments, which is what the console\'s "Container start command" field writes. A JSON object of the form `{"entrypoint":[...],"cmd":[...]}` sets either or both explicitly.\n\nResponses always return both representations: `args` exactly as stored, plus the deconstructed `entrypoint` and `cmd`. Supplying `args` together with `entrypoint` or `cmd` is allowed only when they describe the same command, so a read-modify-write client can send back everything it received. Send `""` to clear, omit to leave unchanged.\n',
1680
+ description: 'The container\'s command as one raw string: a bare shell string is treated as CMD and split into arguments; a JSON object `{"entrypoint":[...],"cmd":[...]}` sets either or both. Send `""` to clear; omit to leave unchanged.',
1475
1681
  examples: [
1476
1682
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
1477
1683
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -1483,7 +1689,7 @@ var generatedTools = [
1483
1689
  items: {
1484
1690
  type: "string"
1485
1691
  },
1486
- description: "Container CMD in exec form. When the image defines an ENTRYPOINT, this is the argument list passed to it. Encoded into the `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.\n",
1692
+ description: "Container CMD in exec form: when the image defines an ENTRYPOINT, the argument list passed to it. Encoded into `args`; supply both only when they describe the same command. Send `[]` to clear; omit to leave unchanged.",
1487
1693
  examples: [
1488
1694
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
1489
1695
  ]
@@ -1493,7 +1699,7 @@ var generatedTools = [
1493
1699
  items: {
1494
1700
  type: "string"
1495
1701
  },
1496
- description: "Container ENTRYPOINT in exec form, overriding the image's own. Encoded into `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.\n",
1702
+ description: "Container ENTRYPOINT in exec form, overriding the image's own. Encoded into `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.",
1497
1703
  examples: [["/bin/bash", "-c"]]
1498
1704
  },
1499
1705
  disk: {
@@ -1530,7 +1736,6 @@ var generatedTools = [
1530
1736
  }
1531
1737
  },
1532
1738
  ContainerConfig: {
1533
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
1534
1739
  allOf: [
1535
1740
  {
1536
1741
  $ref: "#/$defs/BaseContainerConfig"
@@ -1567,7 +1772,7 @@ var generatedTools = [
1567
1772
  type: "string",
1568
1773
  pattern: "^\\d+\\.\\d+$"
1569
1774
  },
1570
- description: "Acceptable CUDA versions for containers created from this\ntemplate, as `major.minor`. Omit to accept any version \u2014 see\nthe same field on `createPod` for matching semantics.\nExpanded into GPU pod and serverless endpoint creates; CPU\npods ignore it.\n",
1775
+ description: "Acceptable CUDA versions for containers created from this template, as `major.minor`. Omit to accept any version \u2014 see the same field on `createPod` for matching semantics.",
1571
1776
  examples: [["12.8", "12.6"]]
1572
1777
  },
1573
1778
  category: {
@@ -1593,13 +1798,13 @@ var generatedTools = [
1593
1798
  startJupyter: {
1594
1799
  type: "boolean",
1595
1800
  default: true,
1596
- description: "Start JupyterLab in containers created from this template:\ninjects a generated `JUPYTER_PASSWORD` environment variable,\nunless `env` already sets one. Only images that honor the\nconvention start Jupyter from it (Runpod official images do);\nexpose `8888/http` in `ports` to reach it. Defaults to `true`\nwhen omitted, matching console-created templates.\n",
1801
+ description: "Start JupyterLab in containers created from this template: injects a generated `JUPYTER_PASSWORD` environment variable, unless `env` already sets one.",
1597
1802
  examples: [false]
1598
1803
  },
1599
1804
  startSsh: {
1600
1805
  type: "boolean",
1601
1806
  default: true,
1602
- description: "Provision SSH access in containers created from this template:\ninjects a `PUBLIC_KEY` environment variable carrying the\ndeployer's registered SSH public keys (`PUT\n/v2/account/ssh-keys` \u2014 with none registered the flag does\nnothing), unless `env` already sets one. Only images that\nhonor the convention start sshd from it (all Runpod official\nimages do); direct SSH also needs a `22/tcp` entry in\n`ports`. Defaults to `true` when omitted, matching\nconsole-created templates.\n",
1807
+ description: "Provision SSH access in containers created from this template: injects a `PUBLIC_KEY` environment variable carrying the deployer's registered SSH public keys (`update-ssh-keys` \u2014 with none registered the flag does nothing), unless `env` already sets one.",
1603
1808
  examples: [true]
1604
1809
  }
1605
1810
  }
@@ -1611,7 +1816,7 @@ var generatedTools = [
1611
1816
  type: "object",
1612
1817
  required: ["size", "path"],
1613
1818
  additionalProperties: false,
1614
- description: "Host-local persistent storage. Pinned to the pod's host machine \u2014 data\ndoes not survive a host failure. Disallowed on CPU pods. Mutually\nexclusive with NetworkMount. Deprecated: prefer NetworkMount for any\ndata you cannot recreate.\n",
1819
+ description: "Host-local persistent storage. Pinned to the pod's host machine \u2014 data does not survive a host failure. Disallowed on CPU pods. Mutually exclusive with NetworkMount. Deprecated: prefer NetworkMount for any data you cannot recreate.",
1615
1820
  properties: {
1616
1821
  size: {
1617
1822
  type: "integer",
@@ -1628,13 +1833,13 @@ var generatedTools = [
1628
1833
  },
1629
1834
  TemplateCategory: {
1630
1835
  type: "string",
1631
- description: "Controls how the template is grouped and filtered in the Runpod console.\nIt does not affect hardware selection, scheduling, or billing.\n- `CPU` \u2014 CPU-only workloads\n- `NVIDIA` \u2014 NVIDIA GPU workloads\n- `AMD` \u2014 AMD GPU workloads\n",
1836
+ description: "Controls how the template is grouped and filtered in the Runpod console. It does not affect hardware selection, scheduling, or billing. `CPU` \u2014 CPU-only workloads. `NVIDIA` \u2014 NVIDIA GPU workloads. `AMD` \u2014 AMD GPU workloads",
1632
1837
  enum: ["CPU", "NVIDIA", "AMD"]
1633
1838
  },
1634
1839
  TemplateMounts: {
1635
1840
  type: "object",
1636
1841
  additionalProperties: false,
1637
- description: "Storage mounts attached to a template. Templates support only a\nsingle persistent mount today; any `network` property is rejected\nwith 422 by the schema validator.\n\nPATCH semantics: omitting `mounts` or sending `{}` leaves the\nexisting mount unchanged.\n",
1842
+ description: "Storage mounts for containers from this template: a single `persistent` mount only (`network` is rejected). On update, omitting `mounts` or sending `{}` leaves the existing mount unchanged.",
1638
1843
  properties: {
1639
1844
  persistent: {
1640
1845
  $ref: "#/$defs/PersistentMount"
@@ -1704,6 +1909,36 @@ var generatedTools = [
1704
1909
  required: ["id"]
1705
1910
  }
1706
1911
  },
1912
+ {
1913
+ name: "delete-global-volume",
1914
+ operationId: "deleteGlobalVolume",
1915
+ description: "Delete a global volume. Permanently deletes a global volume and its stored data. Fails with `409` while the volume is still mounted: terminate every pod that mounts it (stopped pods count) and detach it from every serverless endpoint first. Nothing is detached or terminated on your behalf.",
1916
+ method: "DELETE",
1917
+ path: "/v2/global-volumes/{id}",
1918
+ params: [
1919
+ {
1920
+ name: "id",
1921
+ location: "path"
1922
+ }
1923
+ ],
1924
+ hasBody: false,
1925
+ annotations: {
1926
+ readOnlyHint: false,
1927
+ destructiveHint: true,
1928
+ idempotentHint: true,
1929
+ openWorldHint: true
1930
+ },
1931
+ inputSchema: {
1932
+ type: "object",
1933
+ properties: {
1934
+ id: {
1935
+ type: "string",
1936
+ description: "Global volume identifier"
1937
+ }
1938
+ },
1939
+ required: ["id"]
1940
+ }
1941
+ },
1707
1942
  {
1708
1943
  name: "delete-network-volume",
1709
1944
  operationId: "deleteNetworkVolume",
@@ -2069,12 +2304,42 @@ var generatedTools = [
2069
2304
  },
2070
2305
  buildId: {
2071
2306
  type: "string",
2072
- description: "GitHub build identifier (from GET /v2/serverless/{id}/builds or a release's `buildId`)"
2307
+ description: "GitHub build identifier (from `list-endpoint-builds` or a release's `buildId`)"
2073
2308
  }
2074
2309
  },
2075
2310
  required: ["id", "buildId"]
2076
2311
  }
2077
2312
  },
2313
+ {
2314
+ name: "get-global-volume",
2315
+ operationId: "getGlobalVolume",
2316
+ description: "Get a global volume. Returns a single global volume by ID.",
2317
+ method: "GET",
2318
+ path: "/v2/global-volumes/{id}",
2319
+ params: [
2320
+ {
2321
+ name: "id",
2322
+ location: "path"
2323
+ }
2324
+ ],
2325
+ hasBody: false,
2326
+ annotations: {
2327
+ readOnlyHint: true,
2328
+ destructiveHint: false,
2329
+ idempotentHint: true,
2330
+ openWorldHint: true
2331
+ },
2332
+ inputSchema: {
2333
+ type: "object",
2334
+ properties: {
2335
+ id: {
2336
+ type: "string",
2337
+ description: "Global volume identifier"
2338
+ }
2339
+ },
2340
+ required: ["id"]
2341
+ }
2342
+ },
2078
2343
  {
2079
2344
  name: "get-gpu-type",
2080
2345
  operationId: "getGpuType",
@@ -2145,7 +2410,7 @@ var generatedTools = [
2145
2410
  items: {
2146
2411
  $ref: "#/$defs/Product"
2147
2412
  },
2148
- description: "Comma-separated availability product contexts. Supported values: POD, CLUSTER, SERVERLESS. Required with include=AVAILABILITY, and valid only with it (400 either way). There is no default: the same GPU type can be scarce for pods and plentiful for serverless, so the context has to be stated rather than assumed."
2413
+ description: "Comma-separated availability product contexts. Supported values: POD, CLUSTER, SERVERLESS. Required with include=AVAILABILITY, and valid only with it (400 either way)."
2149
2414
  },
2150
2415
  count: {
2151
2416
  type: "integer",
@@ -2163,7 +2428,7 @@ var generatedTools = [
2163
2428
  type: "string",
2164
2429
  pattern: "^[A-Z]{2}$"
2165
2430
  },
2166
- description: "Comma-separated ISO 3166-1 alpha-2 country codes, uppercase, to constrain availability to \u2014 e.g. FR or FR,DE. Values within this filter use OR semantics. Valid only with include=AVAILABILITY (400 otherwise); a malformed entry is a 422. Scopes availability, lowest-price calculations and the dataCenters array to those countries, so a listed data center outside them is omitted rather than returned with availability NONE. On the list endpoint a GPU type with no data center in those countries drops out entirely; the single-GPU endpoint still returns the requested type, with availability NONE and dataCenters omitted, so a 404 keeps meaning the GPU type does not exist. Read the NONE on availability rather than the absence of dataCenters, which is also absent when availability was not requested."
2431
+ description: "Comma-separated ISO 3166-1 alpha-2 country codes, uppercase, to constrain availability to \u2014 e.g. FR or FR,DE. Values within this filter use OR semantics. Valid only with include=AVAILABILITY; a malformed entry is a 422."
2167
2432
  },
2168
2433
  cudaVersions: {
2169
2434
  type: "array",
@@ -2171,12 +2436,12 @@ var generatedTools = [
2171
2436
  type: "string",
2172
2437
  pattern: "^\\d+\\.\\d+$"
2173
2438
  },
2174
- description: "Comma-separated CUDA versions to scope availability and lowest-price calculations to, matched exactly. Format: major.minor, e.g. 12.8 \u2014 a bare major is rejected here because it identifies no version. Valid only with include=AVAILABILITY (400 otherwise) and mutually exclusive with minCudaVersion (400 if both are sent); a malformed entry is a 422. Also narrows the returned cudaVersions array; omit it to enumerate every version offered."
2439
+ description: "Comma-separated CUDA versions to scope availability and lowest-price calculations to, matched exactly. Format: major.minor, e.g. 12.8 \u2014 a bare major is rejected here because it identifies no version."
2175
2440
  },
2176
2441
  minCudaVersion: {
2177
2442
  type: "string",
2178
2443
  pattern: "^\\d+(\\.\\d+)?$",
2179
- description: "Lowest acceptable CUDA version to scope availability and lowest-price calculations to, compared numerically. Format: integer major or major.minor, e.g. 12 or 12.1 \u2014 unlike the `gpu.minCudaVersion` body field on pod and endpoint create, a bare major is accepted here and means any release of that major, because this filter only widens a read. Valid only with include=AVAILABILITY (400 otherwise) and mutually exclusive with cudaVersions (400 if both are sent); a malformed value is a 422. Use this for an open-ended floor and cudaVersions for an exact set."
2444
+ description: "Lowest acceptable CUDA version to scope availability and lowest-price calculations to, compared numerically."
2180
2445
  }
2181
2446
  },
2182
2447
  required: ["id"],
@@ -2229,6 +2494,25 @@ var generatedTools = [
2229
2494
  required: ["id"]
2230
2495
  }
2231
2496
  },
2497
+ {
2498
+ name: "get-organization",
2499
+ operationId: "getOrganization",
2500
+ description: "Get your current organization. Returns the authenticated caller's current organization. Organization context comes from the caller's membership; no organization selector is required. Returns 404 when the caller does not belong to an organization, or 403 when organizations are unavailable to the caller.",
2501
+ method: "GET",
2502
+ path: "/v2/organization",
2503
+ params: [],
2504
+ hasBody: false,
2505
+ annotations: {
2506
+ readOnlyHint: true,
2507
+ destructiveHint: false,
2508
+ idempotentHint: true,
2509
+ openWorldHint: true
2510
+ },
2511
+ inputSchema: {
2512
+ type: "object",
2513
+ properties: {}
2514
+ }
2515
+ },
2232
2516
  {
2233
2517
  name: "get-pod",
2234
2518
  operationId: "getPod",
@@ -2403,12 +2687,12 @@ var generatedTools = [
2403
2687
  startTime: {
2404
2688
  type: "string",
2405
2689
  format: "date-time",
2406
- description: "Start of the billing period (RFC 3339). Defaults to 30 days ago. Snapped down to the start of its bucketSize bucket so the window aligns with the returned records; provide a boundary-aligned value (e.g. midnight for bucketSize=day) to avoid widening.\n"
2690
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
2407
2691
  },
2408
2692
  endTime: {
2409
2693
  type: "string",
2410
2694
  format: "date-time",
2411
- description: "End of the billing period (RFC 3339), exclusive. Defaults to now. Snapped up to the end of the bucketSize bucket it lands in (unless already on a boundary) so the window aligns with the returned records.\n"
2695
+ description: "End of the billing period (RFC 3339), exclusive. Defaults to now. Snapped up to the end of the bucketSize bucket it lands in (unless already on a boundary) so the window aligns with the returned records."
2412
2696
  },
2413
2697
  bucketSize: {
2414
2698
  $ref: "#/$defs/BillingBucketSize",
@@ -2417,7 +2701,7 @@ var generatedTools = [
2417
2701
  lastN: {
2418
2702
  type: "integer",
2419
2703
  minimum: 1,
2420
- description: 'Return the last N buckets of bucketSize, ending with the current (in-progress) bucket \u2014 e.g. lastN=100 with bucketSize=day is "last 100 days". The resolved window is aligned to bucket boundaries: startTime is the start of the earliest bucket (e.g. midnight of the earliest day) and endTime is the end of the current bucket. Mutually exclusive with startTime/endTime; provide one or the other, not both.\n'
2704
+ description: 'Return the last N buckets of bucketSize, ending with the current (in-progress) bucket \u2014 e.g. lastN=100 with bucketSize=day is "last 100 days".'
2421
2705
  }
2422
2706
  },
2423
2707
  $defs: {
@@ -2479,12 +2763,12 @@ var generatedTools = [
2479
2763
  startTime: {
2480
2764
  type: "string",
2481
2765
  format: "date-time",
2482
- description: "Start of the billing period (RFC 3339). Defaults to 30 days ago. Snapped down to the start of its bucketSize bucket so the window aligns with the returned records; provide a boundary-aligned value (e.g. midnight for bucketSize=day) to avoid widening.\n"
2766
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
2483
2767
  },
2484
2768
  endTime: {
2485
2769
  type: "string",
2486
2770
  format: "date-time",
2487
- description: "End of the billing period (RFC 3339), exclusive. Defaults to now. Snapped up to the end of the bucketSize bucket it lands in (unless already on a boundary) so the window aligns with the returned records.\n"
2771
+ description: "End of the billing period (RFC 3339), exclusive. Defaults to now. Snapped up to the end of the bucketSize bucket it lands in (unless already on a boundary) so the window aligns with the returned records."
2488
2772
  },
2489
2773
  bucketSize: {
2490
2774
  $ref: "#/$defs/BillingBucketSize",
@@ -2493,7 +2777,7 @@ var generatedTools = [
2493
2777
  lastN: {
2494
2778
  type: "integer",
2495
2779
  minimum: 1,
2496
- description: 'Return the last N buckets of bucketSize, ending with the current (in-progress) bucket \u2014 e.g. lastN=100 with bucketSize=day is "last 100 days". The resolved window is aligned to bucket boundaries: startTime is the start of the earliest bucket (e.g. midnight of the earliest day) and endTime is the end of the current bucket. Mutually exclusive with startTime/endTime; provide one or the other, not both.\n'
2780
+ description: 'Return the last N buckets of bucketSize, ending with the current (in-progress) bucket \u2014 e.g. lastN=100 with bucketSize=day is "last 100 days".'
2497
2781
  },
2498
2782
  clusterId: {
2499
2783
  type: "string",
@@ -2551,10 +2835,19 @@ var generatedTools = [
2551
2835
  {
2552
2836
  name: "list-clusters",
2553
2837
  operationId: "listClusters",
2554
- description: "List clusters. Returns all clusters owned by the authenticated user.",
2838
+ description: "List clusters. Returns clusters owned by the authenticated user, cursor-paginated newest-first; an omitted `limit` defaults to 1000. Follow `pagination.nextCursor` until `hasNextPage` is false.",
2555
2839
  method: "GET",
2556
2840
  path: "/v2/clusters",
2557
- params: [],
2841
+ params: [
2842
+ {
2843
+ name: "cursor",
2844
+ location: "query"
2845
+ },
2846
+ {
2847
+ name: "limit",
2848
+ location: "query"
2849
+ }
2850
+ ],
2558
2851
  hasBody: false,
2559
2852
  annotations: {
2560
2853
  readOnlyHint: true,
@@ -2564,7 +2857,19 @@ var generatedTools = [
2564
2857
  },
2565
2858
  inputSchema: {
2566
2859
  type: "object",
2567
- properties: {}
2860
+ properties: {
2861
+ cursor: {
2862
+ type: "string",
2863
+ minLength: 1,
2864
+ description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected."
2865
+ },
2866
+ limit: {
2867
+ type: "integer",
2868
+ minimum: 1,
2869
+ maximum: 1e3,
2870
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
2871
+ }
2872
+ }
2568
2873
  }
2569
2874
  },
2570
2875
  {
@@ -2818,12 +3123,12 @@ var generatedTools = [
2818
3123
  startTime: {
2819
3124
  type: "string",
2820
3125
  format: "date-time",
2821
- description: "Start of the billing period (RFC 3339). Defaults to 30 days ago. Snapped down to the start of its bucketSize bucket so the window aligns with the returned records; provide a boundary-aligned value (e.g. midnight for bucketSize=day) to avoid widening.\n"
3126
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
2822
3127
  },
2823
3128
  endTime: {
2824
3129
  type: "string",
2825
3130
  format: "date-time",
2826
- description: "End of the billing period (RFC 3339), exclusive. Defaults to now. Snapped up to the end of the bucketSize bucket it lands in (unless already on a boundary) so the window aligns with the returned records.\n"
3131
+ description: "End of the billing period (RFC 3339), exclusive. Defaults to now. Snapped up to the end of the bucketSize bucket it lands in (unless already on a boundary) so the window aligns with the returned records."
2827
3132
  },
2828
3133
  bucketSize: {
2829
3134
  $ref: "#/$defs/BillingBucketSize",
@@ -2832,7 +3137,7 @@ var generatedTools = [
2832
3137
  lastN: {
2833
3138
  type: "integer",
2834
3139
  minimum: 1,
2835
- description: 'Return the last N buckets of bucketSize, ending with the current (in-progress) bucket \u2014 e.g. lastN=100 with bucketSize=day is "last 100 days". The resolved window is aligned to bucket boundaries: startTime is the start of the earliest bucket (e.g. midnight of the earliest day) and endTime is the end of the current bucket. Mutually exclusive with startTime/endTime; provide one or the other, not both.\n'
3140
+ description: 'Return the last N buckets of bucketSize, ending with the current (in-progress) bucket \u2014 e.g. lastN=100 with bucketSize=day is "last 100 days".'
2836
3141
  }
2837
3142
  },
2838
3143
  $defs: {
@@ -2890,7 +3195,7 @@ var generatedTools = [
2890
3195
  cursor: {
2891
3196
  type: "string",
2892
3197
  minLength: 1,
2893
- description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected with 422.\n"
3198
+ description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected."
2894
3199
  },
2895
3200
  limit: {
2896
3201
  type: "integer",
@@ -2939,7 +3244,7 @@ var generatedTools = [
2939
3244
  cursor: {
2940
3245
  type: "string",
2941
3246
  minLength: 1,
2942
- description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected with 422.\n"
3247
+ description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected."
2943
3248
  },
2944
3249
  limit: {
2945
3250
  type: "integer",
@@ -2981,6 +3286,46 @@ var generatedTools = [
2981
3286
  required: ["id"]
2982
3287
  }
2983
3288
  },
3289
+ {
3290
+ name: "list-global-volumes",
3291
+ operationId: "listGlobalVolumes",
3292
+ description: "List global volumes. Returns global volumes owned by the authenticated user, cursor-paginated newest-first; an omitted `limit` defaults to 1000. Follow `pagination.nextCursor` until `hasNextPage` is false.",
3293
+ method: "GET",
3294
+ path: "/v2/global-volumes",
3295
+ params: [
3296
+ {
3297
+ name: "cursor",
3298
+ location: "query"
3299
+ },
3300
+ {
3301
+ name: "limit",
3302
+ location: "query"
3303
+ }
3304
+ ],
3305
+ hasBody: false,
3306
+ annotations: {
3307
+ readOnlyHint: true,
3308
+ destructiveHint: false,
3309
+ idempotentHint: true,
3310
+ openWorldHint: true
3311
+ },
3312
+ inputSchema: {
3313
+ type: "object",
3314
+ properties: {
3315
+ cursor: {
3316
+ type: "string",
3317
+ minLength: 1,
3318
+ description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected."
3319
+ },
3320
+ limit: {
3321
+ type: "integer",
3322
+ minimum: 1,
3323
+ maximum: 1e3,
3324
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
3325
+ }
3326
+ }
3327
+ }
3328
+ },
2984
3329
  {
2985
3330
  name: "list-gpu-types",
2986
3331
  operationId: "listGpuTypes",
@@ -3044,7 +3389,7 @@ var generatedTools = [
3044
3389
  items: {
3045
3390
  $ref: "#/$defs/Product"
3046
3391
  },
3047
- description: "Comma-separated availability product contexts. Supported values: POD, CLUSTER, SERVERLESS. Required with include=AVAILABILITY, and valid only with it (400 either way). There is no default: the same GPU type can be scarce for pods and plentiful for serverless, so the context has to be stated rather than assumed."
3392
+ description: "Comma-separated availability product contexts. Supported values: POD, CLUSTER, SERVERLESS. Required with include=AVAILABILITY, and valid only with it (400 either way)."
3048
3393
  },
3049
3394
  count: {
3050
3395
  type: "integer",
@@ -3062,7 +3407,7 @@ var generatedTools = [
3062
3407
  type: "string",
3063
3408
  pattern: "^[A-Z]{2}$"
3064
3409
  },
3065
- description: "Comma-separated ISO 3166-1 alpha-2 country codes, uppercase, to constrain availability to \u2014 e.g. FR or FR,DE. Values within this filter use OR semantics. Valid only with include=AVAILABILITY (400 otherwise); a malformed entry is a 422. Scopes availability, lowest-price calculations and the dataCenters array to those countries, so a listed data center outside them is omitted rather than returned with availability NONE. On the list endpoint a GPU type with no data center in those countries drops out entirely; the single-GPU endpoint still returns the requested type, with availability NONE and dataCenters omitted, so a 404 keeps meaning the GPU type does not exist. Read the NONE on availability rather than the absence of dataCenters, which is also absent when availability was not requested."
3410
+ description: "Comma-separated ISO 3166-1 alpha-2 country codes, uppercase, to constrain availability to \u2014 e.g. FR or FR,DE. Values within this filter use OR semantics. Valid only with include=AVAILABILITY; a malformed entry is a 422."
3066
3411
  },
3067
3412
  cudaVersions: {
3068
3413
  type: "array",
@@ -3070,12 +3415,12 @@ var generatedTools = [
3070
3415
  type: "string",
3071
3416
  pattern: "^\\d+\\.\\d+$"
3072
3417
  },
3073
- description: "Comma-separated CUDA versions to scope availability and lowest-price calculations to, matched exactly. Format: major.minor, e.g. 12.8 \u2014 a bare major is rejected here because it identifies no version. Valid only with include=AVAILABILITY (400 otherwise) and mutually exclusive with minCudaVersion (400 if both are sent); a malformed entry is a 422. Also narrows the returned cudaVersions array; omit it to enumerate every version offered."
3418
+ description: "Comma-separated CUDA versions to scope availability and lowest-price calculations to, matched exactly. Format: major.minor, e.g. 12.8 \u2014 a bare major is rejected here because it identifies no version."
3074
3419
  },
3075
3420
  minCudaVersion: {
3076
3421
  type: "string",
3077
3422
  pattern: "^\\d+(\\.\\d+)?$",
3078
- description: "Lowest acceptable CUDA version to scope availability and lowest-price calculations to, compared numerically. Format: integer major or major.minor, e.g. 12 or 12.1 \u2014 unlike the `gpu.minCudaVersion` body field on pod and endpoint create, a bare major is accepted here and means any release of that major, because this filter only widens a read. Valid only with include=AVAILABILITY (400 otherwise) and mutually exclusive with cudaVersions (400 if both are sent); a malformed value is a 422. Use this for an open-ended floor and cudaVersions for an exact set."
3423
+ description: "Lowest acceptable CUDA version to scope availability and lowest-price calculations to, compared numerically."
3079
3424
  }
3080
3425
  },
3081
3426
  $defs: {
@@ -3138,12 +3483,12 @@ var generatedTools = [
3138
3483
  startTime: {
3139
3484
  type: "string",
3140
3485
  format: "date-time",
3141
- description: "Start of the billing period (RFC 3339). Defaults to 30 days ago. Snapped down to the start of its bucketSize bucket so the window aligns with the returned records; provide a boundary-aligned value (e.g. midnight for bucketSize=day) to avoid widening.\n"
3486
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
3142
3487
  },
3143
3488
  endTime: {
3144
3489
  type: "string",
3145
3490
  format: "date-time",
3146
- description: "End of the billing period (RFC 3339), exclusive. Defaults to now. Snapped up to the end of the bucketSize bucket it lands in (unless already on a boundary) so the window aligns with the returned records.\n"
3491
+ description: "End of the billing period (RFC 3339), exclusive. Defaults to now. Snapped up to the end of the bucketSize bucket it lands in (unless already on a boundary) so the window aligns with the returned records."
3147
3492
  },
3148
3493
  bucketSize: {
3149
3494
  $ref: "#/$defs/BillingBucketSize",
@@ -3152,7 +3497,7 @@ var generatedTools = [
3152
3497
  lastN: {
3153
3498
  type: "integer",
3154
3499
  minimum: 1,
3155
- description: 'Return the last N buckets of bucketSize, ending with the current (in-progress) bucket \u2014 e.g. lastN=100 with bucketSize=day is "last 100 days". The resolved window is aligned to bucket boundaries: startTime is the start of the earliest bucket (e.g. midnight of the earliest day) and endTime is the end of the current bucket. Mutually exclusive with startTime/endTime; provide one or the other, not both.\n'
3500
+ description: 'Return the last N buckets of bucketSize, ending with the current (in-progress) bucket \u2014 e.g. lastN=100 with bucketSize=day is "last 100 days".'
3156
3501
  },
3157
3502
  networkVolumeId: {
3158
3503
  type: "string",
@@ -3180,10 +3525,19 @@ var generatedTools = [
3180
3525
  {
3181
3526
  name: "list-network-volumes",
3182
3527
  operationId: "listNetworkVolumes",
3183
- description: "List network volumes. Returns all network volumes owned by the authenticated user.",
3528
+ description: "List network volumes. Returns network volumes owned by the authenticated user, cursor-paginated newest-first; an omitted `limit` defaults to 1000. Follow `pagination.nextCursor` until `hasNextPage` is false.",
3184
3529
  method: "GET",
3185
3530
  path: "/v2/network-volumes",
3186
- params: [],
3531
+ params: [
3532
+ {
3533
+ name: "cursor",
3534
+ location: "query"
3535
+ },
3536
+ {
3537
+ name: "limit",
3538
+ location: "query"
3539
+ }
3540
+ ],
3187
3541
  hasBody: false,
3188
3542
  annotations: {
3189
3543
  readOnlyHint: true,
@@ -3193,7 +3547,19 @@ var generatedTools = [
3193
3547
  },
3194
3548
  inputSchema: {
3195
3549
  type: "object",
3196
- properties: {}
3550
+ properties: {
3551
+ cursor: {
3552
+ type: "string",
3553
+ minLength: 1,
3554
+ description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected."
3555
+ },
3556
+ limit: {
3557
+ type: "integer",
3558
+ minimum: 1,
3559
+ maximum: 1e3,
3560
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
3561
+ }
3562
+ }
3197
3563
  }
3198
3564
  },
3199
3565
  {
@@ -3237,12 +3603,12 @@ var generatedTools = [
3237
3603
  startTime: {
3238
3604
  type: "string",
3239
3605
  format: "date-time",
3240
- description: "Start of the billing period (RFC 3339). Defaults to 30 days ago. Snapped down to the start of its bucketSize bucket so the window aligns with the returned records; provide a boundary-aligned value (e.g. midnight for bucketSize=day) to avoid widening.\n"
3606
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
3241
3607
  },
3242
3608
  endTime: {
3243
3609
  type: "string",
3244
3610
  format: "date-time",
3245
- description: "End of the billing period (RFC 3339), exclusive. Defaults to now. Snapped up to the end of the bucketSize bucket it lands in (unless already on a boundary) so the window aligns with the returned records.\n"
3611
+ description: "End of the billing period (RFC 3339), exclusive. Defaults to now. Snapped up to the end of the bucketSize bucket it lands in (unless already on a boundary) so the window aligns with the returned records."
3246
3612
  },
3247
3613
  bucketSize: {
3248
3614
  $ref: "#/$defs/BillingBucketSize",
@@ -3251,7 +3617,7 @@ var generatedTools = [
3251
3617
  lastN: {
3252
3618
  type: "integer",
3253
3619
  minimum: 1,
3254
- description: 'Return the last N buckets of bucketSize, ending with the current (in-progress) bucket \u2014 e.g. lastN=100 with bucketSize=day is "last 100 days". The resolved window is aligned to bucket boundaries: startTime is the start of the earliest bucket (e.g. midnight of the earliest day) and endTime is the end of the current bucket. Mutually exclusive with startTime/endTime; provide one or the other, not both.\n'
3620
+ description: 'Return the last N buckets of bucketSize, ending with the current (in-progress) bucket \u2014 e.g. lastN=100 with bucketSize=day is "last 100 days".'
3255
3621
  },
3256
3622
  podId: {
3257
3623
  type: "string",
@@ -3279,7 +3645,7 @@ var generatedTools = [
3279
3645
  {
3280
3646
  name: "list-pods",
3281
3647
  operationId: "listPods",
3282
- description: "List pods. Returns pods owned by the authenticated user. Cluster member pods are excluded by default; set `includeClusterPods=true` to include them (each carries a non-null `cluster` membership block). Results are cursor-paginated newest-first; an omitted `limit` defaults to 1000. When cluster member pods are excluded, the exclusion applies to each page after it is cut, so a page may hold fewer than `limit` pods \u2014 follow `pagination.nextCursor` until `hasNextPage` is false rather than counting items.",
3648
+ description: "List pods. Returns pods owned by the authenticated user. Cluster member pods are excluded by default; set `includeClusterPods=true` to include them (each carries a non-null `cluster` membership block). Results are cursor-paginated newest-first; an omitted `limit` defaults to 1000. The cluster-member exclusion is applied before the page is cut, so a page holds `limit` pods whenever that many remain. Follow `pagination.nextCursor` until `hasNextPage` is false.",
3283
3649
  method: "GET",
3284
3650
  path: "/v2/pods",
3285
3651
  params: [
@@ -3314,7 +3680,7 @@ var generatedTools = [
3314
3680
  cursor: {
3315
3681
  type: "string",
3316
3682
  minLength: 1,
3317
- description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected with 422.\n"
3683
+ description: "Opaque resume cursor \u2014 pass the previous response's `pagination.nextCursor` through verbatim; omit for the first page. A cursor is only valid for the operation and parameters that issued it; a malformed or foreign cursor is rejected."
3318
3684
  },
3319
3685
  limit: {
3320
3686
  type: "integer",
@@ -3351,7 +3717,7 @@ var generatedTools = [
3351
3717
  type: "string",
3352
3718
  enum: ["official", "verified", "community"],
3353
3719
  default: "official",
3354
- description: "Which slice of the catalog to return: `official` for\nRunpod-curated templates (default), `verified` for\nRunpod-verified community templates, or `community` for all other\npublicly shared templates.\n"
3720
+ description: "Which slice of the catalog to return: `official` for Runpod-curated templates (default), `verified` for Runpod-verified community templates, or `community` for all other publicly shared templates."
3355
3721
  }
3356
3722
  }
3357
3723
  }
@@ -3445,12 +3811,12 @@ var generatedTools = [
3445
3811
  startTime: {
3446
3812
  type: "string",
3447
3813
  format: "date-time",
3448
- description: "Start of the billing period (RFC 3339). Defaults to 30 days ago. Snapped down to the start of its bucketSize bucket so the window aligns with the returned records; provide a boundary-aligned value (e.g. midnight for bucketSize=day) to avoid widening.\n"
3814
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
3449
3815
  },
3450
3816
  endTime: {
3451
3817
  type: "string",
3452
3818
  format: "date-time",
3453
- description: "End of the billing period (RFC 3339), exclusive. Defaults to now. Snapped up to the end of the bucketSize bucket it lands in (unless already on a boundary) so the window aligns with the returned records.\n"
3819
+ description: "End of the billing period (RFC 3339), exclusive. Defaults to now. Snapped up to the end of the bucketSize bucket it lands in (unless already on a boundary) so the window aligns with the returned records."
3454
3820
  },
3455
3821
  bucketSize: {
3456
3822
  $ref: "#/$defs/BillingBucketSize",
@@ -3459,7 +3825,7 @@ var generatedTools = [
3459
3825
  lastN: {
3460
3826
  type: "integer",
3461
3827
  minimum: 1,
3462
- description: 'Return the last N buckets of bucketSize, ending with the current (in-progress) bucket \u2014 e.g. lastN=100 with bucketSize=day is "last 100 days". The resolved window is aligned to bucket boundaries: startTime is the start of the earliest bucket (e.g. midnight of the earliest day) and endTime is the end of the current bucket. Mutually exclusive with startTime/endTime; provide one or the other, not both.\n'
3828
+ description: 'Return the last N buckets of bucketSize, ending with the current (in-progress) bucket \u2014 e.g. lastN=100 with bucketSize=day is "last 100 days".'
3463
3829
  },
3464
3830
  serverlessId: {
3465
3831
  type: "string",
@@ -3598,7 +3964,7 @@ var generatedTools = [
3598
3964
  type: "object",
3599
3965
  additionalProperties: false,
3600
3966
  minProperties: 1,
3601
- description: "Request body for updating a cluster. Only the cluster name can be\nchanged \u2014 this endpoint is a rename. Compute shape, type, and container\nconfiguration are fixed at creation.\n",
3967
+ description: "Request body for updating a cluster. Only the cluster name can be changed \u2014 this endpoint is a rename. Compute shape, type, and container configuration are fixed at creation.",
3602
3968
  properties: {
3603
3969
  name: {
3604
3970
  type: "string",
@@ -3629,6 +3995,7 @@ var generatedTools = [
3629
3995
  idempotentHint: false,
3630
3996
  openWorldHint: true
3631
3997
  },
3998
+ schemaOnDemand: true,
3632
3999
  inputSchema: {
3633
4000
  type: "object",
3634
4001
  properties: {
@@ -3644,11 +4011,10 @@ var generatedTools = [
3644
4011
  $defs: {
3645
4012
  BaseContainerConfig: {
3646
4013
  type: "object",
3647
- description: "Container configuration universal to every containerized resource. Compose ContainerConfig instead unless the resource cannot support a direct registry credential (clusters \u2014 there the registry credential arrives via a pod template, see CreateClusterRequest.templateId).\n",
3648
4014
  properties: {
3649
4015
  args: {
3650
4016
  type: "string",
3651
- description: 'The container\'s command, as a single raw string. This is the field `entrypoint` and `cmd` encode into, exposed in its stored form.\n\nTwo shapes are accepted. A bare shell string is treated as CMD and split into arguments, which is what the console\'s "Container start command" field writes. A JSON object of the form `{"entrypoint":[...],"cmd":[...]}` sets either or both explicitly.\n\nResponses always return both representations: `args` exactly as stored, plus the deconstructed `entrypoint` and `cmd`. Supplying `args` together with `entrypoint` or `cmd` is allowed only when they describe the same command, so a read-modify-write client can send back everything it received. Send `""` to clear, omit to leave unchanged.\n',
4017
+ description: 'The container\'s command as one raw string: a bare shell string is treated as CMD and split into arguments; a JSON object `{"entrypoint":[...],"cmd":[...]}` sets either or both. Send `""` to clear; omit to leave unchanged.',
3652
4018
  examples: [
3653
4019
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
3654
4020
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -3660,7 +4026,7 @@ var generatedTools = [
3660
4026
  items: {
3661
4027
  type: "string"
3662
4028
  },
3663
- description: "Container CMD in exec form. When the image defines an ENTRYPOINT, this is the argument list passed to it. Encoded into the `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.\n",
4029
+ description: "Container CMD in exec form: when the image defines an ENTRYPOINT, the argument list passed to it. Encoded into `args`; supply both only when they describe the same command. Send `[]` to clear; omit to leave unchanged.",
3664
4030
  examples: [
3665
4031
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
3666
4032
  ]
@@ -3670,7 +4036,7 @@ var generatedTools = [
3670
4036
  items: {
3671
4037
  type: "string"
3672
4038
  },
3673
- description: "Container ENTRYPOINT in exec form, overriding the image's own. Encoded into `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.\n",
4039
+ description: "Container ENTRYPOINT in exec form, overriding the image's own. Encoded into `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.",
3674
4040
  examples: [["/bin/bash", "-c"]]
3675
4041
  },
3676
4042
  disk: {
@@ -3730,7 +4096,7 @@ var generatedTools = [
3730
4096
  pools: {
3731
4097
  type: "array",
3732
4098
  minItems: 1,
3733
- description: 'Serverless GPU pool IDs (as returned by `GET /v2/catalog/gpus` in\n`pool`). Workers are placed on whichever listed pool has capacity.\nNarrow a pool down to specific cards with `excludedTypes`.\n\nOn `PATCH`, `pools` and `excludedTypes` are one selection and are\nreplaced together, so sending `pools` by itself **clears the\nexclusions**. Two cases:\n\n- **Changing pools, keeping exclusions** \u2014 send both fields in one\n request: `{"gpu": {"pools": ["ADA_24"], "excludedTypes":\n ["NVIDIA L40"]}}`. `GET` the endpoint first to read the current\n `excludedTypes` and resend the ones that still apply to the new\n pools; an exclusion naming a type outside `pools` is a 400.\n- **Changing only `count` or a CUDA constraint** \u2014 omit `pools`:\n `{"gpu": {"minCudaVersion": "12.4"}}`. The pool list and the\n exclusions are both left exactly as they are.\n\n`excludedTypes` documents the full rule.\n',
4099
+ description: "Serverless GPU pool IDs (the `pool` field of `list-gpu-types`). Workers are placed on whichever listed pool has capacity; narrow to specific cards with `excludedTypes`. On update, `pools` and `excludedTypes` are replaced together, so sending `pools` alone clears the exclusions.",
3734
4100
  items: {
3735
4101
  type: "string"
3736
4102
  },
@@ -3739,7 +4105,7 @@ var generatedTools = [
3739
4105
  excludedTypes: {
3740
4106
  type: "array",
3741
4107
  uniqueItems: true,
3742
- description: 'GPU **type** IDs to subtract from the selected pools \u2014 the `id`\nfield of `GET /v2/catalog/gpus`, the same identifiers pods take in\n`gpu.id`. Workers run on every type in `pools` except these. Omit to\nuse the whole pool.\n\nPools stay the unit of selection; types are the unit of\nsubtraction. There is no inclusive allowlist: a card later added to\none of your pools becomes eligible, which is the honest reading of\n"this pool, minus these".\n\nTied to `pools`, because the two together are one selection:\nsupplying `pools` replaces that selection wholesale, so a `PATCH`\nsending `pools` **without `excludedTypes`** **clears** them \u2014\nrestate them to keep them. A `PATCH` that omits `pools` leaves both\nthe pools and the exclusions untouched, so changing only a CUDA\nconstraint cannot widen a pinned endpoint.\n\nRejected with 400 if a value is not a GPU type in one of `pools`;\nupstream accepts unrecognized exclusions silently, so a typo would\notherwise produce a filter that does nothing. Surrounding whitespace\nis trimmed, so `" NVIDIA L40"` and `"NVIDIA L40"` mean the same card.\n',
4108
+ description: "GPU type IDs to subtract from the selected pools \u2014 the `id` field of `list-gpu-types`, the same identifiers pods take in `gpu.id`. Workers run on every type in `pools` except these; omit to use the whole pool. A value outside `pools` is rejected. On update, restate them whenever you send `pools`, or they are cleared.",
3743
4109
  items: {
3744
4110
  type: "string",
3745
4111
  pattern: "^\\s*[^-\\s]"
@@ -3756,7 +4122,6 @@ var generatedTools = [
3756
4122
  }
3757
4123
  },
3758
4124
  ContainerConfig: {
3759
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
3760
4125
  allOf: [
3761
4126
  {
3762
4127
  $ref: "#/$defs/BaseContainerConfig"
@@ -3781,8 +4146,42 @@ var generatedTools = [
3781
4146
  ],
3782
4147
  unevaluatedProperties: false
3783
4148
  },
4149
+ EndpointMount: {
4150
+ type: "object",
4151
+ required: ["volumeId"],
4152
+ additionalProperties: false,
4153
+ description: "A volume mounted on every worker, always at `/runpod-volume`. Custom paths are not supported yet.",
4154
+ properties: {
4155
+ volumeId: {
4156
+ type: "string",
4157
+ description: "ID of a NetworkVolume (under `network`) or GlobalVolume (under `global`).",
4158
+ examples: ["vol_abc"]
4159
+ }
4160
+ }
4161
+ },
4162
+ EndpointMountsInput: {
4163
+ type: "object",
4164
+ additionalProperties: false,
4165
+ description: "Volumes to mount on the endpoint's workers. A `network` or `global` list that is present replaces the endpoint's current list of that kind; an empty list detaches it, and an omitted list is left unchanged.",
4166
+ properties: {
4167
+ network: {
4168
+ type: "array",
4169
+ description: "Network volumes, at most one per data center.",
4170
+ items: {
4171
+ $ref: "#/$defs/EndpointMount"
4172
+ }
4173
+ },
4174
+ global: {
4175
+ type: "array",
4176
+ maxItems: 1,
4177
+ items: {
4178
+ $ref: "#/$defs/EndpointMount"
4179
+ }
4180
+ }
4181
+ }
4182
+ },
3784
4183
  EndpointScaling: {
3785
- description: "Autoscaling signal \u2014 a discriminated union on `type`: `QUEUE_DELAY`\n(queue-based endpoints only) or `REQUEST_COUNT`. The scaler is chosen\nindependently of the endpoint's routing `type` and can be switched on\nupdate.\n",
4184
+ description: "Autoscaling signal \u2014 a discriminated union on `type`: `QUEUE_DELAY` (queue-based endpoints only) or `REQUEST_COUNT`. The scaler is chosen independently of the endpoint's routing `type` and can be switched on update.",
3786
4185
  oneOf: [
3787
4186
  {
3788
4187
  $ref: "#/$defs/QueueDelayScaling"
@@ -3819,14 +4218,14 @@ var generatedTools = [
3819
4218
  type: "integer",
3820
4219
  minimum: 1,
3821
4220
  maximum: 3600,
3822
- description: "Seconds before idle workers scale down. Not applicable to queue-based\nendpoints scaling on `requestCount` \u2014 rejected on create/update and\nomitted from responses for that combination.\n",
4221
+ description: "Seconds before idle workers scale down. Not applicable to queue-based endpoints scaling on `requestCount` \u2014 rejected on create/update and omitted from responses for that combination.",
3823
4222
  examples: [5]
3824
4223
  }
3825
4224
  }
3826
4225
  },
3827
4226
  FlashBoot: {
3828
4227
  type: "string",
3829
- description: "FlashBoot cold-start acceleration mode.\n- `OFF` \u2014 disabled\n- `FLASHBOOT` \u2014 enabled\n- `PRIORITY_FLASHBOOT` \u2014 enabled with priority capacity\n",
4228
+ description: "FlashBoot cold-start acceleration mode. `OFF` \u2014 disabled. `FLASHBOOT` \u2014 enabled. `PRIORITY_FLASHBOOT` \u2014 enabled with priority capacity",
3830
4229
  enum: ["OFF", "FLASHBOOT", "PRIORITY_FLASHBOOT"]
3831
4230
  },
3832
4231
  QueueDelayScaling: {
@@ -3852,7 +4251,7 @@ var generatedTools = [
3852
4251
  RequestCountScaling: {
3853
4252
  type: "object",
3854
4253
  additionalProperties: false,
3855
- description: "Scale on concurrent in-flight requests per worker. Required for\nload-balancing endpoints; also selectable for queue-based.\n",
4254
+ description: "Scale on concurrent in-flight requests per worker. Required for load-balancing endpoints; also selectable for queue-based.",
3856
4255
  required: ["type", "requestCount"],
3857
4256
  properties: {
3858
4257
  type: {
@@ -3869,7 +4268,7 @@ var generatedTools = [
3869
4268
  }
3870
4269
  },
3871
4270
  UpdateEndpointGpuConfig: {
3872
- description: "Partial GPU update \u2014 every field is optional and an omitted one is left\nunchanged. Unlike create, `pools` is optional, so changing only a CUDA\nconstraint does not require resending the pool list.\n\n`excludedTypes` requires `pools`, because the two are one selection and\nonly a supplied `pools` replaces it \u2014 an exclusion on its own would\notherwise be silently dropped.\n",
4271
+ description: "Partial GPU update \u2014 every field is optional and an omitted one is left unchanged. `pools` is optional here, so a CUDA-only change does not resend the pool list; `excludedTypes` needs `pools` in the same request, and `pools` sent alone clears the exclusions.",
3873
4272
  allOf: [
3874
4273
  {
3875
4274
  $ref: "#/$defs/BaseEndpointGpuConfig"
@@ -3886,12 +4285,12 @@ var generatedTools = [
3886
4285
  type: "string",
3887
4286
  pattern: "^\\d+\\.\\d+$"
3888
4287
  },
3889
- description: "Acceptable CUDA versions for worker placement, as\n`major.minor`. An explicit `[]` clears the constraint;\nomitting the field leaves it unchanged. Takes effect as\nworkers are replaced.\n\nA non-empty set is mutually exclusive with minCudaVersion (400\nif both are sent). An explicit `[]` states no constraint, so it\nmay accompany a floor.\nSetting one does not clear the other \u2014 clear it explicitly in\nthe same patch if the endpoint already carries it.\n"
4288
+ description: "Acceptable CUDA versions for worker placement, as `major.minor`. An explicit `[]` clears the constraint; omitting the field leaves it unchanged. A non-empty set cannot be sent with minCudaVersion, and setting one does not clear the other: clear the other explicitly in the same patch. Takes effect as workers are replaced."
3890
4289
  },
3891
4290
  minCudaVersion: {
3892
4291
  type: "string",
3893
4292
  pattern: "^$|^\\d+\\.\\d+$",
3894
- description: 'Lowest acceptable CUDA version for worker placement, as\n`major.minor`. An explicit `""` clears the floor; omitting the\nfield leaves it unchanged. Takes effect as workers are\nreplaced.\n\nMutually exclusive with a non-empty allowedCudaVersions (400 if\nboth are sent); an explicit `[]` there states no constraint and\nmay accompany this floor.\n',
4293
+ description: 'Lowest acceptable CUDA version for worker placement, as `major.minor`. An explicit `""` clears the floor; omitting the field leaves it unchanged. Cannot be sent with a non-empty allowedCudaVersions (an explicit `[]` there is fine); clear that explicitly in the same patch if the endpoint carries one. Takes effect as workers are replaced.',
3895
4294
  examples: ["12.1"]
3896
4295
  }
3897
4296
  }
@@ -3912,7 +4311,7 @@ var generatedTools = [
3912
4311
  type: "array",
3913
4312
  minItems: 1,
3914
4313
  uniqueItems: true,
3915
- description: "Complete replacement CPU selection. Valid only for an existing CPU\nendpoint; endpoint compute family cannot be changed.\n",
4314
+ description: "Complete replacement CPU selection. Valid only for an existing CPU endpoint; endpoint compute family cannot be changed.",
3916
4315
  items: {
3917
4316
  $ref: "#/$defs/CreateCpuConfig"
3918
4317
  }
@@ -3938,14 +4337,19 @@ var generatedTools = [
3938
4337
  type: "array",
3939
4338
  items: {
3940
4339
  type: "string"
3941
- }
4340
+ },
4341
+ description: "Network volume IDs. Pass `[]` to detach.",
4342
+ deprecated: true
4343
+ },
4344
+ mounts: {
4345
+ $ref: "#/$defs/EndpointMountsInput"
3942
4346
  },
3943
4347
  scaling: {
3944
4348
  $ref: "#/$defs/EndpointScaling"
3945
4349
  },
3946
4350
  templateId: {
3947
4351
  type: "string",
3948
- description: "ID of a serverless template whose container settings are\napplied as if they were provided in this PATCH body (image,\nargs, disk, ports, env, registry). Explicit body fields\noverride the template's; `env` merges template and body per\nkey (body wins) and, per PATCH semantics, replaces the\nendpoint's env. One-time application \u2014 no link to the\ntemplate is retained. Must be one of your templates or a\npublic template (unknown or inaccessible ID \u2192 404); must be\na serverless template (\u2192 422).\n",
4352
+ description: "ID of a serverless template whose container settings (image, args, disk, ports, env, registry) are applied as if sent in this PATCH body; explicit body fields win. `env` merges template and body per key (body wins) and replaces the endpoint's existing env. Applied once; must be a serverless template you can access.",
3949
4353
  examples: ["30zmvf89kd"]
3950
4354
  },
3951
4355
  timeout: {
@@ -3962,6 +4366,57 @@ var generatedTools = [
3962
4366
  }
3963
4367
  }
3964
4368
  },
4369
+ {
4370
+ name: "update-global-volume",
4371
+ operationId: "updateGlobalVolume",
4372
+ description: "Update a global volume. Updates mutable fields on a global volume. Only provided fields are changed.",
4373
+ method: "PATCH",
4374
+ path: "/v2/global-volumes/{id}",
4375
+ params: [
4376
+ {
4377
+ name: "id",
4378
+ location: "path"
4379
+ }
4380
+ ],
4381
+ hasBody: true,
4382
+ annotations: {
4383
+ readOnlyHint: false,
4384
+ destructiveHint: false,
4385
+ idempotentHint: false,
4386
+ openWorldHint: true
4387
+ },
4388
+ inputSchema: {
4389
+ type: "object",
4390
+ properties: {
4391
+ id: {
4392
+ type: "string",
4393
+ description: "Global volume identifier"
4394
+ },
4395
+ body: {
4396
+ $ref: "#/$defs/UpdateGlobalVolumeRequest"
4397
+ }
4398
+ },
4399
+ required: ["id", "body"],
4400
+ $defs: {
4401
+ UpdateGlobalVolumeRequest: {
4402
+ type: "object",
4403
+ additionalProperties: false,
4404
+ minProperties: 1,
4405
+ description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
4406
+ properties: {
4407
+ name: {
4408
+ type: "string",
4409
+ minLength: 1,
4410
+ maxLength: 191,
4411
+ pattern: "[^\\p{Z}\\t-\\r\x85]",
4412
+ description: "New human-readable name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed.",
4413
+ examples: ["renamed-volume"]
4414
+ }
4415
+ }
4416
+ }
4417
+ }
4418
+ }
4419
+ },
3965
4420
  {
3966
4421
  name: "update-network-volume",
3967
4422
  operationId: "updateNetworkVolume",
@@ -3998,7 +4453,7 @@ var generatedTools = [
3998
4453
  type: "object",
3999
4454
  additionalProperties: false,
4000
4455
  minProperties: 1,
4001
- description: "Only the provided fields are updated. At least one field must be\npresent; an empty body is rejected.\n",
4456
+ description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
4002
4457
  properties: {
4003
4458
  name: {
4004
4459
  type: "string",
@@ -4010,7 +4465,7 @@ var generatedTools = [
4010
4465
  type: "integer",
4011
4466
  minimum: 10,
4012
4467
  maximum: 4096,
4013
- description: "New size in GB. Must be greater than or equal to the current size \u2014\nnetwork volume storage cannot be reduced.\n",
4468
+ description: "New size in GB. Must be greater than or equal to the current size \u2014 network volume storage cannot be reduced.",
4014
4469
  examples: [100]
4015
4470
  }
4016
4471
  }
@@ -4037,6 +4492,7 @@ var generatedTools = [
4037
4492
  idempotentHint: false,
4038
4493
  openWorldHint: true
4039
4494
  },
4495
+ schemaOnDemand: true,
4040
4496
  inputSchema: {
4041
4497
  type: "object",
4042
4498
  properties: {
@@ -4052,11 +4508,10 @@ var generatedTools = [
4052
4508
  $defs: {
4053
4509
  BaseContainerConfig: {
4054
4510
  type: "object",
4055
- description: "Container configuration universal to every containerized resource. Compose ContainerConfig instead unless the resource cannot support a direct registry credential (clusters \u2014 there the registry credential arrives via a pod template, see CreateClusterRequest.templateId).\n",
4056
4511
  properties: {
4057
4512
  args: {
4058
4513
  type: "string",
4059
- description: 'The container\'s command, as a single raw string. This is the field `entrypoint` and `cmd` encode into, exposed in its stored form.\n\nTwo shapes are accepted. A bare shell string is treated as CMD and split into arguments, which is what the console\'s "Container start command" field writes. A JSON object of the form `{"entrypoint":[...],"cmd":[...]}` sets either or both explicitly.\n\nResponses always return both representations: `args` exactly as stored, plus the deconstructed `entrypoint` and `cmd`. Supplying `args` together with `entrypoint` or `cmd` is allowed only when they describe the same command, so a read-modify-write client can send back everything it received. Send `""` to clear, omit to leave unchanged.\n',
4514
+ description: 'The container\'s command as one raw string: a bare shell string is treated as CMD and split into arguments; a JSON object `{"entrypoint":[...],"cmd":[...]}` sets either or both. Send `""` to clear; omit to leave unchanged.',
4060
4515
  examples: [
4061
4516
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
4062
4517
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -4068,7 +4523,7 @@ var generatedTools = [
4068
4523
  items: {
4069
4524
  type: "string"
4070
4525
  },
4071
- description: "Container CMD in exec form. When the image defines an ENTRYPOINT, this is the argument list passed to it. Encoded into the `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.\n",
4526
+ description: "Container CMD in exec form: when the image defines an ENTRYPOINT, the argument list passed to it. Encoded into `args`; supply both only when they describe the same command. Send `[]` to clear; omit to leave unchanged.",
4072
4527
  examples: [
4073
4528
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
4074
4529
  ]
@@ -4078,7 +4533,7 @@ var generatedTools = [
4078
4533
  items: {
4079
4534
  type: "string"
4080
4535
  },
4081
- description: "Container ENTRYPOINT in exec form, overriding the image's own. Encoded into `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.\n",
4536
+ description: "Container ENTRYPOINT in exec form, overriding the image's own. Encoded into `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.",
4082
4537
  examples: [["/bin/bash", "-c"]]
4083
4538
  },
4084
4539
  disk: {
@@ -4114,8 +4569,22 @@ var generatedTools = [
4114
4569
  }
4115
4570
  }
4116
4571
  },
4572
+ BaseMounts: {
4573
+ type: "object",
4574
+ properties: {
4575
+ persistent: {
4576
+ $ref: "#/$defs/PersistentMount"
4577
+ },
4578
+ network: {
4579
+ type: "array",
4580
+ maxItems: 1,
4581
+ items: {
4582
+ $ref: "#/$defs/NetworkMount"
4583
+ }
4584
+ }
4585
+ }
4586
+ },
4117
4587
  ContainerConfig: {
4118
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
4119
4588
  allOf: [
4120
4589
  {
4121
4590
  $ref: "#/$defs/BaseContainerConfig"
@@ -4132,28 +4601,51 @@ var generatedTools = [
4132
4601
  }
4133
4602
  ]
4134
4603
  },
4135
- Mounts: {
4604
+ GlobalMount: {
4136
4605
  type: "object",
4606
+ required: ["volumeId"],
4137
4607
  additionalProperties: false,
4138
- description: "Storage mounts attached to a pod. At-most-one of `persistent` or\n`network` may be set today (mutually exclusive, enforced at the\nhandler with 400 if both are present). The `network` field is an\narray for forward compatibility with eventual multi-network-volume\nsupport, but `maxItems` is 1 today.\n\nPATCH semantics:\n- Omitting `mounts` or sending `{}` leaves the existing mount\n unchanged.\n- An explicit `network: []` is rejected with 400 (clearing mounts\n is not supported).\n- Mount kind is fixed at create \u2014 a PATCH that introduces a kind\n not present at create (persistent on a network pod, network on\n a persistent pod, or any mount on a previously-mountless pod)\n is rejected with 400.\n- The `volumeId` of a network mount is immutable; a PATCH that\n names a different `volumeId` is rejected with 400.\n- Partial mounts are not supported \u2014 every mount entry must\n include the full schema (`size` + `path` for persistent,\n `volumeId` + `path` for network). Missing required fields \u2192 422.\n",
4608
+ description: "Reference to a GlobalVolume. Usage-billed, so it has no size. The underlying `volumeId` is immutable post-create; the mount `path` may be changed via PATCH.",
4139
4609
  properties: {
4140
- persistent: {
4141
- $ref: "#/$defs/PersistentMount"
4610
+ volumeId: {
4611
+ type: "string",
4612
+ description: "ID of an existing GlobalVolume.",
4613
+ examples: ["gsb_abc"]
4142
4614
  },
4143
- network: {
4144
- type: "array",
4145
- maxItems: 1,
4146
- items: {
4147
- $ref: "#/$defs/NetworkMount"
4148
- }
4615
+ path: {
4616
+ type: "string",
4617
+ description: "Mount path inside the container. Optional on create, where a default is chosen for you; a PATCH that omits it leaves the path unchanged.",
4618
+ examples: ["/data"]
4149
4619
  }
4150
4620
  }
4151
4621
  },
4622
+ Mounts: {
4623
+ description: "Storage mounts for the pod: `persistent` (size + path) excludes both `network` and `global`; `network` (volumeId + path) and `global` (volumeId, optional path) take one entry each and can be combined. Every entry needs its full shape. On update, omit the field to leave the mounts as they are; the mount kind and a volume's volumeId cannot change, and mounts cannot be cleared.",
4624
+ allOf: [
4625
+ {
4626
+ $ref: "#/$defs/BaseMounts"
4627
+ },
4628
+ {
4629
+ type: "object",
4630
+ properties: {
4631
+ global: {
4632
+ type: "array",
4633
+ maxItems: 1,
4634
+ description: "At most one global volume, on GPU and CPU pods, with no data center constraint. Combinable with `network` but not with `persistent` (rejected): upstream drops a persistent disk whenever a volume is attached.",
4635
+ items: {
4636
+ $ref: "#/$defs/GlobalMount"
4637
+ }
4638
+ }
4639
+ }
4640
+ }
4641
+ ],
4642
+ unevaluatedProperties: false
4643
+ },
4152
4644
  NetworkMount: {
4153
4645
  type: "object",
4154
4646
  required: ["volumeId", "path"],
4155
4647
  additionalProperties: false,
4156
- description: "Reference to a NetworkVolume. Custom paths are honored at runtime on\nboth GPU and CPU pods. The underlying `volumeId` is immutable\npost-create; the mount `path` may be changed via PATCH.\n",
4648
+ description: "Reference to a NetworkVolume. Custom paths are honored at runtime on both GPU and CPU pods. The underlying `volumeId` is immutable post-create; the mount `path` may be changed via PATCH.",
4157
4649
  properties: {
4158
4650
  volumeId: {
4159
4651
  type: "string",
@@ -4171,7 +4663,7 @@ var generatedTools = [
4171
4663
  type: "object",
4172
4664
  required: ["size", "path"],
4173
4665
  additionalProperties: false,
4174
- description: "Host-local persistent storage. Pinned to the pod's host machine \u2014 data\ndoes not survive a host failure. Disallowed on CPU pods. Mutually\nexclusive with NetworkMount. Deprecated: prefer NetworkMount for any\ndata you cannot recreate.\n",
4666
+ description: "Host-local persistent storage. Pinned to the pod's host machine \u2014 data does not survive a host failure. Disallowed on CPU pods. Mutually exclusive with NetworkMount. Deprecated: prefer NetworkMount for any data you cannot recreate.",
4175
4667
  properties: {
4176
4668
  size: {
4177
4669
  type: "integer",
@@ -4196,7 +4688,7 @@ var generatedTools = [
4196
4688
  properties: {
4197
4689
  globalNetworking: {
4198
4690
  type: "boolean",
4199
- description: "Enable (true) or disable (false) global networking. Takes effect on the next pod start/restart, not live. Requires an NVIDIA GPU and a global-networking-enabled data center (both enforced upstream). See `GET /v2/catalog/datacenters` (`globalNetwork`) for eligible data centers."
4691
+ description: "Enable (true) or disable (false) global networking. Takes effect on the next pod start/restart, not live. Requires an NVIDIA GPU and a global-networking-enabled data center (both enforced upstream)."
4200
4692
  },
4201
4693
  locked: {
4202
4694
  type: "boolean",
@@ -4211,7 +4703,7 @@ var generatedTools = [
4211
4703
  },
4212
4704
  templateId: {
4213
4705
  type: "string",
4214
- description: "ID of a pod template whose container settings are applied as\nif they were provided in this PATCH body (image, args, disk,\nports, env, registry \u2014 mounts are not applied on update).\nExplicit body fields override the template's; `env` merges\ntemplate and body per key (body wins) and, per PATCH\nsemantics, replaces the pod's env. One-time application \u2014\nno link to the template is retained. Must be one of your\ntemplates or a public template (unknown or inaccessible ID\n\u2192 404); must not be a serverless template (\u2192 422).\n",
4706
+ description: "ID of a pod template whose container settings (image, args, disk, ports, env, registry; not mounts) are applied as if sent in this PATCH body; explicit body fields win. `env` merges template and body per key (body wins) and replaces the pod's existing env. Applied once; must be a non-serverless template you can access.",
4215
4707
  examples: ["30zmvf89kd"]
4216
4708
  }
4217
4709
  }
@@ -4241,6 +4733,7 @@ var generatedTools = [
4241
4733
  idempotentHint: false,
4242
4734
  openWorldHint: true
4243
4735
  },
4736
+ schemaOnDemand: true,
4244
4737
  inputSchema: {
4245
4738
  type: "object",
4246
4739
  properties: {
@@ -4258,7 +4751,7 @@ var generatedTools = [
4258
4751
  type: "object",
4259
4752
  additionalProperties: false,
4260
4753
  minProperties: 1,
4261
- description: "Only the provided fields are updated; at least one field is required.\nThe secret's `name` is immutable.\n",
4754
+ description: "Only the provided fields are updated; at least one field is required. The secret's `name` is immutable.",
4262
4755
  properties: {
4263
4756
  value: {
4264
4757
  type: "string",
@@ -4310,7 +4803,7 @@ var generatedTools = [
4310
4803
  type: "string",
4311
4804
  pattern: "^(ssh|ecdsa|sk)-[^\\s]+ [^\\s]+([ \\t][^\\n\\r]*)?$"
4312
4805
  },
4313
- description: "The full set of SSH public keys to register \u2014 this is a complete\nreplacement, not a merge. Each entry is an authorized_keys-style\nline: `<type> <base64-key> [comment]`, e.g. from\n`~/.ssh/id_ed25519.pub`. Send `[]` to remove all keys. These keys\nare provisioned into pods created with `startSsh` and\nauthenticate both SSH paths reported in the pod's `ssh` block.\n",
4806
+ description: "The full set of SSH public keys to register \u2014 this is a complete replacement, not a merge. Each entry is an authorized_keys-style line: `<type> <base64-key> [comment]`, e.g. from `~/.ssh/id_ed25519.pub`. Send `[]` to remove all keys.",
4314
4807
  examples: [
4315
4808
  [
4316
4809
  "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILXGDN/SclOozk1xsDztpmhGiKkkrfQB9SKoO8dSIQQZ me@example.com"
@@ -4341,6 +4834,7 @@ var generatedTools = [
4341
4834
  idempotentHint: false,
4342
4835
  openWorldHint: true
4343
4836
  },
4837
+ schemaOnDemand: true,
4344
4838
  inputSchema: {
4345
4839
  type: "object",
4346
4840
  properties: {
@@ -4355,11 +4849,10 @@ var generatedTools = [
4355
4849
  $defs: {
4356
4850
  BaseContainerConfig: {
4357
4851
  type: "object",
4358
- description: "Container configuration universal to every containerized resource. Compose ContainerConfig instead unless the resource cannot support a direct registry credential (clusters \u2014 there the registry credential arrives via a pod template, see CreateClusterRequest.templateId).\n",
4359
4852
  properties: {
4360
4853
  args: {
4361
4854
  type: "string",
4362
- description: 'The container\'s command, as a single raw string. This is the field `entrypoint` and `cmd` encode into, exposed in its stored form.\n\nTwo shapes are accepted. A bare shell string is treated as CMD and split into arguments, which is what the console\'s "Container start command" field writes. A JSON object of the form `{"entrypoint":[...],"cmd":[...]}` sets either or both explicitly.\n\nResponses always return both representations: `args` exactly as stored, plus the deconstructed `entrypoint` and `cmd`. Supplying `args` together with `entrypoint` or `cmd` is allowed only when they describe the same command, so a read-modify-write client can send back everything it received. Send `""` to clear, omit to leave unchanged.\n',
4855
+ description: 'The container\'s command as one raw string: a bare shell string is treated as CMD and split into arguments; a JSON object `{"entrypoint":[...],"cmd":[...]}` sets either or both. Send `""` to clear; omit to leave unchanged.',
4363
4856
  examples: [
4364
4857
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
4365
4858
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -4371,7 +4864,7 @@ var generatedTools = [
4371
4864
  items: {
4372
4865
  type: "string"
4373
4866
  },
4374
- description: "Container CMD in exec form. When the image defines an ENTRYPOINT, this is the argument list passed to it. Encoded into the `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.\n",
4867
+ description: "Container CMD in exec form: when the image defines an ENTRYPOINT, the argument list passed to it. Encoded into `args`; supply both only when they describe the same command. Send `[]` to clear; omit to leave unchanged.",
4375
4868
  examples: [
4376
4869
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
4377
4870
  ]
@@ -4381,7 +4874,7 @@ var generatedTools = [
4381
4874
  items: {
4382
4875
  type: "string"
4383
4876
  },
4384
- description: "Container ENTRYPOINT in exec form, overriding the image's own. Encoded into `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.\n",
4877
+ description: "Container ENTRYPOINT in exec form, overriding the image's own. Encoded into `args` field; supplying both is allowed only when they describe the same command. Send `[]` to clear, omit to leave unchanged.",
4385
4878
  examples: [["/bin/bash", "-c"]]
4386
4879
  },
4387
4880
  disk: {
@@ -4418,7 +4911,6 @@ var generatedTools = [
4418
4911
  }
4419
4912
  },
4420
4913
  ContainerConfig: {
4421
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
4422
4914
  allOf: [
4423
4915
  {
4424
4916
  $ref: "#/$defs/BaseContainerConfig"
@@ -4439,7 +4931,7 @@ var generatedTools = [
4439
4931
  type: "object",
4440
4932
  required: ["size", "path"],
4441
4933
  additionalProperties: false,
4442
- description: "Host-local persistent storage. Pinned to the pod's host machine \u2014 data\ndoes not survive a host failure. Disallowed on CPU pods. Mutually\nexclusive with NetworkMount. Deprecated: prefer NetworkMount for any\ndata you cannot recreate.\n",
4934
+ description: "Host-local persistent storage. Pinned to the pod's host machine \u2014 data does not survive a host failure. Disallowed on CPU pods. Mutually exclusive with NetworkMount. Deprecated: prefer NetworkMount for any data you cannot recreate.",
4443
4935
  properties: {
4444
4936
  size: {
4445
4937
  type: "integer",
@@ -4456,13 +4948,13 @@ var generatedTools = [
4456
4948
  },
4457
4949
  TemplateCategory: {
4458
4950
  type: "string",
4459
- description: "Controls how the template is grouped and filtered in the Runpod console.\nIt does not affect hardware selection, scheduling, or billing.\n- `CPU` \u2014 CPU-only workloads\n- `NVIDIA` \u2014 NVIDIA GPU workloads\n- `AMD` \u2014 AMD GPU workloads\n",
4951
+ description: "Controls how the template is grouped and filtered in the Runpod console. It does not affect hardware selection, scheduling, or billing. `CPU` \u2014 CPU-only workloads. `NVIDIA` \u2014 NVIDIA GPU workloads. `AMD` \u2014 AMD GPU workloads",
4460
4952
  enum: ["CPU", "NVIDIA", "AMD"]
4461
4953
  },
4462
4954
  TemplateMounts: {
4463
4955
  type: "object",
4464
4956
  additionalProperties: false,
4465
- description: "Storage mounts attached to a template. Templates support only a\nsingle persistent mount today; any `network` property is rejected\nwith 422 by the schema validator.\n\nPATCH semantics: omitting `mounts` or sending `{}` leaves the\nexisting mount unchanged.\n",
4957
+ description: "Storage mounts for containers from this template: a single `persistent` mount only (`network` is rejected). On update, omitting `mounts` or sending `{}` leaves the existing mount unchanged.",
4466
4958
  properties: {
4467
4959
  persistent: {
4468
4960
  $ref: "#/$defs/PersistentMount"
@@ -5211,6 +5703,8 @@ var listHubRepos = {
5211
5703
  };
5212
5704
  var deployHubRepo = {
5213
5705
  name: "deploy-hub-repo",
5706
+ // Two sequential upstream calls: together they can outlast a client's request timeout.
5707
+ holdsResponse: true,
5214
5708
  annotations: write,
5215
5709
  description: "Deploy a Runpod Hub repo's listed release as a new Serverless endpoint (the same as clicking Deploy on the Hub). Identify the repo by `repo` (\"owner/name\" from list-hub-repos) or by `hubReleaseId`. The release supplies the prebuilt image, container disk, CUDA constraints, and env-var defaults; pass `env` to override or fill in values (required keys without a default must be provided \u2014 check list-hub-repos with includeConfig:true for the schema). GPU selection comes from the release config when it specifies one; otherwise pass gpuIds (GPU pool names, e.g. 'ADA_24' or 'ADA_80_PRO,AMPERE_80'). Uses the authenticated GraphQL API (no REST home for Hub deploys yet).",
5216
5710
  inputSchema: {
@@ -5699,6 +6193,7 @@ var runEndpoint = {
5699
6193
  };
5700
6194
  var runsyncEndpoint = {
5701
6195
  name: "runsync-endpoint",
6196
+ holdsResponse: true,
5702
6197
  annotations: write,
5703
6198
  description: "Submit a synchronous job to a Serverless endpoint and wait for the result. Best for fast tasks: if the job outlives the wait, the response returns a job ID and a non-terminal status (IN_QUEUE or IN_PROGRESS) to poll with get-job-status. Max payload 20 MB; results expire after 1 minute. The wait parameter extends the server-side wait" + (HOSTED ? ` \u2014 this server runs behind a 60-second gateway deadline, so waits clamp to ${HTTP_LONG_POLL_BUDGET_MS} ms, which is also the effective default. For longer jobs use run-endpoint + get-job-status.` : " to up to 5 minutes (300000 ms); it defaults to 90000 (90 seconds)."),
5704
6199
  inputSchema: {
@@ -5743,6 +6238,7 @@ var runsyncEndpoint = {
5743
6238
  };
5744
6239
  var getJobStatus = {
5745
6240
  name: "get-job-status",
6241
+ holdsResponse: true,
5746
6242
  annotations: readOnly,
5747
6243
  description: "Check the status of a Serverless job. Returns the current status and output when complete. Job statuses: IN_QUEUE, IN_PROGRESS, COMPLETED, FAILED, CANCELLED, TIMED_OUT. Pass `wait` (milliseconds, up to " + STATUS_WAIT_MAX_MS + ") to BLOCK server-side, polling until the job reaches a terminal status or the budget expires \u2014 use it to ride out a cold start (a first job on a fresh worker pulls the image and loads the model, commonly 1\u20135+ minutes) in a single call instead of returning on the first IN_QUEUE and tight-looping the tool. Without `wait` it returns the current status immediately (a single check). If the budget expires before a terminal status, it returns the latest non-terminal status with pollingTimedOut:true \u2014 call again (with `wait`) to keep blocking. IMPORTANT: a job stuck IN_QUEUE does not necessarily mean a capacity shortage \u2014 a worker may have spun up and crash-looped. When the job is IN_QUEUE this tool attaches a workerHealth summary and a hint. To dig deeper call list-endpoint-workers, then read stream-worker-logs regardless of the status a worker reports: a container failing to start loops while its worker still shows RUNNING or THROTTLED and unhealthy stays 0, so UNHEALTHY is one symptom of a crash loop and not a test for it. The logs are the authority.",
5748
6244
  inputSchema: {
@@ -5791,6 +6287,7 @@ var getJobStatus = {
5791
6287
  };
5792
6288
  var streamJob = {
5793
6289
  name: "stream-job",
6290
+ holdsResponse: true,
5794
6291
  annotations: readOnly,
5795
6292
  description: `Retrieve streaming output from a Serverless job. The worker must support streaming output. Polls /stream/{jobId} and collects chunks until the status is COMPLETED, FAILED, CANCELLED, or TIMED_OUT, for up to ${formatBudget(STREAM_BUDGET_MS)}. If the budget expires first, returns the chunks collected so far with pollingTimedOut: true \u2014 call stream-job again to resume where it left off, or get-job-status to check without streaming.`,
5796
6293
  inputSchema: {
@@ -6007,7 +6504,7 @@ var listTemplates = {
6007
6504
  }
6008
6505
  };
6009
6506
 
6010
- // node_modules/.pnpm/@runpod+typescript-api-sdk@0.2.0/node_modules/@runpod/typescript-api-sdk/dist/index.js
6507
+ // node_modules/.pnpm/@runpod+typescript-api-sdk@0.3.0/node_modules/@runpod/typescript-api-sdk/dist/index.js
6011
6508
  var PATH_PARAM_RE = /\{[^{}]+\}/g;
6012
6509
  var supportsRequestInitExt = () => {
6013
6510
  return typeof process === "object" && Number.parseInt(process?.versions?.node?.substring(0, 2)) >= 18 && process.versions.undici;
@@ -7411,6 +7908,7 @@ function logParams(args) {
7411
7908
  }
7412
7909
  var streamPodLogs = {
7413
7910
  name: "stream-pod-logs",
7911
+ holdsResponse: true,
7414
7912
  annotations: readOnly,
7415
7913
  description: "Read a bounded snapshot of a Pod's logs (container and/or system source). Holds the live stream open for maxWaitMs and returns the parsed log lines; `truncated: true` means the byte cap cut the output. Use `since` to resume from a timestamp.",
7416
7914
  inputSchema: {
@@ -7433,6 +7931,7 @@ var streamPodLogs = {
7433
7931
  };
7434
7932
  var streamWorkerLogs = {
7435
7933
  name: "stream-worker-logs",
7934
+ holdsResponse: true,
7436
7935
  annotations: readOnly,
7437
7936
  description: "Read a bounded snapshot of a Serverless worker's logs (container and/or system source). Holds the live stream open for maxWaitMs and returns the parsed log lines; `truncated: true` means the byte cap cut the output. Get worker IDs from list-endpoint-workers. Use this whenever a job stays IN_QUEUE while workers exist, whatever status they report \u2014 a crash-looping container often still shows RUNNING or THROTTLED with unhealthy 0, so these logs, not the worker status, are what identify a crash loop.",
7438
7937
  inputSchema: {
@@ -7697,6 +8196,7 @@ var source = {
7697
8196
  var listGuides = {
7698
8197
  name: "list-guides",
7699
8198
  annotations: bundledRead,
8199
+ recordedArguments: ["kind", "query", "concept"],
7700
8200
  description: "List the official Runpod guides: skills (how to work with each tool), reference docs (how Runpod works), and golden paths (live-verified end-to-end walkthroughs such as a dev pod, a Whisper endpoint or a fine-tune-to-serverless pipeline). Read one with read-guide. For a golden path, mcp says whether an agent with only these MCP tools can finish it: full (follow it with tool calls), partial (use it for the plan and facts; name the step the user must run in a terminal), none (reference only). Skills and reference docs have mcp null. concepts lists the concept ids it works with (see lookup-concept). Filter with kind, query or concept.",
7701
8201
  inputSchema: {
7702
8202
  type: "object",
@@ -7732,6 +8232,8 @@ var listGuides = {
7732
8232
  var readGuide = {
7733
8233
  name: "read-guide",
7734
8234
  annotations: bundledRead,
8235
+ recordedArguments: ["id"],
8236
+ recordedGuideKind: (args) => knowledge.guides.find((guide) => guide.id === String(args.id).trim())?.kind,
7735
8237
  description: 'Read one official Runpod guide in full, by the id list-guides returns (for example "runpod-usage/storage" or "golden-path/06-dev-pod").',
7736
8238
  inputSchema: {
7737
8239
  type: "object",
@@ -7769,6 +8271,7 @@ var readGuide = {
7769
8271
  var lookupConcept = {
7770
8272
  name: "lookup-concept",
7771
8273
  annotations: bundledRead,
8274
+ recordedArguments: ["concept"],
7772
8275
  description: 'Look up one Runpod concept (pod, network volume, machine, worker, template, \u2026) by id, name or alias; case, plurals and small typos are tolerated, and `matched_as` shows the key used when the match was not exact. Returns its summary, fields, states, relations and rules, each rule with the public source it comes from, the concepts that point at it, and the linked guides: examples (golden paths that use it, with their mcp level) and docs (skills and reference docs that explain it). Use it for exact facts, for example "is a volume disk tied to one machine" or "what happens to a stopped pod\'s GPU".',
7773
8276
  inputSchema: {
7774
8277
  type: "object",
@@ -7821,6 +8324,7 @@ var lookupConcept = {
7821
8324
  var searchConcepts = {
7822
8325
  name: "search-concepts",
7823
8326
  annotations: bundledRead,
8327
+ recordedArguments: ["query", "limit"],
7824
8328
  description: 'Search the rules of the Runpod concept graph by words, for example "restart stopped pod no gpu" or "network volume data center". Returns the best-matching rules with their concept and public evidence. Follow up with lookup-concept for the full concept.',
7825
8329
  inputSchema: {
7826
8330
  type: "object",
@@ -7885,6 +8389,140 @@ var guideResources = knowledge.guides.map((guide) => ({
7885
8389
  text: guide.body
7886
8390
  }));
7887
8391
 
8392
+ // src/specgen/prose.ts
8393
+ var ABBREVIATIONS = ["e.g.", "i.e.", "etc.", "vs."];
8394
+ var DOT = "\0";
8395
+ function splitSentences(text) {
8396
+ let guarded = text;
8397
+ for (const abbreviation of ABBREVIATIONS) {
8398
+ guarded = guarded.replaceAll(
8399
+ abbreviation,
8400
+ abbreviation.replaceAll(".", DOT)
8401
+ );
8402
+ }
8403
+ return guarded.split(/(?<=[.!?])\s+(?=[A-Z`"'([])/).map((sentence) => sentence.replaceAll(DOT, ".").trim()).filter(Boolean);
8404
+ }
8405
+ function leadSentence(text) {
8406
+ return splitSentences(text)[0] ?? text;
8407
+ }
8408
+
8409
+ // src/specgen/on-demand.ts
8410
+ var DESCRIBE_TOOL_NAME = "describe-tool";
8411
+ var ON_DEMAND_POINTER = ` Call ${DESCRIBE_TOOL_NAME} for the full argument schema before the first use.`;
8412
+ function listedAnnotations(annotations) {
8413
+ const { idempotentHint, ...listed } = annotations;
8414
+ return idempotentHint ? annotations : listed;
8415
+ }
8416
+ function listTool(tool) {
8417
+ const { name, description, inputSchema } = tool;
8418
+ const annotations = listedAnnotations(tool.annotations);
8419
+ if (!tool.schemaOnDemand)
8420
+ return { name, description, inputSchema, annotations };
8421
+ return {
8422
+ name,
8423
+ description: description + ON_DEMAND_POINTER,
8424
+ inputSchema: callShape(inputSchema),
8425
+ annotations
8426
+ };
8427
+ }
8428
+ function callShape(schema) {
8429
+ const defs = schema.$defs ?? {};
8430
+ const resolve = (node) => {
8431
+ if (typeof node.$ref !== "string") return node;
8432
+ const name = node.$ref.split("/").pop() ?? "";
8433
+ return defs[name] ?? {};
8434
+ };
8435
+ const properties = Object.fromEntries(
8436
+ Object.entries(schema.properties ?? {}).map(
8437
+ ([key, value]) => [key, shallowProperty(resolve(value), resolve)]
8438
+ )
8439
+ );
8440
+ return {
8441
+ type: "object",
8442
+ properties,
8443
+ ...schema.required ? { required: schema.required } : {}
8444
+ };
8445
+ }
8446
+ function shallowProperty(node, resolve) {
8447
+ const out = {};
8448
+ const type = schemaType(node, resolve);
8449
+ if (type !== void 0) out.type = type;
8450
+ if (type === "array") {
8451
+ const items = resolve(node.items ?? {});
8452
+ const itemType = schemaType(items, resolve);
8453
+ if (itemType !== void 0) out.items = { type: itemType };
8454
+ }
8455
+ if (Array.isArray(node.enum)) out.enum = node.enum;
8456
+ if (typeof node.description === "string")
8457
+ out.description = leadSentence(node.description);
8458
+ return out;
8459
+ }
8460
+ function schemaType(node, resolve) {
8461
+ if (node.type !== void 0) return node.type;
8462
+ if (node.properties || node.allOf) return "object";
8463
+ const branches2 = node.anyOf ?? node.oneOf;
8464
+ const allObjects = branches2?.every(
8465
+ (branch) => schemaType(resolve(branch), resolve) === "object"
8466
+ ) ?? false;
8467
+ return allObjects ? "object" : void 0;
8468
+ }
8469
+
8470
+ // src/specgen/tools/describe.ts
8471
+ function createDescribeTool(served) {
8472
+ return {
8473
+ name: DESCRIBE_TOOL_NAME,
8474
+ description: "Return one tool's full definition: its description, complete argument schema (every field, nested objects, constraints) and annotations. Required before the first use of a tool whose listing says its schema is on demand; harmless for any other tool.",
8475
+ inputSchema: {
8476
+ type: "object",
8477
+ properties: {
8478
+ name: {
8479
+ type: "string",
8480
+ description: "A tool name exactly as this server's tool list spells it."
8481
+ }
8482
+ },
8483
+ required: ["name"]
8484
+ },
8485
+ annotations: {
8486
+ readOnlyHint: true,
8487
+ destructiveHint: false,
8488
+ idempotentHint: true,
8489
+ openWorldHint: false
8490
+ },
8491
+ handler: async (_ctx, args) => {
8492
+ const name = String(args.name ?? "");
8493
+ const tools = served();
8494
+ const tool = tools.find((candidate) => candidate.name === name);
8495
+ if (!tool) {
8496
+ return {
8497
+ ok: false,
8498
+ status: 404,
8499
+ payload: {
8500
+ error: `No tool named ${name}.`,
8501
+ suggestions: similarNames(
8502
+ name,
8503
+ tools.map((candidate) => candidate.name)
8504
+ ),
8505
+ hint: "Tool names are exactly as this server lists them; the suggestions share a word with the name you sent."
8506
+ }
8507
+ };
8508
+ }
8509
+ const { description, inputSchema, annotations } = tool;
8510
+ return ok({ name: tool.name, description, inputSchema, annotations });
8511
+ }
8512
+ };
8513
+ }
8514
+ function similarNames(query, names) {
8515
+ const wanted = new Set(words2(query));
8516
+ return names.map((name) => {
8517
+ const own = new Set(words2(name));
8518
+ const shared = [...own].filter((word) => wanted.has(word)).length;
8519
+ return { name, score: shared / (/* @__PURE__ */ new Set([...own, ...wanted])).size };
8520
+ }).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || a.name.localeCompare(b.name)).slice(0, 8).map((entry) => entry.name);
8521
+ }
8522
+ function words2(name) {
8523
+ return name.toLowerCase().split(/[-_\s]+/).filter(Boolean).map((word) => word.replace(/s$/, ""));
8524
+ }
8525
+
7888
8526
  // src/specgen/analytics.ts
7889
8527
  import { createHmac } from "crypto";
7890
8528
  var CAPTURE_TIMEOUT_MS = 3e3;
@@ -7899,7 +8537,22 @@ function analyticsCallerId(apiKey, accountId) {
7899
8537
  const prefix = accountId ? "a:" : "k:";
7900
8538
  return prefix + createHmac("sha256", salt).update(identity).digest("hex").slice(0, 16);
7901
8539
  }
7902
- function captureToolCall(apiKey, event) {
8540
+ var MAX_RECORDED_VALUE_LENGTH = 200;
8541
+ function bounded(value) {
8542
+ return value.slice(0, MAX_RECORDED_VALUE_LENGTH);
8543
+ }
8544
+ function recordedArgumentValues(names, args) {
8545
+ if (!names?.length) return void 0;
8546
+ const values = {};
8547
+ for (const name of names) {
8548
+ const value = args[name];
8549
+ if (typeof value === "string" || typeof value === "number") {
8550
+ values[name] = bounded(String(value).trim());
8551
+ }
8552
+ }
8553
+ return Object.keys(values).length ? values : void 0;
8554
+ }
8555
+ function capture(event, distinctId, properties) {
7903
8556
  const posthogKey = process.env.POSTHOG_API_KEY;
7904
8557
  if (!posthogKey) return;
7905
8558
  void fetch(`${posthogHost()}/capture/`, {
@@ -7907,18 +8560,12 @@ function captureToolCall(apiKey, event) {
7907
8560
  headers: { "Content-Type": "application/json" },
7908
8561
  body: JSON.stringify({
7909
8562
  api_key: posthogKey,
7910
- event: "runpod_mcp_tool_call",
7911
- distinct_id: analyticsCallerId(apiKey, event.accountId),
8563
+ event,
8564
+ distinct_id: distinctId,
7912
8565
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7913
8566
  properties: {
7914
- tool: event.tool,
7915
- ok: event.ok,
7916
- status: event.status,
7917
- duration_ms: event.durationMs,
7918
- transport: event.transport,
7919
- server_version: event.serverVersion,
8567
+ ...properties,
7920
8568
  surface_version: SURFACE_VERSION,
7921
- ...event.clientName ? { client_name: event.clientName } : {},
7922
8569
  $process_person_profile: false
7923
8570
  }
7924
8571
  }),
@@ -7926,9 +8573,48 @@ function captureToolCall(apiKey, event) {
7926
8573
  }).catch(() => {
7927
8574
  });
7928
8575
  }
8576
+ function captureToolCall(apiKey, event) {
8577
+ const args = Object.fromEntries(
8578
+ Object.entries(event.args ?? {}).map(([name, value]) => [
8579
+ `arg_${name}`,
8580
+ value
8581
+ ])
8582
+ );
8583
+ capture("runpod_mcp_tool_call", analyticsCallerId(apiKey, event.accountId), {
8584
+ tool: event.tool,
8585
+ ok: event.ok,
8586
+ status: event.status,
8587
+ duration_ms: event.durationMs,
8588
+ transport: event.transport,
8589
+ server_version: event.serverVersion,
8590
+ ...event.clientName ? { client_name: event.clientName } : {},
8591
+ ...event.guideKind ? { guide_kind: event.guideKind } : {},
8592
+ ...event.blockedBy ? { blocked_by: event.blockedBy } : {},
8593
+ ...args
8594
+ });
8595
+ }
8596
+ function captureResourceCall(apiKey, event) {
8597
+ capture(
8598
+ "runpod_mcp_resource_call",
8599
+ analyticsCallerId(apiKey, event.accountId),
8600
+ {
8601
+ method: event.method,
8602
+ uri: bounded(event.uri),
8603
+ ok: event.ok,
8604
+ transport: event.transport,
8605
+ server_version: event.serverVersion,
8606
+ ...event.clientName ? { client_name: event.clientName } : {}
8607
+ }
8608
+ );
8609
+ }
7929
8610
 
7930
8611
  // src/http.ts
7931
8612
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
8613
+ import {
8614
+ CallToolRequestSchema,
8615
+ InitializeRequestSchema,
8616
+ JSONRPCMessageSchema
8617
+ } from "@modelcontextprotocol/sdk/types.js";
7932
8618
 
7933
8619
  // src/specgen/context.ts
7934
8620
  import { randomUUID } from "crypto";
@@ -8091,14 +8777,89 @@ function createToolContext(options = {}) {
8091
8777
  };
8092
8778
  }
8093
8779
 
8780
+ // src/specgen/ops.ts
8781
+ import { createHash, randomBytes } from "crypto";
8782
+ var SALT = randomBytes(16).toString("hex");
8783
+ function callerId(token) {
8784
+ if (!token) return "anonymous";
8785
+ return createHash("sha256").update(SALT).update(token).digest("hex").slice(0, 12);
8786
+ }
8787
+ var noopRateLimiter = async () => ({ allowed: true });
8788
+ function logToolCall(entry) {
8789
+ console.error("tool_call", JSON.stringify(entry));
8790
+ }
8791
+
8792
+ // src/search/knowledge.ts
8793
+ var SEARCH_NAMESPACES = ["docs", "knowledge", "blog", "site"];
8794
+ var MAX_QUERY_CHARS = 2e3;
8795
+ var MAX_SEARCH_LIMIT = 20;
8796
+ var UNREACHABLE = {
8797
+ status: 504,
8798
+ body: {
8799
+ error: "Runpod knowledge search did not respond.",
8800
+ hint: "Retry the same call once."
8801
+ }
8802
+ };
8803
+ function parseSearchRequest(raw) {
8804
+ if (typeof raw.query !== "string" || !raw.query.trim()) {
8805
+ return { error: "query must be a non-empty string." };
8806
+ }
8807
+ if (raw.query.length > MAX_QUERY_CHARS) {
8808
+ return { error: `query exceeds ${MAX_QUERY_CHARS} characters.` };
8809
+ }
8810
+ const request = { query: raw.query };
8811
+ if (raw.namespaces !== void 0) {
8812
+ if (!Array.isArray(raw.namespaces) || raw.namespaces.length === 0 || !raw.namespaces.every(
8813
+ (ns) => SEARCH_NAMESPACES.includes(ns)
8814
+ )) {
8815
+ return {
8816
+ error: `namespaces must be a non-empty list of: ${SEARCH_NAMESPACES.join(", ")}.`
8817
+ };
8818
+ }
8819
+ request.namespaces = raw.namespaces;
8820
+ }
8821
+ if (raw.limit !== void 0) {
8822
+ if (typeof raw.limit !== "number" || !Number.isInteger(raw.limit) || raw.limit < 1 || raw.limit > MAX_SEARCH_LIMIT) {
8823
+ return {
8824
+ error: `limit must be an integer from 1 to ${MAX_SEARCH_LIMIT}.`
8825
+ };
8826
+ }
8827
+ request.limit = raw.limit;
8828
+ }
8829
+ return { request };
8830
+ }
8831
+ var HOSTED_CLIENT_TIMEOUT_MS = 55e3;
8832
+ function hostedSearch(searchUrl, doFetch = fetch) {
8833
+ return async (request, apiKey) => {
8834
+ try {
8835
+ const res = await doFetch(searchUrl, {
8836
+ method: "POST",
8837
+ headers: {
8838
+ "Content-Type": "application/json",
8839
+ ...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
8840
+ },
8841
+ body: JSON.stringify(request),
8842
+ signal: AbortSignal.timeout(HOSTED_CLIENT_TIMEOUT_MS)
8843
+ });
8844
+ const body = await res.json().catch(() => null);
8845
+ return body ? { status: res.status, body } : {
8846
+ status: 502,
8847
+ body: { error: `Runpod knowledge search returned ${res.status}.` }
8848
+ };
8849
+ } catch {
8850
+ return UNREACHABLE;
8851
+ }
8852
+ };
8853
+ }
8854
+
8094
8855
  // src/_shared/credential-check.ts
8095
- import { createHash } from "crypto";
8856
+ import { createHash as createHash2 } from "crypto";
8096
8857
  var VALID_TTL_MS = 6e4;
8097
8858
  var INVALID_TTL_MS = 3e4;
8098
8859
  var REQUEST_TIMEOUT_MS = 4e3;
8099
8860
  var MAX_CACHE_ENTRIES = 1e3;
8100
8861
  function hashToken(token) {
8101
- return createHash("sha256").update(token).digest("hex");
8862
+ return createHash2("sha256").update(token).digest("hex");
8102
8863
  }
8103
8864
  function createCredentialChecker(deps) {
8104
8865
  const now = deps.now ?? (() => performance.now());
@@ -8421,16 +9182,48 @@ function createAlpTools(opts) {
8421
9182
  return [reportFeedback, saveToJournal, askQuestion, readJournalTool];
8422
9183
  }
8423
9184
 
8424
- // src/specgen/ops.ts
8425
- import { createHash as createHash2, randomBytes } from "crypto";
8426
- var SALT = randomBytes(16).toString("hex");
8427
- function callerId(token) {
8428
- if (!token) return "anonymous";
8429
- return createHash2("sha256").update(SALT).update(token).digest("hex").slice(0, 12);
8430
- }
8431
- var noopRateLimiter = async () => ({ allowed: true });
8432
- function logToolCall(entry) {
8433
- console.error("tool_call", JSON.stringify(entry));
9185
+ // src/specgen/tools/search.ts
9186
+ function createSearchDocsTool(search) {
9187
+ return {
9188
+ name: "search-docs",
9189
+ annotations: readOnly,
9190
+ recordedArguments: ["limit"],
9191
+ description: 'Search Runpod documentation, curated support knowledge, the Runpod blog and runpod.io for passages that answer a question, for example "how do I attach a network volume to a Serverless endpoint". Returns ranked passages with their source URLs. Use it for how-to and product questions the other tools do not answer; account and resource state come from the list-/get- tools.',
9192
+ inputSchema: {
9193
+ type: "object",
9194
+ properties: {
9195
+ query: {
9196
+ type: "string",
9197
+ maxLength: MAX_QUERY_CHARS,
9198
+ description: "The question, in natural language."
9199
+ },
9200
+ namespaces: {
9201
+ type: "array",
9202
+ items: { type: "string", enum: [...SEARCH_NAMESPACES] },
9203
+ minItems: 1,
9204
+ description: "Sources to search: docs (docs.runpod.io), knowledge (curated support answers), blog, site (runpod.io pages). Default: docs, knowledge, blog."
9205
+ },
9206
+ limit: {
9207
+ type: "integer",
9208
+ minimum: 1,
9209
+ maximum: MAX_SEARCH_LIMIT,
9210
+ description: "Most passages to return. Default 8."
9211
+ }
9212
+ },
9213
+ required: ["query"],
9214
+ additionalProperties: false
9215
+ },
9216
+ handler: async (ctx, args) => {
9217
+ const parsed = parseSearchRequest(args);
9218
+ if ("error" in parsed) return badRequest(parsed.error);
9219
+ const outcome = await search(parsed.request, ctx.apiKey);
9220
+ return {
9221
+ ok: outcome.status === 200,
9222
+ status: outcome.status,
9223
+ payload: outcome.body
9224
+ };
9225
+ }
9226
+ };
8434
9227
  }
8435
9228
 
8436
9229
  // src/specgen/server.ts
@@ -8445,6 +9238,9 @@ var curatedTools = [
8445
9238
  listPublicEndpoints,
8446
9239
  ...knowledgeTools
8447
9240
  ];
9241
+ var responseHoldingTools = new Set(
9242
+ curatedTools.filter((tool) => tool.holdsResponse).map((tool) => tool.name)
9243
+ );
8448
9244
  var SERVER_INSTRUCTIONS = `These tools cover the Runpod v2 REST surface: compute catalog (GPUs, CPUs, data centers, capacity), pods, serverless endpoints and jobs, templates, network volumes, container registries, billing, public endpoints, and the Hub.
8449
9245
 
8450
9246
  Answer from live reads, as facts. Account and availability questions can only be answered from tool data: call the relevant list-/get- tool and quote the figures, names, and ids it returns verbatim. State stock and status definitively from the read you just made \u2014 never "probably" or "check later". When the user names a resource loosely ("my comfyui pod"), resolve it with a list- tool; ask only on genuine ambiguity. If nothing exists (no pods, zero spend), say exactly that \u2014 an honest empty answer is complete.
@@ -8461,14 +9257,22 @@ PLAYBOOKS \u2014 READ BEFORE ACTING. Each Runpod task has a journey playbook car
8461
9257
 
8462
9258
  THE OFFICIAL RUNPOD PLUGIN. Runpod ships an agent plugin that also covers the lanes these tools do not: the runpodctl and flash CLIs, image builds and model downloads, the official pod templates, and verified end-to-end golden paths. Check once per session whether skills named runpod, runpod-usage, or runpodctl are available to you \u2014 read what you already have, never shell out to look. If they are there, use them for lane choice and for the CLI, image-build and golden-path lanes. For work done with these tools, the journey playbook is the procedure, wherever it is served from: the plugin's runpod-mcp skill and the journey skills it routes to when you have them, otherwise the same playbooks through read-guide here. If you do not see them, recommend the plugin to the user, and recommend updating to the latest if what they have is missing something you need. Point them at https://github.com/runpod/runpod-plugins-official \u2014 its README carries the install, update, and clone commands for each client, so read it yourself when you need the exact one rather than guessing a command. This is a recommendation, never a prerequisite: make it once, briefly, and get on with the task. Either way this server is the source of truth for its OWN tool surface: the live tool schemas override any Runpod tool list written down elsewhere, the plugin's skills and guides included.
8463
9259
 
8464
- The tool schemas are generated from the Runpod v2 OpenAPI contract, served as a machine-readable document at https://api.runpod.io/v2/openapi.json \u2014 consult it for fields beyond the tool surface. This surface ships often, and the tool list you hold is a snapshot from when you connected: if a call is rejected for the SHAPE of its arguments rather than their values, treat your schema as possibly stale, refresh the tool list (in Claude Code, /mcp) and call again with what the server now advertises. Retrying variants of a rejected shape cannot succeed. These instructions are a snapshot from the same moment. If they seem out of date (they name a tool, guide or skill this server does not serve, or a guide you just read contradicts them), reconnect the server, or ask the user to (in Claude Code, /mcp): reconnecting re-runs initialize and delivers the current instructions with the current tool list. Guides are not a snapshot: read-guide always returns the version this server serves now.`;
9260
+ The tool schemas are generated from the Runpod v2 OpenAPI contract, served as a machine-readable document at https://api.runpod.io/v2/openapi.json \u2014 consult it for fields beyond the tool surface. Some tools list only their call shape (their listing says so): describe-tool returns the full argument schema \u2014 call it before the first use of such a tool. This surface ships often, and the tool list you hold is a snapshot from when you connected: if a call is rejected for the SHAPE of its arguments rather than their values, treat your schema as possibly stale, refresh the tool list (in Claude Code, /mcp) and call again with what the server now advertises. Retrying variants of a rejected shape cannot succeed. These instructions are a snapshot from the same moment. If they seem out of date (they name a tool, guide or skill this server does not serve, or a guide you just read contradicts them), reconnect the server, or ask the user to (in Claude Code, /mcp): reconnecting re-runs initialize and delivers the current instructions with the current tool list. Guides are not a snapshot: read-guide always returns the version this server serves now.`;
8465
9261
  var ALP_INSTRUCTIONS = `
8466
9262
 
8467
9263
  CONTRIBUTING BACK IS OPTIONAL. Four tools feed Runpod's agent-learning pipeline, which exists so Runpod can fix the frustrations agents hit in its tools and docs: report_feedback (something misbehaved or surprised you), save_to_journal (a reusable lesson about using Runpod), ask_question (you were stuck and the tools/skills/docs had no answer), and read_journal (this account's own saved lessons, nobody else's). Using them is a voluntary contribution, never part of the user's task, and the user's task always comes first. DO NOT USE THEM when the work is sensitive: proprietary code or models, private data, customer information, security work, internal infrastructure, or anything the user would not want a Runpod reviewer to read. Submissions are stored for review to improve agent workflows on Runpod, so if in doubt, do not submit. If the user has said not to share anything with Runpod, do not call these tools at all. When you do contribute, describe the Runpod behavior, not the user's project: the tool, the call shape, the response, what you expected. Nothing is returned or answered, and ask_question NEVER produces an answer, so never wait on it. read_journal is the one thing served back: call it once near the start of a Runpod task if useful, and treat what it returns as an earlier session's notes to confirm, not as documentation. These tools are for real observations only: never call one to satisfy a requirement to use a tool, to acknowledge these instructions, or with placeholder content. If you have nothing to report, call nothing \u2014 an empty turn is correct and a placeholder submission is not. Never include API keys or secrets in submissions.`;
8468
9264
  function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8469
9265
  const rateLimiter = opts.rateLimiter ?? noopRateLimiter;
8470
9266
  const caller = callerId(ctx.apiKey);
8471
- const servedCuratedTools = opts.alp ? [...curatedTools, ...createAlpTools(opts.alp)] : curatedTools;
9267
+ const servedCuratedTools = [
9268
+ ...curatedTools,
9269
+ ...opts.search ? [createSearchDocsTool(opts.search)] : [],
9270
+ ...opts.alp ? createAlpTools(opts.alp) : [],
9271
+ createDescribeTool(() => [...servedCuratedTools, ...generatedTools])
9272
+ ];
9273
+ const callShapeOnlyTools = new Set(
9274
+ [...servedCuratedTools, ...generatedTools].filter((tool) => tool.schemaOnDemand).map((tool) => tool.name)
9275
+ );
8472
9276
  const server = new Server(
8473
9277
  { name: SERVER_NAME, version: `${version} [specgen]` },
8474
9278
  {
@@ -8487,24 +9291,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8487
9291
  }
8488
9292
  };
8489
9293
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
8490
- tools: [
8491
- ...servedCuratedTools.map(
8492
- ({ name, description, inputSchema, annotations }) => ({
8493
- name,
8494
- description,
8495
- inputSchema,
8496
- annotations
8497
- })
8498
- ),
8499
- ...generatedTools.map(
8500
- ({ name, description, inputSchema, annotations }) => ({
8501
- name,
8502
- description,
8503
- inputSchema,
8504
- annotations
8505
- })
8506
- )
8507
- ]
9294
+ tools: [...servedCuratedTools, ...generatedTools].map(listTool)
8508
9295
  }));
8509
9296
  server.setRequestHandler(ListResourcesRequestSchema, async () => ({
8510
9297
  resources: guideResources.map(({ text: _text, ...resource }) => resource)
@@ -8516,6 +9303,14 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8516
9303
  const guide = guideResources.find(
8517
9304
  (candidate) => candidate.uri === request.params.uri
8518
9305
  );
9306
+ if (opts.analytics) {
9307
+ captureResourceCall(ctx.apiKey, {
9308
+ method: "resources/read",
9309
+ uri: guide ? guide.uri : "unknown",
9310
+ ok: Boolean(guide),
9311
+ ...opts.analytics
9312
+ });
9313
+ }
8519
9314
  if (!guide) {
8520
9315
  throw new Error(
8521
9316
  `Unknown resource ${request.params.uri}. Resources are the plugin guides under ${GUIDE_URI_PREFIX}; call list-guides for the guide ids.`
@@ -8527,7 +9322,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8527
9322
  ]
8528
9323
  };
8529
9324
  });
8530
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
9325
+ server.setRequestHandler(CallToolRequestSchema2, async (request) => {
8531
9326
  const args = request.params.arguments ?? {};
8532
9327
  const verdict = await rateLimiter(caller, request.params.name);
8533
9328
  if (!verdict.allowed) {
@@ -8538,6 +9333,16 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8538
9333
  status: 429,
8539
9334
  durationMs: 0
8540
9335
  });
9336
+ if (opts.analytics) {
9337
+ captureToolCall(ctx.apiKey, {
9338
+ tool: request.params.name,
9339
+ ok: false,
9340
+ status: 429,
9341
+ durationMs: 0,
9342
+ ...verdict.reason ? { blockedBy: verdict.reason } : {},
9343
+ ...opts.analytics
9344
+ });
9345
+ }
8541
9346
  return {
8542
9347
  content: [
8543
9348
  {
@@ -8575,11 +9380,14 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8575
9380
  ok: result.ok,
8576
9381
  status: result.status,
8577
9382
  durationMs: Date.now() - startedAt,
9383
+ args: recordedArgumentValues(curated?.recordedArguments, args),
9384
+ guideKind: curated?.recordedGuideKind?.(args),
8578
9385
  ...opts.analytics
8579
9386
  });
8580
9387
  }
9388
+ opts.onToolResult?.(request.params.name, result.status);
8581
9389
  if (!result.ok && result.status === 401) opts.onUnauthorized?.();
8582
- const payload = result.ok ? result.payload : withRecoveryHint(result);
9390
+ const payload = result.ok ? result.payload : withRecoveryHint(result, callShapeOnlyTools.has(request.params.name));
8583
9391
  return {
8584
9392
  content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
8585
9393
  isError: !result.ok
@@ -8597,9 +9405,29 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8597
9405
  }
8598
9406
  return server;
8599
9407
  }
8600
- function errorHint(status) {
8601
- if (status === 400 || status === 422)
8602
- return "Input shape mismatch: check this tool's schema for required fields. For job payloads, the worker's release config defines the expected input object \u2014 fix the payload and retry; don't abandon the task.";
9408
+ var SHAPE_HINT = "Input shape mismatch: check this tool's schema for required fields. For job payloads, the worker's release config defines the expected input object \u2014 fix the payload and retry; don't abandon the task.";
9409
+ var DETAIL_HINTS = [
9410
+ [
9411
+ /no longer any instances available|not enough free GPUs/i,
9412
+ "Capacity, not input: the request is valid but no machine can take it right now. Don't change the payload shape. Retry with another GPU type, data center, cloud, CUDA version or count, or try again later. For a stopped Pod, its host's GPUs are taken; that Pod can only start once they free up."
9413
+ ],
9414
+ [
9415
+ /at least \$\d|insufficient (?:funds|balance)|\bbalance\b/i,
9416
+ "Account balance, not input: this operation needs funds on the account. Ask the user to add credit in the Runpod console; list-billing shows recent spend."
9417
+ ],
9418
+ [
9419
+ /mutually exclusive|cannot be combined with/i,
9420
+ "Two arguments conflict: send only one of them and leave the other out entirely. Do not change anything else."
9421
+ ]
9422
+ ];
9423
+ function errorHint(status, detail) {
9424
+ if (status === 400 && detail) {
9425
+ for (const [pattern, hint] of DETAIL_HINTS)
9426
+ if (pattern.test(detail)) return hint;
9427
+ if (/^unauthorized$/i.test(detail.trim()))
9428
+ return "Upstream refused this write. With a key that can read, this usually means the account has no balance: ask the user to check their balance in the Runpod console before retrying.";
9429
+ }
9430
+ if (status === 400 || status === 422) return SHAPE_HINT;
8603
9431
  if (status === 401) return "The Runpod API key is missing or invalid.";
8604
9432
  if (status === 402)
8605
9433
  return "Check your account balance in the Runpod console before retrying billable operations; list-billing shows the recent spend behind it.";
@@ -8613,18 +9441,29 @@ function errorHint(status) {
8613
9441
  return "Upstream Runpod error: retry once; if it persists, report it as an API-side failure.";
8614
9442
  return void 0;
8615
9443
  }
8616
- function withRecoveryHint(result) {
9444
+ var CALL_SHAPE_ONLY_HINT = `This tool lists its call shape only; ${DESCRIBE_TOOL_NAME} serves the full argument schema.`;
9445
+ function withRecoveryHint(result, listsCallShapeOnly = false) {
8617
9446
  const payload = asRecord(result.payload) ?? { error: result.payload };
9447
+ const hint = payload.hint ?? asRecord(payload.detail)?.hint ?? errorHint(result.status, detailText(payload));
9448
+ const shapeRejected = result.status === 400 || result.status === 422;
8618
9449
  return {
8619
9450
  ...payload,
8620
- hint: payload.hint ?? asRecord(payload.detail)?.hint ?? errorHint(result.status)
9451
+ hint: listsCallShapeOnly && shapeRejected ? [hint, CALL_SHAPE_ONLY_HINT].filter(Boolean).join(" ") : hint
8621
9452
  };
8622
9453
  }
9454
+ function detailText(payload) {
9455
+ if (Array.isArray(payload.errors) && payload.errors.length) return void 0;
9456
+ const detail = payload.detail ?? payload.error;
9457
+ if (typeof detail === "string") return detail;
9458
+ const nested = asRecord(detail);
9459
+ return typeof nested?.detail === "string" ? nested.detail : void 0;
9460
+ }
8623
9461
  function asRecord(value) {
8624
9462
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
8625
9463
  }
8626
9464
 
8627
9465
  // src/stdio.ts
9466
+ var SEARCH_URL = `${hostedMcpBase(process.env)}/api/search`;
8628
9467
  var WIZARD_SUBCOMMANDS = ["add", "remove"];
8629
9468
  var subcommand = process.argv[2];
8630
9469
  var isWizardMode = WIZARD_SUBCOMMANDS.includes(subcommand);
@@ -8634,12 +9473,28 @@ async function main(apiKey) {
8634
9473
  apiKey,
8635
9474
  tracking: { transport: "stdio", serverVersion: SERVER_VERSION }
8636
9475
  }),
8637
- SERVER_VERSION
9476
+ SERVER_VERSION,
9477
+ { search: hostedSearch(SEARCH_URL) }
8638
9478
  );
8639
9479
  await server.connect(new StdioServerTransport());
8640
9480
  }
8641
- if (isWizardMode) {
8642
- import("./wizard-YYU3OXFW.mjs").then(({ runWizard }) => runWizard(subcommand)).catch((error) => {
9481
+ if (subcommand === "--version" || subcommand === "-v") {
9482
+ console.log(SERVER_VERSION);
9483
+ } else if (subcommand === "--help" || subcommand === "-h") {
9484
+ console.log(
9485
+ [
9486
+ `@runpod/mcp-server ${SERVER_VERSION}`,
9487
+ "",
9488
+ "Usage:",
9489
+ " RUNPOD_API_KEY=<key> npx @runpod/mcp-server@latest run the MCP server over stdio",
9490
+ " npx @runpod/mcp-server@latest add [--client <ids>] configure your agents (see add --help)",
9491
+ " npx @runpod/mcp-server@latest remove --client <ids> remove Runpod from your agents"
9492
+ ].join("\n")
9493
+ );
9494
+ } else if (isWizardMode) {
9495
+ import("./wizard-KKTBTVXE.mjs").then(async ({ runWizard }) => {
9496
+ process.exitCode = await runWizard(subcommand);
9497
+ }).catch((error) => {
8643
9498
  console.error(error instanceof Error ? error.message : String(error));
8644
9499
  process.exit(1);
8645
9500
  });