Skip to main content
Glama

RunPod MCP Server

by Niel-RunPod
index.ts19.8 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" import { z } from "zod" import fetch from "node-fetch" // Base URL for RunPod API const API_BASE_URL = "https://rest.runpod.io/v1" // Get API key from environment variable const API_KEY = process.env.RUNPOD_API_KEY if (!API_KEY) { console.error("RUNPOD_API_KEY environment variable is required") process.exit(1) } // Create an MCP server const server = new McpServer({ name: "RunPod API Server", version: "1.0.0", capabilities: { resources: {}, tools: {}, }, }) // Helper function to make authenticated API requests to RunPod async function runpodRequest( endpoint: string, method: string = "GET", body?: any ) { const url = `${API_BASE_URL}${endpoint}` const headers = { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json", } const options: any = { method, headers, } if (body && (method === "POST" || method === "PATCH")) { options.body = JSON.stringify(body) } try { const response = await fetch(url, options) if (!response.ok) { const errorText = await response.text() throw new Error(`RunPod API Error: ${response.status} - ${errorText}`) } // Some endpoints might not return JSON const contentType = response.headers.get("content-type") if (contentType && contentType.includes("application/json")) { return await response.json() } return { success: true, status: response.status } } catch (error) { console.error("Error calling RunPod API:", error) throw error } } // ============== POD MANAGEMENT TOOLS ============== // List Pods server.tool( "list-pods", { computeType: z .enum(["GPU", "CPU"]) .optional() .describe("Filter to only GPU or only CPU Pods"), gpuTypeId: z .array(z.string()) .optional() .describe("Filter to Pods with any of the listed GPU types"), dataCenterId: z .array(z.string()) .optional() .describe("Filter to Pods in any of the provided data centers"), name: z .string() .optional() .describe("Filter to Pods with the provided name"), includeMachine: z .boolean() .optional() .describe("Include information about the machine"), includeNetworkVolume: z .boolean() .optional() .describe("Include information about attached network volumes"), }, async (params) => { // Construct query parameters const queryParams = new URLSearchParams() if (params.computeType) queryParams.append("computeType", params.computeType) if (params.gpuTypeId) params.gpuTypeId.forEach((type) => queryParams.append("gpuTypeId", type)) if (params.dataCenterId) params.dataCenterId.forEach((dc) => queryParams.append("dataCenterId", dc) ) if (params.name) queryParams.append("name", params.name) if (params.includeMachine) queryParams.append("includeMachine", params.includeMachine.toString()) if (params.includeNetworkVolume) queryParams.append( "includeNetworkVolume", params.includeNetworkVolume.toString() ) const queryString = queryParams.toString() ? `?${queryParams.toString()}` : "" const result = await runpodRequest(`/pods${queryString}`) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Get Pod Details server.tool( "get-pod", { podId: z.string().describe("ID of the pod to retrieve"), includeMachine: z .boolean() .optional() .describe("Include information about the machine"), includeNetworkVolume: z .boolean() .optional() .describe("Include information about attached network volumes"), }, async (params) => { // Construct query parameters const queryParams = new URLSearchParams() if (params.includeMachine) queryParams.append("includeMachine", params.includeMachine.toString()) if (params.includeNetworkVolume) queryParams.append( "includeNetworkVolume", params.includeNetworkVolume.toString() ) const queryString = queryParams.toString() ? `?${queryParams.toString()}` : "" const result = await runpodRequest(`/pods/${params.podId}${queryString}`) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Create Pod server.tool( "create-pod", { name: z.string().optional().describe("Name for the pod"), imageName: z.string().describe("Docker image to use"), cloudType: z .enum(["SECURE", "COMMUNITY"]) .optional() .describe("SECURE or COMMUNITY cloud"), gpuTypeIds: z .array(z.string()) .optional() .describe("List of acceptable GPU types"), gpuCount: z.number().optional().describe("Number of GPUs"), containerDiskInGb: z .number() .optional() .describe("Container disk size in GB"), volumeInGb: z.number().optional().describe("Volume size in GB"), volumeMountPath: z.string().optional().describe("Path to mount the volume"), ports: z .array(z.string()) .optional() .describe("Ports to expose (e.g., '8888/http', '22/tcp')"), env: z.record(z.string()).optional().describe("Environment variables"), dataCenterIds: z .array(z.string()) .optional() .describe("List of data centers"), }, async (params) => { const result = await runpodRequest("/pods", "POST", params) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Update Pod server.tool( "update-pod", { podId: z.string().describe("ID of the pod to update"), name: z.string().optional().describe("New name for the pod"), imageName: z.string().optional().describe("New Docker image"), containerDiskInGb: z .number() .optional() .describe("New container disk size in GB"), volumeInGb: z.number().optional().describe("New volume size in GB"), volumeMountPath: z .string() .optional() .describe("New path to mount the volume"), ports: z.array(z.string()).optional().describe("New ports to expose"), env: z.record(z.string()).optional().describe("New environment variables"), }, async (params) => { const { podId, ...updateParams } = params const result = await runpodRequest(`/pods/${podId}`, "PATCH", updateParams) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Start Pod server.tool( "start-pod", { podId: z.string().describe("ID of the pod to start"), }, async (params) => { const result = await runpodRequest(`/pods/${params.podId}/start`, "POST") return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Stop Pod server.tool( "stop-pod", { podId: z.string().describe("ID of the pod to stop"), }, async (params) => { const result = await runpodRequest(`/pods/${params.podId}/stop`, "POST") return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Delete Pod server.tool( "delete-pod", { podId: z.string().describe("ID of the pod to delete"), }, async (params) => { const result = await runpodRequest(`/pods/${params.podId}`, "DELETE") return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // ============== ENDPOINT MANAGEMENT TOOLS ============== // List Endpoints server.tool( "list-endpoints", { includeTemplate: z .boolean() .optional() .describe("Include template information"), includeWorkers: z .boolean() .optional() .describe("Include information about workers"), }, async (params) => { // Construct query parameters const queryParams = new URLSearchParams() if (params.includeTemplate) queryParams.append("includeTemplate", params.includeTemplate.toString()) if (params.includeWorkers) queryParams.append("includeWorkers", params.includeWorkers.toString()) const queryString = queryParams.toString() ? `?${queryParams.toString()}` : "" const result = await runpodRequest(`/endpoints${queryString}`) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Get Endpoint Details server.tool( "get-endpoint", { endpointId: z.string().describe("ID of the endpoint to retrieve"), includeTemplate: z .boolean() .optional() .describe("Include template information"), includeWorkers: z .boolean() .optional() .describe("Include information about workers"), }, async (params) => { // Construct query parameters const queryParams = new URLSearchParams() if (params.includeTemplate) queryParams.append("includeTemplate", params.includeTemplate.toString()) if (params.includeWorkers) queryParams.append("includeWorkers", params.includeWorkers.toString()) const queryString = queryParams.toString() ? `?${queryParams.toString()}` : "" const result = await runpodRequest( `/endpoints/${params.endpointId}${queryString}` ) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Create Endpoint server.tool( "create-endpoint", { name: z.string().optional().describe("Name for the endpoint"), templateId: z.string().describe("Template ID to use"), computeType: z .enum(["GPU", "CPU"]) .optional() .describe("GPU or CPU endpoint"), gpuTypeIds: z .array(z.string()) .optional() .describe("List of acceptable GPU types"), gpuCount: z.number().optional().describe("Number of GPUs per worker"), workersMin: z.number().optional().describe("Minimum number of workers"), workersMax: z.number().optional().describe("Maximum number of workers"), dataCenterIds: z .array(z.string()) .optional() .describe("List of data centers"), }, async (params) => { const result = await runpodRequest("/endpoints", "POST", params) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Update Endpoint server.tool( "update-endpoint", { endpointId: z.string().describe("ID of the endpoint to update"), name: z.string().optional().describe("New name for the endpoint"), workersMin: z.number().optional().describe("New minimum number of workers"), workersMax: z.number().optional().describe("New maximum number of workers"), idleTimeout: z.number().optional().describe("New idle timeout in seconds"), scalerType: z .enum(["QUEUE_DELAY", "REQUEST_COUNT"]) .optional() .describe("Scaler type"), scalerValue: z.number().optional().describe("Scaler value"), }, async (params) => { const { endpointId, ...updateParams } = params const result = await runpodRequest( `/endpoints/${endpointId}`, "PATCH", updateParams ) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Delete Endpoint server.tool( "delete-endpoint", { endpointId: z.string().describe("ID of the endpoint to delete"), }, async (params) => { const result = await runpodRequest( `/endpoints/${params.endpointId}`, "DELETE" ) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // ============== TEMPLATE MANAGEMENT TOOLS ============== // List Templates server.tool("list-templates", {}, async () => { const result = await runpodRequest("/templates") return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } }) // Get Template Details server.tool( "get-template", { templateId: z.string().describe("ID of the template to retrieve"), }, async (params) => { const result = await runpodRequest(`/templates/${params.templateId}`) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Create Template server.tool( "create-template", { name: z.string().describe("Name for the template"), imageName: z.string().describe("Docker image to use"), isServerless: z .boolean() .optional() .describe("Is this a serverless template"), ports: z.array(z.string()).optional().describe("Ports to expose"), dockerEntrypoint: z .array(z.string()) .optional() .describe("Docker entrypoint commands"), dockerStartCmd: z .array(z.string()) .optional() .describe("Docker start commands"), env: z.record(z.string()).optional().describe("Environment variables"), containerDiskInGb: z .number() .optional() .describe("Container disk size in GB"), volumeInGb: z.number().optional().describe("Volume size in GB"), volumeMountPath: z.string().optional().describe("Path to mount the volume"), readme: z.string().optional().describe("README content in markdown format"), }, async (params) => { const result = await runpodRequest("/templates", "POST", params) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Update Template server.tool( "update-template", { templateId: z.string().describe("ID of the template to update"), name: z.string().optional().describe("New name for the template"), imageName: z.string().optional().describe("New Docker image"), ports: z.array(z.string()).optional().describe("New ports to expose"), env: z.record(z.string()).optional().describe("New environment variables"), readme: z .string() .optional() .describe("New README content in markdown format"), }, async (params) => { const { templateId, ...updateParams } = params const result = await runpodRequest( `/templates/${templateId}`, "PATCH", updateParams ) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Delete Template server.tool( "delete-template", { templateId: z.string().describe("ID of the template to delete"), }, async (params) => { const result = await runpodRequest( `/templates/${params.templateId}`, "DELETE" ) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // ============== NETWORK VOLUME MANAGEMENT TOOLS ============== // List Network Volumes server.tool("list-network-volumes", {}, async () => { const result = await runpodRequest("/networkvolumes") return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } }) // Get Network Volume Details server.tool( "get-network-volume", { networkVolumeId: z .string() .describe("ID of the network volume to retrieve"), }, async (params) => { const result = await runpodRequest( `/networkvolumes/${params.networkVolumeId}` ) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Create Network Volume server.tool( "create-network-volume", { name: z.string().describe("Name for the network volume"), size: z.number().describe("Size in GB (1-4000)"), dataCenterId: z.string().describe("Data center ID"), }, async (params) => { const result = await runpodRequest("/networkvolumes", "POST", params) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Update Network Volume server.tool( "update-network-volume", { networkVolumeId: z.string().describe("ID of the network volume to update"), name: z.string().optional().describe("New name for the network volume"), size: z .number() .optional() .describe("New size in GB (must be larger than current)"), }, async (params) => { const { networkVolumeId, ...updateParams } = params const result = await runpodRequest( `/networkvolumes/${networkVolumeId}`, "PATCH", updateParams ) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Delete Network Volume server.tool( "delete-network-volume", { networkVolumeId: z.string().describe("ID of the network volume to delete"), }, async (params) => { const result = await runpodRequest( `/networkvolumes/${params.networkVolumeId}`, "DELETE" ) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // ============== CONTAINER REGISTRY AUTH TOOLS ============== // List Container Registry Auths server.tool("list-container-registry-auths", {}, async () => { const result = await runpodRequest("/containerregistryauth") return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } }) // Get Container Registry Auth Details server.tool( "get-container-registry-auth", { containerRegistryAuthId: z .string() .describe("ID of the container registry auth to retrieve"), }, async (params) => { const result = await runpodRequest( `/containerregistryauth/${params.containerRegistryAuthId}` ) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Create Container Registry Auth server.tool( "create-container-registry-auth", { name: z.string().describe("Name for the container registry auth"), username: z.string().describe("Registry username"), password: z.string().describe("Registry password"), }, async (params) => { const result = await runpodRequest("/containerregistryauth", "POST", params) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Delete Container Registry Auth server.tool( "delete-container-registry-auth", { containerRegistryAuthId: z .string() .describe("ID of the container registry auth to delete"), }, async (params) => { const result = await runpodRequest( `/containerregistryauth/${params.containerRegistryAuthId}`, "DELETE" ) return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], } } ) // Start receiving messages on stdin and sending messages on stdout const transport = new StdioServerTransport() server.connect(transport)

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Niel-RunPod/MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server