@runpod/mcp-server 4.4.0 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/stdio.mjs CHANGED
@@ -12,7 +12,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
12
12
 
13
13
  // src/server.ts
14
14
  var SERVER_NAME = "Runpod API Server";
15
- var SERVER_VERSION = true ? "4.4.0" : "dev";
15
+ var SERVER_VERSION = true ? "4.5.0" : "dev";
16
16
 
17
17
  // src/specgen/server.ts
18
18
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -345,6 +345,7 @@ function coerceScalars(value, schema, defs, depth = 0) {
345
345
  var generatedTools = [
346
346
  {
347
347
  name: "create-cluster",
348
+ title: "Create cluster",
348
349
  operationId: "createCluster",
349
350
  description: "Create an instant cluster (multiple pods with high-speed interconnect). BILLABLE from creation for every pod in the cluster: it rents compute.podCount identical pods at once, each with compute.gpuCountPerPod GPUs of compute.gpuTypeId \u2014 state the hourly price for the total GPU count (podCount * gpuCountPerPod, priced from the catalog tools) and create only after the user's explicit go on that quoted shape and total hourly price. Verify GPU stock first via the catalog tools. The compute shape and type are fixed at creation; only the name can be changed later. The container configuration applies to every pod, and templateId is the only private-image path \u2014 a bare registry property is rejected. Delete with delete-cluster when finished.",
350
351
  method: "POST",
@@ -582,6 +583,7 @@ var generatedTools = [
582
583
  },
583
584
  {
584
585
  name: "create-delegation",
586
+ title: "Create ECR delegation",
585
587
  operationId: "createDelegation",
586
588
  description: "Register an ECR delegation",
587
589
  method: "POST",
@@ -627,6 +629,7 @@ var generatedTools = [
627
629
  },
628
630
  {
629
631
  name: "create-endpoint",
632
+ title: "Create endpoint",
630
633
  operationId: "createEndpoint",
631
634
  description: "Create a serverless endpoint. BILLABLE: state the worker's hourly price (from the catalog tools) before creating. Worker hardware is one of two choices: the gpu object (gpu.pools takes serverless pool ids from the catalog, e.g. ADA_24, never display names; gpu.count sets GPUs per worker) or the cpu array of CPU flavor configurations for a CPU endpoint. type and scaling are required and fix the request-routing model. Workers cold-start on the first job: expect minutes, poll with get-job-status wait.",
632
635
  method: "POST",
@@ -1072,6 +1075,7 @@ var generatedTools = [
1072
1075
  },
1073
1076
  {
1074
1077
  name: "create-global-volume",
1078
+ title: "Create global volume",
1075
1079
  operationId: "createGlobalVolume",
1076
1080
  description: "Create a global volume. Provisions a new global volume \u2014 storage that mounts into workloads in any data center. Only a `name` is required; storage is provisioned for you. Accounts have a cap on how many global volumes they can hold; creating one past it fails with `400`. Returns `201` with the created global volume, including its assigned `id`.",
1077
1081
  method: "POST",
@@ -1113,6 +1117,7 @@ var generatedTools = [
1113
1117
  },
1114
1118
  {
1115
1119
  name: "create-network-volume",
1120
+ title: "Create network volume",
1116
1121
  operationId: "createNetworkVolume",
1117
1122
  description: "Create a network volume. Provisions a new network volume \u2014 persistent, network-attached storage that can be mounted into pods and serverless workers. Required inputs are `name`, `size` (in GB), and `dataCenter`; an optional `type` selects the storage tier and is immutable after creation. See `CreateNetworkVolumeRequest` for the size bounds and tier options. This creates a billable persistent resource that keeps incurring storage charges until it is deleted. Returns `201` with the created network volume, including its assigned `id`.",
1118
1123
  method: "POST",
@@ -1178,6 +1183,7 @@ var generatedTools = [
1178
1183
  },
1179
1184
  {
1180
1185
  name: "create-pod",
1186
+ title: "Create Pod",
1181
1187
  operationId: "createPod",
1182
1188
  description: 'Rent a GPU/CPU pod. BILLABLE from creation until stop/terminate: state the hourly price before creating. GPU selection is the gpu object: gpu.id takes a catalog GPU type id (e.g. "NVIDIA GeForce RTX 4090"), verified via list-gpu-types, and gpu.count sets the GPU count; cloud/data-center choice constrains stock.',
1183
1189
  method: "POST",
@@ -1545,6 +1551,7 @@ var generatedTools = [
1545
1551
  },
1546
1552
  {
1547
1553
  name: "create-registry",
1554
+ title: "Create registry credential",
1548
1555
  operationId: "createRegistry",
1549
1556
  description: "Create a container registry credential. Stores credentials for a private container registry. Credentials are write-only.",
1550
1557
  method: "POST",
@@ -1596,6 +1603,7 @@ var generatedTools = [
1596
1603
  },
1597
1604
  {
1598
1605
  name: "create-secret",
1606
+ title: "Create secret",
1599
1607
  operationId: "createSecret",
1600
1608
  description: "Create a secret. Stores a new account-scoped encrypted string. `name` must be unique across the account's secrets and is immutable; `value` is write-only and can never be read back through the API. Use the secret from pods, serverless endpoints, and templates by setting an environment variable's value to `{{ RUNPOD_SECRET_<name> }}` \u2014 Runpod substitutes the stored value when the pod or worker boots. Returns `201` with the created secret's metadata, or `409` when the name is already taken.",
1601
1609
  method: "POST",
@@ -1650,6 +1658,7 @@ var generatedTools = [
1650
1658
  },
1651
1659
  {
1652
1660
  name: "create-template",
1661
+ title: "Create template",
1653
1662
  operationId: "createTemplate",
1654
1663
  description: "Create a template. Creates a reusable container-configuration preset \u2014 image, disk, ports, env, registry, and mount settings \u2014 for pods and serverless endpoints. Pass its ID as `templateId` to `createPod` or `createEndpoint`, or spread its fields into the request body directly. Returns the created template.",
1655
1664
  method: "POST",
@@ -1851,6 +1860,7 @@ var generatedTools = [
1851
1860
  },
1852
1861
  {
1853
1862
  name: "delete-cluster",
1863
+ title: "Delete cluster",
1854
1864
  operationId: "deleteCluster",
1855
1865
  description: "Delete a cluster permanently: it terminates every member pod. Verify the id with get-cluster, and list-cluster-pods for what the delete takes down, before deleting; delete only clusters your own tool calls created in this conversation, or the one the user asked you to delete by id \u2014 a name found by listing is not attribution.",
1856
1866
  method: "DELETE",
@@ -1881,6 +1891,7 @@ var generatedTools = [
1881
1891
  },
1882
1892
  {
1883
1893
  name: "delete-endpoint",
1894
+ title: "Delete endpoint",
1884
1895
  operationId: "deleteEndpoint",
1885
1896
  description: "Delete a serverless endpoint permanently and immediately. Verify the id belongs to the intended endpoint (get-endpoint) before deleting; delete only endpoints your own tool calls created in this conversation, or the one the user asked you to delete by id \u2014 a name found by listing is not attribution.",
1886
1897
  method: "DELETE",
@@ -1911,6 +1922,7 @@ var generatedTools = [
1911
1922
  },
1912
1923
  {
1913
1924
  name: "delete-global-volume",
1925
+ title: "Delete global volume",
1914
1926
  operationId: "deleteGlobalVolume",
1915
1927
  description: "Delete a global volume. Permanently deletes a global volume and its stored data. Fails with `409` while the volume is still mounted: terminate every pod that mounts it (stopped pods count) and detach it from every serverless endpoint first. Nothing is detached or terminated on your behalf.",
1916
1928
  method: "DELETE",
@@ -1941,6 +1953,7 @@ var generatedTools = [
1941
1953
  },
1942
1954
  {
1943
1955
  name: "delete-network-volume",
1956
+ title: "Delete network volume",
1944
1957
  operationId: "deleteNetworkVolume",
1945
1958
  description: "Delete a network volume. Permanently deletes a network volume and releases its storage.",
1946
1959
  method: "DELETE",
@@ -1971,6 +1984,7 @@ var generatedTools = [
1971
1984
  },
1972
1985
  {
1973
1986
  name: "delete-pod",
1987
+ title: "Delete Pod",
1974
1988
  operationId: "deletePod",
1975
1989
  description: "Terminate a pod permanently \u2014 its container disk is lost. Distinguish from stopping (pod-action stop) which releases the GPU but keeps volume data. Verify the id with get-pod first; terminate only pods your own tool calls created in this conversation, or the one the user asked you to terminate by id \u2014 a name found by listing is not attribution.",
1976
1990
  method: "DELETE",
@@ -2001,6 +2015,7 @@ var generatedTools = [
2001
2015
  },
2002
2016
  {
2003
2017
  name: "delete-registry",
2018
+ title: "Delete registry credential",
2004
2019
  operationId: "deleteRegistry",
2005
2020
  description: "Delete a container registry credential. Permanently deletes a container registry credential by ID. Rejected if any pod currently uses this credential to pull its image. Templates that reference it are not part of that check \u2014 they silently lose the reference (`registry` becomes null) instead of blocking the delete.",
2006
2021
  method: "DELETE",
@@ -2030,6 +2045,7 @@ var generatedTools = [
2030
2045
  },
2031
2046
  {
2032
2047
  name: "delete-secret",
2048
+ title: "Delete secret",
2033
2049
  operationId: "deleteSecret",
2034
2050
  description: "Delete a secret permanently. Environment variables referencing its name ({{ RUNPOD_SECRET_<name> }}) stop resolving at the next pod or worker boot; running instances keep the value they started with. Verify the id with get-secret first; delete only secrets your own tool calls created in this conversation, or the one the user asked you to delete by id \u2014 a name found by listing is not attribution.",
2035
2051
  method: "DELETE",
@@ -2060,6 +2076,7 @@ var generatedTools = [
2060
2076
  },
2061
2077
  {
2062
2078
  name: "delete-template",
2079
+ title: "Delete template",
2063
2080
  operationId: "deleteTemplate",
2064
2081
  description: "Delete a template permanently. Verify with get-template first; a template referenced by a live endpoint should not be deleted.",
2065
2082
  method: "DELETE",
@@ -2089,6 +2106,7 @@ var generatedTools = [
2089
2106
  },
2090
2107
  {
2091
2108
  name: "get-cluster",
2109
+ title: "Get cluster",
2092
2110
  operationId: "getCluster",
2093
2111
  description: "Get a cluster. Returns a single cluster by ID. The pods field is an aggregate summary (total + count by status); fetch the member pods themselves from /v2/clusters/{id}/pods.",
2094
2112
  method: "GET",
@@ -2119,6 +2137,7 @@ var generatedTools = [
2119
2137
  },
2120
2138
  {
2121
2139
  name: "get-cpu-type",
2140
+ title: "Get CPU type",
2122
2141
  operationId: "getCpuType",
2123
2142
  description: "Get a CPU type. Returns a single CPU type with pricing. Availability details are included only when requested with include=AVAILABILITY, which requires `product` \u2014 stock differs by product context.",
2124
2143
  method: "GET",
@@ -2195,6 +2214,7 @@ var generatedTools = [
2195
2214
  },
2196
2215
  {
2197
2216
  name: "get-data-center",
2217
+ title: "Get data center",
2198
2218
  operationId: "getDataCenter",
2199
2219
  description: "Get a data center. Returns a single data center. Availability is included only when requested with include=GPU_AVAILABILITY or include=CPU_AVAILABILITY.",
2200
2220
  method: "GET",
@@ -2244,6 +2264,7 @@ var generatedTools = [
2244
2264
  },
2245
2265
  {
2246
2266
  name: "get-endpoint",
2267
+ title: "Get endpoint",
2247
2268
  operationId: "getEndpoint",
2248
2269
  description: "Get a serverless endpoint. Returns a single serverless endpoint by ID.",
2249
2270
  method: "GET",
@@ -2274,6 +2295,7 @@ var generatedTools = [
2274
2295
  },
2275
2296
  {
2276
2297
  name: "get-endpoint-build",
2298
+ title: "Get endpoint build",
2277
2299
  operationId: "getEndpointBuild",
2278
2300
  description: "Get a serverless endpoint build. Returns one of the endpoint's GitHub builds by id, regardless of age \u2014 no need to page through `GET /v2/serverless/{id}/builds` to reach it.",
2279
2301
  method: "GET",
@@ -2312,6 +2334,7 @@ var generatedTools = [
2312
2334
  },
2313
2335
  {
2314
2336
  name: "get-global-volume",
2337
+ title: "Get global volume",
2315
2338
  operationId: "getGlobalVolume",
2316
2339
  description: "Get a global volume. Returns a single global volume by ID.",
2317
2340
  method: "GET",
@@ -2342,6 +2365,7 @@ var generatedTools = [
2342
2365
  },
2343
2366
  {
2344
2367
  name: "get-gpu-type",
2368
+ title: "Get GPU type",
2345
2369
  operationId: "getGpuType",
2346
2370
  description: "Get a GPU type. Returns a single GPU type with pricing. Availability details are included only when requested with include=AVAILABILITY, which requires `product` \u2014 stock differs by product context.",
2347
2371
  method: "GET",
@@ -2466,6 +2490,7 @@ var generatedTools = [
2466
2490
  },
2467
2491
  {
2468
2492
  name: "get-network-volume",
2493
+ title: "Get network volume",
2469
2494
  operationId: "getNetworkVolume",
2470
2495
  description: "Get a network volume. Returns a single network volume by ID.",
2471
2496
  method: "GET",
@@ -2496,6 +2521,7 @@ var generatedTools = [
2496
2521
  },
2497
2522
  {
2498
2523
  name: "get-organization",
2524
+ title: "Get organization",
2499
2525
  operationId: "getOrganization",
2500
2526
  description: "Get your current organization. Returns the authenticated caller's current organization. Organization context comes from the caller's membership; no organization selector is required. Returns 404 when the caller does not belong to an organization, or 403 when organizations are unavailable to the caller.",
2501
2527
  method: "GET",
@@ -2515,6 +2541,7 @@ var generatedTools = [
2515
2541
  },
2516
2542
  {
2517
2543
  name: "get-pod",
2544
+ title: "Get Pod",
2518
2545
  operationId: "getPod",
2519
2546
  description: "Get a pod. Returns a single pod by ID.",
2520
2547
  method: "GET",
@@ -2545,6 +2572,7 @@ var generatedTools = [
2545
2572
  },
2546
2573
  {
2547
2574
  name: "get-registry",
2575
+ title: "Get registry credential",
2548
2576
  operationId: "getRegistry",
2549
2577
  description: "Get a container registry credential. Returns a single container registry credential by ID. `username` and `password` are never included in the response \u2014 credentials are write-only, matching `createRegistry`.",
2550
2578
  method: "GET",
@@ -2574,6 +2602,7 @@ var generatedTools = [
2574
2602
  },
2575
2603
  {
2576
2604
  name: "get-secret",
2605
+ title: "Get secret",
2577
2606
  operationId: "getSecret",
2578
2607
  description: "Get a secret. Returns a single secret's metadata by ID. The value is write-only and never returned.",
2579
2608
  method: "GET",
@@ -2604,6 +2633,7 @@ var generatedTools = [
2604
2633
  },
2605
2634
  {
2606
2635
  name: "get-ssh-keys",
2636
+ title: "Get SSH keys",
2607
2637
  operationId: "getSshKeys",
2608
2638
  description: "List registered SSH public keys. Returns the account's registered SSH public keys \u2014 the keys provisioned into pods created with `startSsh` and used to authenticate the SSH connections reported in a pod's `ssh` block.",
2609
2639
  method: "GET",
@@ -2623,6 +2653,7 @@ var generatedTools = [
2623
2653
  },
2624
2654
  {
2625
2655
  name: "get-template",
2656
+ title: "Get template",
2626
2657
  operationId: "getTemplate",
2627
2658
  description: "Get a template. Returns the full configuration of a single template by ID. Serves both templates you own and public catalog templates \u2014 everything you can read. Updates and deletes remain restricted to templates you own.",
2628
2659
  method: "GET",
@@ -2652,6 +2683,7 @@ var generatedTools = [
2652
2683
  },
2653
2684
  {
2654
2685
  name: "list-billing",
2686
+ title: "List billing",
2655
2687
  operationId: "listBilling",
2656
2688
  description: "Get aggregated billing history. Returns time-bucketed total spend across all billable Runpod resources for the authenticated user. Use startTime/endTime with bucketSize for an explicit range, or lastN with bucketSize for the most recent buckets. Each record reports one bucket's total plus pod, serverless, storage, public endpoint, and Instant Cluster cost components. The metadata block echoes the resolved query window, record count, and totals across all returned buckets.",
2657
2689
  method: "GET",
@@ -2724,6 +2756,7 @@ var generatedTools = [
2724
2756
  },
2725
2757
  {
2726
2758
  name: "list-cluster-billing",
2759
+ title: "List cluster billing",
2727
2760
  operationId: "listClusterBilling",
2728
2761
  description: "Get cluster billing history. Returns Cluster billing history for the authenticated user, split into time buckets by startTime/endTime with bucketSize or by lastN recent buckets. Use clusterId to filter to one cluster; without it, records are emitted per cluster per bucket. Each record includes GPU compute, disk, inter-node networking, and total amounts. Clusters are GPU-only, so no CPU cost component is returned.",
2729
2762
  method: "GET",
@@ -2804,6 +2837,7 @@ var generatedTools = [
2804
2837
  },
2805
2838
  {
2806
2839
  name: "list-cluster-pods",
2840
+ title: "List cluster Pods",
2807
2841
  operationId: "listClusterPods",
2808
2842
  description: "List a cluster's pods. Returns the full member pods of a cluster. The cluster summary (`GET /v2/clusters/{id}`) carries only aggregate pod counts; this endpoint returns each member as a complete Pod object.",
2809
2843
  method: "GET",
@@ -2834,6 +2868,7 @@ var generatedTools = [
2834
2868
  },
2835
2869
  {
2836
2870
  name: "list-clusters",
2871
+ title: "List clusters",
2837
2872
  operationId: "listClusters",
2838
2873
  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.",
2839
2874
  method: "GET",
@@ -2874,6 +2909,7 @@ var generatedTools = [
2874
2909
  },
2875
2910
  {
2876
2911
  name: "list-cpu-types",
2912
+ title: "List CPU types",
2877
2913
  operationId: "listCpuTypes",
2878
2914
  description: "List CPU types. Returns available CPU flavors. Availability is included only when requested with include=AVAILABILITY, which requires `product` \u2014 stock differs by product context.",
2879
2915
  method: "GET",
@@ -2942,6 +2978,7 @@ var generatedTools = [
2942
2978
  },
2943
2979
  {
2944
2980
  name: "list-data-centers",
2981
+ title: "List data centers",
2945
2982
  operationId: "listDataCenters",
2946
2983
  description: "List data centers. Returns available data center locations with region, compliance, supported network volume tiers, and global networking support. Use include=GPU_AVAILABILITY or include=CPU_AVAILABILITY to add per-resource availability arrays to each data center. The regions, networkVolumeTypes, compliance, and globalNetwork query parameters filter the list before it is returned.",
2947
2984
  method: "GET",
@@ -3069,6 +3106,7 @@ var generatedTools = [
3069
3106
  },
3070
3107
  {
3071
3108
  name: "list-delegations",
3109
+ title: "List ECR delegations",
3072
3110
  operationId: "listDelegations",
3073
3111
  description: "List all ECR delegations",
3074
3112
  method: "GET",
@@ -3088,6 +3126,7 @@ var generatedTools = [
3088
3126
  },
3089
3127
  {
3090
3128
  name: "list-endpoint-billing",
3129
+ title: "List endpoint billing",
3091
3130
  operationId: "listEndpointBilling",
3092
3131
  description: "Get public endpoint billing history. Returns Runpod public endpoint billing history for the authenticated user, split into time buckets by startTime/endTime with bucketSize or by lastN recent buckets. Each record reports the endpoint total for one bucket, and metadata echoes the resolved query window, record count, and total endpoint amount across all returned records.",
3093
3132
  method: "GET",
@@ -3160,6 +3199,7 @@ var generatedTools = [
3160
3199
  },
3161
3200
  {
3162
3201
  name: "list-endpoint-builds",
3202
+ title: "List endpoint builds",
3163
3203
  operationId: "listEndpointBuilds",
3164
3204
  description: "List serverless endpoint builds. Returns the endpoint's GitHub build history, newest first (Runpod GitHub-build integration), cursor-paginated; an omitted `limit` defaults to 100, so a bare request returns at most the 100 most recent builds. Any build can also be fetched by id via `GET /v2/serverless/{id}/builds/{buildId}`. Stream a build's logs via `/v2/serverless/{id}/builds/{buildId}/logs`.",
3165
3205
  method: "GET",
@@ -3209,6 +3249,7 @@ var generatedTools = [
3209
3249
  },
3210
3250
  {
3211
3251
  name: "list-endpoint-releases",
3252
+ title: "List endpoint releases",
3212
3253
  operationId: "listEndpointReleases",
3213
3254
  description: "List serverless endpoint releases. Returns the endpoint's release history (newest first) plus a rollout summary of how many workers are running the current version. Each release is a versioned configuration snapshot with a `diff` of what changed; build-driven releases carry a `buildId` (fetch build detail via the builds sub-routes). Releases are cursor-paginated newest-first; an omitted `limit` defaults to 1000. The rollout summary always describes the endpoint's current state, independent of the page requested.",
3214
3255
  method: "GET",
@@ -3258,6 +3299,7 @@ var generatedTools = [
3258
3299
  },
3259
3300
  {
3260
3301
  name: "list-endpoint-workers",
3302
+ title: "List endpoint workers",
3261
3303
  operationId: "listEndpointWorkers",
3262
3304
  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.",
3263
3305
  method: "GET",
@@ -3288,6 +3330,7 @@ var generatedTools = [
3288
3330
  },
3289
3331
  {
3290
3332
  name: "list-global-volumes",
3333
+ title: "List global volumes",
3291
3334
  operationId: "listGlobalVolumes",
3292
3335
  description: "List global volumes. Returns global volumes owned by the authenticated user, cursor-paginated newest-first; an omitted `limit` defaults to 1000. Follow `pagination.nextCursor` until `hasNextPage` is false.",
3293
3336
  method: "GET",
@@ -3328,6 +3371,7 @@ var generatedTools = [
3328
3371
  },
3329
3372
  {
3330
3373
  name: "list-gpu-types",
3374
+ title: "List GPU types",
3331
3375
  operationId: "listGpuTypes",
3332
3376
  description: 'List GPU types. Returns available GPU types with pricing. Availability is included only when requested with include=AVAILABILITY, which requires `product` \u2014 stock differs by product context. With countryCodes, the list is narrowed to GPU types deployable in those countries, so "this geography + this chip" resolves in one read.',
3333
3377
  method: "GET",
@@ -3444,6 +3488,7 @@ var generatedTools = [
3444
3488
  },
3445
3489
  {
3446
3490
  name: "list-network-volume-billing",
3491
+ title: "List network volume billing",
3447
3492
  operationId: "listNetworkVolumeBilling",
3448
3493
  description: "Get network volume billing history. Returns network volume billing history for the authenticated user, split into time buckets by startTime/endTime with bucketSize or by lastN recent buckets. Use networkVolumeId to filter to one volume; without it, records are emitted per volume per bucket. Each record includes standard storage, high-performance storage, and total amounts, while metadata reports the resolved query, distinct volume count, and totals across the returned records.",
3449
3494
  method: "GET",
@@ -3524,6 +3569,7 @@ var generatedTools = [
3524
3569
  },
3525
3570
  {
3526
3571
  name: "list-network-volumes",
3572
+ title: "List network volumes",
3527
3573
  operationId: "listNetworkVolumes",
3528
3574
  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.",
3529
3575
  method: "GET",
@@ -3564,6 +3610,7 @@ var generatedTools = [
3564
3610
  },
3565
3611
  {
3566
3612
  name: "list-pod-billing",
3613
+ title: "List Pod billing",
3567
3614
  operationId: "listPodBilling",
3568
3615
  description: "Get pod billing history. Returns pod-only billing detail for the authenticated user, split into time buckets by startTime/endTime with bucketSize or by lastN recent buckets. Use podId to narrow the response to one GPU or CPU pod; without it, records are emitted per pod per bucket. Each record includes podId, GPU, CPU, disk, and total amounts, while metadata echoes the resolved query and totals across the pod records. Use listBilling when you need aggregate spend across every billable resource family.",
3569
3616
  method: "GET",
@@ -3644,6 +3691,7 @@ var generatedTools = [
3644
3691
  },
3645
3692
  {
3646
3693
  name: "list-pods",
3694
+ title: "List Pods",
3647
3695
  operationId: "listPods",
3648
3696
  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.",
3649
3697
  method: "GET",
@@ -3693,6 +3741,7 @@ var generatedTools = [
3693
3741
  },
3694
3742
  {
3695
3743
  name: "list-public-templates",
3744
+ title: "List public templates",
3696
3745
  operationId: "listPublicTemplates",
3697
3746
  description: "List public templates. Returns the public template catalog. `source` selects which slice: `official` (the default) is Runpod-curated templates, `verified` is community templates Runpod has verified, and `community` is everything else other users have shared publicly. Both pod and serverless templates appear \u2014 use each entry's `serverless` flag to tell them apart. `registry` is always null for templates you don't own. Your own templates (public or private) are managed under `/v2/templates`; fetch any individual template \u2014 catalog or owned \u2014 via `/v2/templates/{id}`. At most 100 templates are returned. Cursor pagination is not yet supported here; `pagination` is always the exhausted marker (`nextCursor: null`, `hasNextPage: false`).",
3698
3747
  method: "GET",
@@ -3724,6 +3773,7 @@ var generatedTools = [
3724
3773
  },
3725
3774
  {
3726
3775
  name: "list-registries",
3776
+ title: "List registry credentials",
3727
3777
  operationId: "listRegistries",
3728
3778
  description: "List container registries. Returns all container registry credentials owned by the authenticated user.",
3729
3779
  method: "GET",
@@ -3743,6 +3793,7 @@ var generatedTools = [
3743
3793
  },
3744
3794
  {
3745
3795
  name: "list-secrets",
3796
+ title: "List secrets",
3746
3797
  operationId: "listSecrets",
3747
3798
  description: "List secrets. Returns the account's secrets \u2014 encrypted strings referenced from pod, serverless, and template environment variables with the `{{ RUNPOD_SECRET_<name> }}` placeholder syntax, substituted with the secret's value when the pod or worker boots. Secret values are write-only and are never returned.",
3748
3799
  method: "GET",
@@ -3772,6 +3823,7 @@ var generatedTools = [
3772
3823
  },
3773
3824
  {
3774
3825
  name: "list-serverless-billing",
3826
+ title: "List Serverless billing",
3775
3827
  operationId: "listServerlessBilling",
3776
3828
  description: "Get serverless billing history, split into time buckets. SIZE WARNING: with defaults this emits one record per endpoint PER BUCKET \u2014 on an account with many endpoints the response overflows the tool-result limit and becomes unusable. Always pass lastN: 1 (or a bucketSize spanning the whole window) for one record per endpoint, and serverlessId when you need one endpoint. Distinct from pod billing, which covers standalone pods.",
3777
3829
  method: "GET",
@@ -3852,6 +3904,7 @@ var generatedTools = [
3852
3904
  },
3853
3905
  {
3854
3906
  name: "pod-action",
3907
+ title: "Start, stop, restart, or terminate a Pod",
3855
3908
  operationId: "podAction",
3856
3909
  description: "Trigger a pod state transition. Triggers a state transition on a pod. Send a JSON body with a single `action` field, e.g. `{ \"action\": \"stop\" }`. Valid actions: - `start` \u2014 boot a stopped pod (`EXITED` or `ERROR`) back toward `RUNNING`. - `stop` \u2014 stop a running or provisioning pod, releasing GPU/CPU compute while keeping its disk. The pod moves to `EXITED`. - `restart` \u2014 restart a `RUNNING` pod's container in place. - `terminate` \u2014 permanently delete the pod and release its resources (equivalent to `deletePod`). Which actions are valid depends on the pod's current status, and the currently permitted set is published in the pod's `actions` field: `RUNNING` allows `stop`/`restart`/`terminate`; `EXITED` and `ERROR` allow `start`/`terminate`; `PROVISIONING` and `STARTING` allow `stop`/`terminate`. `start`, `stop`, and `restart` return `200` with the updated pod. `terminate` returns `204` with no body. Requesting an action that is not valid for the pod's current status returns `409`.",
3857
3910
  method: "POST",
@@ -3901,6 +3954,7 @@ var generatedTools = [
3901
3954
  },
3902
3955
  {
3903
3956
  name: "revoke-delegation",
3957
+ title: "Revoke ECR delegation",
3904
3958
  operationId: "revokeDelegation",
3905
3959
  description: "Revoke an ECR delegation",
3906
3960
  method: "DELETE",
@@ -3930,6 +3984,7 @@ var generatedTools = [
3930
3984
  },
3931
3985
  {
3932
3986
  name: "update-cluster",
3987
+ title: "Update cluster",
3933
3988
  operationId: "updateCluster",
3934
3989
  description: "Rename a cluster. Renames a cluster. This endpoint only changes the cluster name \u2014 compute shape, type, and container configuration are fixed at creation and cannot be updated.",
3935
3990
  method: "PATCH",
@@ -3978,6 +4033,7 @@ var generatedTools = [
3978
4033
  },
3979
4034
  {
3980
4035
  name: "update-endpoint",
4036
+ title: "Update endpoint",
3981
4037
  operationId: "updateEndpoint",
3982
4038
  description: "Partially update a serverless endpoint (PATCH): only fields present in the body change, omitted ones are untouched, with one exception. DESTRUCTIVE GOTCHA: gpu.pools and gpu.excludedTypes are a single selection, so sending pools without excludedTypes clears any exclusions pinned elsewhere (the console or set-endpoint-gpus) \u2014 read the endpoint first with get-endpoint and resend excludedTypes if you still want them. An exclusion naming a type outside pools returns 400. A CUDA-only or count-only change does not need pools at all, so omit it unless you mean to replace the selection. Mutable fields: name, gpu, cpu, workers (min/max/idleTimeout), scaling (type plus queueDelay or requestCount), dataCenterIds, networkVolumes, timeout, flashboot, and the container settings (image, args, disk, ports, env, registry). cpu completely replaces a CPU endpoint's flavor selection. Compute family is immutable: gpu on a CPU endpoint, cpu on a GPU one, or both, returns 400. Effect timing differs by field \u2014 workers/scaling/timeout reach the autoscaler promptly, while container changes (image, env) cut a new release that rolls out as workers cycle, so in-flight workers keep the previous version. Track rollout with list-endpoint-releases.",
3983
4039
  method: "PATCH",
@@ -4368,6 +4424,7 @@ var generatedTools = [
4368
4424
  },
4369
4425
  {
4370
4426
  name: "update-global-volume",
4427
+ title: "Update global volume",
4371
4428
  operationId: "updateGlobalVolume",
4372
4429
  description: "Update a global volume. Updates mutable fields on a global volume. Only provided fields are changed.",
4373
4430
  method: "PATCH",
@@ -4419,6 +4476,7 @@ var generatedTools = [
4419
4476
  },
4420
4477
  {
4421
4478
  name: "update-network-volume",
4479
+ title: "Update network volume",
4422
4480
  operationId: "updateNetworkVolume",
4423
4481
  description: "Update a network volume. Updates mutable fields on a network volume. Only provided fields are changed. Note: `size` may only increase; attempts to reduce size will be rejected.",
4424
4482
  method: "PATCH",
@@ -4475,6 +4533,7 @@ var generatedTools = [
4475
4533
  },
4476
4534
  {
4477
4535
  name: "update-pod",
4536
+ title: "Update Pod",
4478
4537
  operationId: "updatePod",
4479
4538
  description: "Update a pod. Partially updates a pod's configuration. This is a PATCH: only the fields present in the body are changed, and omitted fields are left untouched. Use empty values only when you explicitly mean to clear a field (for example, set `registry` to `null` or set `ports` to `[]`). See `UpdatePodRequest` for the full body. Mutable fields: `name`, `image`, `args`, `disk`, `ports`, `env`, `registry`, `mounts`, `locked`, and `globalNetworking`. Some changes apply immediately while others (e.g. `globalNetworking`) take effect on the pod's next start/restart, as noted on the individual fields. Pods that belong to a Cluster cannot be updated here \u2014 manage them through `/v2/clusters/{id}`. Returns `200` with the full updated pod.",
4480
4539
  method: "PATCH",
@@ -4716,6 +4775,7 @@ var generatedTools = [
4716
4775
  },
4717
4776
  {
4718
4777
  name: "update-secret",
4778
+ title: "Update secret",
4719
4779
  operationId: "updateSecret",
4720
4780
  description: "Update a secret. Rotates a secret's value and/or updates its description. Only the provided fields are changed and at least one field is required; the `name` is immutable. Pods and workers receive the new value at their next boot \u2014 running instances keep the value they were started with. When both fields are sent, the value is applied first, then the description. The two updates are not atomic: if the description update fails after the value was rotated, the response is an error but the new value has already taken effect. Send the fields in separate requests when that partial outcome matters.",
4721
4781
  method: "PATCH",
@@ -4771,6 +4831,7 @@ var generatedTools = [
4771
4831
  },
4772
4832
  {
4773
4833
  name: "update-ssh-keys",
4834
+ title: "Update SSH keys",
4774
4835
  operationId: "updateSshKeys",
4775
4836
  description: "Replace registered SSH public keys. Replaces the account's full set of registered SSH public keys. Existing keys not present in the request are removed; send `[]` to remove all keys. Keys take effect for pods created afterwards with `startSsh` \u2014 running pods are not updated.",
4776
4837
  method: "PUT",
@@ -4817,6 +4878,7 @@ var generatedTools = [
4817
4878
  },
4818
4879
  {
4819
4880
  name: "update-template",
4881
+ title: "Update template",
4820
4882
  operationId: "updateTemplate",
4821
4883
  description: "Update a template. Partially updates a template. This is a PATCH: only the fields present in the body are changed; omitted fields are left untouched. See `UpdateTemplateRequest` for the full body. Mutable fields: `name`, `image`, `args`, `disk`, `ports`, `env`, `registry`, `mounts`, `serverless`, `public`, and `category`. Only the template's owner can update it (authenticated via the request's API key); public catalog templates are readable via GET but return `404` here. Returns `200` with the full updated template. Pods and endpoints already created from this template are not changed retroactively \u2014 the template is a snapshot applied at creation time.",
4822
4884
  method: "PATCH",
@@ -5115,6 +5177,43 @@ function isTimeout(error) {
5115
5177
  const cause = error.cause;
5116
5178
  return cause instanceof Error && (cause.name === "TimeoutError" || cause.name === "AbortError");
5117
5179
  }
5180
+ var UNREACHED_CODES = /* @__PURE__ */ new Set([
5181
+ "ENOTFOUND",
5182
+ "EAI_AGAIN",
5183
+ "ECONNREFUSED",
5184
+ "EHOSTUNREACH",
5185
+ "ENETUNREACH",
5186
+ "UND_ERR_CONNECT_TIMEOUT"
5187
+ ]);
5188
+ var TLS_CODE = /^(?:CERT_|ERR_TLS_|UNABLE_TO_|DEPTH_ZERO_|SELF_SIGNED_)/;
5189
+ function causeCode(cause) {
5190
+ const code = cause?.code;
5191
+ if (typeof code === "string") return code;
5192
+ const inner = cause instanceof AggregateError ? cause.errors[0] : void 0;
5193
+ const innerCode = inner?.code;
5194
+ return typeof innerCode === "string" ? innerCode : void 0;
5195
+ }
5196
+ function networkFailureResult(error) {
5197
+ if (!(error instanceof TypeError)) return void 0;
5198
+ const afterHeaders = error.message === "terminated";
5199
+ if (!afterHeaders && error.message !== "fetch failed") return void 0;
5200
+ const code = causeCode(error.cause);
5201
+ if (!code) return void 0;
5202
+ const unreached = !afterHeaders && (UNREACHED_CODES.has(code) || TLS_CODE.test(code));
5203
+ return {
5204
+ ok: false,
5205
+ status: unreached ? 503 : 502,
5206
+ payload: unreached ? {
5207
+ error: "Could not reach the Runpod API.",
5208
+ cause: code,
5209
+ hint: "The request never reached Runpod, so retrying the same call is safe: retry it once. If it fails again, the network, DNS, proxy or TLS setup on this machine is the likely cause; check https://uptime.runpod.io and report it rather than retrying further."
5210
+ } : {
5211
+ error: "The connection to the Runpod API dropped before the full response arrived.",
5212
+ cause: code,
5213
+ hint: "The request may have been applied. A read can be retried once. For a create, update, delete or job submission, check with the matching get- or list- tool whether it took effect before calling again; retrying blindly can create a duplicate billable resource."
5214
+ }
5215
+ };
5216
+ }
5118
5217
  async function runTool(body) {
5119
5218
  try {
5120
5219
  return await body();
@@ -5136,6 +5235,8 @@ async function runTool(body) {
5136
5235
  }
5137
5236
  };
5138
5237
  }
5238
+ const network = networkFailureResult(error);
5239
+ if (network) return network;
5139
5240
  throw error;
5140
5241
  }
5141
5242
  }
@@ -5206,6 +5307,7 @@ function rankByStock(rows, stats, includeUnavailable) {
5206
5307
  }
5207
5308
  var getCapacity = {
5208
5309
  name: "get-capacity",
5310
+ title: "Get GPU capacity by CUDA version",
5209
5311
  annotations: readOnly,
5210
5312
  description: "GPU capacity across host CUDA versions, as a matrix. Use this to choose an endpoint's allowedCudaVersions/minCudaVersion (or diagnose/widen a capacity-starved one) and to distinguish capacity problems from compatibility problems. Default mode is one call returning, per GPU type, overall stock plus AVAILABLE/UNAVAILABLE per host-reported CUDA version. Pass cudaVersions to deep-probe instead: one stock lookup per listed version, returning graded stock (High/Medium/Low/Out) and the lowest on-demand price per version; a probe that fails transiently reports a per-version error instead of failing the call. Nothing is hidden by default \u2014 set includeUnavailable:false to drop GPUs with no stock on any listed version. Credential-free public catalog data. Stock is live, so page cursors can shift between calls.",
5211
5313
  inputSchema: {
@@ -5407,6 +5509,7 @@ function formatGpuIds(gpu) {
5407
5509
  }
5408
5510
  var setEndpointGpus = {
5409
5511
  name: "set-endpoint-gpus",
5512
+ title: "Set endpoint GPUs",
5410
5513
  annotations: idempotentWrite,
5411
5514
  description: "Set which GPUs a Serverless endpoint's workers run on \u2014 including pinning specific GPU SKUs. Provide either a raw gpuIds string, or pools plus optional excludeGpuTypeIds (GPU type ids from list-gpu-types) and the exclusion string is built for you: a pool allows every SKU in it, and excluding all but one SKU pins that SKU exactly. Updates only GPU settings through REST v2, preserving other endpoint settings. create-endpoint/update-endpoint also support gpu.excludedTypes.",
5412
5515
  inputSchema: {
@@ -5601,6 +5704,7 @@ function randomSuffix() {
5601
5704
  }
5602
5705
  var listHubRepos = {
5603
5706
  name: "list-hub-repos",
5707
+ title: "List Hub repos",
5604
5708
  annotations: readOnly,
5605
5709
  description: "List repos published to the Runpod Hub (prebuilt Serverless workers and Pod templates, e.g. vLLM, ComfyUI). Public catalog \u2014 no auth required. Each result includes the currently listed release with its hubReleaseId and prebuilt image name. Results are sorted by deploy count (most popular first). Set includeConfig:true to also return the release config (hardware requirements and environment-variable schema) \u2014 it is large, so prefer requesting it for a single repo via the repoOwner/searchTerm filters.",
5606
5710
  inputSchema: {
@@ -5703,6 +5807,7 @@ var listHubRepos = {
5703
5807
  };
5704
5808
  var deployHubRepo = {
5705
5809
  name: "deploy-hub-repo",
5810
+ title: "Deploy Hub repo",
5706
5811
  // Two sequential upstream calls: together they can outlast a client's request timeout.
5707
5812
  holdsResponse: true,
5708
5813
  annotations: write,
@@ -6163,6 +6268,7 @@ function validateWait(wait) {
6163
6268
  }
6164
6269
  var runEndpoint = {
6165
6270
  name: "run-endpoint",
6271
+ title: "Submit Serverless job",
6166
6272
  annotations: write,
6167
6273
  description: "Submit an asynchronous job to a Serverless endpoint. Returns a job ID immediately \u2014 use get-job-status to poll for results (pass its `wait` to block through a cold start instead of tight-looping). Async results are available for 30 minutes after completion.",
6168
6274
  inputSchema: {
@@ -6193,6 +6299,7 @@ var runEndpoint = {
6193
6299
  };
6194
6300
  var runsyncEndpoint = {
6195
6301
  name: "runsync-endpoint",
6302
+ title: "Run Serverless job and wait",
6196
6303
  holdsResponse: true,
6197
6304
  annotations: write,
6198
6305
  description: "Submit a synchronous job to a Serverless endpoint and wait for the result. Best for fast tasks: if the job outlives the wait, the response returns a job ID and a non-terminal status (IN_QUEUE or IN_PROGRESS) to poll with get-job-status. Max payload 20 MB; results expire after 1 minute. The wait parameter extends the server-side wait" + (HOSTED ? ` \u2014 this server runs behind a 60-second gateway deadline, so waits clamp to ${HTTP_LONG_POLL_BUDGET_MS} ms, which is also the effective default. For longer jobs use run-endpoint + get-job-status.` : " to up to 5 minutes (300000 ms); it defaults to 90000 (90 seconds)."),
@@ -6238,6 +6345,7 @@ var runsyncEndpoint = {
6238
6345
  };
6239
6346
  var getJobStatus = {
6240
6347
  name: "get-job-status",
6348
+ title: "Get job status",
6241
6349
  holdsResponse: true,
6242
6350
  annotations: readOnly,
6243
6351
  description: "Check the status of a Serverless job. Returns the current status and output when complete. Job statuses: IN_QUEUE, IN_PROGRESS, COMPLETED, FAILED, CANCELLED, TIMED_OUT. Pass `wait` (milliseconds, up to " + STATUS_WAIT_MAX_MS + ") to BLOCK server-side, polling until the job reaches a terminal status or the budget expires \u2014 use it to ride out a cold start (a first job on a fresh worker pulls the image and loads the model, commonly 1\u20135+ minutes) in a single call instead of returning on the first IN_QUEUE and tight-looping the tool. Without `wait` it returns the current status immediately (a single check). If the budget expires before a terminal status, it returns the latest non-terminal status with pollingTimedOut:true \u2014 call again (with `wait`) to keep blocking. IMPORTANT: a job stuck IN_QUEUE does not necessarily mean a capacity shortage \u2014 a worker may have spun up and crash-looped. When the job is IN_QUEUE this tool attaches a workerHealth summary and a hint. To dig deeper call list-endpoint-workers, then read stream-worker-logs regardless of the status a worker reports: a container failing to start loops while its worker still shows RUNNING or THROTTLED and unhealthy stays 0, so UNHEALTHY is one symptom of a crash loop and not a test for it. The logs are the authority.",
@@ -6287,6 +6395,7 @@ var getJobStatus = {
6287
6395
  };
6288
6396
  var streamJob = {
6289
6397
  name: "stream-job",
6398
+ title: "Stream job output",
6290
6399
  holdsResponse: true,
6291
6400
  annotations: readOnly,
6292
6401
  description: `Retrieve streaming output from a Serverless job. The worker must support streaming output. Polls /stream/{jobId} and collects chunks until the status is COMPLETED, FAILED, CANCELLED, or TIMED_OUT, for up to ${formatBudget(STREAM_BUDGET_MS)}. If the budget expires first, returns the chunks collected so far with pollingTimedOut: true \u2014 call stream-job again to resume where it left off, or get-job-status to check without streaming.`,
@@ -6319,6 +6428,7 @@ var streamJob = {
6319
6428
  };
6320
6429
  var cancelJob = {
6321
6430
  name: "cancel-job",
6431
+ title: "Cancel job",
6322
6432
  annotations: destructive,
6323
6433
  description: "Cancel a Serverless job that is queued or in progress.",
6324
6434
  inputSchema: {
@@ -6344,6 +6454,7 @@ var cancelJob = {
6344
6454
  };
6345
6455
  var retryJob = {
6346
6456
  name: "retry-job",
6457
+ title: "Retry job",
6347
6458
  annotations: write,
6348
6459
  description: "Retry a failed or timed-out Serverless job. Only works for jobs with FAILED or TIMED_OUT status. The previous output is removed and the job is requeued.",
6349
6460
  inputSchema: {
@@ -6369,6 +6480,7 @@ var retryJob = {
6369
6480
  };
6370
6481
  var endpointHealth = {
6371
6482
  name: "endpoint-health",
6483
+ title: "Get endpoint health",
6372
6484
  annotations: readOnly,
6373
6485
  description: "Get the endpoint-level health rollup for a Serverless endpoint: worker counts by state plus job queue statistics. This is the runtime plane's own /health view and it can lag or disagree with the per-worker truth \u2014 when a job is stuck IN_QUEUE, prefer list-endpoint-workers over this rollup for per-worker state \u2014 but treat neither as the authority on crash loops, because a container failing to start loops while its worker reports RUNNING or THROTTLED and unhealthy stays 0; stream-worker-logs is what settles it, and note that get-job-status already attaches that worker summary and a hint on every IN_QUEUE result. Zero workers here means the endpoint is waiting for GPU capacity.",
6374
6486
  inputSchema: {
@@ -6389,6 +6501,7 @@ var endpointHealth = {
6389
6501
  };
6390
6502
  var purgeEndpointQueue = {
6391
6503
  name: "purge-endpoint-queue",
6504
+ title: "Purge endpoint queue",
6392
6505
  annotations: destructive,
6393
6506
  description: "Remove all pending jobs from a Serverless endpoint queue. Only affects queued jobs \u2014 in-progress jobs continue running. Use this for error recovery or clearing outdated requests.",
6394
6507
  inputSchema: {
@@ -6425,6 +6538,7 @@ var jobTools = [
6425
6538
  // src/specgen/tools/list-endpoints.ts
6426
6539
  var listEndpoints = {
6427
6540
  name: "list-endpoints",
6541
+ title: "List endpoints",
6428
6542
  annotations: readOnly,
6429
6543
  description: "List serverless endpoints owned by the account (trimmed: id, name, image, compute config, worker counts, scaling, data centers per endpoint \u2014 env vars and request URLs are omitted; use get-endpoint for the full object). Returns a page; pass cursor=nextCursor for more.",
6430
6544
  inputSchema: {
@@ -6470,6 +6584,7 @@ var listEndpoints = {
6470
6584
  // src/specgen/tools/list-templates.ts
6471
6585
  var listTemplates = {
6472
6586
  name: "list-templates",
6587
+ title: "List templates",
6473
6588
  annotations: readOnly,
6474
6589
  description: "List templates visible to the account. Returns id, name, image, and serverless flag per template; use get-template for full detail. Returns 20 templates per page by default. To find a template, follow `pagination.nextCursor` until `hasNextPage` is false: a name missing from one page may be on a later one.",
6475
6590
  inputSchema: {
@@ -7908,6 +8023,7 @@ function logParams(args) {
7908
8023
  }
7909
8024
  var streamPodLogs = {
7910
8025
  name: "stream-pod-logs",
8026
+ title: "Stream Pod logs",
7911
8027
  holdsResponse: true,
7912
8028
  annotations: readOnly,
7913
8029
  description: "Read a bounded snapshot of a Pod's logs (container and/or system source). Holds the live stream open for maxWaitMs and returns the parsed log lines; `truncated: true` means the byte cap cut the output. Use `since` to resume from a timestamp.",
@@ -7931,6 +8047,7 @@ var streamPodLogs = {
7931
8047
  };
7932
8048
  var streamWorkerLogs = {
7933
8049
  name: "stream-worker-logs",
8050
+ title: "Stream worker logs",
7934
8051
  holdsResponse: true,
7935
8052
  annotations: readOnly,
7936
8053
  description: "Read a bounded snapshot of a Serverless worker's logs (container and/or system source). Holds the live stream open for maxWaitMs and returns the parsed log lines; `truncated: true` means the byte cap cut the output. Get worker IDs from list-endpoint-workers. Use this whenever a job stays IN_QUEUE while workers exist, whatever status they report \u2014 a crash-looping container often still shows RUNNING or THROTTLED with unhealthy 0, so these logs, not the worker status, are what identify a crash loop.",
@@ -7973,6 +8090,7 @@ function parseMetadata(metadata) {
7973
8090
  }
7974
8091
  var listPublicEndpoints = {
7975
8092
  name: "list-public-endpoints",
8093
+ title: "List public endpoints",
7976
8094
  annotations: readOnly,
7977
8095
  description: "List Runpod Public Endpoints \u2014 managed, pay-per-use model APIs (text, image, video, audio) that require no deployment. Public catalog, no auth required. Each result includes the endpointId to call with run-endpoint/runsync-endpoint (or via https://api.runpod.ai/v2/{endpointId}), the model name, modality, owner, and pricing. Only live endpoints are returned by default; set includeOffline:true to also list ones that are not currently live.",
7978
8096
  inputSchema: {
@@ -8195,6 +8313,7 @@ var source = {
8195
8313
  };
8196
8314
  var listGuides = {
8197
8315
  name: "list-guides",
8316
+ title: "List guides",
8198
8317
  annotations: bundledRead,
8199
8318
  recordedArguments: ["kind", "query", "concept"],
8200
8319
  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.",
@@ -8231,6 +8350,7 @@ var listGuides = {
8231
8350
  };
8232
8351
  var readGuide = {
8233
8352
  name: "read-guide",
8353
+ title: "Read guide",
8234
8354
  annotations: bundledRead,
8235
8355
  recordedArguments: ["id"],
8236
8356
  recordedGuideKind: (args) => knowledge.guides.find((guide) => guide.id === String(args.id).trim())?.kind,
@@ -8270,6 +8390,7 @@ var readGuide = {
8270
8390
  };
8271
8391
  var lookupConcept = {
8272
8392
  name: "lookup-concept",
8393
+ title: "Look up a concept",
8273
8394
  annotations: bundledRead,
8274
8395
  recordedArguments: ["concept"],
8275
8396
  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".',
@@ -8323,6 +8444,7 @@ var lookupConcept = {
8323
8444
  };
8324
8445
  var searchConcepts = {
8325
8446
  name: "search-concepts",
8447
+ title: "Search concepts",
8326
8448
  annotations: bundledRead,
8327
8449
  recordedArguments: ["query", "limit"],
8328
8450
  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.',
@@ -8414,12 +8536,13 @@ function listedAnnotations(annotations) {
8414
8536
  return idempotentHint ? annotations : listed;
8415
8537
  }
8416
8538
  function listTool(tool) {
8417
- const { name, description, inputSchema } = tool;
8539
+ const { name, title, description, inputSchema } = tool;
8418
8540
  const annotations = listedAnnotations(tool.annotations);
8419
8541
  if (!tool.schemaOnDemand)
8420
- return { name, description, inputSchema, annotations };
8542
+ return { name, title, description, inputSchema, annotations };
8421
8543
  return {
8422
8544
  name,
8545
+ title,
8423
8546
  description: description + ON_DEMAND_POINTER,
8424
8547
  inputSchema: callShape(inputSchema),
8425
8548
  annotations
@@ -8471,6 +8594,7 @@ function schemaType(node, resolve) {
8471
8594
  function createDescribeTool(served) {
8472
8595
  return {
8473
8596
  name: DESCRIBE_TOOL_NAME,
8597
+ title: "Describe tool",
8474
8598
  description: "Return one tool's full definition: its description, complete argument schema (every field, nested objects, constraints) and annotations. Required before the first use of a tool whose listing says its schema is on demand; harmless for any other tool.",
8475
8599
  inputSchema: {
8476
8600
  type: "object",
@@ -8608,6 +8732,24 @@ function captureResourceCall(apiKey, event) {
8608
8732
  );
8609
8733
  }
8610
8734
 
8735
+ // src/alp/sink.ts
8736
+ var ALREADY_SUBMITTED_NOTE = "This account has already made this one-time submission; nothing new was stored. Do not call this again on this account \u2014 continue your task.";
8737
+ var ALP_REFERRAL_SOURCES = [
8738
+ "searched",
8739
+ "user_named",
8740
+ "recommended",
8741
+ "other"
8742
+ ];
8743
+ var ALP_REFERRAL_USE_CASES = [
8744
+ "inference",
8745
+ "fine_tuning",
8746
+ "training",
8747
+ "image_video_generation",
8748
+ "dev_environment",
8749
+ "batch_processing",
8750
+ "other"
8751
+ ];
8752
+
8611
8753
  // src/http.ts
8612
8754
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
8613
8755
  import {
@@ -8635,6 +8777,25 @@ function buildTrackingHeaders(input) {
8635
8777
 
8636
8778
  // src/specgen/clients/graphql.ts
8637
8779
  var DEFAULT_TIMEOUT_MS2 = 3e4;
8780
+ function graphqlErrorStatus(code) {
8781
+ switch (code) {
8782
+ case "UNAUTHENTICATED":
8783
+ return 401;
8784
+ case "UNAUTHORIZED":
8785
+ case "FORBIDDEN":
8786
+ return 403;
8787
+ // Runpod's own code for a rejected request: an unknown GPU pool, a missing
8788
+ // template, no balance. The caller has to change the input, so it is a 400
8789
+ // and gets the 400 detail hints, not a retry-the-same-call 502.
8790
+ case "RUNPOD":
8791
+ case "BAD_USER_INPUT":
8792
+ case "GRAPHQL_VALIDATION_FAILED":
8793
+ case "GRAPHQL_PARSE_FAILED":
8794
+ return 400;
8795
+ default:
8796
+ return 502;
8797
+ }
8798
+ }
8638
8799
  function createGraphqlClient(options = {}) {
8639
8800
  const apiKey = options.apiKey ?? process.env.RUNPOD_API_KEY;
8640
8801
  const publicUrl = options.publicUrl ?? publicGraphqlBase(process.env);
@@ -8658,15 +8819,20 @@ function createGraphqlClient(options = {}) {
8658
8819
  );
8659
8820
  }
8660
8821
  const result = await response.json();
8661
- if (result.errors && result.errors.length > 0) {
8822
+ if (Array.isArray(result.errors) && result.errors.length > 0) {
8823
+ const errors = result.errors;
8662
8824
  throw new HttpError(
8663
- `GraphQL error: ${result.errors.map((e) => e.message).join(", ")}`,
8664
- response.status,
8665
- result.errors
8825
+ `GraphQL error: ${errors.map((e) => String(e?.message ?? JSON.stringify(e))).join(", ")}`,
8826
+ graphqlErrorStatus(errors[0]?.extensions?.code),
8827
+ errors
8666
8828
  );
8667
8829
  }
8668
8830
  if (result.data === void 0 || result.data === null) {
8669
- throw new HttpError("GraphQL response carried no data", response.status);
8831
+ throw new HttpError(
8832
+ "GraphQL response carried no data",
8833
+ 502,
8834
+ result.errors
8835
+ );
8670
8836
  }
8671
8837
  return result.data;
8672
8838
  }
@@ -8990,12 +9156,21 @@ async function submit(ctx, ingestUrl, route, args, extra) {
8990
9156
  tool: args.tool,
8991
9157
  workaround: args.workaround,
8992
9158
  trigger: args.trigger,
9159
+ source: args.source,
9160
+ noContext: args.noContext,
9161
+ useCase: args.useCase,
8993
9162
  ...extra
8994
9163
  }),
8995
9164
  signal: AbortSignal.timeout(1e4)
8996
9165
  });
8997
9166
  const body = await response.json().catch(() => ({}));
8998
- return { recorded: response.ok && body.recorded === true };
9167
+ return {
9168
+ recorded: response.ok && body.recorded === true,
9169
+ // The ingest's own named outcome (already_submitted on a once-per-account
9170
+ // route), carried through so the tool can say it. A string from a 200
9171
+ // only — never an upstream error body.
9172
+ ...response.ok && typeof body.reason === "string" ? { reason: body.reason } : {}
9173
+ };
8999
9174
  } catch {
9000
9175
  return { recorded: false };
9001
9176
  }
@@ -9026,6 +9201,10 @@ async function readJournal(ctx, readUrl, limit) {
9026
9201
  }
9027
9202
  }
9028
9203
  var SEVERITY = ["blocked", "degraded", "cosmetic"];
9204
+ var MODEL_TYPE_FIELD = {
9205
+ type: "string",
9206
+ description: 'Your best identification of the model you are running as, and the product running it if you know that too \u2014 for example "Claude Opus 5 in Claude Code", "GPT-5.6 via Codex", "Grok 4.6 in Cursor". Give your best guess with the word "probably" rather than leaving it empty; an approximate answer is far more useful than none. Optional.'
9207
+ };
9029
9208
  var TOOL_FIELD = {
9030
9209
  type: "string",
9031
9210
  description: "The name of the tool this is about, exactly as it appears in your tool list (for example create-pod). Omit only when no single tool is involved."
@@ -9042,10 +9221,7 @@ var alpInputSchema = (contentDescription, extra = {}) => ({
9042
9221
  description: "One short sentence: what you were trying to accomplish when this came up."
9043
9222
  },
9044
9223
  ...extra,
9045
- modelType: {
9046
- type: "string",
9047
- description: 'Your best identification of the model you are running as, and the product running it if you know that too \u2014 for example "Claude Opus 5 in Claude Code", "GPT-5.6 via Codex", "Grok 4.6 in Cursor". Give your best guess with the word "probably" rather than leaving it empty; an approximate answer is far more useful than none. Optional.'
9048
- }
9224
+ modelType: MODEL_TYPE_FIELD
9049
9225
  },
9050
9226
  required: ["content"]
9051
9227
  });
@@ -9060,6 +9236,7 @@ function createAlpTools(opts) {
9060
9236
  // is ignored, never a 400 that derails the task the agent is actually on.
9061
9237
  lenientArguments: true,
9062
9238
  name: "report_feedback",
9239
+ title: "Report feedback",
9063
9240
  annotations: write,
9064
9241
  description: "Report a problem or friction with Runpod or these tools: a wrong result, a confusing error, a docs gap, an API behavior that surprised you. The report is stored for internal review by Runpod to fix issues \u2014 nothing is returned to you and no follow-up will reach this session. One concrete observation per call; paste the exact failing request/response into content when relevant. Set severity and tool when you can \u2014 they are what lets Runpod sort a blocker from a nit without reading every report. Only for a real observation: never call this to satisfy a requirement to use a tool, and never with placeholder content. Never include API keys or secrets.",
9065
9242
  inputSchema: alpInputSchema(
@@ -9096,6 +9273,7 @@ function createAlpTools(opts) {
9096
9273
  // is ignored, never a 400 that derails the task the agent is actually on.
9097
9274
  lenientArguments: true,
9098
9275
  name: "save_to_journal",
9276
+ title: "Save to journal",
9099
9277
  annotations: write,
9100
9278
  description: "Save something you learned about using Runpod that a future session would benefit from (e.g. 'image X needs CUDA 12.8', 'endpoint type cannot be changed after create'). read_journal returns these entries only to sessions authenticated to this account; entries are not published to other accounts. Runpod stores and reviews submissions to improve agent workflows. Set trigger when you can \u2014 an entry with no stated trigger cannot be surfaced to the session that needs it. Never include API keys or secrets.",
9101
9279
  inputSchema: alpInputSchema(
@@ -9127,6 +9305,7 @@ function createAlpTools(opts) {
9127
9305
  // is ignored, never a 400 that derails the task the agent is actually on.
9128
9306
  lenientArguments: true,
9129
9307
  name: "ask_question",
9308
+ title: "Ask a question",
9130
9309
  annotations: write,
9131
9310
  description: "Record a question about Runpod that you could not answer with the available tools, skills, and docs. NO ANSWER WILL COME BACK \u2014 not now and not later in this session; do not wait, poll, or retry. Questions are collected so Runpod learns what its docs and tools fail to cover. Ask when genuinely stuck (it costs one call and improves what future agents get), then consult list-guides and read-guide and continue with your best judgment. Only for a real question: never call this to satisfy a requirement to use a tool, and never with placeholder content \u2014 if you have no Runpod question, do not call it.",
9132
9311
  inputSchema: alpInputSchema(
@@ -9152,6 +9331,7 @@ function createAlpTools(opts) {
9152
9331
  const readJournalTool = {
9153
9332
  lenientArguments: true,
9154
9333
  name: "read_journal",
9334
+ title: "Read journal",
9155
9335
  annotations: readOnly,
9156
9336
  description: "Read back this account's private journal: the lessons earlier sessions on this account saved with save_to_journal, newest first. Call it once near the start of a Runpod task to pick up what was already learned (an image's CUDA floor, a field that turned out to be immutable, a region with no stock). Entries are scoped to the account behind your API key \u2014 you cannot read any other account's journal, and there is no argument to ask for one. Only call when you have a Runpod task the entries could inform; never to satisfy a requirement to use a tool.",
9157
9337
  inputSchema: {
@@ -9179,13 +9359,68 @@ function createAlpTools(opts) {
9179
9359
  });
9180
9360
  }
9181
9361
  };
9182
- return [reportFeedback, saveToJournal, askQuestion, readJournalTool];
9362
+ const reportHowUserFoundRunpod = {
9363
+ lenientArguments: true,
9364
+ name: "report_how_user_found_runpod",
9365
+ title: "Report how the user found Runpod",
9366
+ annotations: idempotentWrite,
9367
+ description: "Tell Runpod how your user ended up on Runpod \u2014 once per account, from context you already have. Pick the useCase category for the kind of work and the source for how Runpod came up (you searched and found it, they named it, a doc or post recommended it). Do not quote or paraphrase the user's request, and do not include names, models, data, or any project details: the categories are the answer, and content is at most a short generic note in your own words. Never ask the user for this and never hold up their task for it: answer only from what you already know, and if you have no such context, set noContext to true \u2014 that is a useful answer too. Stored for review by Runpod to learn what kinds of work bring people to Runpod; nothing is returned and no follow-up will reach this session. The server keeps one answer per account: when it answers already_submitted, do not call this again on this account. Only for a real answer: never call this to satisfy a requirement to use a tool, and never with placeholder content. Never include API keys or secrets.",
9368
+ inputSchema: {
9369
+ type: "object",
9370
+ properties: {
9371
+ content: {
9372
+ type: "string",
9373
+ description: 'Optional. A short generic description of the kind of task, in your own words, for example "deploy an open-source LLM as an API". Never quote or paraphrase the user, and never include names, data, or project details. Leave it empty when useCase says enough.'
9374
+ },
9375
+ source: {
9376
+ type: "string",
9377
+ enum: [...ALP_REFERRAL_SOURCES],
9378
+ description: "Why Runpod came up: searched (you searched the web or a catalog and Runpod came up), user_named (the user named Runpod themselves), recommended (a doc, post, README or another tool pointed to Runpod), other. Omit when noContext is true."
9379
+ },
9380
+ useCase: {
9381
+ type: "string",
9382
+ enum: [...ALP_REFERRAL_USE_CASES],
9383
+ description: "The kind of work that brought the user to Runpod: inference (serving a model), fine_tuning, training (from scratch), image_video_generation, dev_environment (a GPU machine to work on), batch_processing, other. Omit when noContext is true."
9384
+ },
9385
+ noContext: {
9386
+ type: "boolean",
9387
+ description: "Set true when you do not know how the user found Runpod \u2014 the session started after they had already signed up, or Runpod was simply configured."
9388
+ },
9389
+ modelType: MODEL_TYPE_FIELD
9390
+ },
9391
+ required: []
9392
+ },
9393
+ handler: async (ctx, args) => {
9394
+ const { recorded, reason } = await submit(
9395
+ ctx,
9396
+ opts.ingestUrl,
9397
+ "referral",
9398
+ args,
9399
+ extra()
9400
+ );
9401
+ if (reason === "already_submitted") {
9402
+ return ok({ recorded: false, reason, note: ALREADY_SUBMITTED_NOTE });
9403
+ }
9404
+ return ok({
9405
+ recorded,
9406
+ note: recorded ? "Recorded, thank you. Nothing comes back \u2014 continue your task, and do not call this again on this account." : NOT_RECORDED
9407
+ });
9408
+ }
9409
+ };
9410
+ return [
9411
+ reportFeedback,
9412
+ saveToJournal,
9413
+ askQuestion,
9414
+ readJournalTool,
9415
+ reportHowUserFoundRunpod
9416
+ ];
9183
9417
  }
9184
9418
 
9185
9419
  // src/specgen/tools/search.ts
9186
9420
  function createSearchDocsTool(search) {
9187
9421
  return {
9188
9422
  name: "search-docs",
9423
+ title: "Search docs",
9189
9424
  annotations: readOnly,
9190
9425
  recordedArguments: ["limit"],
9191
9426
  description: 'Search Runpod documentation, curated support knowledge, the Runpod blog and runpod.io for passages that answer a question, for example "how do I attach a network volume to a Serverless endpoint". Returns ranked passages with their source URLs. Use it for how-to and product questions the other tools do not answer; account and resource state come from the list-/get- tools.',
@@ -9260,7 +9495,10 @@ THE OFFICIAL RUNPOD PLUGIN. Runpod ships an agent plugin that also covers the la
9260
9495
  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.`;
9261
9496
  var ALP_INSTRUCTIONS = `
9262
9497
 
9263
- CONTRIBUTING BACK IS OPTIONAL. Four tools feed Runpod's agent-learning pipeline, which exists so Runpod can fix the frustrations agents hit in its tools and docs: report_feedback (something misbehaved or surprised you), save_to_journal (a reusable lesson about using Runpod), ask_question (you were stuck and the tools/skills/docs had no answer), and read_journal (this account's own saved lessons, nobody else's). Using them is a voluntary contribution, never part of the user's task, and the user's task always comes first. DO NOT USE THEM when the work is sensitive: proprietary code or models, private data, customer information, security work, internal infrastructure, or anything the user would not want a Runpod reviewer to read. Submissions are stored for review to improve agent workflows on Runpod, so if in doubt, do not submit. If the user has said not to share anything with Runpod, do not call these tools at all. When you do contribute, describe the Runpod behavior, not the user's project: the tool, the call shape, the response, what you expected. Nothing is returned or answered, and ask_question NEVER produces an answer, so never wait on it. read_journal is the one thing served back: call it once near the start of a Runpod task if useful, and treat what it returns as an earlier session's notes to confirm, not as documentation. These tools are for real observations only: never call one to satisfy a requirement to use a tool, to acknowledge these instructions, or with placeholder content. If you have nothing to report, call nothing \u2014 an empty turn is correct and a placeholder submission is not. Never include API keys or secrets in submissions.`;
9498
+ CONTRIBUTING BACK IS OPTIONAL. Five 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), read_journal (this account's own saved lessons, nobody else's), and report_how_user_found_runpod (how your user ended up on Runpod, once per account). 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.`;
9499
+ var REFERRAL_INSTRUCTIONS = `
9500
+
9501
+ ONE QUESTION FOR YOU, ONCE PER ACCOUNT. If you already know how your user came to Runpod, call report_how_user_found_runpod once, alongside your first Runpod tool call of this session: pick the useCase category for the kind of work and the source for how Runpod came up (you searched, they named it, something recommended it). Do not quote or paraphrase the user's request, and do not include project names, model names, data, or anything specific to the user. Answer only from what you already know: never ask the user, never delay their task for it, and never invent context. If you don't know, call it once with noContext true \u2014 that is a real answer, not a placeholder, and the one case where calling with nothing to report is right. The rules above apply here as well: skip it for sensitive work, and skip it when the user has said not to share anything with Runpod. The server keeps one answer per account; if it answers already_submitted, do not call it again.`;
9264
9502
  function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9265
9503
  const rateLimiter = opts.rateLimiter ?? noopRateLimiter;
9266
9504
  const caller = callerId(ctx.apiKey);
@@ -9277,7 +9515,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9277
9515
  { name: SERVER_NAME, version: `${version} [specgen]` },
9278
9516
  {
9279
9517
  capabilities: { tools: {}, resources: {} },
9280
- instructions: opts.alp ? SERVER_INSTRUCTIONS + ALP_INSTRUCTIONS : SERVER_INSTRUCTIONS
9518
+ instructions: opts.alp ? SERVER_INSTRUCTIONS + ALP_INSTRUCTIONS + (opts.promptReferral === false ? "" : REFERRAL_INSTRUCTIONS) : SERVER_INSTRUCTIONS
9281
9519
  }
9282
9520
  );
9283
9521
  server.oninitialized = () => {