@runpod/mcp-server 4.3.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/CHANGELOG.md +35 -0
- package/README.md +16 -0
- package/dist/{chunk-DDX42UTT.mjs → chunk-4GF3CMSH.mjs} +6 -2
- package/dist/{chunk-DDX42UTT.mjs.map → chunk-4GF3CMSH.mjs.map} +1 -1
- package/dist/http.cjs +523 -36
- package/dist/http.cjs.map +1 -1
- package/dist/http.d.cts +12 -1
- package/dist/http.d.ts +12 -1
- package/dist/http.mjs +522 -36
- package/dist/http.mjs.map +1 -1
- package/dist/stdio.cjs +384 -20
- package/dist/stdio.cjs.map +1 -1
- package/dist/stdio.mjs +380 -21
- package/dist/stdio.mjs.map +1 -1
- package/dist/{wizard-L25AYBNQ.mjs → wizard-KKTBTVXE.mjs} +4 -3
- package/dist/wizard-KKTBTVXE.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/wizard-L25AYBNQ.mjs.map +0 -1
package/dist/http.mjs
CHANGED
|
@@ -6,6 +6,80 @@ import {
|
|
|
6
6
|
JSONRPCMessageSchema
|
|
7
7
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
8
8
|
|
|
9
|
+
// src/alp/sink.ts
|
|
10
|
+
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.";
|
|
11
|
+
var ALP_REFERRAL_SOURCES = [
|
|
12
|
+
"searched",
|
|
13
|
+
"user_named",
|
|
14
|
+
"recommended",
|
|
15
|
+
"other"
|
|
16
|
+
];
|
|
17
|
+
var ALP_REFERRAL_USE_CASES = [
|
|
18
|
+
"inference",
|
|
19
|
+
"fine_tuning",
|
|
20
|
+
"training",
|
|
21
|
+
"image_video_generation",
|
|
22
|
+
"dev_environment",
|
|
23
|
+
"batch_processing",
|
|
24
|
+
"other"
|
|
25
|
+
];
|
|
26
|
+
function isSinkUrl(value) {
|
|
27
|
+
try {
|
|
28
|
+
const url = new URL(value);
|
|
29
|
+
return url.protocol === "https:" && url.hostname.endsWith(".convex.site") && url.pathname === "/alp/submit";
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function sinkRouteUrl(submitUrl, path) {
|
|
35
|
+
if (!isSinkUrl(submitUrl)) return null;
|
|
36
|
+
const url = new URL(submitUrl);
|
|
37
|
+
url.pathname = path;
|
|
38
|
+
return url.toString();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/alp/answered.ts
|
|
42
|
+
var ANSWERED_TIMEOUT_MS = 2500;
|
|
43
|
+
async function hasAnswered(identity, route, opts = {}) {
|
|
44
|
+
const env = opts.env ?? process.env;
|
|
45
|
+
const submitUrl = env.ALP_SINK_URL;
|
|
46
|
+
const sinkSecret = env.ALP_SINK_SECRET;
|
|
47
|
+
if (!identity || !submitUrl || !sinkSecret) return null;
|
|
48
|
+
const url = sinkRouteUrl(submitUrl, "/alp/answered");
|
|
49
|
+
if (!url) {
|
|
50
|
+
console.warn("alp_sink_misconfigured");
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const sinkFetch = opts.sinkFetch ?? fetch;
|
|
55
|
+
const response = await sinkFetch(url, {
|
|
56
|
+
method: "POST",
|
|
57
|
+
headers: {
|
|
58
|
+
"Content-Type": "application/json",
|
|
59
|
+
"X-ALP-Secret": sinkSecret
|
|
60
|
+
},
|
|
61
|
+
// The resolved account id and the route, nothing from any request body.
|
|
62
|
+
body: JSON.stringify({ identity, route }),
|
|
63
|
+
signal: AbortSignal.timeout(ANSWERED_TIMEOUT_MS)
|
|
64
|
+
});
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
console.warn("alp_answered_sink_error", { status: response.status });
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const body = await response.json().catch(() => null);
|
|
70
|
+
if (body?.ok !== true || typeof body.answered !== "boolean") {
|
|
71
|
+
console.warn("alp_answered_sink_unrecognized", {
|
|
72
|
+
status: response.status
|
|
73
|
+
});
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return body.answered;
|
|
77
|
+
} catch {
|
|
78
|
+
console.warn("alp_answered_sink_unreachable");
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
9
83
|
// src/specgen/server.ts
|
|
10
84
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
11
85
|
import {
|
|
@@ -337,6 +411,7 @@ function coerceScalars(value, schema, defs, depth = 0) {
|
|
|
337
411
|
var generatedTools = [
|
|
338
412
|
{
|
|
339
413
|
name: "create-cluster",
|
|
414
|
+
title: "Create cluster",
|
|
340
415
|
operationId: "createCluster",
|
|
341
416
|
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.",
|
|
342
417
|
method: "POST",
|
|
@@ -574,6 +649,7 @@ var generatedTools = [
|
|
|
574
649
|
},
|
|
575
650
|
{
|
|
576
651
|
name: "create-delegation",
|
|
652
|
+
title: "Create ECR delegation",
|
|
577
653
|
operationId: "createDelegation",
|
|
578
654
|
description: "Register an ECR delegation",
|
|
579
655
|
method: "POST",
|
|
@@ -619,6 +695,7 @@ var generatedTools = [
|
|
|
619
695
|
},
|
|
620
696
|
{
|
|
621
697
|
name: "create-endpoint",
|
|
698
|
+
title: "Create endpoint",
|
|
622
699
|
operationId: "createEndpoint",
|
|
623
700
|
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.",
|
|
624
701
|
method: "POST",
|
|
@@ -1064,6 +1141,7 @@ var generatedTools = [
|
|
|
1064
1141
|
},
|
|
1065
1142
|
{
|
|
1066
1143
|
name: "create-global-volume",
|
|
1144
|
+
title: "Create global volume",
|
|
1067
1145
|
operationId: "createGlobalVolume",
|
|
1068
1146
|
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`.",
|
|
1069
1147
|
method: "POST",
|
|
@@ -1105,6 +1183,7 @@ var generatedTools = [
|
|
|
1105
1183
|
},
|
|
1106
1184
|
{
|
|
1107
1185
|
name: "create-network-volume",
|
|
1186
|
+
title: "Create network volume",
|
|
1108
1187
|
operationId: "createNetworkVolume",
|
|
1109
1188
|
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`.",
|
|
1110
1189
|
method: "POST",
|
|
@@ -1170,6 +1249,7 @@ var generatedTools = [
|
|
|
1170
1249
|
},
|
|
1171
1250
|
{
|
|
1172
1251
|
name: "create-pod",
|
|
1252
|
+
title: "Create Pod",
|
|
1173
1253
|
operationId: "createPod",
|
|
1174
1254
|
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.',
|
|
1175
1255
|
method: "POST",
|
|
@@ -1537,6 +1617,7 @@ var generatedTools = [
|
|
|
1537
1617
|
},
|
|
1538
1618
|
{
|
|
1539
1619
|
name: "create-registry",
|
|
1620
|
+
title: "Create registry credential",
|
|
1540
1621
|
operationId: "createRegistry",
|
|
1541
1622
|
description: "Create a container registry credential. Stores credentials for a private container registry. Credentials are write-only.",
|
|
1542
1623
|
method: "POST",
|
|
@@ -1588,6 +1669,7 @@ var generatedTools = [
|
|
|
1588
1669
|
},
|
|
1589
1670
|
{
|
|
1590
1671
|
name: "create-secret",
|
|
1672
|
+
title: "Create secret",
|
|
1591
1673
|
operationId: "createSecret",
|
|
1592
1674
|
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.",
|
|
1593
1675
|
method: "POST",
|
|
@@ -1642,6 +1724,7 @@ var generatedTools = [
|
|
|
1642
1724
|
},
|
|
1643
1725
|
{
|
|
1644
1726
|
name: "create-template",
|
|
1727
|
+
title: "Create template",
|
|
1645
1728
|
operationId: "createTemplate",
|
|
1646
1729
|
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.",
|
|
1647
1730
|
method: "POST",
|
|
@@ -1843,6 +1926,7 @@ var generatedTools = [
|
|
|
1843
1926
|
},
|
|
1844
1927
|
{
|
|
1845
1928
|
name: "delete-cluster",
|
|
1929
|
+
title: "Delete cluster",
|
|
1846
1930
|
operationId: "deleteCluster",
|
|
1847
1931
|
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.",
|
|
1848
1932
|
method: "DELETE",
|
|
@@ -1873,6 +1957,7 @@ var generatedTools = [
|
|
|
1873
1957
|
},
|
|
1874
1958
|
{
|
|
1875
1959
|
name: "delete-endpoint",
|
|
1960
|
+
title: "Delete endpoint",
|
|
1876
1961
|
operationId: "deleteEndpoint",
|
|
1877
1962
|
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.",
|
|
1878
1963
|
method: "DELETE",
|
|
@@ -1903,6 +1988,7 @@ var generatedTools = [
|
|
|
1903
1988
|
},
|
|
1904
1989
|
{
|
|
1905
1990
|
name: "delete-global-volume",
|
|
1991
|
+
title: "Delete global volume",
|
|
1906
1992
|
operationId: "deleteGlobalVolume",
|
|
1907
1993
|
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.",
|
|
1908
1994
|
method: "DELETE",
|
|
@@ -1933,6 +2019,7 @@ var generatedTools = [
|
|
|
1933
2019
|
},
|
|
1934
2020
|
{
|
|
1935
2021
|
name: "delete-network-volume",
|
|
2022
|
+
title: "Delete network volume",
|
|
1936
2023
|
operationId: "deleteNetworkVolume",
|
|
1937
2024
|
description: "Delete a network volume. Permanently deletes a network volume and releases its storage.",
|
|
1938
2025
|
method: "DELETE",
|
|
@@ -1963,6 +2050,7 @@ var generatedTools = [
|
|
|
1963
2050
|
},
|
|
1964
2051
|
{
|
|
1965
2052
|
name: "delete-pod",
|
|
2053
|
+
title: "Delete Pod",
|
|
1966
2054
|
operationId: "deletePod",
|
|
1967
2055
|
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.",
|
|
1968
2056
|
method: "DELETE",
|
|
@@ -1993,6 +2081,7 @@ var generatedTools = [
|
|
|
1993
2081
|
},
|
|
1994
2082
|
{
|
|
1995
2083
|
name: "delete-registry",
|
|
2084
|
+
title: "Delete registry credential",
|
|
1996
2085
|
operationId: "deleteRegistry",
|
|
1997
2086
|
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.",
|
|
1998
2087
|
method: "DELETE",
|
|
@@ -2022,6 +2111,7 @@ var generatedTools = [
|
|
|
2022
2111
|
},
|
|
2023
2112
|
{
|
|
2024
2113
|
name: "delete-secret",
|
|
2114
|
+
title: "Delete secret",
|
|
2025
2115
|
operationId: "deleteSecret",
|
|
2026
2116
|
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.",
|
|
2027
2117
|
method: "DELETE",
|
|
@@ -2052,6 +2142,7 @@ var generatedTools = [
|
|
|
2052
2142
|
},
|
|
2053
2143
|
{
|
|
2054
2144
|
name: "delete-template",
|
|
2145
|
+
title: "Delete template",
|
|
2055
2146
|
operationId: "deleteTemplate",
|
|
2056
2147
|
description: "Delete a template permanently. Verify with get-template first; a template referenced by a live endpoint should not be deleted.",
|
|
2057
2148
|
method: "DELETE",
|
|
@@ -2081,6 +2172,7 @@ var generatedTools = [
|
|
|
2081
2172
|
},
|
|
2082
2173
|
{
|
|
2083
2174
|
name: "get-cluster",
|
|
2175
|
+
title: "Get cluster",
|
|
2084
2176
|
operationId: "getCluster",
|
|
2085
2177
|
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.",
|
|
2086
2178
|
method: "GET",
|
|
@@ -2111,6 +2203,7 @@ var generatedTools = [
|
|
|
2111
2203
|
},
|
|
2112
2204
|
{
|
|
2113
2205
|
name: "get-cpu-type",
|
|
2206
|
+
title: "Get CPU type",
|
|
2114
2207
|
operationId: "getCpuType",
|
|
2115
2208
|
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.",
|
|
2116
2209
|
method: "GET",
|
|
@@ -2187,6 +2280,7 @@ var generatedTools = [
|
|
|
2187
2280
|
},
|
|
2188
2281
|
{
|
|
2189
2282
|
name: "get-data-center",
|
|
2283
|
+
title: "Get data center",
|
|
2190
2284
|
operationId: "getDataCenter",
|
|
2191
2285
|
description: "Get a data center. Returns a single data center. Availability is included only when requested with include=GPU_AVAILABILITY or include=CPU_AVAILABILITY.",
|
|
2192
2286
|
method: "GET",
|
|
@@ -2236,6 +2330,7 @@ var generatedTools = [
|
|
|
2236
2330
|
},
|
|
2237
2331
|
{
|
|
2238
2332
|
name: "get-endpoint",
|
|
2333
|
+
title: "Get endpoint",
|
|
2239
2334
|
operationId: "getEndpoint",
|
|
2240
2335
|
description: "Get a serverless endpoint. Returns a single serverless endpoint by ID.",
|
|
2241
2336
|
method: "GET",
|
|
@@ -2266,6 +2361,7 @@ var generatedTools = [
|
|
|
2266
2361
|
},
|
|
2267
2362
|
{
|
|
2268
2363
|
name: "get-endpoint-build",
|
|
2364
|
+
title: "Get endpoint build",
|
|
2269
2365
|
operationId: "getEndpointBuild",
|
|
2270
2366
|
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.",
|
|
2271
2367
|
method: "GET",
|
|
@@ -2304,6 +2400,7 @@ var generatedTools = [
|
|
|
2304
2400
|
},
|
|
2305
2401
|
{
|
|
2306
2402
|
name: "get-global-volume",
|
|
2403
|
+
title: "Get global volume",
|
|
2307
2404
|
operationId: "getGlobalVolume",
|
|
2308
2405
|
description: "Get a global volume. Returns a single global volume by ID.",
|
|
2309
2406
|
method: "GET",
|
|
@@ -2334,6 +2431,7 @@ var generatedTools = [
|
|
|
2334
2431
|
},
|
|
2335
2432
|
{
|
|
2336
2433
|
name: "get-gpu-type",
|
|
2434
|
+
title: "Get GPU type",
|
|
2337
2435
|
operationId: "getGpuType",
|
|
2338
2436
|
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.",
|
|
2339
2437
|
method: "GET",
|
|
@@ -2458,6 +2556,7 @@ var generatedTools = [
|
|
|
2458
2556
|
},
|
|
2459
2557
|
{
|
|
2460
2558
|
name: "get-network-volume",
|
|
2559
|
+
title: "Get network volume",
|
|
2461
2560
|
operationId: "getNetworkVolume",
|
|
2462
2561
|
description: "Get a network volume. Returns a single network volume by ID.",
|
|
2463
2562
|
method: "GET",
|
|
@@ -2488,6 +2587,7 @@ var generatedTools = [
|
|
|
2488
2587
|
},
|
|
2489
2588
|
{
|
|
2490
2589
|
name: "get-organization",
|
|
2590
|
+
title: "Get organization",
|
|
2491
2591
|
operationId: "getOrganization",
|
|
2492
2592
|
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.",
|
|
2493
2593
|
method: "GET",
|
|
@@ -2507,6 +2607,7 @@ var generatedTools = [
|
|
|
2507
2607
|
},
|
|
2508
2608
|
{
|
|
2509
2609
|
name: "get-pod",
|
|
2610
|
+
title: "Get Pod",
|
|
2510
2611
|
operationId: "getPod",
|
|
2511
2612
|
description: "Get a pod. Returns a single pod by ID.",
|
|
2512
2613
|
method: "GET",
|
|
@@ -2537,6 +2638,7 @@ var generatedTools = [
|
|
|
2537
2638
|
},
|
|
2538
2639
|
{
|
|
2539
2640
|
name: "get-registry",
|
|
2641
|
+
title: "Get registry credential",
|
|
2540
2642
|
operationId: "getRegistry",
|
|
2541
2643
|
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`.",
|
|
2542
2644
|
method: "GET",
|
|
@@ -2566,6 +2668,7 @@ var generatedTools = [
|
|
|
2566
2668
|
},
|
|
2567
2669
|
{
|
|
2568
2670
|
name: "get-secret",
|
|
2671
|
+
title: "Get secret",
|
|
2569
2672
|
operationId: "getSecret",
|
|
2570
2673
|
description: "Get a secret. Returns a single secret's metadata by ID. The value is write-only and never returned.",
|
|
2571
2674
|
method: "GET",
|
|
@@ -2596,6 +2699,7 @@ var generatedTools = [
|
|
|
2596
2699
|
},
|
|
2597
2700
|
{
|
|
2598
2701
|
name: "get-ssh-keys",
|
|
2702
|
+
title: "Get SSH keys",
|
|
2599
2703
|
operationId: "getSshKeys",
|
|
2600
2704
|
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.",
|
|
2601
2705
|
method: "GET",
|
|
@@ -2615,6 +2719,7 @@ var generatedTools = [
|
|
|
2615
2719
|
},
|
|
2616
2720
|
{
|
|
2617
2721
|
name: "get-template",
|
|
2722
|
+
title: "Get template",
|
|
2618
2723
|
operationId: "getTemplate",
|
|
2619
2724
|
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.",
|
|
2620
2725
|
method: "GET",
|
|
@@ -2644,6 +2749,7 @@ var generatedTools = [
|
|
|
2644
2749
|
},
|
|
2645
2750
|
{
|
|
2646
2751
|
name: "list-billing",
|
|
2752
|
+
title: "List billing",
|
|
2647
2753
|
operationId: "listBilling",
|
|
2648
2754
|
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.",
|
|
2649
2755
|
method: "GET",
|
|
@@ -2716,6 +2822,7 @@ var generatedTools = [
|
|
|
2716
2822
|
},
|
|
2717
2823
|
{
|
|
2718
2824
|
name: "list-cluster-billing",
|
|
2825
|
+
title: "List cluster billing",
|
|
2719
2826
|
operationId: "listClusterBilling",
|
|
2720
2827
|
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.",
|
|
2721
2828
|
method: "GET",
|
|
@@ -2796,6 +2903,7 @@ var generatedTools = [
|
|
|
2796
2903
|
},
|
|
2797
2904
|
{
|
|
2798
2905
|
name: "list-cluster-pods",
|
|
2906
|
+
title: "List cluster Pods",
|
|
2799
2907
|
operationId: "listClusterPods",
|
|
2800
2908
|
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.",
|
|
2801
2909
|
method: "GET",
|
|
@@ -2826,6 +2934,7 @@ var generatedTools = [
|
|
|
2826
2934
|
},
|
|
2827
2935
|
{
|
|
2828
2936
|
name: "list-clusters",
|
|
2937
|
+
title: "List clusters",
|
|
2829
2938
|
operationId: "listClusters",
|
|
2830
2939
|
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.",
|
|
2831
2940
|
method: "GET",
|
|
@@ -2866,6 +2975,7 @@ var generatedTools = [
|
|
|
2866
2975
|
},
|
|
2867
2976
|
{
|
|
2868
2977
|
name: "list-cpu-types",
|
|
2978
|
+
title: "List CPU types",
|
|
2869
2979
|
operationId: "listCpuTypes",
|
|
2870
2980
|
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.",
|
|
2871
2981
|
method: "GET",
|
|
@@ -2934,6 +3044,7 @@ var generatedTools = [
|
|
|
2934
3044
|
},
|
|
2935
3045
|
{
|
|
2936
3046
|
name: "list-data-centers",
|
|
3047
|
+
title: "List data centers",
|
|
2937
3048
|
operationId: "listDataCenters",
|
|
2938
3049
|
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.",
|
|
2939
3050
|
method: "GET",
|
|
@@ -3061,6 +3172,7 @@ var generatedTools = [
|
|
|
3061
3172
|
},
|
|
3062
3173
|
{
|
|
3063
3174
|
name: "list-delegations",
|
|
3175
|
+
title: "List ECR delegations",
|
|
3064
3176
|
operationId: "listDelegations",
|
|
3065
3177
|
description: "List all ECR delegations",
|
|
3066
3178
|
method: "GET",
|
|
@@ -3080,6 +3192,7 @@ var generatedTools = [
|
|
|
3080
3192
|
},
|
|
3081
3193
|
{
|
|
3082
3194
|
name: "list-endpoint-billing",
|
|
3195
|
+
title: "List endpoint billing",
|
|
3083
3196
|
operationId: "listEndpointBilling",
|
|
3084
3197
|
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.",
|
|
3085
3198
|
method: "GET",
|
|
@@ -3152,6 +3265,7 @@ var generatedTools = [
|
|
|
3152
3265
|
},
|
|
3153
3266
|
{
|
|
3154
3267
|
name: "list-endpoint-builds",
|
|
3268
|
+
title: "List endpoint builds",
|
|
3155
3269
|
operationId: "listEndpointBuilds",
|
|
3156
3270
|
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`.",
|
|
3157
3271
|
method: "GET",
|
|
@@ -3201,6 +3315,7 @@ var generatedTools = [
|
|
|
3201
3315
|
},
|
|
3202
3316
|
{
|
|
3203
3317
|
name: "list-endpoint-releases",
|
|
3318
|
+
title: "List endpoint releases",
|
|
3204
3319
|
operationId: "listEndpointReleases",
|
|
3205
3320
|
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.",
|
|
3206
3321
|
method: "GET",
|
|
@@ -3250,6 +3365,7 @@ var generatedTools = [
|
|
|
3250
3365
|
},
|
|
3251
3366
|
{
|
|
3252
3367
|
name: "list-endpoint-workers",
|
|
3368
|
+
title: "List endpoint workers",
|
|
3253
3369
|
operationId: "listEndpointWorkers",
|
|
3254
3370
|
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.",
|
|
3255
3371
|
method: "GET",
|
|
@@ -3280,6 +3396,7 @@ var generatedTools = [
|
|
|
3280
3396
|
},
|
|
3281
3397
|
{
|
|
3282
3398
|
name: "list-global-volumes",
|
|
3399
|
+
title: "List global volumes",
|
|
3283
3400
|
operationId: "listGlobalVolumes",
|
|
3284
3401
|
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.",
|
|
3285
3402
|
method: "GET",
|
|
@@ -3320,6 +3437,7 @@ var generatedTools = [
|
|
|
3320
3437
|
},
|
|
3321
3438
|
{
|
|
3322
3439
|
name: "list-gpu-types",
|
|
3440
|
+
title: "List GPU types",
|
|
3323
3441
|
operationId: "listGpuTypes",
|
|
3324
3442
|
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.',
|
|
3325
3443
|
method: "GET",
|
|
@@ -3436,6 +3554,7 @@ var generatedTools = [
|
|
|
3436
3554
|
},
|
|
3437
3555
|
{
|
|
3438
3556
|
name: "list-network-volume-billing",
|
|
3557
|
+
title: "List network volume billing",
|
|
3439
3558
|
operationId: "listNetworkVolumeBilling",
|
|
3440
3559
|
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.",
|
|
3441
3560
|
method: "GET",
|
|
@@ -3516,6 +3635,7 @@ var generatedTools = [
|
|
|
3516
3635
|
},
|
|
3517
3636
|
{
|
|
3518
3637
|
name: "list-network-volumes",
|
|
3638
|
+
title: "List network volumes",
|
|
3519
3639
|
operationId: "listNetworkVolumes",
|
|
3520
3640
|
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.",
|
|
3521
3641
|
method: "GET",
|
|
@@ -3556,6 +3676,7 @@ var generatedTools = [
|
|
|
3556
3676
|
},
|
|
3557
3677
|
{
|
|
3558
3678
|
name: "list-pod-billing",
|
|
3679
|
+
title: "List Pod billing",
|
|
3559
3680
|
operationId: "listPodBilling",
|
|
3560
3681
|
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.",
|
|
3561
3682
|
method: "GET",
|
|
@@ -3636,6 +3757,7 @@ var generatedTools = [
|
|
|
3636
3757
|
},
|
|
3637
3758
|
{
|
|
3638
3759
|
name: "list-pods",
|
|
3760
|
+
title: "List Pods",
|
|
3639
3761
|
operationId: "listPods",
|
|
3640
3762
|
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.",
|
|
3641
3763
|
method: "GET",
|
|
@@ -3685,6 +3807,7 @@ var generatedTools = [
|
|
|
3685
3807
|
},
|
|
3686
3808
|
{
|
|
3687
3809
|
name: "list-public-templates",
|
|
3810
|
+
title: "List public templates",
|
|
3688
3811
|
operationId: "listPublicTemplates",
|
|
3689
3812
|
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`).",
|
|
3690
3813
|
method: "GET",
|
|
@@ -3716,6 +3839,7 @@ var generatedTools = [
|
|
|
3716
3839
|
},
|
|
3717
3840
|
{
|
|
3718
3841
|
name: "list-registries",
|
|
3842
|
+
title: "List registry credentials",
|
|
3719
3843
|
operationId: "listRegistries",
|
|
3720
3844
|
description: "List container registries. Returns all container registry credentials owned by the authenticated user.",
|
|
3721
3845
|
method: "GET",
|
|
@@ -3735,6 +3859,7 @@ var generatedTools = [
|
|
|
3735
3859
|
},
|
|
3736
3860
|
{
|
|
3737
3861
|
name: "list-secrets",
|
|
3862
|
+
title: "List secrets",
|
|
3738
3863
|
operationId: "listSecrets",
|
|
3739
3864
|
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.",
|
|
3740
3865
|
method: "GET",
|
|
@@ -3764,6 +3889,7 @@ var generatedTools = [
|
|
|
3764
3889
|
},
|
|
3765
3890
|
{
|
|
3766
3891
|
name: "list-serverless-billing",
|
|
3892
|
+
title: "List Serverless billing",
|
|
3767
3893
|
operationId: "listServerlessBilling",
|
|
3768
3894
|
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.",
|
|
3769
3895
|
method: "GET",
|
|
@@ -3844,6 +3970,7 @@ var generatedTools = [
|
|
|
3844
3970
|
},
|
|
3845
3971
|
{
|
|
3846
3972
|
name: "pod-action",
|
|
3973
|
+
title: "Start, stop, restart, or terminate a Pod",
|
|
3847
3974
|
operationId: "podAction",
|
|
3848
3975
|
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`.",
|
|
3849
3976
|
method: "POST",
|
|
@@ -3893,6 +4020,7 @@ var generatedTools = [
|
|
|
3893
4020
|
},
|
|
3894
4021
|
{
|
|
3895
4022
|
name: "revoke-delegation",
|
|
4023
|
+
title: "Revoke ECR delegation",
|
|
3896
4024
|
operationId: "revokeDelegation",
|
|
3897
4025
|
description: "Revoke an ECR delegation",
|
|
3898
4026
|
method: "DELETE",
|
|
@@ -3922,6 +4050,7 @@ var generatedTools = [
|
|
|
3922
4050
|
},
|
|
3923
4051
|
{
|
|
3924
4052
|
name: "update-cluster",
|
|
4053
|
+
title: "Update cluster",
|
|
3925
4054
|
operationId: "updateCluster",
|
|
3926
4055
|
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.",
|
|
3927
4056
|
method: "PATCH",
|
|
@@ -3970,6 +4099,7 @@ var generatedTools = [
|
|
|
3970
4099
|
},
|
|
3971
4100
|
{
|
|
3972
4101
|
name: "update-endpoint",
|
|
4102
|
+
title: "Update endpoint",
|
|
3973
4103
|
operationId: "updateEndpoint",
|
|
3974
4104
|
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.",
|
|
3975
4105
|
method: "PATCH",
|
|
@@ -4360,6 +4490,7 @@ var generatedTools = [
|
|
|
4360
4490
|
},
|
|
4361
4491
|
{
|
|
4362
4492
|
name: "update-global-volume",
|
|
4493
|
+
title: "Update global volume",
|
|
4363
4494
|
operationId: "updateGlobalVolume",
|
|
4364
4495
|
description: "Update a global volume. Updates mutable fields on a global volume. Only provided fields are changed.",
|
|
4365
4496
|
method: "PATCH",
|
|
@@ -4411,6 +4542,7 @@ var generatedTools = [
|
|
|
4411
4542
|
},
|
|
4412
4543
|
{
|
|
4413
4544
|
name: "update-network-volume",
|
|
4545
|
+
title: "Update network volume",
|
|
4414
4546
|
operationId: "updateNetworkVolume",
|
|
4415
4547
|
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.",
|
|
4416
4548
|
method: "PATCH",
|
|
@@ -4467,6 +4599,7 @@ var generatedTools = [
|
|
|
4467
4599
|
},
|
|
4468
4600
|
{
|
|
4469
4601
|
name: "update-pod",
|
|
4602
|
+
title: "Update Pod",
|
|
4470
4603
|
operationId: "updatePod",
|
|
4471
4604
|
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.",
|
|
4472
4605
|
method: "PATCH",
|
|
@@ -4708,6 +4841,7 @@ var generatedTools = [
|
|
|
4708
4841
|
},
|
|
4709
4842
|
{
|
|
4710
4843
|
name: "update-secret",
|
|
4844
|
+
title: "Update secret",
|
|
4711
4845
|
operationId: "updateSecret",
|
|
4712
4846
|
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.",
|
|
4713
4847
|
method: "PATCH",
|
|
@@ -4763,6 +4897,7 @@ var generatedTools = [
|
|
|
4763
4897
|
},
|
|
4764
4898
|
{
|
|
4765
4899
|
name: "update-ssh-keys",
|
|
4900
|
+
title: "Update SSH keys",
|
|
4766
4901
|
operationId: "updateSshKeys",
|
|
4767
4902
|
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.",
|
|
4768
4903
|
method: "PUT",
|
|
@@ -4809,6 +4944,7 @@ var generatedTools = [
|
|
|
4809
4944
|
},
|
|
4810
4945
|
{
|
|
4811
4946
|
name: "update-template",
|
|
4947
|
+
title: "Update template",
|
|
4812
4948
|
operationId: "updateTemplate",
|
|
4813
4949
|
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.",
|
|
4814
4950
|
method: "PATCH",
|
|
@@ -5107,6 +5243,43 @@ function isTimeout(error) {
|
|
|
5107
5243
|
const cause = error.cause;
|
|
5108
5244
|
return cause instanceof Error && (cause.name === "TimeoutError" || cause.name === "AbortError");
|
|
5109
5245
|
}
|
|
5246
|
+
var UNREACHED_CODES = /* @__PURE__ */ new Set([
|
|
5247
|
+
"ENOTFOUND",
|
|
5248
|
+
"EAI_AGAIN",
|
|
5249
|
+
"ECONNREFUSED",
|
|
5250
|
+
"EHOSTUNREACH",
|
|
5251
|
+
"ENETUNREACH",
|
|
5252
|
+
"UND_ERR_CONNECT_TIMEOUT"
|
|
5253
|
+
]);
|
|
5254
|
+
var TLS_CODE = /^(?:CERT_|ERR_TLS_|UNABLE_TO_|DEPTH_ZERO_|SELF_SIGNED_)/;
|
|
5255
|
+
function causeCode(cause) {
|
|
5256
|
+
const code = cause?.code;
|
|
5257
|
+
if (typeof code === "string") return code;
|
|
5258
|
+
const inner = cause instanceof AggregateError ? cause.errors[0] : void 0;
|
|
5259
|
+
const innerCode = inner?.code;
|
|
5260
|
+
return typeof innerCode === "string" ? innerCode : void 0;
|
|
5261
|
+
}
|
|
5262
|
+
function networkFailureResult(error) {
|
|
5263
|
+
if (!(error instanceof TypeError)) return void 0;
|
|
5264
|
+
const afterHeaders = error.message === "terminated";
|
|
5265
|
+
if (!afterHeaders && error.message !== "fetch failed") return void 0;
|
|
5266
|
+
const code = causeCode(error.cause);
|
|
5267
|
+
if (!code) return void 0;
|
|
5268
|
+
const unreached = !afterHeaders && (UNREACHED_CODES.has(code) || TLS_CODE.test(code));
|
|
5269
|
+
return {
|
|
5270
|
+
ok: false,
|
|
5271
|
+
status: unreached ? 503 : 502,
|
|
5272
|
+
payload: unreached ? {
|
|
5273
|
+
error: "Could not reach the Runpod API.",
|
|
5274
|
+
cause: code,
|
|
5275
|
+
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."
|
|
5276
|
+
} : {
|
|
5277
|
+
error: "The connection to the Runpod API dropped before the full response arrived.",
|
|
5278
|
+
cause: code,
|
|
5279
|
+
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."
|
|
5280
|
+
}
|
|
5281
|
+
};
|
|
5282
|
+
}
|
|
5110
5283
|
async function runTool(body) {
|
|
5111
5284
|
try {
|
|
5112
5285
|
return await body();
|
|
@@ -5128,6 +5301,8 @@ async function runTool(body) {
|
|
|
5128
5301
|
}
|
|
5129
5302
|
};
|
|
5130
5303
|
}
|
|
5304
|
+
const network = networkFailureResult(error);
|
|
5305
|
+
if (network) return network;
|
|
5131
5306
|
throw error;
|
|
5132
5307
|
}
|
|
5133
5308
|
}
|
|
@@ -5198,6 +5373,7 @@ function rankByStock(rows, stats, includeUnavailable) {
|
|
|
5198
5373
|
}
|
|
5199
5374
|
var getCapacity = {
|
|
5200
5375
|
name: "get-capacity",
|
|
5376
|
+
title: "Get GPU capacity by CUDA version",
|
|
5201
5377
|
annotations: readOnly,
|
|
5202
5378
|
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.",
|
|
5203
5379
|
inputSchema: {
|
|
@@ -5399,6 +5575,7 @@ function formatGpuIds(gpu) {
|
|
|
5399
5575
|
}
|
|
5400
5576
|
var setEndpointGpus = {
|
|
5401
5577
|
name: "set-endpoint-gpus",
|
|
5578
|
+
title: "Set endpoint GPUs",
|
|
5402
5579
|
annotations: idempotentWrite,
|
|
5403
5580
|
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.",
|
|
5404
5581
|
inputSchema: {
|
|
@@ -5593,6 +5770,7 @@ function randomSuffix() {
|
|
|
5593
5770
|
}
|
|
5594
5771
|
var listHubRepos = {
|
|
5595
5772
|
name: "list-hub-repos",
|
|
5773
|
+
title: "List Hub repos",
|
|
5596
5774
|
annotations: readOnly,
|
|
5597
5775
|
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.",
|
|
5598
5776
|
inputSchema: {
|
|
@@ -5695,6 +5873,9 @@ var listHubRepos = {
|
|
|
5695
5873
|
};
|
|
5696
5874
|
var deployHubRepo = {
|
|
5697
5875
|
name: "deploy-hub-repo",
|
|
5876
|
+
title: "Deploy Hub repo",
|
|
5877
|
+
// Two sequential upstream calls: together they can outlast a client's request timeout.
|
|
5878
|
+
holdsResponse: true,
|
|
5698
5879
|
annotations: write,
|
|
5699
5880
|
description: "Deploy a Runpod Hub repo's listed release as a new Serverless endpoint (the same as clicking Deploy on the Hub). Identify the repo by `repo` (\"owner/name\" from list-hub-repos) or by `hubReleaseId`. The release supplies the prebuilt image, container disk, CUDA constraints, and env-var defaults; pass `env` to override or fill in values (required keys without a default must be provided \u2014 check list-hub-repos with includeConfig:true for the schema). GPU selection comes from the release config when it specifies one; otherwise pass gpuIds (GPU pool names, e.g. 'ADA_24' or 'ADA_80_PRO,AMPERE_80'). Uses the authenticated GraphQL API (no REST home for Hub deploys yet).",
|
|
5700
5881
|
inputSchema: {
|
|
@@ -6153,6 +6334,7 @@ function validateWait(wait) {
|
|
|
6153
6334
|
}
|
|
6154
6335
|
var runEndpoint = {
|
|
6155
6336
|
name: "run-endpoint",
|
|
6337
|
+
title: "Submit Serverless job",
|
|
6156
6338
|
annotations: write,
|
|
6157
6339
|
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.",
|
|
6158
6340
|
inputSchema: {
|
|
@@ -6183,6 +6365,8 @@ var runEndpoint = {
|
|
|
6183
6365
|
};
|
|
6184
6366
|
var runsyncEndpoint = {
|
|
6185
6367
|
name: "runsync-endpoint",
|
|
6368
|
+
title: "Run Serverless job and wait",
|
|
6369
|
+
holdsResponse: true,
|
|
6186
6370
|
annotations: write,
|
|
6187
6371
|
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)."),
|
|
6188
6372
|
inputSchema: {
|
|
@@ -6227,6 +6411,8 @@ var runsyncEndpoint = {
|
|
|
6227
6411
|
};
|
|
6228
6412
|
var getJobStatus = {
|
|
6229
6413
|
name: "get-job-status",
|
|
6414
|
+
title: "Get job status",
|
|
6415
|
+
holdsResponse: true,
|
|
6230
6416
|
annotations: readOnly,
|
|
6231
6417
|
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.",
|
|
6232
6418
|
inputSchema: {
|
|
@@ -6275,6 +6461,8 @@ var getJobStatus = {
|
|
|
6275
6461
|
};
|
|
6276
6462
|
var streamJob = {
|
|
6277
6463
|
name: "stream-job",
|
|
6464
|
+
title: "Stream job output",
|
|
6465
|
+
holdsResponse: true,
|
|
6278
6466
|
annotations: readOnly,
|
|
6279
6467
|
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.`,
|
|
6280
6468
|
inputSchema: {
|
|
@@ -6306,6 +6494,7 @@ var streamJob = {
|
|
|
6306
6494
|
};
|
|
6307
6495
|
var cancelJob = {
|
|
6308
6496
|
name: "cancel-job",
|
|
6497
|
+
title: "Cancel job",
|
|
6309
6498
|
annotations: destructive,
|
|
6310
6499
|
description: "Cancel a Serverless job that is queued or in progress.",
|
|
6311
6500
|
inputSchema: {
|
|
@@ -6331,6 +6520,7 @@ var cancelJob = {
|
|
|
6331
6520
|
};
|
|
6332
6521
|
var retryJob = {
|
|
6333
6522
|
name: "retry-job",
|
|
6523
|
+
title: "Retry job",
|
|
6334
6524
|
annotations: write,
|
|
6335
6525
|
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.",
|
|
6336
6526
|
inputSchema: {
|
|
@@ -6356,6 +6546,7 @@ var retryJob = {
|
|
|
6356
6546
|
};
|
|
6357
6547
|
var endpointHealth = {
|
|
6358
6548
|
name: "endpoint-health",
|
|
6549
|
+
title: "Get endpoint health",
|
|
6359
6550
|
annotations: readOnly,
|
|
6360
6551
|
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.",
|
|
6361
6552
|
inputSchema: {
|
|
@@ -6376,6 +6567,7 @@ var endpointHealth = {
|
|
|
6376
6567
|
};
|
|
6377
6568
|
var purgeEndpointQueue = {
|
|
6378
6569
|
name: "purge-endpoint-queue",
|
|
6570
|
+
title: "Purge endpoint queue",
|
|
6379
6571
|
annotations: destructive,
|
|
6380
6572
|
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.",
|
|
6381
6573
|
inputSchema: {
|
|
@@ -6412,6 +6604,7 @@ var jobTools = [
|
|
|
6412
6604
|
// src/specgen/tools/list-endpoints.ts
|
|
6413
6605
|
var listEndpoints = {
|
|
6414
6606
|
name: "list-endpoints",
|
|
6607
|
+
title: "List endpoints",
|
|
6415
6608
|
annotations: readOnly,
|
|
6416
6609
|
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.",
|
|
6417
6610
|
inputSchema: {
|
|
@@ -6457,6 +6650,7 @@ var listEndpoints = {
|
|
|
6457
6650
|
// src/specgen/tools/list-templates.ts
|
|
6458
6651
|
var listTemplates = {
|
|
6459
6652
|
name: "list-templates",
|
|
6653
|
+
title: "List templates",
|
|
6460
6654
|
annotations: readOnly,
|
|
6461
6655
|
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.",
|
|
6462
6656
|
inputSchema: {
|
|
@@ -6507,6 +6701,9 @@ function publicGraphqlBase(env) {
|
|
|
6507
6701
|
function authedGraphqlBase(env) {
|
|
6508
6702
|
return pick(env.RUNPOD_AUTHED_GRAPHQL_URL, "https://api.runpod.io/graphql");
|
|
6509
6703
|
}
|
|
6704
|
+
function knowledgeSearchBase(env) {
|
|
6705
|
+
return pick(env.KNOWLEDGE_SEARCH_URL, "") || void 0;
|
|
6706
|
+
}
|
|
6510
6707
|
|
|
6511
6708
|
// node_modules/.pnpm/@runpod+typescript-api-sdk@0.3.0/node_modules/@runpod/typescript-api-sdk/dist/index.js
|
|
6512
6709
|
var PATH_PARAM_RE = /\{[^{}]+\}/g;
|
|
@@ -7912,6 +8109,8 @@ function logParams(args) {
|
|
|
7912
8109
|
}
|
|
7913
8110
|
var streamPodLogs = {
|
|
7914
8111
|
name: "stream-pod-logs",
|
|
8112
|
+
title: "Stream Pod logs",
|
|
8113
|
+
holdsResponse: true,
|
|
7915
8114
|
annotations: readOnly,
|
|
7916
8115
|
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.",
|
|
7917
8116
|
inputSchema: {
|
|
@@ -7934,6 +8133,8 @@ var streamPodLogs = {
|
|
|
7934
8133
|
};
|
|
7935
8134
|
var streamWorkerLogs = {
|
|
7936
8135
|
name: "stream-worker-logs",
|
|
8136
|
+
title: "Stream worker logs",
|
|
8137
|
+
holdsResponse: true,
|
|
7937
8138
|
annotations: readOnly,
|
|
7938
8139
|
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.",
|
|
7939
8140
|
inputSchema: {
|
|
@@ -7975,6 +8176,7 @@ function parseMetadata(metadata) {
|
|
|
7975
8176
|
}
|
|
7976
8177
|
var listPublicEndpoints = {
|
|
7977
8178
|
name: "list-public-endpoints",
|
|
8179
|
+
title: "List public endpoints",
|
|
7978
8180
|
annotations: readOnly,
|
|
7979
8181
|
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.",
|
|
7980
8182
|
inputSchema: {
|
|
@@ -8197,6 +8399,7 @@ var source = {
|
|
|
8197
8399
|
};
|
|
8198
8400
|
var listGuides = {
|
|
8199
8401
|
name: "list-guides",
|
|
8402
|
+
title: "List guides",
|
|
8200
8403
|
annotations: bundledRead,
|
|
8201
8404
|
recordedArguments: ["kind", "query", "concept"],
|
|
8202
8405
|
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.",
|
|
@@ -8233,6 +8436,7 @@ var listGuides = {
|
|
|
8233
8436
|
};
|
|
8234
8437
|
var readGuide = {
|
|
8235
8438
|
name: "read-guide",
|
|
8439
|
+
title: "Read guide",
|
|
8236
8440
|
annotations: bundledRead,
|
|
8237
8441
|
recordedArguments: ["id"],
|
|
8238
8442
|
recordedGuideKind: (args) => knowledge.guides.find((guide) => guide.id === String(args.id).trim())?.kind,
|
|
@@ -8272,6 +8476,7 @@ var readGuide = {
|
|
|
8272
8476
|
};
|
|
8273
8477
|
var lookupConcept = {
|
|
8274
8478
|
name: "lookup-concept",
|
|
8479
|
+
title: "Look up a concept",
|
|
8275
8480
|
annotations: bundledRead,
|
|
8276
8481
|
recordedArguments: ["concept"],
|
|
8277
8482
|
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".',
|
|
@@ -8325,6 +8530,7 @@ var lookupConcept = {
|
|
|
8325
8530
|
};
|
|
8326
8531
|
var searchConcepts = {
|
|
8327
8532
|
name: "search-concepts",
|
|
8533
|
+
title: "Search concepts",
|
|
8328
8534
|
annotations: bundledRead,
|
|
8329
8535
|
recordedArguments: ["query", "limit"],
|
|
8330
8536
|
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.',
|
|
@@ -8416,12 +8622,13 @@ function listedAnnotations(annotations) {
|
|
|
8416
8622
|
return idempotentHint ? annotations : listed;
|
|
8417
8623
|
}
|
|
8418
8624
|
function listTool(tool) {
|
|
8419
|
-
const { name, description, inputSchema } = tool;
|
|
8625
|
+
const { name, title, description, inputSchema } = tool;
|
|
8420
8626
|
const annotations = listedAnnotations(tool.annotations);
|
|
8421
8627
|
if (!tool.schemaOnDemand)
|
|
8422
|
-
return { name, description, inputSchema, annotations };
|
|
8628
|
+
return { name, title, description, inputSchema, annotations };
|
|
8423
8629
|
return {
|
|
8424
8630
|
name,
|
|
8631
|
+
title,
|
|
8425
8632
|
description: description + ON_DEMAND_POINTER,
|
|
8426
8633
|
inputSchema: callShape(inputSchema),
|
|
8427
8634
|
annotations
|
|
@@ -8473,6 +8680,7 @@ function schemaType(node, resolve) {
|
|
|
8473
8680
|
function createDescribeTool(served) {
|
|
8474
8681
|
return {
|
|
8475
8682
|
name: DESCRIBE_TOOL_NAME,
|
|
8683
|
+
title: "Describe tool",
|
|
8476
8684
|
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.",
|
|
8477
8685
|
inputSchema: {
|
|
8478
8686
|
type: "object",
|
|
@@ -8527,7 +8735,7 @@ function words2(name) {
|
|
|
8527
8735
|
|
|
8528
8736
|
// src/server.ts
|
|
8529
8737
|
var SERVER_NAME = "Runpod API Server";
|
|
8530
|
-
var SERVER_VERSION = true ? "4.
|
|
8738
|
+
var SERVER_VERSION = true ? "4.5.0" : "dev";
|
|
8531
8739
|
|
|
8532
8740
|
// src/specgen/analytics.ts
|
|
8533
8741
|
import { createHmac } from "crypto";
|
|
@@ -8627,16 +8835,6 @@ function captureResourceCall(apiKey, event) {
|
|
|
8627
8835
|
);
|
|
8628
8836
|
}
|
|
8629
8837
|
|
|
8630
|
-
// src/alp/ingest.ts
|
|
8631
|
-
function isSinkUrl(value) {
|
|
8632
|
-
try {
|
|
8633
|
-
const url = new URL(value);
|
|
8634
|
-
return url.protocol === "https:" && url.hostname.endsWith(".convex.site") && url.pathname === "/alp/submit";
|
|
8635
|
-
} catch {
|
|
8636
|
-
return false;
|
|
8637
|
-
}
|
|
8638
|
-
}
|
|
8639
|
-
|
|
8640
8838
|
// src/alp/read.ts
|
|
8641
8839
|
var DEFAULT_JOURNAL_LIMIT = 20;
|
|
8642
8840
|
var MAX_JOURNAL_LIMIT = 50;
|
|
@@ -8665,12 +8863,21 @@ async function submit(ctx, ingestUrl, route, args, extra) {
|
|
|
8665
8863
|
tool: args.tool,
|
|
8666
8864
|
workaround: args.workaround,
|
|
8667
8865
|
trigger: args.trigger,
|
|
8866
|
+
source: args.source,
|
|
8867
|
+
noContext: args.noContext,
|
|
8868
|
+
useCase: args.useCase,
|
|
8668
8869
|
...extra
|
|
8669
8870
|
}),
|
|
8670
8871
|
signal: AbortSignal.timeout(1e4)
|
|
8671
8872
|
});
|
|
8672
8873
|
const body = await response.json().catch(() => ({}));
|
|
8673
|
-
return {
|
|
8874
|
+
return {
|
|
8875
|
+
recorded: response.ok && body.recorded === true,
|
|
8876
|
+
// The ingest's own named outcome (already_submitted on a once-per-account
|
|
8877
|
+
// route), carried through so the tool can say it. A string from a 200
|
|
8878
|
+
// only — never an upstream error body.
|
|
8879
|
+
...response.ok && typeof body.reason === "string" ? { reason: body.reason } : {}
|
|
8880
|
+
};
|
|
8674
8881
|
} catch {
|
|
8675
8882
|
return { recorded: false };
|
|
8676
8883
|
}
|
|
@@ -8701,6 +8908,10 @@ async function readJournal(ctx, readUrl, limit) {
|
|
|
8701
8908
|
}
|
|
8702
8909
|
}
|
|
8703
8910
|
var SEVERITY = ["blocked", "degraded", "cosmetic"];
|
|
8911
|
+
var MODEL_TYPE_FIELD = {
|
|
8912
|
+
type: "string",
|
|
8913
|
+
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.'
|
|
8914
|
+
};
|
|
8704
8915
|
var TOOL_FIELD = {
|
|
8705
8916
|
type: "string",
|
|
8706
8917
|
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."
|
|
@@ -8717,10 +8928,7 @@ var alpInputSchema = (contentDescription, extra = {}) => ({
|
|
|
8717
8928
|
description: "One short sentence: what you were trying to accomplish when this came up."
|
|
8718
8929
|
},
|
|
8719
8930
|
...extra,
|
|
8720
|
-
modelType:
|
|
8721
|
-
type: "string",
|
|
8722
|
-
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.'
|
|
8723
|
-
}
|
|
8931
|
+
modelType: MODEL_TYPE_FIELD
|
|
8724
8932
|
},
|
|
8725
8933
|
required: ["content"]
|
|
8726
8934
|
});
|
|
@@ -8735,6 +8943,7 @@ function createAlpTools(opts) {
|
|
|
8735
8943
|
// is ignored, never a 400 that derails the task the agent is actually on.
|
|
8736
8944
|
lenientArguments: true,
|
|
8737
8945
|
name: "report_feedback",
|
|
8946
|
+
title: "Report feedback",
|
|
8738
8947
|
annotations: write,
|
|
8739
8948
|
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.",
|
|
8740
8949
|
inputSchema: alpInputSchema(
|
|
@@ -8771,6 +8980,7 @@ function createAlpTools(opts) {
|
|
|
8771
8980
|
// is ignored, never a 400 that derails the task the agent is actually on.
|
|
8772
8981
|
lenientArguments: true,
|
|
8773
8982
|
name: "save_to_journal",
|
|
8983
|
+
title: "Save to journal",
|
|
8774
8984
|
annotations: write,
|
|
8775
8985
|
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.",
|
|
8776
8986
|
inputSchema: alpInputSchema(
|
|
@@ -8802,6 +9012,7 @@ function createAlpTools(opts) {
|
|
|
8802
9012
|
// is ignored, never a 400 that derails the task the agent is actually on.
|
|
8803
9013
|
lenientArguments: true,
|
|
8804
9014
|
name: "ask_question",
|
|
9015
|
+
title: "Ask a question",
|
|
8805
9016
|
annotations: write,
|
|
8806
9017
|
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.",
|
|
8807
9018
|
inputSchema: alpInputSchema(
|
|
@@ -8827,6 +9038,7 @@ function createAlpTools(opts) {
|
|
|
8827
9038
|
const readJournalTool = {
|
|
8828
9039
|
lenientArguments: true,
|
|
8829
9040
|
name: "read_journal",
|
|
9041
|
+
title: "Read journal",
|
|
8830
9042
|
annotations: readOnly,
|
|
8831
9043
|
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.",
|
|
8832
9044
|
inputSchema: {
|
|
@@ -8854,7 +9066,233 @@ function createAlpTools(opts) {
|
|
|
8854
9066
|
});
|
|
8855
9067
|
}
|
|
8856
9068
|
};
|
|
8857
|
-
|
|
9069
|
+
const reportHowUserFoundRunpod = {
|
|
9070
|
+
lenientArguments: true,
|
|
9071
|
+
name: "report_how_user_found_runpod",
|
|
9072
|
+
title: "Report how the user found Runpod",
|
|
9073
|
+
annotations: idempotentWrite,
|
|
9074
|
+
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.",
|
|
9075
|
+
inputSchema: {
|
|
9076
|
+
type: "object",
|
|
9077
|
+
properties: {
|
|
9078
|
+
content: {
|
|
9079
|
+
type: "string",
|
|
9080
|
+
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.'
|
|
9081
|
+
},
|
|
9082
|
+
source: {
|
|
9083
|
+
type: "string",
|
|
9084
|
+
enum: [...ALP_REFERRAL_SOURCES],
|
|
9085
|
+
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."
|
|
9086
|
+
},
|
|
9087
|
+
useCase: {
|
|
9088
|
+
type: "string",
|
|
9089
|
+
enum: [...ALP_REFERRAL_USE_CASES],
|
|
9090
|
+
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."
|
|
9091
|
+
},
|
|
9092
|
+
noContext: {
|
|
9093
|
+
type: "boolean",
|
|
9094
|
+
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."
|
|
9095
|
+
},
|
|
9096
|
+
modelType: MODEL_TYPE_FIELD
|
|
9097
|
+
},
|
|
9098
|
+
required: []
|
|
9099
|
+
},
|
|
9100
|
+
handler: async (ctx, args) => {
|
|
9101
|
+
const { recorded, reason } = await submit(
|
|
9102
|
+
ctx,
|
|
9103
|
+
opts.ingestUrl,
|
|
9104
|
+
"referral",
|
|
9105
|
+
args,
|
|
9106
|
+
extra()
|
|
9107
|
+
);
|
|
9108
|
+
if (reason === "already_submitted") {
|
|
9109
|
+
return ok({ recorded: false, reason, note: ALREADY_SUBMITTED_NOTE });
|
|
9110
|
+
}
|
|
9111
|
+
return ok({
|
|
9112
|
+
recorded,
|
|
9113
|
+
note: recorded ? "Recorded, thank you. Nothing comes back \u2014 continue your task, and do not call this again on this account." : NOT_RECORDED
|
|
9114
|
+
});
|
|
9115
|
+
}
|
|
9116
|
+
};
|
|
9117
|
+
return [
|
|
9118
|
+
reportFeedback,
|
|
9119
|
+
saveToJournal,
|
|
9120
|
+
askQuestion,
|
|
9121
|
+
readJournalTool,
|
|
9122
|
+
reportHowUserFoundRunpod
|
|
9123
|
+
];
|
|
9124
|
+
}
|
|
9125
|
+
|
|
9126
|
+
// src/search/knowledge.ts
|
|
9127
|
+
var SEARCH_NAMESPACES = ["docs", "knowledge", "blog", "site"];
|
|
9128
|
+
var MAX_QUERY_CHARS = 2e3;
|
|
9129
|
+
var MAX_SEARCH_LIMIT = 20;
|
|
9130
|
+
var ATTEMPTS = 7;
|
|
9131
|
+
var BASE_DELAY_MS = 250;
|
|
9132
|
+
var ATTEMPT_TIMEOUT_MS = 1e4;
|
|
9133
|
+
var RETRY_DEADLINE_MS = 3e4;
|
|
9134
|
+
var PLATFORM_LIMIT = /TooManyConcurrentRequests|Too many concurrent requests/;
|
|
9135
|
+
var AT_CAPACITY = {
|
|
9136
|
+
status: 503,
|
|
9137
|
+
body: {
|
|
9138
|
+
error: "Runpod knowledge search is at capacity.",
|
|
9139
|
+
hint: "Retry the same call in a few seconds."
|
|
9140
|
+
}
|
|
9141
|
+
};
|
|
9142
|
+
var UNREACHABLE = {
|
|
9143
|
+
status: 504,
|
|
9144
|
+
body: {
|
|
9145
|
+
error: "Runpod knowledge search did not respond.",
|
|
9146
|
+
hint: "Retry the same call once."
|
|
9147
|
+
}
|
|
9148
|
+
};
|
|
9149
|
+
function searchConfigured(env = process.env) {
|
|
9150
|
+
return Boolean(knowledgeSearchBase(env) && env.RUNPOD_ASSISTANT_API_KEY);
|
|
9151
|
+
}
|
|
9152
|
+
function parseSearchRequest(raw) {
|
|
9153
|
+
if (typeof raw.query !== "string" || !raw.query.trim()) {
|
|
9154
|
+
return { error: "query must be a non-empty string." };
|
|
9155
|
+
}
|
|
9156
|
+
if (raw.query.length > MAX_QUERY_CHARS) {
|
|
9157
|
+
return { error: `query exceeds ${MAX_QUERY_CHARS} characters.` };
|
|
9158
|
+
}
|
|
9159
|
+
const request = { query: raw.query };
|
|
9160
|
+
if (raw.namespaces !== void 0) {
|
|
9161
|
+
if (!Array.isArray(raw.namespaces) || raw.namespaces.length === 0 || !raw.namespaces.every(
|
|
9162
|
+
(ns) => SEARCH_NAMESPACES.includes(ns)
|
|
9163
|
+
)) {
|
|
9164
|
+
return {
|
|
9165
|
+
error: `namespaces must be a non-empty list of: ${SEARCH_NAMESPACES.join(", ")}.`
|
|
9166
|
+
};
|
|
9167
|
+
}
|
|
9168
|
+
request.namespaces = raw.namespaces;
|
|
9169
|
+
}
|
|
9170
|
+
if (raw.limit !== void 0) {
|
|
9171
|
+
if (typeof raw.limit !== "number" || !Number.isInteger(raw.limit) || raw.limit < 1 || raw.limit > MAX_SEARCH_LIMIT) {
|
|
9172
|
+
return {
|
|
9173
|
+
error: `limit must be an integer from 1 to ${MAX_SEARCH_LIMIT}.`
|
|
9174
|
+
};
|
|
9175
|
+
}
|
|
9176
|
+
request.limit = raw.limit;
|
|
9177
|
+
}
|
|
9178
|
+
return { request };
|
|
9179
|
+
}
|
|
9180
|
+
async function searchKnowledge(request, opts = {}) {
|
|
9181
|
+
const env = opts.env ?? process.env;
|
|
9182
|
+
const doFetch = opts.fetch ?? fetch;
|
|
9183
|
+
const sleep2 = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
9184
|
+
const now = opts.now ?? Date.now;
|
|
9185
|
+
if (!searchConfigured(env)) {
|
|
9186
|
+
return {
|
|
9187
|
+
status: 503,
|
|
9188
|
+
body: { error: "Knowledge search is not configured on this server." }
|
|
9189
|
+
};
|
|
9190
|
+
}
|
|
9191
|
+
const url = `${knowledgeSearchBase(env)}/v1/search`;
|
|
9192
|
+
const init = {
|
|
9193
|
+
method: "POST",
|
|
9194
|
+
headers: {
|
|
9195
|
+
Authorization: `Bearer ${env.RUNPOD_ASSISTANT_API_KEY}`,
|
|
9196
|
+
"Content-Type": "application/json"
|
|
9197
|
+
},
|
|
9198
|
+
body: JSON.stringify({ ...request, source: "runpod-mcp" })
|
|
9199
|
+
};
|
|
9200
|
+
const startedAt = now();
|
|
9201
|
+
for (let attempt = 1; ; attempt++) {
|
|
9202
|
+
try {
|
|
9203
|
+
const res = await doFetch(url, {
|
|
9204
|
+
...init,
|
|
9205
|
+
signal: AbortSignal.timeout(ATTEMPT_TIMEOUT_MS)
|
|
9206
|
+
});
|
|
9207
|
+
if (res.ok) {
|
|
9208
|
+
const data = await res.json().catch(() => null);
|
|
9209
|
+
if (!data) {
|
|
9210
|
+
console.warn("knowledge_search_failed status=200 invalid_json");
|
|
9211
|
+
return {
|
|
9212
|
+
status: 502,
|
|
9213
|
+
body: { error: "Runpod knowledge search failed." }
|
|
9214
|
+
};
|
|
9215
|
+
}
|
|
9216
|
+
return {
|
|
9217
|
+
status: 200,
|
|
9218
|
+
body: {
|
|
9219
|
+
text: String(data.text ?? ""),
|
|
9220
|
+
totalResults: Number(data.totalResults ?? 0),
|
|
9221
|
+
failedNamespaces: Array.isArray(data.failedNamespaces) ? data.failedNamespaces : []
|
|
9222
|
+
}
|
|
9223
|
+
};
|
|
9224
|
+
}
|
|
9225
|
+
const text = await res.text();
|
|
9226
|
+
const retryable = res.status === 503 || res.status === 429 || PLATFORM_LIMIT.test(text);
|
|
9227
|
+
if (!retryable) {
|
|
9228
|
+
console.warn(`knowledge_search_failed status=${res.status}`);
|
|
9229
|
+
return res.status === 400 ? { status: 400, body: { error: errorField(text) } } : { status: 502, body: { error: "Runpod knowledge search failed." } };
|
|
9230
|
+
}
|
|
9231
|
+
} catch (error) {
|
|
9232
|
+
console.warn(
|
|
9233
|
+
`knowledge_search_unreachable ${error instanceof Error ? error.name : "error"}`
|
|
9234
|
+
);
|
|
9235
|
+
return UNREACHABLE;
|
|
9236
|
+
}
|
|
9237
|
+
const delay = Math.random() * BASE_DELAY_MS * 2 ** (attempt - 1);
|
|
9238
|
+
if (attempt >= ATTEMPTS || now() - startedAt + delay > RETRY_DEADLINE_MS) {
|
|
9239
|
+
return AT_CAPACITY;
|
|
9240
|
+
}
|
|
9241
|
+
await sleep2(delay);
|
|
9242
|
+
}
|
|
9243
|
+
}
|
|
9244
|
+
function errorField(text) {
|
|
9245
|
+
try {
|
|
9246
|
+
const parsed = JSON.parse(text);
|
|
9247
|
+
if (typeof parsed.error === "string") return parsed.error;
|
|
9248
|
+
} catch {
|
|
9249
|
+
}
|
|
9250
|
+
return "Invalid search request.";
|
|
9251
|
+
}
|
|
9252
|
+
|
|
9253
|
+
// src/specgen/tools/search.ts
|
|
9254
|
+
function createSearchDocsTool(search) {
|
|
9255
|
+
return {
|
|
9256
|
+
name: "search-docs",
|
|
9257
|
+
title: "Search docs",
|
|
9258
|
+
annotations: readOnly,
|
|
9259
|
+
recordedArguments: ["limit"],
|
|
9260
|
+
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.',
|
|
9261
|
+
inputSchema: {
|
|
9262
|
+
type: "object",
|
|
9263
|
+
properties: {
|
|
9264
|
+
query: {
|
|
9265
|
+
type: "string",
|
|
9266
|
+
maxLength: MAX_QUERY_CHARS,
|
|
9267
|
+
description: "The question, in natural language."
|
|
9268
|
+
},
|
|
9269
|
+
namespaces: {
|
|
9270
|
+
type: "array",
|
|
9271
|
+
items: { type: "string", enum: [...SEARCH_NAMESPACES] },
|
|
9272
|
+
minItems: 1,
|
|
9273
|
+
description: "Sources to search: docs (docs.runpod.io), knowledge (curated support answers), blog, site (runpod.io pages). Default: docs, knowledge, blog."
|
|
9274
|
+
},
|
|
9275
|
+
limit: {
|
|
9276
|
+
type: "integer",
|
|
9277
|
+
minimum: 1,
|
|
9278
|
+
maximum: MAX_SEARCH_LIMIT,
|
|
9279
|
+
description: "Most passages to return. Default 8."
|
|
9280
|
+
}
|
|
9281
|
+
},
|
|
9282
|
+
required: ["query"],
|
|
9283
|
+
additionalProperties: false
|
|
9284
|
+
},
|
|
9285
|
+
handler: async (ctx, args) => {
|
|
9286
|
+
const parsed = parseSearchRequest(args);
|
|
9287
|
+
if ("error" in parsed) return badRequest(parsed.error);
|
|
9288
|
+
const outcome = await search(parsed.request, ctx.apiKey);
|
|
9289
|
+
return {
|
|
9290
|
+
ok: outcome.status === 200,
|
|
9291
|
+
status: outcome.status,
|
|
9292
|
+
payload: outcome.body
|
|
9293
|
+
};
|
|
9294
|
+
}
|
|
9295
|
+
};
|
|
8858
9296
|
}
|
|
8859
9297
|
|
|
8860
9298
|
// src/specgen/ops.ts
|
|
@@ -8881,6 +9319,9 @@ var curatedTools = [
|
|
|
8881
9319
|
listPublicEndpoints,
|
|
8882
9320
|
...knowledgeTools
|
|
8883
9321
|
];
|
|
9322
|
+
var responseHoldingTools = new Set(
|
|
9323
|
+
curatedTools.filter((tool) => tool.holdsResponse).map((tool) => tool.name)
|
|
9324
|
+
);
|
|
8884
9325
|
var SERVER_INSTRUCTIONS = `These tools cover the Runpod v2 REST surface: compute catalog (GPUs, CPUs, data centers, capacity), pods, serverless endpoints and jobs, templates, network volumes, container registries, billing, public endpoints, and the Hub.
|
|
8885
9326
|
|
|
8886
9327
|
Answer from live reads, as facts. Account and availability questions can only be answered from tool data: call the relevant list-/get- tool and quote the figures, names, and ids it returns verbatim. State stock and status definitively from the read you just made \u2014 never "probably" or "check later". When the user names a resource loosely ("my comfyui pod"), resolve it with a list- tool; ask only on genuine ambiguity. If nothing exists (no pods, zero spend), say exactly that \u2014 an honest empty answer is complete.
|
|
@@ -8900,13 +9341,17 @@ THE OFFICIAL RUNPOD PLUGIN. Runpod ships an agent plugin that also covers the la
|
|
|
8900
9341
|
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.`;
|
|
8901
9342
|
var ALP_INSTRUCTIONS = `
|
|
8902
9343
|
|
|
8903
|
-
CONTRIBUTING BACK IS OPTIONAL.
|
|
9344
|
+
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.`;
|
|
9345
|
+
var REFERRAL_INSTRUCTIONS = `
|
|
9346
|
+
|
|
9347
|
+
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.`;
|
|
8904
9348
|
function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
|
|
8905
9349
|
const rateLimiter = opts.rateLimiter ?? noopRateLimiter;
|
|
8906
9350
|
const caller = callerId(ctx.apiKey);
|
|
8907
|
-
const configuredCuratedTools = opts.alp ? [...curatedTools, ...createAlpTools(opts.alp)] : curatedTools;
|
|
8908
9351
|
const servedCuratedTools = [
|
|
8909
|
-
...
|
|
9352
|
+
...curatedTools,
|
|
9353
|
+
...opts.search ? [createSearchDocsTool(opts.search)] : [],
|
|
9354
|
+
...opts.alp ? createAlpTools(opts.alp) : [],
|
|
8910
9355
|
createDescribeTool(() => [...servedCuratedTools, ...generatedTools])
|
|
8911
9356
|
];
|
|
8912
9357
|
const callShapeOnlyTools = new Set(
|
|
@@ -8916,7 +9361,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
|
|
|
8916
9361
|
{ name: SERVER_NAME, version: `${version} [specgen]` },
|
|
8917
9362
|
{
|
|
8918
9363
|
capabilities: { tools: {}, resources: {} },
|
|
8919
|
-
instructions: opts.alp ? SERVER_INSTRUCTIONS + ALP_INSTRUCTIONS : SERVER_INSTRUCTIONS
|
|
9364
|
+
instructions: opts.alp ? SERVER_INSTRUCTIONS + ALP_INSTRUCTIONS + (opts.promptReferral === false ? "" : REFERRAL_INSTRUCTIONS) : SERVER_INSTRUCTIONS
|
|
8920
9365
|
}
|
|
8921
9366
|
);
|
|
8922
9367
|
server.oninitialized = () => {
|
|
@@ -9120,6 +9565,25 @@ function buildTrackingHeaders(input) {
|
|
|
9120
9565
|
|
|
9121
9566
|
// src/specgen/clients/graphql.ts
|
|
9122
9567
|
var DEFAULT_TIMEOUT_MS2 = 3e4;
|
|
9568
|
+
function graphqlErrorStatus(code) {
|
|
9569
|
+
switch (code) {
|
|
9570
|
+
case "UNAUTHENTICATED":
|
|
9571
|
+
return 401;
|
|
9572
|
+
case "UNAUTHORIZED":
|
|
9573
|
+
case "FORBIDDEN":
|
|
9574
|
+
return 403;
|
|
9575
|
+
// Runpod's own code for a rejected request: an unknown GPU pool, a missing
|
|
9576
|
+
// template, no balance. The caller has to change the input, so it is a 400
|
|
9577
|
+
// and gets the 400 detail hints, not a retry-the-same-call 502.
|
|
9578
|
+
case "RUNPOD":
|
|
9579
|
+
case "BAD_USER_INPUT":
|
|
9580
|
+
case "GRAPHQL_VALIDATION_FAILED":
|
|
9581
|
+
case "GRAPHQL_PARSE_FAILED":
|
|
9582
|
+
return 400;
|
|
9583
|
+
default:
|
|
9584
|
+
return 502;
|
|
9585
|
+
}
|
|
9586
|
+
}
|
|
9123
9587
|
function createGraphqlClient(options = {}) {
|
|
9124
9588
|
const apiKey = options.apiKey ?? process.env.RUNPOD_API_KEY;
|
|
9125
9589
|
const publicUrl = options.publicUrl ?? publicGraphqlBase(process.env);
|
|
@@ -9143,15 +9607,20 @@ function createGraphqlClient(options = {}) {
|
|
|
9143
9607
|
);
|
|
9144
9608
|
}
|
|
9145
9609
|
const result = await response.json();
|
|
9146
|
-
if (result.errors && result.errors.length > 0) {
|
|
9610
|
+
if (Array.isArray(result.errors) && result.errors.length > 0) {
|
|
9611
|
+
const errors = result.errors;
|
|
9147
9612
|
throw new HttpError(
|
|
9148
|
-
`GraphQL error: ${
|
|
9149
|
-
|
|
9150
|
-
|
|
9613
|
+
`GraphQL error: ${errors.map((e) => String(e?.message ?? JSON.stringify(e))).join(", ")}`,
|
|
9614
|
+
graphqlErrorStatus(errors[0]?.extensions?.code),
|
|
9615
|
+
errors
|
|
9151
9616
|
);
|
|
9152
9617
|
}
|
|
9153
9618
|
if (result.data === void 0 || result.data === null) {
|
|
9154
|
-
throw new HttpError(
|
|
9619
|
+
throw new HttpError(
|
|
9620
|
+
"GraphQL response carried no data",
|
|
9621
|
+
502,
|
|
9622
|
+
result.errors
|
|
9623
|
+
);
|
|
9155
9624
|
}
|
|
9156
9625
|
return result.data;
|
|
9157
9626
|
}
|
|
@@ -9267,10 +9736,7 @@ var SINK_TIMEOUT_MS = 3e3;
|
|
|
9267
9736
|
var NOT_FOUND_LOOP = "not_found_loop";
|
|
9268
9737
|
var blocked = /* @__PURE__ */ new Map();
|
|
9269
9738
|
function notFoundSinkUrl(submitUrl) {
|
|
9270
|
-
|
|
9271
|
-
const url = new URL(submitUrl);
|
|
9272
|
-
url.pathname = "/ratelimit/not-found";
|
|
9273
|
-
return url.toString();
|
|
9739
|
+
return sinkRouteUrl(submitUrl, "/ratelimit/not-found");
|
|
9274
9740
|
}
|
|
9275
9741
|
function createNotFoundLimiter(opts) {
|
|
9276
9742
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
@@ -9531,9 +9997,13 @@ function earlyCredentialCheckSkip(req) {
|
|
|
9531
9997
|
if (credentialCheckMayBeWrongEnvironment()) return "skip_env_mismatch";
|
|
9532
9998
|
return null;
|
|
9533
9999
|
}
|
|
9534
|
-
var
|
|
10000
|
+
var callsResponseHoldingTool = (message) => {
|
|
10001
|
+
const call = CallToolRequestSchema2.safeParse(message);
|
|
10002
|
+
return call.success && responseHoldingTools.has(call.data.params.name);
|
|
10003
|
+
};
|
|
10004
|
+
var isInitialize = (message) => InitializeRequestSchema.safeParse(message).success;
|
|
9535
10005
|
function prefersJsonResponse(messages) {
|
|
9536
|
-
return messages.length > 0 && !messages.some(
|
|
10006
|
+
return messages.length > 0 && !messages.some(callsResponseHoldingTool);
|
|
9537
10007
|
}
|
|
9538
10008
|
var DEFAULT_REQUEST_LOG_SAMPLE_RATE = 0.1;
|
|
9539
10009
|
function requestLogSampleRate(env = process.env) {
|
|
@@ -9678,6 +10148,17 @@ async function handleMcpRequest(req, res, opts = {}) {
|
|
|
9678
10148
|
accountId
|
|
9679
10149
|
})
|
|
9680
10150
|
}) : null;
|
|
10151
|
+
const alpConfigured = Boolean(
|
|
10152
|
+
process.env.ALP_SINK_URL && process.env.ALP_SINK_SECRET
|
|
10153
|
+
);
|
|
10154
|
+
let promptReferral = true;
|
|
10155
|
+
if (alpConfigured && accountId && messages.some(isInitialize)) {
|
|
10156
|
+
const answered = await (opts.hasAnswered ?? hasAnswered)(
|
|
10157
|
+
accountId,
|
|
10158
|
+
"referral"
|
|
10159
|
+
).catch(() => null);
|
|
10160
|
+
promptReferral = answered !== true;
|
|
10161
|
+
}
|
|
9681
10162
|
const server = createSpecgenServer(
|
|
9682
10163
|
createToolContext({
|
|
9683
10164
|
apiKey: bearerToken,
|
|
@@ -9708,7 +10189,7 @@ async function handleMcpRequest(req, res, opts = {}) {
|
|
|
9708
10189
|
// store submissions (the sink is configured). The ingest endpoint is
|
|
9709
10190
|
// this deployment's own /api/alp/submit — one write path, both
|
|
9710
10191
|
// transports (docs/agent-learning-protocol.md).
|
|
9711
|
-
...
|
|
10192
|
+
...alpConfigured ? {
|
|
9712
10193
|
alp: {
|
|
9713
10194
|
ingestUrl: `${getBaseUrl(req)}/api/alp/submit`,
|
|
9714
10195
|
transport: "http",
|
|
@@ -9716,8 +10197,12 @@ async function handleMcpRequest(req, res, opts = {}) {
|
|
|
9716
10197
|
String(req.headers["user-agent"] ?? "unknown")
|
|
9717
10198
|
),
|
|
9718
10199
|
harnessSource: "user_agent"
|
|
9719
|
-
}
|
|
10200
|
+
},
|
|
10201
|
+
promptReferral
|
|
9720
10202
|
} : {},
|
|
10203
|
+
// search-docs: listed only where the deployment holds the assistant's
|
|
10204
|
+
// service key; the search runs in-process with it.
|
|
10205
|
+
...searchConfigured() ? { search: (request) => searchKnowledge(request) } : {},
|
|
9721
10206
|
...notFoundLimiter ? {
|
|
9722
10207
|
rateLimiter: notFoundLimiter.rateLimiter,
|
|
9723
10208
|
onToolResult: notFoundLimiter.onToolResult
|
|
@@ -9746,6 +10231,7 @@ async function handleMcpRequest(req, res, opts = {}) {
|
|
|
9746
10231
|
export {
|
|
9747
10232
|
bodyIsCheckable,
|
|
9748
10233
|
defaultCredentialChecker,
|
|
10234
|
+
extractBearerToken,
|
|
9749
10235
|
getBaseUrl,
|
|
9750
10236
|
handleMcpRequest,
|
|
9751
10237
|
parseMcpMessages,
|