Skip to main content
Glama

Superjolt MCP Server

by scoritz
service-list.command.ts•6.56 kB
import { Command, Option } from 'nest-commander'; import { ApiService } from '../services/api.service'; import { AuthService } from '../services/auth.service'; import { LoggerService } from '../services/logger.service'; import { Injectable } from '@nestjs/common'; import { AuthenticatedCommand } from './authenticated.command'; import { createResourceTable, formatStatus, formatDate, formatBytes, } from '../utils/table.utils'; import chalk from 'chalk'; interface ServiceListOptions { machineId?: string; } @Injectable() @Command({ name: 'service:list', aliases: ['list', 'services'], description: 'List services for a machine', }) export class ServiceListCommand extends AuthenticatedCommand { constructor( protected readonly apiService: ApiService, protected readonly authService: AuthService, protected readonly logger: LoggerService, ) { super(); } protected async execute( passedParams: string[], options: ServiceListOptions, ): Promise<void> { try { const machineId = options.machineId || passedParams[0]; if (machineId) { this.logger.log( chalk.dim(`Fetching services for machine: ${machineId}...`), ); } else { this.logger.log(chalk.dim('Fetching services...')); } const response = await this.apiService.listServices(machineId); // Check if machine selection is needed if (response.needsSelection) { this.logger.log( chalk.cyan('\nšŸ–„ļø Multiple machines available. Please select one:'), ); const machines = response.availableMachines; // Create selection table const selectionTable = createResourceTable([ '#', 'ID', 'Name', 'Status', ]); machines.forEach((machine: any, index: number) => { selectionTable.push([ chalk.yellow(`${index + 1}`), machine.id, machine.name, formatStatus(machine.status || 'unknown'), ]); }); this.logger.log(selectionTable.toString()); // Prompt for selection const readline = require('readline'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); const selection = await new Promise<number>((resolve) => { rl.question( chalk.cyan('\nSelect a machine (enter number): '), (answer) => { rl.close(); resolve(parseInt(answer)); }, ); }); if (selection < 1 || selection > machines.length) { this.logger.error(chalk.red('Invalid selection')); process.exit(1); } const selectedMachine = machines[selection - 1]; this.logger.log( chalk.dim( `\nFetching services for machine: ${selectedMachine.id}...`, ), ); // Retry with selected machine const retryResponse = await this.apiService.listServices( selectedMachine.id, ); Object.assign(response, retryResponse); } if (response.total === 0) { this.logger.log(chalk.yellow('\nNo services found for this machine.')); this.logger.log(chalk.dim('\nDeploy a service with:')); this.logger.log(chalk.cyan(' superjolt deploy')); return; } const displayMachineId = response.machineId || machineId; this.logger.log( chalk.cyan(`\nServices for machine ${displayMachineId}:`), ); // Create the table const table = createResourceTable( ['ID', 'Name', 'Status', 'CPU %', 'Memory', 'Created'], { wordWrap: true, wrapOnWordBoundary: false, }, ); // Sort services - running first const sortedServices = [...response.services].sort((a, b) => { const aRunning = (a.state || a.status || '').toLowerCase() === 'running' ? 0 : 1; const bRunning = (b.state || b.status || '').toLowerCase() === 'running' ? 0 : 1; return aRunning - bRunning; }); // Add rows sortedServices.forEach((service) => { // Use state for the indicator, and show the status text if available const state = service.state || 'unknown'; const statusText = service.status && service.status !== state ? `${formatStatus(state)} (${service.status})` : formatStatus(state); // Format metrics const cpuUsage = service.stats?.cpuPercent !== undefined ? `${service.stats.cpuPercent.toFixed(1)}%` : '-'; const memoryUsage = service.stats?.memoryUsage !== undefined ? formatBytes(service.stats.memoryUsage) : '-'; // Format name with URL const nameWithUrl = service.url ? `${service.name}\n${chalk.dim(service.url)}` : service.name; table.push([ service.id, nameWithUrl, statusText, cpuUsage, memoryUsage, formatDate(service.createdAt || new Date().toISOString()), ]); }); this.logger.log(table.toString()); // Summary const statusCounts = response.services.reduce( (acc: Record<string, number>, s: any) => { const state = (s.state || s.status || 'unknown').toLowerCase(); acc[state] = (acc[state] || 0) + 1; return acc; }, {} as Record<string, number>, ); let summary = chalk.dim( `\nTotal: ${response.total} service${response.total === 1 ? '' : 's'}`, ); const statusParts: string[] = []; if (statusCounts.running) statusParts.push(`${statusCounts.running} running`); if (statusCounts.stopped) statusParts.push(`${statusCounts.stopped} stopped`); if (statusCounts.deployed) statusParts.push(`${statusCounts.deployed} deployed`); if (statusCounts.unknown) statusParts.push(`${statusCounts.unknown} pending`); if (statusParts.length > 0) { summary += chalk.dim(` (${statusParts.join(', ')})`); } this.logger.log(summary); } catch (error: any) { this.logger.error(`\n${error.message}`); process.exit(1); } } @Option({ flags: '-m, --machineId <machineId>', description: 'Machine ID to list services for', }) parseMachineId(val: string): string { return val; } }

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/scoritz/superjolt'

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