@runpod/mcp-server 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @runpod/mcp-server
2
2
 
3
+ ## 3.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - bae0e5d: Expose the REST v2 CUDA host constraints on create-pod, create-endpoint, and update-endpoint (`allowedCudaVersions`, `minCudaVersion`, nested under `gpu.*` on the wire per rphttp2 2.9.0), support CUDA-only endpoint patches without resending `gpuPoolIds`, warn when a `gpuPoolIds` update clears GPU-type exclusions set elsewhere, and add the `minCudaVersion` availability filter to list-gpu-types. Requires a server running rphttp2 2.9.0 or later for the new fields.
8
+
3
9
  ## 3.3.0
4
10
 
5
11
  ### Minor Changes
@@ -380,10 +380,15 @@ function containerConfigToV2(p) {
380
380
  mounts: persistentMount(p.volumeInGb, p.volumeMountPath)
381
381
  });
382
382
  }
383
- function gpuConfigToV2(gpuTypeIds, gpuCount) {
383
+ function gpuConfigToV2(gpuTypeIds, gpuCount, allowedCudaVersions, minCudaVersion2) {
384
384
  const id = gpuTypeIds?.[0];
385
385
  if (id === void 0) return void 0;
386
- return { id, count: gpuCount ?? 1 };
386
+ return compact({
387
+ id,
388
+ count: gpuCount ?? 1,
389
+ allowedCudaVersions,
390
+ minCudaVersion: minCudaVersion2
391
+ });
387
392
  }
388
393
  function mapPodCreateToV2(params) {
389
394
  return compact({
@@ -404,7 +409,12 @@ function mapPodCreateToV2(params) {
404
409
  // singular on v2 (`gpu.id`): gpuConfigToV2 takes the first id; the
405
410
  // create-pod handler warns when more than one gpuTypeId was supplied.
406
411
  dataCenterIds: params.dataCenterIds?.length ? params.dataCenterIds : void 0,
407
- gpu: gpuConfigToV2(params.gpuTypeIds, params.gpuCount)
412
+ gpu: gpuConfigToV2(
413
+ params.gpuTypeIds,
414
+ params.gpuCount,
415
+ params.allowedCudaVersions,
416
+ params.minCudaVersion
417
+ )
408
418
  });
409
419
  }
410
420
  function mapPodUpdateToV2(params) {
@@ -421,9 +431,15 @@ function defaultScalerType(endpointType) {
421
431
  function endpointScaling(scalerType, value) {
422
432
  return scalerType === "REQUEST_COUNT" ? { type: "REQUEST_COUNT", requestCount: value } : { type: "QUEUE_DELAY", queueDelay: value };
423
433
  }
424
- function endpointGpuConfig(pools, count) {
425
- if (!pools?.length) return void 0;
426
- return compact({ pools, count });
434
+ function endpointGpuConfig(params, mode) {
435
+ const gpu = compact({
436
+ pools: params.gpuPoolIds?.length ? params.gpuPoolIds : void 0,
437
+ count: params.gpuCount,
438
+ allowedCudaVersions: params.allowedCudaVersions,
439
+ minCudaVersion: params.minCudaVersion
440
+ });
441
+ if (mode === "create" && !("pools" in gpu)) return void 0;
442
+ return Object.keys(gpu).length ? gpu : void 0;
427
443
  }
428
444
  function endpointWorkers(params) {
429
445
  const workers = compact({
@@ -433,7 +449,7 @@ function endpointWorkers(params) {
433
449
  });
434
450
  return Object.keys(workers).length ? workers : void 0;
435
451
  }
436
- function endpointCommonToV2(params) {
452
+ function endpointCommonToV2(params, mode) {
437
453
  return compact({
438
454
  name: params.name,
439
455
  image: params.imageName,
@@ -442,7 +458,7 @@ function endpointCommonToV2(params) {
442
458
  ports: params.ports,
443
459
  env: params.env,
444
460
  registry: params.containerRegistryAuthId,
445
- gpu: endpointGpuConfig(params.gpuPoolIds, params.gpuCount),
461
+ gpu: endpointGpuConfig(params, mode),
446
462
  workers: endpointWorkers(params),
447
463
  dataCenterIds: params.dataCenterIds?.length ? params.dataCenterIds : void 0,
448
464
  networkVolumes: params.networkVolumeIds?.length ? params.networkVolumeIds : void 0,
@@ -453,7 +469,7 @@ function endpointCommonToV2(params) {
453
469
  function mapEndpointCreateToV2(params) {
454
470
  const endpointType = params.endpointType ?? DEFAULT_ENDPOINT_TYPE;
455
471
  return compact({
456
- ...endpointCommonToV2(params),
472
+ ...endpointCommonToV2(params, "create"),
457
473
  type: endpointType,
458
474
  scaling: endpointScaling(
459
475
  params.scalerType ?? defaultScalerType(endpointType),
@@ -463,13 +479,28 @@ function mapEndpointCreateToV2(params) {
463
479
  }
464
480
  function mapEndpointUpdateToV2(params) {
465
481
  return compact({
466
- ...endpointCommonToV2(params),
482
+ ...endpointCommonToV2(params, "update"),
467
483
  scaling: params.scalerType ? endpointScaling(
468
484
  params.scalerType,
469
485
  params.scalerValue ?? DEFAULT_SCALER_VALUE
470
486
  ) : void 0
471
487
  });
472
488
  }
489
+ var CUDA_MAJOR_MINOR = /^\d+\.\d+$/;
490
+ function cudaConstraintError(params, opts = {}) {
491
+ const { allowedCudaVersions, minCudaVersion: minCudaVersion2 } = params;
492
+ if (allowedCudaVersions?.length && minCudaVersion2) {
493
+ return "allowedCudaVersions and minCudaVersion are mutually exclusive \u2014 pass either an exact set or an open-ended floor, not both.";
494
+ }
495
+ if (minCudaVersion2 !== void 0 && !CUDA_MAJOR_MINOR.test(minCudaVersion2) && !(opts.allowClear && minCudaVersion2 === "")) {
496
+ return `minCudaVersion must be major.minor (e.g. "12.0", not "12")${opts.allowClear ? "; an empty string clears the floor" : ""}.`;
497
+ }
498
+ const bad = allowedCudaVersions?.find((v) => !CUDA_MAJOR_MINOR.test(v));
499
+ if (bad !== void 0) {
500
+ return `allowedCudaVersions entries must be major.minor (e.g. "12.8"); got "${bad}". Discover valid values via get-capacity or list-gpu-types.`;
501
+ }
502
+ return void 0;
503
+ }
473
504
  function mapNetworkVolumeCreateToV2(params) {
474
505
  return compact({
475
506
  name: params.name,
@@ -760,7 +791,7 @@ function resolveBackend(opts) {
760
791
  }
761
792
 
762
793
  // src/tools/runtime.ts
763
- var MCP_SERVER_VERSION = true ? "3.3.0" : "dev";
794
+ var MCP_SERVER_VERSION = true ? "3.4.0" : "dev";
764
795
  var SESSION_ID = randomUUID();
765
796
  function trackingHeaders(server, ctx) {
766
797
  const info = server.server.getClientVersion();
@@ -1090,16 +1121,47 @@ function registerCatalogTools(server, rt) {
1090
1121
  ),
1091
1122
  product: z3.enum(["POD", "CLUSTER", "SERVERLESS"]).optional().describe(
1092
1123
  "Product context for the availability lookup \u2014 the same GPU can be scarce for Pods and plentiful for Serverless. Default POD. Use SERVERLESS when picking a GPU for an endpoint, CLUSTER for Instant Clusters. Ignored when includeAvailability is false."
1124
+ ),
1125
+ minCudaVersion: z3.string().optional().describe(
1126
+ 'Scope the availability lookup to hosts at or above this CUDA version (v2 only). Integer major or major.minor \u2014 "12" means any 12.x release; unlike the create-pod/create-endpoint body fields, a bare major is accepted here because the filter only widens a read. Requires includeAvailability (the default).'
1093
1127
  )
1094
1128
  },
1095
1129
  { title: "List GPU types", ...READ_ONLY },
1096
1130
  async (params) => {
1097
1131
  const backend = backendFor("gpus");
1132
+ if (params.minCudaVersion !== void 0 && backend.version !== "v2") {
1133
+ return jsonReply2({
1134
+ error: "The minCudaVersion filter is only supported on the v2 REST API. Set RUNPOD_REST_VERSION=v2, or use get-capacity for per-CUDA-version availability.",
1135
+ status: 501
1136
+ });
1137
+ }
1098
1138
  if (backend.version === "v2") {
1099
1139
  const wantAvailability = params.includeAvailability !== false;
1100
1140
  const product = GPU_PRODUCTS.has(String(params.product)) ? String(params.product) : "POD";
1141
+ if (params.minCudaVersion !== void 0) {
1142
+ if (!wantAvailability) {
1143
+ return jsonReply2({
1144
+ error: "minCudaVersion filters the availability lookup, so it requires includeAvailability (the default). Drop includeAvailability:false or the filter.",
1145
+ status: 400
1146
+ });
1147
+ }
1148
+ if (!/^\d+(\.\d+)?$/.test(params.minCudaVersion)) {
1149
+ return jsonReply2({
1150
+ error: 'minCudaVersion must be an integer major ("12") or major.minor ("12.1").',
1151
+ status: 400
1152
+ });
1153
+ }
1154
+ }
1155
+ const query = new URLSearchParams();
1156
+ if (wantAvailability) {
1157
+ query.set("include", "AVAILABILITY");
1158
+ query.set("product", product);
1159
+ if (params.minCudaVersion !== void 0) {
1160
+ query.set("minCudaVersion", params.minCudaVersion);
1161
+ }
1162
+ }
1101
1163
  const raw = await callRestUrl(
1102
- `${backend.base}${backend.list}${wantAvailability ? `?include=AVAILABILITY&product=${product}` : ""}`
1164
+ `${backend.base}${backend.list}${query.size ? `?${query}` : ""}`
1103
1165
  );
1104
1166
  let gpus = backend.unwrap(raw);
1105
1167
  gpus = gpus.filter((g) => g.id !== "unknown");
@@ -2197,7 +2259,13 @@ function registerPodTools(server, rt) {
2197
2259
  containerRegistryAuthId: z7.string().optional().describe(
2198
2260
  "Container registry credential ID for pulling a private image (see list-container-registry-auths). When deploying from a templateId, this overrides the credential the template carries; pass an empty string to deploy with NO credential (clears a template\u2019s inherited registry)."
2199
2261
  ),
2200
- dataCenterIds: z7.array(z7.string()).optional().describe("List of data centers")
2262
+ dataCenterIds: z7.array(z7.string()).optional().describe("List of data centers"),
2263
+ allowedCudaVersions: z7.array(z7.string()).optional().describe(
2264
+ 'Acceptable host CUDA versions as major.minor, e.g. ["12.8","12.6"] (v2, GPU pods only). Exact match \u2014 discover valid values per GPU type with get-capacity. Mutually exclusive with minCudaVersion.'
2265
+ ),
2266
+ minCudaVersion: z7.string().optional().describe(
2267
+ 'Lowest acceptable host CUDA version as major.minor, e.g. "12.4" (v2, GPU pods only). A bare major like "12" is rejected. Open-ended floor; mutually exclusive with a non-empty allowedCudaVersions.'
2268
+ )
2201
2269
  },
2202
2270
  { title: "Create pod", ...WRITE },
2203
2271
  async (params) => {
@@ -2211,6 +2279,13 @@ function registerPodTools(server, rt) {
2211
2279
  sshKey = validated.key;
2212
2280
  }
2213
2281
  const sshNote = "sshPublicKey applied: PUBLIC_KEY env set and 22/tcp exposed. Full SSH (incl. SCP/SFTP/rsync) also requires an image that installs PUBLIC_KEY into authorized_keys and starts sshd (all runpod/* official images do) and a machine with a public IP \u2014 read the pod\u2019s portMappings for port 22 once it is running.";
2282
+ const hasCudaParams = params.allowedCudaVersions !== void 0 || params.minCudaVersion !== void 0;
2283
+ if (hasCudaParams && backend.version !== "v2") {
2284
+ return jsonReply2({
2285
+ error: "allowedCudaVersions/minCudaVersion are only supported on the v2 REST API. Set RUNPOD_REST_VERSION=v2.",
2286
+ status: 501
2287
+ });
2288
+ }
2214
2289
  if (params.templateId && backend.version !== "v2") {
2215
2290
  return jsonReply2({
2216
2291
  error: "Deploying a pod from a templateId is only supported on the v2 REST API. Set RUNPOD_REST_VERSION=v2, or pass imageName instead.",
@@ -2246,6 +2321,16 @@ function registerPodTools(server, rt) {
2246
2321
  status: 400
2247
2322
  });
2248
2323
  }
2324
+ if (hasCudaParams && !wantsGpu) {
2325
+ return jsonReply2({
2326
+ error: "allowedCudaVersions/minCudaVersion apply to GPU pods only \u2014 pass gpuTypeIds (see list-gpu-types).",
2327
+ status: 400
2328
+ });
2329
+ }
2330
+ if (hasCudaParams) {
2331
+ const cudaError = cudaConstraintError(params);
2332
+ if (cudaError) return jsonReply2({ error: cudaError, status: 400 });
2333
+ }
2249
2334
  if (!wantsCpu && !params.name && !params.templateId) {
2250
2335
  return jsonReply2({
2251
2336
  error: "Provide a name for the pod (deploying from a templateId inherits the template name).",
@@ -2435,6 +2520,9 @@ function registerPodTools(server, rt) {
2435
2520
  import { z as z8 } from "zod";
2436
2521
  var MIN_QUEUE_DELAY = 0.5;
2437
2522
  var MIN_REQUEST_COUNT = 1;
2523
+ function isRequestCountScaler(type) {
2524
+ return type === "REQUEST_COUNT";
2525
+ }
2438
2526
  var MIN_IDLE_TIMEOUT = 1;
2439
2527
  var MAX_IDLE_TIMEOUT = 3600;
2440
2528
  function scalerValueError(scalerType, scalerValue) {
@@ -2527,6 +2615,12 @@ function registerEndpointTools(server, rt) {
2527
2615
  'GPU pool names (v2, required). The `pool` field from list-gpu-types, e.g. ["AMPERE_80"]. NOT GPU type ids.'
2528
2616
  ),
2529
2617
  gpuCount: z8.number().optional().describe("GPUs per worker (v2)"),
2618
+ allowedCudaVersions: z8.array(z8.string()).optional().describe(
2619
+ 'Acceptable host CUDA versions for worker placement as major.minor, e.g. ["12.8"] (v2). Exact match \u2014 see get-capacity for which versions have stock. Mutually exclusive with minCudaVersion.'
2620
+ ),
2621
+ minCudaVersion: z8.string().optional().describe(
2622
+ 'Lowest acceptable host CUDA version as major.minor, e.g. "12.4" (v2). A bare major like "12" is rejected. Mutually exclusive with a non-empty allowedCudaVersions.'
2623
+ ),
2530
2624
  args: z8.string().optional().describe("Container start command/args (v2)"),
2531
2625
  containerDiskInGb: z8.number().optional().describe("Container disk size in GB (v2)"),
2532
2626
  ports: z8.array(z8.string()).optional().describe("Ports to expose (v2), e.g. ['8000/http']"),
@@ -2556,6 +2650,13 @@ function registerEndpointTools(server, rt) {
2556
2650
  { title: "Create endpoint", ...WRITE },
2557
2651
  async (params) => {
2558
2652
  const backend = backendFor("endpoints");
2653
+ const hasCudaParams = params.allowedCudaVersions !== void 0 || params.minCudaVersion !== void 0;
2654
+ if (hasCudaParams && backend.version !== "v2") {
2655
+ return jsonReply2({
2656
+ error: "allowedCudaVersions/minCudaVersion are only supported on the v2 REST API. Set RUNPOD_REST_VERSION=v2.",
2657
+ status: 501
2658
+ });
2659
+ }
2559
2660
  if (backend.version === "v2") {
2560
2661
  if (!params.name) {
2561
2662
  return jsonReply2({
@@ -2588,6 +2689,10 @@ function registerEndpointTools(server, rt) {
2588
2689
  if (scalerError) {
2589
2690
  return jsonReply2({ error: scalerError, status: 400 });
2590
2691
  }
2692
+ if (hasCudaParams) {
2693
+ const cudaError = cudaConstraintError(params);
2694
+ if (cudaError) return jsonReply2({ error: cudaError, status: 400 });
2695
+ }
2591
2696
  const body = backend.mapCreate(params);
2592
2697
  const result2 = await callRestUrl(
2593
2698
  `${backend.base}${backend.list}`,
@@ -2625,7 +2730,7 @@ function registerEndpointTools(server, rt) {
2625
2730
  );
2626
2731
  server.tool(
2627
2732
  "update-endpoint",
2628
- "Update a Serverless endpoint's config. On v2 you can change image/disk/env/ports/registry/workers/scaling/networkVolumes/timeout/flashboot; on v1, scaling fields (worker min/max, idle timeout, scaler type/value, name). Only provided fields change. An endpoint's request routing (queue vs load balancer) is fixed at creation and cannot be changed here \u2014 recreate the endpoint instead.",
2733
+ "Update a Serverless endpoint's config. On v2 you can change image/disk/env/ports/registry/workers/scaling/networkVolumes/timeout/flashboot, plus the GPU selection (gpuPoolIds/gpuCount) and its CUDA constraints (allowedCudaVersions/minCudaVersion) \u2014 CUDA-only or count-only changes do not require resending gpuPoolIds; on v1, scaling fields (worker min/max, idle timeout, scaler type/value, name). Only provided fields change. An endpoint's request routing (queue vs load balancer) is fixed at creation and cannot be changed here \u2014 recreate the endpoint instead. Note: passing gpuPoolIds replaces the GPU selection wholesale, which clears any GPU-type exclusions set elsewhere (console or set-endpoint-gpus) \u2014 the reply carries a _warning when that happens.",
2629
2734
  {
2630
2735
  endpointId: z8.string().describe("ID of the endpoint to update"),
2631
2736
  name: z8.string().optional().describe("New name for the endpoint"),
@@ -2644,6 +2749,12 @@ function registerEndpointTools(server, rt) {
2644
2749
  imageName: z8.string().optional().describe("New Docker image (v2)"),
2645
2750
  gpuPoolIds: z8.array(z8.string()).optional().describe('New GPU pool names (v2), e.g. ["AMPERE_80"]'),
2646
2751
  gpuCount: z8.number().optional().describe("New GPUs per worker (v2)"),
2752
+ allowedCudaVersions: z8.array(z8.string()).optional().describe(
2753
+ "New acceptable host CUDA versions as major.minor (v2). Changing only this does not require resending gpuPoolIds. An explicit [] clears the constraint. Mutually exclusive with minCudaVersion."
2754
+ ),
2755
+ minCudaVersion: z8.string().optional().describe(
2756
+ 'New lowest acceptable host CUDA version as major.minor (v2). An empty string clears the floor; a bare major like "12" is rejected. Mutually exclusive with a non-empty allowedCudaVersions.'
2757
+ ),
2647
2758
  args: z8.string().optional().describe("New container args (v2)"),
2648
2759
  containerDiskInGb: z8.number().optional().describe("New container disk size in GB (v2)"),
2649
2760
  ports: z8.array(z8.string()).optional().describe("New ports (v2)"),
@@ -2658,14 +2769,41 @@ function registerEndpointTools(server, rt) {
2658
2769
  const { endpointId, ...updateParams } = params;
2659
2770
  const backend = backendFor("endpoints");
2660
2771
  const url = `${backend.base}${backend.get(endpointId)}`;
2772
+ const hasCudaParams = updateParams.allowedCudaVersions !== void 0 || updateParams.minCudaVersion !== void 0;
2661
2773
  if (backend.version !== "v2") {
2774
+ if (hasCudaParams) {
2775
+ return jsonReply2({
2776
+ error: "allowedCudaVersions/minCudaVersion are only supported on the v2 REST API. Set RUNPOD_REST_VERSION=v2.",
2777
+ status: 501
2778
+ });
2779
+ }
2662
2780
  const body2 = backend.mapUpdate(updateParams);
2663
2781
  return jsonReply2(await callRestUrl(url, "PATCH", body2));
2664
2782
  }
2783
+ if (hasCudaParams) {
2784
+ const cudaError = cudaConstraintError(updateParams, {
2785
+ allowClear: true
2786
+ });
2787
+ if (cudaError) return jsonReply2({ error: cudaError, status: 400 });
2788
+ }
2789
+ if (updateParams.gpuPoolIds !== void 0 && !updateParams.gpuPoolIds.length) {
2790
+ return jsonReply2({
2791
+ error: "gpuPoolIds cannot be empty \u2014 omit it to keep the current GPU selection.",
2792
+ status: 400
2793
+ });
2794
+ }
2665
2795
  let scalerType = updateParams.scalerType;
2666
- if (scalerType === void 0 && updateParams.scalerValue !== void 0) {
2796
+ const needScalerRead = scalerType === void 0 && updateParams.scalerValue !== void 0;
2797
+ const needExclusionRead = (updateParams.gpuPoolIds?.length ?? 0) > 0;
2798
+ let clearedExclusions = [];
2799
+ if (needScalerRead || needExclusionRead) {
2667
2800
  const current = await callRestUrl(url);
2668
- scalerType = current?.scaling?.type === "REQUEST_COUNT" ? "REQUEST_COUNT" : "QUEUE_DELAY";
2801
+ if (needScalerRead) {
2802
+ scalerType = isRequestCountScaler(current?.scaling?.type) ? "REQUEST_COUNT" : "QUEUE_DELAY";
2803
+ }
2804
+ if (needExclusionRead && current?.gpu?.excludedTypes?.length) {
2805
+ clearedExclusions = current.gpu.excludedTypes;
2806
+ }
2669
2807
  }
2670
2808
  const scalerError = scalerValueError(
2671
2809
  scalerType,
@@ -2678,7 +2816,14 @@ function registerEndpointTools(server, rt) {
2678
2816
  ...updateParams,
2679
2817
  scalerType
2680
2818
  });
2681
- return jsonReply2(await callRestUrl(url, "PATCH", body));
2819
+ const result = await callRestUrl(url, "PATCH", body);
2820
+ if (clearedExclusions.length) {
2821
+ return jsonReply2({
2822
+ ...result,
2823
+ _warning: `Supplying gpuPoolIds replaced the GPU selection wholesale, clearing ${clearedExclusions.length} GPU-type exclusion(s) previously set on this endpoint: ${clearedExclusions.join(", ")}. Re-apply them with set-endpoint-gpus if still wanted.`
2824
+ });
2825
+ }
2826
+ return jsonReply2(result);
2682
2827
  }
2683
2828
  );
2684
2829
  server.tool(
@@ -3628,4 +3773,4 @@ export {
3628
3773
  restVersionSummary,
3629
3774
  registerTools
3630
3775
  };
3631
- //# sourceMappingURL=chunk-ICUS4KGX.mjs.map
3776
+ //# sourceMappingURL=chunk-G4URDODF.mjs.map