#
tokens: 8878/50000 8/8 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .cursor
│   └── mcp.json
├── .gitignore
├── build
│   └── index.js
├── Dockerfile
├── LICENSE
├── package.json
├── README.md
├── smithery.yaml
├── src
│   └── index.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
1 | /node_modules
2 | /package-lock.json
3 | .env
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # RunPod MCP Server
  2 | [![smithery badge](https://smithery.ai/badge/@runpod/runpod-mcp-ts)](https://smithery.ai/server/@runpod/runpod-mcp-ts)
  3 | 
  4 | This Model Context Protocol (MCP) server enables you to interact with the RunPod REST API through Claude or other MCP-compatible clients.
  5 | 
  6 | ## Features
  7 | 
  8 | The server provides tools for managing:
  9 | 
 10 | - **Pods**: Create, list, get details, update, start, stop, and delete pods
 11 | - **Endpoints**: Create, list, get details, update, and delete serverless endpoints
 12 | - **Templates**: Create, list, get details, update, and delete templates
 13 | - **Network Volumes**: Create, list, get details, update, and delete network volumes
 14 | - **Container Registry Authentications**: Create, list, get details, and delete authentications
 15 | 
 16 | ## Setup
 17 | 
 18 | ### Prerequisites
 19 | 
 20 | - Node.js 18 or higher
 21 | - A RunPod account and API key
 22 | - Claude for Desktop or another MCP-compatible client
 23 | 
 24 | ### Installing via Smithery
 25 | 
 26 | To install runpod-mcp-ts for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@runpod/runpod-mcp-ts):
 27 | 
 28 | ```bash
 29 | npx -y @smithery/cli install @runpod/runpod-mcp-ts --client claude
 30 | ```
 31 | 
 32 | ### Installation
 33 | 
 34 | 1. Clone the repository
 35 | 2. Install dependencies:
 36 |    ```
 37 |    npm install
 38 |    ```
 39 | 3. Build the server:
 40 |    ```
 41 |    npm run build
 42 |    ```
 43 | 
 44 | ### Configuration
 45 | 
 46 | Set your RunPod API key as an environment variable:
 47 | 
 48 | ```bash
 49 | # Linux/macOS
 50 | export RUNPOD_API_KEY=your_api_key_here
 51 | 
 52 | # Windows (Command Prompt)
 53 | set RUNPOD_API_KEY=your_api_key_here
 54 | 
 55 | # Windows (PowerShell)
 56 | $env:RUNPOD_API_KEY="your_api_key_here"
 57 | ```
 58 | 
 59 | You can get your API key from the [RunPod console](https://www.runpod.io/console/user/settings).
 60 | 
 61 | ### Running the Server
 62 | 
 63 | Start the server:
 64 | 
 65 | ```bash
 66 | npm start
 67 | ```
 68 | 
 69 | ## Setting up with Claude for Desktop
 70 | 
 71 | 1. Open Claude for Desktop
 72 | 2. Edit the config file: `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows)
 73 | 3. Add the server configuration:
 74 | 
 75 | ```json
 76 | {
 77 |   "mcpServers": {
 78 |     "runpod": {
 79 |       "command": "node",
 80 |       "args": ["/path/to/runpod-mcp-server/build/index.js"],
 81 |       "env": {
 82 |         "RUNPOD_API_KEY": "your_api_key_here"
 83 |       }
 84 |     }
 85 |   }
 86 | }
 87 | ```
 88 | 
 89 | Make sure to replace the `"args": ["/path/to/runpod-mcp-server/build/index.js"]` with the path to the build folder in the repository.
 90 | 
 91 | 4. Restart Claude for Desktop
 92 | 
 93 | ## Usage Examples
 94 | 
 95 | Here are some examples of how to use the server with Claude:
 96 | 
 97 | ### List all pods
 98 | 
 99 | ```
100 | Can you list all my RunPod pods?
101 | ```
102 | 
103 | ### Create a new pod
104 | 
105 | ```
106 | Create a new RunPod pod with the following specifications:
107 | - Name: test-pod
108 | - Image: runpod/pytorch:2.1.0-py3.10-cuda11.8.0-devel-ubuntu22.04
109 | - GPU Type: NVIDIA GeForce RTX 4090
110 | - GPU Count: 1
111 | ```
112 | 
113 | ### Create a serverless endpoint
114 | 
115 | ```
116 | Create a RunPod serverless endpoint with the following configuration:
117 | - Name: my-endpoint
118 | - Template ID: 30zmvf89kd
119 | - Minimum workers: 0
120 | - Maximum workers: 3
121 | ```
122 | 
123 | ## Security Considerations
124 | 
125 | This server requires your RunPod API key, which grants full access to your RunPod account. For security:
126 | 
127 | - Never share your API key
128 | - Be cautious about what operations you perform
129 | - Consider setting up a separate API key with limited permissions
130 | - Don't use this in a production environment without proper security measures
131 | 
132 | ## License
133 | 
134 | MIT
135 | 
```

--------------------------------------------------------------------------------
/.cursor/mcp.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "mcpServers": {
 3 |     "runpod": {
 4 |       "command": "node",
 5 |       "args": ["your/path/to/runpod-mcp-ts/build/index.js"],
 6 |       "env": {
 7 |         "RUNPOD_API_KEY": "your_api_key_here"
 8 |       }
 9 |     }
10 |   }
11 | }
12 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2022",
 4 |     "module": "Node16",
 5 |     "moduleResolution": "Node16",
 6 |     "outDir": "./build",
 7 |     "rootDir": "./src",
 8 |     "strict": true,
 9 |     "esModuleInterop": true,
10 |     "skipLibCheck": true,
11 |     "forceConsistentCasingInFileNames": true
12 |   },
13 |   "include": ["src/**/*"],
14 |   "exclude": ["node_modules"]
15 | }
16 | 
```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
 2 | FROM node:lts-alpine
 3 | 
 4 | # Set working directory
 5 | WORKDIR /app
 6 | 
 7 | # Copy package.json and package-lock.json if available
 8 | COPY package*.json ./
 9 | 
10 | # Install dependencies
11 | RUN npm install --ignore-scripts
12 | 
13 | # Copy the rest of the application
14 | COPY . .
15 | 
16 | # Build the project
17 | RUN npm run build
18 | 
19 | # Expose any necessary port if required (not specified, so leaving out)
20 | 
21 | # Start the server
22 | CMD ["npm", "start"]
23 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "runpod-mcp-server",
 3 |   "version": "1.0.0",
 4 |   "description": "MCP server for interacting with RunPod API",
 5 |   "type": "module",
 6 |   "main": "build/index.js",
 7 |   "scripts": {
 8 |     "build": "tsc",
 9 |     "start": "node build/index.js",
10 |     "dev": "tsx index.ts"
11 |   },
12 |   "dependencies": {
13 |     "@modelcontextprotocol/sdk": "^1.7.0",
14 |     "node-fetch": "^3.3.2",
15 |     "zod": "^3.22.4"
16 |   },
17 |   "devDependencies": {
18 |     "@types/node": "^20.10.5",
19 |     "tsx": "^4.7.0",
20 |     "typescript": "^5.3.3"
21 |   }
22 | }
23 | 
```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
 2 | 
 3 | startCommand:
 4 |   type: stdio
 5 |   configSchema:
 6 |     # JSON Schema defining the configuration options for the MCP.
 7 |     type: object
 8 |     required:
 9 |       - runpodApiKey
10 |     properties:
11 |       runpodApiKey:
12 |         type: string
13 |         description: Your RunPod API key
14 |   commandFunction:
15 |     # A JS function that produces the CLI command based on the given config to start the MCP on stdio.
16 |     |-
17 |     (config) => ({
18 |       command: 'node',
19 |       args: ['build/index.js'],
20 |       env: { RUNPOD_API_KEY: config.runpodApiKey }
21 |     })
22 |   exampleConfig:
23 |     runpodApiKey: your_dummy_runpod_api_key_here
24 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
  2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
  3 | import { z } from "zod"
  4 | import fetch from "node-fetch"
  5 | 
  6 | // Base URL for RunPod API
  7 | const API_BASE_URL = "https://rest.runpod.io/v1"
  8 | 
  9 | // Get API key from environment variable
 10 | const API_KEY = process.env.RUNPOD_API_KEY
 11 | if (!API_KEY) {
 12 |   console.error("RUNPOD_API_KEY environment variable is required")
 13 |   process.exit(1)
 14 | }
 15 | 
 16 | // Create an MCP server
 17 | const server = new McpServer({
 18 |   name: "RunPod API Server",
 19 |   version: "1.0.0",
 20 |   capabilities: {
 21 |     resources: {},
 22 |     tools: {},
 23 |   },
 24 | })
 25 | 
 26 | // Helper function to make authenticated API requests to RunPod
 27 | async function runpodRequest(
 28 |   endpoint: string,
 29 |   method: string = "GET",
 30 |   body?: any
 31 | ) {
 32 |   const url = `${API_BASE_URL}${endpoint}`
 33 |   const headers = {
 34 |     Authorization: `Bearer ${API_KEY}`,
 35 |     "Content-Type": "application/json",
 36 |   }
 37 | 
 38 |   const options: any = {
 39 |     method,
 40 |     headers,
 41 |   }
 42 | 
 43 |   if (body && (method === "POST" || method === "PATCH")) {
 44 |     options.body = JSON.stringify(body)
 45 |   }
 46 | 
 47 |   try {
 48 |     const response = await fetch(url, options)
 49 | 
 50 |     if (!response.ok) {
 51 |       const errorText = await response.text()
 52 |       throw new Error(`RunPod API Error: ${response.status} - ${errorText}`)
 53 |     }
 54 | 
 55 |     // Some endpoints might not return JSON
 56 |     const contentType = response.headers.get("content-type")
 57 |     if (contentType && contentType.includes("application/json")) {
 58 |       return await response.json()
 59 |     }
 60 | 
 61 |     return { success: true, status: response.status }
 62 |   } catch (error) {
 63 |     console.error("Error calling RunPod API:", error)
 64 |     throw error
 65 |   }
 66 | }
 67 | 
 68 | // ============== POD MANAGEMENT TOOLS ==============
 69 | 
 70 | // List Pods
 71 | server.tool(
 72 |   "list-pods",
 73 |   {
 74 |     computeType: z
 75 |       .enum(["GPU", "CPU"])
 76 |       .optional()
 77 |       .describe("Filter to only GPU or only CPU Pods"),
 78 |     gpuTypeId: z
 79 |       .array(z.string())
 80 |       .optional()
 81 |       .describe("Filter to Pods with any of the listed GPU types"),
 82 |     dataCenterId: z
 83 |       .array(z.string())
 84 |       .optional()
 85 |       .describe("Filter to Pods in any of the provided data centers"),
 86 |     name: z
 87 |       .string()
 88 |       .optional()
 89 |       .describe("Filter to Pods with the provided name"),
 90 |     includeMachine: z
 91 |       .boolean()
 92 |       .optional()
 93 |       .describe("Include information about the machine"),
 94 |     includeNetworkVolume: z
 95 |       .boolean()
 96 |       .optional()
 97 |       .describe("Include information about attached network volumes"),
 98 |   },
 99 |   async (params) => {
100 |     // Construct query parameters
101 |     const queryParams = new URLSearchParams()
102 | 
103 |     if (params.computeType)
104 |       queryParams.append("computeType", params.computeType)
105 |     if (params.gpuTypeId)
106 |       params.gpuTypeId.forEach((type) => queryParams.append("gpuTypeId", type))
107 |     if (params.dataCenterId)
108 |       params.dataCenterId.forEach((dc) =>
109 |         queryParams.append("dataCenterId", dc)
110 |       )
111 |     if (params.name) queryParams.append("name", params.name)
112 |     if (params.includeMachine)
113 |       queryParams.append("includeMachine", params.includeMachine.toString())
114 |     if (params.includeNetworkVolume)
115 |       queryParams.append(
116 |         "includeNetworkVolume",
117 |         params.includeNetworkVolume.toString()
118 |       )
119 | 
120 |     const queryString = queryParams.toString()
121 |       ? `?${queryParams.toString()}`
122 |       : ""
123 |     const result = await runpodRequest(`/pods${queryString}`)
124 | 
125 |     return {
126 |       content: [
127 |         {
128 |           type: "text",
129 |           text: JSON.stringify(result, null, 2),
130 |         },
131 |       ],
132 |     }
133 |   }
134 | )
135 | 
136 | // Get Pod Details
137 | server.tool(
138 |   "get-pod",
139 |   {
140 |     podId: z.string().describe("ID of the pod to retrieve"),
141 |     includeMachine: z
142 |       .boolean()
143 |       .optional()
144 |       .describe("Include information about the machine"),
145 |     includeNetworkVolume: z
146 |       .boolean()
147 |       .optional()
148 |       .describe("Include information about attached network volumes"),
149 |   },
150 |   async (params) => {
151 |     // Construct query parameters
152 |     const queryParams = new URLSearchParams()
153 | 
154 |     if (params.includeMachine)
155 |       queryParams.append("includeMachine", params.includeMachine.toString())
156 |     if (params.includeNetworkVolume)
157 |       queryParams.append(
158 |         "includeNetworkVolume",
159 |         params.includeNetworkVolume.toString()
160 |       )
161 | 
162 |     const queryString = queryParams.toString()
163 |       ? `?${queryParams.toString()}`
164 |       : ""
165 |     const result = await runpodRequest(`/pods/${params.podId}${queryString}`)
166 | 
167 |     return {
168 |       content: [
169 |         {
170 |           type: "text",
171 |           text: JSON.stringify(result, null, 2),
172 |         },
173 |       ],
174 |     }
175 |   }
176 | )
177 | 
178 | // Create Pod
179 | server.tool(
180 |   "create-pod",
181 |   {
182 |     name: z.string().optional().describe("Name for the pod"),
183 |     imageName: z.string().describe("Docker image to use"),
184 |     cloudType: z
185 |       .enum(["SECURE", "COMMUNITY"])
186 |       .optional()
187 |       .describe("SECURE or COMMUNITY cloud"),
188 |     gpuTypeIds: z
189 |       .array(z.string())
190 |       .optional()
191 |       .describe("List of acceptable GPU types"),
192 |     gpuCount: z.number().optional().describe("Number of GPUs"),
193 |     containerDiskInGb: z
194 |       .number()
195 |       .optional()
196 |       .describe("Container disk size in GB"),
197 |     volumeInGb: z.number().optional().describe("Volume size in GB"),
198 |     volumeMountPath: z.string().optional().describe("Path to mount the volume"),
199 |     ports: z
200 |       .array(z.string())
201 |       .optional()
202 |       .describe("Ports to expose (e.g., '8888/http', '22/tcp')"),
203 |     env: z.record(z.string()).optional().describe("Environment variables"),
204 |     dataCenterIds: z
205 |       .array(z.string())
206 |       .optional()
207 |       .describe("List of data centers"),
208 |   },
209 |   async (params) => {
210 |     const result = await runpodRequest("/pods", "POST", params)
211 | 
212 |     return {
213 |       content: [
214 |         {
215 |           type: "text",
216 |           text: JSON.stringify(result, null, 2),
217 |         },
218 |       ],
219 |     }
220 |   }
221 | )
222 | 
223 | // Update Pod
224 | server.tool(
225 |   "update-pod",
226 |   {
227 |     podId: z.string().describe("ID of the pod to update"),
228 |     name: z.string().optional().describe("New name for the pod"),
229 |     imageName: z.string().optional().describe("New Docker image"),
230 |     containerDiskInGb: z
231 |       .number()
232 |       .optional()
233 |       .describe("New container disk size in GB"),
234 |     volumeInGb: z.number().optional().describe("New volume size in GB"),
235 |     volumeMountPath: z
236 |       .string()
237 |       .optional()
238 |       .describe("New path to mount the volume"),
239 |     ports: z.array(z.string()).optional().describe("New ports to expose"),
240 |     env: z.record(z.string()).optional().describe("New environment variables"),
241 |   },
242 |   async (params) => {
243 |     const { podId, ...updateParams } = params
244 |     const result = await runpodRequest(`/pods/${podId}`, "PATCH", updateParams)
245 | 
246 |     return {
247 |       content: [
248 |         {
249 |           type: "text",
250 |           text: JSON.stringify(result, null, 2),
251 |         },
252 |       ],
253 |     }
254 |   }
255 | )
256 | 
257 | // Start Pod
258 | server.tool(
259 |   "start-pod",
260 |   {
261 |     podId: z.string().describe("ID of the pod to start"),
262 |   },
263 |   async (params) => {
264 |     const result = await runpodRequest(`/pods/${params.podId}/start`, "POST")
265 | 
266 |     return {
267 |       content: [
268 |         {
269 |           type: "text",
270 |           text: JSON.stringify(result, null, 2),
271 |         },
272 |       ],
273 |     }
274 |   }
275 | )
276 | 
277 | // Stop Pod
278 | server.tool(
279 |   "stop-pod",
280 |   {
281 |     podId: z.string().describe("ID of the pod to stop"),
282 |   },
283 |   async (params) => {
284 |     const result = await runpodRequest(`/pods/${params.podId}/stop`, "POST")
285 | 
286 |     return {
287 |       content: [
288 |         {
289 |           type: "text",
290 |           text: JSON.stringify(result, null, 2),
291 |         },
292 |       ],
293 |     }
294 |   }
295 | )
296 | 
297 | // Delete Pod
298 | server.tool(
299 |   "delete-pod",
300 |   {
301 |     podId: z.string().describe("ID of the pod to delete"),
302 |   },
303 |   async (params) => {
304 |     const result = await runpodRequest(`/pods/${params.podId}`, "DELETE")
305 | 
306 |     return {
307 |       content: [
308 |         {
309 |           type: "text",
310 |           text: JSON.stringify(result, null, 2),
311 |         },
312 |       ],
313 |     }
314 |   }
315 | )
316 | 
317 | // ============== ENDPOINT MANAGEMENT TOOLS ==============
318 | 
319 | // List Endpoints
320 | server.tool(
321 |   "list-endpoints",
322 |   {
323 |     includeTemplate: z
324 |       .boolean()
325 |       .optional()
326 |       .describe("Include template information"),
327 |     includeWorkers: z
328 |       .boolean()
329 |       .optional()
330 |       .describe("Include information about workers"),
331 |   },
332 |   async (params) => {
333 |     // Construct query parameters
334 |     const queryParams = new URLSearchParams()
335 | 
336 |     if (params.includeTemplate)
337 |       queryParams.append("includeTemplate", params.includeTemplate.toString())
338 |     if (params.includeWorkers)
339 |       queryParams.append("includeWorkers", params.includeWorkers.toString())
340 | 
341 |     const queryString = queryParams.toString()
342 |       ? `?${queryParams.toString()}`
343 |       : ""
344 |     const result = await runpodRequest(`/endpoints${queryString}`)
345 | 
346 |     return {
347 |       content: [
348 |         {
349 |           type: "text",
350 |           text: JSON.stringify(result, null, 2),
351 |         },
352 |       ],
353 |     }
354 |   }
355 | )
356 | 
357 | // Get Endpoint Details
358 | server.tool(
359 |   "get-endpoint",
360 |   {
361 |     endpointId: z.string().describe("ID of the endpoint to retrieve"),
362 |     includeTemplate: z
363 |       .boolean()
364 |       .optional()
365 |       .describe("Include template information"),
366 |     includeWorkers: z
367 |       .boolean()
368 |       .optional()
369 |       .describe("Include information about workers"),
370 |   },
371 |   async (params) => {
372 |     // Construct query parameters
373 |     const queryParams = new URLSearchParams()
374 | 
375 |     if (params.includeTemplate)
376 |       queryParams.append("includeTemplate", params.includeTemplate.toString())
377 |     if (params.includeWorkers)
378 |       queryParams.append("includeWorkers", params.includeWorkers.toString())
379 | 
380 |     const queryString = queryParams.toString()
381 |       ? `?${queryParams.toString()}`
382 |       : ""
383 |     const result = await runpodRequest(
384 |       `/endpoints/${params.endpointId}${queryString}`
385 |     )
386 | 
387 |     return {
388 |       content: [
389 |         {
390 |           type: "text",
391 |           text: JSON.stringify(result, null, 2),
392 |         },
393 |       ],
394 |     }
395 |   }
396 | )
397 | 
398 | // Create Endpoint
399 | server.tool(
400 |   "create-endpoint",
401 |   {
402 |     name: z.string().optional().describe("Name for the endpoint"),
403 |     templateId: z.string().describe("Template ID to use"),
404 |     computeType: z
405 |       .enum(["GPU", "CPU"])
406 |       .optional()
407 |       .describe("GPU or CPU endpoint"),
408 |     gpuTypeIds: z
409 |       .array(z.string())
410 |       .optional()
411 |       .describe("List of acceptable GPU types"),
412 |     gpuCount: z.number().optional().describe("Number of GPUs per worker"),
413 |     workersMin: z.number().optional().describe("Minimum number of workers"),
414 |     workersMax: z.number().optional().describe("Maximum number of workers"),
415 |     dataCenterIds: z
416 |       .array(z.string())
417 |       .optional()
418 |       .describe("List of data centers"),
419 |   },
420 |   async (params) => {
421 |     const result = await runpodRequest("/endpoints", "POST", params)
422 | 
423 |     return {
424 |       content: [
425 |         {
426 |           type: "text",
427 |           text: JSON.stringify(result, null, 2),
428 |         },
429 |       ],
430 |     }
431 |   }
432 | )
433 | 
434 | // Update Endpoint
435 | server.tool(
436 |   "update-endpoint",
437 |   {
438 |     endpointId: z.string().describe("ID of the endpoint to update"),
439 |     name: z.string().optional().describe("New name for the endpoint"),
440 |     workersMin: z.number().optional().describe("New minimum number of workers"),
441 |     workersMax: z.number().optional().describe("New maximum number of workers"),
442 |     idleTimeout: z.number().optional().describe("New idle timeout in seconds"),
443 |     scalerType: z
444 |       .enum(["QUEUE_DELAY", "REQUEST_COUNT"])
445 |       .optional()
446 |       .describe("Scaler type"),
447 |     scalerValue: z.number().optional().describe("Scaler value"),
448 |   },
449 |   async (params) => {
450 |     const { endpointId, ...updateParams } = params
451 |     const result = await runpodRequest(
452 |       `/endpoints/${endpointId}`,
453 |       "PATCH",
454 |       updateParams
455 |     )
456 | 
457 |     return {
458 |       content: [
459 |         {
460 |           type: "text",
461 |           text: JSON.stringify(result, null, 2),
462 |         },
463 |       ],
464 |     }
465 |   }
466 | )
467 | 
468 | // Delete Endpoint
469 | server.tool(
470 |   "delete-endpoint",
471 |   {
472 |     endpointId: z.string().describe("ID of the endpoint to delete"),
473 |   },
474 |   async (params) => {
475 |     const result = await runpodRequest(
476 |       `/endpoints/${params.endpointId}`,
477 |       "DELETE"
478 |     )
479 | 
480 |     return {
481 |       content: [
482 |         {
483 |           type: "text",
484 |           text: JSON.stringify(result, null, 2),
485 |         },
486 |       ],
487 |     }
488 |   }
489 | )
490 | 
491 | // ============== TEMPLATE MANAGEMENT TOOLS ==============
492 | 
493 | // List Templates
494 | server.tool("list-templates", {}, async () => {
495 |   const result = await runpodRequest("/templates")
496 | 
497 |   return {
498 |     content: [
499 |       {
500 |         type: "text",
501 |         text: JSON.stringify(result, null, 2),
502 |       },
503 |     ],
504 |   }
505 | })
506 | 
507 | // Get Template Details
508 | server.tool(
509 |   "get-template",
510 |   {
511 |     templateId: z.string().describe("ID of the template to retrieve"),
512 |   },
513 |   async (params) => {
514 |     const result = await runpodRequest(`/templates/${params.templateId}`)
515 | 
516 |     return {
517 |       content: [
518 |         {
519 |           type: "text",
520 |           text: JSON.stringify(result, null, 2),
521 |         },
522 |       ],
523 |     }
524 |   }
525 | )
526 | 
527 | // Create Template
528 | server.tool(
529 |   "create-template",
530 |   {
531 |     name: z.string().describe("Name for the template"),
532 |     imageName: z.string().describe("Docker image to use"),
533 |     isServerless: z
534 |       .boolean()
535 |       .optional()
536 |       .describe("Is this a serverless template"),
537 |     ports: z.array(z.string()).optional().describe("Ports to expose"),
538 |     dockerEntrypoint: z
539 |       .array(z.string())
540 |       .optional()
541 |       .describe("Docker entrypoint commands"),
542 |     dockerStartCmd: z
543 |       .array(z.string())
544 |       .optional()
545 |       .describe("Docker start commands"),
546 |     env: z.record(z.string()).optional().describe("Environment variables"),
547 |     containerDiskInGb: z
548 |       .number()
549 |       .optional()
550 |       .describe("Container disk size in GB"),
551 |     volumeInGb: z.number().optional().describe("Volume size in GB"),
552 |     volumeMountPath: z.string().optional().describe("Path to mount the volume"),
553 |     readme: z.string().optional().describe("README content in markdown format"),
554 |   },
555 |   async (params) => {
556 |     const result = await runpodRequest("/templates", "POST", params)
557 | 
558 |     return {
559 |       content: [
560 |         {
561 |           type: "text",
562 |           text: JSON.stringify(result, null, 2),
563 |         },
564 |       ],
565 |     }
566 |   }
567 | )
568 | 
569 | // Update Template
570 | server.tool(
571 |   "update-template",
572 |   {
573 |     templateId: z.string().describe("ID of the template to update"),
574 |     name: z.string().optional().describe("New name for the template"),
575 |     imageName: z.string().optional().describe("New Docker image"),
576 |     ports: z.array(z.string()).optional().describe("New ports to expose"),
577 |     env: z.record(z.string()).optional().describe("New environment variables"),
578 |     readme: z
579 |       .string()
580 |       .optional()
581 |       .describe("New README content in markdown format"),
582 |   },
583 |   async (params) => {
584 |     const { templateId, ...updateParams } = params
585 |     const result = await runpodRequest(
586 |       `/templates/${templateId}`,
587 |       "PATCH",
588 |       updateParams
589 |     )
590 | 
591 |     return {
592 |       content: [
593 |         {
594 |           type: "text",
595 |           text: JSON.stringify(result, null, 2),
596 |         },
597 |       ],
598 |     }
599 |   }
600 | )
601 | 
602 | // Delete Template
603 | server.tool(
604 |   "delete-template",
605 |   {
606 |     templateId: z.string().describe("ID of the template to delete"),
607 |   },
608 |   async (params) => {
609 |     const result = await runpodRequest(
610 |       `/templates/${params.templateId}`,
611 |       "DELETE"
612 |     )
613 | 
614 |     return {
615 |       content: [
616 |         {
617 |           type: "text",
618 |           text: JSON.stringify(result, null, 2),
619 |         },
620 |       ],
621 |     }
622 |   }
623 | )
624 | 
625 | // ============== NETWORK VOLUME MANAGEMENT TOOLS ==============
626 | 
627 | // List Network Volumes
628 | server.tool("list-network-volumes", {}, async () => {
629 |   const result = await runpodRequest("/networkvolumes")
630 | 
631 |   return {
632 |     content: [
633 |       {
634 |         type: "text",
635 |         text: JSON.stringify(result, null, 2),
636 |       },
637 |     ],
638 |   }
639 | })
640 | 
641 | // Get Network Volume Details
642 | server.tool(
643 |   "get-network-volume",
644 |   {
645 |     networkVolumeId: z
646 |       .string()
647 |       .describe("ID of the network volume to retrieve"),
648 |   },
649 |   async (params) => {
650 |     const result = await runpodRequest(
651 |       `/networkvolumes/${params.networkVolumeId}`
652 |     )
653 | 
654 |     return {
655 |       content: [
656 |         {
657 |           type: "text",
658 |           text: JSON.stringify(result, null, 2),
659 |         },
660 |       ],
661 |     }
662 |   }
663 | )
664 | 
665 | // Create Network Volume
666 | server.tool(
667 |   "create-network-volume",
668 |   {
669 |     name: z.string().describe("Name for the network volume"),
670 |     size: z.number().describe("Size in GB (1-4000)"),
671 |     dataCenterId: z.string().describe("Data center ID"),
672 |   },
673 |   async (params) => {
674 |     const result = await runpodRequest("/networkvolumes", "POST", params)
675 | 
676 |     return {
677 |       content: [
678 |         {
679 |           type: "text",
680 |           text: JSON.stringify(result, null, 2),
681 |         },
682 |       ],
683 |     }
684 |   }
685 | )
686 | 
687 | // Update Network Volume
688 | server.tool(
689 |   "update-network-volume",
690 |   {
691 |     networkVolumeId: z.string().describe("ID of the network volume to update"),
692 |     name: z.string().optional().describe("New name for the network volume"),
693 |     size: z
694 |       .number()
695 |       .optional()
696 |       .describe("New size in GB (must be larger than current)"),
697 |   },
698 |   async (params) => {
699 |     const { networkVolumeId, ...updateParams } = params
700 |     const result = await runpodRequest(
701 |       `/networkvolumes/${networkVolumeId}`,
702 |       "PATCH",
703 |       updateParams
704 |     )
705 | 
706 |     return {
707 |       content: [
708 |         {
709 |           type: "text",
710 |           text: JSON.stringify(result, null, 2),
711 |         },
712 |       ],
713 |     }
714 |   }
715 | )
716 | 
717 | // Delete Network Volume
718 | server.tool(
719 |   "delete-network-volume",
720 |   {
721 |     networkVolumeId: z.string().describe("ID of the network volume to delete"),
722 |   },
723 |   async (params) => {
724 |     const result = await runpodRequest(
725 |       `/networkvolumes/${params.networkVolumeId}`,
726 |       "DELETE"
727 |     )
728 | 
729 |     return {
730 |       content: [
731 |         {
732 |           type: "text",
733 |           text: JSON.stringify(result, null, 2),
734 |         },
735 |       ],
736 |     }
737 |   }
738 | )
739 | 
740 | // ============== CONTAINER REGISTRY AUTH TOOLS ==============
741 | 
742 | // List Container Registry Auths
743 | server.tool("list-container-registry-auths", {}, async () => {
744 |   const result = await runpodRequest("/containerregistryauth")
745 | 
746 |   return {
747 |     content: [
748 |       {
749 |         type: "text",
750 |         text: JSON.stringify(result, null, 2),
751 |       },
752 |     ],
753 |   }
754 | })
755 | 
756 | // Get Container Registry Auth Details
757 | server.tool(
758 |   "get-container-registry-auth",
759 |   {
760 |     containerRegistryAuthId: z
761 |       .string()
762 |       .describe("ID of the container registry auth to retrieve"),
763 |   },
764 |   async (params) => {
765 |     const result = await runpodRequest(
766 |       `/containerregistryauth/${params.containerRegistryAuthId}`
767 |     )
768 | 
769 |     return {
770 |       content: [
771 |         {
772 |           type: "text",
773 |           text: JSON.stringify(result, null, 2),
774 |         },
775 |       ],
776 |     }
777 |   }
778 | )
779 | 
780 | // Create Container Registry Auth
781 | server.tool(
782 |   "create-container-registry-auth",
783 |   {
784 |     name: z.string().describe("Name for the container registry auth"),
785 |     username: z.string().describe("Registry username"),
786 |     password: z.string().describe("Registry password"),
787 |   },
788 |   async (params) => {
789 |     const result = await runpodRequest("/containerregistryauth", "POST", params)
790 | 
791 |     return {
792 |       content: [
793 |         {
794 |           type: "text",
795 |           text: JSON.stringify(result, null, 2),
796 |         },
797 |       ],
798 |     }
799 |   }
800 | )
801 | 
802 | // Delete Container Registry Auth
803 | server.tool(
804 |   "delete-container-registry-auth",
805 |   {
806 |     containerRegistryAuthId: z
807 |       .string()
808 |       .describe("ID of the container registry auth to delete"),
809 |   },
810 |   async (params) => {
811 |     const result = await runpodRequest(
812 |       `/containerregistryauth/${params.containerRegistryAuthId}`,
813 |       "DELETE"
814 |     )
815 | 
816 |     return {
817 |       content: [
818 |         {
819 |           type: "text",
820 |           text: JSON.stringify(result, null, 2),
821 |         },
822 |       ],
823 |     }
824 |   }
825 | )
826 | 
827 | // Start receiving messages on stdin and sending messages on stdout
828 | const transport = new StdioServerTransport()
829 | server.connect(transport)
830 | 
```