Skip to main content
Glama

MCP Bridge Server

user.js8.59 kB
import { createHash } from 'crypto'; import { promises as fs } from 'fs'; import { join } from 'path'; import { IdentityError, IdentityErrorType } from './types.js'; import { defaultProvider as machineIdProvider } from './platform/index.js'; /** * User identity manager */ export class UserManager { constructor(options) { this.initialized = false; this.storage = options.storage; this.validationRules = options.validationRules || {}; this.userDataPath = options.userDataPath || join(this.storage.directory, 'users'); } /** * Initialize user manager */ async initialize() { if (this.initialized) { return; } try { // Create user data directory if it doesn't exist await fs.mkdir(this.userDataPath, { recursive: true }); this.initialized = true; } catch (error) { throw new IdentityError(IdentityErrorType.USER_INVALID, 'Failed to initialize user manager', error); } } /** * Create new user identity */ async createUser(machineId) { if (!this.initialized) { throw new IdentityError(IdentityErrorType.USER_INVALID, 'User manager not initialized'); } try { // Get and validate machine ID const currentMachineId = machineId || await machineIdProvider.getId(); if (!machineIdProvider.validate(currentMachineId)) { throw new IdentityError(IdentityErrorType.USER_INVALID, 'Invalid machine ID'); } // Generate user ID const userId = await this.generateUserId(currentMachineId); // Create user identity const user = { id: userId, machineIds: [currentMachineId], preferences: this.getDefaultPreferences(), created: new Date(), lastSeen: new Date(), sessions: [] }; // Validate user if (!this.validateUser(user)) { throw new IdentityError(IdentityErrorType.USER_INVALID, 'Invalid user data'); } // Store user await this.storage.write(join(this.userDataPath, `${userId}.json`), user); return user; } catch (error) { if (error instanceof IdentityError) { throw error; } throw new IdentityError(IdentityErrorType.USER_INVALID, 'Failed to create user', error); } } /** * Get user by ID */ async getUser(userId) { try { const user = await this.storage.read(join(this.userDataPath, `${userId}.json`)); if (!this.validateUser(user)) { throw new IdentityError(IdentityErrorType.USER_INVALID, 'Invalid user data'); } return user; } catch (error) { if (error instanceof IdentityError) { throw error; } throw new IdentityError(IdentityErrorType.USER_NOT_FOUND, 'User not found', error); } } /** * Get user by machine ID */ async getUserByMachine(machineId) { try { // List all user files const userFiles = await fs.readdir(this.userDataPath); // Find user with matching machine ID for (const file of userFiles) { if (!file.endsWith('.json')) continue; const user = await this.storage.read(join(this.userDataPath, file)); if (user.machineIds.includes(machineId)) { return user; } } throw new IdentityError(IdentityErrorType.USER_NOT_FOUND, 'No user found for machine ID'); } catch (error) { if (error instanceof IdentityError) { throw error; } throw new IdentityError(IdentityErrorType.USER_NOT_FOUND, 'Failed to find user', error); } } /** * Update user */ async updateUser(userId, updates) { try { const user = await this.getUser(userId); // Apply updates const updated = { ...user, ...updates, lastSeen: new Date() }; // Validate updated user if (!this.validateUser(updated)) { throw new IdentityError(IdentityErrorType.USER_INVALID, 'Invalid user data'); } // Store updated user await this.storage.write(join(this.userDataPath, `${userId}.json`), updated); return updated; } catch (error) { if (error instanceof IdentityError) { throw error; } throw new IdentityError(IdentityErrorType.USER_INVALID, 'Failed to update user', error); } } /** * Delete user */ async deleteUser(userId) { try { await this.storage.delete(join(this.userDataPath, `${userId}.json`)); } catch (error) { throw new IdentityError(IdentityErrorType.USER_NOT_FOUND, 'Failed to delete user', error); } } /** * Generate user ID */ async generateUserId(machineId) { // Create hash from machine ID and timestamp const hash = createHash('sha256') .update(`${machineId}:${Date.now()}`) .digest('hex'); // Use first 16 characters as user ID return hash.slice(0, 16); } /** * Get default user preferences */ getDefaultPreferences() { return { defaultClientType: 'claude', autoStartClients: false, clientSettings: { claude: { startupTimeout: 30000, healthCheckInterval: 30000, maxRetries: 3 }, cline: { startupTimeout: 30000, healthCheckInterval: 30000, maxRetries: 3 } } }; } /** * Validate user data */ validateUser(user) { try { // Basic structure validation if (!user || typeof user !== 'object') return false; if (!user.id || typeof user.id !== 'string') return false; if (!Array.isArray(user.machineIds)) return false; if (!user.preferences || typeof user.preferences !== 'object') return false; if (!(user.created instanceof Date)) return false; if (!(user.lastSeen instanceof Date)) return false; if (!Array.isArray(user.sessions)) return false; // ID format validation if (this.validationRules.userId?.pattern) { if (!this.validationRules.userId.pattern.test(user.id)) { return false; } } // Machine ID validation if (user.machineIds.length === 0) return false; for (const machineId of user.machineIds) { if (typeof machineId !== 'string' || !machineIdProvider.validate(machineId)) { return false; } } // Preferences validation const prefs = user.preferences; if (!prefs.defaultClientType || !['claude', 'cline'].includes(prefs.defaultClientType)) { return false; } if (!prefs.clientSettings) return false; for (const [clientType, settings] of Object.entries(prefs.clientSettings)) { if (!['claude', 'cline'].includes(clientType)) return false; if (typeof settings !== 'object') return false; if (typeof settings.startupTimeout !== 'number' || settings.startupTimeout <= 0) return false; if (typeof settings.healthCheckInterval !== 'number' || settings.healthCheckInterval <= 0) return false; if (typeof settings.maxRetries !== 'number' || settings.maxRetries <= 0) return false; } return true; } catch (error) { return false; } } } //# sourceMappingURL=user.js.map

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/glassBead-tc/SubspaceDomain'

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