@runpod/mcp-server 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tools.d.cts CHANGED
@@ -25,6 +25,18 @@ declare function createHttpClient(deps: {
25
25
  errorPrefix: string;
26
26
  }): HttpClient;
27
27
 
28
+ interface SseResponse {
29
+ ok: boolean;
30
+ status: number;
31
+ text(): Promise<string>;
32
+ body: AsyncIterable<Buffer> | null;
33
+ }
34
+ type SseFetch = (url: string, init: {
35
+ method: string;
36
+ headers: Record<string, string>;
37
+ signal: AbortSignal;
38
+ }) => Promise<SseResponse>;
39
+
28
40
  /**
29
41
  * Context passed to every tool handler. Contains the API key used to
30
42
  * authenticate against the Runpod REST and Serverless APIs, the transport the
@@ -36,12 +48,14 @@ interface ToolContext {
36
48
  transport: 'stdio' | 'http';
37
49
  clientUserAgent?: string;
38
50
  serverVersion?: string;
51
+ onUnauthorized?: () => void;
39
52
  }
40
53
  type HttpFetch = Parameters<typeof createHttpClient>[0]['fetch'];
41
54
  interface ToolDeps {
42
55
  fetch?: HttpFetch;
43
56
  v2Available?: boolean;
44
57
  streamSse?: StreamSse;
58
+ sseFetch?: SseFetch;
45
59
  }
46
60
  interface StreamSseResult {
47
61
  raw: string;
package/dist/tools.d.ts CHANGED
@@ -25,6 +25,18 @@ declare function createHttpClient(deps: {
25
25
  errorPrefix: string;
26
26
  }): HttpClient;
27
27
 
28
+ interface SseResponse {
29
+ ok: boolean;
30
+ status: number;
31
+ text(): Promise<string>;
32
+ body: AsyncIterable<Buffer> | null;
33
+ }
34
+ type SseFetch = (url: string, init: {
35
+ method: string;
36
+ headers: Record<string, string>;
37
+ signal: AbortSignal;
38
+ }) => Promise<SseResponse>;
39
+
28
40
  /**
29
41
  * Context passed to every tool handler. Contains the API key used to
30
42
  * authenticate against the Runpod REST and Serverless APIs, the transport the
@@ -36,12 +48,14 @@ interface ToolContext {
36
48
  transport: 'stdio' | 'http';
37
49
  clientUserAgent?: string;
38
50
  serverVersion?: string;
51
+ onUnauthorized?: () => void;
39
52
  }
40
53
  type HttpFetch = Parameters<typeof createHttpClient>[0]['fetch'];
41
54
  interface ToolDeps {
42
55
  fetch?: HttpFetch;
43
56
  v2Available?: boolean;
44
57
  streamSse?: StreamSse;
58
+ sseFetch?: SseFetch;
45
59
  }
46
60
  interface StreamSseResult {
47
61
  raw: string;
package/dist/tools.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  registerTools
3
- } from "./chunk-ZP3TCVXG.mjs";
3
+ } from "./chunk-GTBCNGHE.mjs";
4
4
  export {
5
5
  registerTools
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runpod/mcp-server",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "MCP server for interacting with Runpod API",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/tools/runtime.ts","../src/_shared/http.ts","../src/_shared/tracking.ts","../src/_shared/mappers.ts","../src/_shared/backend.ts","../src/tools/catalog.ts","../src/pagination.ts","../src/tools/pods.ts","../src/tools/endpoints.ts","../src/tools/jobs.ts","../src/tools/templates.ts","../src/tools/network-volumes.ts","../src/tools/registries.ts","../src/tools/tags.ts","../src/tools/billing.ts","../src/tools.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport fetch from 'node-fetch';\nimport { randomUUID } from 'node:crypto';\nimport { createHttpClient, HttpError } from '../_shared/http.js';\nimport { buildTrackingHeaders } from '../_shared/tracking.js';\nimport {\n resolveBackend,\n restV1Base,\n serverlessBase,\n publicGraphqlBase,\n type Env,\n type Backend,\n type Resource,\n} from '../_shared/backend.js';\n\n// ============== TOOL RUNTIME (shared per-server wiring) ==============\n// The per-resource tool modules (src/tools/<resource>.ts) are pure registration\n// functions: each takes the MCP server + this runtime and calls server.tool(...).\n// Everything that depends on the live server instance or the caller's API key —\n// caller-tracking, the authenticated request clients, the v1/v2 backend resolver\n// — is built ONCE here by createToolRuntime and threaded in, so the resource\n// modules stay free of wiring and are individually testable.\n\n// Base URLs are resolved LIVE per call (via restV1Base/serverlessBase/\n// publicGraphqlBase reading process.env), not frozen at module import — so they\n// match callRestUrl's behavior and a test/env change takes effect without a\n// module reload. The defaults live in _shared/backend.ts. The public GraphQL\n// endpoint is distinct from the flash auth backend (RUNPOD_GRAPHQL_URL, used\n// only by the OAuth flow in api/index.ts).\n\n// ============== CALLER TRACKING ==============\n// Adds structured caller identification to every outbound API call so the\n// Runpod platform can attribute traffic to a specific MCP client (Claude Code,\n// Cursor, Codex, Gemini CLI, etc.) and count distinct agent sessions. Pure\n// observability — no tool behavior changes.\n//\n// `__PACKAGE_VERSION__` is replaced at build time by tsup's `define` with the\n// current `version` from package.json. Falls back to `'dev'` when the\n// substitution doesn't happen (e.g. `pnpm dev` via tsx).\ndeclare const __PACKAGE_VERSION__: string;\nconst MCP_SERVER_VERSION =\n typeof __PACKAGE_VERSION__ !== 'undefined' ? __PACKAGE_VERSION__ : 'dev';\n\n// One session ID per server process. Anonymous; lets the data side count\n// distinct agent sessions without per-user attribution.\nconst SESSION_ID = randomUUID();\n\n/**\n * Context passed to every tool handler. Contains the API key used to\n * authenticate against the Runpod REST and Serverless APIs, the transport the\n * server is running under, and (for stateless HTTP) the inbound client\n * User-Agent — all used for outbound caller-tracking headers.\n */\nexport interface ToolContext {\n apiKey: string;\n transport: 'stdio' | 'http';\n clientUserAgent?: string;\n serverVersion?: string;\n}\n\n/**\n * Builds the headers that identify the calling MCP client and session on every\n * outbound HTTP call. Client identity comes from the `initialize` handshake's\n * `clientInfo` (exposed by the SDK via `server.server.getClientVersion()`),\n * which works for the long-lived stdio server. In stateless HTTP that handshake\n * is on a different request, so we fall back to the inbound HTTP User-Agent.\n *\n * Resolution of clientInfo → fallback happens here (the SDK touch); the pure\n * string formatting lives in `_shared/tracking.ts`.\n */\nfunction trackingHeaders(\n server: McpServer,\n ctx: ToolContext\n): Record<string, string> {\n const info = server.server.getClientVersion();\n return buildTrackingHeaders({\n clientName: info?.name || ctx.clientUserAgent || 'unknown',\n clientVersion: info?.version || 'unknown',\n transport: ctx.transport,\n // Prefer a server version supplied by the caller (the hosted entrypoint reads\n // it from package.json at runtime, since tsup's build-time define does not\n // run when Vercel compiles api/index.ts). Fall back to the build-time value.\n serverVersion: ctx.serverVersion || MCP_SERVER_VERSION,\n sessionId: SESSION_ID,\n });\n}\n\n// The fetch implementation the unified client uses. Defaults to node-fetch;\n// tests inject a fake to capture outbound requests offline (the A4 seam).\nexport type HttpFetch = Parameters<typeof createHttpClient>[0]['fetch'];\nconst defaultFetch = fetch as HttpFetch;\n\n// Wrap a unified-client call with the legacy `console.error(label) + re-throw`\n// the pre-adapter runpodRequest/serverlessRequest helpers had. Centralizes the\n// identical try/catch blocks into one place.\nasync function withApiErrorLog<T>(\n label: string,\n call: () => Promise<T>\n): Promise<T> {\n try {\n return await call();\n } catch (error) {\n console.error(label, error);\n throw error;\n }\n}\n\n// The standard MCP tool reply: a single text block holding pretty-printed JSON.\n// Every tool returns this shape.\nexport function jsonReply(result: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n };\n}\n\n// Shared MCP tool-annotation presets (advisory hints clients use to label tools\n// and gate confirmations). `openWorldHint` is true on all of these — every tool\n// talks to the external Runpod API. READ_ONLY = pure reads; WRITE = creates/\n// mutations; DESTRUCTIVE = deletes (also idempotent: re-deleting a gone resource\n// is a no-op). Spread a preset and add a per-tool `title`.\nexport const READ_ONLY = { readOnlyHint: true, openWorldHint: true };\nexport const WRITE = { readOnlyHint: false, openWorldHint: true };\nexport const DESTRUCTIVE = {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: true,\n};\n\n// Optional test seam: inject a fake fetch to capture outbound requests offline.\n// Production passes nothing → node-fetch. `v2Available` is the already-resolved\n// stdio `auto`-probe verdict (see backend.resolveVersion); omitted → `auto`\n// resolves to v1.\nexport interface ToolDeps {\n fetch?: HttpFetch;\n v2Available?: boolean;\n // Test seam for the SSE log stream (stream-pod-logs). Production builds a\n // node-fetch reader; tests inject a fake returning canned event-stream text.\n streamSse?: StreamSse;\n}\n\n// A bounded Server-Sent-Events read. Returns the raw accumulated stream text\n// (caller parses frames) plus whether the byte cap truncated it. Time-bounded\n// by maxWaitMs (the stream may stay open for live tail), so a clean timeout is\n// a normal end, not an error.\nexport interface StreamSseResult {\n raw: string;\n truncated: boolean;\n}\nexport type StreamSse = (\n url: string,\n opts: { maxWaitMs: number; maxBytes: number }\n) => Promise<StreamSseResult>;\n\n/**\n * The shared, server-instance-bound runtime threaded into every per-resource\n * registrar. Built once by createToolRuntime.\n */\nexport interface ToolRuntime {\n // Pretty-printed JSON reply helper (re-exported for convenience).\n jsonReply: typeof jsonReply;\n // Public, no-auth GraphQL query against the Runpod GraphQL endpoint.\n graphql: <T>(query: string) => Promise<T>;\n // Authenticated v1 REST call, path-relative to the v1 base (e.g. `/endpoints`).\n runpodRequest: (\n endpoint: string,\n method?: string,\n body?: Record<string, unknown>\n ) => Promise<unknown>;\n // Authenticated Serverless runtime call (api.runpod.ai/v2/{endpointId}{path}).\n serverlessRequest: (\n endpointId: string,\n path: string,\n method?: string,\n body?: Record<string, unknown>\n ) => Promise<unknown>;\n // Authenticated REST call to a fully-resolved URL (the adapter builds the URL).\n callRestUrl: (\n url: string,\n method?: string,\n body?: Record<string, unknown>\n ) => Promise<unknown>;\n // Resolve a resource's v1/v2 backend descriptor for the current env/transport.\n backendFor: (resource: Resource) => Backend;\n // Bounded SSE reader (stream-pod-logs). Authenticated + tracked; reads a\n // text/event-stream until the byte cap or the time bound, returning the raw\n // accumulated text. Throws HttpError on a non-OK response.\n streamSse: StreamSse;\n // Pod state transition (v1 subpaths vs v2 unified /action).\n podAction: (\n podId: string,\n action: 'start' | 'stop' | 'restart'\n ) => Promise<unknown>;\n // The process env, exposed for the few handlers that resolve base URLs\n // directly (e.g. create-pod's CPU → v1 fallback).\n env: Env;\n}\n\n// Helper to make GraphQL requests to Runpod (public, no auth required). Bound to\n// the per-request tracking headers AND the injected fetch by createToolRuntime —\n// it must use the same `httpFetch` seam as the REST clients, NOT the module-level\n// node-fetch, or test fakes wouldn't intercept the v1 catalog (list-gpu-types /\n// list-data-centers) GraphQL calls and those \"offline\" goldens would hit the net.\nasync function graphqlRequest<T>(\n query: string,\n tracking: () => Record<string, string>,\n fetchImpl: HttpFetch,\n url: string\n): Promise<T> {\n const response = await fetchImpl(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...tracking(),\n },\n body: JSON.stringify({ query }),\n });\n\n const result = (await response.json()) as {\n data?: T;\n errors?: Array<{ message: string }>;\n };\n\n if (result.errors && result.errors.length > 0) {\n throw new Error(\n `GraphQL Error: ${result.errors.map((e) => e.message).join(', ')}`\n );\n }\n\n if (!result.data) {\n throw new Error('No data returned from GraphQL query');\n }\n\n return result.data;\n}\n\n/**\n * Build the shared runtime once per registerTools call. Constructs the\n * caller-tracking closure, the authenticated REST/Serverless/GraphQL clients,\n * and the version-aware backend resolver — all from the live server + context.\n */\nexport function createToolRuntime(\n server: McpServer,\n ctx: ToolContext,\n deps: ToolDeps = {}\n): ToolRuntime {\n const tracking = () => trackingHeaders(server, ctx);\n const httpFetch = deps.fetch ?? defaultFetch;\n\n // v1 REST client (path-relative to the v1 base).\n const v1Client = createHttpClient({\n apiKey: ctx.apiKey,\n fetch: httpFetch,\n tracking,\n errorPrefix: 'Runpod API Error',\n });\n const runpodRequest = (\n endpoint: string,\n method: string = 'GET',\n body?: Record<string, unknown>\n ) =>\n withApiErrorLog('Error calling Runpod API:', () =>\n v1Client(`${restV1Base(process.env as Env)}${endpoint}`, method, body)\n );\n\n // Serverless runtime client (endpointId + path against the serverless base).\n const serverlessClient = createHttpClient({\n apiKey: ctx.apiKey,\n fetch: httpFetch,\n tracking,\n errorPrefix: 'Runpod Serverless API Error',\n });\n const serverlessRequest = (\n endpointId: string,\n path: string,\n method: string = 'GET',\n body?: Record<string, unknown>\n ) =>\n withApiErrorLog('Error calling Runpod Serverless API:', () =>\n serverlessClient(\n `${serverlessBase(process.env as Env)}/${endpointId}${path}`,\n method,\n body\n )\n );\n\n // v2-aware REST routing (keystone): a full-URL REST client (the adapter\n // resolves base+path per version) used by all adapter-routed resources.\n const restClient = createHttpClient({\n apiKey: ctx.apiKey,\n fetch: httpFetch,\n tracking,\n errorPrefix: 'Runpod API Error',\n });\n const callRestUrl = (\n url: string,\n method: string = 'GET',\n body?: Record<string, unknown>\n ): Promise<unknown> =>\n withApiErrorLog('Error calling Runpod API:', () =>\n restClient(url, method, body)\n );\n\n // Bounded SSE reader for stream-pod-logs. Uses node-fetch directly (not the\n // JSON client) so we can read the response body incrementally and stop on the\n // byte cap or the time bound. The log stream stays open to tail live output,\n // so a timeout-abort is a NORMAL end (return what we collected), not an error;\n // only a non-OK HTTP status throws. Overridable via deps for offline tests.\n const streamSse: StreamSse =\n deps.streamSse ??\n (async (url, opts) => {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), opts.maxWaitMs);\n try {\n const res = await fetch(url, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${ctx.apiKey}`,\n Accept: 'text/event-stream',\n ...tracking(),\n },\n signal: controller.signal,\n });\n if (!res.ok) {\n throw new HttpError(\n 'Runpod API Error',\n res.status,\n await res.text().catch(() => '')\n );\n }\n // Collect raw Buffers and decode ONCE at the end: avoids both a UTF-8\n // char being split across two network chunks (per-chunk toString would\n // corrupt it) and the O(n²) cost of re-measuring a growing string.\n const chunks: Buffer[] = [];\n let bytes = 0;\n let truncated = false;\n try {\n for await (const chunk of res.body as AsyncIterable<Buffer>) {\n chunks.push(chunk);\n bytes += chunk.length;\n if (bytes >= opts.maxBytes) {\n truncated = true;\n controller.abort();\n break;\n }\n }\n } catch (err) {\n // AbortError (timeout or byte-cap abort) is the expected stream end —\n // keep what we read. Anything else propagates.\n if (!(err instanceof Error && err.name === 'AbortError')) throw err;\n }\n return { raw: Buffer.concat(chunks).toString('utf8'), truncated };\n } finally {\n clearTimeout(timer);\n }\n });\n\n const backendFor = (resource: Resource): Backend =>\n resolveBackend({\n resource,\n env: process.env as Env,\n ctx: { transport: ctx.transport },\n v2Available: deps.v2Available,\n });\n\n // B4: pod state transition. v1 has dedicated subpaths (/start, /stop); v2\n // unifies them under POST /pods/{id}/action with an `{action}` body.\n const podAction = async (\n podId: string,\n action: 'start' | 'stop' | 'restart'\n ): Promise<unknown> => {\n const backend = backendFor('pods');\n const podPath = backend.get!(podId);\n if (backend.version === 'v2') {\n return callRestUrl(`${backend.base}${podPath}/action`, 'POST', {\n action,\n });\n }\n return callRestUrl(`${backend.base}${podPath}/${action}`, 'POST');\n };\n\n return {\n jsonReply,\n graphql: <T>(query: string) =>\n graphqlRequest<T>(\n query,\n tracking,\n httpFetch,\n publicGraphqlBase(process.env as Env)\n ),\n runpodRequest,\n serverlessRequest,\n callRestUrl,\n backendFor,\n streamSse,\n podAction,\n env: process.env as Env,\n };\n}\n","// ============== UNIFIED HTTP CLIENT (REST + Serverless) ==============\n// One authenticated JSON client that replaces the byte-identical\n// `runpodRequest` + `serverlessRequest` helpers. The adapter\n// (src/_shared/backend.ts) resolves the full URL, so this client takes a\n// resolved URL — the old `endpointId`-split shape collapses into the path.\n//\n// GraphQL is intentionally NOT routed through here: it sends no Authorization\n// header and has its own `{data,errors}` envelope + error prefix. It stays a\n// separate helper through Phase A.\n//\n// `fetch` and `tracking` are injected so this is unit-testable offline with no\n// network and no MCP SDK.\n\n// The shape of a `fetch` request init this client builds. Named so the client's\n// internal `init` literal can't drift from what `FetchLike` actually accepts.\ninterface RequestInitLike {\n method: string;\n headers: Record<string, string>;\n body?: string;\n}\n\ntype FetchLike = (url: string, init: RequestInitLike) => Promise<HttpResponseLike>;\n\ninterface HttpResponseLike {\n ok: boolean;\n status: number;\n text(): Promise<string>;\n json(): Promise<unknown>;\n headers: { get(name: string): string | null };\n}\n\n// Thrown on any non-OK response. Carries `status` + `body` so callers can\n// branch (e.g. create-pod maps a 501 to a clean \"CPU pods not yet supported\"\n// message) without parsing the message string. Because it still THROWS, loops\n// that count consecutive errors (e.g. stream-job) keep working — a 501 mid-poll\n// is surfaced as an error, not swallowed.\nexport class HttpError extends Error {\n readonly status: number;\n readonly body: string;\n constructor(prefix: string, status: number, body: string) {\n super(`${prefix}: ${status} - ${body}`);\n this.name = 'HttpError';\n this.status = status;\n this.body = body;\n }\n}\n\n// A response whose content-type marks it as JSON — including v2's RFC-9457\n// `application/problem+json` error bodies. We match the `+json`/`/json` shape,\n// NOT the literal substring `application/json` (which `problem+json` does not\n// contain), so v2 error bodies are parsed rather than swallowed.\nfunction isJsonContentType(contentType: string | null): boolean {\n if (!contentType) return false;\n const ct = contentType.toLowerCase();\n return ct.includes('application/json') || ct.includes('+json');\n}\n\n// Body-carrying methods. This is now the single unified client for all REST\n// calls, so include PUT — otherwise a future tool passing method='PUT' with a\n// body would have the payload silently dropped (request sent with no body, no\n// error). GET/DELETE never carry a body.\nfunction methodSendsBody(method: string): boolean {\n return method === 'POST' || method === 'PATCH' || method === 'PUT';\n}\n\n// The auth + content-type + caller-tracking headers sent on every request.\nfunction buildRequestHeaders(\n apiKey: string,\n tracking: () => Record<string, string>\n): Record<string, string> {\n return {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n ...tracking(),\n };\n}\n\nexport interface HttpClient {\n (\n url: string,\n method?: string,\n body?: Record<string, unknown>\n ): Promise<unknown>;\n}\n\nexport function createHttpClient(deps: {\n apiKey: string;\n fetch: FetchLike;\n tracking: () => Record<string, string>;\n // Distinct per backend so error messages stay attributable\n // (\"Runpod API Error\" / \"Runpod Serverless API Error\").\n errorPrefix: string;\n}): HttpClient {\n return async function request(\n url: string,\n method: string = 'GET',\n body?: Record<string, unknown>\n ): Promise<unknown> {\n const init: RequestInitLike = {\n method,\n headers: buildRequestHeaders(deps.apiKey, deps.tracking),\n };\n if (body && methodSendsBody(method)) {\n init.body = JSON.stringify(body);\n }\n\n const response = await deps.fetch(url, init);\n\n if (!response.ok) {\n throw new HttpError(\n deps.errorPrefix,\n response.status,\n await response.text()\n );\n }\n\n // 204 / empty / non-JSON → a uniform success marker (matches today's helpers\n // and covers pod-action responses that return no JSON body).\n return isJsonContentType(response.headers.get('content-type'))\n ? response.json()\n : { success: true, status: response.status };\n };\n}\n","// ============== CALLER TRACKING (pure header construction) ==============\n// The structured User-Agent + session id that identify the calling MCP client\n// on every outbound API call. Pure functions here so they're unit-testable\n// without the MCP SDK; the SDK-touching part (reading clientInfo off the\n// handshake) stays at the call site and is reduced to plain strings before\n// reaching `buildTrackingHeaders`.\n\n// Sanitizes a value for inclusion in a User-Agent token. RFC 7230 reserves\n// parens and a few other chars; strip them so the structured UA stays\n// parseable, and bound the length so a hostile client can't blow up a header.\nexport function sanitizeUaToken(value: string): string {\n return value.replace(/[()<>@,;:\\\\\"/[\\]?={}\\s]/g, '_').slice(0, 64);\n}\n\nexport interface TrackingInput {\n // Client identity resolved from the MCP `initialize` handshake's clientInfo,\n // or the inbound HTTP User-Agent as a fallback (stateless HTTP).\n clientName?: string;\n clientVersion?: string;\n transport: 'stdio' | 'http';\n serverVersion: string;\n sessionId: string;\n}\n\n// Builds the `User-Agent` + `X-Runpod-Session-Id` headers. The clientInfo →\n// fallback → 'unknown' resolution is the caller's job (it owns the SDK handle);\n// this function just sanitizes and formats whatever identity it's given.\nexport function buildTrackingHeaders(\n input: TrackingInput\n): Record<string, string> {\n const name = sanitizeUaToken(input.clientName || 'unknown');\n const version = sanitizeUaToken(input.clientVersion || 'unknown');\n const userAgent = `runpod-mcp-server/${input.serverVersion} (caller=mcp; client=${name}; client_version=${version}; transport=${input.transport})`;\n return {\n 'User-Agent': userAgent,\n 'X-Runpod-Session-Id': input.sessionId,\n };\n}\n","// ============== v1 → v2 REQUEST BODY MAPPERS ==============\n// Pure functions that translate the v1-shaped tool params (what the MCP tool\n// schemas accept today) into v2 REST request bodies. Isolated here so the v2\n// shape is a one-file change when the spec moves.\n//\n// ⚠️ SPEC IS MID-FLIGHT (see PLAN.md Part 7/8). These mappers target the current\n// v2 dev spec snapshot and are pinned by committed fixtures under tests/fixtures.\n// Known pending upstream changes that will require updating these + the fixtures:\n// - `cloud` enum may drop `ALL`\n// - template `category` may become optional / be removed (v2 marks it required\n// today; the create-template tool now exposes `category`, defaulting to\n// NVIDIA only when the caller omits it)\n// When the live spec changes, update the mapper AND its fixture in one commit.\n\n// v1 params accepted by the create-pod / update-pod tool schemas (the fields the\n// mapper knows how to translate). Unknown keys are intentionally dropped.\ninterface V1PodParams {\n name?: string;\n imageName?: string;\n cloudType?: 'SECURE' | 'COMMUNITY';\n gpuTypeIds?: string[];\n gpuCount?: number;\n containerDiskInGb?: number;\n volumeInGb?: number;\n volumeMountPath?: string;\n ports?: string[];\n env?: Record<string, string>;\n dataCenterIds?: string[];\n}\n\n// Drop undefined entries so we never emit explicit `undefined`/`null` the API\n// would reject; keeps the body minimal and the fixtures clean.\nfunction compact<T extends Record<string, unknown>>(obj: T): Partial<T> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (v !== undefined) out[k] = v;\n }\n return out as Partial<T>;\n}\n\n// volumeInGb / volumeMountPath → mounts.persistent.{size,path}.\n// v2 PersistentMount requires BOTH size and path (additionalProperties:false,\n// 422 on a partial). So only emit the mount when both are present; a partial\n// v1 input (size-only or path-only) yields `undefined` and is dropped rather\n// than producing a body the API rejects. (Tool-schema validation should require\n// both together.)\ntype PersistentMount = { persistent: { size: number; path: string } };\nfunction persistentMount(\n size?: number,\n path?: string\n): PersistentMount | undefined {\n if (size === undefined || path === undefined) return undefined;\n return { persistent: { size, path } };\n}\n\n// ---- ContainerConfig flatten shared by pod + template create/update ----\nfunction containerConfigToV2(p: {\n imageName?: string;\n containerDiskInGb?: number;\n volumeInGb?: number;\n volumeMountPath?: string;\n ports?: string[];\n env?: Record<string, string>;\n}): Record<string, unknown> {\n return compact({\n image: p.imageName,\n disk: p.containerDiskInGb,\n ports: p.ports,\n env: p.env,\n mounts: persistentMount(p.volumeInGb, p.volumeMountPath),\n });\n}\n\n// gpuTypeIds[] + gpuCount → gpu: { id, count }. v2 GpuConfig requires `id`, so\n// return undefined for a count-only input — an idless gpu would be rejected\n// (422). count is optional (defaults to 1 server-side).\nfunction gpuConfigToV2(\n gpuTypeIds?: string[],\n gpuCount?: number\n): Record<string, unknown> | undefined {\n const id = gpuTypeIds?.[0];\n if (id === undefined) return undefined;\n return compact({ id, count: gpuCount });\n}\n\nexport function mapPodCreateToV2(params: V1PodParams): Record<string, unknown> {\n return compact({\n ...containerConfigToV2(params),\n name: params.name,\n cloud: params.cloudType,\n // v2 CreatePodRequest takes `dataCenterIds` as an ARRAY (preferred data\n // centers; omit/empty = scheduler chooses). Pass the v1 list straight\n // through — the earlier singular `dataCenter` key did not exist on the pod\n // schema, so placement was being silently dropped. GPU, by contrast, IS\n // singular on v2 (`gpu.id`): gpuConfigToV2 takes the first id; the\n // create-pod handler warns when more than one gpuTypeId was supplied.\n dataCenterIds: params.dataCenterIds?.length\n ? params.dataCenterIds\n : undefined,\n gpu: gpuConfigToV2(params.gpuTypeIds, params.gpuCount),\n });\n}\n\nexport function mapPodUpdateToV2(params: V1PodParams): Record<string, unknown> {\n // Update has no gpu/cloud/dataCenter; just name + ContainerConfig fields.\n return {\n ...containerConfigToV2(params),\n ...compact({ name: params.name }),\n };\n}\n\n// ---- Endpoint (serverless) create/update → v2 /v2/serverless body ----\n// v1 create-endpoint is templateId-based (a fundamentally different model); v2\n// takes an INLINE container + compute config (image, gpu.pools, workers, scaling)\n// and has NO templateId. So this mapper translates the v2-shaped tool params into\n// the nested v2 body. It deliberately does NOT reuse containerConfigToV2 — that\n// helper emits `mounts` (a pod/template concept), which the endpoint schema\n// rejects; endpoints attach storage via `networkVolumes` instead.\n//\n// Required by v2 CreateEndpointRequest: `name` and `gpu` (with `gpu.pools`\n// minItems 1). The create-endpoint handler guards those before calling, so a\n// missing field yields a clean 400 rather than a raw 422 from the API.\ninterface V2EndpointParams {\n name?: string;\n imageName?: string;\n args?: string;\n gpuPoolIds?: string[];\n gpuCount?: number;\n workersMin?: number;\n workersMax?: number;\n scalerType?: 'QUEUE_DELAY' | 'REQUEST_COUNT';\n scalerValue?: number;\n idleTimeout?: number;\n dataCenterIds?: string[];\n networkVolumeIds?: string[];\n executionTimeoutMs?: number;\n flashboot?: 'OFF' | 'FLASHBOOT' | 'PRIORITY_FLASHBOOT';\n containerDiskInGb?: number;\n ports?: string[];\n env?: Record<string, string>;\n containerRegistryAuthId?: string;\n}\n\n// Emit a nested object only when at least one field is set, else undefined (so\n// compact drops it). Prevents sending an empty `workers: {}` / `scaling: {}`.\nfunction nestedOrUndefined(\n obj: Record<string, unknown>\n): Record<string, unknown> | undefined {\n const c = compact(obj);\n return Object.keys(c).length ? c : undefined;\n}\n\n// gpu requires `pools` (minItems 1) — return undefined when no pools so the\n// handler's guard, not the API, reports the omission.\nfunction endpointGpuConfig(\n pools?: string[],\n count?: number\n): Record<string, unknown> | undefined {\n if (!pools?.length) return undefined;\n return compact({ pools, count });\n}\n\nfunction mapEndpointToV2(params: V2EndpointParams): Record<string, unknown> {\n return compact({\n name: params.name,\n image: params.imageName,\n args: params.args,\n disk: params.containerDiskInGb,\n ports: params.ports,\n env: params.env,\n registry: params.containerRegistryAuthId,\n gpu: endpointGpuConfig(params.gpuPoolIds, params.gpuCount),\n workers: nestedOrUndefined({\n min: params.workersMin,\n max: params.workersMax,\n }),\n scaling: nestedOrUndefined({\n type: params.scalerType,\n value: params.scalerValue,\n idleTimeout: params.idleTimeout,\n }),\n dataCenterIds: params.dataCenterIds?.length\n ? params.dataCenterIds\n : undefined,\n networkVolumes: params.networkVolumeIds?.length\n ? params.networkVolumeIds\n : undefined,\n timeout: params.executionTimeoutMs,\n flashboot: params.flashboot,\n });\n}\n\n// Create and update share the same v2 body shape (Update just makes every field\n// optional — including `name`/`gpu`). Same mapper for both; the handler decides\n// which fields are required.\nexport const mapEndpointCreateToV2 = mapEndpointToV2;\nexport const mapEndpointUpdateToV2 = mapEndpointToV2;\n\n// ---- Network volume: dataCenterId → dataCenter (only field change) ----\ninterface V1NetworkVolumeCreate {\n name?: string;\n size?: number;\n dataCenterId?: string;\n}\nexport function mapNetworkVolumeCreateToV2(\n params: V1NetworkVolumeCreate\n): Record<string, unknown> {\n return compact({\n name: params.name,\n size: params.size,\n dataCenter: params.dataCenterId,\n });\n}\n\n// ---- Template: imageName→image, isServerless→serverless, ContainerConfig flatten,\n// drop readme/dockerEntrypoint/dockerStartCmd (no v2 equivalent; args collapse is\n// lossy — see PLAN B3, deferred to a follow-up that decides join semantics). ----\ninterface V1TemplateCreate {\n name?: string;\n imageName?: string;\n isServerless?: boolean;\n ports?: string[];\n env?: Record<string, string>;\n containerDiskInGb?: number;\n volumeInGb?: number;\n volumeMountPath?: string;\n // v2 CreateTemplateRequest requires `category` (CPU/NVIDIA/AMD). The v1 tool\n // has no such field; default to NVIDIA (the documented default) so the body is\n // valid, and accept an explicit override once the tool schema exposes it.\n category?: 'CPU' | 'NVIDIA' | 'AMD';\n // Container registry credential for pulling a private image. v1's field is\n // `containerRegistryAuthId`; v2 ContainerConfig calls it `registry`.\n containerRegistryAuthId?: string;\n}\nexport function mapTemplateCreateToV2(\n params: V1TemplateCreate\n): Record<string, unknown> {\n return {\n ...containerConfigToV2(params),\n ...compact({\n name: params.name,\n serverless: params.isServerless,\n registry: params.containerRegistryAuthId,\n }),\n // Required by v2 — always emit, defaulting to NVIDIA.\n category: params.category ?? 'NVIDIA',\n };\n}\n\ninterface V1TemplateUpdate {\n name?: string;\n imageName?: string;\n ports?: string[];\n env?: Record<string, string>;\n containerRegistryAuthId?: string;\n // readme has no v2 equivalent — dropped.\n}\n// Update has no required `category`; just flatten ContainerConfig + name. Crucial:\n// this maps imageName→image (an identity mapper would wrongly send `imageName` to\n// v2, which expects `image`).\nexport function mapTemplateUpdateToV2(\n params: V1TemplateUpdate\n): Record<string, unknown> {\n return {\n ...containerConfigToV2(params),\n ...compact({\n name: params.name,\n registry: params.containerRegistryAuthId,\n }),\n };\n}\n","// ============== BACKEND ADAPTER (v1/v2 routing core) ==============\n// Pure routing layer that lets tools stay version-agnostic. A tool asks for a\n// resource backend; the adapter decides v1 vs v2, resolves the base URL + path,\n// normalizes the list response shape (`unwrapList`), and maps request bodies.\n//\n// This module is intentionally pure and side-effect free: env, transport, and\n// the v2 probe are all passed in, so every function is unit-testable offline\n// with no network and no MCP SDK. The HTTP client (src/_shared/http.ts) and the\n// per-resource tool handlers (src/tools/<resource>.ts) consume this.\n\nimport {\n mapPodCreateToV2,\n mapPodUpdateToV2,\n mapTemplateCreateToV2,\n mapTemplateUpdateToV2,\n mapNetworkVolumeCreateToV2,\n mapEndpointCreateToV2,\n mapEndpointUpdateToV2,\n} from './mappers.js';\n\n// One canonical resource union used by unwrapList, resolveVersion, and\n// resolveBackend alike — so the steps don't each invent a spelling. The v2\n// list-envelope key happens to equal the resource name.\nexport type Resource =\n | 'pods'\n | 'templates'\n | 'networkVolumes'\n | 'registries'\n | 'gpus'\n | 'cpus'\n | 'dataCenters'\n | 'endpoints'\n | 'jobs'\n | 'tags'\n | 'workers'\n | 'billing';\n\nexport type RestVersion = 'v1' | 'v2';\n\n// Resources that have no v2 REST home and stay on v1 regardless of any flag.\n// - jobs: serverless runtime, lives on api.runpod.ai/v2 (a different service)\n// (endpoints now route to v2 at /v2/serverless — see V2_REST_PATHS/V2_MAPPERS.)\nconst V1_ONLY: ReadonlySet<Resource> = new Set<Resource>(['jobs']);\n\n// The key each v2 list response is wrapped in: `{ pods: [...] }`, etc.\n// `billing` wraps its rows under `records` (not the resource name); `workers`\n// under `workers`.\nconst V2_UNWRAP_KEY: Record<Resource, string> = {\n pods: 'pods',\n templates: 'templates',\n networkVolumes: 'networkVolumes',\n registries: 'registries',\n gpus: 'gpus',\n cpus: 'cpus',\n dataCenters: 'dataCenters',\n endpoints: 'endpoints',\n jobs: 'jobs',\n tags: 'tags',\n workers: 'workers',\n billing: 'records',\n};\n\n// Env-var suffix for the per-resource version override (RUNPOD_REST_VERSION_<KEY>).\nconst RESOURCE_ENV_KEY: Record<Resource, string> = {\n pods: 'PODS',\n templates: 'TEMPLATES',\n networkVolumes: 'NETWORK_VOLUMES',\n registries: 'REGISTRIES',\n gpus: 'GPUS',\n cpus: 'CPUS',\n dataCenters: 'DATA_CENTERS',\n endpoints: 'ENDPOINTS',\n jobs: 'JOBS',\n tags: 'TAGS',\n workers: 'WORKERS',\n billing: 'BILLING',\n};\n\n// ---- Base URLs (resolved from env, with the same defaults tools.ts uses) ----\n\nexport type Env = Record<string, string | undefined>;\n\nexport function restV1Base(env: Env): string {\n return env.RUNPOD_REST_API_URL ?? 'https://rest.runpod.io/v1';\n}\nexport function restV2Base(env: Env): string {\n return env.RUNPOD_REST_V2_API_URL ?? 'https://v2-rest.runpod.io/v2';\n}\nexport function serverlessBase(env: Env): string {\n return env.RUNPOD_SERVERLESS_API_URL ?? 'https://api.runpod.ai/v2';\n}\nexport function publicGraphqlBase(env: Env): string {\n return env.RUNPOD_PUBLIC_GRAPHQL_URL ?? 'https://api.runpod.io/graphql';\n}\n\n// ---- A0: unwrap a list response into a plain array ----\n// v1 returns a bare array; v2 wraps it in a per-resource envelope. Either way\n// we hand `capListResult` a plain array. Anything unexpected → [] (never throws\n// in an agent's face, and never passes a non-array through to the cap).\nexport function unwrapList(\n resource: Resource,\n version: RestVersion,\n raw: unknown\n): unknown[] {\n if (version === 'v1') return Array.isArray(raw) ? raw : [];\n if (raw === null || typeof raw !== 'object') return [];\n const arr = (raw as Record<string, unknown>)[V2_UNWRAP_KEY[resource]];\n return Array.isArray(arr) ? arr : [];\n}\n\n// ---- A1: resolve which version a given resource call should use ----\n// Pure: env, transport, and the already-resolved v2 probe verdict are injected.\n// Precedence: v1-only resources → v1; else RUNPOD_REST_VERSION_<RESOURCE> →\n// RUNPOD_REST_VERSION → default 'v2'. `auto` probes only on stdio (one process =\n// one key); on hosted HTTP `auto` is treated as v1 (a warm instance serves many\n// keys, so a cached probe verdict would leak across users).\n//\n// `v2Available` is a *resolved boolean*, NOT a thunk or a promise: the caller\n// awaits createV2Prober() once at startup (stdio only) and passes the boolean\n// in. This is deliberate — a `() => Promise<boolean>` would be truthy even when\n// the probe failed, silently forcing v2. Keeping it a plain boolean removes that\n// footgun. When omitted under `auto`, we conservatively resolve to v1.\nexport interface VersionCtx {\n transport: 'stdio' | 'http';\n}\n\nexport function resolveVersion(opts: {\n resource: Resource;\n env: Env;\n ctx: VersionCtx;\n v2Available?: boolean;\n}): RestVersion {\n const { resource, env, ctx, v2Available } = opts;\n if (V1_ONLY.has(resource)) return 'v1';\n\n const perResource = env[`RUNPOD_REST_VERSION_${RESOURCE_ENV_KEY[resource]}`];\n const setting = (\n perResource ??\n env.RUNPOD_REST_VERSION ??\n 'v2'\n ).toLowerCase();\n\n if (setting === 'v2') return 'v2';\n if (setting === 'auto') {\n if (ctx.transport === 'http') return 'v1';\n return v2Available === true ? 'v2' : 'v1';\n }\n return 'v1';\n}\n\n// True if any version setting (global or per-resource) is `auto` — i.e. the\n// stdio entrypoint should run the v2 probe once at startup. Pure.\nexport function wantsAutoProbe(env: Env): boolean {\n return Object.entries(env).some(\n ([k, v]) =>\n k.startsWith('RUNPOD_REST_VERSION') && (v ?? '').toLowerCase() === 'auto'\n );\n}\n\n// ---- A1b: the real `auto` probe (decision logic, injected fetch) ----\n// Returns an async resolver that probes once and memoizes the verdict for the\n// process. The startup wiring (stdio only) awaits this once, then passes the\n// resulting boolean to resolveVersion as `v2Available`. Never wire into the HTTP\n// path (see resolveVersion).\n//\n// Only *definitive* verdicts are cached: a reachable endpoint (any non-5xx\n// response) pins true/false, but a transient failure (thrown error or 5xx) is\n// NOT cached — it returns false for this call and re-probes next time, so a\n// one-time startup network blip doesn't pin the process to v1 forever.\n// Exported so the stdio entrypoint can type its node-fetch adapter against the\n// exact shape the prober needs (just `{ status }`), instead of casting.\n// Named `ProbeFetch` (not `FetchLike`) to avoid colliding with the unrelated,\n// stricter `FetchLike` in _shared/http.ts (which requires method+headers) — the\n// two model different contracts and the distinct name keeps that obvious.\nexport type ProbeFetch = (\n url: string,\n init?: {\n method?: string;\n headers?: Record<string, string>;\n signal?: AbortSignal;\n }\n) => Promise<{ status: number }>;\n\nexport interface ProbeDeps {\n fetch: ProbeFetch;\n baseUrl: string;\n apiKey: string;\n timeoutMs?: number;\n // Optional, injected — called when the probe falls back to v1 for a TRANSIENT\n // reason (timeout / network error / 5xx / 401-403), so an operator who set\n // `auto` and silently got v1 has a signal. Kept as an injected callback\n // (defaulting to no-op) rather than a direct console.* so this module stays\n // pure/side-effect-free and unit tests stay silent. NOT called on a definitive\n // 404 (v2 genuinely not deployed) — that's the expected answer, not a blip.\n warn?: (message: string) => void;\n}\n\n// Bound the startup probe so a slow-but-reachable v2 host can't hang stdio\n// startup forever — an abort throws, which runV2Probe degrades to a transient.\nexport const PROBE_TIMEOUT_MS = 4000;\n\n// Classify a probe response status into a definitive verdict, or `undefined` for\n// a transient failure that must NOT be cached (so the next call re-probes).\n// Transient = 5xx AND 401/403: an auth failure during a rollout, or a key whose\n// v2 scope is later expanded, must not pin the process to v1 for its lifetime\n// with no recovery short of restart. A 2xx pins true; any other definitive 4xx\n// (e.g. 404 = no v2 endpoint) pins false.\nfunction classifyProbeStatus(status: number): boolean | undefined {\n if (status >= 500) return undefined;\n if (status === 401 || status === 403) return undefined;\n return status >= 200 && status < 300;\n}\n\n// One probe attempt. Returns a definitive verdict, or `undefined` when the\n// attempt was transient (network throw / timeout / 5xx) and should be retried.\nasync function runV2Probe(deps: ProbeDeps): Promise<boolean | undefined> {\n try {\n const res = await deps.fetch(`${deps.baseUrl}/catalog/gpus`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${deps.apiKey}` },\n signal: AbortSignal.timeout(deps.timeoutMs ?? PROBE_TIMEOUT_MS),\n });\n const verdict = classifyProbeStatus(res.status);\n if (verdict === undefined) {\n deps.warn?.(\n `v2 auto-probe: ${deps.baseUrl} returned ${res.status} (transient) — using v1 for now; will re-probe. Pin RUNPOD_REST_VERSION=v2 to force v2.`\n );\n }\n return verdict;\n } catch {\n // Thrown = timeout (AbortSignal) or network error — transient, not cached.\n deps.warn?.(\n `v2 auto-probe: ${deps.baseUrl} unreachable (timed out after ${\n deps.timeoutMs ?? PROBE_TIMEOUT_MS\n }ms or network error) — using v1 for now; will re-probe. Pin RUNPOD_REST_VERSION=v2 to force v2.`\n );\n return undefined;\n }\n}\n\n// Memoizing v2 prober: probes once, caches only a *definitive* verdict, and\n// coalesces concurrent first-calls onto a single in-flight probe. A transient\n// attempt resolves to false for this call but leaves the cache unset, so the\n// next call re-probes (a one-time startup blip can't pin the process to v1).\nexport function createV2Prober(deps: ProbeDeps): () => Promise<boolean> {\n let cached: boolean | undefined;\n let inFlight: Promise<boolean> | undefined;\n\n const probeOnce = async (): Promise<boolean> => {\n const verdict = await runV2Probe(deps);\n if (verdict !== undefined) cached = verdict;\n return verdict ?? false;\n };\n\n return () => {\n if (cached !== undefined) return Promise.resolve(cached);\n if (!inFlight) {\n inFlight = probeOnce().finally(() => {\n inFlight = undefined;\n });\n }\n return inFlight;\n };\n}\n\n// ---- A2: resolve the backend descriptor for a resource ----\n// `kind` distinguishes a normal REST resource from the GraphQL-backed catalog\n// (v1) and the serverless-runtime jobs base. mapCreate/mapUpdate default to\n// identity under v1; Phase B fills the v2 half (paths + body mappers + unwrap).\nexport type BackendKind = 'rest' | 'graphql';\n\nexport interface Backend {\n kind: BackendKind;\n version: RestVersion;\n base: string;\n list?: string;\n get?: (id: string) => string;\n unwrap: (raw: unknown) => unknown[];\n mapCreate: (body: unknown) => unknown;\n mapUpdate: (body: unknown) => unknown;\n}\n\nconst identity = (body: unknown): unknown => body;\n\n// v1 REST paths, exactly as tools.ts uses them today.\nconst V1_REST_PATHS: Partial<\n Record<Resource, { list: string; get: (id: string) => string }>\n> = {\n pods: { list: '/pods', get: (id) => `/pods/${id}` },\n templates: { list: '/templates', get: (id) => `/templates/${id}` },\n networkVolumes: {\n list: '/networkvolumes',\n get: (id) => `/networkvolumes/${id}`,\n },\n registries: {\n list: '/containerregistryauth',\n get: (id) => `/containerregistryauth/${id}`,\n },\n endpoints: { list: '/endpoints', get: (id) => `/endpoints/${id}` },\n};\n\nconst CATALOG: ReadonlySet<Resource> = new Set<Resource>([\n 'gpus',\n 'cpus',\n 'dataCenters',\n]);\n\n// Resources that exist ONLY on v2 REST — no v1 REST path and no v1 GraphQL\n// equivalent. Under v1 they resolve to a stub backend (version 'v1', no paths)\n// so the tool emits a clean \"v2 only\" 501 notice instead of throwing on a\n// missing path table entry (mirrors how get-gpu-type/list-cpu-types degrade).\nconst V2_ONLY: ReadonlySet<Resource> = new Set<Resource>([\n 'tags',\n 'workers',\n 'billing',\n]);\n\n// v2 REST paths (the control-plane resources). Paths are relative to the v2\n// base which ALREADY includes `/v2` (restV2Base → https://v2-rest.runpod.io/v2),\n// so they do NOT repeat the `/v2` prefix — `base + list` = `.../v2/pods`. (The\n// prober relies on the same convention: `base + /catalog/gpus`.)\n// Renames vs v1: network-volumes (hyphen), registries (was containerregistryauth),\n// catalog/*. Catalog `datacenters` is intentionally NOT hyphenated (matches the spec).\nconst V2_REST_PATHS: Partial<\n Record<Resource, { list: string; get?: (id: string) => string }>\n> = {\n pods: { list: '/pods', get: (id) => `/pods/${id}` },\n templates: { list: '/templates', get: (id) => `/templates/${id}` },\n networkVolumes: {\n list: '/network-volumes',\n get: (id) => `/network-volumes/${id}`,\n },\n registries: { list: '/registries', get: (id) => `/registries/${id}` },\n gpus: { list: '/catalog/gpus', get: (id) => `/catalog/gpus/${id}` },\n cpus: { list: '/catalog/cpus', get: (id) => `/catalog/cpus/${id}` },\n dataCenters: {\n list: '/catalog/datacenters',\n get: (id) => `/catalog/datacenters/${id}`,\n },\n tags: { list: '/tags', get: (id) => `/tags/${id}` },\n // Endpoints live under /v2/serverless on v2 (NOT /v2/endpoints). The v2 list\n // response wraps rows under `endpoints` (see V2_UNWRAP_KEY), so the existing\n // unwrap key stays correct.\n endpoints: { list: '/serverless', get: (id) => `/serverless/${id}` },\n // workers/billing build their own sub-paths in the tool from `base`; the\n // list entry just satisfies buildV2Backend (which requires a paths entry).\n workers: { list: '/serverless' },\n billing: { list: '/billing' },\n};\n\n// v2 request-body mappers per resource (identity where v1==v2, e.g. registries).\nconst V2_MAPPERS: Partial<\n Record<\n Resource,\n {\n create: (body: unknown) => unknown;\n update: (body: unknown) => unknown;\n }\n >\n> = {\n pods: {\n create: (b) =>\n mapPodCreateToV2(b as Parameters<typeof mapPodCreateToV2>[0]),\n update: (b) =>\n mapPodUpdateToV2(b as Parameters<typeof mapPodUpdateToV2>[0]),\n },\n templates: {\n create: (b) =>\n mapTemplateCreateToV2(b as Parameters<typeof mapTemplateCreateToV2>[0]),\n update: (b) =>\n mapTemplateUpdateToV2(b as Parameters<typeof mapTemplateUpdateToV2>[0]),\n },\n networkVolumes: {\n create: (b) =>\n mapNetworkVolumeCreateToV2(\n b as Parameters<typeof mapNetworkVolumeCreateToV2>[0]\n ),\n update: identity,\n },\n registries: { create: identity, update: identity },\n endpoints: {\n create: (b) =>\n mapEndpointCreateToV2(b as Parameters<typeof mapEndpointCreateToV2>[0]),\n update: (b) =>\n mapEndpointUpdateToV2(b as Parameters<typeof mapEndpointUpdateToV2>[0]),\n },\n};\n\n// The unwrap closure is built once in resolveBackend and threaded into whichever\n// builder runs, so the per-version builders stay free of unwrap plumbing.\ntype Unwrap = Backend['unwrap'];\n\n// jobs never reaches here (pinned v1 by resolveVersion). Endpoints DO reach here\n// on v2 → /v2/serverless via V2_REST_PATHS + V2_MAPPERS. Catalog v2 is REST (not\n// GraphQL); CRUD resources use their v2 paths + body mappers.\nfunction buildV2Backend(resource: Resource, env: Env, unwrap: Unwrap): Backend {\n const paths = V2_REST_PATHS[resource];\n if (!paths) throw new Error(`no v2 REST paths for resource \"${resource}\"`);\n const mappers = V2_MAPPERS[resource];\n return {\n kind: 'rest',\n version: 'v2',\n base: restV2Base(env),\n list: paths.list,\n get: paths.get,\n unwrap,\n mapCreate: mappers?.create ?? identity,\n mapUpdate: mappers?.update ?? identity,\n };\n}\n\nfunction buildV1Backend(resource: Resource, env: Env, unwrap: Unwrap): Backend {\n // jobs: serverless runtime base; no list/get/path table entry.\n if (resource === 'jobs') {\n return {\n kind: 'rest',\n version: 'v1',\n base: serverlessBase(env),\n unwrap,\n mapCreate: identity,\n mapUpdate: identity,\n };\n }\n // v2-only resources have no v1 home: return a stub so the tool sees\n // version 'v1' and returns a clean 501 (it never reads list/get on this).\n if (V2_ONLY.has(resource)) {\n return {\n kind: 'rest',\n version: 'v1',\n base: restV1Base(env),\n unwrap,\n mapCreate: identity,\n mapUpdate: identity,\n };\n }\n // v1 catalog is GraphQL via a separate helper (kept out of the unified REST\n // client through Phase A); folds into REST at Step B5.\n if (CATALOG.has(resource)) {\n return {\n kind: 'graphql',\n version: 'v1',\n base: publicGraphqlBase(env),\n unwrap,\n mapCreate: identity,\n mapUpdate: identity,\n };\n }\n const paths = V1_REST_PATHS[resource];\n if (!paths) throw new Error(`no v1 REST paths for resource \"${resource}\"`);\n return {\n kind: 'rest',\n version: 'v1',\n base: restV1Base(env),\n list: paths.list,\n get: paths.get,\n unwrap,\n mapCreate: identity,\n mapUpdate: identity,\n };\n}\n\nexport function resolveBackend(opts: {\n resource: Resource;\n env: Env;\n ctx: VersionCtx;\n v2Available?: boolean;\n}): Backend {\n const { resource, env } = opts;\n const version = resolveVersion(opts);\n const unwrap: Unwrap = (raw) => unwrapList(resource, version, raw);\n return version === 'v2'\n ? buildV2Backend(resource, env, unwrap)\n : buildV1Backend(resource, env, unwrap);\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { capListResult, listPaginationParams } from '../pagination.js';\nimport { READ_ONLY, type ToolRuntime } from './runtime.js';\n\n// ============== INFRASTRUCTURE / CATALOG TOOLS ==============\n// GPU types, data centers, CPU types. v1 reads these over GraphQL; v2 has a REST\n// catalog (GET /v2/catalog/*). The adapter picks the backend; v2-only entries\n// return a 501 notice on v1.\n\nexport function registerCatalogTools(server: McpServer, rt: ToolRuntime): void {\n const { jsonReply, graphql, callRestUrl, backendFor } = rt;\n\n // List GPU Types\n server.tool(\n 'list-gpu-types',\n 'List available GPU types with stock/pricing and capability filters (minimum VRAM, secure/community cloud, name search). Use this to discover valid gpuTypeIds before creating a pod or endpoint. On v2 the realtime stock filter is a no-op.',\n {\n ...listPaginationParams,\n minMemoryGb: z\n .number()\n .optional()\n .describe('Filter to GPUs with at least this much VRAM in GB'),\n secureCloudOnly: z\n .boolean()\n .optional()\n .describe('Filter to only GPUs available in secure cloud'),\n communityCloudOnly: z\n .boolean()\n .optional()\n .describe('Filter to only GPUs available in community cloud'),\n searchTerm: z\n .string()\n .optional()\n .describe(\n \"Search term to filter GPUs by name (e.g., 'A100', 'RTX 4090')\"\n ),\n includeUnavailable: z\n .boolean()\n .optional()\n .describe(\n 'Include GPUs that are currently out of stock. Default is false'\n ),\n },\n { title: 'List GPU types', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('gpus');\n if (backend.version === 'v2') {\n // v2 REST: GET /v2/catalog/gpus → { gpus: [...] }. Filters re-applied\n // against v2 field names (memory/secure/community/name). v2 has no\n // realtime stockStatus, so `includeUnavailable` is a documented no-op.\n const raw = await callRestUrl(`${backend.base}${backend.list}`);\n let gpus = backend.unwrap(raw) as Array<Record<string, unknown>>;\n if (params.minMemoryGb !== undefined)\n gpus = gpus.filter(\n (g) => Number(g.memory ?? 0) >= params.minMemoryGb!\n );\n if (params.secureCloudOnly) gpus = gpus.filter((g) => g.secure);\n if (params.communityCloudOnly) gpus = gpus.filter((g) => g.community);\n if (params.searchTerm) {\n const t = params.searchTerm.toLowerCase();\n gpus = gpus.filter(\n (g) =>\n String(g.id ?? '')\n .toLowerCase()\n .includes(t) ||\n String(g.name ?? '')\n .toLowerCase()\n .includes(t)\n );\n }\n return capListResult(gpus, {\n limit: params.limit,\n cursor: params.cursor,\n });\n }\n\n interface GpuTypesResponse {\n gpuTypes: Array<{\n id: string;\n displayName: string;\n memoryInGb: number;\n secureCloud: boolean;\n communityCloud: boolean;\n lowestPrice?: { stockStatus: string | null } | null;\n }>;\n }\n\n const data = await graphql<GpuTypesResponse>(`\n query {\n gpuTypes {\n id\n displayName\n memoryInGb\n secureCloud\n communityCloud\n lowestPrice(input: { gpuCount: 1 }) {\n stockStatus\n }\n }\n }\n `);\n\n const stockPriority: Record<string, number> = {\n High: 3,\n Medium: 2,\n Low: 1,\n };\n\n const isAvailable = (gpu: GpuTypesResponse['gpuTypes'][number]) => {\n const status = gpu.lowestPrice?.stockStatus;\n return !!status && status !== 'Out';\n };\n\n let gpuTypes = data.gpuTypes.filter((gpu) => gpu.id !== 'unknown');\n\n if (!params.includeUnavailable) {\n gpuTypes = gpuTypes.filter(isAvailable);\n }\n if (params.minMemoryGb) {\n gpuTypes = gpuTypes.filter(\n (gpu) => gpu.memoryInGb >= params.minMemoryGb!\n );\n }\n if (params.secureCloudOnly) {\n gpuTypes = gpuTypes.filter((gpu) => gpu.secureCloud);\n }\n if (params.communityCloudOnly) {\n gpuTypes = gpuTypes.filter((gpu) => gpu.communityCloud);\n }\n if (params.searchTerm) {\n const term = params.searchTerm.toLowerCase();\n gpuTypes = gpuTypes.filter(\n (gpu) =>\n gpu.id.toLowerCase().includes(term) ||\n gpu.displayName.toLowerCase().includes(term)\n );\n }\n\n gpuTypes.sort((a, b) => {\n const aP = stockPriority[a.lowestPrice?.stockStatus || ''] || 0;\n const bP = stockPriority[b.lowestPrice?.stockStatus || ''] || 0;\n if (bP !== aP) return bP - aP;\n return b.memoryInGb - a.memoryInGb;\n });\n\n const result = gpuTypes.map((gpu) => ({\n id: gpu.id,\n displayName: gpu.displayName,\n memoryGb: gpu.memoryInGb,\n secureCloud: gpu.secureCloud,\n communityCloud: gpu.communityCloud,\n stockStatus: gpu.lowestPrice?.stockStatus || 'unavailable',\n available: isAvailable(gpu),\n }));\n\n return capListResult(result, {\n limit: params.limit,\n cursor: params.cursor,\n });\n }\n );\n\n // List Data Centers\n server.tool(\n 'list-data-centers',\n 'List Runpod data centers (id, name, region/location). Use this to discover valid dataCenterIds for placing pods, endpoints, or network volumes.',\n {\n ...listPaginationParams,\n region: z\n .string()\n .optional()\n .describe(\n \"Filter by region/location (e.g., 'United States', 'Europe', 'Canada')\"\n ),\n },\n { title: 'List data centers', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('dataCenters');\n if (backend.version === 'v2') {\n // v2 REST: GET /v2/catalog/datacenters → { dataCenters: [...] }. v2 uses\n // a `region` enum (vs v1 free-text `location`); region filter matches it.\n const raw = await callRestUrl(`${backend.base}${backend.list}`);\n let dcs = backend.unwrap(raw) as Array<Record<string, unknown>>;\n if (params.region) {\n const t = params.region.toLowerCase();\n dcs = dcs.filter((dc) =>\n String(dc.region ?? '')\n .toLowerCase()\n .includes(t)\n );\n }\n return capListResult(dcs, {\n limit: params.limit,\n cursor: params.cursor,\n });\n }\n\n interface DataCentersResponse {\n dataCenters: Array<{\n id: string;\n name: string;\n location: string;\n }>;\n }\n\n const data = await graphql<DataCentersResponse>(`\n query {\n dataCenters {\n id\n name\n location\n }\n }\n `);\n\n let dataCenters = data.dataCenters;\n\n if (params.region) {\n const term = params.region.toLowerCase();\n dataCenters = dataCenters.filter((dc) =>\n dc.location.toLowerCase().includes(term)\n );\n }\n\n return capListResult(dataCenters, {\n limit: params.limit,\n cursor: params.cursor,\n });\n }\n );\n\n // List CPU Types (v2-only — v2 catalog REST has no v1/GraphQL equivalent)\n server.tool(\n 'list-cpu-types',\n 'List available CPU flavor types for CPU pods/endpoints. v2-only — returns a 501 notice on the v1 API.',\n { ...listPaginationParams },\n { title: 'List CPU types', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('cpus');\n if (backend.version === 'v1') {\n return jsonReply({\n error:\n 'list-cpu-types is only available on the v2 REST API. Set RUNPOD_REST_VERSION=v2.',\n status: 501,\n });\n }\n const raw = await callRestUrl(`${backend.base}${backend.list}`);\n return capListResult(backend.unwrap(raw), {\n limit: params.limit,\n cursor: params.cursor,\n });\n }\n );\n\n // Get GPU Type by id (v2-only — GET /v2/catalog/gpus/{id})\n server.tool(\n 'get-gpu-type',\n 'Get details for a single GPU type by id. v2-only — returns a 501 notice on the v1 API (use list-gpu-types there).',\n {\n gpuTypeId: z.string().describe('ID of the GPU type to retrieve'),\n },\n { title: 'Get GPU type', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('gpus');\n if (backend.version === 'v1') {\n return jsonReply({\n error:\n 'get-gpu-type is only available on the v2 REST API. Set RUNPOD_REST_VERSION=v2 (or use list-gpu-types on v1).',\n status: 501,\n });\n }\n const result = await callRestUrl(\n `${backend.base}${backend.get!(params.gpuTypeId)}`\n );\n return jsonReply(result);\n }\n );\n\n // Get CPU Type by id (v2-only — GET /v2/catalog/cpus/{id})\n server.tool(\n 'get-cpu-type',\n 'Get details for a single CPU flavor type by id. v2-only — returns a 501 notice on the v1 API (use list-cpu-types there).',\n {\n cpuTypeId: z.string().describe('ID of the CPU type to retrieve'),\n },\n { title: 'Get CPU type', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('cpus');\n if (backend.version === 'v1') {\n return jsonReply({\n error:\n 'get-cpu-type is only available on the v2 REST API. Set RUNPOD_REST_VERSION=v2.',\n status: 501,\n });\n }\n const result = await callRestUrl(\n `${backend.base}${backend.get!(params.cpuTypeId)}`\n );\n return jsonReply(result);\n }\n );\n\n // Get Data Center by id (v2-only — GET /v2/catalog/datacenters/{id})\n server.tool(\n 'get-data-center',\n 'Get details for a single data center by id. v2-only — returns a 501 notice on the v1 API (use list-data-centers there).',\n {\n dataCenterId: z.string().describe('ID of the data center to retrieve'),\n },\n { title: 'Get data center', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('dataCenters');\n if (backend.version === 'v1') {\n return jsonReply({\n error:\n 'get-data-center is only available on the v2 REST API. Set RUNPOD_REST_VERSION=v2.',\n status: 501,\n });\n }\n const result = await callRestUrl(\n `${backend.base}${backend.get!(params.dataCenterId)}`\n );\n return jsonReply(result);\n }\n );\n}\n","// ============== LIST RESULT PAGINATION / TOKEN CAPS ==============\n// The REST list endpoints do not yet support server-side pagination, so a\n// large account can return a payload many times the size of an LLM's context\n// window (e.g. list-templates has been observed at ~15x Claude Code's window),\n// which ends the agent session. Until the REST API ships cursor pagination,\n// the MCP caps list responses client-side: it returns at most `limit` items\n// and reports how many were omitted so the agent knows to narrow its query.\n//\n// The `limit` and `cursor` parameters are shaped to match the cursor-based\n// pagination the REST API will add, so the tool signatures do not change when\n// server-side pagination lands; the MCP will simply forward them and surface a\n// real `nextCursor` instead of the client-side offset used here.\n\nimport { z } from 'zod';\n\n// Default and maximum number of items returned by a list tool in a single\n// call. The cap is what protects the agent's context; `limit` lets a caller\n// ask for fewer (or more, up to the ceiling) when they know what they want.\nexport const DEFAULT_LIST_LIMIT = 20;\nexport const MAX_LIST_LIMIT = 100;\n\n// Shared Zod parameters added to every list-* tool. Optional so existing\n// callers that pass nothing keep working (they get the default page).\nexport const listPaginationParams = {\n limit: z\n .number()\n .int()\n .positive()\n .max(MAX_LIST_LIMIT)\n .optional()\n .describe(\n `Maximum number of items to return (default ${DEFAULT_LIST_LIMIT}, max ${MAX_LIST_LIMIT}). Use the returned nextCursor to fetch the next page.`\n ),\n cursor: z\n .string()\n .optional()\n .describe(\n 'Opaque pagination cursor from a previous response (nextCursor). Omit to start from the beginning.'\n ),\n};\n\n// Decodes the opaque cursor into a numeric offset. The cursor is currently a\n// base64-encoded offset; when the REST API ships real cursors this becomes a\n// passthrough. Invalid cursors are treated as the start so a bad value never\n// throws in an agent's face.\nexport function decodeCursorOffset(cursor: string | undefined): number {\n if (!cursor) return 0;\n try {\n const decoded = Buffer.from(cursor, 'base64').toString('utf8');\n const offset = Number.parseInt(decoded, 10);\n return Number.isFinite(offset) && offset >= 0 ? offset : 0;\n } catch {\n return 0;\n }\n}\n\nexport function encodeCursorOffset(offset: number): string {\n return Buffer.from(String(offset), 'utf8').toString('base64');\n}\n\n// Caps a list response to `limit` items starting at `cursor`, returning an\n// envelope that tells the agent how much it is seeing and how to get the rest.\n// Only arrays are paginated; any other shape is passed through untouched.\n//\n// `extra` merges sibling fields alongside `items`/`pagination` (e.g. billing\n// `metadata`, an endpoint-workers `summary`) so a capped list response can still\n// carry the small aggregate fields that accompany the array. Reserved keys\n// (`items`, `pagination`) in `extra` are ignored so they can't shadow the cap.\nexport function capListResult(\n result: unknown,\n options: { limit?: number; cursor?: string },\n extra?: Record<string, unknown>\n): { content: Array<{ type: 'text'; text: string }> } {\n const limit = Math.min(options.limit ?? DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT);\n const offset = decodeCursorOffset(options.cursor);\n\n if (!Array.isArray(result)) {\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) },\n ],\n };\n }\n\n const total = result.length;\n const page = result.slice(offset, offset + limit);\n const nextOffset = offset + page.length;\n const hasMore = nextOffset < total;\n\n const { items: _i, pagination: _p, ...safeExtra } = extra ?? {};\n void _i;\n void _p;\n\n const envelope = {\n ...safeExtra,\n items: page,\n pagination: {\n total,\n returned: page.length,\n offset,\n truncated: hasMore,\n nextCursor: hasMore ? encodeCursorOffset(nextOffset) : null,\n note: hasMore\n ? `Showing ${page.length} of ${total}. Pass cursor=nextCursor to fetch more, or narrow the query with filters.`\n : undefined,\n },\n };\n\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(envelope, null, 2) },\n ],\n };\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { capListResult, listPaginationParams } from '../pagination.js';\nimport { HttpError } from '../_shared/http.js';\nimport { restV1Base } from '../_shared/backend.js';\nimport { READ_ONLY, WRITE, DESTRUCTIVE, type ToolRuntime } from './runtime.js';\n\n// ============== POD MANAGEMENT TOOLS ==============\n\n// One parsed Server-Sent-Events log frame from GET /v2/pods/{id}/logs. The API\n// emits `data: {\"source\",\"line\",\"ts\"}` events; a frame that doesn't parse as\n// JSON is preserved verbatim under `raw` rather than dropped.\nexport interface PodLogEntry {\n source?: string;\n line?: string;\n ts?: string;\n raw?: string;\n}\n\n// Parse the raw accumulated event-stream text into log entries. Pure (no I/O),\n// so the risky parsing logic is unit-tested without a network. SSE events are\n// separated by a blank line; the payload is the `data:` field (possibly spanning\n// multiple `data:` lines per the SSE spec). Non-JSON payloads fall back to\n// `{raw}`. Comment lines (`:`...) and other fields (event:/id:) are ignored.\nexport function parsePodLogSse(raw: string): PodLogEntry[] {\n const items: PodLogEntry[] = [];\n for (const block of raw.split(/\\r?\\n\\r?\\n/)) {\n const dataLines = block\n .split(/\\r?\\n/)\n .filter((l) => l.startsWith('data:'));\n if (!dataLines.length) continue;\n // Strip the `data:` field name and one optional leading space per line.\n const payload = dataLines\n .map((l) => l.slice(5).replace(/^ /, ''))\n .join('\\n');\n if (!payload.trim()) continue;\n try {\n items.push(JSON.parse(payload) as PodLogEntry);\n } catch {\n items.push({ raw: payload });\n }\n }\n return items;\n}\n\nexport function registerPodTools(server: McpServer, rt: ToolRuntime): void {\n // NOTE: `streamSse` (used by the stream-pod-logs tool) is intentionally not\n // destructured while that tool is disabled — see the commented block below.\n const { jsonReply, callRestUrl, backendFor, podAction, env } = rt;\n\n // List Pods\n server.tool(\n 'list-pods',\n 'List your pods (running and stopped) with optional filters — compute type, GPU type, data center, name — plus machine/network-volume expansion. Paginated via limit/cursor.',\n {\n ...listPaginationParams,\n computeType: z\n .enum(['GPU', 'CPU'])\n .optional()\n .describe('Filter to only GPU or only CPU Pods'),\n gpuTypeId: z\n .array(z.string())\n .optional()\n .describe('Filter to Pods with any of the listed GPU types'),\n dataCenterId: z\n .array(z.string())\n .optional()\n .describe('Filter to Pods in any of the provided data centers'),\n name: z\n .string()\n .optional()\n .describe('Filter to Pods with the provided name'),\n includeMachine: z\n .boolean()\n .optional()\n .describe('Include information about the machine'),\n includeNetworkVolume: z\n .boolean()\n .optional()\n .describe('Include information about attached network volumes'),\n },\n { title: 'List pods', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('pods');\n\n // v1 supports query-param filters; v2 has none (the spec declares no query\n // params and ignores them), so we only build the query string under v1.\n let queryString = '';\n if (backend.version === 'v1') {\n const queryParams = new URLSearchParams();\n if (params.computeType)\n queryParams.append('computeType', params.computeType);\n if (params.gpuTypeId)\n params.gpuTypeId.forEach((type) =>\n queryParams.append('gpuTypeId', type)\n );\n if (params.dataCenterId)\n params.dataCenterId.forEach((dc) =>\n queryParams.append('dataCenterId', dc)\n );\n if (params.name) queryParams.append('name', params.name);\n if (params.includeMachine)\n queryParams.append(\n 'includeMachine',\n params.includeMachine.toString()\n );\n if (params.includeNetworkVolume)\n queryParams.append(\n 'includeNetworkVolume',\n params.includeNetworkVolume.toString()\n );\n queryString = queryParams.toString()\n ? `?${queryParams.toString()}`\n : '';\n }\n\n const result = await callRestUrl(\n `${backend.base}${backend.list}${queryString}`\n );\n\n return capListResult(backend.unwrap(result), {\n limit: params.limit,\n cursor: params.cursor,\n });\n }\n );\n\n // Get Pod Details\n server.tool(\n 'get-pod',\n 'Get full details for one pod by id, optionally expanding machine and attached network-volume info.',\n {\n podId: z.string().describe('ID of the pod to retrieve'),\n includeMachine: z\n .boolean()\n .optional()\n .describe('Include information about the machine'),\n includeNetworkVolume: z\n .boolean()\n .optional()\n .describe('Include information about attached network volumes'),\n },\n { title: 'Get pod', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('pods');\n\n // v1-only expansion query params (v2 has no query params on get).\n let queryString = '';\n if (backend.version === 'v1') {\n const queryParams = new URLSearchParams();\n if (params.includeMachine)\n queryParams.append(\n 'includeMachine',\n params.includeMachine.toString()\n );\n if (params.includeNetworkVolume)\n queryParams.append(\n 'includeNetworkVolume',\n params.includeNetworkVolume.toString()\n );\n queryString = queryParams.toString()\n ? `?${queryParams.toString()}`\n : '';\n }\n\n const result = await callRestUrl(\n `${backend.base}${backend.get!(params.podId)}${queryString}`\n );\n return jsonReply(result);\n }\n );\n\n // Create Pod\n server.tool(\n 'create-pod',\n 'Create a new GPU/CPU pod on Runpod. Pass gpuTypeIds for a GPU pod, or computeType:\"CPU\" for a CPU pod (CPU pods are served by the v1 API for now). If the user does not specify an image, recommend the \"Runpod Pytorch 2.8.0\" image (runpod/pytorch:1.0.2-cu1281-torch280-ubuntu2404) as the default — it has the most up-to-date CUDA and PyTorch versions.',\n {\n name: z.string().optional().describe('Name for the pod'),\n imageName: z.string().describe('Docker image to use'),\n computeType: z\n .enum(['GPU', 'CPU'])\n .optional()\n .describe(\n \"Pod type. On v2, required when not passing gpuTypeIds. 'CPU' is served by the v1 API for now (v2 has no CPU pods yet).\"\n ),\n cloudType: z\n .enum(['SECURE', 'COMMUNITY'])\n .optional()\n .describe('SECURE or COMMUNITY cloud'),\n gpuTypeIds: z\n .array(z.string())\n .optional()\n .describe('List of acceptable GPU types'),\n gpuCount: z.number().optional().describe('Number of GPUs'),\n containerDiskInGb: z\n .number()\n .optional()\n .describe('Container disk size in GB'),\n volumeInGb: z.number().optional().describe('Volume size in GB'),\n volumeMountPath: z\n .string()\n .optional()\n .describe('Path to mount the volume'),\n ports: z\n .array(z.string())\n .optional()\n .describe(\"Ports to expose (e.g., '8888/http', '22/tcp')\"),\n env: z.record(z.string()).optional().describe('Environment variables'),\n dataCenterIds: z\n .array(z.string())\n .optional()\n .describe('List of data centers'),\n },\n { title: 'Create pod', ...WRITE },\n async (params) => {\n const backend = backendFor('pods');\n\n // Pod type is decided by STATED intent, never inferred from the absence of\n // GPU fields (absence used to silently downgrade a misspecified GPU request\n // into a CPU pod). On v2 we require an explicit, non-contradictory choice;\n // v1 keeps its passthrough behavior (it validates the body itself).\n const hasGpuTypes = (params.gpuTypeIds?.length ?? 0) > 0;\n const hasGpuCount = (params.gpuCount ?? 0) > 0;\n const wantsCpu = params.computeType === 'CPU';\n const wantsGpu =\n hasGpuTypes || hasGpuCount || params.computeType === 'GPU';\n\n if (backend.version === 'v2') {\n if (wantsGpu && wantsCpu) {\n return jsonReply({\n error:\n \"Specify a GPU pod (gpuTypeIds or computeType:'GPU') OR a CPU pod (computeType:'CPU'), not both.\",\n status: 400,\n });\n }\n if (!wantsGpu && !wantsCpu) {\n return jsonReply({\n error:\n \"No pod type specified. Pass gpuTypeIds (see list-gpu-types) for a GPU pod, or computeType:'CPU' for a CPU pod.\",\n status: 400,\n });\n }\n if (wantsGpu && !hasGpuTypes) {\n return jsonReply({\n error:\n 'A GPU pod needs gpuTypeIds (see list-gpu-types); gpuCount or computeType alone is under-specified.',\n status: 400,\n });\n }\n // v2 has no CPU pods yet (AE-2991): serve a CPU pod from v1 (which supports\n // them) instead of failing, and tell the caller which API answered.\n if (wantsCpu) {\n // v1 is a passthrough (mapCreate is identity there), so the raw tool\n // params are the correct v1 body — `computeType` is a valid v1 field.\n // Wrap the call so a v1 error surfaces as a clean message, not a raw\n // throw (matching the GPU/v2 path's error handling below).\n try {\n const result = (await callRestUrl(\n `${restV1Base(env)}/pods`,\n 'POST',\n params as Record<string, unknown>\n )) as Record<string, unknown>;\n return jsonReply({\n ...result,\n _servedBy: 'v1',\n _note:\n 'CPU pod: created on the v1 API, because the v2 REST API does not support CPU pods yet.',\n });\n } catch (error) {\n if (error instanceof HttpError) {\n return jsonReply({\n error: error.message,\n status: error.status,\n _servedBy: 'v1',\n _note: 'CPU pod create was routed to the v1 API and failed.',\n });\n }\n throw error;\n }\n }\n }\n\n const body = backend.mapCreate(params) as Record<string, unknown>;\n try {\n const result = (await callRestUrl(\n `${backend.base}${backend.list}`,\n 'POST',\n body\n )) as Record<string, unknown>;\n // v2 accepts a single GPU type (`gpu.id`); v1's gpuTypeIds is \"any of\n // these\". If the caller offered several, only the first was sent — say\n // so in the reply so the narrowing isn't silent.\n if (\n backend.version === 'v2' &&\n (params.gpuTypeIds?.length ?? 0) > 1\n ) {\n return jsonReply({\n ...result,\n _warning: `v2 accepts one GPU type per pod; used \"${params.gpuTypeIds![0]}\" and ignored ${\n params.gpuTypeIds!.length - 1\n } other(s): ${params.gpuTypeIds!.slice(1).join(', ')}.`,\n });\n }\n return jsonReply(result);\n } catch (error) {\n // Defense for if/when the v2 CPU stub starts returning 501 directly\n // (AE-2991): surface a clean message instead of a raw stack.\n if (error instanceof HttpError && error.status === 501) {\n return jsonReply({\n error:\n 'CPU pods are not yet supported on the v2 REST API. Use a GPU pod, or create the CPU pod via the v1 API (set RUNPOD_REST_VERSION=v1).',\n status: 501,\n });\n }\n throw error;\n }\n }\n );\n\n // Update Pod\n server.tool(\n 'update-pod',\n \"Update a pod's mutable fields (name, image, container disk, volume, ports, env). Only the fields you provide change.\",\n {\n podId: z.string().describe('ID of the pod to update'),\n name: z.string().optional().describe('New name for the pod'),\n imageName: z.string().optional().describe('New Docker image'),\n containerDiskInGb: z\n .number()\n .optional()\n .describe('New container disk size in GB'),\n volumeInGb: z.number().optional().describe('New volume size in GB'),\n volumeMountPath: z\n .string()\n .optional()\n .describe('New path to mount the volume'),\n ports: z.array(z.string()).optional().describe('New ports to expose'),\n env: z\n .record(z.string())\n .optional()\n .describe('New environment variables'),\n },\n { title: 'Update pod', ...WRITE, idempotentHint: true },\n async (params) => {\n const { podId, ...updateParams } = params;\n const backend = backendFor('pods');\n const body = backend.mapUpdate(updateParams) as Record<string, unknown>;\n const result = await callRestUrl(\n `${backend.base}${backend.get!(podId)}`,\n 'PATCH',\n body\n );\n return jsonReply(result);\n }\n );\n\n // Start Pod — v1: POST /pods/{id}/start ; v2: POST /pods/{id}/action {action:'start'}\n server.tool(\n 'start-pod',\n 'Start a stopped pod (resumes the same pod and its billing).',\n {\n podId: z.string().describe('ID of the pod to start'),\n },\n { title: 'Start pod', ...WRITE, idempotentHint: true },\n async (params) => {\n const result = await podAction(params.podId, 'start');\n return jsonReply(result);\n }\n );\n\n // Stop Pod — v1: POST /pods/{id}/stop ; v2: POST /pods/{id}/action {action:'stop'}\n server.tool(\n 'stop-pod',\n 'Stop a running pod. The pod and its disk persist; GPU billing stops.',\n {\n podId: z.string().describe('ID of the pod to stop'),\n },\n { title: 'Stop pod', ...WRITE, idempotentHint: true },\n async (params) => {\n const result = await podAction(params.podId, 'stop');\n return jsonReply(result);\n }\n );\n\n // Restart Pod — v2-only state transition (PodAction enum). The v1 REST API has\n // no restart endpoint, so under v1 we return a clean message instead of firing\n // a request that would 404 (mirrors the create-pod 501 handling).\n server.tool(\n 'restart-pod',\n 'Restart a running pod (single PodAction call). v2-only — on the v1 API returns a 501 notice (stop then start the pod instead).',\n {\n podId: z.string().describe('ID of the pod to restart'),\n },\n { title: 'Restart pod', ...WRITE },\n async (params) => {\n const backend = backendFor('pods');\n if (backend.version === 'v1') {\n return jsonReply({\n error:\n 'restart-pod is only available on the v2 REST API. Set RUNPOD_REST_VERSION=v2 (or stop then start the pod on v1).',\n status: 501,\n });\n }\n const result = await podAction(params.podId, 'restart');\n return jsonReply(result);\n }\n );\n\n // ⛔ DISABLED until prod ships the endpoint — DO NOT register yet.\n // `stream-pod-logs` calls GET /v2/pods/{id}/logs (SSE). That op exists ONLY on\n // the dev v2 deployment (v2-rest.runpod.dev, 47-op spec); prod\n // (v2-rest.runpod.io, 45-op spec) returns 422 \"path not found\". Keeping the\n // tool registered would expose a call that 422s in prod. The implementation\n // (parsePodLogSse + the runtime streamSse reader) stays in the tree and is\n // unit-tested; re-enable this block (and re-add `streamSse` to the destructure\n // above) once getPodLogs is live on prod.\n /*\n server.tool(\n 'stream-pod-logs',\n 'Fetch a bounded snapshot of a pod\\'s live logs (container and/or system) via Server-Sent Events. v2-only — returns a 501 notice on the v1 API. Reads for up to maxWaitMs (default 5s) and returns the collected log lines; use `tail` to backfill recent lines first. Large output is truncated (see the `truncated` flag).',\n {\n podId: z.string().describe('ID of the pod whose logs to stream'),\n source: z\n .enum(['container', 'system', 'both'])\n .optional()\n .describe('Which log source to read (default: both)'),\n tail: z\n .number()\n .int()\n .min(0)\n .optional()\n .describe('Number of recent lines to backfill before live output'),\n maxWaitMs: z\n .number()\n .int()\n .min(500)\n .max(30000)\n .optional()\n .describe('How long to read the stream, in ms (default 5000, max 30000)'),\n },\n { title: 'Stream pod logs', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('pods');\n if (backend.version === 'v1') {\n return jsonReply({\n error:\n 'stream-pod-logs is only available on the v2 REST API. Set RUNPOD_REST_VERSION=v2.',\n status: 501,\n });\n }\n const qs = new URLSearchParams();\n if (params.source) qs.append('source', params.source);\n if (params.tail !== undefined) qs.append('tail', String(params.tail));\n const queryString = qs.toString() ? `?${qs.toString()}` : '';\n try {\n const { raw, truncated } = await streamSse(\n `${backend.base}${backend.get!(params.podId)}/logs${queryString}`,\n { maxWaitMs: params.maxWaitMs ?? 5000, maxBytes: 256 * 1024 }\n );\n const items = parsePodLogSse(raw);\n return jsonReply({ items, count: items.length, truncated });\n } catch (error) {\n if (error instanceof HttpError) {\n return jsonReply({ error: error.message, status: error.status });\n }\n throw error;\n }\n }\n );\n */\n\n // Delete Pod\n server.tool(\n 'delete-pod',\n 'Permanently delete a pod and its data. This cannot be undone.',\n {\n podId: z.string().describe('ID of the pod to delete'),\n },\n { title: 'Delete pod', ...DESTRUCTIVE },\n async (params) => {\n const backend = backendFor('pods');\n const result = await callRestUrl(\n `${backend.base}${backend.get!(params.podId)}`,\n 'DELETE'\n );\n return jsonReply(result);\n }\n );\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { capListResult, listPaginationParams } from '../pagination.js';\nimport { READ_ONLY, WRITE, DESTRUCTIVE, type ToolRuntime } from './runtime.js';\n\n// ============== ENDPOINT MANAGEMENT TOOLS ==============\n// Serverless endpoint CRUD, version-aware via the backend adapter.\n// v2 (default): endpoints live at /v2/serverless with an INLINE config —\n// image + gpu.pools + workers + scaling (NO templateId).\n// v1: the legacy /endpoints model — templateId-based.\n// list/get/delete route through the adapter on both versions; create/update\n// branch on backend.version because the request model differs fundamentally.\n\nexport function registerEndpointTools(\n server: McpServer,\n rt: ToolRuntime\n): void {\n const { jsonReply, callRestUrl, backendFor } = rt;\n\n // List Endpoints — v1 supports includeTemplate/includeWorkers query params;\n // v2 (GET /v2/serverless) declares none, so we only build the query under v1.\n server.tool(\n 'list-endpoints',\n 'List your Serverless endpoints, optionally expanding template and worker details (v1 only). Paginated via limit/cursor.',\n {\n ...listPaginationParams,\n includeTemplate: z\n .boolean()\n .optional()\n .describe('Include template information (v1 only)'),\n includeWorkers: z\n .boolean()\n .optional()\n .describe('Include information about workers (v1 only)'),\n },\n { title: 'List endpoints', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('endpoints');\n\n let queryString = '';\n if (backend.version === 'v1') {\n const queryParams = new URLSearchParams();\n if (params.includeTemplate)\n queryParams.append(\n 'includeTemplate',\n params.includeTemplate.toString()\n );\n if (params.includeWorkers)\n queryParams.append(\n 'includeWorkers',\n params.includeWorkers.toString()\n );\n queryString = queryParams.toString()\n ? `?${queryParams.toString()}`\n : '';\n }\n\n const result = await callRestUrl(\n `${backend.base}${backend.list}${queryString}`\n );\n\n return capListResult(backend.unwrap(result), {\n limit: params.limit,\n cursor: params.cursor,\n });\n }\n );\n\n // Get Endpoint Details\n server.tool(\n 'get-endpoint',\n 'Get one Serverless endpoint by id, optionally expanding template and worker details (v1 only).',\n {\n endpointId: z.string().describe('ID of the endpoint to retrieve'),\n includeTemplate: z\n .boolean()\n .optional()\n .describe('Include template information (v1 only)'),\n includeWorkers: z\n .boolean()\n .optional()\n .describe('Include information about workers (v1 only)'),\n },\n { title: 'Get endpoint', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('endpoints');\n\n let queryString = '';\n if (backend.version === 'v1') {\n const queryParams = new URLSearchParams();\n if (params.includeTemplate)\n queryParams.append(\n 'includeTemplate',\n params.includeTemplate.toString()\n );\n if (params.includeWorkers)\n queryParams.append(\n 'includeWorkers',\n params.includeWorkers.toString()\n );\n queryString = queryParams.toString()\n ? `?${queryParams.toString()}`\n : '';\n }\n\n const result = await callRestUrl(\n `${backend.base}${backend.get!(params.endpointId)}${queryString}`\n );\n\n return jsonReply(result);\n }\n );\n\n // Create Endpoint — model differs by version:\n // v2: inline config. Requires imageName + gpuPoolIds (gpu.pools minItems 1).\n // v1: templateId-based.\n server.tool(\n 'create-endpoint',\n 'Create a Serverless endpoint. On v2 (default), pass an inline config: imageName + gpuPoolIds (GPU pool names from list-gpu-types — the `pool` field, e.g. AMPERE_80/ADA_24) plus optional workers/scaling/disk/env. On v1, pass a templateId instead. Worker min/max set autoscaling bounds (min 0 = scale to zero).',\n {\n name: z.string().optional().describe('Name for the endpoint'),\n // --- v2 inline-config fields ---\n imageName: z\n .string()\n .optional()\n .describe('Docker image (v2). Required on v2 instead of a templateId.'),\n gpuPoolIds: z\n .array(z.string())\n .optional()\n .describe(\n 'GPU pool names (v2, required). The `pool` field from list-gpu-types, e.g. [\"AMPERE_80\"]. NOT GPU type ids.'\n ),\n gpuCount: z.number().optional().describe('GPUs per worker (v2)'),\n args: z.string().optional().describe('Container start command/args (v2)'),\n containerDiskInGb: z\n .number()\n .optional()\n .describe('Container disk size in GB (v2)'),\n ports: z\n .array(z.string())\n .optional()\n .describe(\"Ports to expose (v2), e.g. ['8000/http']\"),\n env: z\n .record(z.string())\n .optional()\n .describe('Environment variables (v2)'),\n containerRegistryAuthId: z\n .string()\n .optional()\n .describe('Container registry auth id for a private image (v2)'),\n networkVolumeIds: z\n .array(z.string())\n .optional()\n .describe('Network volume ids to attach (v2)'),\n executionTimeoutMs: z\n .number()\n .optional()\n .describe('Per-job execution timeout in ms (v2)'),\n flashboot: z\n .enum(['OFF', 'FLASHBOOT', 'PRIORITY_FLASHBOOT'])\n .optional()\n .describe('FlashBoot mode (v2)'),\n // --- v1 template-based fields ---\n templateId: z\n .string()\n .optional()\n .describe('Template ID (v1). Required on v1.'),\n computeType: z\n .enum(['GPU', 'CPU'])\n .optional()\n .describe('GPU or CPU endpoint (v1)'),\n gpuTypeIds: z\n .array(z.string())\n .optional()\n .describe('List of acceptable GPU types (v1)'),\n // --- shared autoscaling fields ---\n workersMin: z.number().optional().describe('Minimum number of workers'),\n workersMax: z.number().optional().describe('Maximum number of workers'),\n scalerType: z\n .enum(['QUEUE_DELAY', 'REQUEST_COUNT'])\n .optional()\n .describe('Autoscaler type'),\n scalerValue: z.number().optional().describe('Autoscaler target value'),\n idleTimeout: z\n .number()\n .optional()\n .describe('Idle timeout in seconds before scaling a worker down'),\n dataCenterIds: z\n .array(z.string())\n .optional()\n .describe('List of preferred data centers'),\n },\n { title: 'Create endpoint', ...WRITE },\n async (params) => {\n const backend = backendFor('endpoints');\n\n if (backend.version === 'v2') {\n // Guard the v2-required fields before calling, so the caller gets a\n // clean 400 rather than a raw 422 from the API (mirrors create-pod).\n // v2 CreateEndpointRequest marks `name` required too — guard it so an\n // omitted name is a clean 400, not an opaque 422.\n if (!params.name) {\n return jsonReply({\n error: 'On the v2 REST API, create-endpoint requires a name.',\n status: 400,\n });\n }\n if (!params.imageName) {\n return jsonReply({\n error:\n 'On the v2 REST API, create-endpoint needs an imageName (v2 endpoints are image-based, not template-based). To use a templateId, set RUNPOD_REST_VERSION=v1.',\n status: 400,\n });\n }\n if (!params.gpuPoolIds?.length) {\n return jsonReply({\n error:\n 'create-endpoint needs gpuPoolIds on v2 (GPU pool names from list-gpu-types — the `pool` field, e.g. [\"AMPERE_80\"]).',\n status: 400,\n });\n }\n const body = backend.mapCreate(params) as Record<string, unknown>;\n const result = await callRestUrl(\n `${backend.base}${backend.list}`,\n 'POST',\n body\n );\n return jsonReply(result);\n }\n\n // v1: legacy /endpoints, templateId-based. Guard the v1-required field.\n if (!params.templateId) {\n return jsonReply({\n error:\n 'On the v1 REST API, create-endpoint needs a templateId. (On v2 — the default — pass imageName + gpuPoolIds instead.)',\n status: 400,\n });\n }\n // Send ONLY v1-relevant fields — the tool schema is a v1+v2 union, so\n // forwarding the raw params would also send v2-only keys (gpuPoolIds,\n // flashboot, networkVolumeIds, …) to the v1 API. Harmless today (v1 ignores\n // unknowns) but imprecise; filter explicitly.\n const v1Body: Record<string, unknown> = {};\n for (const k of [\n 'templateId',\n 'name',\n 'computeType',\n 'gpuTypeIds',\n 'gpuCount',\n 'workersMin',\n 'workersMax',\n 'dataCenterIds',\n ] as const) {\n if (params[k] !== undefined) v1Body[k] = params[k];\n }\n const result = await callRestUrl(\n `${backend.base}${backend.list}`,\n 'POST',\n v1Body\n );\n return jsonReply(result);\n }\n );\n\n // Update Endpoint — only provided fields change. v2 maps to the nested\n // /v2/serverless body; v1 passes the flat fields through.\n server.tool(\n 'update-endpoint',\n \"Update a Serverless endpoint's config. On v2 you can change image/disk/env/ports/registry/workers/scaling/networkVolumes/timeout/flashboot; on v1, scaling fields (worker min/max, idle timeout, scaler type/value, name). Only provided fields change.\",\n {\n endpointId: z.string().describe('ID of the endpoint to update'),\n name: z.string().optional().describe('New name for the endpoint'),\n workersMin: z\n .number()\n .optional()\n .describe('New minimum number of workers'),\n workersMax: z\n .number()\n .optional()\n .describe('New maximum number of workers'),\n idleTimeout: z\n .number()\n .optional()\n .describe('New idle timeout in seconds'),\n scalerType: z\n .enum(['QUEUE_DELAY', 'REQUEST_COUNT'])\n .optional()\n .describe('Scaler type'),\n scalerValue: z.number().optional().describe('Scaler value'),\n // --- v2-only inline-config fields ---\n imageName: z.string().optional().describe('New Docker image (v2)'),\n gpuPoolIds: z\n .array(z.string())\n .optional()\n .describe('New GPU pool names (v2), e.g. [\"AMPERE_80\"]'),\n gpuCount: z.number().optional().describe('New GPUs per worker (v2)'),\n args: z.string().optional().describe('New container args (v2)'),\n containerDiskInGb: z\n .number()\n .optional()\n .describe('New container disk size in GB (v2)'),\n ports: z.array(z.string()).optional().describe('New ports (v2)'),\n env: z\n .record(z.string())\n .optional()\n .describe('New environment variables (v2)'),\n containerRegistryAuthId: z\n .string()\n .optional()\n .describe('New container registry auth id (v2)'),\n networkVolumeIds: z\n .array(z.string())\n .optional()\n .describe('New network volume ids (v2)'),\n executionTimeoutMs: z\n .number()\n .optional()\n .describe('New per-job execution timeout in ms (v2)'),\n flashboot: z\n .enum(['OFF', 'FLASHBOOT', 'PRIORITY_FLASHBOOT'])\n .optional()\n .describe('New FlashBoot mode (v2)'),\n },\n { title: 'Update endpoint', ...WRITE, idempotentHint: true },\n async (params) => {\n const { endpointId, ...updateParams } = params;\n const backend = backendFor('endpoints');\n const body = backend.mapUpdate(updateParams) as Record<string, unknown>;\n const result = await callRestUrl(\n `${backend.base}${backend.get!(endpointId)}`,\n 'PATCH',\n body\n );\n return jsonReply(result);\n }\n );\n\n // Delete Endpoint\n server.tool(\n 'delete-endpoint',\n 'Permanently delete a Serverless endpoint. This cannot be undone.',\n {\n endpointId: z.string().describe('ID of the endpoint to delete'),\n },\n { title: 'Delete endpoint', ...DESTRUCTIVE },\n async (params) => {\n const backend = backendFor('endpoints');\n const result = await callRestUrl(\n `${backend.base}${backend.get!(params.endpointId)}`,\n 'DELETE'\n );\n return jsonReply(result);\n }\n );\n\n // ⛔ DISABLED until prod ships the endpoint — DO NOT register yet.\n // `list-endpoint-releases` calls GET /v2/serverless/{id}/releases. That op\n // exists ONLY on the dev v2 deployment (v2-rest.runpod.dev, 47-op spec); prod\n // (v2-rest.runpod.io, 45-op spec) returns 422 \"path not found\". Keeping the\n // tool registered would expose a call that 422s in prod. Re-enable this block\n // once listEndpointReleases is live on prod.\n /*\n server.tool(\n 'list-endpoint-releases',\n 'List a Serverless endpoint\\'s release history and current rollout status (workers on the latest version). v2-only — returns a 501 notice on the v1 API. Paginated via limit/cursor.',\n {\n ...listPaginationParams,\n endpointId: z\n .string()\n .describe('ID of the Serverless endpoint whose releases to list'),\n },\n { title: 'List endpoint releases', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('endpoints');\n if (backend.version === 'v1') {\n return jsonReply({\n error:\n 'list-endpoint-releases is only available on the v2 REST API. Set RUNPOD_REST_VERSION=v2.',\n status: 501,\n });\n }\n const raw = (await callRestUrl(\n `${backend.base}/serverless/${params.endpointId}/releases`\n )) as Record<string, unknown> | undefined;\n const releases = Array.isArray(raw?.releases) ? raw!.releases : [];\n return capListResult(\n releases,\n { limit: params.limit, cursor: params.cursor },\n { rollout: raw?.rollout, endpointVersion: raw?.endpointVersion }\n );\n }\n );\n */\n\n // List Endpoint Workers (v2-only — GET /v2/serverless/{id}/workers)\n // Returns the workers backing an endpoint plus an aggregate summary. v2-only:\n // returns a 501 notice on the v1 API. The workers array is capped via\n // limit/cursor; the `summary` and `endpointVersion` are preserved alongside.\n server.tool(\n 'list-endpoint-workers',\n 'List the workers backing a Serverless endpoint, with their status and an aggregate summary. v2-only — returns a 501 notice on the v1 API. Paginated via limit/cursor.',\n {\n ...listPaginationParams,\n endpointId: z\n .string()\n .describe('ID of the Serverless endpoint whose workers to list'),\n },\n { title: 'List endpoint workers', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('workers');\n if (backend.version === 'v1') {\n return jsonReply({\n error:\n 'list-endpoint-workers is only available on the v2 REST API. Set RUNPOD_REST_VERSION=v2 (on v1, use get-endpoint with includeWorkers).',\n status: 501,\n });\n }\n const raw = (await callRestUrl(\n `${backend.base}/serverless/${params.endpointId}/workers`\n )) as Record<string, unknown> | undefined;\n return capListResult(\n backend.unwrap(raw),\n { limit: params.limit, cursor: params.cursor },\n { summary: raw?.summary, endpointVersion: raw?.endpointVersion }\n );\n }\n );\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { READ_ONLY, WRITE, type ToolRuntime } from './runtime.js';\n\n// ============== SERVERLESS RUNTIME TOOLS ==============\n// Job submission and lifecycle against the Serverless runtime API\n// (api.runpod.ai/v2/{endpointId}/...). Distinct from endpoint CRUD.\n\nexport function registerJobTools(server: McpServer, rt: ToolRuntime): void {\n const { jsonReply, serverlessRequest } = rt;\n\n // Shared schemas for serverless tools\n const endpointIdSchema = z\n .string()\n .regex(/^[a-zA-Z0-9_-]+$/, 'Invalid endpoint ID format');\n\n const jobIdSchema = z\n .string()\n .regex(/^[a-zA-Z0-9_-]+$/, 'Invalid job ID format');\n\n const inputSchema = z\n .record(z.unknown())\n .describe(\n 'Input payload for the worker handler. The expected fields depend on the deployed model or worker.'\n );\n\n const webhookSchema = z\n .string()\n .url()\n .optional()\n .describe(\n 'Webhook URL to receive job completion notifications instead of polling'\n );\n\n const policySchema = z\n .object({\n executionTimeout: z\n .number()\n .optional()\n .describe('Maximum execution time in milliseconds'),\n lowPriority: z\n .boolean()\n .optional()\n .describe('Submit as a low-priority job'),\n ttl: z\n .number()\n .optional()\n .describe('Time-to-live for the job result in milliseconds'),\n })\n .optional()\n .describe('Execution policy options');\n\n const s3ConfigSchema = z\n .object({\n accessId: z.string().describe('S3 access key ID'),\n accessSecret: z.string().describe('S3 secret access key'),\n bucketName: z.string().describe('S3 bucket name'),\n endpointUrl: z.string().describe('S3 endpoint URL'),\n })\n .optional()\n .describe('S3-compatible storage config for large outputs');\n\n // Run Endpoint (Async)\n server.tool(\n 'run-endpoint',\n 'Submit an asynchronous job to a Serverless endpoint. Returns a job ID immediately — use get-job-status to poll for results. Async results are available for 30 minutes after completion.',\n {\n endpointId: endpointIdSchema.describe(\n 'ID of the Serverless endpoint to run'\n ),\n input: inputSchema,\n webhook: webhookSchema,\n policy: policySchema,\n s3Config: s3ConfigSchema,\n },\n { title: 'Run endpoint (async)', ...WRITE },\n async (params) => {\n const { endpointId, ...body } = params;\n const result = await serverlessRequest(\n endpointId,\n '/run',\n 'POST',\n body as Record<string, unknown>\n );\n\n return jsonReply(result);\n }\n );\n\n // Run Endpoint Sync\n server.tool(\n 'runsync-endpoint',\n 'Submit a synchronous job to a Serverless endpoint and wait for the result. Best for tasks completing within 90 seconds. If processing exceeds 90 seconds, the response returns a job ID to poll with get-job-status. Max payload: 20 MB. Results expire after 1 minute. Use the wait parameter to extend the server-side wait up to 5 minutes (300000 ms).',\n {\n endpointId: endpointIdSchema.describe(\n 'ID of the Serverless endpoint to run synchronously'\n ),\n input: inputSchema,\n wait: z\n .number()\n .min(1000)\n .max(300000)\n .optional()\n .describe(\n 'How long in milliseconds the server should wait for a result before returning a job ID to poll (1000–300000). Defaults to 90000 (90 seconds).'\n ),\n webhook: webhookSchema,\n policy: policySchema,\n s3Config: s3ConfigSchema,\n },\n { title: 'Run endpoint (sync)', ...WRITE },\n async (params) => {\n const { endpointId, wait, ...body } = params;\n const path = wait ? `/runsync?wait=${wait}` : '/runsync';\n const result = await serverlessRequest(\n endpointId,\n path,\n 'POST',\n body as Record<string, unknown>\n );\n\n return jsonReply(result);\n }\n );\n\n // Get Job Status\n server.tool(\n 'get-job-status',\n 'Check the status of an asynchronous Serverless job. Returns the current status and output when complete. Job statuses: IN_QUEUE, IN_PROGRESS, COMPLETED, FAILED, CANCELLED, TIMED_OUT.',\n {\n endpointId: endpointIdSchema.describe(\n 'ID of the Serverless endpoint the job belongs to'\n ),\n jobId: jobIdSchema.describe('ID of the job to check'),\n },\n { title: 'Get job status', ...READ_ONLY },\n async (params) => {\n const result = await serverlessRequest(\n params.endpointId,\n `/status/${params.jobId}`\n );\n\n return jsonReply(result);\n }\n );\n\n // Stream Job Results\n server.tool(\n 'stream-job',\n 'Retrieve all streaming output from a Serverless job by polling until the job reaches a terminal state. The worker must support streaming output. Polls /stream/{jobId} repeatedly and collects every chunk until status is COMPLETED, FAILED, CANCELLED, or TIMED_OUT.',\n {\n endpointId: endpointIdSchema.describe(\n 'ID of the Serverless endpoint the job belongs to'\n ),\n jobId: jobIdSchema.describe('ID of the job to stream results from'),\n },\n { title: 'Stream job', ...READ_ONLY },\n async (params) => {\n const TERMINAL_STATUSES = new Set([\n 'COMPLETED',\n 'FAILED',\n 'CANCELLED',\n 'TIMED_OUT',\n ]);\n const MAX_POLL_TIME_MS = 5 * 60 * 1000; // 5 minutes\n const POLL_INTERVAL_MS = 1000;\n const MAX_CONSECUTIVE_ERRORS = 5;\n const allChunks: unknown[] = [];\n let finalResult: Record<string, unknown> = {};\n let consecutiveErrors = 0;\n let lastError: string | undefined;\n const startTime = Date.now();\n\n while (true) {\n try {\n const result = (await serverlessRequest(\n params.endpointId,\n `/stream/${params.jobId}`\n )) as Record<string, unknown>;\n\n consecutiveErrors = 0;\n\n if (Array.isArray(result.stream)) {\n allChunks.push(...result.stream);\n }\n\n finalResult = result;\n\n if (TERMINAL_STATUSES.has(result.status as string)) {\n break;\n }\n } catch (error) {\n consecutiveErrors++;\n lastError = error instanceof Error ? error.message : String(error);\n if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {\n finalResult.error = `Polling aborted after ${MAX_CONSECUTIVE_ERRORS} consecutive errors: ${lastError}`;\n break;\n }\n }\n\n if (Date.now() - startTime > MAX_POLL_TIME_MS) {\n finalResult.pollingTimedOut = true;\n finalResult.note =\n 'Polling timed out after 5 minutes. Use get-job-status to check the job later.';\n // Surface the most recent error (if any) instead of discarding it —\n // the last poll may have been failing (e.g. job expired) even though\n // earlier polls succeeded.\n if (lastError) finalResult.lastError = lastError;\n break;\n }\n\n await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));\n }\n\n return jsonReply({ ...finalResult, stream: allChunks });\n }\n );\n\n // Cancel Job\n server.tool(\n 'cancel-job',\n 'Cancel a Serverless job that is queued or in progress.',\n {\n endpointId: endpointIdSchema.describe(\n 'ID of the Serverless endpoint the job belongs to'\n ),\n jobId: jobIdSchema.describe('ID of the job to cancel'),\n },\n { title: 'Cancel job', ...WRITE, idempotentHint: true },\n async (params) => {\n const result = await serverlessRequest(\n params.endpointId,\n `/cancel/${params.jobId}`,\n 'POST'\n );\n\n return jsonReply(result);\n }\n );\n\n // Retry Job\n server.tool(\n 'retry-job',\n '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.',\n {\n endpointId: endpointIdSchema.describe(\n 'ID of the Serverless endpoint the job belongs to'\n ),\n jobId: jobIdSchema.describe('ID of the job to retry'),\n },\n { title: 'Retry job', ...WRITE },\n async (params) => {\n const result = await serverlessRequest(\n params.endpointId,\n `/retry/${params.jobId}`,\n 'POST'\n );\n\n return jsonReply(result);\n }\n );\n\n // Endpoint Health\n server.tool(\n 'endpoint-health',\n 'Get the health and operational status of a Serverless endpoint, including worker counts and job statistics.',\n {\n endpointId: endpointIdSchema.describe(\n 'ID of the Serverless endpoint to check health for'\n ),\n },\n { title: 'Endpoint health', ...READ_ONLY },\n async (params) => {\n const result = await serverlessRequest(params.endpointId, '/health');\n\n return jsonReply(result);\n }\n );\n\n // Purge Endpoint Queue\n server.tool(\n 'purge-endpoint-queue',\n 'Remove all pending jobs from a Serverless endpoint queue. Only affects queued jobs — in-progress jobs continue running. Use this for error recovery or clearing outdated requests.',\n {\n endpointId: endpointIdSchema.describe(\n 'ID of the Serverless endpoint to purge the queue for'\n ),\n },\n { title: 'Purge endpoint queue', ...WRITE, idempotentHint: true },\n async (params) => {\n const result = await serverlessRequest(\n params.endpointId,\n '/purge-queue',\n 'POST'\n );\n\n return jsonReply(result);\n }\n );\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { capListResult, listPaginationParams } from '../pagination.js';\nimport { READ_ONLY, WRITE, DESTRUCTIVE, type ToolRuntime } from './runtime.js';\n\n// ============== TEMPLATE MANAGEMENT TOOLS ==============\n\nexport function registerTemplateTools(\n server: McpServer,\n rt: ToolRuntime\n): void {\n const { jsonReply, callRestUrl, backendFor } = rt;\n\n // List Templates\n server.tool(\n 'list-templates',\n 'List available templates. By default returns only the user\\'s own templates. Use includeRunpodTemplates to also include official Runpod templates. The recommended default template for new pods is \"Runpod Pytorch 2.8.0\" (ID: runpod-torch-v280) — it has the latest CUDA and PyTorch versions.',\n {\n ...listPaginationParams,\n includeRunpodTemplates: z\n .boolean()\n .optional()\n .describe('Include official Runpod templates in the response'),\n includePublicTemplates: z\n .boolean()\n .optional()\n .describe('Include community-made public templates in the response'),\n includeEndpointBoundTemplates: z\n .boolean()\n .optional()\n .describe(\n 'Include templates bound to Serverless endpoints in the response'\n ),\n },\n { title: 'List templates', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('templates');\n let query = '';\n if (backend.version === 'v1') {\n const queryParams = new URLSearchParams();\n if (params.includeRunpodTemplates)\n queryParams.set('includeRunpodTemplates', 'true');\n if (params.includePublicTemplates)\n queryParams.set('includePublicTemplates', 'true');\n if (params.includeEndpointBoundTemplates)\n queryParams.set('includeEndpointBoundTemplates', 'true');\n query = queryParams.toString() ? `?${queryParams.toString()}` : '';\n }\n const result = await callRestUrl(\n `${backend.base}${backend.list}${query}`\n );\n\n return capListResult(backend.unwrap(result), {\n limit: params.limit,\n cursor: params.cursor,\n });\n }\n );\n\n // Get Template Details\n server.tool(\n 'get-template',\n 'Get one template by id (image, env, ports, disk/volume config).',\n {\n templateId: z.string().describe('ID of the template to retrieve'),\n },\n { title: 'Get template', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('templates');\n const result = await callRestUrl(\n `${backend.base}${backend.get!(params.templateId)}`\n );\n return jsonReply(result);\n }\n );\n\n // Create Template\n server.tool(\n 'create-template',\n 'Create a reusable pod/endpoint template (image, ports, env, disk/volume). On v2 a category is required and defaults to NVIDIA.',\n {\n name: z.string().describe('Name for the template'),\n imageName: z.string().describe('Docker image to use'),\n isServerless: z\n .boolean()\n .optional()\n .describe('Is this a serverless template'),\n ports: z.array(z.string()).optional().describe('Ports to expose'),\n dockerEntrypoint: z\n .array(z.string())\n .optional()\n .describe('Docker entrypoint commands'),\n dockerStartCmd: z\n .array(z.string())\n .optional()\n .describe('Docker start commands'),\n env: z.record(z.string()).optional().describe('Environment variables'),\n containerDiskInGb: z\n .number()\n .optional()\n .describe('Container disk size in GB'),\n volumeInGb: z.number().optional().describe('Volume size in GB'),\n volumeMountPath: z\n .string()\n .optional()\n .describe('Path to mount the volume'),\n readme: z\n .string()\n .optional()\n .describe('README content in markdown format'),\n category: z\n .enum(['CPU', 'NVIDIA', 'AMD'])\n .optional()\n .describe(\n 'Hardware category for the template. On v2 a category is required and defaults to NVIDIA when omitted — set CPU or AMD for non-NVIDIA templates so they are not mis-categorized.'\n ),\n containerRegistryAuthId: z\n .string()\n .optional()\n .describe(\n 'ID of a container registry credential (from create-container-registry-auth / list-container-registry-auths) used to pull a private image. Required when imageName points at a private registry; without it serverless workers fail the image pull.'\n ),\n },\n { title: 'Create template', ...WRITE },\n async (params) => {\n const backend = backendFor('templates');\n const body = backend.mapCreate(params) as Record<string, unknown>;\n const result = await callRestUrl(\n `${backend.base}${backend.list}`,\n 'POST',\n body\n );\n return jsonReply(result);\n }\n );\n\n // Update Template\n server.tool(\n 'update-template',\n \"Update a template's mutable fields (name, image, ports, env, readme). Only provided fields change.\",\n {\n templateId: z.string().describe('ID of the template to update'),\n name: z.string().optional().describe('New name for the template'),\n imageName: z.string().optional().describe('New Docker image'),\n ports: z.array(z.string()).optional().describe('New ports to expose'),\n env: z\n .record(z.string())\n .optional()\n .describe('New environment variables'),\n readme: z\n .string()\n .optional()\n .describe('New README content in markdown format'),\n containerRegistryAuthId: z\n .string()\n .optional()\n .describe(\n 'ID of a container registry credential (from create-container-registry-auth / list-container-registry-auths) used to pull a private image. Set this to attach a credential to a template referencing a private registry.'\n ),\n },\n { title: 'Update template', ...WRITE, idempotentHint: true },\n async (params) => {\n const { templateId, ...updateParams } = params;\n const backend = backendFor('templates');\n const body = backend.mapUpdate(updateParams) as Record<string, unknown>;\n const result = await callRestUrl(\n `${backend.base}${backend.get!(templateId)}`,\n 'PATCH',\n body\n );\n return jsonReply(result);\n }\n );\n\n // Delete Template\n server.tool(\n 'delete-template',\n 'Permanently delete a template. This cannot be undone.',\n {\n templateId: z.string().describe('ID of the template to delete'),\n },\n { title: 'Delete template', ...DESTRUCTIVE },\n async (params) => {\n const backend = backendFor('templates');\n const result = await callRestUrl(\n `${backend.base}${backend.get!(params.templateId)}`,\n 'DELETE'\n );\n return jsonReply(result);\n }\n );\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { capListResult, listPaginationParams } from '../pagination.js';\nimport { READ_ONLY, WRITE, DESTRUCTIVE, type ToolRuntime } from './runtime.js';\n\n// ============== NETWORK VOLUME MANAGEMENT TOOLS ==============\n\nexport function registerNetworkVolumeTools(\n server: McpServer,\n rt: ToolRuntime\n): void {\n const { jsonReply, callRestUrl, backendFor } = rt;\n\n // List Network Volumes\n server.tool(\n 'list-network-volumes',\n 'List your network volumes (persistent storage attachable to pods). Paginated via limit/cursor.',\n listPaginationParams,\n { title: 'List network volumes', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('networkVolumes');\n const result = await callRestUrl(`${backend.base}${backend.list}`);\n\n return capListResult(backend.unwrap(result), {\n limit: params.limit,\n cursor: params.cursor,\n });\n }\n );\n\n // Get Network Volume Details\n server.tool(\n 'get-network-volume',\n 'Get one network volume by id (name, size, data center).',\n {\n networkVolumeId: z\n .string()\n .describe('ID of the network volume to retrieve'),\n },\n { title: 'Get network volume', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('networkVolumes');\n const result = await callRestUrl(\n `${backend.base}${backend.get!(params.networkVolumeId)}`\n );\n return jsonReply(result);\n }\n );\n\n // Create Network Volume\n server.tool(\n 'create-network-volume',\n 'Create a persistent network volume in a data center (size 1–4000 GB) that pods can mount.',\n {\n name: z.string().describe('Name for the network volume'),\n size: z.number().describe('Size in GB (1-4000)'),\n dataCenterId: z\n .string()\n .describe('Data center ID (see list-data-centers)'),\n },\n { title: 'Create network volume', ...WRITE },\n async (params) => {\n const backend = backendFor('networkVolumes');\n const body = backend.mapCreate(params) as Record<string, unknown>;\n const result = await callRestUrl(\n `${backend.base}${backend.list}`,\n 'POST',\n body\n );\n return jsonReply(result);\n }\n );\n\n // Update Network Volume\n server.tool(\n 'update-network-volume',\n 'Update a network volume (rename, or grow size — the new size must exceed the current size). Only provided fields change.',\n {\n networkVolumeId: z\n .string()\n .describe('ID of the network volume to update'),\n name: z.string().optional().describe('New name for the network volume'),\n size: z\n .number()\n .optional()\n .describe('New size in GB (must be larger than current)'),\n },\n { title: 'Update network volume', ...WRITE, idempotentHint: true },\n async (params) => {\n const { networkVolumeId, ...updateParams } = params;\n const backend = backendFor('networkVolumes');\n const body = backend.mapUpdate(updateParams) as Record<string, unknown>;\n const result = await callRestUrl(\n `${backend.base}${backend.get!(networkVolumeId)}`,\n 'PATCH',\n body\n );\n return jsonReply(result);\n }\n );\n\n // Delete Network Volume\n server.tool(\n 'delete-network-volume',\n 'Permanently delete a network volume and its data. This cannot be undone.',\n {\n networkVolumeId: z\n .string()\n .describe('ID of the network volume to delete'),\n },\n { title: 'Delete network volume', ...DESTRUCTIVE },\n async (params) => {\n const backend = backendFor('networkVolumes');\n const result = await callRestUrl(\n `${backend.base}${backend.get!(params.networkVolumeId)}`,\n 'DELETE'\n );\n return jsonReply(result);\n }\n );\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { capListResult, listPaginationParams } from '../pagination.js';\nimport { READ_ONLY, WRITE, DESTRUCTIVE, type ToolRuntime } from './runtime.js';\n\n// ============== CONTAINER REGISTRY AUTH TOOLS ==============\n\nexport function registerRegistryTools(\n server: McpServer,\n rt: ToolRuntime\n): void {\n const { jsonReply, callRestUrl, backendFor } = rt;\n\n // List Container Registry Auths\n server.tool(\n 'list-container-registry-auths',\n 'List your saved container-registry credentials (private image-pull auth). Paginated via limit/cursor.',\n listPaginationParams,\n { title: 'List container registry auths', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('registries');\n const result = await callRestUrl(`${backend.base}${backend.list}`);\n\n return capListResult(backend.unwrap(result), {\n limit: params.limit,\n cursor: params.cursor,\n });\n }\n );\n\n // Get Container Registry Auth Details\n server.tool(\n 'get-container-registry-auth',\n 'Get one saved container-registry credential by id (the secret/password is not returned).',\n {\n containerRegistryAuthId: z\n .string()\n .describe('ID of the container registry auth to retrieve'),\n },\n { title: 'Get container registry auth', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('registries');\n const result = await callRestUrl(\n `${backend.base}${backend.get!(params.containerRegistryAuthId)}`\n );\n return jsonReply(result);\n }\n );\n\n // Create Container Registry Auth\n server.tool(\n 'create-container-registry-auth',\n 'Save container-registry credentials (username + password/token) so pods and endpoints can pull private images.',\n {\n name: z.string().describe('Name for the container registry auth'),\n username: z.string().describe('Registry username'),\n password: z\n .string()\n .describe('Registry password or access token (stored as a secret)'),\n },\n { title: 'Create container registry auth', ...WRITE },\n async (params) => {\n const backend = backendFor('registries');\n const body = backend.mapCreate(params) as Record<string, unknown>;\n const result = await callRestUrl(\n `${backend.base}${backend.list}`,\n 'POST',\n body\n );\n return jsonReply(result);\n }\n );\n\n // Delete Container Registry Auth\n server.tool(\n 'delete-container-registry-auth',\n 'Permanently delete a saved container-registry credential. This cannot be undone.',\n {\n containerRegistryAuthId: z\n .string()\n .describe('ID of the container registry auth to delete'),\n },\n { title: 'Delete container registry auth', ...DESTRUCTIVE },\n async (params) => {\n const backend = backendFor('registries');\n const result = await callRestUrl(\n `${backend.base}${backend.get!(params.containerRegistryAuthId)}`,\n 'DELETE'\n );\n return jsonReply(result);\n }\n );\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { capListResult, listPaginationParams } from '../pagination.js';\nimport { READ_ONLY, WRITE, DESTRUCTIVE, type ToolRuntime } from './runtime.js';\n\n// ============== TAG MANAGEMENT TOOLS (v2-only) ==============\n// Tags are key/value labels attachable to pods, network volumes, clusters, and\n// serverless endpoints. The whole resource is v2-only: there is no v1 REST or\n// GraphQL equivalent, so every tool returns a clean 501 notice on v1 (matching\n// list-cpu-types / get-gpu-type).\n\n// The resource types a tag can attach to (TagResourceType in the v2 spec).\nconst TAG_RESOURCE_TYPES = [\n 'POD',\n 'NETWORK_VOLUME',\n 'CLUSTER',\n 'SERVERLESS_ENDPOINT',\n] as const;\n\nconst taggedResourceSchema = z.object({\n id: z.string().describe('Resource identifier (e.g. a pod or endpoint id)'),\n type: z\n .enum(TAG_RESOURCE_TYPES)\n .describe('Type of resource to associate with the tag'),\n});\n\n// Shared 501 reply for v1. Returned by every tag tool when not on v2.\nfunction v2OnlyNotice(tool: string) {\n return {\n error: `${tool} is only available on the v2 REST API. Set RUNPOD_REST_VERSION=v2.`,\n status: 501,\n };\n}\n\nexport function registerTagTools(server: McpServer, rt: ToolRuntime): void {\n const { jsonReply, callRestUrl, backendFor } = rt;\n\n // List Tags\n server.tool(\n 'list-tags',\n 'List your tags (key/value labels on pods, volumes, clusters, and endpoints). v2-only — returns a 501 notice on the v1 API. Paginated via limit/cursor.',\n {\n ...listPaginationParams,\n includeResources: z\n .boolean()\n .optional()\n .describe('Include the resources attached to each tag in the response'),\n },\n { title: 'List tags', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('tags');\n if (backend.version === 'v1') return jsonReply(v2OnlyNotice('list-tags'));\n const query = params.includeResources ? '?include=resources' : '';\n const raw = await callRestUrl(`${backend.base}${backend.list}${query}`);\n return capListResult(backend.unwrap(raw), {\n limit: params.limit,\n cursor: params.cursor,\n });\n }\n );\n\n // Get Tag\n server.tool(\n 'get-tag',\n 'Get one tag by id, optionally including its attached resources. v2-only — returns a 501 notice on the v1 API.',\n {\n tagId: z.string().describe('ID of the tag to retrieve'),\n includeResources: z\n .boolean()\n .optional()\n .describe('Include the resources attached to this tag'),\n },\n { title: 'Get tag', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('tags');\n if (backend.version === 'v1') return jsonReply(v2OnlyNotice('get-tag'));\n const query = params.includeResources ? '?include=resources' : '';\n const result = await callRestUrl(\n `${backend.base}${backend.get!(params.tagId)}${query}`\n );\n return jsonReply(result);\n }\n );\n\n // Create Tag\n server.tool(\n 'create-tag',\n 'Create a key/value tag, optionally attaching resources at creation. v2-only — returns a 501 notice on the v1 API.',\n {\n key: z.string().min(1).describe('Tag key (e.g. \"project\")'),\n value: z.string().min(1).describe('Tag value (e.g. \"gpt-training\")'),\n resources: z\n .array(taggedResourceSchema)\n .optional()\n .describe('Resources to attach to the tag at creation time'),\n },\n { title: 'Create tag', ...WRITE },\n async (params) => {\n const backend = backendFor('tags');\n if (backend.version === 'v1')\n return jsonReply(v2OnlyNotice('create-tag'));\n const result = await callRestUrl(\n `${backend.base}${backend.list}`,\n 'POST',\n params as Record<string, unknown>\n );\n return jsonReply(result);\n }\n );\n\n // Update Tag\n server.tool(\n 'update-tag',\n 'Update a tag (rename its key/value, or replace its resource associations). Provide at least one of key, value, or resources. Sending resources:[] removes all associations; omitting resources leaves them unchanged. Replacing resources is NOT atomic — a failed update can leave a partially-applied set, so for small association changes prefer attach-tag/detach-tag. v2-only — returns a 501 notice on the v1 API.',\n {\n tagId: z.string().describe('ID of the tag to update'),\n key: z.string().min(1).optional().describe('New tag key'),\n value: z.string().min(1).optional().describe('New tag value'),\n resources: z\n .array(taggedResourceSchema)\n .optional()\n .describe(\n \"Replace the tag's resource associations with exactly these. Omit to leave unchanged; pass [] to detach all.\"\n ),\n },\n // No idempotentHint: replacing `resources` is non-atomic upstream, so a\n // retried-after-partial-failure update is not guaranteed to be a no-op.\n { title: 'Update tag', ...WRITE },\n async (params) => {\n const { tagId, ...updateParams } = params;\n const backend = backendFor('tags');\n if (backend.version === 'v1')\n return jsonReply(v2OnlyNotice('update-tag'));\n const result = await callRestUrl(\n `${backend.base}${backend.get!(tagId)}`,\n 'PATCH',\n updateParams as Record<string, unknown>\n );\n return jsonReply(result);\n }\n );\n\n // Delete Tag\n server.tool(\n 'delete-tag',\n 'Permanently delete a tag (removes it from all resources it is attached to). This cannot be undone. v2-only — returns a 501 notice on the v1 API.',\n {\n tagId: z.string().describe('ID of the tag to delete'),\n },\n { title: 'Delete tag', ...DESTRUCTIVE },\n async (params) => {\n const backend = backendFor('tags');\n if (backend.version === 'v1')\n return jsonReply(v2OnlyNotice('delete-tag'));\n const result = await callRestUrl(\n `${backend.base}${backend.get!(params.tagId)}`,\n 'DELETE'\n );\n return jsonReply(result);\n }\n );\n\n // Attach Tag to a Resource\n server.tool(\n 'attach-tag',\n 'Attach a tag to a resource (pod, network volume, cluster, or serverless endpoint). Idempotent — attaching an already-associated resource is a no-op. v2-only — returns a 501 notice on the v1 API.',\n {\n tagId: z.string().describe('ID of the tag'),\n resourceType: z\n .enum(TAG_RESOURCE_TYPES)\n .describe('Type of resource to attach the tag to'),\n resourceId: z\n .string()\n .describe('ID of the resource to attach the tag to'),\n },\n { title: 'Attach tag to resource', ...WRITE, idempotentHint: true },\n async (params) => {\n const backend = backendFor('tags');\n if (backend.version === 'v1')\n return jsonReply(v2OnlyNotice('attach-tag'));\n const result = await callRestUrl(\n `${backend.base}/tags/${encodeURIComponent(params.tagId)}/resources/${params.resourceType}/${encodeURIComponent(params.resourceId)}`,\n 'PUT'\n );\n return jsonReply(result);\n }\n );\n\n // Detach Tag from a Resource\n server.tool(\n 'detach-tag',\n 'Detach a tag from a resource (removes the association only — the resource and tag are not deleted). Idempotent — detaching a resource that is not associated is a no-op. v2-only — returns a 501 notice on the v1 API.',\n {\n tagId: z.string().describe('ID of the tag'),\n resourceType: z\n .enum(TAG_RESOURCE_TYPES)\n .describe('Type of resource to detach the tag from'),\n resourceId: z\n .string()\n .describe('ID of the resource to detach the tag from'),\n },\n { title: 'Detach tag from resource', ...WRITE, idempotentHint: true },\n async (params) => {\n const backend = backendFor('tags');\n if (backend.version === 'v1')\n return jsonReply(v2OnlyNotice('detach-tag'));\n const result = await callRestUrl(\n `${backend.base}/tags/${encodeURIComponent(params.tagId)}/resources/${params.resourceType}/${encodeURIComponent(params.resourceId)}`,\n 'DELETE'\n );\n return jsonReply(result);\n }\n );\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { capListResult, listPaginationParams } from '../pagination.js';\nimport { READ_ONLY, type ToolRuntime } from './runtime.js';\n\n// ============== BILLING TOOLS (v2-only) ==============\n// Read-only, time-bucketed spend history. v2-only — returns a 501 notice on the\n// v1 API. The records array can be large (buckets × resources), so it is capped\n// via limit/cursor; the aggregate `metadata` is preserved alongside the page.\n\n// scope → URL sub-path. 'all' is the aggregate /v2/billing; the rest are\n// per-resource breakdowns. Values match the v2 spec sub-paths exactly\n// (note: 'networkvolumes' has no hyphen here, unlike the /v2/network-volumes\n// resource path).\nconst BILLING_SCOPE_PATH: Record<string, string> = {\n all: '/billing',\n pods: '/billing/pods',\n serverless: '/billing/serverless',\n endpoints: '/billing/endpoints',\n networkvolumes: '/billing/networkvolumes',\n clusters: '/billing/clusters',\n};\n\nexport function registerBillingTools(server: McpServer, rt: ToolRuntime): void {\n const { jsonReply, callRestUrl, backendFor } = rt;\n\n server.tool(\n 'get-billing',\n 'Get time-bucketed Runpod spend history, either aggregated across all resources or broken down by resource type (pods, serverless, endpoints, network volumes, clusters). v2-only — returns a 501 notice on the v1 API. Records are capped via limit/cursor; narrow the window with startTime/endTime or lastN.',\n {\n ...listPaginationParams,\n scope: z\n .enum([\n 'all',\n 'pods',\n 'serverless',\n 'endpoints',\n 'networkvolumes',\n 'clusters',\n ])\n .optional()\n .describe(\n 'Which billing breakdown to fetch. \"all\" (default) is the aggregate across every resource.'\n ),\n startTime: z\n .string()\n .optional()\n .describe(\n 'Start of the period (RFC 3339, e.g. 2026-05-01T00:00:00Z). Defaults to 30 days ago. Mutually exclusive with lastN.'\n ),\n endTime: z\n .string()\n .optional()\n .describe(\n 'End of the period (RFC 3339), exclusive. Defaults to now. Mutually exclusive with lastN.'\n ),\n bucketSize: z\n .enum(['hour', 'day', 'week', 'month', 'year'])\n .optional()\n .describe('Length of each time bucket. Defaults to day.'),\n lastN: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\n 'Return the last N buckets of bucketSize, ending with the current one. Mutually exclusive with startTime/endTime.'\n ),\n },\n { title: 'Get billing history', ...READ_ONLY },\n async (params) => {\n const backend = backendFor('billing');\n if (backend.version === 'v1') {\n return jsonReply({\n error:\n 'get-billing is only available on the v2 REST API. Set RUNPOD_REST_VERSION=v2.',\n status: 501,\n });\n }\n\n const subPath = BILLING_SCOPE_PATH[params.scope ?? 'all'];\n const query = new URLSearchParams();\n if (params.startTime) query.set('startTime', params.startTime);\n if (params.endTime) query.set('endTime', params.endTime);\n if (params.bucketSize) query.set('bucketSize', params.bucketSize);\n // lastN is mutually exclusive with startTime/endTime; if a window was\n // given, ignore lastN so we never forward a contradictory request.\n if (\n params.lastN !== undefined &&\n !params.startTime &&\n !params.endTime\n )\n query.set('lastN', String(params.lastN));\n const qs = query.toString() ? `?${query.toString()}` : '';\n\n const raw = (await callRestUrl(`${backend.base}${subPath}${qs}`)) as\n | Record<string, unknown>\n | undefined;\n // Cap the (potentially large) records array but keep the aggregate\n // `metadata` alongside the page so totals aren't lost to truncation.\n return capListResult(\n backend.unwrap(raw),\n { limit: params.limit, cursor: params.cursor },\n { metadata: raw?.metadata }\n );\n }\n );\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {\n createToolRuntime,\n type ToolContext,\n type ToolDeps,\n} from './tools/runtime.js';\nimport { registerCatalogTools } from './tools/catalog.js';\nimport { registerPodTools } from './tools/pods.js';\nimport { registerEndpointTools } from './tools/endpoints.js';\nimport { registerJobTools } from './tools/jobs.js';\nimport { registerTemplateTools } from './tools/templates.js';\nimport { registerNetworkVolumeTools } from './tools/network-volumes.js';\nimport { registerRegistryTools } from './tools/registries.js';\nimport { registerTagTools } from './tools/tags.js';\nimport { registerBillingTools } from './tools/billing.js';\n\n// Re-export so existing importers (entrypoints, tests) keep their import path.\nexport type { ToolContext } from './tools/runtime.js';\n\n/**\n * Register all Runpod tools on the given MCP server instance.\n *\n * The tool surface is split by resource under `src/tools/<resource>.ts`; this\n * function builds the shared runtime (caller-tracking, authenticated REST/\n * Serverless/GraphQL clients, the v1/v2 backend resolver) once and threads it\n * into each per-resource registrar.\n *\n * @param ctx supplies the API key the request helpers authenticate with.\n * @param deps optional test seam (inject a fake fetch / the resolved v2-probe\n * verdict). Production passes nothing.\n */\nexport function registerTools(\n server: McpServer,\n ctx: ToolContext,\n deps: ToolDeps = {}\n): void {\n const rt = createToolRuntime(server, ctx, deps);\n\n registerCatalogTools(server, rt);\n registerPodTools(server, rt);\n registerEndpointTools(server, rt);\n registerJobTools(server, rt);\n registerTemplateTools(server, rt);\n registerNetworkVolumeTools(server, rt);\n registerRegistryTools(server, rt);\n registerTagTools(server, rt);\n registerBillingTools(server, rt);\n}\n"],"mappings":"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","names":["z","jsonReply","z","z","jsonReply","z","z","jsonReply","z","result","z","jsonReply","z","z","jsonReply","z","z","jsonReply","z","z","jsonReply","z","z","z","jsonReply","z","jsonReply","z"]}