@runpod/mcp-server 4.2.0 → 4.3.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
@@ -11,12 +11,12 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
11
11
 
12
12
  // src/server.ts
13
13
  var SERVER_NAME = "Runpod API Server";
14
- var SERVER_VERSION = true ? "4.2.0" : "dev";
14
+ var SERVER_VERSION = true ? "4.3.0" : "dev";
15
15
 
16
16
  // src/specgen/server.ts
17
17
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
18
18
  import {
19
- CallToolRequestSchema,
19
+ CallToolRequestSchema as CallToolRequestSchema2,
20
20
  ListResourcesRequestSchema,
21
21
  ListResourceTemplatesRequestSchema,
22
22
  ListToolsRequestSchema,
@@ -221,10 +221,13 @@ function validateArguments(inputSchema, args) {
221
221
  async function dispatchGeneratedTool(client, tool, args) {
222
222
  const rejected = validateArguments(tool.inputSchema, args);
223
223
  if (rejected) return rejected;
224
+ const required = new Set(tool.inputSchema.required ?? []);
224
225
  const pathParams = {};
225
226
  const query = {};
226
227
  for (const param of tool.params) {
227
228
  if (args[param.name] === void 0) continue;
229
+ if (param.location === "query" && !required.has(param.name) && isEmptyValue(args[param.name]))
230
+ continue;
228
231
  if (param.location === "path") {
229
232
  pathParams[param.name] = args[param.name];
230
233
  continue;
@@ -246,6 +249,11 @@ async function dispatchGeneratedTool(client, tool, args) {
246
249
  };
247
250
  }
248
251
  }
252
+ if (tool.hasBody && requestBody !== void 0) {
253
+ const properties = tool.inputSchema.properties ?? {};
254
+ const defs = tool.inputSchema.$defs ?? {};
255
+ requestBody = coerceScalars(requestBody, properties.body, defs);
256
+ }
249
257
  const verbs = client;
250
258
  const { data, error, response } = await verbs[tool.method](tool.path, {
251
259
  params: { path: pathParams, query },
@@ -258,6 +266,79 @@ async function dispatchGeneratedTool(client, tool, args) {
258
266
  payload: data ?? { status: response.status }
259
267
  };
260
268
  }
269
+ function isEmptyValue(value) {
270
+ return value === null || value === "" || Array.isArray(value) && !value.length;
271
+ }
272
+ function asSchema(value) {
273
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
274
+ }
275
+ function resolveSchema(schema, defs, depth = 0) {
276
+ const node = asSchema(schema);
277
+ if (!node || depth > 16) return node;
278
+ const ref = node.$ref;
279
+ if (typeof ref === "string" && ref.startsWith("#/$defs/"))
280
+ return resolveSchema(defs[ref.slice("#/$defs/".length)], defs, depth + 1);
281
+ return node;
282
+ }
283
+ function branches(schema, defs, depth = 0) {
284
+ const node = resolveSchema(schema, defs);
285
+ if (!node || depth > 16) return [];
286
+ const out = [node];
287
+ for (const key of ["allOf", "anyOf", "oneOf"]) {
288
+ const list = node[key];
289
+ if (Array.isArray(list))
290
+ for (const sub of list) out.push(...branches(sub, defs, depth + 1));
291
+ }
292
+ return out;
293
+ }
294
+ function types(schemas) {
295
+ const out = /* @__PURE__ */ new Set();
296
+ for (const s of schemas) {
297
+ if (typeof s.type === "string") out.add(s.type);
298
+ else if (Array.isArray(s.type)) {
299
+ for (const t of s.type) if (typeof t === "string") out.add(t);
300
+ }
301
+ }
302
+ return out;
303
+ }
304
+ var NUMERIC = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
305
+ function coerceScalars(value, schema, defs, depth = 0) {
306
+ if (depth > 32) return value;
307
+ const applicable = branches(schema, defs);
308
+ if (!applicable.length) return value;
309
+ const allowed = types(applicable);
310
+ if (typeof value === "string") {
311
+ if (allowed.has("string")) return value;
312
+ if ((allowed.has("integer") || allowed.has("number")) && NUMERIC.test(value)) {
313
+ const n = Number(value);
314
+ if (allowed.has("number") || Number.isInteger(n)) return n;
315
+ }
316
+ if (allowed.has("boolean") && (value === "true" || value === "false"))
317
+ return value === "true";
318
+ return value;
319
+ }
320
+ if (Array.isArray(value)) {
321
+ const items = applicable.map((s) => s.items).filter(Boolean);
322
+ if (!items.length) return value;
323
+ return value.map(
324
+ (item) => coerceScalars(item, { anyOf: items }, defs, depth + 1)
325
+ );
326
+ }
327
+ const object = asSchema(value);
328
+ if (!object) return value;
329
+ const out = {};
330
+ for (const [key, child] of Object.entries(object)) {
331
+ const childSchemas = [];
332
+ for (const s of applicable) {
333
+ const props = asSchema(s.properties);
334
+ if (props && key in props) childSchemas.push(props[key]);
335
+ else if (asSchema(s.additionalProperties))
336
+ childSchemas.push(s.additionalProperties);
337
+ }
338
+ out[key] = childSchemas.length ? coerceScalars(child, { anyOf: childSchemas }, defs, depth + 1) : child;
339
+ }
340
+ return out;
341
+ }
261
342
 
262
343
  // src/specgen/generated/tools.gen.ts
263
344
  var generatedTools = [
@@ -275,6 +356,7 @@ var generatedTools = [
275
356
  idempotentHint: false,
276
357
  openWorldHint: true
277
358
  },
359
+ schemaOnDemand: true,
278
360
  inputSchema: {
279
361
  type: "object",
280
362
  properties: {
@@ -286,11 +368,10 @@ var generatedTools = [
286
368
  $defs: {
287
369
  BaseContainerConfig: {
288
370
  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
371
  properties: {
291
372
  args: {
292
373
  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',
374
+ 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
375
  examples: [
295
376
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
296
377
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -302,7 +383,7 @@ var generatedTools = [
302
383
  items: {
303
384
  type: "string"
304
385
  },
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",
386
+ 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
387
  examples: [
307
388
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
308
389
  ]
@@ -312,7 +393,7 @@ var generatedTools = [
312
393
  items: {
313
394
  type: "string"
314
395
  },
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",
396
+ 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
397
  examples: [["/bin/bash", "-c"]]
317
398
  },
318
399
  disk: {
@@ -348,6 +429,21 @@ var generatedTools = [
348
429
  }
349
430
  }
350
431
  },
432
+ BaseMounts: {
433
+ type: "object",
434
+ properties: {
435
+ persistent: {
436
+ $ref: "#/$defs/PersistentMount"
437
+ },
438
+ network: {
439
+ type: "array",
440
+ maxItems: 1,
441
+ items: {
442
+ $ref: "#/$defs/NetworkMount"
443
+ }
444
+ }
445
+ }
446
+ },
351
447
  ClusterCompute: {
352
448
  type: "object",
353
449
  additionalProperties: false,
@@ -375,6 +471,15 @@ var generatedTools = [
375
471
  }
376
472
  }
377
473
  },
474
+ ClusterMounts: {
475
+ description: "Storage mounts for a cluster create: `persistent` or `network`, not both. Clusters cannot mount global volumes, so `global` is rejected.",
476
+ allOf: [
477
+ {
478
+ $ref: "#/$defs/BaseMounts"
479
+ }
480
+ ],
481
+ unevaluatedProperties: false
482
+ },
378
483
  ClusterType: {
379
484
  type: "string",
380
485
  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 +494,7 @@ var generatedTools = [
389
494
  {
390
495
  type: "object",
391
496
  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",
497
+ 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
498
  properties: {
394
499
  compute: {
395
500
  $ref: "#/$defs/ClusterCompute"
@@ -405,7 +510,7 @@ var generatedTools = [
405
510
  templateId: {
406
511
  type: "string",
407
512
  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",
513
+ 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
514
  examples: ["30zmvf89kd"]
410
515
  },
411
516
  dataCenterIds: {
@@ -413,11 +518,11 @@ var generatedTools = [
413
518
  items: {
414
519
  type: "string"
415
520
  },
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",
521
+ 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
522
  example: ["US-TX-3"]
418
523
  },
419
524
  mounts: {
420
- $ref: "#/$defs/Mounts"
525
+ $ref: "#/$defs/ClusterMounts"
421
526
  },
422
527
  startJupyter: {
423
528
  type: "boolean",
@@ -434,28 +539,11 @@ var generatedTools = [
434
539
  ],
435
540
  unevaluatedProperties: false
436
541
  },
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
542
  NetworkMount: {
455
543
  type: "object",
456
544
  required: ["volumeId", "path"],
457
545
  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",
546
+ 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
547
  properties: {
460
548
  volumeId: {
461
549
  type: "string",
@@ -473,7 +561,7 @@ var generatedTools = [
473
561
  type: "object",
474
562
  required: ["size", "path"],
475
563
  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",
564
+ 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
565
  properties: {
478
566
  size: {
479
567
  type: "integer",
@@ -550,6 +638,7 @@ var generatedTools = [
550
638
  idempotentHint: false,
551
639
  openWorldHint: true
552
640
  },
641
+ schemaOnDemand: true,
553
642
  inputSchema: {
554
643
  type: "object",
555
644
  properties: {
@@ -561,11 +650,10 @@ var generatedTools = [
561
650
  $defs: {
562
651
  BaseContainerConfig: {
563
652
  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
653
  properties: {
566
654
  args: {
567
655
  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',
656
+ 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
657
  examples: [
570
658
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
571
659
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -577,7 +665,7 @@ var generatedTools = [
577
665
  items: {
578
666
  type: "string"
579
667
  },
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",
668
+ 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
669
  examples: [
582
670
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
583
671
  ]
@@ -587,7 +675,7 @@ var generatedTools = [
587
675
  items: {
588
676
  type: "string"
589
677
  },
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",
678
+ 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
679
  examples: [["/bin/bash", "-c"]]
592
680
  },
593
681
  disk: {
@@ -647,7 +735,7 @@ var generatedTools = [
647
735
  pools: {
648
736
  type: "array",
649
737
  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',
738
+ 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
739
  items: {
652
740
  type: "string"
653
741
  },
@@ -656,7 +744,7 @@ var generatedTools = [
656
744
  excludedTypes: {
657
745
  type: "array",
658
746
  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',
747
+ 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
748
  items: {
661
749
  type: "string",
662
750
  pattern: "^\\s*[^-\\s]"
@@ -673,7 +761,6 @@ var generatedTools = [
673
761
  }
674
762
  },
675
763
  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
764
  allOf: [
678
765
  {
679
766
  $ref: "#/$defs/BaseContainerConfig"
@@ -699,7 +786,7 @@ var generatedTools = [
699
786
  unevaluatedProperties: false
700
787
  },
701
788
  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",
789
+ 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
790
  allOf: [
704
791
  {
705
792
  $ref: "#/$defs/BaseEndpointGpuConfig"
@@ -714,13 +801,13 @@ var generatedTools = [
714
801
  type: "string",
715
802
  pattern: "^\\d+\\.\\d+$"
716
803
  },
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",
804
+ 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
805
  examples: [["12.8", "12.6"]]
719
806
  },
720
807
  minCudaVersion: {
721
808
  type: "string",
722
809
  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",
810
+ 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
811
  examples: ["12.1"]
725
812
  }
726
813
  }
@@ -764,13 +851,13 @@ var generatedTools = [
764
851
  $ref: "#/$defs/EndpointType"
765
852
  }
766
853
  ],
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"
854
+ description: "Request-routing model. Required \u2014 it determines the valid scaler and request URLs, so it must be chosen explicitly on every create."
768
855
  },
769
856
  cpu: {
770
857
  type: "array",
771
858
  minItems: 1,
772
859
  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",
860
+ 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
861
  items: {
775
862
  $ref: "#/$defs/CreateCpuConfig"
776
863
  }
@@ -794,12 +881,17 @@ var generatedTools = [
794
881
  type: "array",
795
882
  items: {
796
883
  type: "string"
797
- }
884
+ },
885
+ description: "Network volume IDs.",
886
+ deprecated: true
887
+ },
888
+ mounts: {
889
+ $ref: "#/$defs/EndpointMountsInput"
798
890
  },
799
891
  templateId: {
800
892
  type: "string",
801
893
  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",
894
+ 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
895
  examples: ["30zmvf89kd"]
804
896
  },
805
897
  timeout: {
@@ -847,8 +939,42 @@ var generatedTools = [
847
939
  }
848
940
  }
849
941
  },
942
+ EndpointMount: {
943
+ type: "object",
944
+ required: ["volumeId"],
945
+ additionalProperties: false,
946
+ description: "A volume mounted on every worker, always at `/runpod-volume`. Custom paths are not supported yet.",
947
+ properties: {
948
+ volumeId: {
949
+ type: "string",
950
+ description: "ID of a NetworkVolume (under `network`) or GlobalVolume (under `global`).",
951
+ examples: ["vol_abc"]
952
+ }
953
+ }
954
+ },
955
+ EndpointMountsInput: {
956
+ type: "object",
957
+ additionalProperties: false,
958
+ 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.",
959
+ properties: {
960
+ network: {
961
+ type: "array",
962
+ description: "Network volumes, at most one per data center.",
963
+ items: {
964
+ $ref: "#/$defs/EndpointMount"
965
+ }
966
+ },
967
+ global: {
968
+ type: "array",
969
+ maxItems: 1,
970
+ items: {
971
+ $ref: "#/$defs/EndpointMount"
972
+ }
973
+ }
974
+ }
975
+ },
850
976
  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",
977
+ 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
978
  oneOf: [
853
979
  {
854
980
  $ref: "#/$defs/QueueDelayScaling"
@@ -867,7 +993,7 @@ var generatedTools = [
867
993
  },
868
994
  EndpointType: {
869
995
  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",
996
+ 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
997
  "x-enum-varnames": ["EndpointTypeQueue", "EndpointTypeLoadBalancer"],
872
998
  enum: ["QUEUE", "LOAD_BALANCER"]
873
999
  },
@@ -891,14 +1017,14 @@ var generatedTools = [
891
1017
  type: "integer",
892
1018
  minimum: 1,
893
1019
  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",
1020
+ 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
1021
  examples: [5]
896
1022
  }
897
1023
  }
898
1024
  },
899
1025
  FlashBoot: {
900
1026
  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",
1027
+ description: "FlashBoot cold-start acceleration mode. `OFF` \u2014 disabled. `FLASHBOOT` \u2014 enabled. `PRIORITY_FLASHBOOT` \u2014 enabled with priority capacity",
902
1028
  enum: ["OFF", "FLASHBOOT", "PRIORITY_FLASHBOOT"]
903
1029
  },
904
1030
  QueueDelayScaling: {
@@ -924,7 +1050,7 @@ var generatedTools = [
924
1050
  RequestCountScaling: {
925
1051
  type: "object",
926
1052
  additionalProperties: false,
927
- description: "Scale on concurrent in-flight requests per worker. Required for\nload-balancing endpoints; also selectable for queue-based.\n",
1053
+ description: "Scale on concurrent in-flight requests per worker. Required for load-balancing endpoints; also selectable for queue-based.",
928
1054
  required: ["type", "requestCount"],
929
1055
  properties: {
930
1056
  type: {
@@ -943,6 +1069,47 @@ var generatedTools = [
943
1069
  }
944
1070
  }
945
1071
  },
1072
+ {
1073
+ name: "create-global-volume",
1074
+ operationId: "createGlobalVolume",
1075
+ 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`.",
1076
+ method: "POST",
1077
+ path: "/v2/global-volumes",
1078
+ params: [],
1079
+ hasBody: true,
1080
+ annotations: {
1081
+ readOnlyHint: false,
1082
+ destructiveHint: false,
1083
+ idempotentHint: false,
1084
+ openWorldHint: true
1085
+ },
1086
+ inputSchema: {
1087
+ type: "object",
1088
+ properties: {
1089
+ body: {
1090
+ $ref: "#/$defs/CreateGlobalVolumeRequest"
1091
+ }
1092
+ },
1093
+ required: ["body"],
1094
+ $defs: {
1095
+ CreateGlobalVolumeRequest: {
1096
+ type: "object",
1097
+ additionalProperties: false,
1098
+ required: ["name"],
1099
+ properties: {
1100
+ name: {
1101
+ type: "string",
1102
+ minLength: 1,
1103
+ maxLength: 191,
1104
+ pattern: "[^\\p{Z}\\t-\\r\x85]",
1105
+ description: "Human-readable name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed.",
1106
+ examples: ["training-data"]
1107
+ }
1108
+ }
1109
+ }
1110
+ }
1111
+ }
1112
+ },
946
1113
  {
947
1114
  name: "create-network-volume",
948
1115
  operationId: "createNetworkVolume",
@@ -996,7 +1163,7 @@ var generatedTools = [
996
1163
  $ref: "#/$defs/VolumeType"
997
1164
  }
998
1165
  ],
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"
1166
+ description: "Storage tier for the volume. Optional. When omitted, the volume is provisioned using the requested data center's default (primary) storage tier."
1000
1167
  }
1001
1168
  }
1002
1169
  },
@@ -1022,6 +1189,7 @@ var generatedTools = [
1022
1189
  idempotentHint: false,
1023
1190
  openWorldHint: true
1024
1191
  },
1192
+ schemaOnDemand: true,
1025
1193
  inputSchema: {
1026
1194
  type: "object",
1027
1195
  properties: {
@@ -1033,11 +1201,10 @@ var generatedTools = [
1033
1201
  $defs: {
1034
1202
  BaseContainerConfig: {
1035
1203
  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
1204
  properties: {
1038
1205
  args: {
1039
1206
  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',
1207
+ 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
1208
  examples: [
1042
1209
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
1043
1210
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -1049,7 +1216,7 @@ var generatedTools = [
1049
1216
  items: {
1050
1217
  type: "string"
1051
1218
  },
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",
1219
+ 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
1220
  examples: [
1054
1221
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
1055
1222
  ]
@@ -1059,7 +1226,7 @@ var generatedTools = [
1059
1226
  items: {
1060
1227
  type: "string"
1061
1228
  },
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",
1229
+ 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
1230
  examples: [["/bin/bash", "-c"]]
1064
1231
  },
1065
1232
  disk: {
@@ -1131,13 +1298,27 @@ var generatedTools = [
1131
1298
  }
1132
1299
  }
1133
1300
  },
1301
+ BaseMounts: {
1302
+ type: "object",
1303
+ properties: {
1304
+ persistent: {
1305
+ $ref: "#/$defs/PersistentMount"
1306
+ },
1307
+ network: {
1308
+ type: "array",
1309
+ maxItems: 1,
1310
+ items: {
1311
+ $ref: "#/$defs/NetworkMount"
1312
+ }
1313
+ }
1314
+ }
1315
+ },
1134
1316
  Cloud: {
1135
1317
  type: "string",
1136
- description: "Cloud tier.\n- `SECURE` \u2014 Runpod-owned datacenter hardware\n- `COMMUNITY` \u2014 community-hosted hardware\n",
1318
+ description: "Cloud tier. `SECURE` \u2014 Runpod-owned datacenter hardware. `COMMUNITY` \u2014 community-hosted hardware",
1137
1319
  enum: ["SECURE", "COMMUNITY"]
1138
1320
  },
1139
1321
  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
1322
  allOf: [
1142
1323
  {
1143
1324
  $ref: "#/$defs/BaseContainerConfig"
@@ -1163,7 +1344,7 @@ var generatedTools = [
1163
1344
  unevaluatedProperties: false
1164
1345
  },
1165
1346
  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",
1347
+ 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
1348
  allOf: [
1168
1349
  {
1169
1350
  $ref: "#/$defs/BaseGpuConfig"
@@ -1177,13 +1358,13 @@ var generatedTools = [
1177
1358
  type: "string",
1178
1359
  pattern: "^\\d+\\.\\d+$"
1179
1360
  },
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",
1361
+ description: "Acceptable CUDA versions for the host machine, as `major.minor`. Omit to accept any version.",
1181
1362
  examples: [["12.8", "12.6"]]
1182
1363
  },
1183
1364
  minCudaVersion: {
1184
1365
  type: "string",
1185
1366
  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",
1367
+ 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
1368
  examples: ["12.1"]
1188
1369
  },
1189
1370
  minRamPerGpu: {
@@ -1211,7 +1392,7 @@ var generatedTools = [
1211
1392
  {
1212
1393
  type: "object",
1213
1394
  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",
1395
+ description: "Request body for creating a pod. Exactly one of `gpu` or `cpu` must be set \u2014 enforced at the handler layer.",
1215
1396
  properties: {
1216
1397
  name: {
1217
1398
  type: "string",
@@ -1235,13 +1416,13 @@ var generatedTools = [
1235
1416
  items: {
1236
1417
  type: "string"
1237
1418
  },
1238
- description: "Preferred data centers for placement. Omit or pass an empty\narray to let the scheduler choose.\n",
1419
+ description: "Preferred data centers for placement. Omit or pass an empty array to let the scheduler choose.",
1239
1420
  examples: [["US-TX-3"]]
1240
1421
  },
1241
1422
  globalNetworking: {
1242
1423
  type: "boolean",
1243
1424
  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.",
1425
+ 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
1426
  examples: [false]
1246
1427
  },
1247
1428
  gpu: {
@@ -1253,19 +1434,19 @@ var generatedTools = [
1253
1434
  startJupyter: {
1254
1435
  type: "boolean",
1255
1436
  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",
1437
+ 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
1438
  examples: [true]
1258
1439
  },
1259
1440
  startSsh: {
1260
1441
  type: "boolean",
1261
1442
  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",
1443
+ 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
1444
  examples: [true]
1264
1445
  },
1265
1446
  templateId: {
1266
1447
  type: "string",
1267
1448
  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",
1449
+ 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
1450
  examples: ["30zmvf89kd"]
1270
1451
  }
1271
1452
  }
@@ -1281,28 +1462,51 @@ var generatedTools = [
1281
1462
  required: ["image"]
1282
1463
  }
1283
1464
  },
1284
- Mounts: {
1465
+ GlobalMount: {
1285
1466
  type: "object",
1467
+ required: ["volumeId"],
1286
1468
  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",
1469
+ 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
1470
  properties: {
1289
- persistent: {
1290
- $ref: "#/$defs/PersistentMount"
1471
+ volumeId: {
1472
+ type: "string",
1473
+ description: "ID of an existing GlobalVolume.",
1474
+ examples: ["gsb_abc"]
1291
1475
  },
1292
- network: {
1293
- type: "array",
1294
- maxItems: 1,
1295
- items: {
1296
- $ref: "#/$defs/NetworkMount"
1297
- }
1476
+ path: {
1477
+ type: "string",
1478
+ 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.",
1479
+ examples: ["/data"]
1298
1480
  }
1299
1481
  }
1300
1482
  },
1483
+ Mounts: {
1484
+ 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.",
1485
+ allOf: [
1486
+ {
1487
+ $ref: "#/$defs/BaseMounts"
1488
+ },
1489
+ {
1490
+ type: "object",
1491
+ properties: {
1492
+ global: {
1493
+ type: "array",
1494
+ maxItems: 1,
1495
+ 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.",
1496
+ items: {
1497
+ $ref: "#/$defs/GlobalMount"
1498
+ }
1499
+ }
1500
+ }
1501
+ }
1502
+ ],
1503
+ unevaluatedProperties: false
1504
+ },
1301
1505
  NetworkMount: {
1302
1506
  type: "object",
1303
1507
  required: ["volumeId", "path"],
1304
1508
  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",
1509
+ 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
1510
  properties: {
1307
1511
  volumeId: {
1308
1512
  type: "string",
@@ -1320,7 +1524,7 @@ var generatedTools = [
1320
1524
  type: "object",
1321
1525
  required: ["size", "path"],
1322
1526
  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",
1527
+ 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
1528
  properties: {
1325
1529
  size: {
1326
1530
  type: "integer",
@@ -1403,6 +1607,7 @@ var generatedTools = [
1403
1607
  idempotentHint: false,
1404
1608
  openWorldHint: true
1405
1609
  },
1610
+ schemaOnDemand: true,
1406
1611
  inputSchema: {
1407
1612
  type: "object",
1408
1613
  properties: {
@@ -1422,7 +1627,7 @@ var generatedTools = [
1422
1627
  minLength: 1,
1423
1628
  maxLength: 191,
1424
1629
  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).",
1630
+ 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
1631
  examples: ["hf-token"]
1427
1632
  },
1428
1633
  value: {
@@ -1456,6 +1661,7 @@ var generatedTools = [
1456
1661
  idempotentHint: false,
1457
1662
  openWorldHint: true
1458
1663
  },
1664
+ schemaOnDemand: true,
1459
1665
  inputSchema: {
1460
1666
  type: "object",
1461
1667
  properties: {
@@ -1467,11 +1673,10 @@ var generatedTools = [
1467
1673
  $defs: {
1468
1674
  BaseContainerConfig: {
1469
1675
  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
1676
  properties: {
1472
1677
  args: {
1473
1678
  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',
1679
+ 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
1680
  examples: [
1476
1681
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
1477
1682
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -1483,7 +1688,7 @@ var generatedTools = [
1483
1688
  items: {
1484
1689
  type: "string"
1485
1690
  },
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",
1691
+ 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
1692
  examples: [
1488
1693
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
1489
1694
  ]
@@ -1493,7 +1698,7 @@ var generatedTools = [
1493
1698
  items: {
1494
1699
  type: "string"
1495
1700
  },
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",
1701
+ 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
1702
  examples: [["/bin/bash", "-c"]]
1498
1703
  },
1499
1704
  disk: {
@@ -1530,7 +1735,6 @@ var generatedTools = [
1530
1735
  }
1531
1736
  },
1532
1737
  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
1738
  allOf: [
1535
1739
  {
1536
1740
  $ref: "#/$defs/BaseContainerConfig"
@@ -1567,7 +1771,7 @@ var generatedTools = [
1567
1771
  type: "string",
1568
1772
  pattern: "^\\d+\\.\\d+$"
1569
1773
  },
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",
1774
+ 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
1775
  examples: [["12.8", "12.6"]]
1572
1776
  },
1573
1777
  category: {
@@ -1593,13 +1797,13 @@ var generatedTools = [
1593
1797
  startJupyter: {
1594
1798
  type: "boolean",
1595
1799
  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",
1800
+ description: "Start JupyterLab in containers created from this template: injects a generated `JUPYTER_PASSWORD` environment variable, unless `env` already sets one.",
1597
1801
  examples: [false]
1598
1802
  },
1599
1803
  startSsh: {
1600
1804
  type: "boolean",
1601
1805
  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",
1806
+ 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
1807
  examples: [true]
1604
1808
  }
1605
1809
  }
@@ -1611,7 +1815,7 @@ var generatedTools = [
1611
1815
  type: "object",
1612
1816
  required: ["size", "path"],
1613
1817
  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",
1818
+ 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
1819
  properties: {
1616
1820
  size: {
1617
1821
  type: "integer",
@@ -1628,13 +1832,13 @@ var generatedTools = [
1628
1832
  },
1629
1833
  TemplateCategory: {
1630
1834
  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",
1835
+ 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
1836
  enum: ["CPU", "NVIDIA", "AMD"]
1633
1837
  },
1634
1838
  TemplateMounts: {
1635
1839
  type: "object",
1636
1840
  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",
1841
+ 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
1842
  properties: {
1639
1843
  persistent: {
1640
1844
  $ref: "#/$defs/PersistentMount"
@@ -1704,6 +1908,36 @@ var generatedTools = [
1704
1908
  required: ["id"]
1705
1909
  }
1706
1910
  },
1911
+ {
1912
+ name: "delete-global-volume",
1913
+ operationId: "deleteGlobalVolume",
1914
+ 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.",
1915
+ method: "DELETE",
1916
+ path: "/v2/global-volumes/{id}",
1917
+ params: [
1918
+ {
1919
+ name: "id",
1920
+ location: "path"
1921
+ }
1922
+ ],
1923
+ hasBody: false,
1924
+ annotations: {
1925
+ readOnlyHint: false,
1926
+ destructiveHint: true,
1927
+ idempotentHint: true,
1928
+ openWorldHint: true
1929
+ },
1930
+ inputSchema: {
1931
+ type: "object",
1932
+ properties: {
1933
+ id: {
1934
+ type: "string",
1935
+ description: "Global volume identifier"
1936
+ }
1937
+ },
1938
+ required: ["id"]
1939
+ }
1940
+ },
1707
1941
  {
1708
1942
  name: "delete-network-volume",
1709
1943
  operationId: "deleteNetworkVolume",
@@ -2069,12 +2303,42 @@ var generatedTools = [
2069
2303
  },
2070
2304
  buildId: {
2071
2305
  type: "string",
2072
- description: "GitHub build identifier (from GET /v2/serverless/{id}/builds or a release's `buildId`)"
2306
+ description: "GitHub build identifier (from `list-endpoint-builds` or a release's `buildId`)"
2073
2307
  }
2074
2308
  },
2075
2309
  required: ["id", "buildId"]
2076
2310
  }
2077
2311
  },
2312
+ {
2313
+ name: "get-global-volume",
2314
+ operationId: "getGlobalVolume",
2315
+ description: "Get a global volume. Returns a single global volume by ID.",
2316
+ method: "GET",
2317
+ path: "/v2/global-volumes/{id}",
2318
+ params: [
2319
+ {
2320
+ name: "id",
2321
+ location: "path"
2322
+ }
2323
+ ],
2324
+ hasBody: false,
2325
+ annotations: {
2326
+ readOnlyHint: true,
2327
+ destructiveHint: false,
2328
+ idempotentHint: true,
2329
+ openWorldHint: true
2330
+ },
2331
+ inputSchema: {
2332
+ type: "object",
2333
+ properties: {
2334
+ id: {
2335
+ type: "string",
2336
+ description: "Global volume identifier"
2337
+ }
2338
+ },
2339
+ required: ["id"]
2340
+ }
2341
+ },
2078
2342
  {
2079
2343
  name: "get-gpu-type",
2080
2344
  operationId: "getGpuType",
@@ -2145,7 +2409,7 @@ var generatedTools = [
2145
2409
  items: {
2146
2410
  $ref: "#/$defs/Product"
2147
2411
  },
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."
2412
+ description: "Comma-separated availability product contexts. Supported values: POD, CLUSTER, SERVERLESS. Required with include=AVAILABILITY, and valid only with it (400 either way)."
2149
2413
  },
2150
2414
  count: {
2151
2415
  type: "integer",
@@ -2163,7 +2427,7 @@ var generatedTools = [
2163
2427
  type: "string",
2164
2428
  pattern: "^[A-Z]{2}$"
2165
2429
  },
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."
2430
+ 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
2431
  },
2168
2432
  cudaVersions: {
2169
2433
  type: "array",
@@ -2171,12 +2435,12 @@ var generatedTools = [
2171
2435
  type: "string",
2172
2436
  pattern: "^\\d+\\.\\d+$"
2173
2437
  },
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."
2438
+ 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
2439
  },
2176
2440
  minCudaVersion: {
2177
2441
  type: "string",
2178
2442
  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."
2443
+ description: "Lowest acceptable CUDA version to scope availability and lowest-price calculations to, compared numerically."
2180
2444
  }
2181
2445
  },
2182
2446
  required: ["id"],
@@ -2229,6 +2493,25 @@ var generatedTools = [
2229
2493
  required: ["id"]
2230
2494
  }
2231
2495
  },
2496
+ {
2497
+ name: "get-organization",
2498
+ operationId: "getOrganization",
2499
+ 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.",
2500
+ method: "GET",
2501
+ path: "/v2/organization",
2502
+ params: [],
2503
+ hasBody: false,
2504
+ annotations: {
2505
+ readOnlyHint: true,
2506
+ destructiveHint: false,
2507
+ idempotentHint: true,
2508
+ openWorldHint: true
2509
+ },
2510
+ inputSchema: {
2511
+ type: "object",
2512
+ properties: {}
2513
+ }
2514
+ },
2232
2515
  {
2233
2516
  name: "get-pod",
2234
2517
  operationId: "getPod",
@@ -2403,12 +2686,12 @@ var generatedTools = [
2403
2686
  startTime: {
2404
2687
  type: "string",
2405
2688
  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"
2689
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
2407
2690
  },
2408
2691
  endTime: {
2409
2692
  type: "string",
2410
2693
  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"
2694
+ 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
2695
  },
2413
2696
  bucketSize: {
2414
2697
  $ref: "#/$defs/BillingBucketSize",
@@ -2417,7 +2700,7 @@ var generatedTools = [
2417
2700
  lastN: {
2418
2701
  type: "integer",
2419
2702
  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'
2703
+ 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
2704
  }
2422
2705
  },
2423
2706
  $defs: {
@@ -2479,12 +2762,12 @@ var generatedTools = [
2479
2762
  startTime: {
2480
2763
  type: "string",
2481
2764
  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"
2765
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
2483
2766
  },
2484
2767
  endTime: {
2485
2768
  type: "string",
2486
2769
  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"
2770
+ 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
2771
  },
2489
2772
  bucketSize: {
2490
2773
  $ref: "#/$defs/BillingBucketSize",
@@ -2493,7 +2776,7 @@ var generatedTools = [
2493
2776
  lastN: {
2494
2777
  type: "integer",
2495
2778
  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'
2779
+ 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
2780
  },
2498
2781
  clusterId: {
2499
2782
  type: "string",
@@ -2551,10 +2834,19 @@ var generatedTools = [
2551
2834
  {
2552
2835
  name: "list-clusters",
2553
2836
  operationId: "listClusters",
2554
- description: "List clusters. Returns all clusters owned by the authenticated user.",
2837
+ 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
2838
  method: "GET",
2556
2839
  path: "/v2/clusters",
2557
- params: [],
2840
+ params: [
2841
+ {
2842
+ name: "cursor",
2843
+ location: "query"
2844
+ },
2845
+ {
2846
+ name: "limit",
2847
+ location: "query"
2848
+ }
2849
+ ],
2558
2850
  hasBody: false,
2559
2851
  annotations: {
2560
2852
  readOnlyHint: true,
@@ -2564,7 +2856,19 @@ var generatedTools = [
2564
2856
  },
2565
2857
  inputSchema: {
2566
2858
  type: "object",
2567
- properties: {}
2859
+ properties: {
2860
+ cursor: {
2861
+ type: "string",
2862
+ minLength: 1,
2863
+ 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."
2864
+ },
2865
+ limit: {
2866
+ type: "integer",
2867
+ minimum: 1,
2868
+ maximum: 1e3,
2869
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
2870
+ }
2871
+ }
2568
2872
  }
2569
2873
  },
2570
2874
  {
@@ -2818,12 +3122,12 @@ var generatedTools = [
2818
3122
  startTime: {
2819
3123
  type: "string",
2820
3124
  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"
3125
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
2822
3126
  },
2823
3127
  endTime: {
2824
3128
  type: "string",
2825
3129
  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"
3130
+ 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
3131
  },
2828
3132
  bucketSize: {
2829
3133
  $ref: "#/$defs/BillingBucketSize",
@@ -2832,7 +3136,7 @@ var generatedTools = [
2832
3136
  lastN: {
2833
3137
  type: "integer",
2834
3138
  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'
3139
+ 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
3140
  }
2837
3141
  },
2838
3142
  $defs: {
@@ -2890,7 +3194,7 @@ var generatedTools = [
2890
3194
  cursor: {
2891
3195
  type: "string",
2892
3196
  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"
3197
+ 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
3198
  },
2895
3199
  limit: {
2896
3200
  type: "integer",
@@ -2939,7 +3243,7 @@ var generatedTools = [
2939
3243
  cursor: {
2940
3244
  type: "string",
2941
3245
  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"
3246
+ 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
3247
  },
2944
3248
  limit: {
2945
3249
  type: "integer",
@@ -2981,6 +3285,46 @@ var generatedTools = [
2981
3285
  required: ["id"]
2982
3286
  }
2983
3287
  },
3288
+ {
3289
+ name: "list-global-volumes",
3290
+ operationId: "listGlobalVolumes",
3291
+ 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.",
3292
+ method: "GET",
3293
+ path: "/v2/global-volumes",
3294
+ params: [
3295
+ {
3296
+ name: "cursor",
3297
+ location: "query"
3298
+ },
3299
+ {
3300
+ name: "limit",
3301
+ location: "query"
3302
+ }
3303
+ ],
3304
+ hasBody: false,
3305
+ annotations: {
3306
+ readOnlyHint: true,
3307
+ destructiveHint: false,
3308
+ idempotentHint: true,
3309
+ openWorldHint: true
3310
+ },
3311
+ inputSchema: {
3312
+ type: "object",
3313
+ properties: {
3314
+ cursor: {
3315
+ type: "string",
3316
+ 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."
3318
+ },
3319
+ limit: {
3320
+ type: "integer",
3321
+ minimum: 1,
3322
+ maximum: 1e3,
3323
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
3324
+ }
3325
+ }
3326
+ }
3327
+ },
2984
3328
  {
2985
3329
  name: "list-gpu-types",
2986
3330
  operationId: "listGpuTypes",
@@ -3044,7 +3388,7 @@ var generatedTools = [
3044
3388
  items: {
3045
3389
  $ref: "#/$defs/Product"
3046
3390
  },
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."
3391
+ description: "Comma-separated availability product contexts. Supported values: POD, CLUSTER, SERVERLESS. Required with include=AVAILABILITY, and valid only with it (400 either way)."
3048
3392
  },
3049
3393
  count: {
3050
3394
  type: "integer",
@@ -3062,7 +3406,7 @@ var generatedTools = [
3062
3406
  type: "string",
3063
3407
  pattern: "^[A-Z]{2}$"
3064
3408
  },
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."
3409
+ 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
3410
  },
3067
3411
  cudaVersions: {
3068
3412
  type: "array",
@@ -3070,12 +3414,12 @@ var generatedTools = [
3070
3414
  type: "string",
3071
3415
  pattern: "^\\d+\\.\\d+$"
3072
3416
  },
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."
3417
+ 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
3418
  },
3075
3419
  minCudaVersion: {
3076
3420
  type: "string",
3077
3421
  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."
3422
+ description: "Lowest acceptable CUDA version to scope availability and lowest-price calculations to, compared numerically."
3079
3423
  }
3080
3424
  },
3081
3425
  $defs: {
@@ -3138,12 +3482,12 @@ var generatedTools = [
3138
3482
  startTime: {
3139
3483
  type: "string",
3140
3484
  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"
3485
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
3142
3486
  },
3143
3487
  endTime: {
3144
3488
  type: "string",
3145
3489
  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"
3490
+ 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
3491
  },
3148
3492
  bucketSize: {
3149
3493
  $ref: "#/$defs/BillingBucketSize",
@@ -3152,7 +3496,7 @@ var generatedTools = [
3152
3496
  lastN: {
3153
3497
  type: "integer",
3154
3498
  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'
3499
+ 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
3500
  },
3157
3501
  networkVolumeId: {
3158
3502
  type: "string",
@@ -3180,10 +3524,19 @@ var generatedTools = [
3180
3524
  {
3181
3525
  name: "list-network-volumes",
3182
3526
  operationId: "listNetworkVolumes",
3183
- description: "List network volumes. Returns all network volumes owned by the authenticated user.",
3527
+ 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
3528
  method: "GET",
3185
3529
  path: "/v2/network-volumes",
3186
- params: [],
3530
+ params: [
3531
+ {
3532
+ name: "cursor",
3533
+ location: "query"
3534
+ },
3535
+ {
3536
+ name: "limit",
3537
+ location: "query"
3538
+ }
3539
+ ],
3187
3540
  hasBody: false,
3188
3541
  annotations: {
3189
3542
  readOnlyHint: true,
@@ -3193,7 +3546,19 @@ var generatedTools = [
3193
3546
  },
3194
3547
  inputSchema: {
3195
3548
  type: "object",
3196
- properties: {}
3549
+ properties: {
3550
+ cursor: {
3551
+ type: "string",
3552
+ minLength: 1,
3553
+ 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."
3554
+ },
3555
+ limit: {
3556
+ type: "integer",
3557
+ minimum: 1,
3558
+ maximum: 1e3,
3559
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
3560
+ }
3561
+ }
3197
3562
  }
3198
3563
  },
3199
3564
  {
@@ -3237,12 +3602,12 @@ var generatedTools = [
3237
3602
  startTime: {
3238
3603
  type: "string",
3239
3604
  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"
3605
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
3241
3606
  },
3242
3607
  endTime: {
3243
3608
  type: "string",
3244
3609
  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"
3610
+ 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
3611
  },
3247
3612
  bucketSize: {
3248
3613
  $ref: "#/$defs/BillingBucketSize",
@@ -3251,7 +3616,7 @@ var generatedTools = [
3251
3616
  lastN: {
3252
3617
  type: "integer",
3253
3618
  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'
3619
+ 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
3620
  },
3256
3621
  podId: {
3257
3622
  type: "string",
@@ -3279,7 +3644,7 @@ var generatedTools = [
3279
3644
  {
3280
3645
  name: "list-pods",
3281
3646
  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.",
3647
+ 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
3648
  method: "GET",
3284
3649
  path: "/v2/pods",
3285
3650
  params: [
@@ -3314,7 +3679,7 @@ var generatedTools = [
3314
3679
  cursor: {
3315
3680
  type: "string",
3316
3681
  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"
3682
+ 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
3683
  },
3319
3684
  limit: {
3320
3685
  type: "integer",
@@ -3351,7 +3716,7 @@ var generatedTools = [
3351
3716
  type: "string",
3352
3717
  enum: ["official", "verified", "community"],
3353
3718
  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"
3719
+ 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
3720
  }
3356
3721
  }
3357
3722
  }
@@ -3445,12 +3810,12 @@ var generatedTools = [
3445
3810
  startTime: {
3446
3811
  type: "string",
3447
3812
  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"
3813
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
3449
3814
  },
3450
3815
  endTime: {
3451
3816
  type: "string",
3452
3817
  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"
3818
+ 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
3819
  },
3455
3820
  bucketSize: {
3456
3821
  $ref: "#/$defs/BillingBucketSize",
@@ -3459,7 +3824,7 @@ var generatedTools = [
3459
3824
  lastN: {
3460
3825
  type: "integer",
3461
3826
  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'
3827
+ 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
3828
  },
3464
3829
  serverlessId: {
3465
3830
  type: "string",
@@ -3598,7 +3963,7 @@ var generatedTools = [
3598
3963
  type: "object",
3599
3964
  additionalProperties: false,
3600
3965
  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",
3966
+ 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
3967
  properties: {
3603
3968
  name: {
3604
3969
  type: "string",
@@ -3629,6 +3994,7 @@ var generatedTools = [
3629
3994
  idempotentHint: false,
3630
3995
  openWorldHint: true
3631
3996
  },
3997
+ schemaOnDemand: true,
3632
3998
  inputSchema: {
3633
3999
  type: "object",
3634
4000
  properties: {
@@ -3644,11 +4010,10 @@ var generatedTools = [
3644
4010
  $defs: {
3645
4011
  BaseContainerConfig: {
3646
4012
  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
4013
  properties: {
3649
4014
  args: {
3650
4015
  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',
4016
+ 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
4017
  examples: [
3653
4018
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
3654
4019
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -3660,7 +4025,7 @@ var generatedTools = [
3660
4025
  items: {
3661
4026
  type: "string"
3662
4027
  },
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",
4028
+ 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
4029
  examples: [
3665
4030
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
3666
4031
  ]
@@ -3670,7 +4035,7 @@ var generatedTools = [
3670
4035
  items: {
3671
4036
  type: "string"
3672
4037
  },
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",
4038
+ 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
4039
  examples: [["/bin/bash", "-c"]]
3675
4040
  },
3676
4041
  disk: {
@@ -3730,7 +4095,7 @@ var generatedTools = [
3730
4095
  pools: {
3731
4096
  type: "array",
3732
4097
  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',
4098
+ 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
4099
  items: {
3735
4100
  type: "string"
3736
4101
  },
@@ -3739,7 +4104,7 @@ var generatedTools = [
3739
4104
  excludedTypes: {
3740
4105
  type: "array",
3741
4106
  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',
4107
+ 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
4108
  items: {
3744
4109
  type: "string",
3745
4110
  pattern: "^\\s*[^-\\s]"
@@ -3756,7 +4121,6 @@ var generatedTools = [
3756
4121
  }
3757
4122
  },
3758
4123
  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
4124
  allOf: [
3761
4125
  {
3762
4126
  $ref: "#/$defs/BaseContainerConfig"
@@ -3781,8 +4145,42 @@ var generatedTools = [
3781
4145
  ],
3782
4146
  unevaluatedProperties: false
3783
4147
  },
4148
+ EndpointMount: {
4149
+ type: "object",
4150
+ required: ["volumeId"],
4151
+ additionalProperties: false,
4152
+ description: "A volume mounted on every worker, always at `/runpod-volume`. Custom paths are not supported yet.",
4153
+ properties: {
4154
+ volumeId: {
4155
+ type: "string",
4156
+ description: "ID of a NetworkVolume (under `network`) or GlobalVolume (under `global`).",
4157
+ examples: ["vol_abc"]
4158
+ }
4159
+ }
4160
+ },
4161
+ EndpointMountsInput: {
4162
+ type: "object",
4163
+ additionalProperties: false,
4164
+ 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.",
4165
+ properties: {
4166
+ network: {
4167
+ type: "array",
4168
+ description: "Network volumes, at most one per data center.",
4169
+ items: {
4170
+ $ref: "#/$defs/EndpointMount"
4171
+ }
4172
+ },
4173
+ global: {
4174
+ type: "array",
4175
+ maxItems: 1,
4176
+ items: {
4177
+ $ref: "#/$defs/EndpointMount"
4178
+ }
4179
+ }
4180
+ }
4181
+ },
3784
4182
  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",
4183
+ 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
4184
  oneOf: [
3787
4185
  {
3788
4186
  $ref: "#/$defs/QueueDelayScaling"
@@ -3819,14 +4217,14 @@ var generatedTools = [
3819
4217
  type: "integer",
3820
4218
  minimum: 1,
3821
4219
  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",
4220
+ 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
4221
  examples: [5]
3824
4222
  }
3825
4223
  }
3826
4224
  },
3827
4225
  FlashBoot: {
3828
4226
  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",
4227
+ description: "FlashBoot cold-start acceleration mode. `OFF` \u2014 disabled. `FLASHBOOT` \u2014 enabled. `PRIORITY_FLASHBOOT` \u2014 enabled with priority capacity",
3830
4228
  enum: ["OFF", "FLASHBOOT", "PRIORITY_FLASHBOOT"]
3831
4229
  },
3832
4230
  QueueDelayScaling: {
@@ -3852,7 +4250,7 @@ var generatedTools = [
3852
4250
  RequestCountScaling: {
3853
4251
  type: "object",
3854
4252
  additionalProperties: false,
3855
- description: "Scale on concurrent in-flight requests per worker. Required for\nload-balancing endpoints; also selectable for queue-based.\n",
4253
+ description: "Scale on concurrent in-flight requests per worker. Required for load-balancing endpoints; also selectable for queue-based.",
3856
4254
  required: ["type", "requestCount"],
3857
4255
  properties: {
3858
4256
  type: {
@@ -3869,7 +4267,7 @@ var generatedTools = [
3869
4267
  }
3870
4268
  },
3871
4269
  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",
4270
+ 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
4271
  allOf: [
3874
4272
  {
3875
4273
  $ref: "#/$defs/BaseEndpointGpuConfig"
@@ -3886,12 +4284,12 @@ var generatedTools = [
3886
4284
  type: "string",
3887
4285
  pattern: "^\\d+\\.\\d+$"
3888
4286
  },
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"
4287
+ 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
4288
  },
3891
4289
  minCudaVersion: {
3892
4290
  type: "string",
3893
4291
  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',
4292
+ 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
4293
  examples: ["12.1"]
3896
4294
  }
3897
4295
  }
@@ -3912,7 +4310,7 @@ var generatedTools = [
3912
4310
  type: "array",
3913
4311
  minItems: 1,
3914
4312
  uniqueItems: true,
3915
- description: "Complete replacement CPU selection. Valid only for an existing CPU\nendpoint; endpoint compute family cannot be changed.\n",
4313
+ description: "Complete replacement CPU selection. Valid only for an existing CPU endpoint; endpoint compute family cannot be changed.",
3916
4314
  items: {
3917
4315
  $ref: "#/$defs/CreateCpuConfig"
3918
4316
  }
@@ -3938,14 +4336,19 @@ var generatedTools = [
3938
4336
  type: "array",
3939
4337
  items: {
3940
4338
  type: "string"
3941
- }
4339
+ },
4340
+ description: "Network volume IDs. Pass `[]` to detach.",
4341
+ deprecated: true
4342
+ },
4343
+ mounts: {
4344
+ $ref: "#/$defs/EndpointMountsInput"
3942
4345
  },
3943
4346
  scaling: {
3944
4347
  $ref: "#/$defs/EndpointScaling"
3945
4348
  },
3946
4349
  templateId: {
3947
4350
  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",
4351
+ 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
4352
  examples: ["30zmvf89kd"]
3950
4353
  },
3951
4354
  timeout: {
@@ -3962,6 +4365,57 @@ var generatedTools = [
3962
4365
  }
3963
4366
  }
3964
4367
  },
4368
+ {
4369
+ name: "update-global-volume",
4370
+ operationId: "updateGlobalVolume",
4371
+ description: "Update a global volume. Updates mutable fields on a global volume. Only provided fields are changed.",
4372
+ method: "PATCH",
4373
+ path: "/v2/global-volumes/{id}",
4374
+ params: [
4375
+ {
4376
+ name: "id",
4377
+ location: "path"
4378
+ }
4379
+ ],
4380
+ hasBody: true,
4381
+ annotations: {
4382
+ readOnlyHint: false,
4383
+ destructiveHint: false,
4384
+ idempotentHint: false,
4385
+ openWorldHint: true
4386
+ },
4387
+ inputSchema: {
4388
+ type: "object",
4389
+ properties: {
4390
+ id: {
4391
+ type: "string",
4392
+ description: "Global volume identifier"
4393
+ },
4394
+ body: {
4395
+ $ref: "#/$defs/UpdateGlobalVolumeRequest"
4396
+ }
4397
+ },
4398
+ required: ["id", "body"],
4399
+ $defs: {
4400
+ UpdateGlobalVolumeRequest: {
4401
+ type: "object",
4402
+ additionalProperties: false,
4403
+ minProperties: 1,
4404
+ description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
4405
+ properties: {
4406
+ name: {
4407
+ type: "string",
4408
+ minLength: 1,
4409
+ maxLength: 191,
4410
+ pattern: "[^\\p{Z}\\t-\\r\x85]",
4411
+ description: "New human-readable name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed.",
4412
+ examples: ["renamed-volume"]
4413
+ }
4414
+ }
4415
+ }
4416
+ }
4417
+ }
4418
+ },
3965
4419
  {
3966
4420
  name: "update-network-volume",
3967
4421
  operationId: "updateNetworkVolume",
@@ -3998,7 +4452,7 @@ var generatedTools = [
3998
4452
  type: "object",
3999
4453
  additionalProperties: false,
4000
4454
  minProperties: 1,
4001
- description: "Only the provided fields are updated. At least one field must be\npresent; an empty body is rejected.\n",
4455
+ description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
4002
4456
  properties: {
4003
4457
  name: {
4004
4458
  type: "string",
@@ -4010,7 +4464,7 @@ var generatedTools = [
4010
4464
  type: "integer",
4011
4465
  minimum: 10,
4012
4466
  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",
4467
+ description: "New size in GB. Must be greater than or equal to the current size \u2014 network volume storage cannot be reduced.",
4014
4468
  examples: [100]
4015
4469
  }
4016
4470
  }
@@ -4037,6 +4491,7 @@ var generatedTools = [
4037
4491
  idempotentHint: false,
4038
4492
  openWorldHint: true
4039
4493
  },
4494
+ schemaOnDemand: true,
4040
4495
  inputSchema: {
4041
4496
  type: "object",
4042
4497
  properties: {
@@ -4052,11 +4507,10 @@ var generatedTools = [
4052
4507
  $defs: {
4053
4508
  BaseContainerConfig: {
4054
4509
  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
4510
  properties: {
4057
4511
  args: {
4058
4512
  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',
4513
+ 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
4514
  examples: [
4061
4515
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
4062
4516
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -4068,7 +4522,7 @@ var generatedTools = [
4068
4522
  items: {
4069
4523
  type: "string"
4070
4524
  },
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",
4525
+ 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
4526
  examples: [
4073
4527
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
4074
4528
  ]
@@ -4078,7 +4532,7 @@ var generatedTools = [
4078
4532
  items: {
4079
4533
  type: "string"
4080
4534
  },
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",
4535
+ 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
4536
  examples: [["/bin/bash", "-c"]]
4083
4537
  },
4084
4538
  disk: {
@@ -4114,8 +4568,22 @@ var generatedTools = [
4114
4568
  }
4115
4569
  }
4116
4570
  },
4571
+ BaseMounts: {
4572
+ type: "object",
4573
+ properties: {
4574
+ persistent: {
4575
+ $ref: "#/$defs/PersistentMount"
4576
+ },
4577
+ network: {
4578
+ type: "array",
4579
+ maxItems: 1,
4580
+ items: {
4581
+ $ref: "#/$defs/NetworkMount"
4582
+ }
4583
+ }
4584
+ }
4585
+ },
4117
4586
  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
4587
  allOf: [
4120
4588
  {
4121
4589
  $ref: "#/$defs/BaseContainerConfig"
@@ -4132,28 +4600,51 @@ var generatedTools = [
4132
4600
  }
4133
4601
  ]
4134
4602
  },
4135
- Mounts: {
4603
+ GlobalMount: {
4136
4604
  type: "object",
4605
+ required: ["volumeId"],
4137
4606
  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",
4607
+ 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
4608
  properties: {
4140
- persistent: {
4141
- $ref: "#/$defs/PersistentMount"
4609
+ volumeId: {
4610
+ type: "string",
4611
+ description: "ID of an existing GlobalVolume.",
4612
+ examples: ["gsb_abc"]
4142
4613
  },
4143
- network: {
4144
- type: "array",
4145
- maxItems: 1,
4146
- items: {
4147
- $ref: "#/$defs/NetworkMount"
4148
- }
4614
+ path: {
4615
+ type: "string",
4616
+ 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.",
4617
+ examples: ["/data"]
4149
4618
  }
4150
4619
  }
4151
4620
  },
4621
+ Mounts: {
4622
+ 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.",
4623
+ allOf: [
4624
+ {
4625
+ $ref: "#/$defs/BaseMounts"
4626
+ },
4627
+ {
4628
+ type: "object",
4629
+ properties: {
4630
+ global: {
4631
+ type: "array",
4632
+ maxItems: 1,
4633
+ 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.",
4634
+ items: {
4635
+ $ref: "#/$defs/GlobalMount"
4636
+ }
4637
+ }
4638
+ }
4639
+ }
4640
+ ],
4641
+ unevaluatedProperties: false
4642
+ },
4152
4643
  NetworkMount: {
4153
4644
  type: "object",
4154
4645
  required: ["volumeId", "path"],
4155
4646
  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",
4647
+ 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
4648
  properties: {
4158
4649
  volumeId: {
4159
4650
  type: "string",
@@ -4171,7 +4662,7 @@ var generatedTools = [
4171
4662
  type: "object",
4172
4663
  required: ["size", "path"],
4173
4664
  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",
4665
+ 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
4666
  properties: {
4176
4667
  size: {
4177
4668
  type: "integer",
@@ -4196,7 +4687,7 @@ var generatedTools = [
4196
4687
  properties: {
4197
4688
  globalNetworking: {
4198
4689
  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."
4690
+ 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
4691
  },
4201
4692
  locked: {
4202
4693
  type: "boolean",
@@ -4211,7 +4702,7 @@ var generatedTools = [
4211
4702
  },
4212
4703
  templateId: {
4213
4704
  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",
4705
+ 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
4706
  examples: ["30zmvf89kd"]
4216
4707
  }
4217
4708
  }
@@ -4241,6 +4732,7 @@ var generatedTools = [
4241
4732
  idempotentHint: false,
4242
4733
  openWorldHint: true
4243
4734
  },
4735
+ schemaOnDemand: true,
4244
4736
  inputSchema: {
4245
4737
  type: "object",
4246
4738
  properties: {
@@ -4258,7 +4750,7 @@ var generatedTools = [
4258
4750
  type: "object",
4259
4751
  additionalProperties: false,
4260
4752
  minProperties: 1,
4261
- description: "Only the provided fields are updated; at least one field is required.\nThe secret's `name` is immutable.\n",
4753
+ description: "Only the provided fields are updated; at least one field is required. The secret's `name` is immutable.",
4262
4754
  properties: {
4263
4755
  value: {
4264
4756
  type: "string",
@@ -4310,7 +4802,7 @@ var generatedTools = [
4310
4802
  type: "string",
4311
4803
  pattern: "^(ssh|ecdsa|sk)-[^\\s]+ [^\\s]+([ \\t][^\\n\\r]*)?$"
4312
4804
  },
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",
4805
+ 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
4806
  examples: [
4315
4807
  [
4316
4808
  "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILXGDN/SclOozk1xsDztpmhGiKkkrfQB9SKoO8dSIQQZ me@example.com"
@@ -4341,6 +4833,7 @@ var generatedTools = [
4341
4833
  idempotentHint: false,
4342
4834
  openWorldHint: true
4343
4835
  },
4836
+ schemaOnDemand: true,
4344
4837
  inputSchema: {
4345
4838
  type: "object",
4346
4839
  properties: {
@@ -4355,11 +4848,10 @@ var generatedTools = [
4355
4848
  $defs: {
4356
4849
  BaseContainerConfig: {
4357
4850
  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
4851
  properties: {
4360
4852
  args: {
4361
4853
  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',
4854
+ 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
4855
  examples: [
4364
4856
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
4365
4857
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -4371,7 +4863,7 @@ var generatedTools = [
4371
4863
  items: {
4372
4864
  type: "string"
4373
4865
  },
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",
4866
+ 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
4867
  examples: [
4376
4868
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
4377
4869
  ]
@@ -4381,7 +4873,7 @@ var generatedTools = [
4381
4873
  items: {
4382
4874
  type: "string"
4383
4875
  },
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",
4876
+ 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
4877
  examples: [["/bin/bash", "-c"]]
4386
4878
  },
4387
4879
  disk: {
@@ -4418,7 +4910,6 @@ var generatedTools = [
4418
4910
  }
4419
4911
  },
4420
4912
  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
4913
  allOf: [
4423
4914
  {
4424
4915
  $ref: "#/$defs/BaseContainerConfig"
@@ -4439,7 +4930,7 @@ var generatedTools = [
4439
4930
  type: "object",
4440
4931
  required: ["size", "path"],
4441
4932
  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",
4933
+ 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
4934
  properties: {
4444
4935
  size: {
4445
4936
  type: "integer",
@@ -4456,13 +4947,13 @@ var generatedTools = [
4456
4947
  },
4457
4948
  TemplateCategory: {
4458
4949
  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",
4950
+ 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
4951
  enum: ["CPU", "NVIDIA", "AMD"]
4461
4952
  },
4462
4953
  TemplateMounts: {
4463
4954
  type: "object",
4464
4955
  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",
4956
+ 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
4957
  properties: {
4467
4958
  persistent: {
4468
4959
  $ref: "#/$defs/PersistentMount"
@@ -6007,7 +6498,7 @@ var listTemplates = {
6007
6498
  }
6008
6499
  };
6009
6500
 
6010
- // node_modules/.pnpm/@runpod+typescript-api-sdk@0.2.0/node_modules/@runpod/typescript-api-sdk/dist/index.js
6501
+ // node_modules/.pnpm/@runpod+typescript-api-sdk@0.3.0/node_modules/@runpod/typescript-api-sdk/dist/index.js
6011
6502
  var PATH_PARAM_RE = /\{[^{}]+\}/g;
6012
6503
  var supportsRequestInitExt = () => {
6013
6504
  return typeof process === "object" && Number.parseInt(process?.versions?.node?.substring(0, 2)) >= 18 && process.versions.undici;
@@ -7697,6 +8188,7 @@ var source = {
7697
8188
  var listGuides = {
7698
8189
  name: "list-guides",
7699
8190
  annotations: bundledRead,
8191
+ recordedArguments: ["kind", "query", "concept"],
7700
8192
  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
8193
  inputSchema: {
7702
8194
  type: "object",
@@ -7732,6 +8224,8 @@ var listGuides = {
7732
8224
  var readGuide = {
7733
8225
  name: "read-guide",
7734
8226
  annotations: bundledRead,
8227
+ recordedArguments: ["id"],
8228
+ recordedGuideKind: (args) => knowledge.guides.find((guide) => guide.id === String(args.id).trim())?.kind,
7735
8229
  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
8230
  inputSchema: {
7737
8231
  type: "object",
@@ -7769,6 +8263,7 @@ var readGuide = {
7769
8263
  var lookupConcept = {
7770
8264
  name: "lookup-concept",
7771
8265
  annotations: bundledRead,
8266
+ recordedArguments: ["concept"],
7772
8267
  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
8268
  inputSchema: {
7774
8269
  type: "object",
@@ -7821,6 +8316,7 @@ var lookupConcept = {
7821
8316
  var searchConcepts = {
7822
8317
  name: "search-concepts",
7823
8318
  annotations: bundledRead,
8319
+ recordedArguments: ["query", "limit"],
7824
8320
  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
8321
  inputSchema: {
7826
8322
  type: "object",
@@ -7885,6 +8381,140 @@ var guideResources = knowledge.guides.map((guide) => ({
7885
8381
  text: guide.body
7886
8382
  }));
7887
8383
 
8384
+ // src/specgen/prose.ts
8385
+ var ABBREVIATIONS = ["e.g.", "i.e.", "etc.", "vs."];
8386
+ var DOT = "\0";
8387
+ function splitSentences(text) {
8388
+ let guarded = text;
8389
+ for (const abbreviation of ABBREVIATIONS) {
8390
+ guarded = guarded.replaceAll(
8391
+ abbreviation,
8392
+ abbreviation.replaceAll(".", DOT)
8393
+ );
8394
+ }
8395
+ return guarded.split(/(?<=[.!?])\s+(?=[A-Z`"'([])/).map((sentence) => sentence.replaceAll(DOT, ".").trim()).filter(Boolean);
8396
+ }
8397
+ function leadSentence(text) {
8398
+ return splitSentences(text)[0] ?? text;
8399
+ }
8400
+
8401
+ // src/specgen/on-demand.ts
8402
+ var DESCRIBE_TOOL_NAME = "describe-tool";
8403
+ var ON_DEMAND_POINTER = ` Call ${DESCRIBE_TOOL_NAME} for the full argument schema before the first use.`;
8404
+ function listedAnnotations(annotations) {
8405
+ const { idempotentHint, ...listed } = annotations;
8406
+ return idempotentHint ? annotations : listed;
8407
+ }
8408
+ function listTool(tool) {
8409
+ const { name, description, inputSchema } = tool;
8410
+ const annotations = listedAnnotations(tool.annotations);
8411
+ if (!tool.schemaOnDemand)
8412
+ return { name, description, inputSchema, annotations };
8413
+ return {
8414
+ name,
8415
+ description: description + ON_DEMAND_POINTER,
8416
+ inputSchema: callShape(inputSchema),
8417
+ annotations
8418
+ };
8419
+ }
8420
+ function callShape(schema) {
8421
+ const defs = schema.$defs ?? {};
8422
+ const resolve = (node) => {
8423
+ if (typeof node.$ref !== "string") return node;
8424
+ const name = node.$ref.split("/").pop() ?? "";
8425
+ return defs[name] ?? {};
8426
+ };
8427
+ const properties = Object.fromEntries(
8428
+ Object.entries(schema.properties ?? {}).map(
8429
+ ([key, value]) => [key, shallowProperty(resolve(value), resolve)]
8430
+ )
8431
+ );
8432
+ return {
8433
+ type: "object",
8434
+ properties,
8435
+ ...schema.required ? { required: schema.required } : {}
8436
+ };
8437
+ }
8438
+ function shallowProperty(node, resolve) {
8439
+ const out = {};
8440
+ const type = schemaType(node, resolve);
8441
+ if (type !== void 0) out.type = type;
8442
+ if (type === "array") {
8443
+ const items = resolve(node.items ?? {});
8444
+ const itemType = schemaType(items, resolve);
8445
+ if (itemType !== void 0) out.items = { type: itemType };
8446
+ }
8447
+ if (Array.isArray(node.enum)) out.enum = node.enum;
8448
+ if (typeof node.description === "string")
8449
+ out.description = leadSentence(node.description);
8450
+ return out;
8451
+ }
8452
+ function schemaType(node, resolve) {
8453
+ if (node.type !== void 0) return node.type;
8454
+ if (node.properties || node.allOf) return "object";
8455
+ const branches2 = node.anyOf ?? node.oneOf;
8456
+ const allObjects = branches2?.every(
8457
+ (branch) => schemaType(resolve(branch), resolve) === "object"
8458
+ ) ?? false;
8459
+ return allObjects ? "object" : void 0;
8460
+ }
8461
+
8462
+ // src/specgen/tools/describe.ts
8463
+ function createDescribeTool(served) {
8464
+ return {
8465
+ name: DESCRIBE_TOOL_NAME,
8466
+ 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.",
8467
+ inputSchema: {
8468
+ type: "object",
8469
+ properties: {
8470
+ name: {
8471
+ type: "string",
8472
+ description: "A tool name exactly as this server's tool list spells it."
8473
+ }
8474
+ },
8475
+ required: ["name"]
8476
+ },
8477
+ annotations: {
8478
+ readOnlyHint: true,
8479
+ destructiveHint: false,
8480
+ idempotentHint: true,
8481
+ openWorldHint: false
8482
+ },
8483
+ handler: async (_ctx, args) => {
8484
+ const name = String(args.name ?? "");
8485
+ const tools = served();
8486
+ const tool = tools.find((candidate) => candidate.name === name);
8487
+ if (!tool) {
8488
+ return {
8489
+ ok: false,
8490
+ status: 404,
8491
+ payload: {
8492
+ error: `No tool named ${name}.`,
8493
+ suggestions: similarNames(
8494
+ name,
8495
+ tools.map((candidate) => candidate.name)
8496
+ ),
8497
+ hint: "Tool names are exactly as this server lists them; the suggestions share a word with the name you sent."
8498
+ }
8499
+ };
8500
+ }
8501
+ const { description, inputSchema, annotations } = tool;
8502
+ return ok({ name: tool.name, description, inputSchema, annotations });
8503
+ }
8504
+ };
8505
+ }
8506
+ function similarNames(query, names) {
8507
+ const wanted = new Set(words2(query));
8508
+ return names.map((name) => {
8509
+ const own = new Set(words2(name));
8510
+ const shared = [...own].filter((word) => wanted.has(word)).length;
8511
+ return { name, score: shared / (/* @__PURE__ */ new Set([...own, ...wanted])).size };
8512
+ }).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || a.name.localeCompare(b.name)).slice(0, 8).map((entry) => entry.name);
8513
+ }
8514
+ function words2(name) {
8515
+ return name.toLowerCase().split(/[-_\s]+/).filter(Boolean).map((word) => word.replace(/s$/, ""));
8516
+ }
8517
+
7888
8518
  // src/specgen/analytics.ts
7889
8519
  import { createHmac } from "crypto";
7890
8520
  var CAPTURE_TIMEOUT_MS = 3e3;
@@ -7899,7 +8529,22 @@ function analyticsCallerId(apiKey, accountId) {
7899
8529
  const prefix = accountId ? "a:" : "k:";
7900
8530
  return prefix + createHmac("sha256", salt).update(identity).digest("hex").slice(0, 16);
7901
8531
  }
7902
- function captureToolCall(apiKey, event) {
8532
+ var MAX_RECORDED_VALUE_LENGTH = 200;
8533
+ function bounded(value) {
8534
+ return value.slice(0, MAX_RECORDED_VALUE_LENGTH);
8535
+ }
8536
+ function recordedArgumentValues(names, args) {
8537
+ if (!names?.length) return void 0;
8538
+ const values = {};
8539
+ for (const name of names) {
8540
+ const value = args[name];
8541
+ if (typeof value === "string" || typeof value === "number") {
8542
+ values[name] = bounded(String(value).trim());
8543
+ }
8544
+ }
8545
+ return Object.keys(values).length ? values : void 0;
8546
+ }
8547
+ function capture(event, distinctId, properties) {
7903
8548
  const posthogKey = process.env.POSTHOG_API_KEY;
7904
8549
  if (!posthogKey) return;
7905
8550
  void fetch(`${posthogHost()}/capture/`, {
@@ -7907,18 +8552,12 @@ function captureToolCall(apiKey, event) {
7907
8552
  headers: { "Content-Type": "application/json" },
7908
8553
  body: JSON.stringify({
7909
8554
  api_key: posthogKey,
7910
- event: "runpod_mcp_tool_call",
7911
- distinct_id: analyticsCallerId(apiKey, event.accountId),
8555
+ event,
8556
+ distinct_id: distinctId,
7912
8557
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7913
8558
  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,
8559
+ ...properties,
7920
8560
  surface_version: SURFACE_VERSION,
7921
- ...event.clientName ? { client_name: event.clientName } : {},
7922
8561
  $process_person_profile: false
7923
8562
  }
7924
8563
  }),
@@ -7926,9 +8565,48 @@ function captureToolCall(apiKey, event) {
7926
8565
  }).catch(() => {
7927
8566
  });
7928
8567
  }
8568
+ function captureToolCall(apiKey, event) {
8569
+ const args = Object.fromEntries(
8570
+ Object.entries(event.args ?? {}).map(([name, value]) => [
8571
+ `arg_${name}`,
8572
+ value
8573
+ ])
8574
+ );
8575
+ capture("runpod_mcp_tool_call", analyticsCallerId(apiKey, event.accountId), {
8576
+ tool: event.tool,
8577
+ ok: event.ok,
8578
+ status: event.status,
8579
+ duration_ms: event.durationMs,
8580
+ transport: event.transport,
8581
+ server_version: event.serverVersion,
8582
+ ...event.clientName ? { client_name: event.clientName } : {},
8583
+ ...event.guideKind ? { guide_kind: event.guideKind } : {},
8584
+ ...event.blockedBy ? { blocked_by: event.blockedBy } : {},
8585
+ ...args
8586
+ });
8587
+ }
8588
+ function captureResourceCall(apiKey, event) {
8589
+ capture(
8590
+ "runpod_mcp_resource_call",
8591
+ analyticsCallerId(apiKey, event.accountId),
8592
+ {
8593
+ method: event.method,
8594
+ uri: bounded(event.uri),
8595
+ ok: event.ok,
8596
+ transport: event.transport,
8597
+ server_version: event.serverVersion,
8598
+ ...event.clientName ? { client_name: event.clientName } : {}
8599
+ }
8600
+ );
8601
+ }
7929
8602
 
7930
8603
  // src/http.ts
7931
8604
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
8605
+ import {
8606
+ CallToolRequestSchema,
8607
+ InitializeRequestSchema,
8608
+ JSONRPCMessageSchema
8609
+ } from "@modelcontextprotocol/sdk/types.js";
7932
8610
 
7933
8611
  // src/specgen/context.ts
7934
8612
  import { randomUUID } from "crypto";
@@ -8091,14 +8769,26 @@ function createToolContext(options = {}) {
8091
8769
  };
8092
8770
  }
8093
8771
 
8772
+ // src/specgen/ops.ts
8773
+ import { createHash, randomBytes } from "crypto";
8774
+ var SALT = randomBytes(16).toString("hex");
8775
+ function callerId(token) {
8776
+ if (!token) return "anonymous";
8777
+ return createHash("sha256").update(SALT).update(token).digest("hex").slice(0, 12);
8778
+ }
8779
+ var noopRateLimiter = async () => ({ allowed: true });
8780
+ function logToolCall(entry) {
8781
+ console.error("tool_call", JSON.stringify(entry));
8782
+ }
8783
+
8094
8784
  // src/_shared/credential-check.ts
8095
- import { createHash } from "crypto";
8785
+ import { createHash as createHash2 } from "crypto";
8096
8786
  var VALID_TTL_MS = 6e4;
8097
8787
  var INVALID_TTL_MS = 3e4;
8098
8788
  var REQUEST_TIMEOUT_MS = 4e3;
8099
8789
  var MAX_CACHE_ENTRIES = 1e3;
8100
8790
  function hashToken(token) {
8101
- return createHash("sha256").update(token).digest("hex");
8791
+ return createHash2("sha256").update(token).digest("hex");
8102
8792
  }
8103
8793
  function createCredentialChecker(deps) {
8104
8794
  const now = deps.now ?? (() => performance.now());
@@ -8421,18 +9111,6 @@ function createAlpTools(opts) {
8421
9111
  return [reportFeedback, saveToJournal, askQuestion, readJournalTool];
8422
9112
  }
8423
9113
 
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));
8434
- }
8435
-
8436
9114
  // src/specgen/server.ts
8437
9115
  var curatedTools = [
8438
9116
  listEndpoints,
@@ -8461,14 +9139,21 @@ PLAYBOOKS \u2014 READ BEFORE ACTING. Each Runpod task has a journey playbook car
8461
9139
 
8462
9140
  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
9141
 
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.`;
9142
+ 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
9143
  var ALP_INSTRUCTIONS = `
8466
9144
 
8467
9145
  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
9146
  function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8469
9147
  const rateLimiter = opts.rateLimiter ?? noopRateLimiter;
8470
9148
  const caller = callerId(ctx.apiKey);
8471
- const servedCuratedTools = opts.alp ? [...curatedTools, ...createAlpTools(opts.alp)] : curatedTools;
9149
+ const configuredCuratedTools = opts.alp ? [...curatedTools, ...createAlpTools(opts.alp)] : curatedTools;
9150
+ const servedCuratedTools = [
9151
+ ...configuredCuratedTools,
9152
+ createDescribeTool(() => [...servedCuratedTools, ...generatedTools])
9153
+ ];
9154
+ const callShapeOnlyTools = new Set(
9155
+ [...servedCuratedTools, ...generatedTools].filter((tool) => tool.schemaOnDemand).map((tool) => tool.name)
9156
+ );
8472
9157
  const server = new Server(
8473
9158
  { name: SERVER_NAME, version: `${version} [specgen]` },
8474
9159
  {
@@ -8487,24 +9172,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8487
9172
  }
8488
9173
  };
8489
9174
  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
- ]
9175
+ tools: [...servedCuratedTools, ...generatedTools].map(listTool)
8508
9176
  }));
8509
9177
  server.setRequestHandler(ListResourcesRequestSchema, async () => ({
8510
9178
  resources: guideResources.map(({ text: _text, ...resource }) => resource)
@@ -8516,6 +9184,14 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8516
9184
  const guide = guideResources.find(
8517
9185
  (candidate) => candidate.uri === request.params.uri
8518
9186
  );
9187
+ if (opts.analytics) {
9188
+ captureResourceCall(ctx.apiKey, {
9189
+ method: "resources/read",
9190
+ uri: guide ? guide.uri : "unknown",
9191
+ ok: Boolean(guide),
9192
+ ...opts.analytics
9193
+ });
9194
+ }
8519
9195
  if (!guide) {
8520
9196
  throw new Error(
8521
9197
  `Unknown resource ${request.params.uri}. Resources are the plugin guides under ${GUIDE_URI_PREFIX}; call list-guides for the guide ids.`
@@ -8527,7 +9203,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8527
9203
  ]
8528
9204
  };
8529
9205
  });
8530
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
9206
+ server.setRequestHandler(CallToolRequestSchema2, async (request) => {
8531
9207
  const args = request.params.arguments ?? {};
8532
9208
  const verdict = await rateLimiter(caller, request.params.name);
8533
9209
  if (!verdict.allowed) {
@@ -8538,6 +9214,16 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8538
9214
  status: 429,
8539
9215
  durationMs: 0
8540
9216
  });
9217
+ if (opts.analytics) {
9218
+ captureToolCall(ctx.apiKey, {
9219
+ tool: request.params.name,
9220
+ ok: false,
9221
+ status: 429,
9222
+ durationMs: 0,
9223
+ ...verdict.reason ? { blockedBy: verdict.reason } : {},
9224
+ ...opts.analytics
9225
+ });
9226
+ }
8541
9227
  return {
8542
9228
  content: [
8543
9229
  {
@@ -8575,11 +9261,14 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8575
9261
  ok: result.ok,
8576
9262
  status: result.status,
8577
9263
  durationMs: Date.now() - startedAt,
9264
+ args: recordedArgumentValues(curated?.recordedArguments, args),
9265
+ guideKind: curated?.recordedGuideKind?.(args),
8578
9266
  ...opts.analytics
8579
9267
  });
8580
9268
  }
9269
+ opts.onToolResult?.(request.params.name, result.status);
8581
9270
  if (!result.ok && result.status === 401) opts.onUnauthorized?.();
8582
- const payload = result.ok ? result.payload : withRecoveryHint(result);
9271
+ const payload = result.ok ? result.payload : withRecoveryHint(result, callShapeOnlyTools.has(request.params.name));
8583
9272
  return {
8584
9273
  content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
8585
9274
  isError: !result.ok
@@ -8597,9 +9286,29 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8597
9286
  }
8598
9287
  return server;
8599
9288
  }
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.";
9289
+ 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.";
9290
+ var DETAIL_HINTS = [
9291
+ [
9292
+ /no longer any instances available|not enough free GPUs/i,
9293
+ "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."
9294
+ ],
9295
+ [
9296
+ /at least \$\d|insufficient (?:funds|balance)|\bbalance\b/i,
9297
+ "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."
9298
+ ],
9299
+ [
9300
+ /mutually exclusive|cannot be combined with/i,
9301
+ "Two arguments conflict: send only one of them and leave the other out entirely. Do not change anything else."
9302
+ ]
9303
+ ];
9304
+ function errorHint(status, detail) {
9305
+ if (status === 400 && detail) {
9306
+ for (const [pattern, hint] of DETAIL_HINTS)
9307
+ if (pattern.test(detail)) return hint;
9308
+ if (/^unauthorized$/i.test(detail.trim()))
9309
+ 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.";
9310
+ }
9311
+ if (status === 400 || status === 422) return SHAPE_HINT;
8603
9312
  if (status === 401) return "The Runpod API key is missing or invalid.";
8604
9313
  if (status === 402)
8605
9314
  return "Check your account balance in the Runpod console before retrying billable operations; list-billing shows the recent spend behind it.";
@@ -8613,13 +9322,23 @@ function errorHint(status) {
8613
9322
  return "Upstream Runpod error: retry once; if it persists, report it as an API-side failure.";
8614
9323
  return void 0;
8615
9324
  }
8616
- function withRecoveryHint(result) {
9325
+ var CALL_SHAPE_ONLY_HINT = `This tool lists its call shape only; ${DESCRIBE_TOOL_NAME} serves the full argument schema.`;
9326
+ function withRecoveryHint(result, listsCallShapeOnly = false) {
8617
9327
  const payload = asRecord(result.payload) ?? { error: result.payload };
9328
+ const hint = payload.hint ?? asRecord(payload.detail)?.hint ?? errorHint(result.status, detailText(payload));
9329
+ const shapeRejected = result.status === 400 || result.status === 422;
8618
9330
  return {
8619
9331
  ...payload,
8620
- hint: payload.hint ?? asRecord(payload.detail)?.hint ?? errorHint(result.status)
9332
+ hint: listsCallShapeOnly && shapeRejected ? [hint, CALL_SHAPE_ONLY_HINT].filter(Boolean).join(" ") : hint
8621
9333
  };
8622
9334
  }
9335
+ function detailText(payload) {
9336
+ if (Array.isArray(payload.errors) && payload.errors.length) return void 0;
9337
+ const detail = payload.detail ?? payload.error;
9338
+ if (typeof detail === "string") return detail;
9339
+ const nested = asRecord(detail);
9340
+ return typeof nested?.detail === "string" ? nested.detail : void 0;
9341
+ }
8623
9342
  function asRecord(value) {
8624
9343
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
8625
9344
  }
@@ -8638,8 +9357,23 @@ async function main(apiKey) {
8638
9357
  );
8639
9358
  await server.connect(new StdioServerTransport());
8640
9359
  }
8641
- if (isWizardMode) {
8642
- import("./wizard-YYU3OXFW.mjs").then(({ runWizard }) => runWizard(subcommand)).catch((error) => {
9360
+ if (subcommand === "--version" || subcommand === "-v") {
9361
+ console.log(SERVER_VERSION);
9362
+ } else if (subcommand === "--help" || subcommand === "-h") {
9363
+ console.log(
9364
+ [
9365
+ `@runpod/mcp-server ${SERVER_VERSION}`,
9366
+ "",
9367
+ "Usage:",
9368
+ " RUNPOD_API_KEY=<key> npx @runpod/mcp-server@latest run the MCP server over stdio",
9369
+ " npx @runpod/mcp-server@latest add [--client <ids>] configure your agents (see add --help)",
9370
+ " npx @runpod/mcp-server@latest remove --client <ids> remove Runpod from your agents"
9371
+ ].join("\n")
9372
+ );
9373
+ } else if (isWizardMode) {
9374
+ import("./wizard-L25AYBNQ.mjs").then(async ({ runWizard }) => {
9375
+ process.exitCode = await runWizard(subcommand);
9376
+ }).catch((error) => {
8643
9377
  console.error(error instanceof Error ? error.message : String(error));
8644
9378
  process.exit(1);
8645
9379
  });