@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/http.cjs CHANGED
@@ -23,10 +23,15 @@ __export(http_exports, {
23
23
  bodyIsCheckable: () => bodyIsCheckable,
24
24
  defaultCredentialChecker: () => defaultCredentialChecker,
25
25
  getBaseUrl: () => getBaseUrl,
26
- handleMcpRequest: () => handleMcpRequest
26
+ handleMcpRequest: () => handleMcpRequest,
27
+ parseMcpMessages: () => parseMcpMessages,
28
+ prefersJsonResponse: () => prefersJsonResponse,
29
+ requestLogSampleRate: () => requestLogSampleRate,
30
+ summarizeRpc: () => summarizeRpc
27
31
  });
28
32
  module.exports = __toCommonJS(http_exports);
29
33
  var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
34
+ var import_types2 = require("@modelcontextprotocol/sdk/types.js");
30
35
 
31
36
  // src/specgen/server.ts
32
37
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
@@ -108,11 +113,11 @@ function readParams(params) {
108
113
  }
109
114
  return { values, unreadable };
110
115
  }
111
- function parseExhaustedWindows(header) {
112
- if (!header) return { windows: [], dropped: false };
116
+ function parseExhaustedWindows(header2) {
117
+ if (!header2) return { windows: [], dropped: false };
113
118
  const windows = [];
114
119
  let dropped = false;
115
- for (const item of splitOutsideStrings(header, ",")) {
120
+ for (const item of splitOutsideStrings(header2, ",")) {
116
121
  if (item.trim() === "") continue;
117
122
  const [rawName, ...params] = splitOutsideStrings(item, ";");
118
123
  if (!params.every(isWellFormedParam)) {
@@ -230,10 +235,13 @@ function validateArguments(inputSchema, args) {
230
235
  async function dispatchGeneratedTool(client, tool, args) {
231
236
  const rejected = validateArguments(tool.inputSchema, args);
232
237
  if (rejected) return rejected;
238
+ const required = new Set(tool.inputSchema.required ?? []);
233
239
  const pathParams = {};
234
240
  const query = {};
235
241
  for (const param of tool.params) {
236
242
  if (args[param.name] === void 0) continue;
243
+ if (param.location === "query" && !required.has(param.name) && isEmptyValue(args[param.name]))
244
+ continue;
237
245
  if (param.location === "path") {
238
246
  pathParams[param.name] = args[param.name];
239
247
  continue;
@@ -255,6 +263,11 @@ async function dispatchGeneratedTool(client, tool, args) {
255
263
  };
256
264
  }
257
265
  }
266
+ if (tool.hasBody && requestBody !== void 0) {
267
+ const properties = tool.inputSchema.properties ?? {};
268
+ const defs = tool.inputSchema.$defs ?? {};
269
+ requestBody = coerceScalars(requestBody, properties.body, defs);
270
+ }
258
271
  const verbs = client;
259
272
  const { data, error, response } = await verbs[tool.method](tool.path, {
260
273
  params: { path: pathParams, query },
@@ -267,6 +280,79 @@ async function dispatchGeneratedTool(client, tool, args) {
267
280
  payload: data ?? { status: response.status }
268
281
  };
269
282
  }
283
+ function isEmptyValue(value) {
284
+ return value === null || value === "" || Array.isArray(value) && !value.length;
285
+ }
286
+ function asSchema(value) {
287
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
288
+ }
289
+ function resolveSchema(schema, defs, depth = 0) {
290
+ const node = asSchema(schema);
291
+ if (!node || depth > 16) return node;
292
+ const ref = node.$ref;
293
+ if (typeof ref === "string" && ref.startsWith("#/$defs/"))
294
+ return resolveSchema(defs[ref.slice("#/$defs/".length)], defs, depth + 1);
295
+ return node;
296
+ }
297
+ function branches(schema, defs, depth = 0) {
298
+ const node = resolveSchema(schema, defs);
299
+ if (!node || depth > 16) return [];
300
+ const out = [node];
301
+ for (const key of ["allOf", "anyOf", "oneOf"]) {
302
+ const list = node[key];
303
+ if (Array.isArray(list))
304
+ for (const sub of list) out.push(...branches(sub, defs, depth + 1));
305
+ }
306
+ return out;
307
+ }
308
+ function types(schemas) {
309
+ const out = /* @__PURE__ */ new Set();
310
+ for (const s of schemas) {
311
+ if (typeof s.type === "string") out.add(s.type);
312
+ else if (Array.isArray(s.type)) {
313
+ for (const t of s.type) if (typeof t === "string") out.add(t);
314
+ }
315
+ }
316
+ return out;
317
+ }
318
+ var NUMERIC = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
319
+ function coerceScalars(value, schema, defs, depth = 0) {
320
+ if (depth > 32) return value;
321
+ const applicable = branches(schema, defs);
322
+ if (!applicable.length) return value;
323
+ const allowed = types(applicable);
324
+ if (typeof value === "string") {
325
+ if (allowed.has("string")) return value;
326
+ if ((allowed.has("integer") || allowed.has("number")) && NUMERIC.test(value)) {
327
+ const n = Number(value);
328
+ if (allowed.has("number") || Number.isInteger(n)) return n;
329
+ }
330
+ if (allowed.has("boolean") && (value === "true" || value === "false"))
331
+ return value === "true";
332
+ return value;
333
+ }
334
+ if (Array.isArray(value)) {
335
+ const items = applicable.map((s) => s.items).filter(Boolean);
336
+ if (!items.length) return value;
337
+ return value.map(
338
+ (item) => coerceScalars(item, { anyOf: items }, defs, depth + 1)
339
+ );
340
+ }
341
+ const object = asSchema(value);
342
+ if (!object) return value;
343
+ const out = {};
344
+ for (const [key, child] of Object.entries(object)) {
345
+ const childSchemas = [];
346
+ for (const s of applicable) {
347
+ const props = asSchema(s.properties);
348
+ if (props && key in props) childSchemas.push(props[key]);
349
+ else if (asSchema(s.additionalProperties))
350
+ childSchemas.push(s.additionalProperties);
351
+ }
352
+ out[key] = childSchemas.length ? coerceScalars(child, { anyOf: childSchemas }, defs, depth + 1) : child;
353
+ }
354
+ return out;
355
+ }
270
356
 
271
357
  // src/specgen/generated/tools.gen.ts
272
358
  var generatedTools = [
@@ -284,6 +370,7 @@ var generatedTools = [
284
370
  idempotentHint: false,
285
371
  openWorldHint: true
286
372
  },
373
+ schemaOnDemand: true,
287
374
  inputSchema: {
288
375
  type: "object",
289
376
  properties: {
@@ -295,11 +382,10 @@ var generatedTools = [
295
382
  $defs: {
296
383
  BaseContainerConfig: {
297
384
  type: "object",
298
- 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",
299
385
  properties: {
300
386
  args: {
301
387
  type: "string",
302
- 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',
388
+ 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.',
303
389
  examples: [
304
390
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
305
391
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -311,7 +397,7 @@ var generatedTools = [
311
397
  items: {
312
398
  type: "string"
313
399
  },
314
- 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",
400
+ 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.",
315
401
  examples: [
316
402
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
317
403
  ]
@@ -321,7 +407,7 @@ var generatedTools = [
321
407
  items: {
322
408
  type: "string"
323
409
  },
324
- 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",
410
+ 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.",
325
411
  examples: [["/bin/bash", "-c"]]
326
412
  },
327
413
  disk: {
@@ -357,6 +443,21 @@ var generatedTools = [
357
443
  }
358
444
  }
359
445
  },
446
+ BaseMounts: {
447
+ type: "object",
448
+ properties: {
449
+ persistent: {
450
+ $ref: "#/$defs/PersistentMount"
451
+ },
452
+ network: {
453
+ type: "array",
454
+ maxItems: 1,
455
+ items: {
456
+ $ref: "#/$defs/NetworkMount"
457
+ }
458
+ }
459
+ }
460
+ },
360
461
  ClusterCompute: {
361
462
  type: "object",
362
463
  additionalProperties: false,
@@ -384,6 +485,15 @@ var generatedTools = [
384
485
  }
385
486
  }
386
487
  },
488
+ ClusterMounts: {
489
+ description: "Storage mounts for a cluster create: `persistent` or `network`, not both. Clusters cannot mount global volumes, so `global` is rejected.",
490
+ allOf: [
491
+ {
492
+ $ref: "#/$defs/BaseMounts"
493
+ }
494
+ ],
495
+ unevaluatedProperties: false
496
+ },
387
497
  ClusterType: {
388
498
  type: "string",
389
499
  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.",
@@ -398,7 +508,7 @@ var generatedTools = [
398
508
  {
399
509
  type: "object",
400
510
  required: ["name", "type", "compute"],
401
- 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",
511
+ description: "Request body for creating a cluster. `compute` defines the homogeneous pod shape; the container configuration (image, env, ports, \u2026) applies to every pod.",
402
512
  properties: {
403
513
  compute: {
404
514
  $ref: "#/$defs/ClusterCompute"
@@ -414,7 +524,7 @@ var generatedTools = [
414
524
  templateId: {
415
525
  type: "string",
416
526
  minLength: 1,
417
- 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",
527
+ 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.",
418
528
  examples: ["30zmvf89kd"]
419
529
  },
420
530
  dataCenterIds: {
@@ -422,11 +532,11 @@ var generatedTools = [
422
532
  items: {
423
533
  type: "string"
424
534
  },
425
- 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",
535
+ 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.",
426
536
  example: ["US-TX-3"]
427
537
  },
428
538
  mounts: {
429
- $ref: "#/$defs/Mounts"
539
+ $ref: "#/$defs/ClusterMounts"
430
540
  },
431
541
  startJupyter: {
432
542
  type: "boolean",
@@ -443,28 +553,11 @@ var generatedTools = [
443
553
  ],
444
554
  unevaluatedProperties: false
445
555
  },
446
- Mounts: {
447
- type: "object",
448
- additionalProperties: false,
449
- 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",
450
- properties: {
451
- persistent: {
452
- $ref: "#/$defs/PersistentMount"
453
- },
454
- network: {
455
- type: "array",
456
- maxItems: 1,
457
- items: {
458
- $ref: "#/$defs/NetworkMount"
459
- }
460
- }
461
- }
462
- },
463
556
  NetworkMount: {
464
557
  type: "object",
465
558
  required: ["volumeId", "path"],
466
559
  additionalProperties: false,
467
- 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",
560
+ 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.",
468
561
  properties: {
469
562
  volumeId: {
470
563
  type: "string",
@@ -482,7 +575,7 @@ var generatedTools = [
482
575
  type: "object",
483
576
  required: ["size", "path"],
484
577
  additionalProperties: false,
485
- 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",
578
+ 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.",
486
579
  properties: {
487
580
  size: {
488
581
  type: "integer",
@@ -559,6 +652,7 @@ var generatedTools = [
559
652
  idempotentHint: false,
560
653
  openWorldHint: true
561
654
  },
655
+ schemaOnDemand: true,
562
656
  inputSchema: {
563
657
  type: "object",
564
658
  properties: {
@@ -570,11 +664,10 @@ var generatedTools = [
570
664
  $defs: {
571
665
  BaseContainerConfig: {
572
666
  type: "object",
573
- 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",
574
667
  properties: {
575
668
  args: {
576
669
  type: "string",
577
- 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',
670
+ 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.',
578
671
  examples: [
579
672
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
580
673
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -586,7 +679,7 @@ var generatedTools = [
586
679
  items: {
587
680
  type: "string"
588
681
  },
589
- 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",
682
+ 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.",
590
683
  examples: [
591
684
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
592
685
  ]
@@ -596,7 +689,7 @@ var generatedTools = [
596
689
  items: {
597
690
  type: "string"
598
691
  },
599
- 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",
692
+ 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.",
600
693
  examples: [["/bin/bash", "-c"]]
601
694
  },
602
695
  disk: {
@@ -656,7 +749,7 @@ var generatedTools = [
656
749
  pools: {
657
750
  type: "array",
658
751
  minItems: 1,
659
- 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',
752
+ 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.",
660
753
  items: {
661
754
  type: "string"
662
755
  },
@@ -665,7 +758,7 @@ var generatedTools = [
665
758
  excludedTypes: {
666
759
  type: "array",
667
760
  uniqueItems: true,
668
- 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',
761
+ 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.",
669
762
  items: {
670
763
  type: "string",
671
764
  pattern: "^\\s*[^-\\s]"
@@ -682,7 +775,6 @@ var generatedTools = [
682
775
  }
683
776
  },
684
777
  ContainerConfig: {
685
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
686
778
  allOf: [
687
779
  {
688
780
  $ref: "#/$defs/BaseContainerConfig"
@@ -708,7 +800,7 @@ var generatedTools = [
708
800
  unevaluatedProperties: false
709
801
  },
710
802
  CreateEndpointGpuConfig: {
711
- 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",
803
+ 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.",
712
804
  allOf: [
713
805
  {
714
806
  $ref: "#/$defs/BaseEndpointGpuConfig"
@@ -723,13 +815,13 @@ var generatedTools = [
723
815
  type: "string",
724
816
  pattern: "^\\d+\\.\\d+$"
725
817
  },
726
- 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",
818
+ 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`).",
727
819
  examples: [["12.8", "12.6"]]
728
820
  },
729
821
  minCudaVersion: {
730
822
  type: "string",
731
823
  pattern: "^\\d+\\.\\d+$",
732
- 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",
824
+ 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.",
733
825
  examples: ["12.1"]
734
826
  }
735
827
  }
@@ -773,13 +865,13 @@ var generatedTools = [
773
865
  $ref: "#/$defs/EndpointType"
774
866
  }
775
867
  ],
776
- description: "Request-routing model. Required \u2014 it determines the valid scaler\nand request URLs, so it must be chosen explicitly on every create.\n"
868
+ description: "Request-routing model. Required \u2014 it determines the valid scaler and request URLs, so it must be chosen explicitly on every create."
777
869
  },
778
870
  cpu: {
779
871
  type: "array",
780
872
  minItems: 1,
781
873
  uniqueItems: true,
782
- 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",
874
+ 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.",
783
875
  items: {
784
876
  $ref: "#/$defs/CreateCpuConfig"
785
877
  }
@@ -803,12 +895,17 @@ var generatedTools = [
803
895
  type: "array",
804
896
  items: {
805
897
  type: "string"
806
- }
898
+ },
899
+ description: "Network volume IDs.",
900
+ deprecated: true
901
+ },
902
+ mounts: {
903
+ $ref: "#/$defs/EndpointMountsInput"
807
904
  },
808
905
  templateId: {
809
906
  type: "string",
810
907
  minLength: 1,
811
- 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",
908
+ 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.",
812
909
  examples: ["30zmvf89kd"]
813
910
  },
814
911
  timeout: {
@@ -856,8 +953,42 @@ var generatedTools = [
856
953
  }
857
954
  }
858
955
  },
956
+ EndpointMount: {
957
+ type: "object",
958
+ required: ["volumeId"],
959
+ additionalProperties: false,
960
+ description: "A volume mounted on every worker, always at `/runpod-volume`. Custom paths are not supported yet.",
961
+ properties: {
962
+ volumeId: {
963
+ type: "string",
964
+ description: "ID of a NetworkVolume (under `network`) or GlobalVolume (under `global`).",
965
+ examples: ["vol_abc"]
966
+ }
967
+ }
968
+ },
969
+ EndpointMountsInput: {
970
+ type: "object",
971
+ additionalProperties: false,
972
+ 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.",
973
+ properties: {
974
+ network: {
975
+ type: "array",
976
+ description: "Network volumes, at most one per data center.",
977
+ items: {
978
+ $ref: "#/$defs/EndpointMount"
979
+ }
980
+ },
981
+ global: {
982
+ type: "array",
983
+ maxItems: 1,
984
+ items: {
985
+ $ref: "#/$defs/EndpointMount"
986
+ }
987
+ }
988
+ }
989
+ },
859
990
  EndpointScaling: {
860
- 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",
991
+ 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.",
861
992
  oneOf: [
862
993
  {
863
994
  $ref: "#/$defs/QueueDelayScaling"
@@ -876,7 +1007,7 @@ var generatedTools = [
876
1007
  },
877
1008
  EndpointType: {
878
1009
  type: "string",
879
- 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",
1010
+ 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.",
880
1011
  "x-enum-varnames": ["EndpointTypeQueue", "EndpointTypeLoadBalancer"],
881
1012
  enum: ["QUEUE", "LOAD_BALANCER"]
882
1013
  },
@@ -900,14 +1031,14 @@ var generatedTools = [
900
1031
  type: "integer",
901
1032
  minimum: 1,
902
1033
  maximum: 3600,
903
- 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",
1034
+ 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.",
904
1035
  examples: [5]
905
1036
  }
906
1037
  }
907
1038
  },
908
1039
  FlashBoot: {
909
1040
  type: "string",
910
- description: "FlashBoot cold-start acceleration mode.\n- `OFF` \u2014 disabled\n- `FLASHBOOT` \u2014 enabled\n- `PRIORITY_FLASHBOOT` \u2014 enabled with priority capacity\n",
1041
+ description: "FlashBoot cold-start acceleration mode. `OFF` \u2014 disabled. `FLASHBOOT` \u2014 enabled. `PRIORITY_FLASHBOOT` \u2014 enabled with priority capacity",
911
1042
  enum: ["OFF", "FLASHBOOT", "PRIORITY_FLASHBOOT"]
912
1043
  },
913
1044
  QueueDelayScaling: {
@@ -933,7 +1064,7 @@ var generatedTools = [
933
1064
  RequestCountScaling: {
934
1065
  type: "object",
935
1066
  additionalProperties: false,
936
- description: "Scale on concurrent in-flight requests per worker. Required for\nload-balancing endpoints; also selectable for queue-based.\n",
1067
+ description: "Scale on concurrent in-flight requests per worker. Required for load-balancing endpoints; also selectable for queue-based.",
937
1068
  required: ["type", "requestCount"],
938
1069
  properties: {
939
1070
  type: {
@@ -952,6 +1083,47 @@ var generatedTools = [
952
1083
  }
953
1084
  }
954
1085
  },
1086
+ {
1087
+ name: "create-global-volume",
1088
+ operationId: "createGlobalVolume",
1089
+ 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`.",
1090
+ method: "POST",
1091
+ path: "/v2/global-volumes",
1092
+ params: [],
1093
+ hasBody: true,
1094
+ annotations: {
1095
+ readOnlyHint: false,
1096
+ destructiveHint: false,
1097
+ idempotentHint: false,
1098
+ openWorldHint: true
1099
+ },
1100
+ inputSchema: {
1101
+ type: "object",
1102
+ properties: {
1103
+ body: {
1104
+ $ref: "#/$defs/CreateGlobalVolumeRequest"
1105
+ }
1106
+ },
1107
+ required: ["body"],
1108
+ $defs: {
1109
+ CreateGlobalVolumeRequest: {
1110
+ type: "object",
1111
+ additionalProperties: false,
1112
+ required: ["name"],
1113
+ properties: {
1114
+ name: {
1115
+ type: "string",
1116
+ minLength: 1,
1117
+ maxLength: 191,
1118
+ pattern: "[^\\p{Z}\\t-\\r\x85]",
1119
+ description: "Human-readable name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed.",
1120
+ examples: ["training-data"]
1121
+ }
1122
+ }
1123
+ }
1124
+ }
1125
+ }
1126
+ },
955
1127
  {
956
1128
  name: "create-network-volume",
957
1129
  operationId: "createNetworkVolume",
@@ -1005,7 +1177,7 @@ var generatedTools = [
1005
1177
  $ref: "#/$defs/VolumeType"
1006
1178
  }
1007
1179
  ],
1008
- 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"
1180
+ description: "Storage tier for the volume. Optional. When omitted, the volume is provisioned using the requested data center's default (primary) storage tier."
1009
1181
  }
1010
1182
  }
1011
1183
  },
@@ -1031,6 +1203,7 @@ var generatedTools = [
1031
1203
  idempotentHint: false,
1032
1204
  openWorldHint: true
1033
1205
  },
1206
+ schemaOnDemand: true,
1034
1207
  inputSchema: {
1035
1208
  type: "object",
1036
1209
  properties: {
@@ -1042,11 +1215,10 @@ var generatedTools = [
1042
1215
  $defs: {
1043
1216
  BaseContainerConfig: {
1044
1217
  type: "object",
1045
- 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",
1046
1218
  properties: {
1047
1219
  args: {
1048
1220
  type: "string",
1049
- 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',
1221
+ 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.',
1050
1222
  examples: [
1051
1223
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
1052
1224
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -1058,7 +1230,7 @@ var generatedTools = [
1058
1230
  items: {
1059
1231
  type: "string"
1060
1232
  },
1061
- 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",
1233
+ 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.",
1062
1234
  examples: [
1063
1235
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
1064
1236
  ]
@@ -1068,7 +1240,7 @@ var generatedTools = [
1068
1240
  items: {
1069
1241
  type: "string"
1070
1242
  },
1071
- 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",
1243
+ 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.",
1072
1244
  examples: [["/bin/bash", "-c"]]
1073
1245
  },
1074
1246
  disk: {
@@ -1140,13 +1312,27 @@ var generatedTools = [
1140
1312
  }
1141
1313
  }
1142
1314
  },
1315
+ BaseMounts: {
1316
+ type: "object",
1317
+ properties: {
1318
+ persistent: {
1319
+ $ref: "#/$defs/PersistentMount"
1320
+ },
1321
+ network: {
1322
+ type: "array",
1323
+ maxItems: 1,
1324
+ items: {
1325
+ $ref: "#/$defs/NetworkMount"
1326
+ }
1327
+ }
1328
+ }
1329
+ },
1143
1330
  Cloud: {
1144
1331
  type: "string",
1145
- description: "Cloud tier.\n- `SECURE` \u2014 Runpod-owned datacenter hardware\n- `COMMUNITY` \u2014 community-hosted hardware\n",
1332
+ description: "Cloud tier. `SECURE` \u2014 Runpod-owned datacenter hardware. `COMMUNITY` \u2014 community-hosted hardware",
1146
1333
  enum: ["SECURE", "COMMUNITY"]
1147
1334
  },
1148
1335
  ContainerConfig: {
1149
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
1150
1336
  allOf: [
1151
1337
  {
1152
1338
  $ref: "#/$defs/BaseContainerConfig"
@@ -1172,7 +1358,7 @@ var generatedTools = [
1172
1358
  unevaluatedProperties: false
1173
1359
  },
1174
1360
  CreateGpuConfig: {
1175
- 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",
1361
+ 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.",
1176
1362
  allOf: [
1177
1363
  {
1178
1364
  $ref: "#/$defs/BaseGpuConfig"
@@ -1186,13 +1372,13 @@ var generatedTools = [
1186
1372
  type: "string",
1187
1373
  pattern: "^\\d+\\.\\d+$"
1188
1374
  },
1189
- 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",
1375
+ description: "Acceptable CUDA versions for the host machine, as `major.minor`. Omit to accept any version.",
1190
1376
  examples: [["12.8", "12.6"]]
1191
1377
  },
1192
1378
  minCudaVersion: {
1193
1379
  type: "string",
1194
1380
  pattern: "^\\d+\\.\\d+$",
1195
- 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",
1381
+ 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.",
1196
1382
  examples: ["12.1"]
1197
1383
  },
1198
1384
  minRamPerGpu: {
@@ -1220,7 +1406,7 @@ var generatedTools = [
1220
1406
  {
1221
1407
  type: "object",
1222
1408
  required: ["name"],
1223
- 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",
1409
+ description: "Request body for creating a pod. Exactly one of `gpu` or `cpu` must be set \u2014 enforced at the handler layer.",
1224
1410
  properties: {
1225
1411
  name: {
1226
1412
  type: "string",
@@ -1244,13 +1430,13 @@ var generatedTools = [
1244
1430
  items: {
1245
1431
  type: "string"
1246
1432
  },
1247
- description: "Preferred data centers for placement. Omit or pass an empty\narray to let the scheduler choose.\n",
1433
+ description: "Preferred data centers for placement. Omit or pass an empty array to let the scheduler choose.",
1248
1434
  examples: [["US-TX-3"]]
1249
1435
  },
1250
1436
  globalNetworking: {
1251
1437
  type: "boolean",
1252
1438
  default: false,
1253
- 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.",
1439
+ 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).",
1254
1440
  examples: [false]
1255
1441
  },
1256
1442
  gpu: {
@@ -1262,19 +1448,19 @@ var generatedTools = [
1262
1448
  startJupyter: {
1263
1449
  type: "boolean",
1264
1450
  default: false,
1265
- 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",
1451
+ 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.",
1266
1452
  examples: [true]
1267
1453
  },
1268
1454
  startSsh: {
1269
1455
  type: "boolean",
1270
1456
  default: false,
1271
- 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",
1457
+ 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`.",
1272
1458
  examples: [true]
1273
1459
  },
1274
1460
  templateId: {
1275
1461
  type: "string",
1276
1462
  minLength: 1,
1277
- 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",
1463
+ 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.",
1278
1464
  examples: ["30zmvf89kd"]
1279
1465
  }
1280
1466
  }
@@ -1290,28 +1476,51 @@ var generatedTools = [
1290
1476
  required: ["image"]
1291
1477
  }
1292
1478
  },
1293
- Mounts: {
1479
+ GlobalMount: {
1294
1480
  type: "object",
1481
+ required: ["volumeId"],
1295
1482
  additionalProperties: false,
1296
- 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",
1483
+ 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.",
1297
1484
  properties: {
1298
- persistent: {
1299
- $ref: "#/$defs/PersistentMount"
1485
+ volumeId: {
1486
+ type: "string",
1487
+ description: "ID of an existing GlobalVolume.",
1488
+ examples: ["gsb_abc"]
1300
1489
  },
1301
- network: {
1302
- type: "array",
1303
- maxItems: 1,
1304
- items: {
1305
- $ref: "#/$defs/NetworkMount"
1306
- }
1490
+ path: {
1491
+ type: "string",
1492
+ 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.",
1493
+ examples: ["/data"]
1307
1494
  }
1308
1495
  }
1309
1496
  },
1497
+ Mounts: {
1498
+ 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.",
1499
+ allOf: [
1500
+ {
1501
+ $ref: "#/$defs/BaseMounts"
1502
+ },
1503
+ {
1504
+ type: "object",
1505
+ properties: {
1506
+ global: {
1507
+ type: "array",
1508
+ maxItems: 1,
1509
+ 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.",
1510
+ items: {
1511
+ $ref: "#/$defs/GlobalMount"
1512
+ }
1513
+ }
1514
+ }
1515
+ }
1516
+ ],
1517
+ unevaluatedProperties: false
1518
+ },
1310
1519
  NetworkMount: {
1311
1520
  type: "object",
1312
1521
  required: ["volumeId", "path"],
1313
1522
  additionalProperties: false,
1314
- 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",
1523
+ 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.",
1315
1524
  properties: {
1316
1525
  volumeId: {
1317
1526
  type: "string",
@@ -1329,7 +1538,7 @@ var generatedTools = [
1329
1538
  type: "object",
1330
1539
  required: ["size", "path"],
1331
1540
  additionalProperties: false,
1332
- 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",
1541
+ 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.",
1333
1542
  properties: {
1334
1543
  size: {
1335
1544
  type: "integer",
@@ -1412,6 +1621,7 @@ var generatedTools = [
1412
1621
  idempotentHint: false,
1413
1622
  openWorldHint: true
1414
1623
  },
1624
+ schemaOnDemand: true,
1415
1625
  inputSchema: {
1416
1626
  type: "object",
1417
1627
  properties: {
@@ -1431,7 +1641,7 @@ var generatedTools = [
1431
1641
  minLength: 1,
1432
1642
  maxLength: 191,
1433
1643
  pattern: "^[a-zA-Z_][a-zA-Z0-9_.\\-/]*$",
1434
- 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).",
1644
+ 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 `_.-/`.",
1435
1645
  examples: ["hf-token"]
1436
1646
  },
1437
1647
  value: {
@@ -1465,6 +1675,7 @@ var generatedTools = [
1465
1675
  idempotentHint: false,
1466
1676
  openWorldHint: true
1467
1677
  },
1678
+ schemaOnDemand: true,
1468
1679
  inputSchema: {
1469
1680
  type: "object",
1470
1681
  properties: {
@@ -1476,11 +1687,10 @@ var generatedTools = [
1476
1687
  $defs: {
1477
1688
  BaseContainerConfig: {
1478
1689
  type: "object",
1479
- 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",
1480
1690
  properties: {
1481
1691
  args: {
1482
1692
  type: "string",
1483
- 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',
1693
+ 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.',
1484
1694
  examples: [
1485
1695
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
1486
1696
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -1492,7 +1702,7 @@ var generatedTools = [
1492
1702
  items: {
1493
1703
  type: "string"
1494
1704
  },
1495
- 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",
1705
+ 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.",
1496
1706
  examples: [
1497
1707
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
1498
1708
  ]
@@ -1502,7 +1712,7 @@ var generatedTools = [
1502
1712
  items: {
1503
1713
  type: "string"
1504
1714
  },
1505
- 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",
1715
+ 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.",
1506
1716
  examples: [["/bin/bash", "-c"]]
1507
1717
  },
1508
1718
  disk: {
@@ -1539,7 +1749,6 @@ var generatedTools = [
1539
1749
  }
1540
1750
  },
1541
1751
  ContainerConfig: {
1542
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
1543
1752
  allOf: [
1544
1753
  {
1545
1754
  $ref: "#/$defs/BaseContainerConfig"
@@ -1576,7 +1785,7 @@ var generatedTools = [
1576
1785
  type: "string",
1577
1786
  pattern: "^\\d+\\.\\d+$"
1578
1787
  },
1579
- 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",
1788
+ 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.",
1580
1789
  examples: [["12.8", "12.6"]]
1581
1790
  },
1582
1791
  category: {
@@ -1602,13 +1811,13 @@ var generatedTools = [
1602
1811
  startJupyter: {
1603
1812
  type: "boolean",
1604
1813
  default: true,
1605
- 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",
1814
+ description: "Start JupyterLab in containers created from this template: injects a generated `JUPYTER_PASSWORD` environment variable, unless `env` already sets one.",
1606
1815
  examples: [false]
1607
1816
  },
1608
1817
  startSsh: {
1609
1818
  type: "boolean",
1610
1819
  default: true,
1611
- 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",
1820
+ 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.",
1612
1821
  examples: [true]
1613
1822
  }
1614
1823
  }
@@ -1620,7 +1829,7 @@ var generatedTools = [
1620
1829
  type: "object",
1621
1830
  required: ["size", "path"],
1622
1831
  additionalProperties: false,
1623
- 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",
1832
+ 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.",
1624
1833
  properties: {
1625
1834
  size: {
1626
1835
  type: "integer",
@@ -1637,13 +1846,13 @@ var generatedTools = [
1637
1846
  },
1638
1847
  TemplateCategory: {
1639
1848
  type: "string",
1640
- 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",
1849
+ 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",
1641
1850
  enum: ["CPU", "NVIDIA", "AMD"]
1642
1851
  },
1643
1852
  TemplateMounts: {
1644
1853
  type: "object",
1645
1854
  additionalProperties: false,
1646
- 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",
1855
+ 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.",
1647
1856
  properties: {
1648
1857
  persistent: {
1649
1858
  $ref: "#/$defs/PersistentMount"
@@ -1713,6 +1922,36 @@ var generatedTools = [
1713
1922
  required: ["id"]
1714
1923
  }
1715
1924
  },
1925
+ {
1926
+ name: "delete-global-volume",
1927
+ operationId: "deleteGlobalVolume",
1928
+ 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.",
1929
+ method: "DELETE",
1930
+ path: "/v2/global-volumes/{id}",
1931
+ params: [
1932
+ {
1933
+ name: "id",
1934
+ location: "path"
1935
+ }
1936
+ ],
1937
+ hasBody: false,
1938
+ annotations: {
1939
+ readOnlyHint: false,
1940
+ destructiveHint: true,
1941
+ idempotentHint: true,
1942
+ openWorldHint: true
1943
+ },
1944
+ inputSchema: {
1945
+ type: "object",
1946
+ properties: {
1947
+ id: {
1948
+ type: "string",
1949
+ description: "Global volume identifier"
1950
+ }
1951
+ },
1952
+ required: ["id"]
1953
+ }
1954
+ },
1716
1955
  {
1717
1956
  name: "delete-network-volume",
1718
1957
  operationId: "deleteNetworkVolume",
@@ -2078,12 +2317,42 @@ var generatedTools = [
2078
2317
  },
2079
2318
  buildId: {
2080
2319
  type: "string",
2081
- description: "GitHub build identifier (from GET /v2/serverless/{id}/builds or a release's `buildId`)"
2320
+ description: "GitHub build identifier (from `list-endpoint-builds` or a release's `buildId`)"
2082
2321
  }
2083
2322
  },
2084
2323
  required: ["id", "buildId"]
2085
2324
  }
2086
2325
  },
2326
+ {
2327
+ name: "get-global-volume",
2328
+ operationId: "getGlobalVolume",
2329
+ description: "Get a global volume. Returns a single global volume by ID.",
2330
+ method: "GET",
2331
+ path: "/v2/global-volumes/{id}",
2332
+ params: [
2333
+ {
2334
+ name: "id",
2335
+ location: "path"
2336
+ }
2337
+ ],
2338
+ hasBody: false,
2339
+ annotations: {
2340
+ readOnlyHint: true,
2341
+ destructiveHint: false,
2342
+ idempotentHint: true,
2343
+ openWorldHint: true
2344
+ },
2345
+ inputSchema: {
2346
+ type: "object",
2347
+ properties: {
2348
+ id: {
2349
+ type: "string",
2350
+ description: "Global volume identifier"
2351
+ }
2352
+ },
2353
+ required: ["id"]
2354
+ }
2355
+ },
2087
2356
  {
2088
2357
  name: "get-gpu-type",
2089
2358
  operationId: "getGpuType",
@@ -2154,7 +2423,7 @@ var generatedTools = [
2154
2423
  items: {
2155
2424
  $ref: "#/$defs/Product"
2156
2425
  },
2157
- 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."
2426
+ description: "Comma-separated availability product contexts. Supported values: POD, CLUSTER, SERVERLESS. Required with include=AVAILABILITY, and valid only with it (400 either way)."
2158
2427
  },
2159
2428
  count: {
2160
2429
  type: "integer",
@@ -2172,7 +2441,7 @@ var generatedTools = [
2172
2441
  type: "string",
2173
2442
  pattern: "^[A-Z]{2}$"
2174
2443
  },
2175
- 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."
2444
+ 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."
2176
2445
  },
2177
2446
  cudaVersions: {
2178
2447
  type: "array",
@@ -2180,12 +2449,12 @@ var generatedTools = [
2180
2449
  type: "string",
2181
2450
  pattern: "^\\d+\\.\\d+$"
2182
2451
  },
2183
- 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."
2452
+ 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."
2184
2453
  },
2185
2454
  minCudaVersion: {
2186
2455
  type: "string",
2187
2456
  pattern: "^\\d+(\\.\\d+)?$",
2188
- 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."
2457
+ description: "Lowest acceptable CUDA version to scope availability and lowest-price calculations to, compared numerically."
2189
2458
  }
2190
2459
  },
2191
2460
  required: ["id"],
@@ -2238,6 +2507,25 @@ var generatedTools = [
2238
2507
  required: ["id"]
2239
2508
  }
2240
2509
  },
2510
+ {
2511
+ name: "get-organization",
2512
+ operationId: "getOrganization",
2513
+ 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.",
2514
+ method: "GET",
2515
+ path: "/v2/organization",
2516
+ params: [],
2517
+ hasBody: false,
2518
+ annotations: {
2519
+ readOnlyHint: true,
2520
+ destructiveHint: false,
2521
+ idempotentHint: true,
2522
+ openWorldHint: true
2523
+ },
2524
+ inputSchema: {
2525
+ type: "object",
2526
+ properties: {}
2527
+ }
2528
+ },
2241
2529
  {
2242
2530
  name: "get-pod",
2243
2531
  operationId: "getPod",
@@ -2412,12 +2700,12 @@ var generatedTools = [
2412
2700
  startTime: {
2413
2701
  type: "string",
2414
2702
  format: "date-time",
2415
- 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"
2703
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
2416
2704
  },
2417
2705
  endTime: {
2418
2706
  type: "string",
2419
2707
  format: "date-time",
2420
- 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"
2708
+ 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."
2421
2709
  },
2422
2710
  bucketSize: {
2423
2711
  $ref: "#/$defs/BillingBucketSize",
@@ -2426,7 +2714,7 @@ var generatedTools = [
2426
2714
  lastN: {
2427
2715
  type: "integer",
2428
2716
  minimum: 1,
2429
- 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'
2717
+ 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".'
2430
2718
  }
2431
2719
  },
2432
2720
  $defs: {
@@ -2488,12 +2776,12 @@ var generatedTools = [
2488
2776
  startTime: {
2489
2777
  type: "string",
2490
2778
  format: "date-time",
2491
- 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"
2779
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
2492
2780
  },
2493
2781
  endTime: {
2494
2782
  type: "string",
2495
2783
  format: "date-time",
2496
- 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"
2784
+ 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."
2497
2785
  },
2498
2786
  bucketSize: {
2499
2787
  $ref: "#/$defs/BillingBucketSize",
@@ -2502,7 +2790,7 @@ var generatedTools = [
2502
2790
  lastN: {
2503
2791
  type: "integer",
2504
2792
  minimum: 1,
2505
- 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'
2793
+ 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".'
2506
2794
  },
2507
2795
  clusterId: {
2508
2796
  type: "string",
@@ -2560,10 +2848,19 @@ var generatedTools = [
2560
2848
  {
2561
2849
  name: "list-clusters",
2562
2850
  operationId: "listClusters",
2563
- description: "List clusters. Returns all clusters owned by the authenticated user.",
2851
+ 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.",
2564
2852
  method: "GET",
2565
2853
  path: "/v2/clusters",
2566
- params: [],
2854
+ params: [
2855
+ {
2856
+ name: "cursor",
2857
+ location: "query"
2858
+ },
2859
+ {
2860
+ name: "limit",
2861
+ location: "query"
2862
+ }
2863
+ ],
2567
2864
  hasBody: false,
2568
2865
  annotations: {
2569
2866
  readOnlyHint: true,
@@ -2573,7 +2870,19 @@ var generatedTools = [
2573
2870
  },
2574
2871
  inputSchema: {
2575
2872
  type: "object",
2576
- properties: {}
2873
+ properties: {
2874
+ cursor: {
2875
+ type: "string",
2876
+ minLength: 1,
2877
+ 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."
2878
+ },
2879
+ limit: {
2880
+ type: "integer",
2881
+ minimum: 1,
2882
+ maximum: 1e3,
2883
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
2884
+ }
2885
+ }
2577
2886
  }
2578
2887
  },
2579
2888
  {
@@ -2827,12 +3136,12 @@ var generatedTools = [
2827
3136
  startTime: {
2828
3137
  type: "string",
2829
3138
  format: "date-time",
2830
- 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"
3139
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
2831
3140
  },
2832
3141
  endTime: {
2833
3142
  type: "string",
2834
3143
  format: "date-time",
2835
- 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"
3144
+ 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."
2836
3145
  },
2837
3146
  bucketSize: {
2838
3147
  $ref: "#/$defs/BillingBucketSize",
@@ -2841,7 +3150,7 @@ var generatedTools = [
2841
3150
  lastN: {
2842
3151
  type: "integer",
2843
3152
  minimum: 1,
2844
- 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'
3153
+ 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".'
2845
3154
  }
2846
3155
  },
2847
3156
  $defs: {
@@ -2899,7 +3208,7 @@ var generatedTools = [
2899
3208
  cursor: {
2900
3209
  type: "string",
2901
3210
  minLength: 1,
2902
- 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"
3211
+ 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."
2903
3212
  },
2904
3213
  limit: {
2905
3214
  type: "integer",
@@ -2948,7 +3257,7 @@ var generatedTools = [
2948
3257
  cursor: {
2949
3258
  type: "string",
2950
3259
  minLength: 1,
2951
- 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"
3260
+ 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."
2952
3261
  },
2953
3262
  limit: {
2954
3263
  type: "integer",
@@ -2990,6 +3299,46 @@ var generatedTools = [
2990
3299
  required: ["id"]
2991
3300
  }
2992
3301
  },
3302
+ {
3303
+ name: "list-global-volumes",
3304
+ operationId: "listGlobalVolumes",
3305
+ 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.",
3306
+ method: "GET",
3307
+ path: "/v2/global-volumes",
3308
+ params: [
3309
+ {
3310
+ name: "cursor",
3311
+ location: "query"
3312
+ },
3313
+ {
3314
+ name: "limit",
3315
+ location: "query"
3316
+ }
3317
+ ],
3318
+ hasBody: false,
3319
+ annotations: {
3320
+ readOnlyHint: true,
3321
+ destructiveHint: false,
3322
+ idempotentHint: true,
3323
+ openWorldHint: true
3324
+ },
3325
+ inputSchema: {
3326
+ type: "object",
3327
+ properties: {
3328
+ cursor: {
3329
+ type: "string",
3330
+ minLength: 1,
3331
+ 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."
3332
+ },
3333
+ limit: {
3334
+ type: "integer",
3335
+ minimum: 1,
3336
+ maximum: 1e3,
3337
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
3338
+ }
3339
+ }
3340
+ }
3341
+ },
2993
3342
  {
2994
3343
  name: "list-gpu-types",
2995
3344
  operationId: "listGpuTypes",
@@ -3053,7 +3402,7 @@ var generatedTools = [
3053
3402
  items: {
3054
3403
  $ref: "#/$defs/Product"
3055
3404
  },
3056
- 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."
3405
+ description: "Comma-separated availability product contexts. Supported values: POD, CLUSTER, SERVERLESS. Required with include=AVAILABILITY, and valid only with it (400 either way)."
3057
3406
  },
3058
3407
  count: {
3059
3408
  type: "integer",
@@ -3071,7 +3420,7 @@ var generatedTools = [
3071
3420
  type: "string",
3072
3421
  pattern: "^[A-Z]{2}$"
3073
3422
  },
3074
- 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."
3423
+ 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."
3075
3424
  },
3076
3425
  cudaVersions: {
3077
3426
  type: "array",
@@ -3079,12 +3428,12 @@ var generatedTools = [
3079
3428
  type: "string",
3080
3429
  pattern: "^\\d+\\.\\d+$"
3081
3430
  },
3082
- 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."
3431
+ 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."
3083
3432
  },
3084
3433
  minCudaVersion: {
3085
3434
  type: "string",
3086
3435
  pattern: "^\\d+(\\.\\d+)?$",
3087
- 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."
3436
+ description: "Lowest acceptable CUDA version to scope availability and lowest-price calculations to, compared numerically."
3088
3437
  }
3089
3438
  },
3090
3439
  $defs: {
@@ -3147,12 +3496,12 @@ var generatedTools = [
3147
3496
  startTime: {
3148
3497
  type: "string",
3149
3498
  format: "date-time",
3150
- 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"
3499
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
3151
3500
  },
3152
3501
  endTime: {
3153
3502
  type: "string",
3154
3503
  format: "date-time",
3155
- 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"
3504
+ 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."
3156
3505
  },
3157
3506
  bucketSize: {
3158
3507
  $ref: "#/$defs/BillingBucketSize",
@@ -3161,7 +3510,7 @@ var generatedTools = [
3161
3510
  lastN: {
3162
3511
  type: "integer",
3163
3512
  minimum: 1,
3164
- 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'
3513
+ 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".'
3165
3514
  },
3166
3515
  networkVolumeId: {
3167
3516
  type: "string",
@@ -3189,10 +3538,19 @@ var generatedTools = [
3189
3538
  {
3190
3539
  name: "list-network-volumes",
3191
3540
  operationId: "listNetworkVolumes",
3192
- description: "List network volumes. Returns all network volumes owned by the authenticated user.",
3541
+ 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.",
3193
3542
  method: "GET",
3194
3543
  path: "/v2/network-volumes",
3195
- params: [],
3544
+ params: [
3545
+ {
3546
+ name: "cursor",
3547
+ location: "query"
3548
+ },
3549
+ {
3550
+ name: "limit",
3551
+ location: "query"
3552
+ }
3553
+ ],
3196
3554
  hasBody: false,
3197
3555
  annotations: {
3198
3556
  readOnlyHint: true,
@@ -3202,7 +3560,19 @@ var generatedTools = [
3202
3560
  },
3203
3561
  inputSchema: {
3204
3562
  type: "object",
3205
- properties: {}
3563
+ properties: {
3564
+ cursor: {
3565
+ type: "string",
3566
+ minLength: 1,
3567
+ 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."
3568
+ },
3569
+ limit: {
3570
+ type: "integer",
3571
+ minimum: 1,
3572
+ maximum: 1e3,
3573
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
3574
+ }
3575
+ }
3206
3576
  }
3207
3577
  },
3208
3578
  {
@@ -3246,12 +3616,12 @@ var generatedTools = [
3246
3616
  startTime: {
3247
3617
  type: "string",
3248
3618
  format: "date-time",
3249
- 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"
3619
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
3250
3620
  },
3251
3621
  endTime: {
3252
3622
  type: "string",
3253
3623
  format: "date-time",
3254
- 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"
3624
+ 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."
3255
3625
  },
3256
3626
  bucketSize: {
3257
3627
  $ref: "#/$defs/BillingBucketSize",
@@ -3260,7 +3630,7 @@ var generatedTools = [
3260
3630
  lastN: {
3261
3631
  type: "integer",
3262
3632
  minimum: 1,
3263
- 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'
3633
+ 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".'
3264
3634
  },
3265
3635
  podId: {
3266
3636
  type: "string",
@@ -3288,7 +3658,7 @@ var generatedTools = [
3288
3658
  {
3289
3659
  name: "list-pods",
3290
3660
  operationId: "listPods",
3291
- 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.",
3661
+ 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.",
3292
3662
  method: "GET",
3293
3663
  path: "/v2/pods",
3294
3664
  params: [
@@ -3323,7 +3693,7 @@ var generatedTools = [
3323
3693
  cursor: {
3324
3694
  type: "string",
3325
3695
  minLength: 1,
3326
- 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"
3696
+ 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."
3327
3697
  },
3328
3698
  limit: {
3329
3699
  type: "integer",
@@ -3360,7 +3730,7 @@ var generatedTools = [
3360
3730
  type: "string",
3361
3731
  enum: ["official", "verified", "community"],
3362
3732
  default: "official",
3363
- 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"
3733
+ 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."
3364
3734
  }
3365
3735
  }
3366
3736
  }
@@ -3454,12 +3824,12 @@ var generatedTools = [
3454
3824
  startTime: {
3455
3825
  type: "string",
3456
3826
  format: "date-time",
3457
- 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"
3827
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
3458
3828
  },
3459
3829
  endTime: {
3460
3830
  type: "string",
3461
3831
  format: "date-time",
3462
- 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"
3832
+ 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."
3463
3833
  },
3464
3834
  bucketSize: {
3465
3835
  $ref: "#/$defs/BillingBucketSize",
@@ -3468,7 +3838,7 @@ var generatedTools = [
3468
3838
  lastN: {
3469
3839
  type: "integer",
3470
3840
  minimum: 1,
3471
- 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'
3841
+ 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".'
3472
3842
  },
3473
3843
  serverlessId: {
3474
3844
  type: "string",
@@ -3607,7 +3977,7 @@ var generatedTools = [
3607
3977
  type: "object",
3608
3978
  additionalProperties: false,
3609
3979
  minProperties: 1,
3610
- 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",
3980
+ 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.",
3611
3981
  properties: {
3612
3982
  name: {
3613
3983
  type: "string",
@@ -3638,6 +4008,7 @@ var generatedTools = [
3638
4008
  idempotentHint: false,
3639
4009
  openWorldHint: true
3640
4010
  },
4011
+ schemaOnDemand: true,
3641
4012
  inputSchema: {
3642
4013
  type: "object",
3643
4014
  properties: {
@@ -3653,11 +4024,10 @@ var generatedTools = [
3653
4024
  $defs: {
3654
4025
  BaseContainerConfig: {
3655
4026
  type: "object",
3656
- 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",
3657
4027
  properties: {
3658
4028
  args: {
3659
4029
  type: "string",
3660
- 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',
4030
+ 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.',
3661
4031
  examples: [
3662
4032
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
3663
4033
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -3669,7 +4039,7 @@ var generatedTools = [
3669
4039
  items: {
3670
4040
  type: "string"
3671
4041
  },
3672
- 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",
4042
+ 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.",
3673
4043
  examples: [
3674
4044
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
3675
4045
  ]
@@ -3679,7 +4049,7 @@ var generatedTools = [
3679
4049
  items: {
3680
4050
  type: "string"
3681
4051
  },
3682
- 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",
4052
+ 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.",
3683
4053
  examples: [["/bin/bash", "-c"]]
3684
4054
  },
3685
4055
  disk: {
@@ -3739,7 +4109,7 @@ var generatedTools = [
3739
4109
  pools: {
3740
4110
  type: "array",
3741
4111
  minItems: 1,
3742
- 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',
4112
+ 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.",
3743
4113
  items: {
3744
4114
  type: "string"
3745
4115
  },
@@ -3748,7 +4118,7 @@ var generatedTools = [
3748
4118
  excludedTypes: {
3749
4119
  type: "array",
3750
4120
  uniqueItems: true,
3751
- 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',
4121
+ 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.",
3752
4122
  items: {
3753
4123
  type: "string",
3754
4124
  pattern: "^\\s*[^-\\s]"
@@ -3765,7 +4135,6 @@ var generatedTools = [
3765
4135
  }
3766
4136
  },
3767
4137
  ContainerConfig: {
3768
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
3769
4138
  allOf: [
3770
4139
  {
3771
4140
  $ref: "#/$defs/BaseContainerConfig"
@@ -3790,8 +4159,42 @@ var generatedTools = [
3790
4159
  ],
3791
4160
  unevaluatedProperties: false
3792
4161
  },
4162
+ EndpointMount: {
4163
+ type: "object",
4164
+ required: ["volumeId"],
4165
+ additionalProperties: false,
4166
+ description: "A volume mounted on every worker, always at `/runpod-volume`. Custom paths are not supported yet.",
4167
+ properties: {
4168
+ volumeId: {
4169
+ type: "string",
4170
+ description: "ID of a NetworkVolume (under `network`) or GlobalVolume (under `global`).",
4171
+ examples: ["vol_abc"]
4172
+ }
4173
+ }
4174
+ },
4175
+ EndpointMountsInput: {
4176
+ type: "object",
4177
+ additionalProperties: false,
4178
+ 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.",
4179
+ properties: {
4180
+ network: {
4181
+ type: "array",
4182
+ description: "Network volumes, at most one per data center.",
4183
+ items: {
4184
+ $ref: "#/$defs/EndpointMount"
4185
+ }
4186
+ },
4187
+ global: {
4188
+ type: "array",
4189
+ maxItems: 1,
4190
+ items: {
4191
+ $ref: "#/$defs/EndpointMount"
4192
+ }
4193
+ }
4194
+ }
4195
+ },
3793
4196
  EndpointScaling: {
3794
- 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",
4197
+ 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.",
3795
4198
  oneOf: [
3796
4199
  {
3797
4200
  $ref: "#/$defs/QueueDelayScaling"
@@ -3828,14 +4231,14 @@ var generatedTools = [
3828
4231
  type: "integer",
3829
4232
  minimum: 1,
3830
4233
  maximum: 3600,
3831
- 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",
4234
+ 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.",
3832
4235
  examples: [5]
3833
4236
  }
3834
4237
  }
3835
4238
  },
3836
4239
  FlashBoot: {
3837
4240
  type: "string",
3838
- description: "FlashBoot cold-start acceleration mode.\n- `OFF` \u2014 disabled\n- `FLASHBOOT` \u2014 enabled\n- `PRIORITY_FLASHBOOT` \u2014 enabled with priority capacity\n",
4241
+ description: "FlashBoot cold-start acceleration mode. `OFF` \u2014 disabled. `FLASHBOOT` \u2014 enabled. `PRIORITY_FLASHBOOT` \u2014 enabled with priority capacity",
3839
4242
  enum: ["OFF", "FLASHBOOT", "PRIORITY_FLASHBOOT"]
3840
4243
  },
3841
4244
  QueueDelayScaling: {
@@ -3861,7 +4264,7 @@ var generatedTools = [
3861
4264
  RequestCountScaling: {
3862
4265
  type: "object",
3863
4266
  additionalProperties: false,
3864
- description: "Scale on concurrent in-flight requests per worker. Required for\nload-balancing endpoints; also selectable for queue-based.\n",
4267
+ description: "Scale on concurrent in-flight requests per worker. Required for load-balancing endpoints; also selectable for queue-based.",
3865
4268
  required: ["type", "requestCount"],
3866
4269
  properties: {
3867
4270
  type: {
@@ -3878,7 +4281,7 @@ var generatedTools = [
3878
4281
  }
3879
4282
  },
3880
4283
  UpdateEndpointGpuConfig: {
3881
- 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",
4284
+ 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.",
3882
4285
  allOf: [
3883
4286
  {
3884
4287
  $ref: "#/$defs/BaseEndpointGpuConfig"
@@ -3895,12 +4298,12 @@ var generatedTools = [
3895
4298
  type: "string",
3896
4299
  pattern: "^\\d+\\.\\d+$"
3897
4300
  },
3898
- 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"
4301
+ 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."
3899
4302
  },
3900
4303
  minCudaVersion: {
3901
4304
  type: "string",
3902
4305
  pattern: "^$|^\\d+\\.\\d+$",
3903
- 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',
4306
+ 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.',
3904
4307
  examples: ["12.1"]
3905
4308
  }
3906
4309
  }
@@ -3921,7 +4324,7 @@ var generatedTools = [
3921
4324
  type: "array",
3922
4325
  minItems: 1,
3923
4326
  uniqueItems: true,
3924
- description: "Complete replacement CPU selection. Valid only for an existing CPU\nendpoint; endpoint compute family cannot be changed.\n",
4327
+ description: "Complete replacement CPU selection. Valid only for an existing CPU endpoint; endpoint compute family cannot be changed.",
3925
4328
  items: {
3926
4329
  $ref: "#/$defs/CreateCpuConfig"
3927
4330
  }
@@ -3947,14 +4350,19 @@ var generatedTools = [
3947
4350
  type: "array",
3948
4351
  items: {
3949
4352
  type: "string"
3950
- }
4353
+ },
4354
+ description: "Network volume IDs. Pass `[]` to detach.",
4355
+ deprecated: true
4356
+ },
4357
+ mounts: {
4358
+ $ref: "#/$defs/EndpointMountsInput"
3951
4359
  },
3952
4360
  scaling: {
3953
4361
  $ref: "#/$defs/EndpointScaling"
3954
4362
  },
3955
4363
  templateId: {
3956
4364
  type: "string",
3957
- 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",
4365
+ 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.",
3958
4366
  examples: ["30zmvf89kd"]
3959
4367
  },
3960
4368
  timeout: {
@@ -3971,6 +4379,57 @@ var generatedTools = [
3971
4379
  }
3972
4380
  }
3973
4381
  },
4382
+ {
4383
+ name: "update-global-volume",
4384
+ operationId: "updateGlobalVolume",
4385
+ description: "Update a global volume. Updates mutable fields on a global volume. Only provided fields are changed.",
4386
+ method: "PATCH",
4387
+ path: "/v2/global-volumes/{id}",
4388
+ params: [
4389
+ {
4390
+ name: "id",
4391
+ location: "path"
4392
+ }
4393
+ ],
4394
+ hasBody: true,
4395
+ annotations: {
4396
+ readOnlyHint: false,
4397
+ destructiveHint: false,
4398
+ idempotentHint: false,
4399
+ openWorldHint: true
4400
+ },
4401
+ inputSchema: {
4402
+ type: "object",
4403
+ properties: {
4404
+ id: {
4405
+ type: "string",
4406
+ description: "Global volume identifier"
4407
+ },
4408
+ body: {
4409
+ $ref: "#/$defs/UpdateGlobalVolumeRequest"
4410
+ }
4411
+ },
4412
+ required: ["id", "body"],
4413
+ $defs: {
4414
+ UpdateGlobalVolumeRequest: {
4415
+ type: "object",
4416
+ additionalProperties: false,
4417
+ minProperties: 1,
4418
+ description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
4419
+ properties: {
4420
+ name: {
4421
+ type: "string",
4422
+ minLength: 1,
4423
+ maxLength: 191,
4424
+ pattern: "[^\\p{Z}\\t-\\r\x85]",
4425
+ description: "New human-readable name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed.",
4426
+ examples: ["renamed-volume"]
4427
+ }
4428
+ }
4429
+ }
4430
+ }
4431
+ }
4432
+ },
3974
4433
  {
3975
4434
  name: "update-network-volume",
3976
4435
  operationId: "updateNetworkVolume",
@@ -4007,7 +4466,7 @@ var generatedTools = [
4007
4466
  type: "object",
4008
4467
  additionalProperties: false,
4009
4468
  minProperties: 1,
4010
- description: "Only the provided fields are updated. At least one field must be\npresent; an empty body is rejected.\n",
4469
+ description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
4011
4470
  properties: {
4012
4471
  name: {
4013
4472
  type: "string",
@@ -4019,7 +4478,7 @@ var generatedTools = [
4019
4478
  type: "integer",
4020
4479
  minimum: 10,
4021
4480
  maximum: 4096,
4022
- description: "New size in GB. Must be greater than or equal to the current size \u2014\nnetwork volume storage cannot be reduced.\n",
4481
+ description: "New size in GB. Must be greater than or equal to the current size \u2014 network volume storage cannot be reduced.",
4023
4482
  examples: [100]
4024
4483
  }
4025
4484
  }
@@ -4046,6 +4505,7 @@ var generatedTools = [
4046
4505
  idempotentHint: false,
4047
4506
  openWorldHint: true
4048
4507
  },
4508
+ schemaOnDemand: true,
4049
4509
  inputSchema: {
4050
4510
  type: "object",
4051
4511
  properties: {
@@ -4061,11 +4521,10 @@ var generatedTools = [
4061
4521
  $defs: {
4062
4522
  BaseContainerConfig: {
4063
4523
  type: "object",
4064
- 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",
4065
4524
  properties: {
4066
4525
  args: {
4067
4526
  type: "string",
4068
- 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',
4527
+ 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.',
4069
4528
  examples: [
4070
4529
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
4071
4530
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -4077,7 +4536,7 @@ var generatedTools = [
4077
4536
  items: {
4078
4537
  type: "string"
4079
4538
  },
4080
- 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",
4539
+ 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.",
4081
4540
  examples: [
4082
4541
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
4083
4542
  ]
@@ -4087,7 +4546,7 @@ var generatedTools = [
4087
4546
  items: {
4088
4547
  type: "string"
4089
4548
  },
4090
- 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",
4549
+ 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.",
4091
4550
  examples: [["/bin/bash", "-c"]]
4092
4551
  },
4093
4552
  disk: {
@@ -4123,8 +4582,22 @@ var generatedTools = [
4123
4582
  }
4124
4583
  }
4125
4584
  },
4585
+ BaseMounts: {
4586
+ type: "object",
4587
+ properties: {
4588
+ persistent: {
4589
+ $ref: "#/$defs/PersistentMount"
4590
+ },
4591
+ network: {
4592
+ type: "array",
4593
+ maxItems: 1,
4594
+ items: {
4595
+ $ref: "#/$defs/NetworkMount"
4596
+ }
4597
+ }
4598
+ }
4599
+ },
4126
4600
  ContainerConfig: {
4127
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
4128
4601
  allOf: [
4129
4602
  {
4130
4603
  $ref: "#/$defs/BaseContainerConfig"
@@ -4141,28 +4614,51 @@ var generatedTools = [
4141
4614
  }
4142
4615
  ]
4143
4616
  },
4144
- Mounts: {
4617
+ GlobalMount: {
4145
4618
  type: "object",
4619
+ required: ["volumeId"],
4146
4620
  additionalProperties: false,
4147
- 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",
4621
+ 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.",
4148
4622
  properties: {
4149
- persistent: {
4150
- $ref: "#/$defs/PersistentMount"
4623
+ volumeId: {
4624
+ type: "string",
4625
+ description: "ID of an existing GlobalVolume.",
4626
+ examples: ["gsb_abc"]
4151
4627
  },
4152
- network: {
4153
- type: "array",
4154
- maxItems: 1,
4155
- items: {
4156
- $ref: "#/$defs/NetworkMount"
4157
- }
4628
+ path: {
4629
+ type: "string",
4630
+ 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.",
4631
+ examples: ["/data"]
4158
4632
  }
4159
4633
  }
4160
4634
  },
4635
+ Mounts: {
4636
+ 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.",
4637
+ allOf: [
4638
+ {
4639
+ $ref: "#/$defs/BaseMounts"
4640
+ },
4641
+ {
4642
+ type: "object",
4643
+ properties: {
4644
+ global: {
4645
+ type: "array",
4646
+ maxItems: 1,
4647
+ 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.",
4648
+ items: {
4649
+ $ref: "#/$defs/GlobalMount"
4650
+ }
4651
+ }
4652
+ }
4653
+ }
4654
+ ],
4655
+ unevaluatedProperties: false
4656
+ },
4161
4657
  NetworkMount: {
4162
4658
  type: "object",
4163
4659
  required: ["volumeId", "path"],
4164
4660
  additionalProperties: false,
4165
- 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",
4661
+ 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.",
4166
4662
  properties: {
4167
4663
  volumeId: {
4168
4664
  type: "string",
@@ -4180,7 +4676,7 @@ var generatedTools = [
4180
4676
  type: "object",
4181
4677
  required: ["size", "path"],
4182
4678
  additionalProperties: false,
4183
- 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",
4679
+ 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.",
4184
4680
  properties: {
4185
4681
  size: {
4186
4682
  type: "integer",
@@ -4205,7 +4701,7 @@ var generatedTools = [
4205
4701
  properties: {
4206
4702
  globalNetworking: {
4207
4703
  type: "boolean",
4208
- 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."
4704
+ 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)."
4209
4705
  },
4210
4706
  locked: {
4211
4707
  type: "boolean",
@@ -4220,7 +4716,7 @@ var generatedTools = [
4220
4716
  },
4221
4717
  templateId: {
4222
4718
  type: "string",
4223
- 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",
4719
+ 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.",
4224
4720
  examples: ["30zmvf89kd"]
4225
4721
  }
4226
4722
  }
@@ -4250,6 +4746,7 @@ var generatedTools = [
4250
4746
  idempotentHint: false,
4251
4747
  openWorldHint: true
4252
4748
  },
4749
+ schemaOnDemand: true,
4253
4750
  inputSchema: {
4254
4751
  type: "object",
4255
4752
  properties: {
@@ -4267,7 +4764,7 @@ var generatedTools = [
4267
4764
  type: "object",
4268
4765
  additionalProperties: false,
4269
4766
  minProperties: 1,
4270
- description: "Only the provided fields are updated; at least one field is required.\nThe secret's `name` is immutable.\n",
4767
+ description: "Only the provided fields are updated; at least one field is required. The secret's `name` is immutable.",
4271
4768
  properties: {
4272
4769
  value: {
4273
4770
  type: "string",
@@ -4319,7 +4816,7 @@ var generatedTools = [
4319
4816
  type: "string",
4320
4817
  pattern: "^(ssh|ecdsa|sk)-[^\\s]+ [^\\s]+([ \\t][^\\n\\r]*)?$"
4321
4818
  },
4322
- 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",
4819
+ 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.",
4323
4820
  examples: [
4324
4821
  [
4325
4822
  "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILXGDN/SclOozk1xsDztpmhGiKkkrfQB9SKoO8dSIQQZ me@example.com"
@@ -4350,6 +4847,7 @@ var generatedTools = [
4350
4847
  idempotentHint: false,
4351
4848
  openWorldHint: true
4352
4849
  },
4850
+ schemaOnDemand: true,
4353
4851
  inputSchema: {
4354
4852
  type: "object",
4355
4853
  properties: {
@@ -4364,11 +4862,10 @@ var generatedTools = [
4364
4862
  $defs: {
4365
4863
  BaseContainerConfig: {
4366
4864
  type: "object",
4367
- 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",
4368
4865
  properties: {
4369
4866
  args: {
4370
4867
  type: "string",
4371
- 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',
4868
+ 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.',
4372
4869
  examples: [
4373
4870
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
4374
4871
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -4380,7 +4877,7 @@ var generatedTools = [
4380
4877
  items: {
4381
4878
  type: "string"
4382
4879
  },
4383
- 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",
4880
+ 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.",
4384
4881
  examples: [
4385
4882
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
4386
4883
  ]
@@ -4390,7 +4887,7 @@ var generatedTools = [
4390
4887
  items: {
4391
4888
  type: "string"
4392
4889
  },
4393
- 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",
4890
+ 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.",
4394
4891
  examples: [["/bin/bash", "-c"]]
4395
4892
  },
4396
4893
  disk: {
@@ -4427,7 +4924,6 @@ var generatedTools = [
4427
4924
  }
4428
4925
  },
4429
4926
  ContainerConfig: {
4430
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
4431
4927
  allOf: [
4432
4928
  {
4433
4929
  $ref: "#/$defs/BaseContainerConfig"
@@ -4448,7 +4944,7 @@ var generatedTools = [
4448
4944
  type: "object",
4449
4945
  required: ["size", "path"],
4450
4946
  additionalProperties: false,
4451
- 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",
4947
+ 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.",
4452
4948
  properties: {
4453
4949
  size: {
4454
4950
  type: "integer",
@@ -4465,13 +4961,13 @@ var generatedTools = [
4465
4961
  },
4466
4962
  TemplateCategory: {
4467
4963
  type: "string",
4468
- 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",
4964
+ 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",
4469
4965
  enum: ["CPU", "NVIDIA", "AMD"]
4470
4966
  },
4471
4967
  TemplateMounts: {
4472
4968
  type: "object",
4473
4969
  additionalProperties: false,
4474
- 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",
4970
+ 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.",
4475
4971
  properties: {
4476
4972
  persistent: {
4477
4973
  $ref: "#/$defs/PersistentMount"
@@ -6033,7 +6529,7 @@ function authedGraphqlBase(env) {
6033
6529
  return pick(env.RUNPOD_AUTHED_GRAPHQL_URL, "https://api.runpod.io/graphql");
6034
6530
  }
6035
6531
 
6036
- // node_modules/.pnpm/@runpod+typescript-api-sdk@0.2.0/node_modules/@runpod/typescript-api-sdk/dist/index.js
6532
+ // node_modules/.pnpm/@runpod+typescript-api-sdk@0.3.0/node_modules/@runpod/typescript-api-sdk/dist/index.js
6037
6533
  var PATH_PARAM_RE = /\{[^{}]+\}/g;
6038
6534
  var supportsRequestInitExt = () => {
6039
6535
  return typeof process === "object" && Number.parseInt(process?.versions?.node?.substring(0, 2)) >= 18 && process.versions.undici;
@@ -6930,9 +7426,9 @@ function isDigit(char) {
6930
7426
  return false;
6931
7427
  return isDigitRegex.test(char);
6932
7428
  }
6933
- function parseRetryAfter(header, now = Date.now()) {
6934
- if (header === null) return void 0;
6935
- const value = header.trim();
7429
+ function parseRetryAfter(header2, now = Date.now()) {
7430
+ if (header2 === null) return void 0;
7431
+ const value = header2.trim();
6936
7432
  if (/^\d+$/.test(value)) {
6937
7433
  const ms = Number(value) * 1e3;
6938
7434
  return Number.isSafeInteger(ms) ? ms : void 0;
@@ -7721,6 +8217,7 @@ var source = {
7721
8217
  var listGuides = {
7722
8218
  name: "list-guides",
7723
8219
  annotations: bundledRead,
8220
+ recordedArguments: ["kind", "query", "concept"],
7724
8221
  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.",
7725
8222
  inputSchema: {
7726
8223
  type: "object",
@@ -7756,6 +8253,8 @@ var listGuides = {
7756
8253
  var readGuide = {
7757
8254
  name: "read-guide",
7758
8255
  annotations: bundledRead,
8256
+ recordedArguments: ["id"],
8257
+ recordedGuideKind: (args) => knowledge.guides.find((guide) => guide.id === String(args.id).trim())?.kind,
7759
8258
  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").',
7760
8259
  inputSchema: {
7761
8260
  type: "object",
@@ -7793,6 +8292,7 @@ var readGuide = {
7793
8292
  var lookupConcept = {
7794
8293
  name: "lookup-concept",
7795
8294
  annotations: bundledRead,
8295
+ recordedArguments: ["concept"],
7796
8296
  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".',
7797
8297
  inputSchema: {
7798
8298
  type: "object",
@@ -7845,6 +8345,7 @@ var lookupConcept = {
7845
8345
  var searchConcepts = {
7846
8346
  name: "search-concepts",
7847
8347
  annotations: bundledRead,
8348
+ recordedArguments: ["query", "limit"],
7848
8349
  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.',
7849
8350
  inputSchema: {
7850
8351
  type: "object",
@@ -7909,9 +8410,143 @@ var guideResources = knowledge.guides.map((guide) => ({
7909
8410
  text: guide.body
7910
8411
  }));
7911
8412
 
8413
+ // src/specgen/prose.ts
8414
+ var ABBREVIATIONS = ["e.g.", "i.e.", "etc.", "vs."];
8415
+ var DOT = "\0";
8416
+ function splitSentences(text) {
8417
+ let guarded = text;
8418
+ for (const abbreviation of ABBREVIATIONS) {
8419
+ guarded = guarded.replaceAll(
8420
+ abbreviation,
8421
+ abbreviation.replaceAll(".", DOT)
8422
+ );
8423
+ }
8424
+ return guarded.split(/(?<=[.!?])\s+(?=[A-Z`"'([])/).map((sentence) => sentence.replaceAll(DOT, ".").trim()).filter(Boolean);
8425
+ }
8426
+ function leadSentence(text) {
8427
+ return splitSentences(text)[0] ?? text;
8428
+ }
8429
+
8430
+ // src/specgen/on-demand.ts
8431
+ var DESCRIBE_TOOL_NAME = "describe-tool";
8432
+ var ON_DEMAND_POINTER = ` Call ${DESCRIBE_TOOL_NAME} for the full argument schema before the first use.`;
8433
+ function listedAnnotations(annotations) {
8434
+ const { idempotentHint, ...listed } = annotations;
8435
+ return idempotentHint ? annotations : listed;
8436
+ }
8437
+ function listTool(tool) {
8438
+ const { name, description, inputSchema } = tool;
8439
+ const annotations = listedAnnotations(tool.annotations);
8440
+ if (!tool.schemaOnDemand)
8441
+ return { name, description, inputSchema, annotations };
8442
+ return {
8443
+ name,
8444
+ description: description + ON_DEMAND_POINTER,
8445
+ inputSchema: callShape(inputSchema),
8446
+ annotations
8447
+ };
8448
+ }
8449
+ function callShape(schema) {
8450
+ const defs = schema.$defs ?? {};
8451
+ const resolve = (node) => {
8452
+ if (typeof node.$ref !== "string") return node;
8453
+ const name = node.$ref.split("/").pop() ?? "";
8454
+ return defs[name] ?? {};
8455
+ };
8456
+ const properties = Object.fromEntries(
8457
+ Object.entries(schema.properties ?? {}).map(
8458
+ ([key, value]) => [key, shallowProperty(resolve(value), resolve)]
8459
+ )
8460
+ );
8461
+ return {
8462
+ type: "object",
8463
+ properties,
8464
+ ...schema.required ? { required: schema.required } : {}
8465
+ };
8466
+ }
8467
+ function shallowProperty(node, resolve) {
8468
+ const out = {};
8469
+ const type = schemaType(node, resolve);
8470
+ if (type !== void 0) out.type = type;
8471
+ if (type === "array") {
8472
+ const items = resolve(node.items ?? {});
8473
+ const itemType = schemaType(items, resolve);
8474
+ if (itemType !== void 0) out.items = { type: itemType };
8475
+ }
8476
+ if (Array.isArray(node.enum)) out.enum = node.enum;
8477
+ if (typeof node.description === "string")
8478
+ out.description = leadSentence(node.description);
8479
+ return out;
8480
+ }
8481
+ function schemaType(node, resolve) {
8482
+ if (node.type !== void 0) return node.type;
8483
+ if (node.properties || node.allOf) return "object";
8484
+ const branches2 = node.anyOf ?? node.oneOf;
8485
+ const allObjects = branches2?.every(
8486
+ (branch) => schemaType(resolve(branch), resolve) === "object"
8487
+ ) ?? false;
8488
+ return allObjects ? "object" : void 0;
8489
+ }
8490
+
8491
+ // src/specgen/tools/describe.ts
8492
+ function createDescribeTool(served) {
8493
+ return {
8494
+ name: DESCRIBE_TOOL_NAME,
8495
+ 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.",
8496
+ inputSchema: {
8497
+ type: "object",
8498
+ properties: {
8499
+ name: {
8500
+ type: "string",
8501
+ description: "A tool name exactly as this server's tool list spells it."
8502
+ }
8503
+ },
8504
+ required: ["name"]
8505
+ },
8506
+ annotations: {
8507
+ readOnlyHint: true,
8508
+ destructiveHint: false,
8509
+ idempotentHint: true,
8510
+ openWorldHint: false
8511
+ },
8512
+ handler: async (_ctx, args) => {
8513
+ const name = String(args.name ?? "");
8514
+ const tools = served();
8515
+ const tool = tools.find((candidate) => candidate.name === name);
8516
+ if (!tool) {
8517
+ return {
8518
+ ok: false,
8519
+ status: 404,
8520
+ payload: {
8521
+ error: `No tool named ${name}.`,
8522
+ suggestions: similarNames(
8523
+ name,
8524
+ tools.map((candidate) => candidate.name)
8525
+ ),
8526
+ hint: "Tool names are exactly as this server lists them; the suggestions share a word with the name you sent."
8527
+ }
8528
+ };
8529
+ }
8530
+ const { description, inputSchema, annotations } = tool;
8531
+ return ok({ name: tool.name, description, inputSchema, annotations });
8532
+ }
8533
+ };
8534
+ }
8535
+ function similarNames(query, names) {
8536
+ const wanted = new Set(words2(query));
8537
+ return names.map((name) => {
8538
+ const own = new Set(words2(name));
8539
+ const shared = [...own].filter((word) => wanted.has(word)).length;
8540
+ return { name, score: shared / (/* @__PURE__ */ new Set([...own, ...wanted])).size };
8541
+ }).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || a.name.localeCompare(b.name)).slice(0, 8).map((entry) => entry.name);
8542
+ }
8543
+ function words2(name) {
8544
+ return name.toLowerCase().split(/[-_\s]+/).filter(Boolean).map((word) => word.replace(/s$/, ""));
8545
+ }
8546
+
7912
8547
  // src/server.ts
7913
8548
  var SERVER_NAME = "Runpod API Server";
7914
- var SERVER_VERSION = true ? "4.2.0" : "dev";
8549
+ var SERVER_VERSION = true ? "4.3.0" : "dev";
7915
8550
 
7916
8551
  // src/specgen/analytics.ts
7917
8552
  var import_node_crypto = require("crypto");
@@ -7927,7 +8562,22 @@ function analyticsCallerId(apiKey, accountId) {
7927
8562
  const prefix = accountId ? "a:" : "k:";
7928
8563
  return prefix + (0, import_node_crypto.createHmac)("sha256", salt).update(identity).digest("hex").slice(0, 16);
7929
8564
  }
7930
- function captureToolCall(apiKey, event) {
8565
+ var MAX_RECORDED_VALUE_LENGTH = 200;
8566
+ function bounded(value) {
8567
+ return value.slice(0, MAX_RECORDED_VALUE_LENGTH);
8568
+ }
8569
+ function recordedArgumentValues(names, args) {
8570
+ if (!names?.length) return void 0;
8571
+ const values = {};
8572
+ for (const name of names) {
8573
+ const value = args[name];
8574
+ if (typeof value === "string" || typeof value === "number") {
8575
+ values[name] = bounded(String(value).trim());
8576
+ }
8577
+ }
8578
+ return Object.keys(values).length ? values : void 0;
8579
+ }
8580
+ function capture(event, distinctId, properties) {
7931
8581
  const posthogKey = process.env.POSTHOG_API_KEY;
7932
8582
  if (!posthogKey) return;
7933
8583
  void fetch(`${posthogHost()}/capture/`, {
@@ -7935,18 +8585,12 @@ function captureToolCall(apiKey, event) {
7935
8585
  headers: { "Content-Type": "application/json" },
7936
8586
  body: JSON.stringify({
7937
8587
  api_key: posthogKey,
7938
- event: "runpod_mcp_tool_call",
7939
- distinct_id: analyticsCallerId(apiKey, event.accountId),
8588
+ event,
8589
+ distinct_id: distinctId,
7940
8590
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7941
8591
  properties: {
7942
- tool: event.tool,
7943
- ok: event.ok,
7944
- status: event.status,
7945
- duration_ms: event.durationMs,
7946
- transport: event.transport,
7947
- server_version: event.serverVersion,
8592
+ ...properties,
7948
8593
  surface_version: SURFACE_VERSION,
7949
- ...event.clientName ? { client_name: event.clientName } : {},
7950
8594
  $process_person_profile: false
7951
8595
  }
7952
8596
  }),
@@ -7954,6 +8598,63 @@ function captureToolCall(apiKey, event) {
7954
8598
  }).catch(() => {
7955
8599
  });
7956
8600
  }
8601
+ function captureToolCall(apiKey, event) {
8602
+ const args = Object.fromEntries(
8603
+ Object.entries(event.args ?? {}).map(([name, value]) => [
8604
+ `arg_${name}`,
8605
+ value
8606
+ ])
8607
+ );
8608
+ capture("runpod_mcp_tool_call", analyticsCallerId(apiKey, event.accountId), {
8609
+ tool: event.tool,
8610
+ ok: event.ok,
8611
+ status: event.status,
8612
+ duration_ms: event.durationMs,
8613
+ transport: event.transport,
8614
+ server_version: event.serverVersion,
8615
+ ...event.clientName ? { client_name: event.clientName } : {},
8616
+ ...event.guideKind ? { guide_kind: event.guideKind } : {},
8617
+ ...event.blockedBy ? { blocked_by: event.blockedBy } : {},
8618
+ ...args
8619
+ });
8620
+ }
8621
+ function captureLoopBlocked(apiKey, event) {
8622
+ capture(
8623
+ "runpod_mcp_loop_blocked",
8624
+ analyticsCallerId(apiKey, event.accountId),
8625
+ {
8626
+ tool: event.tool,
8627
+ blocked_for_s: event.blockedForS,
8628
+ transport: event.transport,
8629
+ server_version: event.serverVersion,
8630
+ ...event.clientName ? { client_name: event.clientName } : {}
8631
+ }
8632
+ );
8633
+ }
8634
+ function captureResourceCall(apiKey, event) {
8635
+ capture(
8636
+ "runpod_mcp_resource_call",
8637
+ analyticsCallerId(apiKey, event.accountId),
8638
+ {
8639
+ method: event.method,
8640
+ uri: bounded(event.uri),
8641
+ ok: event.ok,
8642
+ transport: event.transport,
8643
+ server_version: event.serverVersion,
8644
+ ...event.clientName ? { client_name: event.clientName } : {}
8645
+ }
8646
+ );
8647
+ }
8648
+
8649
+ // src/alp/ingest.ts
8650
+ function isSinkUrl(value) {
8651
+ try {
8652
+ const url = new URL(value);
8653
+ return url.protocol === "https:" && url.hostname.endsWith(".convex.site") && url.pathname === "/alp/submit";
8654
+ } catch {
8655
+ return false;
8656
+ }
8657
+ }
7957
8658
 
7958
8659
  // src/alp/read.ts
7959
8660
  var DEFAULT_JOURNAL_LIMIT = 20;
@@ -8215,14 +8916,21 @@ PLAYBOOKS \u2014 READ BEFORE ACTING. Each Runpod task has a journey playbook car
8215
8916
 
8216
8917
  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.
8217
8918
 
8218
- 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.`;
8919
+ 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.`;
8219
8920
  var ALP_INSTRUCTIONS = `
8220
8921
 
8221
8922
  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.`;
8222
8923
  function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8223
8924
  const rateLimiter = opts.rateLimiter ?? noopRateLimiter;
8224
8925
  const caller = callerId(ctx.apiKey);
8225
- const servedCuratedTools = opts.alp ? [...curatedTools, ...createAlpTools(opts.alp)] : curatedTools;
8926
+ const configuredCuratedTools = opts.alp ? [...curatedTools, ...createAlpTools(opts.alp)] : curatedTools;
8927
+ const servedCuratedTools = [
8928
+ ...configuredCuratedTools,
8929
+ createDescribeTool(() => [...servedCuratedTools, ...generatedTools])
8930
+ ];
8931
+ const callShapeOnlyTools = new Set(
8932
+ [...servedCuratedTools, ...generatedTools].filter((tool) => tool.schemaOnDemand).map((tool) => tool.name)
8933
+ );
8226
8934
  const server = new import_server.Server(
8227
8935
  { name: SERVER_NAME, version: `${version} [specgen]` },
8228
8936
  {
@@ -8241,24 +8949,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8241
8949
  }
8242
8950
  };
8243
8951
  server.setRequestHandler(import_types.ListToolsRequestSchema, async () => ({
8244
- tools: [
8245
- ...servedCuratedTools.map(
8246
- ({ name, description, inputSchema, annotations }) => ({
8247
- name,
8248
- description,
8249
- inputSchema,
8250
- annotations
8251
- })
8252
- ),
8253
- ...generatedTools.map(
8254
- ({ name, description, inputSchema, annotations }) => ({
8255
- name,
8256
- description,
8257
- inputSchema,
8258
- annotations
8259
- })
8260
- )
8261
- ]
8952
+ tools: [...servedCuratedTools, ...generatedTools].map(listTool)
8262
8953
  }));
8263
8954
  server.setRequestHandler(import_types.ListResourcesRequestSchema, async () => ({
8264
8955
  resources: guideResources.map(({ text: _text, ...resource }) => resource)
@@ -8270,6 +8961,14 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8270
8961
  const guide = guideResources.find(
8271
8962
  (candidate) => candidate.uri === request.params.uri
8272
8963
  );
8964
+ if (opts.analytics) {
8965
+ captureResourceCall(ctx.apiKey, {
8966
+ method: "resources/read",
8967
+ uri: guide ? guide.uri : "unknown",
8968
+ ok: Boolean(guide),
8969
+ ...opts.analytics
8970
+ });
8971
+ }
8273
8972
  if (!guide) {
8274
8973
  throw new Error(
8275
8974
  `Unknown resource ${request.params.uri}. Resources are the plugin guides under ${GUIDE_URI_PREFIX}; call list-guides for the guide ids.`
@@ -8292,6 +8991,16 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8292
8991
  status: 429,
8293
8992
  durationMs: 0
8294
8993
  });
8994
+ if (opts.analytics) {
8995
+ captureToolCall(ctx.apiKey, {
8996
+ tool: request.params.name,
8997
+ ok: false,
8998
+ status: 429,
8999
+ durationMs: 0,
9000
+ ...verdict.reason ? { blockedBy: verdict.reason } : {},
9001
+ ...opts.analytics
9002
+ });
9003
+ }
8295
9004
  return {
8296
9005
  content: [
8297
9006
  {
@@ -8329,11 +9038,14 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8329
9038
  ok: result.ok,
8330
9039
  status: result.status,
8331
9040
  durationMs: Date.now() - startedAt,
9041
+ args: recordedArgumentValues(curated?.recordedArguments, args),
9042
+ guideKind: curated?.recordedGuideKind?.(args),
8332
9043
  ...opts.analytics
8333
9044
  });
8334
9045
  }
9046
+ opts.onToolResult?.(request.params.name, result.status);
8335
9047
  if (!result.ok && result.status === 401) opts.onUnauthorized?.();
8336
- const payload = result.ok ? result.payload : withRecoveryHint(result);
9048
+ const payload = result.ok ? result.payload : withRecoveryHint(result, callShapeOnlyTools.has(request.params.name));
8337
9049
  return {
8338
9050
  content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
8339
9051
  isError: !result.ok
@@ -8351,9 +9063,29 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
8351
9063
  }
8352
9064
  return server;
8353
9065
  }
8354
- function errorHint(status) {
8355
- if (status === 400 || status === 422)
8356
- 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.";
9066
+ 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.";
9067
+ var DETAIL_HINTS = [
9068
+ [
9069
+ /no longer any instances available|not enough free GPUs/i,
9070
+ "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."
9071
+ ],
9072
+ [
9073
+ /at least \$\d|insufficient (?:funds|balance)|\bbalance\b/i,
9074
+ "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."
9075
+ ],
9076
+ [
9077
+ /mutually exclusive|cannot be combined with/i,
9078
+ "Two arguments conflict: send only one of them and leave the other out entirely. Do not change anything else."
9079
+ ]
9080
+ ];
9081
+ function errorHint(status, detail) {
9082
+ if (status === 400 && detail) {
9083
+ for (const [pattern, hint] of DETAIL_HINTS)
9084
+ if (pattern.test(detail)) return hint;
9085
+ if (/^unauthorized$/i.test(detail.trim()))
9086
+ 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.";
9087
+ }
9088
+ if (status === 400 || status === 422) return SHAPE_HINT;
8357
9089
  if (status === 401) return "The Runpod API key is missing or invalid.";
8358
9090
  if (status === 402)
8359
9091
  return "Check your account balance in the Runpod console before retrying billable operations; list-billing shows the recent spend behind it.";
@@ -8367,13 +9099,23 @@ function errorHint(status) {
8367
9099
  return "Upstream Runpod error: retry once; if it persists, report it as an API-side failure.";
8368
9100
  return void 0;
8369
9101
  }
8370
- function withRecoveryHint(result) {
9102
+ var CALL_SHAPE_ONLY_HINT = `This tool lists its call shape only; ${DESCRIBE_TOOL_NAME} serves the full argument schema.`;
9103
+ function withRecoveryHint(result, listsCallShapeOnly = false) {
8371
9104
  const payload = asRecord(result.payload) ?? { error: result.payload };
9105
+ const hint = payload.hint ?? asRecord(payload.detail)?.hint ?? errorHint(result.status, detailText(payload));
9106
+ const shapeRejected = result.status === 400 || result.status === 422;
8372
9107
  return {
8373
9108
  ...payload,
8374
- hint: payload.hint ?? asRecord(payload.detail)?.hint ?? errorHint(result.status)
9109
+ hint: listsCallShapeOnly && shapeRejected ? [hint, CALL_SHAPE_ONLY_HINT].filter(Boolean).join(" ") : hint
8375
9110
  };
8376
9111
  }
9112
+ function detailText(payload) {
9113
+ if (Array.isArray(payload.errors) && payload.errors.length) return void 0;
9114
+ const detail = payload.detail ?? payload.error;
9115
+ if (typeof detail === "string") return detail;
9116
+ const nested = asRecord(detail);
9117
+ return typeof nested?.detail === "string" ? nested.detail : void 0;
9118
+ }
8377
9119
  function asRecord(value) {
8378
9120
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
8379
9121
  }
@@ -8539,6 +9281,58 @@ function createToolContext(options = {}) {
8539
9281
  };
8540
9282
  }
8541
9283
 
9284
+ // src/specgen/not-found-limiter.ts
9285
+ var SINK_TIMEOUT_MS = 3e3;
9286
+ var NOT_FOUND_LOOP = "not_found_loop";
9287
+ var blocked = /* @__PURE__ */ new Map();
9288
+ function notFoundSinkUrl(submitUrl) {
9289
+ if (!isSinkUrl(submitUrl)) return null;
9290
+ const url = new URL(submitUrl);
9291
+ url.pathname = "/ratelimit/not-found";
9292
+ return url.toString();
9293
+ }
9294
+ function createNotFoundLimiter(opts) {
9295
+ const fetchImpl = opts.fetchImpl ?? fetch;
9296
+ const now = opts.now ?? Date.now;
9297
+ return {
9298
+ rateLimiter: async (_caller, tool) => {
9299
+ const pair = `${opts.key}|${tool}`;
9300
+ const until = blocked.get(pair);
9301
+ if (until === void 0) return { allowed: true };
9302
+ if (until <= now()) {
9303
+ blocked.delete(pair);
9304
+ return { allowed: true };
9305
+ }
9306
+ return {
9307
+ allowed: false,
9308
+ retryAfterS: Math.ceil((until - now()) / 1e3),
9309
+ reason: NOT_FOUND_LOOP
9310
+ };
9311
+ },
9312
+ onToolResult: (tool, status) => {
9313
+ if (status !== 404) return;
9314
+ const pair = `${opts.key}|${tool}`;
9315
+ void fetchImpl(opts.sinkUrl, {
9316
+ method: "POST",
9317
+ headers: {
9318
+ "Content-Type": "application/json",
9319
+ "X-ALP-Secret": opts.secret
9320
+ },
9321
+ body: JSON.stringify({ key: pair }),
9322
+ signal: AbortSignal.timeout(SINK_TIMEOUT_MS)
9323
+ }).then((response) => response.ok ? response.json() : null).then((body) => {
9324
+ const reply = body;
9325
+ const until = reply?.blockedUntil;
9326
+ if (typeof until === "number" && until > now()) {
9327
+ blocked.set(pair, until);
9328
+ if (reply?.started === true) opts.onBlockStarted?.(tool, until);
9329
+ }
9330
+ }).catch(() => {
9331
+ });
9332
+ }
9333
+ };
9334
+ }
9335
+
8542
9336
  // src/_shared/credential-check.ts
8543
9337
  var import_node_crypto4 = require("crypto");
8544
9338
  var VALID_TTL_MS = 6e4;
@@ -8650,8 +9444,7 @@ function extractBearerToken(req) {
8650
9444
  return parts[1];
8651
9445
  }
8652
9446
  function analyticsOptedOut(req) {
8653
- const raw = req.headers["x-runpod-analytics"];
8654
- const value = (Array.isArray(raw) ? raw[0] : raw)?.trim().toLowerCase();
9447
+ const value = header(req, "x-runpod-analytics")?.trim().toLowerCase();
8655
9448
  return value === "off" || value === "0" || value === "false";
8656
9449
  }
8657
9450
  function getBaseUrl(req) {
@@ -8710,14 +9503,16 @@ function credentialCheckMayBeWrongEnvironment(env = process.env) {
8710
9503
  );
8711
9504
  return restMoved !== graphqlMoved;
8712
9505
  }
8713
- function looksLikeMcpMessage(value) {
8714
- if (typeof value !== "object" || value === null) return false;
8715
- const msg = value;
8716
- return msg.jsonrpc === "2.0" && typeof msg.method === "string";
9506
+ function parseMcpMessages(body) {
9507
+ const elements = Array.isArray(body) ? body : [body];
9508
+ return elements.flatMap((element) => {
9509
+ const parsed = import_types2.JSONRPCMessageSchema.safeParse(element);
9510
+ return parsed.success ? [parsed.data] : [];
9511
+ });
8717
9512
  }
9513
+ var invokesMethod = (message) => "method" in message;
8718
9514
  function bodyIsCheckable(body) {
8719
- const messages = Array.isArray(body) ? body : [body];
8720
- return messages.some(looksLikeMcpMessage);
9515
+ return parseMcpMessages(body).some(invokesMethod);
8721
9516
  }
8722
9517
  function isJsonContentType(req) {
8723
9518
  const ct = req.headers["content-type"];
@@ -8755,17 +9550,76 @@ function earlyCredentialCheckSkip(req) {
8755
9550
  if (credentialCheckMayBeWrongEnvironment()) return "skip_env_mismatch";
8756
9551
  return null;
8757
9552
  }
9553
+ var isToolCall = (message) => import_types2.CallToolRequestSchema.safeParse(message).success;
9554
+ function prefersJsonResponse(messages) {
9555
+ return messages.length > 0 && !messages.some(isToolCall);
9556
+ }
9557
+ var DEFAULT_REQUEST_LOG_SAMPLE_RATE = 0.1;
9558
+ function requestLogSampleRate(env = process.env) {
9559
+ if (env.MCP_VERBOSE_LOGS === "true") return 1;
9560
+ const raw = env.MCP_REQUEST_LOG_SAMPLE_RATE;
9561
+ if (raw === void 0 || raw === "") return DEFAULT_REQUEST_LOG_SAMPLE_RATE;
9562
+ const parsed = Number(raw);
9563
+ if (!Number.isFinite(parsed)) return DEFAULT_REQUEST_LOG_SAMPLE_RATE;
9564
+ return Math.min(1, Math.max(0, parsed));
9565
+ }
9566
+ function summarizeRpc(messages) {
9567
+ const rpc = messages.map((message) => {
9568
+ const call = import_types2.CallToolRequestSchema.safeParse(message);
9569
+ if (call.success) return `tools/call:${call.data.params.name}`;
9570
+ return "method" in message ? message.method : "response";
9571
+ });
9572
+ const init = messages.map((message) => import_types2.InitializeRequestSchema.safeParse(message)).find((result) => result.success);
9573
+ const client = init?.success ? `${init.data.params.clientInfo.name}/${init.data.params.clientInfo.version}` : void 0;
9574
+ return { rpc: rpc.length > 0 ? rpc.join(",") : void 0, client };
9575
+ }
9576
+ function header(req, name) {
9577
+ const value = req.headers[name];
9578
+ return Array.isArray(value) ? value[0] : value;
9579
+ }
9580
+ function clientAddress(req) {
9581
+ return header(req, "x-real-ip") || header(req, "x-forwarded-for")?.split(",")[0].trim() || req.socket?.remoteAddress || void 0;
9582
+ }
9583
+ function startRequestLog(req, res, bearerToken) {
9584
+ const sampleRate = requestLogSampleRate();
9585
+ if (Math.random() >= sampleRate) return { note() {
9586
+ } };
9587
+ const startedAt = Date.now();
9588
+ let summary = {};
9589
+ const address = clientAddress(req);
9590
+ const userAgent = header(req, "user-agent");
9591
+ let emitted = false;
9592
+ const emit = () => {
9593
+ if (emitted) return;
9594
+ emitted = true;
9595
+ console.log("mcp_request", {
9596
+ method: req.method,
9597
+ url: req.url,
9598
+ status: res.writableFinished ? res.statusCode : void 0,
9599
+ durationMs: Date.now() - startedAt,
9600
+ rpc: summary.rpc,
9601
+ client: summary.client,
9602
+ ua: userAgent?.slice(0, 160),
9603
+ caller: callerId(bearerToken ?? void 0),
9604
+ ip: address ? callerId(address) : void 0,
9605
+ sampleRate
9606
+ });
9607
+ };
9608
+ res.on("finish", emit);
9609
+ res.on("close", emit);
9610
+ return {
9611
+ note(next) {
9612
+ summary = next;
9613
+ }
9614
+ };
9615
+ }
8758
9616
  function logCredentialCheck(outcome) {
8759
9617
  if (outcome === "pass" && process.env.MCP_VERBOSE_LOGS !== "true") return;
8760
9618
  console.log("credential_check", { outcome });
8761
9619
  }
8762
9620
  async function handleMcpRequest(req, res, opts = {}) {
8763
9621
  const bearerToken = extractBearerToken(req);
8764
- console.log("mcp_request", {
8765
- method: req.method,
8766
- url: req.url,
8767
- hasBearerToken: !!bearerToken
8768
- });
9622
+ const requestLog = startRequestLog(req, res, bearerToken);
8769
9623
  if (req.method === "GET") {
8770
9624
  res.writeHead(405, {
8771
9625
  Allow: "POST, DELETE",
@@ -8794,9 +9648,7 @@ async function handleMcpRequest(req, res, opts = {}) {
8794
9648
  let body = req.body;
8795
9649
  let accountId;
8796
9650
  const early = earlyCredentialCheckSkip(req);
8797
- if (early) {
8798
- logCredentialCheck(early);
8799
- } else {
9651
+ if (!early) {
8800
9652
  const read = await readJsonBody(req);
8801
9653
  if (read === BODY_TOO_LARGE) {
8802
9654
  res.writeHead(413, { "Content-Type": "application/json" });
@@ -8804,26 +9656,47 @@ async function handleMcpRequest(req, res, opts = {}) {
8804
9656
  return;
8805
9657
  }
8806
9658
  body = read;
8807
- if (!bodyIsCheckable(body)) {
8808
- logCredentialCheck("skip_not_mcp_body");
8809
- } else {
8810
- const verify = opts.verifyCredential ?? defaultCredentialChecker.verify;
8811
- const verdict = await verify(bearerToken);
8812
- if (verdict.status === "valid") accountId = verdict.accountId;
8813
- logCredentialCheck(
8814
- verdict.status === "invalid" ? "reject" : verdict.status === "valid" ? "pass" : "fail_open"
9659
+ }
9660
+ const messages = parseMcpMessages(body);
9661
+ requestLog.note(summarizeRpc(messages));
9662
+ if (early) {
9663
+ logCredentialCheck(early);
9664
+ } else if (!messages.some(invokesMethod)) {
9665
+ logCredentialCheck("skip_not_mcp_body");
9666
+ } else {
9667
+ const verify = opts.verifyCredential ?? defaultCredentialChecker.verify;
9668
+ const verdict = await verify(bearerToken);
9669
+ if (verdict.status === "valid") accountId = verdict.accountId;
9670
+ logCredentialCheck(
9671
+ verdict.status === "invalid" ? "reject" : verdict.status === "valid" ? "pass" : "fail_open"
9672
+ );
9673
+ if (verdict.status === "invalid") {
9674
+ writeUnauthorized(
9675
+ req,
9676
+ res,
9677
+ `${verdict.reason} Re-authenticate to continue.`,
9678
+ true
8815
9679
  );
8816
- if (verdict.status === "invalid") {
8817
- writeUnauthorized(
8818
- req,
8819
- res,
8820
- `${verdict.reason} Re-authenticate to continue.`,
8821
- true
8822
- );
8823
- return;
8824
- }
9680
+ return;
8825
9681
  }
8826
9682
  }
9683
+ const notFoundUrl = process.env.ALP_SINK_URL ? notFoundSinkUrl(process.env.ALP_SINK_URL) : null;
9684
+ const notFoundLimiter = bearerToken && notFoundUrl && process.env.ALP_SINK_SECRET ? createNotFoundLimiter({
9685
+ key: analyticsCallerId(bearerToken, accountId),
9686
+ sinkUrl: notFoundUrl,
9687
+ secret: process.env.ALP_SINK_SECRET,
9688
+ // One usage event per block, unless this client opted out.
9689
+ onBlockStarted: analyticsOptedOut(req) ? void 0 : (tool, blockedUntil) => captureLoopBlocked(bearerToken, {
9690
+ tool,
9691
+ blockedForS: Math.round((blockedUntil - Date.now()) / 1e3),
9692
+ transport: "http",
9693
+ serverVersion: opts.serverVersion ?? SERVER_VERSION,
9694
+ clientName: sanitizeUaToken(
9695
+ String(req.headers["user-agent"] ?? "unknown")
9696
+ ),
9697
+ accountId
9698
+ })
9699
+ }) : null;
8827
9700
  const server = createSpecgenServer(
8828
9701
  createToolContext({
8829
9702
  apiKey: bearerToken,
@@ -8864,6 +9737,10 @@ async function handleMcpRequest(req, res, opts = {}) {
8864
9737
  harnessSource: "user_agent"
8865
9738
  }
8866
9739
  } : {},
9740
+ ...notFoundLimiter ? {
9741
+ rateLimiter: notFoundLimiter.rateLimiter,
9742
+ onToolResult: notFoundLimiter.onToolResult
9743
+ } : {},
8867
9744
  // A tool call that 401s proves a cached "valid" verdict wrong before
8868
9745
  // its TTL — drop it so the next request re-checks and re-auths.
8869
9746
  onUnauthorized: () => (opts.invalidateCredential ?? defaultCredentialChecker.invalidate)(
@@ -8872,8 +9749,9 @@ async function handleMcpRequest(req, res, opts = {}) {
8872
9749
  }
8873
9750
  );
8874
9751
  const transport = new import_streamableHttp.StreamableHTTPServerTransport({
8875
- sessionIdGenerator: void 0
9752
+ sessionIdGenerator: void 0,
8876
9753
  // stateless — no session persistence
9754
+ enableJsonResponse: prefersJsonResponse(messages)
8877
9755
  });
8878
9756
  res.on("close", () => {
8879
9757
  transport.close().catch(() => {
@@ -8889,6 +9767,10 @@ async function handleMcpRequest(req, res, opts = {}) {
8889
9767
  bodyIsCheckable,
8890
9768
  defaultCredentialChecker,
8891
9769
  getBaseUrl,
8892
- handleMcpRequest
9770
+ handleMcpRequest,
9771
+ parseMcpMessages,
9772
+ prefersJsonResponse,
9773
+ requestLogSampleRate,
9774
+ summarizeRpc
8893
9775
  });
8894
9776
  //# sourceMappingURL=http.cjs.map