Skip to main content
Glama

ServeMyAPI

by Jktfe
import keytar from 'keytar'; import fs from 'fs'; import path from 'path'; const SERVICE_NAME = 'serveMyAPI'; const PERMISSION_MARKER = '_permission_granted'; const STORAGE_DIR = process.env.STORAGE_DIR || '/app/data'; const IS_DOCKER = process.env.DOCKER_ENV === 'true'; /** * Service for securely storing and retrieving API keys * - Uses macOS Keychain on macOS systems * - Falls back to file-based storage in Docker environments */ export class KeychainService { private hasStoredPermissionMarker = false; constructor() { // Check for permission marker on initialization if (!IS_DOCKER) { this.checkPermissionMarker(); } else { this.ensureStorageDirectory(); } } /** * Ensure the storage directory exists for Docker environments */ private ensureStorageDirectory(): void { if (IS_DOCKER) { try { if (!fs.existsSync(STORAGE_DIR)) { fs.mkdirSync(STORAGE_DIR, { recursive: true }); } } catch (error) { console.error('Error creating storage directory:', error); } } } /** * Check if the permission marker exists, which indicates * that the user has previously granted permission */ private async checkPermissionMarker(): Promise<void> { if (IS_DOCKER) return; try { const marker = await keytar.getPassword(SERVICE_NAME, PERMISSION_MARKER); this.hasStoredPermissionMarker = !!marker; if (!this.hasStoredPermissionMarker) { // If no marker exists, create one to consolidate permission requests await keytar.setPassword(SERVICE_NAME, PERMISSION_MARKER, 'true'); this.hasStoredPermissionMarker = true; } } catch (error) { console.error('Error checking permission marker:', error); } } /** * Store an API key * @param name The name/identifier for the API key * @param key The API key to store * @returns Promise that resolves when the key is stored */ async storeKey(name: string, key: string): Promise<void> { if (IS_DOCKER) { return this.storeKeyFile(name, key); } // Ensure permission marker exists before storing key if (!this.hasStoredPermissionMarker) { await this.checkPermissionMarker(); } return keytar.setPassword(SERVICE_NAME, name, key); } /** * Store an API key in a file (Docker fallback) */ private async storeKeyFile(name: string, key: string): Promise<void> { try { const filePath = path.join(STORAGE_DIR, `${name}.key`); fs.writeFileSync(filePath, key, { encoding: 'utf8' }); } catch (error) { console.error(`Error storing key ${name} in file:`, error); throw error; } } /** * Retrieve an API key * @param name The name/identifier of the API key to retrieve * @returns Promise that resolves with the API key or null if not found */ async getKey(name: string): Promise<string | null> { if (IS_DOCKER) { return this.getKeyFile(name); } // Ensure permission marker exists before retrieving key if (!this.hasStoredPermissionMarker) { await this.checkPermissionMarker(); } return keytar.getPassword(SERVICE_NAME, name); } /** * Retrieve an API key from a file (Docker fallback) */ private getKeyFile(name: string): string | null { try { const filePath = path.join(STORAGE_DIR, `${name}.key`); if (fs.existsSync(filePath)) { return fs.readFileSync(filePath, { encoding: 'utf8' }); } return null; } catch (error) { console.error(`Error retrieving key ${name} from file:`, error); return null; } } /** * Delete an API key * @param name The name/identifier of the API key to delete * @returns Promise that resolves with true if deleted, false otherwise */ async deleteKey(name: string): Promise<boolean> { if (IS_DOCKER) { return this.deleteKeyFile(name); } // Ensure permission marker exists before deleting key if (!this.hasStoredPermissionMarker) { await this.checkPermissionMarker(); } return keytar.deletePassword(SERVICE_NAME, name); } /** * Delete an API key file (Docker fallback) */ private deleteKeyFile(name: string): boolean { try { const filePath = path.join(STORAGE_DIR, `${name}.key`); if (fs.existsSync(filePath)) { fs.unlinkSync(filePath); return true; } return false; } catch (error) { console.error(`Error deleting key ${name} file:`, error); return false; } } /** * List all stored API keys * @returns Promise that resolves with an array of key names */ async listKeys(): Promise<string[]> { if (IS_DOCKER) { return this.listKeyFiles(); } // Ensure permission marker exists before listing keys if (!this.hasStoredPermissionMarker) { await this.checkPermissionMarker(); } const credentials = await keytar.findCredentials(SERVICE_NAME); // Filter out the permission marker from the list of keys return credentials .map(cred => cred.account) .filter(account => account !== PERMISSION_MARKER); } /** * List all stored API key files (Docker fallback) */ private listKeyFiles(): string[] { try { const files = fs.readdirSync(STORAGE_DIR); return files .filter(file => file.endsWith('.key')) .map(file => file.replace(/\.key$/, '')); } catch (error) { console.error('Error listing key files:', error); return []; } } } export default new KeychainService();

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/Jktfe/serveMyAPI'

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