Skip to main content
Glama

OPNSense MCP Server

storage.ts6.05 kB
import { promises as fs } from 'fs'; import path from 'path'; import { MacroRecording, IMacroStorage } from './types.js'; export class MacroStorage implements IMacroStorage { private storagePath: string; constructor(storagePath?: string) { this.storagePath = storagePath || path.join(process.cwd(), '.opnsense-macros'); } async ensureStorageExists(): Promise<void> { try { await fs.access(this.storagePath); } catch { await fs.mkdir(this.storagePath, { recursive: true }); } } async save(macro: MacroRecording): Promise<void> { await this.ensureStorageExists(); const filePath = path.join(this.storagePath, `${macro.id}.json`); const data = JSON.stringify(macro, null, 2); await fs.writeFile(filePath, data, 'utf8'); // Also update the index await this.updateIndex(macro); } async load(id: string): Promise<MacroRecording | null> { try { const filePath = path.join(this.storagePath, `${id}.json`); const data = await fs.readFile(filePath, 'utf8'); const macro = JSON.parse(data); // Convert date strings back to Date objects macro.created = new Date(macro.created); macro.updated = new Date(macro.updated); return macro; } catch { return null; } } async list(): Promise<MacroRecording[]> { await this.ensureStorageExists(); try { const indexPath = path.join(this.storagePath, 'index.json'); const data = await fs.readFile(indexPath, 'utf8'); const index = JSON.parse(data); // Load each macro const macros: MacroRecording[] = []; for (const entry of index.macros) { const macro = await this.load(entry.id); if (macro) { macros.push(macro); } } return macros; } catch { // No index or error reading it - scan directory return await this.scanDirectory(); } } async delete(id: string): Promise<void> { const filePath = path.join(this.storagePath, `${id}.json`); try { await fs.unlink(filePath); await this.removeFromIndex(id); } catch { // File doesn't exist or can't be deleted } } async search(query: { name?: string; tags?: string[]; category?: string; }): Promise<MacroRecording[]> { const allMacros = await this.list(); return allMacros.filter(macro => { if (query.name && !macro.name.toLowerCase().includes(query.name.toLowerCase())) { return false; } if (query.category && macro.metadata.category !== query.category) { return false; } if (query.tags && query.tags.length > 0) { const macroTags = macro.metadata.tags || []; const hasAllTags = query.tags.every(tag => macroTags.includes(tag)); if (!hasAllTags) { return false; } } return true; }); } // Helper methods private async updateIndex(macro: MacroRecording): Promise<void> { const indexPath = path.join(this.storagePath, 'index.json'); let index: any; try { const data = await fs.readFile(indexPath, 'utf8'); index = JSON.parse(data); } catch { index = { macros: [] }; } // Remove existing entry if any index.macros = index.macros.filter((m: any) => m.id !== macro.id); // Add new entry index.macros.push({ id: macro.id, name: macro.name, description: macro.description, created: macro.created, updated: macro.updated, category: macro.metadata.category, tags: macro.metadata.tags || [] }); // Sort by updated date index.macros.sort((a: any, b: any) => new Date(b.updated).getTime() - new Date(a.updated).getTime() ); await fs.writeFile(indexPath, JSON.stringify(index, null, 2), 'utf8'); } private async removeFromIndex(id: string): Promise<void> { const indexPath = path.join(this.storagePath, 'index.json'); try { const data = await fs.readFile(indexPath, 'utf8'); const index = JSON.parse(data); index.macros = index.macros.filter((m: any) => m.id !== id); await fs.writeFile(indexPath, JSON.stringify(index, null, 2), 'utf8'); } catch { // No index to update } } private async scanDirectory(): Promise<MacroRecording[]> { await this.ensureStorageExists(); const files = await fs.readdir(this.storagePath); const macros: MacroRecording[] = []; for (const file of files) { if (file.endsWith('.json') && file !== 'index.json') { const id = file.replace('.json', ''); const macro = await this.load(id); if (macro) { macros.push(macro); } } } // Sort by updated date macros.sort((a, b) => b.updated.getTime() - a.updated.getTime()); return macros; } /** * Export all macros to a single file */ async exportAll(exportPath: string): Promise<void> { const macros = await this.list(); const exportData = { version: '1.0', exported: new Date().toISOString(), macros }; await fs.writeFile(exportPath, JSON.stringify(exportData, null, 2), 'utf8'); } /** * Import macros from an export file */ async importAll(importPath: string, overwrite: boolean = false): Promise<number> { const data = await fs.readFile(importPath, 'utf8'); const importData = JSON.parse(data); let imported = 0; for (const macro of importData.macros) { // Check if macro already exists if (!overwrite) { const existing = await this.load(macro.id); if (existing) { continue; } } // Convert date strings back to Date objects macro.created = new Date(macro.created); macro.updated = new Date(macro.updated); await this.save(macro); imported++; } return imported; } }

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/vespo92/OPNSenseMCP'

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