Skip to main content
Glama

RunPod MCP Server

by runpod
index.ts20 kB
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { z } from 'zod'; import fetch, { type RequestInit as NodeFetchRequestInit } 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?: Record<string, unknown> ) { const url = `${API_BASE_URL}${endpoint}`; const headers = { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json', }; const options: NodeFetchRequestInit = { 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/runpod/runpod-mcp'

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