@runpod/mcp-server 4.2.0 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/stdio.cjs CHANGED
@@ -224,7 +224,7 @@ function jsonClient(opts) {
224
224
  (opts.detectPaths?.() ?? [path.dirname(opts.configPath())]).some(exists)
225
225
  ),
226
226
  add: (mode) => {
227
- const value = mode.kind === "local" ? localServerConfig(mode.apiKey, serverProperty) : hostedServerConfig(hostedStrategy, serverProperty, mode.url);
227
+ const value = opts.serverValue ? opts.serverValue(mode) : mode.kind === "local" ? localServerConfig(mode.apiKey, serverProperty) : hostedServerConfig(hostedStrategy, serverProperty, mode.url);
228
228
  return Promise.resolve(
229
229
  upsertJsonServer(opts.configPath(), serverProperty, value, opts.dialect)
230
230
  );
@@ -702,6 +702,31 @@ function vsCodeConfigPath() {
702
702
  "mcp.json"
703
703
  );
704
704
  }
705
+ function xdgConfigHome() {
706
+ return absoluteEnvDir(
707
+ process.env.XDG_CONFIG_HOME,
708
+ () => path.join(home, ".config")
709
+ );
710
+ }
711
+ function opencodeConfigPath() {
712
+ const dir = path.join(xdgConfigHome(), "opencode");
713
+ const jsoncPath = path.join(dir, "opencode.jsonc");
714
+ return exists(jsoncPath) ? jsoncPath : path.join(dir, "opencode.json");
715
+ }
716
+ function opencodeServerValue(mode) {
717
+ return mode.kind === "local" ? {
718
+ type: "local",
719
+ command: ["npx", "-y", NPM_PACKAGE],
720
+ environment: { RUNPOD_API_KEY: mode.apiKey },
721
+ enabled: true
722
+ } : { type: "remote", url: mode.url, enabled: true };
723
+ }
724
+ function piMcpConfigPath() {
725
+ const configured = process.env.PI_CODING_AGENT_DIR?.trim();
726
+ if (!configured) return path.join(home, ".pi", "agent", "mcp.json");
727
+ const expanded = configured === "~" ? home : configured.startsWith("~/") ? path.join(home, configured.slice(2)) : configured;
728
+ return path.join(expanded, "mcp.json");
729
+ }
705
730
  var fs, os, path, import_child_process, jsonc, SERVER_NAME2, NPM_PACKAGE, HOSTED_URL, PROJECT_BOUNDARY_MARKERS, CMD_META, KEY_PLACEHOLDER, claudeCodeClient, home, CLIENTS;
706
731
  var init_clients = __esm({
707
732
  "src/install/clients.ts"() {
@@ -770,6 +795,25 @@ var init_clients = __esm({
770
795
  dialect: "jsonc",
771
796
  configPath: vsCodeConfigPath,
772
797
  detectPaths: () => [path.dirname(vsCodeConfigPath())]
798
+ }),
799
+ jsonClient({
800
+ id: "opencode",
801
+ name: "opencode",
802
+ serverProperty: "mcp",
803
+ // Loaded a commented, trailing-comma config when run; see ConfigDialect.
804
+ dialect: "jsonc",
805
+ configPath: opencodeConfigPath,
806
+ detectPaths: () => [path.dirname(opencodeConfigPath())],
807
+ serverValue: opencodeServerValue
808
+ }),
809
+ jsonClient({
810
+ id: "pi",
811
+ name: "Pi",
812
+ // Rejected a single comment when run; see ConfigDialect.
813
+ dialect: "json",
814
+ // Native Streamable HTTP (`pi mcp add --url` writes exactly `{ url }`).
815
+ configPath: piMcpConfigPath,
816
+ detectPaths: () => [path.dirname(piMcpConfigPath())]
773
817
  })
774
818
  ];
775
819
  }
@@ -802,6 +846,68 @@ var init_verify_key = __esm({
802
846
  }
803
847
  });
804
848
 
849
+ // src/install/args.ts
850
+ function parseWizardArgs(argv, knownIds) {
851
+ const parsed = { help: false, list: false, errors: [] };
852
+ const addClients = (raw, flag) => {
853
+ if (raw === void 0 || raw.startsWith("-")) {
854
+ parsed.errors.push(`${flag} needs a value, e.g. ${flag} cursor`);
855
+ return;
856
+ }
857
+ for (const id of raw.split(",").map((s) => s.trim().toLowerCase())) {
858
+ if (!id) continue;
859
+ if (!knownIds.includes(id)) {
860
+ parsed.errors.push(
861
+ `unknown client "${id}". Supported: ${knownIds.join(", ")}`
862
+ );
863
+ continue;
864
+ }
865
+ parsed.clients ??= [];
866
+ if (!parsed.clients.includes(id)) parsed.clients.push(id);
867
+ }
868
+ };
869
+ const setMode = (mode) => {
870
+ if (parsed.mode && parsed.mode !== mode)
871
+ parsed.errors.push("--hosted and --local cannot be combined");
872
+ parsed.mode = mode;
873
+ };
874
+ for (let i = 0; i < argv.length; i++) {
875
+ const arg = argv[i];
876
+ if (arg === "-h" || arg === "--help") parsed.help = true;
877
+ else if (arg === "--list") parsed.list = true;
878
+ else if (arg === "--hosted") setMode("hosted");
879
+ else if (arg === "--local") setMode("local");
880
+ else if (arg === "--client" || arg === "-c") addClients(argv[++i], arg);
881
+ else if (arg.startsWith("--client="))
882
+ addClients(arg.slice("--client=".length), "--client");
883
+ else parsed.errors.push(`unknown option "${arg}"`);
884
+ }
885
+ return parsed;
886
+ }
887
+ function wizardUsage(knownIds) {
888
+ return [
889
+ "Usage:",
890
+ " npx @runpod/mcp-server@latest add interactive setup (needs a terminal)",
891
+ " npx @runpod/mcp-server@latest add --client <ids> no prompts: configure these clients",
892
+ " npx @runpod/mcp-server@latest remove --client <ids> no prompts: remove Runpod from them",
893
+ " npx @runpod/mcp-server@latest add --list list clients, whether each is detected, and its config file",
894
+ "",
895
+ "Options:",
896
+ ` -c, --client <ids> comma-separated or repeated: ${knownIds.join(", ")}`,
897
+ " --hosted connect to the hosted server and sign in with Runpod (OAuth); the default",
898
+ " --local run the server locally with the API key in RUNPOD_API_KEY",
899
+ " (read from the environment only, never from an argument)",
900
+ " -h, --help show this help",
901
+ "",
902
+ "Exit codes: 0 configured, 1 a client failed, 2 bad usage or no terminal for prompts."
903
+ ].join("\n");
904
+ }
905
+ var init_args = __esm({
906
+ "src/install/args.ts"() {
907
+ "use strict";
908
+ }
909
+ });
910
+
805
911
  // src/install/wizard.ts
806
912
  var wizard_exports = {};
807
913
  __export(wizard_exports, {
@@ -950,15 +1056,107 @@ async function runRemove() {
950
1056
  p.note(results.join("\n"), "Results");
951
1057
  p.outro("Done.");
952
1058
  }
953
- async function runWizard(subcommand2) {
1059
+ function signInStep(client) {
1060
+ return SIGN_IN[client.id] ?? 'restart it; it opens "Sign in with Runpod" when it connects';
1061
+ }
1062
+ async function runNonInteractive(subcommand2, clientIds, requestedMode) {
1063
+ const clients = clientIds.map((id) => CLIENTS.find((c) => c.id === id));
1064
+ if (subcommand2 === "remove") {
1065
+ let failed2 = 0;
1066
+ for (const client of clients) {
1067
+ const result = await client.remove();
1068
+ if (!result.success) failed2++;
1069
+ console.log(
1070
+ `${result.success ? "\u2713" : "\u2717"} ${client.name}${result.message ? ` \u2014 ${result.message}` : ""}`
1071
+ );
1072
+ }
1073
+ return failed2 ? 1 : 0;
1074
+ }
1075
+ let mode = { kind: "hosted", url: HOSTED_URL };
1076
+ if (requestedMode === "local") {
1077
+ const apiKey = process.env.RUNPOD_API_KEY?.trim();
1078
+ if (!apiKey) {
1079
+ console.error(
1080
+ "--local reads the API key from RUNPOD_API_KEY, which is not set. Export it, or use --hosted to sign in with Runpod instead."
1081
+ );
1082
+ return 2;
1083
+ }
1084
+ const valid = await verifyApiKey(apiKey);
1085
+ if (valid === false) {
1086
+ console.error(
1087
+ "RUNPOD_API_KEY was rejected by Runpod. Nothing was configured."
1088
+ );
1089
+ return 1;
1090
+ }
1091
+ if (valid === null)
1092
+ console.error("Could not verify RUNPOD_API_KEY (offline?). Continuing.");
1093
+ mode = { kind: "local", apiKey };
1094
+ }
1095
+ let failed = 0;
1096
+ for (const client of clients) {
1097
+ const result = await client.add(mode);
1098
+ if (result.success) {
1099
+ console.log(
1100
+ `\u2713 ${client.name} \u2014 ${result.message ? `${result.message}; ` : ""}${client.describeTarget()}`
1101
+ );
1102
+ } else {
1103
+ failed++;
1104
+ console.log(`\u2717 ${client.name} \u2014 ${result.message ?? "unknown error"}`);
1105
+ }
1106
+ }
1107
+ for (const client of clients) {
1108
+ console.log(
1109
+ mode.kind === "hosted" ? `Next for ${client.name}: ${signInStep(client)}.` : `Next for ${client.name}: restart it (or reconnect MCP) to load the Runpod tools.`
1110
+ );
1111
+ }
1112
+ return failed ? 1 : 0;
1113
+ }
1114
+ async function listClients() {
1115
+ const detected = await detectAll();
1116
+ for (const client of CLIENTS) {
1117
+ console.log(
1118
+ `${client.id.padEnd(15)} ${detected.get(client.id) ? "detected " : "not detected"} ${client.describeTarget()}`
1119
+ );
1120
+ }
1121
+ }
1122
+ async function runWizard(subcommand2, argv = process.argv.slice(3), isInteractive = Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
1123
+ const ids = CLIENTS.map((c) => c.id);
1124
+ const args = parseWizardArgs(argv, ids);
1125
+ if (args.errors.length) {
1126
+ for (const error of args.errors) console.error(`error: ${error}`);
1127
+ console.error(`
1128
+ ${wizardUsage(ids)}`);
1129
+ return 2;
1130
+ }
1131
+ if (args.help) {
1132
+ console.log(wizardUsage(ids));
1133
+ return 0;
1134
+ }
1135
+ if (args.list) {
1136
+ await listClients();
1137
+ return 0;
1138
+ }
1139
+ if (args.clients) {
1140
+ return runNonInteractive(subcommand2, args.clients, args.mode);
1141
+ }
1142
+ if (!isInteractive) {
1143
+ console.error(
1144
+ `This installer needs an interactive terminal to show its prompts, and none is attached. Nothing was configured.
1145
+ To run it without prompts, name the clients: npx @runpod/mcp-server@latest ${subcommand2} --client <ids>
1146
+
1147
+ ${wizardUsage(ids)}`
1148
+ );
1149
+ return 2;
1150
+ }
954
1151
  p.intro("Runpod MCP server installer");
955
1152
  if (subcommand2 === "remove") {
956
1153
  await runRemove();
957
1154
  } else {
958
1155
  await runAdd();
959
1156
  }
1157
+ return 0;
960
1158
  }
961
- var import_child_process2, p, API_KEYS_URL;
1159
+ var import_child_process2, p, API_KEYS_URL, SIGN_IN;
962
1160
  var init_wizard = __esm({
963
1161
  "src/install/wizard.ts"() {
964
1162
  "use strict";
@@ -966,7 +1164,13 @@ var init_wizard = __esm({
966
1164
  p = __toESM(require("@clack/prompts"), 1);
967
1165
  init_clients();
968
1166
  init_verify_key();
1167
+ init_args();
969
1168
  API_KEYS_URL = "https://www.runpod.io/console/user/settings";
1169
+ SIGN_IN = {
1170
+ "claude-code": "in Claude Code run /mcp \u2192 runpod \u2192 Sign in with Runpod",
1171
+ opencode: "run `opencode mcp auth runpod`",
1172
+ pi: "run `pi mcp login runpod`"
1173
+ };
970
1174
  }
971
1175
  });
972
1176
 
@@ -975,11 +1179,11 @@ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
975
1179
 
976
1180
  // src/server.ts
977
1181
  var SERVER_NAME = "Runpod API Server";
978
- var SERVER_VERSION = true ? "4.2.0" : "dev";
1182
+ var SERVER_VERSION = true ? "4.3.0" : "dev";
979
1183
 
980
1184
  // src/specgen/server.ts
981
1185
  var import_server3 = require("@modelcontextprotocol/sdk/server/index.js");
982
- var import_types = require("@modelcontextprotocol/sdk/types.js");
1186
+ var import_types2 = require("@modelcontextprotocol/sdk/types.js");
983
1187
 
984
1188
  // src/_shared/rate-limit.ts
985
1189
  var GENERIC_RATE_LIMIT_HINT = "rate limited \u2014 back off before retrying, and pace bulk operations";
@@ -1179,10 +1383,13 @@ function validateArguments(inputSchema, args) {
1179
1383
  async function dispatchGeneratedTool(client, tool, args) {
1180
1384
  const rejected = validateArguments(tool.inputSchema, args);
1181
1385
  if (rejected) return rejected;
1386
+ const required = new Set(tool.inputSchema.required ?? []);
1182
1387
  const pathParams = {};
1183
1388
  const query = {};
1184
1389
  for (const param of tool.params) {
1185
1390
  if (args[param.name] === void 0) continue;
1391
+ if (param.location === "query" && !required.has(param.name) && isEmptyValue(args[param.name]))
1392
+ continue;
1186
1393
  if (param.location === "path") {
1187
1394
  pathParams[param.name] = args[param.name];
1188
1395
  continue;
@@ -1204,6 +1411,11 @@ async function dispatchGeneratedTool(client, tool, args) {
1204
1411
  };
1205
1412
  }
1206
1413
  }
1414
+ if (tool.hasBody && requestBody !== void 0) {
1415
+ const properties = tool.inputSchema.properties ?? {};
1416
+ const defs = tool.inputSchema.$defs ?? {};
1417
+ requestBody = coerceScalars(requestBody, properties.body, defs);
1418
+ }
1207
1419
  const verbs = client;
1208
1420
  const { data, error, response } = await verbs[tool.method](tool.path, {
1209
1421
  params: { path: pathParams, query },
@@ -1216,6 +1428,79 @@ async function dispatchGeneratedTool(client, tool, args) {
1216
1428
  payload: data ?? { status: response.status }
1217
1429
  };
1218
1430
  }
1431
+ function isEmptyValue(value) {
1432
+ return value === null || value === "" || Array.isArray(value) && !value.length;
1433
+ }
1434
+ function asSchema(value) {
1435
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1436
+ }
1437
+ function resolveSchema(schema, defs, depth = 0) {
1438
+ const node = asSchema(schema);
1439
+ if (!node || depth > 16) return node;
1440
+ const ref = node.$ref;
1441
+ if (typeof ref === "string" && ref.startsWith("#/$defs/"))
1442
+ return resolveSchema(defs[ref.slice("#/$defs/".length)], defs, depth + 1);
1443
+ return node;
1444
+ }
1445
+ function branches(schema, defs, depth = 0) {
1446
+ const node = resolveSchema(schema, defs);
1447
+ if (!node || depth > 16) return [];
1448
+ const out = [node];
1449
+ for (const key of ["allOf", "anyOf", "oneOf"]) {
1450
+ const list = node[key];
1451
+ if (Array.isArray(list))
1452
+ for (const sub of list) out.push(...branches(sub, defs, depth + 1));
1453
+ }
1454
+ return out;
1455
+ }
1456
+ function types(schemas) {
1457
+ const out = /* @__PURE__ */ new Set();
1458
+ for (const s of schemas) {
1459
+ if (typeof s.type === "string") out.add(s.type);
1460
+ else if (Array.isArray(s.type)) {
1461
+ for (const t of s.type) if (typeof t === "string") out.add(t);
1462
+ }
1463
+ }
1464
+ return out;
1465
+ }
1466
+ var NUMERIC = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
1467
+ function coerceScalars(value, schema, defs, depth = 0) {
1468
+ if (depth > 32) return value;
1469
+ const applicable = branches(schema, defs);
1470
+ if (!applicable.length) return value;
1471
+ const allowed = types(applicable);
1472
+ if (typeof value === "string") {
1473
+ if (allowed.has("string")) return value;
1474
+ if ((allowed.has("integer") || allowed.has("number")) && NUMERIC.test(value)) {
1475
+ const n = Number(value);
1476
+ if (allowed.has("number") || Number.isInteger(n)) return n;
1477
+ }
1478
+ if (allowed.has("boolean") && (value === "true" || value === "false"))
1479
+ return value === "true";
1480
+ return value;
1481
+ }
1482
+ if (Array.isArray(value)) {
1483
+ const items = applicable.map((s) => s.items).filter(Boolean);
1484
+ if (!items.length) return value;
1485
+ return value.map(
1486
+ (item) => coerceScalars(item, { anyOf: items }, defs, depth + 1)
1487
+ );
1488
+ }
1489
+ const object = asSchema(value);
1490
+ if (!object) return value;
1491
+ const out = {};
1492
+ for (const [key, child] of Object.entries(object)) {
1493
+ const childSchemas = [];
1494
+ for (const s of applicable) {
1495
+ const props = asSchema(s.properties);
1496
+ if (props && key in props) childSchemas.push(props[key]);
1497
+ else if (asSchema(s.additionalProperties))
1498
+ childSchemas.push(s.additionalProperties);
1499
+ }
1500
+ out[key] = childSchemas.length ? coerceScalars(child, { anyOf: childSchemas }, defs, depth + 1) : child;
1501
+ }
1502
+ return out;
1503
+ }
1219
1504
 
1220
1505
  // src/specgen/generated/tools.gen.ts
1221
1506
  var generatedTools = [
@@ -1233,6 +1518,7 @@ var generatedTools = [
1233
1518
  idempotentHint: false,
1234
1519
  openWorldHint: true
1235
1520
  },
1521
+ schemaOnDemand: true,
1236
1522
  inputSchema: {
1237
1523
  type: "object",
1238
1524
  properties: {
@@ -1244,11 +1530,10 @@ var generatedTools = [
1244
1530
  $defs: {
1245
1531
  BaseContainerConfig: {
1246
1532
  type: "object",
1247
- 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",
1248
1533
  properties: {
1249
1534
  args: {
1250
1535
  type: "string",
1251
- 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',
1536
+ 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.',
1252
1537
  examples: [
1253
1538
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
1254
1539
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -1260,7 +1545,7 @@ var generatedTools = [
1260
1545
  items: {
1261
1546
  type: "string"
1262
1547
  },
1263
- 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",
1548
+ 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.",
1264
1549
  examples: [
1265
1550
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
1266
1551
  ]
@@ -1270,7 +1555,7 @@ var generatedTools = [
1270
1555
  items: {
1271
1556
  type: "string"
1272
1557
  },
1273
- 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",
1558
+ 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.",
1274
1559
  examples: [["/bin/bash", "-c"]]
1275
1560
  },
1276
1561
  disk: {
@@ -1306,6 +1591,21 @@ var generatedTools = [
1306
1591
  }
1307
1592
  }
1308
1593
  },
1594
+ BaseMounts: {
1595
+ type: "object",
1596
+ properties: {
1597
+ persistent: {
1598
+ $ref: "#/$defs/PersistentMount"
1599
+ },
1600
+ network: {
1601
+ type: "array",
1602
+ maxItems: 1,
1603
+ items: {
1604
+ $ref: "#/$defs/NetworkMount"
1605
+ }
1606
+ }
1607
+ }
1608
+ },
1309
1609
  ClusterCompute: {
1310
1610
  type: "object",
1311
1611
  additionalProperties: false,
@@ -1333,6 +1633,15 @@ var generatedTools = [
1333
1633
  }
1334
1634
  }
1335
1635
  },
1636
+ ClusterMounts: {
1637
+ description: "Storage mounts for a cluster create: `persistent` or `network`, not both. Clusters cannot mount global volumes, so `global` is rejected.",
1638
+ allOf: [
1639
+ {
1640
+ $ref: "#/$defs/BaseMounts"
1641
+ }
1642
+ ],
1643
+ unevaluatedProperties: false
1644
+ },
1336
1645
  ClusterType: {
1337
1646
  type: "string",
1338
1647
  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.",
@@ -1347,7 +1656,7 @@ var generatedTools = [
1347
1656
  {
1348
1657
  type: "object",
1349
1658
  required: ["name", "type", "compute"],
1350
- 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",
1659
+ description: "Request body for creating a cluster. `compute` defines the homogeneous pod shape; the container configuration (image, env, ports, \u2026) applies to every pod.",
1351
1660
  properties: {
1352
1661
  compute: {
1353
1662
  $ref: "#/$defs/ClusterCompute"
@@ -1363,7 +1672,7 @@ var generatedTools = [
1363
1672
  templateId: {
1364
1673
  type: "string",
1365
1674
  minLength: 1,
1366
- 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",
1675
+ 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.",
1367
1676
  examples: ["30zmvf89kd"]
1368
1677
  },
1369
1678
  dataCenterIds: {
@@ -1371,11 +1680,11 @@ var generatedTools = [
1371
1680
  items: {
1372
1681
  type: "string"
1373
1682
  },
1374
- 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",
1683
+ 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.",
1375
1684
  example: ["US-TX-3"]
1376
1685
  },
1377
1686
  mounts: {
1378
- $ref: "#/$defs/Mounts"
1687
+ $ref: "#/$defs/ClusterMounts"
1379
1688
  },
1380
1689
  startJupyter: {
1381
1690
  type: "boolean",
@@ -1392,28 +1701,11 @@ var generatedTools = [
1392
1701
  ],
1393
1702
  unevaluatedProperties: false
1394
1703
  },
1395
- Mounts: {
1396
- type: "object",
1397
- additionalProperties: false,
1398
- 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",
1399
- properties: {
1400
- persistent: {
1401
- $ref: "#/$defs/PersistentMount"
1402
- },
1403
- network: {
1404
- type: "array",
1405
- maxItems: 1,
1406
- items: {
1407
- $ref: "#/$defs/NetworkMount"
1408
- }
1409
- }
1410
- }
1411
- },
1412
1704
  NetworkMount: {
1413
1705
  type: "object",
1414
1706
  required: ["volumeId", "path"],
1415
1707
  additionalProperties: false,
1416
- 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",
1708
+ 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.",
1417
1709
  properties: {
1418
1710
  volumeId: {
1419
1711
  type: "string",
@@ -1431,7 +1723,7 @@ var generatedTools = [
1431
1723
  type: "object",
1432
1724
  required: ["size", "path"],
1433
1725
  additionalProperties: false,
1434
- 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",
1726
+ 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.",
1435
1727
  properties: {
1436
1728
  size: {
1437
1729
  type: "integer",
@@ -1508,6 +1800,7 @@ var generatedTools = [
1508
1800
  idempotentHint: false,
1509
1801
  openWorldHint: true
1510
1802
  },
1803
+ schemaOnDemand: true,
1511
1804
  inputSchema: {
1512
1805
  type: "object",
1513
1806
  properties: {
@@ -1519,11 +1812,10 @@ var generatedTools = [
1519
1812
  $defs: {
1520
1813
  BaseContainerConfig: {
1521
1814
  type: "object",
1522
- 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",
1523
1815
  properties: {
1524
1816
  args: {
1525
1817
  type: "string",
1526
- 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',
1818
+ 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.',
1527
1819
  examples: [
1528
1820
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
1529
1821
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -1535,7 +1827,7 @@ var generatedTools = [
1535
1827
  items: {
1536
1828
  type: "string"
1537
1829
  },
1538
- 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",
1830
+ 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.",
1539
1831
  examples: [
1540
1832
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
1541
1833
  ]
@@ -1545,7 +1837,7 @@ var generatedTools = [
1545
1837
  items: {
1546
1838
  type: "string"
1547
1839
  },
1548
- 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",
1840
+ 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.",
1549
1841
  examples: [["/bin/bash", "-c"]]
1550
1842
  },
1551
1843
  disk: {
@@ -1605,7 +1897,7 @@ var generatedTools = [
1605
1897
  pools: {
1606
1898
  type: "array",
1607
1899
  minItems: 1,
1608
- 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',
1900
+ 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.",
1609
1901
  items: {
1610
1902
  type: "string"
1611
1903
  },
@@ -1614,7 +1906,7 @@ var generatedTools = [
1614
1906
  excludedTypes: {
1615
1907
  type: "array",
1616
1908
  uniqueItems: true,
1617
- 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',
1909
+ 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.",
1618
1910
  items: {
1619
1911
  type: "string",
1620
1912
  pattern: "^\\s*[^-\\s]"
@@ -1631,7 +1923,6 @@ var generatedTools = [
1631
1923
  }
1632
1924
  },
1633
1925
  ContainerConfig: {
1634
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
1635
1926
  allOf: [
1636
1927
  {
1637
1928
  $ref: "#/$defs/BaseContainerConfig"
@@ -1657,7 +1948,7 @@ var generatedTools = [
1657
1948
  unevaluatedProperties: false
1658
1949
  },
1659
1950
  CreateEndpointGpuConfig: {
1660
- 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",
1951
+ 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.",
1661
1952
  allOf: [
1662
1953
  {
1663
1954
  $ref: "#/$defs/BaseEndpointGpuConfig"
@@ -1672,13 +1963,13 @@ var generatedTools = [
1672
1963
  type: "string",
1673
1964
  pattern: "^\\d+\\.\\d+$"
1674
1965
  },
1675
- 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",
1966
+ 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`).",
1676
1967
  examples: [["12.8", "12.6"]]
1677
1968
  },
1678
1969
  minCudaVersion: {
1679
1970
  type: "string",
1680
1971
  pattern: "^\\d+\\.\\d+$",
1681
- 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",
1972
+ 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.",
1682
1973
  examples: ["12.1"]
1683
1974
  }
1684
1975
  }
@@ -1722,13 +2013,13 @@ var generatedTools = [
1722
2013
  $ref: "#/$defs/EndpointType"
1723
2014
  }
1724
2015
  ],
1725
- description: "Request-routing model. Required \u2014 it determines the valid scaler\nand request URLs, so it must be chosen explicitly on every create.\n"
2016
+ description: "Request-routing model. Required \u2014 it determines the valid scaler and request URLs, so it must be chosen explicitly on every create."
1726
2017
  },
1727
2018
  cpu: {
1728
2019
  type: "array",
1729
2020
  minItems: 1,
1730
2021
  uniqueItems: true,
1731
- 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",
2022
+ 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.",
1732
2023
  items: {
1733
2024
  $ref: "#/$defs/CreateCpuConfig"
1734
2025
  }
@@ -1752,12 +2043,17 @@ var generatedTools = [
1752
2043
  type: "array",
1753
2044
  items: {
1754
2045
  type: "string"
1755
- }
2046
+ },
2047
+ description: "Network volume IDs.",
2048
+ deprecated: true
2049
+ },
2050
+ mounts: {
2051
+ $ref: "#/$defs/EndpointMountsInput"
1756
2052
  },
1757
2053
  templateId: {
1758
2054
  type: "string",
1759
2055
  minLength: 1,
1760
- 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",
2056
+ 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.",
1761
2057
  examples: ["30zmvf89kd"]
1762
2058
  },
1763
2059
  timeout: {
@@ -1805,8 +2101,42 @@ var generatedTools = [
1805
2101
  }
1806
2102
  }
1807
2103
  },
2104
+ EndpointMount: {
2105
+ type: "object",
2106
+ required: ["volumeId"],
2107
+ additionalProperties: false,
2108
+ description: "A volume mounted on every worker, always at `/runpod-volume`. Custom paths are not supported yet.",
2109
+ properties: {
2110
+ volumeId: {
2111
+ type: "string",
2112
+ description: "ID of a NetworkVolume (under `network`) or GlobalVolume (under `global`).",
2113
+ examples: ["vol_abc"]
2114
+ }
2115
+ }
2116
+ },
2117
+ EndpointMountsInput: {
2118
+ type: "object",
2119
+ additionalProperties: false,
2120
+ 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.",
2121
+ properties: {
2122
+ network: {
2123
+ type: "array",
2124
+ description: "Network volumes, at most one per data center.",
2125
+ items: {
2126
+ $ref: "#/$defs/EndpointMount"
2127
+ }
2128
+ },
2129
+ global: {
2130
+ type: "array",
2131
+ maxItems: 1,
2132
+ items: {
2133
+ $ref: "#/$defs/EndpointMount"
2134
+ }
2135
+ }
2136
+ }
2137
+ },
1808
2138
  EndpointScaling: {
1809
- 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",
2139
+ 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.",
1810
2140
  oneOf: [
1811
2141
  {
1812
2142
  $ref: "#/$defs/QueueDelayScaling"
@@ -1825,7 +2155,7 @@ var generatedTools = [
1825
2155
  },
1826
2156
  EndpointType: {
1827
2157
  type: "string",
1828
- 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",
2158
+ 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.",
1829
2159
  "x-enum-varnames": ["EndpointTypeQueue", "EndpointTypeLoadBalancer"],
1830
2160
  enum: ["QUEUE", "LOAD_BALANCER"]
1831
2161
  },
@@ -1849,14 +2179,14 @@ var generatedTools = [
1849
2179
  type: "integer",
1850
2180
  minimum: 1,
1851
2181
  maximum: 3600,
1852
- 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",
2182
+ 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.",
1853
2183
  examples: [5]
1854
2184
  }
1855
2185
  }
1856
2186
  },
1857
2187
  FlashBoot: {
1858
2188
  type: "string",
1859
- description: "FlashBoot cold-start acceleration mode.\n- `OFF` \u2014 disabled\n- `FLASHBOOT` \u2014 enabled\n- `PRIORITY_FLASHBOOT` \u2014 enabled with priority capacity\n",
2189
+ description: "FlashBoot cold-start acceleration mode. `OFF` \u2014 disabled. `FLASHBOOT` \u2014 enabled. `PRIORITY_FLASHBOOT` \u2014 enabled with priority capacity",
1860
2190
  enum: ["OFF", "FLASHBOOT", "PRIORITY_FLASHBOOT"]
1861
2191
  },
1862
2192
  QueueDelayScaling: {
@@ -1882,7 +2212,7 @@ var generatedTools = [
1882
2212
  RequestCountScaling: {
1883
2213
  type: "object",
1884
2214
  additionalProperties: false,
1885
- description: "Scale on concurrent in-flight requests per worker. Required for\nload-balancing endpoints; also selectable for queue-based.\n",
2215
+ description: "Scale on concurrent in-flight requests per worker. Required for load-balancing endpoints; also selectable for queue-based.",
1886
2216
  required: ["type", "requestCount"],
1887
2217
  properties: {
1888
2218
  type: {
@@ -1901,6 +2231,47 @@ var generatedTools = [
1901
2231
  }
1902
2232
  }
1903
2233
  },
2234
+ {
2235
+ name: "create-global-volume",
2236
+ operationId: "createGlobalVolume",
2237
+ 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`.",
2238
+ method: "POST",
2239
+ path: "/v2/global-volumes",
2240
+ params: [],
2241
+ hasBody: true,
2242
+ annotations: {
2243
+ readOnlyHint: false,
2244
+ destructiveHint: false,
2245
+ idempotentHint: false,
2246
+ openWorldHint: true
2247
+ },
2248
+ inputSchema: {
2249
+ type: "object",
2250
+ properties: {
2251
+ body: {
2252
+ $ref: "#/$defs/CreateGlobalVolumeRequest"
2253
+ }
2254
+ },
2255
+ required: ["body"],
2256
+ $defs: {
2257
+ CreateGlobalVolumeRequest: {
2258
+ type: "object",
2259
+ additionalProperties: false,
2260
+ required: ["name"],
2261
+ properties: {
2262
+ name: {
2263
+ type: "string",
2264
+ minLength: 1,
2265
+ maxLength: 191,
2266
+ pattern: "[^\\p{Z}\\t-\\r\x85]",
2267
+ description: "Human-readable name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed.",
2268
+ examples: ["training-data"]
2269
+ }
2270
+ }
2271
+ }
2272
+ }
2273
+ }
2274
+ },
1904
2275
  {
1905
2276
  name: "create-network-volume",
1906
2277
  operationId: "createNetworkVolume",
@@ -1954,7 +2325,7 @@ var generatedTools = [
1954
2325
  $ref: "#/$defs/VolumeType"
1955
2326
  }
1956
2327
  ],
1957
- 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"
2328
+ description: "Storage tier for the volume. Optional. When omitted, the volume is provisioned using the requested data center's default (primary) storage tier."
1958
2329
  }
1959
2330
  }
1960
2331
  },
@@ -1980,6 +2351,7 @@ var generatedTools = [
1980
2351
  idempotentHint: false,
1981
2352
  openWorldHint: true
1982
2353
  },
2354
+ schemaOnDemand: true,
1983
2355
  inputSchema: {
1984
2356
  type: "object",
1985
2357
  properties: {
@@ -1991,11 +2363,10 @@ var generatedTools = [
1991
2363
  $defs: {
1992
2364
  BaseContainerConfig: {
1993
2365
  type: "object",
1994
- 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",
1995
2366
  properties: {
1996
2367
  args: {
1997
2368
  type: "string",
1998
- 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',
2369
+ 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.',
1999
2370
  examples: [
2000
2371
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
2001
2372
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -2007,7 +2378,7 @@ var generatedTools = [
2007
2378
  items: {
2008
2379
  type: "string"
2009
2380
  },
2010
- 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",
2381
+ 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.",
2011
2382
  examples: [
2012
2383
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
2013
2384
  ]
@@ -2017,7 +2388,7 @@ var generatedTools = [
2017
2388
  items: {
2018
2389
  type: "string"
2019
2390
  },
2020
- 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",
2391
+ 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.",
2021
2392
  examples: [["/bin/bash", "-c"]]
2022
2393
  },
2023
2394
  disk: {
@@ -2089,13 +2460,27 @@ var generatedTools = [
2089
2460
  }
2090
2461
  }
2091
2462
  },
2463
+ BaseMounts: {
2464
+ type: "object",
2465
+ properties: {
2466
+ persistent: {
2467
+ $ref: "#/$defs/PersistentMount"
2468
+ },
2469
+ network: {
2470
+ type: "array",
2471
+ maxItems: 1,
2472
+ items: {
2473
+ $ref: "#/$defs/NetworkMount"
2474
+ }
2475
+ }
2476
+ }
2477
+ },
2092
2478
  Cloud: {
2093
2479
  type: "string",
2094
- description: "Cloud tier.\n- `SECURE` \u2014 Runpod-owned datacenter hardware\n- `COMMUNITY` \u2014 community-hosted hardware\n",
2480
+ description: "Cloud tier. `SECURE` \u2014 Runpod-owned datacenter hardware. `COMMUNITY` \u2014 community-hosted hardware",
2095
2481
  enum: ["SECURE", "COMMUNITY"]
2096
2482
  },
2097
2483
  ContainerConfig: {
2098
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
2099
2484
  allOf: [
2100
2485
  {
2101
2486
  $ref: "#/$defs/BaseContainerConfig"
@@ -2121,7 +2506,7 @@ var generatedTools = [
2121
2506
  unevaluatedProperties: false
2122
2507
  },
2123
2508
  CreateGpuConfig: {
2124
- 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",
2509
+ 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.",
2125
2510
  allOf: [
2126
2511
  {
2127
2512
  $ref: "#/$defs/BaseGpuConfig"
@@ -2135,13 +2520,13 @@ var generatedTools = [
2135
2520
  type: "string",
2136
2521
  pattern: "^\\d+\\.\\d+$"
2137
2522
  },
2138
- 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",
2523
+ description: "Acceptable CUDA versions for the host machine, as `major.minor`. Omit to accept any version.",
2139
2524
  examples: [["12.8", "12.6"]]
2140
2525
  },
2141
2526
  minCudaVersion: {
2142
2527
  type: "string",
2143
2528
  pattern: "^\\d+\\.\\d+$",
2144
- 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",
2529
+ 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.",
2145
2530
  examples: ["12.1"]
2146
2531
  },
2147
2532
  minRamPerGpu: {
@@ -2169,7 +2554,7 @@ var generatedTools = [
2169
2554
  {
2170
2555
  type: "object",
2171
2556
  required: ["name"],
2172
- 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",
2557
+ description: "Request body for creating a pod. Exactly one of `gpu` or `cpu` must be set \u2014 enforced at the handler layer.",
2173
2558
  properties: {
2174
2559
  name: {
2175
2560
  type: "string",
@@ -2193,13 +2578,13 @@ var generatedTools = [
2193
2578
  items: {
2194
2579
  type: "string"
2195
2580
  },
2196
- description: "Preferred data centers for placement. Omit or pass an empty\narray to let the scheduler choose.\n",
2581
+ description: "Preferred data centers for placement. Omit or pass an empty array to let the scheduler choose.",
2197
2582
  examples: [["US-TX-3"]]
2198
2583
  },
2199
2584
  globalNetworking: {
2200
2585
  type: "boolean",
2201
2586
  default: false,
2202
- 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.",
2587
+ 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).",
2203
2588
  examples: [false]
2204
2589
  },
2205
2590
  gpu: {
@@ -2211,19 +2596,19 @@ var generatedTools = [
2211
2596
  startJupyter: {
2212
2597
  type: "boolean",
2213
2598
  default: false,
2214
- 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",
2599
+ 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.",
2215
2600
  examples: [true]
2216
2601
  },
2217
2602
  startSsh: {
2218
2603
  type: "boolean",
2219
2604
  default: false,
2220
- 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",
2605
+ 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`.",
2221
2606
  examples: [true]
2222
2607
  },
2223
2608
  templateId: {
2224
2609
  type: "string",
2225
2610
  minLength: 1,
2226
- 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",
2611
+ 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.",
2227
2612
  examples: ["30zmvf89kd"]
2228
2613
  }
2229
2614
  }
@@ -2239,28 +2624,51 @@ var generatedTools = [
2239
2624
  required: ["image"]
2240
2625
  }
2241
2626
  },
2242
- Mounts: {
2627
+ GlobalMount: {
2243
2628
  type: "object",
2629
+ required: ["volumeId"],
2244
2630
  additionalProperties: false,
2245
- 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",
2631
+ 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.",
2246
2632
  properties: {
2247
- persistent: {
2248
- $ref: "#/$defs/PersistentMount"
2633
+ volumeId: {
2634
+ type: "string",
2635
+ description: "ID of an existing GlobalVolume.",
2636
+ examples: ["gsb_abc"]
2249
2637
  },
2250
- network: {
2251
- type: "array",
2252
- maxItems: 1,
2253
- items: {
2254
- $ref: "#/$defs/NetworkMount"
2255
- }
2638
+ path: {
2639
+ type: "string",
2640
+ 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.",
2641
+ examples: ["/data"]
2256
2642
  }
2257
2643
  }
2258
2644
  },
2645
+ Mounts: {
2646
+ 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.",
2647
+ allOf: [
2648
+ {
2649
+ $ref: "#/$defs/BaseMounts"
2650
+ },
2651
+ {
2652
+ type: "object",
2653
+ properties: {
2654
+ global: {
2655
+ type: "array",
2656
+ maxItems: 1,
2657
+ 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.",
2658
+ items: {
2659
+ $ref: "#/$defs/GlobalMount"
2660
+ }
2661
+ }
2662
+ }
2663
+ }
2664
+ ],
2665
+ unevaluatedProperties: false
2666
+ },
2259
2667
  NetworkMount: {
2260
2668
  type: "object",
2261
2669
  required: ["volumeId", "path"],
2262
2670
  additionalProperties: false,
2263
- 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",
2671
+ 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.",
2264
2672
  properties: {
2265
2673
  volumeId: {
2266
2674
  type: "string",
@@ -2278,7 +2686,7 @@ var generatedTools = [
2278
2686
  type: "object",
2279
2687
  required: ["size", "path"],
2280
2688
  additionalProperties: false,
2281
- 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",
2689
+ 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.",
2282
2690
  properties: {
2283
2691
  size: {
2284
2692
  type: "integer",
@@ -2361,6 +2769,7 @@ var generatedTools = [
2361
2769
  idempotentHint: false,
2362
2770
  openWorldHint: true
2363
2771
  },
2772
+ schemaOnDemand: true,
2364
2773
  inputSchema: {
2365
2774
  type: "object",
2366
2775
  properties: {
@@ -2380,7 +2789,7 @@ var generatedTools = [
2380
2789
  minLength: 1,
2381
2790
  maxLength: 191,
2382
2791
  pattern: "^[a-zA-Z_][a-zA-Z0-9_.\\-/]*$",
2383
- 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).",
2792
+ 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 `_.-/`.",
2384
2793
  examples: ["hf-token"]
2385
2794
  },
2386
2795
  value: {
@@ -2414,6 +2823,7 @@ var generatedTools = [
2414
2823
  idempotentHint: false,
2415
2824
  openWorldHint: true
2416
2825
  },
2826
+ schemaOnDemand: true,
2417
2827
  inputSchema: {
2418
2828
  type: "object",
2419
2829
  properties: {
@@ -2425,11 +2835,10 @@ var generatedTools = [
2425
2835
  $defs: {
2426
2836
  BaseContainerConfig: {
2427
2837
  type: "object",
2428
- 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",
2429
2838
  properties: {
2430
2839
  args: {
2431
2840
  type: "string",
2432
- 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',
2841
+ 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.',
2433
2842
  examples: [
2434
2843
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
2435
2844
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -2441,7 +2850,7 @@ var generatedTools = [
2441
2850
  items: {
2442
2851
  type: "string"
2443
2852
  },
2444
- 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",
2853
+ 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.",
2445
2854
  examples: [
2446
2855
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
2447
2856
  ]
@@ -2451,7 +2860,7 @@ var generatedTools = [
2451
2860
  items: {
2452
2861
  type: "string"
2453
2862
  },
2454
- 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",
2863
+ 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.",
2455
2864
  examples: [["/bin/bash", "-c"]]
2456
2865
  },
2457
2866
  disk: {
@@ -2488,7 +2897,6 @@ var generatedTools = [
2488
2897
  }
2489
2898
  },
2490
2899
  ContainerConfig: {
2491
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
2492
2900
  allOf: [
2493
2901
  {
2494
2902
  $ref: "#/$defs/BaseContainerConfig"
@@ -2525,7 +2933,7 @@ var generatedTools = [
2525
2933
  type: "string",
2526
2934
  pattern: "^\\d+\\.\\d+$"
2527
2935
  },
2528
- 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",
2936
+ 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.",
2529
2937
  examples: [["12.8", "12.6"]]
2530
2938
  },
2531
2939
  category: {
@@ -2551,13 +2959,13 @@ var generatedTools = [
2551
2959
  startJupyter: {
2552
2960
  type: "boolean",
2553
2961
  default: true,
2554
- 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",
2962
+ description: "Start JupyterLab in containers created from this template: injects a generated `JUPYTER_PASSWORD` environment variable, unless `env` already sets one.",
2555
2963
  examples: [false]
2556
2964
  },
2557
2965
  startSsh: {
2558
2966
  type: "boolean",
2559
2967
  default: true,
2560
- 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",
2968
+ 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.",
2561
2969
  examples: [true]
2562
2970
  }
2563
2971
  }
@@ -2569,7 +2977,7 @@ var generatedTools = [
2569
2977
  type: "object",
2570
2978
  required: ["size", "path"],
2571
2979
  additionalProperties: false,
2572
- 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",
2980
+ 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.",
2573
2981
  properties: {
2574
2982
  size: {
2575
2983
  type: "integer",
@@ -2586,13 +2994,13 @@ var generatedTools = [
2586
2994
  },
2587
2995
  TemplateCategory: {
2588
2996
  type: "string",
2589
- 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",
2997
+ 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",
2590
2998
  enum: ["CPU", "NVIDIA", "AMD"]
2591
2999
  },
2592
3000
  TemplateMounts: {
2593
3001
  type: "object",
2594
3002
  additionalProperties: false,
2595
- 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",
3003
+ 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.",
2596
3004
  properties: {
2597
3005
  persistent: {
2598
3006
  $ref: "#/$defs/PersistentMount"
@@ -2662,6 +3070,36 @@ var generatedTools = [
2662
3070
  required: ["id"]
2663
3071
  }
2664
3072
  },
3073
+ {
3074
+ name: "delete-global-volume",
3075
+ operationId: "deleteGlobalVolume",
3076
+ 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.",
3077
+ method: "DELETE",
3078
+ path: "/v2/global-volumes/{id}",
3079
+ params: [
3080
+ {
3081
+ name: "id",
3082
+ location: "path"
3083
+ }
3084
+ ],
3085
+ hasBody: false,
3086
+ annotations: {
3087
+ readOnlyHint: false,
3088
+ destructiveHint: true,
3089
+ idempotentHint: true,
3090
+ openWorldHint: true
3091
+ },
3092
+ inputSchema: {
3093
+ type: "object",
3094
+ properties: {
3095
+ id: {
3096
+ type: "string",
3097
+ description: "Global volume identifier"
3098
+ }
3099
+ },
3100
+ required: ["id"]
3101
+ }
3102
+ },
2665
3103
  {
2666
3104
  name: "delete-network-volume",
2667
3105
  operationId: "deleteNetworkVolume",
@@ -3027,12 +3465,42 @@ var generatedTools = [
3027
3465
  },
3028
3466
  buildId: {
3029
3467
  type: "string",
3030
- description: "GitHub build identifier (from GET /v2/serverless/{id}/builds or a release's `buildId`)"
3468
+ description: "GitHub build identifier (from `list-endpoint-builds` or a release's `buildId`)"
3031
3469
  }
3032
3470
  },
3033
3471
  required: ["id", "buildId"]
3034
3472
  }
3035
3473
  },
3474
+ {
3475
+ name: "get-global-volume",
3476
+ operationId: "getGlobalVolume",
3477
+ description: "Get a global volume. Returns a single global volume by ID.",
3478
+ method: "GET",
3479
+ path: "/v2/global-volumes/{id}",
3480
+ params: [
3481
+ {
3482
+ name: "id",
3483
+ location: "path"
3484
+ }
3485
+ ],
3486
+ hasBody: false,
3487
+ annotations: {
3488
+ readOnlyHint: true,
3489
+ destructiveHint: false,
3490
+ idempotentHint: true,
3491
+ openWorldHint: true
3492
+ },
3493
+ inputSchema: {
3494
+ type: "object",
3495
+ properties: {
3496
+ id: {
3497
+ type: "string",
3498
+ description: "Global volume identifier"
3499
+ }
3500
+ },
3501
+ required: ["id"]
3502
+ }
3503
+ },
3036
3504
  {
3037
3505
  name: "get-gpu-type",
3038
3506
  operationId: "getGpuType",
@@ -3103,7 +3571,7 @@ var generatedTools = [
3103
3571
  items: {
3104
3572
  $ref: "#/$defs/Product"
3105
3573
  },
3106
- 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."
3574
+ description: "Comma-separated availability product contexts. Supported values: POD, CLUSTER, SERVERLESS. Required with include=AVAILABILITY, and valid only with it (400 either way)."
3107
3575
  },
3108
3576
  count: {
3109
3577
  type: "integer",
@@ -3121,7 +3589,7 @@ var generatedTools = [
3121
3589
  type: "string",
3122
3590
  pattern: "^[A-Z]{2}$"
3123
3591
  },
3124
- 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."
3592
+ 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."
3125
3593
  },
3126
3594
  cudaVersions: {
3127
3595
  type: "array",
@@ -3129,12 +3597,12 @@ var generatedTools = [
3129
3597
  type: "string",
3130
3598
  pattern: "^\\d+\\.\\d+$"
3131
3599
  },
3132
- 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."
3600
+ 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."
3133
3601
  },
3134
3602
  minCudaVersion: {
3135
3603
  type: "string",
3136
3604
  pattern: "^\\d+(\\.\\d+)?$",
3137
- 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."
3605
+ description: "Lowest acceptable CUDA version to scope availability and lowest-price calculations to, compared numerically."
3138
3606
  }
3139
3607
  },
3140
3608
  required: ["id"],
@@ -3187,6 +3655,25 @@ var generatedTools = [
3187
3655
  required: ["id"]
3188
3656
  }
3189
3657
  },
3658
+ {
3659
+ name: "get-organization",
3660
+ operationId: "getOrganization",
3661
+ 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.",
3662
+ method: "GET",
3663
+ path: "/v2/organization",
3664
+ params: [],
3665
+ hasBody: false,
3666
+ annotations: {
3667
+ readOnlyHint: true,
3668
+ destructiveHint: false,
3669
+ idempotentHint: true,
3670
+ openWorldHint: true
3671
+ },
3672
+ inputSchema: {
3673
+ type: "object",
3674
+ properties: {}
3675
+ }
3676
+ },
3190
3677
  {
3191
3678
  name: "get-pod",
3192
3679
  operationId: "getPod",
@@ -3361,12 +3848,12 @@ var generatedTools = [
3361
3848
  startTime: {
3362
3849
  type: "string",
3363
3850
  format: "date-time",
3364
- 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"
3851
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
3365
3852
  },
3366
3853
  endTime: {
3367
3854
  type: "string",
3368
3855
  format: "date-time",
3369
- 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"
3856
+ 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."
3370
3857
  },
3371
3858
  bucketSize: {
3372
3859
  $ref: "#/$defs/BillingBucketSize",
@@ -3375,7 +3862,7 @@ var generatedTools = [
3375
3862
  lastN: {
3376
3863
  type: "integer",
3377
3864
  minimum: 1,
3378
- 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'
3865
+ 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".'
3379
3866
  }
3380
3867
  },
3381
3868
  $defs: {
@@ -3437,12 +3924,12 @@ var generatedTools = [
3437
3924
  startTime: {
3438
3925
  type: "string",
3439
3926
  format: "date-time",
3440
- 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"
3927
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
3441
3928
  },
3442
3929
  endTime: {
3443
3930
  type: "string",
3444
3931
  format: "date-time",
3445
- 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"
3932
+ 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."
3446
3933
  },
3447
3934
  bucketSize: {
3448
3935
  $ref: "#/$defs/BillingBucketSize",
@@ -3451,7 +3938,7 @@ var generatedTools = [
3451
3938
  lastN: {
3452
3939
  type: "integer",
3453
3940
  minimum: 1,
3454
- 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'
3941
+ 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".'
3455
3942
  },
3456
3943
  clusterId: {
3457
3944
  type: "string",
@@ -3509,10 +3996,19 @@ var generatedTools = [
3509
3996
  {
3510
3997
  name: "list-clusters",
3511
3998
  operationId: "listClusters",
3512
- description: "List clusters. Returns all clusters owned by the authenticated user.",
3999
+ 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.",
3513
4000
  method: "GET",
3514
4001
  path: "/v2/clusters",
3515
- params: [],
4002
+ params: [
4003
+ {
4004
+ name: "cursor",
4005
+ location: "query"
4006
+ },
4007
+ {
4008
+ name: "limit",
4009
+ location: "query"
4010
+ }
4011
+ ],
3516
4012
  hasBody: false,
3517
4013
  annotations: {
3518
4014
  readOnlyHint: true,
@@ -3522,7 +4018,19 @@ var generatedTools = [
3522
4018
  },
3523
4019
  inputSchema: {
3524
4020
  type: "object",
3525
- properties: {}
4021
+ properties: {
4022
+ cursor: {
4023
+ type: "string",
4024
+ minLength: 1,
4025
+ 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."
4026
+ },
4027
+ limit: {
4028
+ type: "integer",
4029
+ minimum: 1,
4030
+ maximum: 1e3,
4031
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
4032
+ }
4033
+ }
3526
4034
  }
3527
4035
  },
3528
4036
  {
@@ -3776,12 +4284,12 @@ var generatedTools = [
3776
4284
  startTime: {
3777
4285
  type: "string",
3778
4286
  format: "date-time",
3779
- 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"
4287
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
3780
4288
  },
3781
4289
  endTime: {
3782
4290
  type: "string",
3783
4291
  format: "date-time",
3784
- 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"
4292
+ 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."
3785
4293
  },
3786
4294
  bucketSize: {
3787
4295
  $ref: "#/$defs/BillingBucketSize",
@@ -3790,7 +4298,7 @@ var generatedTools = [
3790
4298
  lastN: {
3791
4299
  type: "integer",
3792
4300
  minimum: 1,
3793
- 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'
4301
+ 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".'
3794
4302
  }
3795
4303
  },
3796
4304
  $defs: {
@@ -3848,7 +4356,7 @@ var generatedTools = [
3848
4356
  cursor: {
3849
4357
  type: "string",
3850
4358
  minLength: 1,
3851
- 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"
4359
+ 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."
3852
4360
  },
3853
4361
  limit: {
3854
4362
  type: "integer",
@@ -3897,7 +4405,7 @@ var generatedTools = [
3897
4405
  cursor: {
3898
4406
  type: "string",
3899
4407
  minLength: 1,
3900
- 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"
4408
+ 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."
3901
4409
  },
3902
4410
  limit: {
3903
4411
  type: "integer",
@@ -3910,15 +4418,49 @@ var generatedTools = [
3910
4418
  }
3911
4419
  },
3912
4420
  {
3913
- name: "list-endpoint-workers",
3914
- operationId: "listEndpointWorkers",
3915
- description: "List serverless endpoint workers. Lists the active workers for a serverless endpoint. **Returns.** A `200` with a `ListEndpointWorkersResponse`: a `workers` array (one entry per active worker, each carrying its `id`, `status`, and runtime details) plus a `summary` of worker counts grouped by status. Only currently active workers are included; scaled-down workers are not returned. **How `status` is determined.** Each worker's `status` is derived by reconciling the worker pod's lifecycle status with the endpoint's live job-queue view (which workers are actively serving requests). When the job-queue view is unavailable, the response degrades gracefully: the shape is unchanged, but each `status` and the summary counts fall back to pod lifecycle alone.",
4421
+ name: "list-endpoint-workers",
4422
+ operationId: "listEndpointWorkers",
4423
+ description: "List serverless endpoint workers. Lists the active workers for a serverless endpoint. **Returns.** A `200` with a `ListEndpointWorkersResponse`: a `workers` array (one entry per active worker, each carrying its `id`, `status`, and runtime details) plus a `summary` of worker counts grouped by status. Only currently active workers are included; scaled-down workers are not returned. **How `status` is determined.** Each worker's `status` is derived by reconciling the worker pod's lifecycle status with the endpoint's live job-queue view (which workers are actively serving requests). When the job-queue view is unavailable, the response degrades gracefully: the shape is unchanged, but each `status` and the summary counts fall back to pod lifecycle alone.",
4424
+ method: "GET",
4425
+ path: "/v2/serverless/{id}/workers",
4426
+ params: [
4427
+ {
4428
+ name: "id",
4429
+ location: "path"
4430
+ }
4431
+ ],
4432
+ hasBody: false,
4433
+ annotations: {
4434
+ readOnlyHint: true,
4435
+ destructiveHint: false,
4436
+ idempotentHint: true,
4437
+ openWorldHint: true
4438
+ },
4439
+ inputSchema: {
4440
+ type: "object",
4441
+ properties: {
4442
+ id: {
4443
+ type: "string",
4444
+ description: "Serverless endpoint identifier"
4445
+ }
4446
+ },
4447
+ required: ["id"]
4448
+ }
4449
+ },
4450
+ {
4451
+ name: "list-global-volumes",
4452
+ operationId: "listGlobalVolumes",
4453
+ 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.",
3916
4454
  method: "GET",
3917
- path: "/v2/serverless/{id}/workers",
4455
+ path: "/v2/global-volumes",
3918
4456
  params: [
3919
4457
  {
3920
- name: "id",
3921
- location: "path"
4458
+ name: "cursor",
4459
+ location: "query"
4460
+ },
4461
+ {
4462
+ name: "limit",
4463
+ location: "query"
3922
4464
  }
3923
4465
  ],
3924
4466
  hasBody: false,
@@ -3931,12 +4473,18 @@ var generatedTools = [
3931
4473
  inputSchema: {
3932
4474
  type: "object",
3933
4475
  properties: {
3934
- id: {
4476
+ cursor: {
3935
4477
  type: "string",
3936
- description: "Serverless endpoint identifier"
4478
+ minLength: 1,
4479
+ 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."
4480
+ },
4481
+ limit: {
4482
+ type: "integer",
4483
+ minimum: 1,
4484
+ maximum: 1e3,
4485
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
3937
4486
  }
3938
- },
3939
- required: ["id"]
4487
+ }
3940
4488
  }
3941
4489
  },
3942
4490
  {
@@ -4002,7 +4550,7 @@ var generatedTools = [
4002
4550
  items: {
4003
4551
  $ref: "#/$defs/Product"
4004
4552
  },
4005
- 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."
4553
+ description: "Comma-separated availability product contexts. Supported values: POD, CLUSTER, SERVERLESS. Required with include=AVAILABILITY, and valid only with it (400 either way)."
4006
4554
  },
4007
4555
  count: {
4008
4556
  type: "integer",
@@ -4020,7 +4568,7 @@ var generatedTools = [
4020
4568
  type: "string",
4021
4569
  pattern: "^[A-Z]{2}$"
4022
4570
  },
4023
- 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."
4571
+ 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."
4024
4572
  },
4025
4573
  cudaVersions: {
4026
4574
  type: "array",
@@ -4028,12 +4576,12 @@ var generatedTools = [
4028
4576
  type: "string",
4029
4577
  pattern: "^\\d+\\.\\d+$"
4030
4578
  },
4031
- 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."
4579
+ 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."
4032
4580
  },
4033
4581
  minCudaVersion: {
4034
4582
  type: "string",
4035
4583
  pattern: "^\\d+(\\.\\d+)?$",
4036
- 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."
4584
+ description: "Lowest acceptable CUDA version to scope availability and lowest-price calculations to, compared numerically."
4037
4585
  }
4038
4586
  },
4039
4587
  $defs: {
@@ -4096,12 +4644,12 @@ var generatedTools = [
4096
4644
  startTime: {
4097
4645
  type: "string",
4098
4646
  format: "date-time",
4099
- 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"
4647
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
4100
4648
  },
4101
4649
  endTime: {
4102
4650
  type: "string",
4103
4651
  format: "date-time",
4104
- 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"
4652
+ 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."
4105
4653
  },
4106
4654
  bucketSize: {
4107
4655
  $ref: "#/$defs/BillingBucketSize",
@@ -4110,7 +4658,7 @@ var generatedTools = [
4110
4658
  lastN: {
4111
4659
  type: "integer",
4112
4660
  minimum: 1,
4113
- 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'
4661
+ 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".'
4114
4662
  },
4115
4663
  networkVolumeId: {
4116
4664
  type: "string",
@@ -4138,10 +4686,19 @@ var generatedTools = [
4138
4686
  {
4139
4687
  name: "list-network-volumes",
4140
4688
  operationId: "listNetworkVolumes",
4141
- description: "List network volumes. Returns all network volumes owned by the authenticated user.",
4689
+ 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.",
4142
4690
  method: "GET",
4143
4691
  path: "/v2/network-volumes",
4144
- params: [],
4692
+ params: [
4693
+ {
4694
+ name: "cursor",
4695
+ location: "query"
4696
+ },
4697
+ {
4698
+ name: "limit",
4699
+ location: "query"
4700
+ }
4701
+ ],
4145
4702
  hasBody: false,
4146
4703
  annotations: {
4147
4704
  readOnlyHint: true,
@@ -4151,7 +4708,19 @@ var generatedTools = [
4151
4708
  },
4152
4709
  inputSchema: {
4153
4710
  type: "object",
4154
- properties: {}
4711
+ properties: {
4712
+ cursor: {
4713
+ type: "string",
4714
+ minLength: 1,
4715
+ 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."
4716
+ },
4717
+ limit: {
4718
+ type: "integer",
4719
+ minimum: 1,
4720
+ maximum: 1e3,
4721
+ description: "Page size, 1\u20131000. Defaults to 1000 when omitted."
4722
+ }
4723
+ }
4155
4724
  }
4156
4725
  },
4157
4726
  {
@@ -4195,12 +4764,12 @@ var generatedTools = [
4195
4764
  startTime: {
4196
4765
  type: "string",
4197
4766
  format: "date-time",
4198
- 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"
4767
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
4199
4768
  },
4200
4769
  endTime: {
4201
4770
  type: "string",
4202
4771
  format: "date-time",
4203
- 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"
4772
+ 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."
4204
4773
  },
4205
4774
  bucketSize: {
4206
4775
  $ref: "#/$defs/BillingBucketSize",
@@ -4209,7 +4778,7 @@ var generatedTools = [
4209
4778
  lastN: {
4210
4779
  type: "integer",
4211
4780
  minimum: 1,
4212
- 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'
4781
+ 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".'
4213
4782
  },
4214
4783
  podId: {
4215
4784
  type: "string",
@@ -4237,7 +4806,7 @@ var generatedTools = [
4237
4806
  {
4238
4807
  name: "list-pods",
4239
4808
  operationId: "listPods",
4240
- 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.",
4809
+ 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.",
4241
4810
  method: "GET",
4242
4811
  path: "/v2/pods",
4243
4812
  params: [
@@ -4272,7 +4841,7 @@ var generatedTools = [
4272
4841
  cursor: {
4273
4842
  type: "string",
4274
4843
  minLength: 1,
4275
- 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"
4844
+ 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."
4276
4845
  },
4277
4846
  limit: {
4278
4847
  type: "integer",
@@ -4309,7 +4878,7 @@ var generatedTools = [
4309
4878
  type: "string",
4310
4879
  enum: ["official", "verified", "community"],
4311
4880
  default: "official",
4312
- 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"
4881
+ 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."
4313
4882
  }
4314
4883
  }
4315
4884
  }
@@ -4403,12 +4972,12 @@ var generatedTools = [
4403
4972
  startTime: {
4404
4973
  type: "string",
4405
4974
  format: "date-time",
4406
- 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"
4975
+ description: "Start of the billing period (RFC 3339). Defaults to 30 days ago."
4407
4976
  },
4408
4977
  endTime: {
4409
4978
  type: "string",
4410
4979
  format: "date-time",
4411
- 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"
4980
+ 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."
4412
4981
  },
4413
4982
  bucketSize: {
4414
4983
  $ref: "#/$defs/BillingBucketSize",
@@ -4417,7 +4986,7 @@ var generatedTools = [
4417
4986
  lastN: {
4418
4987
  type: "integer",
4419
4988
  minimum: 1,
4420
- 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'
4989
+ 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".'
4421
4990
  },
4422
4991
  serverlessId: {
4423
4992
  type: "string",
@@ -4556,7 +5125,7 @@ var generatedTools = [
4556
5125
  type: "object",
4557
5126
  additionalProperties: false,
4558
5127
  minProperties: 1,
4559
- 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",
5128
+ 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.",
4560
5129
  properties: {
4561
5130
  name: {
4562
5131
  type: "string",
@@ -4587,6 +5156,7 @@ var generatedTools = [
4587
5156
  idempotentHint: false,
4588
5157
  openWorldHint: true
4589
5158
  },
5159
+ schemaOnDemand: true,
4590
5160
  inputSchema: {
4591
5161
  type: "object",
4592
5162
  properties: {
@@ -4602,11 +5172,10 @@ var generatedTools = [
4602
5172
  $defs: {
4603
5173
  BaseContainerConfig: {
4604
5174
  type: "object",
4605
- 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",
4606
5175
  properties: {
4607
5176
  args: {
4608
5177
  type: "string",
4609
- 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',
5178
+ 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.',
4610
5179
  examples: [
4611
5180
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
4612
5181
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -4618,7 +5187,7 @@ var generatedTools = [
4618
5187
  items: {
4619
5188
  type: "string"
4620
5189
  },
4621
- 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",
5190
+ 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.",
4622
5191
  examples: [
4623
5192
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
4624
5193
  ]
@@ -4628,7 +5197,7 @@ var generatedTools = [
4628
5197
  items: {
4629
5198
  type: "string"
4630
5199
  },
4631
- 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",
5200
+ 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.",
4632
5201
  examples: [["/bin/bash", "-c"]]
4633
5202
  },
4634
5203
  disk: {
@@ -4688,7 +5257,7 @@ var generatedTools = [
4688
5257
  pools: {
4689
5258
  type: "array",
4690
5259
  minItems: 1,
4691
- 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',
5260
+ 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.",
4692
5261
  items: {
4693
5262
  type: "string"
4694
5263
  },
@@ -4697,7 +5266,7 @@ var generatedTools = [
4697
5266
  excludedTypes: {
4698
5267
  type: "array",
4699
5268
  uniqueItems: true,
4700
- 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',
5269
+ 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.",
4701
5270
  items: {
4702
5271
  type: "string",
4703
5272
  pattern: "^\\s*[^-\\s]"
@@ -4714,7 +5283,6 @@ var generatedTools = [
4714
5283
  }
4715
5284
  },
4716
5285
  ContainerConfig: {
4717
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
4718
5286
  allOf: [
4719
5287
  {
4720
5288
  $ref: "#/$defs/BaseContainerConfig"
@@ -4739,8 +5307,42 @@ var generatedTools = [
4739
5307
  ],
4740
5308
  unevaluatedProperties: false
4741
5309
  },
5310
+ EndpointMount: {
5311
+ type: "object",
5312
+ required: ["volumeId"],
5313
+ additionalProperties: false,
5314
+ description: "A volume mounted on every worker, always at `/runpod-volume`. Custom paths are not supported yet.",
5315
+ properties: {
5316
+ volumeId: {
5317
+ type: "string",
5318
+ description: "ID of a NetworkVolume (under `network`) or GlobalVolume (under `global`).",
5319
+ examples: ["vol_abc"]
5320
+ }
5321
+ }
5322
+ },
5323
+ EndpointMountsInput: {
5324
+ type: "object",
5325
+ additionalProperties: false,
5326
+ 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.",
5327
+ properties: {
5328
+ network: {
5329
+ type: "array",
5330
+ description: "Network volumes, at most one per data center.",
5331
+ items: {
5332
+ $ref: "#/$defs/EndpointMount"
5333
+ }
5334
+ },
5335
+ global: {
5336
+ type: "array",
5337
+ maxItems: 1,
5338
+ items: {
5339
+ $ref: "#/$defs/EndpointMount"
5340
+ }
5341
+ }
5342
+ }
5343
+ },
4742
5344
  EndpointScaling: {
4743
- 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",
5345
+ 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.",
4744
5346
  oneOf: [
4745
5347
  {
4746
5348
  $ref: "#/$defs/QueueDelayScaling"
@@ -4777,14 +5379,14 @@ var generatedTools = [
4777
5379
  type: "integer",
4778
5380
  minimum: 1,
4779
5381
  maximum: 3600,
4780
- 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",
5382
+ 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.",
4781
5383
  examples: [5]
4782
5384
  }
4783
5385
  }
4784
5386
  },
4785
5387
  FlashBoot: {
4786
5388
  type: "string",
4787
- description: "FlashBoot cold-start acceleration mode.\n- `OFF` \u2014 disabled\n- `FLASHBOOT` \u2014 enabled\n- `PRIORITY_FLASHBOOT` \u2014 enabled with priority capacity\n",
5389
+ description: "FlashBoot cold-start acceleration mode. `OFF` \u2014 disabled. `FLASHBOOT` \u2014 enabled. `PRIORITY_FLASHBOOT` \u2014 enabled with priority capacity",
4788
5390
  enum: ["OFF", "FLASHBOOT", "PRIORITY_FLASHBOOT"]
4789
5391
  },
4790
5392
  QueueDelayScaling: {
@@ -4810,7 +5412,7 @@ var generatedTools = [
4810
5412
  RequestCountScaling: {
4811
5413
  type: "object",
4812
5414
  additionalProperties: false,
4813
- description: "Scale on concurrent in-flight requests per worker. Required for\nload-balancing endpoints; also selectable for queue-based.\n",
5415
+ description: "Scale on concurrent in-flight requests per worker. Required for load-balancing endpoints; also selectable for queue-based.",
4814
5416
  required: ["type", "requestCount"],
4815
5417
  properties: {
4816
5418
  type: {
@@ -4827,7 +5429,7 @@ var generatedTools = [
4827
5429
  }
4828
5430
  },
4829
5431
  UpdateEndpointGpuConfig: {
4830
- 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",
5432
+ 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.",
4831
5433
  allOf: [
4832
5434
  {
4833
5435
  $ref: "#/$defs/BaseEndpointGpuConfig"
@@ -4844,12 +5446,12 @@ var generatedTools = [
4844
5446
  type: "string",
4845
5447
  pattern: "^\\d+\\.\\d+$"
4846
5448
  },
4847
- 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"
5449
+ 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."
4848
5450
  },
4849
5451
  minCudaVersion: {
4850
5452
  type: "string",
4851
5453
  pattern: "^$|^\\d+\\.\\d+$",
4852
- 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',
5454
+ 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.',
4853
5455
  examples: ["12.1"]
4854
5456
  }
4855
5457
  }
@@ -4870,7 +5472,7 @@ var generatedTools = [
4870
5472
  type: "array",
4871
5473
  minItems: 1,
4872
5474
  uniqueItems: true,
4873
- description: "Complete replacement CPU selection. Valid only for an existing CPU\nendpoint; endpoint compute family cannot be changed.\n",
5475
+ description: "Complete replacement CPU selection. Valid only for an existing CPU endpoint; endpoint compute family cannot be changed.",
4874
5476
  items: {
4875
5477
  $ref: "#/$defs/CreateCpuConfig"
4876
5478
  }
@@ -4896,14 +5498,19 @@ var generatedTools = [
4896
5498
  type: "array",
4897
5499
  items: {
4898
5500
  type: "string"
4899
- }
5501
+ },
5502
+ description: "Network volume IDs. Pass `[]` to detach.",
5503
+ deprecated: true
5504
+ },
5505
+ mounts: {
5506
+ $ref: "#/$defs/EndpointMountsInput"
4900
5507
  },
4901
5508
  scaling: {
4902
5509
  $ref: "#/$defs/EndpointScaling"
4903
5510
  },
4904
5511
  templateId: {
4905
5512
  type: "string",
4906
- 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",
5513
+ 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.",
4907
5514
  examples: ["30zmvf89kd"]
4908
5515
  },
4909
5516
  timeout: {
@@ -4920,6 +5527,57 @@ var generatedTools = [
4920
5527
  }
4921
5528
  }
4922
5529
  },
5530
+ {
5531
+ name: "update-global-volume",
5532
+ operationId: "updateGlobalVolume",
5533
+ description: "Update a global volume. Updates mutable fields on a global volume. Only provided fields are changed.",
5534
+ method: "PATCH",
5535
+ path: "/v2/global-volumes/{id}",
5536
+ params: [
5537
+ {
5538
+ name: "id",
5539
+ location: "path"
5540
+ }
5541
+ ],
5542
+ hasBody: true,
5543
+ annotations: {
5544
+ readOnlyHint: false,
5545
+ destructiveHint: false,
5546
+ idempotentHint: false,
5547
+ openWorldHint: true
5548
+ },
5549
+ inputSchema: {
5550
+ type: "object",
5551
+ properties: {
5552
+ id: {
5553
+ type: "string",
5554
+ description: "Global volume identifier"
5555
+ },
5556
+ body: {
5557
+ $ref: "#/$defs/UpdateGlobalVolumeRequest"
5558
+ }
5559
+ },
5560
+ required: ["id", "body"],
5561
+ $defs: {
5562
+ UpdateGlobalVolumeRequest: {
5563
+ type: "object",
5564
+ additionalProperties: false,
5565
+ minProperties: 1,
5566
+ description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
5567
+ properties: {
5568
+ name: {
5569
+ type: "string",
5570
+ minLength: 1,
5571
+ maxLength: 191,
5572
+ pattern: "[^\\p{Z}\\t-\\r\x85]",
5573
+ description: "New human-readable name, 1\u2013191 characters with at least one non-whitespace character. Leading and trailing whitespace is trimmed.",
5574
+ examples: ["renamed-volume"]
5575
+ }
5576
+ }
5577
+ }
5578
+ }
5579
+ }
5580
+ },
4923
5581
  {
4924
5582
  name: "update-network-volume",
4925
5583
  operationId: "updateNetworkVolume",
@@ -4956,7 +5614,7 @@ var generatedTools = [
4956
5614
  type: "object",
4957
5615
  additionalProperties: false,
4958
5616
  minProperties: 1,
4959
- description: "Only the provided fields are updated. At least one field must be\npresent; an empty body is rejected.\n",
5617
+ description: "Only the provided fields are updated. At least one field must be present; an empty body is rejected.",
4960
5618
  properties: {
4961
5619
  name: {
4962
5620
  type: "string",
@@ -4968,7 +5626,7 @@ var generatedTools = [
4968
5626
  type: "integer",
4969
5627
  minimum: 10,
4970
5628
  maximum: 4096,
4971
- description: "New size in GB. Must be greater than or equal to the current size \u2014\nnetwork volume storage cannot be reduced.\n",
5629
+ description: "New size in GB. Must be greater than or equal to the current size \u2014 network volume storage cannot be reduced.",
4972
5630
  examples: [100]
4973
5631
  }
4974
5632
  }
@@ -4995,6 +5653,7 @@ var generatedTools = [
4995
5653
  idempotentHint: false,
4996
5654
  openWorldHint: true
4997
5655
  },
5656
+ schemaOnDemand: true,
4998
5657
  inputSchema: {
4999
5658
  type: "object",
5000
5659
  properties: {
@@ -5010,11 +5669,10 @@ var generatedTools = [
5010
5669
  $defs: {
5011
5670
  BaseContainerConfig: {
5012
5671
  type: "object",
5013
- 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",
5014
5672
  properties: {
5015
5673
  args: {
5016
5674
  type: "string",
5017
- 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',
5675
+ 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.',
5018
5676
  examples: [
5019
5677
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
5020
5678
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -5026,7 +5684,7 @@ var generatedTools = [
5026
5684
  items: {
5027
5685
  type: "string"
5028
5686
  },
5029
- 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",
5687
+ 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.",
5030
5688
  examples: [
5031
5689
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
5032
5690
  ]
@@ -5036,7 +5694,7 @@ var generatedTools = [
5036
5694
  items: {
5037
5695
  type: "string"
5038
5696
  },
5039
- 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",
5697
+ 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.",
5040
5698
  examples: [["/bin/bash", "-c"]]
5041
5699
  },
5042
5700
  disk: {
@@ -5072,8 +5730,22 @@ var generatedTools = [
5072
5730
  }
5073
5731
  }
5074
5732
  },
5733
+ BaseMounts: {
5734
+ type: "object",
5735
+ properties: {
5736
+ persistent: {
5737
+ $ref: "#/$defs/PersistentMount"
5738
+ },
5739
+ network: {
5740
+ type: "array",
5741
+ maxItems: 1,
5742
+ items: {
5743
+ $ref: "#/$defs/NetworkMount"
5744
+ }
5745
+ }
5746
+ }
5747
+ },
5075
5748
  ContainerConfig: {
5076
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
5077
5749
  allOf: [
5078
5750
  {
5079
5751
  $ref: "#/$defs/BaseContainerConfig"
@@ -5090,28 +5762,51 @@ var generatedTools = [
5090
5762
  }
5091
5763
  ]
5092
5764
  },
5093
- Mounts: {
5765
+ GlobalMount: {
5094
5766
  type: "object",
5767
+ required: ["volumeId"],
5095
5768
  additionalProperties: false,
5096
- 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",
5769
+ 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.",
5097
5770
  properties: {
5098
- persistent: {
5099
- $ref: "#/$defs/PersistentMount"
5771
+ volumeId: {
5772
+ type: "string",
5773
+ description: "ID of an existing GlobalVolume.",
5774
+ examples: ["gsb_abc"]
5100
5775
  },
5101
- network: {
5102
- type: "array",
5103
- maxItems: 1,
5104
- items: {
5105
- $ref: "#/$defs/NetworkMount"
5106
- }
5776
+ path: {
5777
+ type: "string",
5778
+ 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.",
5779
+ examples: ["/data"]
5107
5780
  }
5108
5781
  }
5109
5782
  },
5783
+ Mounts: {
5784
+ 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.",
5785
+ allOf: [
5786
+ {
5787
+ $ref: "#/$defs/BaseMounts"
5788
+ },
5789
+ {
5790
+ type: "object",
5791
+ properties: {
5792
+ global: {
5793
+ type: "array",
5794
+ maxItems: 1,
5795
+ 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.",
5796
+ items: {
5797
+ $ref: "#/$defs/GlobalMount"
5798
+ }
5799
+ }
5800
+ }
5801
+ }
5802
+ ],
5803
+ unevaluatedProperties: false
5804
+ },
5110
5805
  NetworkMount: {
5111
5806
  type: "object",
5112
5807
  required: ["volumeId", "path"],
5113
5808
  additionalProperties: false,
5114
- 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",
5809
+ 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.",
5115
5810
  properties: {
5116
5811
  volumeId: {
5117
5812
  type: "string",
@@ -5129,7 +5824,7 @@ var generatedTools = [
5129
5824
  type: "object",
5130
5825
  required: ["size", "path"],
5131
5826
  additionalProperties: false,
5132
- 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",
5827
+ 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.",
5133
5828
  properties: {
5134
5829
  size: {
5135
5830
  type: "integer",
@@ -5154,7 +5849,7 @@ var generatedTools = [
5154
5849
  properties: {
5155
5850
  globalNetworking: {
5156
5851
  type: "boolean",
5157
- 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."
5852
+ 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)."
5158
5853
  },
5159
5854
  locked: {
5160
5855
  type: "boolean",
@@ -5169,7 +5864,7 @@ var generatedTools = [
5169
5864
  },
5170
5865
  templateId: {
5171
5866
  type: "string",
5172
- 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",
5867
+ 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.",
5173
5868
  examples: ["30zmvf89kd"]
5174
5869
  }
5175
5870
  }
@@ -5199,6 +5894,7 @@ var generatedTools = [
5199
5894
  idempotentHint: false,
5200
5895
  openWorldHint: true
5201
5896
  },
5897
+ schemaOnDemand: true,
5202
5898
  inputSchema: {
5203
5899
  type: "object",
5204
5900
  properties: {
@@ -5216,7 +5912,7 @@ var generatedTools = [
5216
5912
  type: "object",
5217
5913
  additionalProperties: false,
5218
5914
  minProperties: 1,
5219
- description: "Only the provided fields are updated; at least one field is required.\nThe secret's `name` is immutable.\n",
5915
+ description: "Only the provided fields are updated; at least one field is required. The secret's `name` is immutable.",
5220
5916
  properties: {
5221
5917
  value: {
5222
5918
  type: "string",
@@ -5268,7 +5964,7 @@ var generatedTools = [
5268
5964
  type: "string",
5269
5965
  pattern: "^(ssh|ecdsa|sk)-[^\\s]+ [^\\s]+([ \\t][^\\n\\r]*)?$"
5270
5966
  },
5271
- 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",
5967
+ 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.",
5272
5968
  examples: [
5273
5969
  [
5274
5970
  "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILXGDN/SclOozk1xsDztpmhGiKkkrfQB9SKoO8dSIQQZ me@example.com"
@@ -5299,6 +5995,7 @@ var generatedTools = [
5299
5995
  idempotentHint: false,
5300
5996
  openWorldHint: true
5301
5997
  },
5998
+ schemaOnDemand: true,
5302
5999
  inputSchema: {
5303
6000
  type: "object",
5304
6001
  properties: {
@@ -5313,11 +6010,10 @@ var generatedTools = [
5313
6010
  $defs: {
5314
6011
  BaseContainerConfig: {
5315
6012
  type: "object",
5316
- 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",
5317
6013
  properties: {
5318
6014
  args: {
5319
6015
  type: "string",
5320
- 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',
6016
+ 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.',
5321
6017
  examples: [
5322
6018
  "--model meta-llama/Llama-3-8B --max-model-len 8192",
5323
6019
  '{"entrypoint":["/bin/bash","-c"],"cmd":["python -u main.py"]}',
@@ -5329,7 +6025,7 @@ var generatedTools = [
5329
6025
  items: {
5330
6026
  type: "string"
5331
6027
  },
5332
- 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",
6028
+ 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.",
5333
6029
  examples: [
5334
6030
  ["--model", "meta-llama/Llama-3-8B", "--max-model-len", "8192"]
5335
6031
  ]
@@ -5339,7 +6035,7 @@ var generatedTools = [
5339
6035
  items: {
5340
6036
  type: "string"
5341
6037
  },
5342
- 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",
6038
+ 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.",
5343
6039
  examples: [["/bin/bash", "-c"]]
5344
6040
  },
5345
6041
  disk: {
@@ -5376,7 +6072,6 @@ var generatedTools = [
5376
6072
  }
5377
6073
  },
5378
6074
  ContainerConfig: {
5379
- description: "Reusable container configuration shared across templates, pods, and serverless endpoints. Adding a field here automatically propagates to all three resources.\n",
5380
6075
  allOf: [
5381
6076
  {
5382
6077
  $ref: "#/$defs/BaseContainerConfig"
@@ -5397,7 +6092,7 @@ var generatedTools = [
5397
6092
  type: "object",
5398
6093
  required: ["size", "path"],
5399
6094
  additionalProperties: false,
5400
- 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",
6095
+ 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.",
5401
6096
  properties: {
5402
6097
  size: {
5403
6098
  type: "integer",
@@ -5414,13 +6109,13 @@ var generatedTools = [
5414
6109
  },
5415
6110
  TemplateCategory: {
5416
6111
  type: "string",
5417
- 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",
6112
+ 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",
5418
6113
  enum: ["CPU", "NVIDIA", "AMD"]
5419
6114
  },
5420
6115
  TemplateMounts: {
5421
6116
  type: "object",
5422
6117
  additionalProperties: false,
5423
- 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",
6118
+ 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.",
5424
6119
  properties: {
5425
6120
  persistent: {
5426
6121
  $ref: "#/$defs/PersistentMount"
@@ -6968,7 +7663,7 @@ var listTemplates = {
6968
7663
  // src/specgen/tools/logs.ts
6969
7664
  init_hosts();
6970
7665
 
6971
- // node_modules/.pnpm/@runpod+typescript-api-sdk@0.2.0/node_modules/@runpod/typescript-api-sdk/dist/index.js
7666
+ // node_modules/.pnpm/@runpod+typescript-api-sdk@0.3.0/node_modules/@runpod/typescript-api-sdk/dist/index.js
6972
7667
  var PATH_PARAM_RE = /\{[^{}]+\}/g;
6973
7668
  var supportsRequestInitExt = () => {
6974
7669
  return typeof process === "object" && Number.parseInt(process?.versions?.node?.substring(0, 2)) >= 18 && process.versions.undici;
@@ -8656,6 +9351,7 @@ var source = {
8656
9351
  var listGuides = {
8657
9352
  name: "list-guides",
8658
9353
  annotations: bundledRead,
9354
+ recordedArguments: ["kind", "query", "concept"],
8659
9355
  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.",
8660
9356
  inputSchema: {
8661
9357
  type: "object",
@@ -8691,6 +9387,8 @@ var listGuides = {
8691
9387
  var readGuide = {
8692
9388
  name: "read-guide",
8693
9389
  annotations: bundledRead,
9390
+ recordedArguments: ["id"],
9391
+ recordedGuideKind: (args) => knowledge.guides.find((guide) => guide.id === String(args.id).trim())?.kind,
8694
9392
  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").',
8695
9393
  inputSchema: {
8696
9394
  type: "object",
@@ -8728,6 +9426,7 @@ var readGuide = {
8728
9426
  var lookupConcept = {
8729
9427
  name: "lookup-concept",
8730
9428
  annotations: bundledRead,
9429
+ recordedArguments: ["concept"],
8731
9430
  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".',
8732
9431
  inputSchema: {
8733
9432
  type: "object",
@@ -8780,6 +9479,7 @@ var lookupConcept = {
8780
9479
  var searchConcepts = {
8781
9480
  name: "search-concepts",
8782
9481
  annotations: bundledRead,
9482
+ recordedArguments: ["query", "limit"],
8783
9483
  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.',
8784
9484
  inputSchema: {
8785
9485
  type: "object",
@@ -8844,6 +9544,140 @@ var guideResources = knowledge.guides.map((guide) => ({
8844
9544
  text: guide.body
8845
9545
  }));
8846
9546
 
9547
+ // src/specgen/prose.ts
9548
+ var ABBREVIATIONS = ["e.g.", "i.e.", "etc.", "vs."];
9549
+ var DOT = "\0";
9550
+ function splitSentences(text) {
9551
+ let guarded = text;
9552
+ for (const abbreviation of ABBREVIATIONS) {
9553
+ guarded = guarded.replaceAll(
9554
+ abbreviation,
9555
+ abbreviation.replaceAll(".", DOT)
9556
+ );
9557
+ }
9558
+ return guarded.split(/(?<=[.!?])\s+(?=[A-Z`"'([])/).map((sentence) => sentence.replaceAll(DOT, ".").trim()).filter(Boolean);
9559
+ }
9560
+ function leadSentence(text) {
9561
+ return splitSentences(text)[0] ?? text;
9562
+ }
9563
+
9564
+ // src/specgen/on-demand.ts
9565
+ var DESCRIBE_TOOL_NAME = "describe-tool";
9566
+ var ON_DEMAND_POINTER = ` Call ${DESCRIBE_TOOL_NAME} for the full argument schema before the first use.`;
9567
+ function listedAnnotations(annotations) {
9568
+ const { idempotentHint, ...listed } = annotations;
9569
+ return idempotentHint ? annotations : listed;
9570
+ }
9571
+ function listTool(tool) {
9572
+ const { name, description, inputSchema } = tool;
9573
+ const annotations = listedAnnotations(tool.annotations);
9574
+ if (!tool.schemaOnDemand)
9575
+ return { name, description, inputSchema, annotations };
9576
+ return {
9577
+ name,
9578
+ description: description + ON_DEMAND_POINTER,
9579
+ inputSchema: callShape(inputSchema),
9580
+ annotations
9581
+ };
9582
+ }
9583
+ function callShape(schema) {
9584
+ const defs = schema.$defs ?? {};
9585
+ const resolve2 = (node) => {
9586
+ if (typeof node.$ref !== "string") return node;
9587
+ const name = node.$ref.split("/").pop() ?? "";
9588
+ return defs[name] ?? {};
9589
+ };
9590
+ const properties = Object.fromEntries(
9591
+ Object.entries(schema.properties ?? {}).map(
9592
+ ([key, value]) => [key, shallowProperty(resolve2(value), resolve2)]
9593
+ )
9594
+ );
9595
+ return {
9596
+ type: "object",
9597
+ properties,
9598
+ ...schema.required ? { required: schema.required } : {}
9599
+ };
9600
+ }
9601
+ function shallowProperty(node, resolve2) {
9602
+ const out = {};
9603
+ const type = schemaType(node, resolve2);
9604
+ if (type !== void 0) out.type = type;
9605
+ if (type === "array") {
9606
+ const items = resolve2(node.items ?? {});
9607
+ const itemType = schemaType(items, resolve2);
9608
+ if (itemType !== void 0) out.items = { type: itemType };
9609
+ }
9610
+ if (Array.isArray(node.enum)) out.enum = node.enum;
9611
+ if (typeof node.description === "string")
9612
+ out.description = leadSentence(node.description);
9613
+ return out;
9614
+ }
9615
+ function schemaType(node, resolve2) {
9616
+ if (node.type !== void 0) return node.type;
9617
+ if (node.properties || node.allOf) return "object";
9618
+ const branches2 = node.anyOf ?? node.oneOf;
9619
+ const allObjects = branches2?.every(
9620
+ (branch) => schemaType(resolve2(branch), resolve2) === "object"
9621
+ ) ?? false;
9622
+ return allObjects ? "object" : void 0;
9623
+ }
9624
+
9625
+ // src/specgen/tools/describe.ts
9626
+ function createDescribeTool(served) {
9627
+ return {
9628
+ name: DESCRIBE_TOOL_NAME,
9629
+ 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.",
9630
+ inputSchema: {
9631
+ type: "object",
9632
+ properties: {
9633
+ name: {
9634
+ type: "string",
9635
+ description: "A tool name exactly as this server's tool list spells it."
9636
+ }
9637
+ },
9638
+ required: ["name"]
9639
+ },
9640
+ annotations: {
9641
+ readOnlyHint: true,
9642
+ destructiveHint: false,
9643
+ idempotentHint: true,
9644
+ openWorldHint: false
9645
+ },
9646
+ handler: async (_ctx, args) => {
9647
+ const name = String(args.name ?? "");
9648
+ const tools = served();
9649
+ const tool = tools.find((candidate) => candidate.name === name);
9650
+ if (!tool) {
9651
+ return {
9652
+ ok: false,
9653
+ status: 404,
9654
+ payload: {
9655
+ error: `No tool named ${name}.`,
9656
+ suggestions: similarNames(
9657
+ name,
9658
+ tools.map((candidate) => candidate.name)
9659
+ ),
9660
+ hint: "Tool names are exactly as this server lists them; the suggestions share a word with the name you sent."
9661
+ }
9662
+ };
9663
+ }
9664
+ const { description, inputSchema, annotations } = tool;
9665
+ return ok({ name: tool.name, description, inputSchema, annotations });
9666
+ }
9667
+ };
9668
+ }
9669
+ function similarNames(query, names) {
9670
+ const wanted = new Set(words2(query));
9671
+ return names.map((name) => {
9672
+ const own = new Set(words2(name));
9673
+ const shared = [...own].filter((word) => wanted.has(word)).length;
9674
+ return { name, score: shared / (/* @__PURE__ */ new Set([...own, ...wanted])).size };
9675
+ }).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || a.name.localeCompare(b.name)).slice(0, 8).map((entry) => entry.name);
9676
+ }
9677
+ function words2(name) {
9678
+ return name.toLowerCase().split(/[-_\s]+/).filter(Boolean).map((word) => word.replace(/s$/, ""));
9679
+ }
9680
+
8847
9681
  // src/specgen/analytics.ts
8848
9682
  var import_node_crypto = require("crypto");
8849
9683
  var CAPTURE_TIMEOUT_MS = 3e3;
@@ -8858,7 +9692,22 @@ function analyticsCallerId(apiKey, accountId) {
8858
9692
  const prefix = accountId ? "a:" : "k:";
8859
9693
  return prefix + (0, import_node_crypto.createHmac)("sha256", salt).update(identity).digest("hex").slice(0, 16);
8860
9694
  }
8861
- function captureToolCall(apiKey, event) {
9695
+ var MAX_RECORDED_VALUE_LENGTH = 200;
9696
+ function bounded(value) {
9697
+ return value.slice(0, MAX_RECORDED_VALUE_LENGTH);
9698
+ }
9699
+ function recordedArgumentValues(names, args) {
9700
+ if (!names?.length) return void 0;
9701
+ const values = {};
9702
+ for (const name of names) {
9703
+ const value = args[name];
9704
+ if (typeof value === "string" || typeof value === "number") {
9705
+ values[name] = bounded(String(value).trim());
9706
+ }
9707
+ }
9708
+ return Object.keys(values).length ? values : void 0;
9709
+ }
9710
+ function capture(event, distinctId, properties) {
8862
9711
  const posthogKey = process.env.POSTHOG_API_KEY;
8863
9712
  if (!posthogKey) return;
8864
9713
  void fetch(`${posthogHost()}/capture/`, {
@@ -8866,18 +9715,12 @@ function captureToolCall(apiKey, event) {
8866
9715
  headers: { "Content-Type": "application/json" },
8867
9716
  body: JSON.stringify({
8868
9717
  api_key: posthogKey,
8869
- event: "runpod_mcp_tool_call",
8870
- distinct_id: analyticsCallerId(apiKey, event.accountId),
9718
+ event,
9719
+ distinct_id: distinctId,
8871
9720
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
8872
9721
  properties: {
8873
- tool: event.tool,
8874
- ok: event.ok,
8875
- status: event.status,
8876
- duration_ms: event.durationMs,
8877
- transport: event.transport,
8878
- server_version: event.serverVersion,
9722
+ ...properties,
8879
9723
  surface_version: SURFACE_VERSION,
8880
- ...event.clientName ? { client_name: event.clientName } : {},
8881
9724
  $process_person_profile: false
8882
9725
  }
8883
9726
  }),
@@ -8885,9 +9728,44 @@ function captureToolCall(apiKey, event) {
8885
9728
  }).catch(() => {
8886
9729
  });
8887
9730
  }
9731
+ function captureToolCall(apiKey, event) {
9732
+ const args = Object.fromEntries(
9733
+ Object.entries(event.args ?? {}).map(([name, value]) => [
9734
+ `arg_${name}`,
9735
+ value
9736
+ ])
9737
+ );
9738
+ capture("runpod_mcp_tool_call", analyticsCallerId(apiKey, event.accountId), {
9739
+ tool: event.tool,
9740
+ ok: event.ok,
9741
+ status: event.status,
9742
+ duration_ms: event.durationMs,
9743
+ transport: event.transport,
9744
+ server_version: event.serverVersion,
9745
+ ...event.clientName ? { client_name: event.clientName } : {},
9746
+ ...event.guideKind ? { guide_kind: event.guideKind } : {},
9747
+ ...event.blockedBy ? { blocked_by: event.blockedBy } : {},
9748
+ ...args
9749
+ });
9750
+ }
9751
+ function captureResourceCall(apiKey, event) {
9752
+ capture(
9753
+ "runpod_mcp_resource_call",
9754
+ analyticsCallerId(apiKey, event.accountId),
9755
+ {
9756
+ method: event.method,
9757
+ uri: bounded(event.uri),
9758
+ ok: event.ok,
9759
+ transport: event.transport,
9760
+ server_version: event.serverVersion,
9761
+ ...event.clientName ? { client_name: event.clientName } : {}
9762
+ }
9763
+ );
9764
+ }
8888
9765
 
8889
9766
  // src/http.ts
8890
9767
  var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
9768
+ var import_types = require("@modelcontextprotocol/sdk/types.js");
8891
9769
 
8892
9770
  // src/specgen/context.ts
8893
9771
  var import_node_crypto2 = require("crypto");
@@ -9053,14 +9931,26 @@ function createToolContext(options = {}) {
9053
9931
  };
9054
9932
  }
9055
9933
 
9056
- // src/_shared/credential-check.ts
9934
+ // src/specgen/ops.ts
9057
9935
  var import_node_crypto3 = require("crypto");
9936
+ var SALT = (0, import_node_crypto3.randomBytes)(16).toString("hex");
9937
+ function callerId(token) {
9938
+ if (!token) return "anonymous";
9939
+ return (0, import_node_crypto3.createHash)("sha256").update(SALT).update(token).digest("hex").slice(0, 12);
9940
+ }
9941
+ var noopRateLimiter = async () => ({ allowed: true });
9942
+ function logToolCall(entry) {
9943
+ console.error("tool_call", JSON.stringify(entry));
9944
+ }
9945
+
9946
+ // src/_shared/credential-check.ts
9947
+ var import_node_crypto4 = require("crypto");
9058
9948
  var VALID_TTL_MS = 6e4;
9059
9949
  var INVALID_TTL_MS = 3e4;
9060
9950
  var REQUEST_TIMEOUT_MS = 4e3;
9061
9951
  var MAX_CACHE_ENTRIES = 1e3;
9062
9952
  function hashToken(token) {
9063
- return (0, import_node_crypto3.createHash)("sha256").update(token).digest("hex");
9953
+ return (0, import_node_crypto4.createHash)("sha256").update(token).digest("hex");
9064
9954
  }
9065
9955
  function createCredentialChecker(deps) {
9066
9956
  const now = deps.now ?? (() => performance.now());
@@ -9384,18 +10274,6 @@ function createAlpTools(opts) {
9384
10274
  return [reportFeedback, saveToJournal, askQuestion, readJournalTool];
9385
10275
  }
9386
10276
 
9387
- // src/specgen/ops.ts
9388
- var import_node_crypto4 = require("crypto");
9389
- var SALT = (0, import_node_crypto4.randomBytes)(16).toString("hex");
9390
- function callerId(token) {
9391
- if (!token) return "anonymous";
9392
- return (0, import_node_crypto4.createHash)("sha256").update(SALT).update(token).digest("hex").slice(0, 12);
9393
- }
9394
- var noopRateLimiter = async () => ({ allowed: true });
9395
- function logToolCall(entry) {
9396
- console.error("tool_call", JSON.stringify(entry));
9397
- }
9398
-
9399
10277
  // src/specgen/server.ts
9400
10278
  var curatedTools = [
9401
10279
  listEndpoints,
@@ -9424,14 +10302,21 @@ PLAYBOOKS \u2014 READ BEFORE ACTING. Each Runpod task has a journey playbook car
9424
10302
 
9425
10303
  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.
9426
10304
 
9427
- 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.`;
10305
+ 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.`;
9428
10306
  var ALP_INSTRUCTIONS = `
9429
10307
 
9430
10308
  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.`;
9431
10309
  function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9432
10310
  const rateLimiter = opts.rateLimiter ?? noopRateLimiter;
9433
10311
  const caller = callerId(ctx.apiKey);
9434
- const servedCuratedTools = opts.alp ? [...curatedTools, ...createAlpTools(opts.alp)] : curatedTools;
10312
+ const configuredCuratedTools = opts.alp ? [...curatedTools, ...createAlpTools(opts.alp)] : curatedTools;
10313
+ const servedCuratedTools = [
10314
+ ...configuredCuratedTools,
10315
+ createDescribeTool(() => [...servedCuratedTools, ...generatedTools])
10316
+ ];
10317
+ const callShapeOnlyTools = new Set(
10318
+ [...servedCuratedTools, ...generatedTools].filter((tool) => tool.schemaOnDemand).map((tool) => tool.name)
10319
+ );
9435
10320
  const server = new import_server3.Server(
9436
10321
  { name: SERVER_NAME, version: `${version} [specgen]` },
9437
10322
  {
@@ -9449,36 +10334,27 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9449
10334
  }
9450
10335
  }
9451
10336
  };
9452
- server.setRequestHandler(import_types.ListToolsRequestSchema, async () => ({
9453
- tools: [
9454
- ...servedCuratedTools.map(
9455
- ({ name, description, inputSchema, annotations }) => ({
9456
- name,
9457
- description,
9458
- inputSchema,
9459
- annotations
9460
- })
9461
- ),
9462
- ...generatedTools.map(
9463
- ({ name, description, inputSchema, annotations }) => ({
9464
- name,
9465
- description,
9466
- inputSchema,
9467
- annotations
9468
- })
9469
- )
9470
- ]
10337
+ server.setRequestHandler(import_types2.ListToolsRequestSchema, async () => ({
10338
+ tools: [...servedCuratedTools, ...generatedTools].map(listTool)
9471
10339
  }));
9472
- server.setRequestHandler(import_types.ListResourcesRequestSchema, async () => ({
10340
+ server.setRequestHandler(import_types2.ListResourcesRequestSchema, async () => ({
9473
10341
  resources: guideResources.map(({ text: _text, ...resource }) => resource)
9474
10342
  }));
9475
- server.setRequestHandler(import_types.ListResourceTemplatesRequestSchema, async () => ({
10343
+ server.setRequestHandler(import_types2.ListResourceTemplatesRequestSchema, async () => ({
9476
10344
  resourceTemplates: []
9477
10345
  }));
9478
- server.setRequestHandler(import_types.ReadResourceRequestSchema, async (request) => {
10346
+ server.setRequestHandler(import_types2.ReadResourceRequestSchema, async (request) => {
9479
10347
  const guide = guideResources.find(
9480
10348
  (candidate) => candidate.uri === request.params.uri
9481
10349
  );
10350
+ if (opts.analytics) {
10351
+ captureResourceCall(ctx.apiKey, {
10352
+ method: "resources/read",
10353
+ uri: guide ? guide.uri : "unknown",
10354
+ ok: Boolean(guide),
10355
+ ...opts.analytics
10356
+ });
10357
+ }
9482
10358
  if (!guide) {
9483
10359
  throw new Error(
9484
10360
  `Unknown resource ${request.params.uri}. Resources are the plugin guides under ${GUIDE_URI_PREFIX}; call list-guides for the guide ids.`
@@ -9490,7 +10366,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9490
10366
  ]
9491
10367
  };
9492
10368
  });
9493
- server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
10369
+ server.setRequestHandler(import_types2.CallToolRequestSchema, async (request) => {
9494
10370
  const args = request.params.arguments ?? {};
9495
10371
  const verdict = await rateLimiter(caller, request.params.name);
9496
10372
  if (!verdict.allowed) {
@@ -9501,6 +10377,16 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9501
10377
  status: 429,
9502
10378
  durationMs: 0
9503
10379
  });
10380
+ if (opts.analytics) {
10381
+ captureToolCall(ctx.apiKey, {
10382
+ tool: request.params.name,
10383
+ ok: false,
10384
+ status: 429,
10385
+ durationMs: 0,
10386
+ ...verdict.reason ? { blockedBy: verdict.reason } : {},
10387
+ ...opts.analytics
10388
+ });
10389
+ }
9504
10390
  return {
9505
10391
  content: [
9506
10392
  {
@@ -9538,11 +10424,14 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9538
10424
  ok: result.ok,
9539
10425
  status: result.status,
9540
10426
  durationMs: Date.now() - startedAt,
10427
+ args: recordedArgumentValues(curated?.recordedArguments, args),
10428
+ guideKind: curated?.recordedGuideKind?.(args),
9541
10429
  ...opts.analytics
9542
10430
  });
9543
10431
  }
10432
+ opts.onToolResult?.(request.params.name, result.status);
9544
10433
  if (!result.ok && result.status === 401) opts.onUnauthorized?.();
9545
- const payload = result.ok ? result.payload : withRecoveryHint(result);
10434
+ const payload = result.ok ? result.payload : withRecoveryHint(result, callShapeOnlyTools.has(request.params.name));
9546
10435
  return {
9547
10436
  content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
9548
10437
  isError: !result.ok
@@ -9560,9 +10449,29 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9560
10449
  }
9561
10450
  return server;
9562
10451
  }
9563
- function errorHint(status) {
9564
- if (status === 400 || status === 422)
9565
- 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.";
10452
+ 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.";
10453
+ var DETAIL_HINTS = [
10454
+ [
10455
+ /no longer any instances available|not enough free GPUs/i,
10456
+ "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."
10457
+ ],
10458
+ [
10459
+ /at least \$\d|insufficient (?:funds|balance)|\bbalance\b/i,
10460
+ "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."
10461
+ ],
10462
+ [
10463
+ /mutually exclusive|cannot be combined with/i,
10464
+ "Two arguments conflict: send only one of them and leave the other out entirely. Do not change anything else."
10465
+ ]
10466
+ ];
10467
+ function errorHint(status, detail) {
10468
+ if (status === 400 && detail) {
10469
+ for (const [pattern, hint] of DETAIL_HINTS)
10470
+ if (pattern.test(detail)) return hint;
10471
+ if (/^unauthorized$/i.test(detail.trim()))
10472
+ 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.";
10473
+ }
10474
+ if (status === 400 || status === 422) return SHAPE_HINT;
9566
10475
  if (status === 401) return "The Runpod API key is missing or invalid.";
9567
10476
  if (status === 402)
9568
10477
  return "Check your account balance in the Runpod console before retrying billable operations; list-billing shows the recent spend behind it.";
@@ -9576,13 +10485,23 @@ function errorHint(status) {
9576
10485
  return "Upstream Runpod error: retry once; if it persists, report it as an API-side failure.";
9577
10486
  return void 0;
9578
10487
  }
9579
- function withRecoveryHint(result) {
10488
+ var CALL_SHAPE_ONLY_HINT = `This tool lists its call shape only; ${DESCRIBE_TOOL_NAME} serves the full argument schema.`;
10489
+ function withRecoveryHint(result, listsCallShapeOnly = false) {
9580
10490
  const payload = asRecord(result.payload) ?? { error: result.payload };
10491
+ const hint = payload.hint ?? asRecord(payload.detail)?.hint ?? errorHint(result.status, detailText(payload));
10492
+ const shapeRejected = result.status === 400 || result.status === 422;
9581
10493
  return {
9582
10494
  ...payload,
9583
- hint: payload.hint ?? asRecord(payload.detail)?.hint ?? errorHint(result.status)
10495
+ hint: listsCallShapeOnly && shapeRejected ? [hint, CALL_SHAPE_ONLY_HINT].filter(Boolean).join(" ") : hint
9584
10496
  };
9585
10497
  }
10498
+ function detailText(payload) {
10499
+ if (Array.isArray(payload.errors) && payload.errors.length) return void 0;
10500
+ const detail = payload.detail ?? payload.error;
10501
+ if (typeof detail === "string") return detail;
10502
+ const nested = asRecord(detail);
10503
+ return typeof nested?.detail === "string" ? nested.detail : void 0;
10504
+ }
9586
10505
  function asRecord(value) {
9587
10506
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
9588
10507
  }
@@ -9601,8 +10520,23 @@ async function main(apiKey) {
9601
10520
  );
9602
10521
  await server.connect(new import_stdio.StdioServerTransport());
9603
10522
  }
9604
- if (isWizardMode) {
9605
- Promise.resolve().then(() => (init_wizard(), wizard_exports)).then(({ runWizard: runWizard2 }) => runWizard2(subcommand)).catch((error) => {
10523
+ if (subcommand === "--version" || subcommand === "-v") {
10524
+ console.log(SERVER_VERSION);
10525
+ } else if (subcommand === "--help" || subcommand === "-h") {
10526
+ console.log(
10527
+ [
10528
+ `@runpod/mcp-server ${SERVER_VERSION}`,
10529
+ "",
10530
+ "Usage:",
10531
+ " RUNPOD_API_KEY=<key> npx @runpod/mcp-server@latest run the MCP server over stdio",
10532
+ " npx @runpod/mcp-server@latest add [--client <ids>] configure your agents (see add --help)",
10533
+ " npx @runpod/mcp-server@latest remove --client <ids> remove Runpod from your agents"
10534
+ ].join("\n")
10535
+ );
10536
+ } else if (isWizardMode) {
10537
+ Promise.resolve().then(() => (init_wizard(), wizard_exports)).then(async ({ runWizard: runWizard2 }) => {
10538
+ process.exitCode = await runWizard2(subcommand);
10539
+ }).catch((error) => {
9606
10540
  console.error(error instanceof Error ? error.message : String(error));
9607
10541
  process.exit(1);
9608
10542
  });