Skip to main content
Glama
settings-manager.ts5.62 kB
import { homedir } from 'os'; import { join } from 'path'; import { promises as fs } from 'fs'; import { GlobalSettings, AutomationJob } from '../types.js'; export class SettingsManager { private settingsPath: string; private settingsDir: string; constructor() { this.settingsDir = join(homedir(), '.spec-workflow-mcp'); this.settingsPath = join(this.settingsDir, 'settings.json'); } /** * Ensure the settings directory exists */ private async ensureSettingsDir(): Promise<void> { try { await fs.mkdir(this.settingsDir, { recursive: true }); } catch (error) { // Directory might already exist, ignore } } /** * Load global settings from file */ async loadSettings(): Promise<GlobalSettings> { await this.ensureSettingsDir(); try { const content = await fs.readFile(this.settingsPath, 'utf-8'); // Handle empty or whitespace-only files const trimmedContent = content.trim(); if (!trimmedContent) { console.error(`[SettingsManager] Warning: ${this.settingsPath} is empty, using default settings`); const defaultSettings = { automationJobs: [], createdAt: new Date().toISOString(), lastModified: new Date().toISOString() }; // Write default settings to file await this.saveSettings(defaultSettings); return defaultSettings; } return JSON.parse(trimmedContent) as GlobalSettings; } catch (error: any) { if (error.code === 'ENOENT') { // File doesn't exist yet, create it with default settings const defaultSettings = { automationJobs: [], createdAt: new Date().toISOString(), lastModified: new Date().toISOString() }; await this.saveSettings(defaultSettings); return defaultSettings; } if (error instanceof SyntaxError) { // JSON parsing error - file is corrupted or invalid console.error(`[SettingsManager] Error: Failed to parse ${this.settingsPath}: ${error.message}`); console.error(`[SettingsManager] The file may be corrupted. Using default settings.`); // Back up the corrupted file try { const backupPath = `${this.settingsPath}.corrupted.${Date.now()}`; await fs.copyFile(this.settingsPath, backupPath); console.error(`[SettingsManager] Corrupted file backed up to: ${backupPath}`); } catch (backupError) { // Ignore backup errors } const defaultSettings = { automationJobs: [], createdAt: new Date().toISOString(), lastModified: new Date().toISOString() }; // Write default settings to file await this.saveSettings(defaultSettings); return defaultSettings; } throw error; } } /** * Save global settings to file atomically */ async saveSettings(settings: GlobalSettings): Promise<void> { await this.ensureSettingsDir(); // Update modification timestamp settings.lastModified = new Date().toISOString(); if (!settings.createdAt) { settings.createdAt = new Date().toISOString(); } const content = JSON.stringify(settings, null, 2); // Write to temporary file first, then rename for atomic operation const tempPath = `${this.settingsPath}.tmp`; await fs.writeFile(tempPath, content, 'utf-8'); await fs.rename(tempPath, this.settingsPath); } /** * Get a specific automation job by ID */ async getJob(jobId: string): Promise<AutomationJob | null> { const settings = await this.loadSettings(); return settings.automationJobs.find(job => job.id === jobId) || null; } /** * Get all automation jobs */ async getAllJobs(): Promise<AutomationJob[]> { const settings = await this.loadSettings(); return settings.automationJobs; } /** * Add a new automation job */ async addJob(job: AutomationJob): Promise<void> { const settings = await this.loadSettings(); // Check for duplicate ID if (settings.automationJobs.some(j => j.id === job.id)) { throw new Error(`Job with ID ${job.id} already exists`); } settings.automationJobs.push(job); await this.saveSettings(settings); } /** * Update an existing automation job */ async updateJob(jobId: string, updates: Partial<AutomationJob>): Promise<void> { const settings = await this.loadSettings(); const jobIndex = settings.automationJobs.findIndex(j => j.id === jobId); if (jobIndex === -1) { throw new Error(`Job with ID ${jobId} not found`); } // Merge updates, but don't allow changing ID or type settings.automationJobs[jobIndex] = { ...settings.automationJobs[jobIndex], ...updates, id: settings.automationJobs[jobIndex].id, type: settings.automationJobs[jobIndex].type }; await this.saveSettings(settings); } /** * Delete an automation job */ async deleteJob(jobId: string): Promise<void> { const settings = await this.loadSettings(); const originalLength = settings.automationJobs.length; settings.automationJobs = settings.automationJobs.filter(j => j.id !== jobId); if (settings.automationJobs.length === originalLength) { throw new Error(`Job with ID ${jobId} not found`); } await this.saveSettings(settings); } /** * Get the settings file path */ getSettingsPath(): string { return this.settingsPath; } /** * Get the settings directory path */ getSettingsDir(): string { return this.settingsDir; } }

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/Pimzino/spec-workflow-mcp'

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