Skip to main content
Glama

mcp-voice-hooks

hook-merger.ts2.97 kB
interface Hook { type: string; command: string; } interface HookConfig { matcher: string; hooks: Hook[]; } export interface HookSettings { [hookType: string]: HookConfig[]; } /** * Removes any existing voice hooks that use MCP_VOICE_HOOKS_PORT * @param hooks - The current hooks configuration * @returns The hooks with voice hooks removed */ export function removeVoiceHooks(hooks: HookSettings = {}): HookSettings { const cleaned: HookSettings = {}; // Pattern to match voice hooks by the unique environment variable const voiceHookPattern = /MCP_VOICE_HOOKS_PORT/; for (const [hookType, hookArray] of Object.entries(hooks)) { cleaned[hookType] = hookArray.filter(hookConfig => { // Keep this hook config only if none of its commands match our pattern return !hookConfig.hooks.some(hook => voiceHookPattern.test(hook.command)); }); // Remove empty arrays if (cleaned[hookType].length === 0) { delete cleaned[hookType]; } } return cleaned; } /** * Replaces voice hooks - removes any existing ones and adds new ones * @param existingHooks - The current hooks configuration * @param voiceHooks - The voice hooks to add * @returns The updated hooks configuration */ export function replaceVoiceHooks(existingHooks: HookSettings = {}, voiceHooks: HookSettings): HookSettings { // First, remove any existing voice hooks const cleaned = removeVoiceHooks(existingHooks); // Then merge in the new voice hooks const result: HookSettings = JSON.parse(JSON.stringify(cleaned)); // Deep clone for (const [hookType, hookArray] of Object.entries(voiceHooks)) { if (!result[hookType]) { result[hookType] = hookArray; } else { result[hookType].push(...hookArray); } } return result; } /** * Checks if two hook settings are semantically equal (ignoring order) * @param hooks1 - First hooks configuration * @param hooks2 - Second hooks configuration * @returns True if they contain the same hooks regardless of order */ export function areHooksEqual(hooks1: HookSettings = {}, hooks2: HookSettings = {}): boolean { const types1 = Object.keys(hooks1).sort(); const types2 = Object.keys(hooks2).sort(); // Different hook types if (types1.join(',') !== types2.join(',')) { return false; } // Check each hook type for (const hookType of types1) { const configs1 = hooks1[hookType]; const configs2 = hooks2[hookType]; if (configs1.length !== configs2.length) { return false; } // Create normalized strings for comparison const normalized1 = configs1 .map(config => JSON.stringify(config)) .sort(); const normalized2 = configs2 .map(config => JSON.stringify(config)) .sort(); // Compare sorted arrays for (let i = 0; i < normalized1.length; i++) { if (normalized1[i] !== normalized2[i]) { return false; } } } return true; }

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/johnmatthewtennant/mcp-voice-hooks'

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