Skip to main content
Glama

mcp-voice-hooks

app-setup.ts4.72 kB
import express from 'express'; import type { Request, Response } from 'express'; import cors from 'cors'; import { randomUUID } from 'crypto'; import { exec } from 'child_process'; import { promisify } from 'util'; const execAsync = promisify(exec); // Mock the execAsync for tests jest.mock('child_process', () => ({ exec: jest.fn((cmd, callback) => { // Simulate successful command execution callback(null, { stdout: '', stderr: '' }); }) })); // Shared utterance queue interface Utterance { id: string; text: string; timestamp: Date; status: 'pending' | 'delivered' | 'responded'; } class UtteranceQueue { utterances: Utterance[] = []; add(text: string, timestamp?: Date): Utterance { const utterance: Utterance = { id: randomUUID(), text: text.trim(), timestamp: timestamp || new Date(), status: 'pending' }; this.utterances.push(utterance); return utterance; } getRecent(limit: number = 10): Utterance[] { return this.utterances .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()) .slice(0, limit); } markDelivered(id: string): void { const utterance = this.utterances.find(u => u.id === id); if (utterance) { utterance.status = 'delivered'; } } clear(): void { this.utterances = []; } } // Voice preferences (controlled by browser) let voicePreferences = { voiceResponsesEnabled: false }; // Server-Sent Events clients const ttsClients = new Set<Response>(); // Helper function to notify all connected TTS clients function notifyTTSClients(text: string) { const message = JSON.stringify({ type: 'speak', text }); ttsClients.forEach(client => { client.write(`data: ${message}\n\n`); }); } export function setupApp() { const app = express(); app.use(cors()); app.use(express.json()); const queue = new UtteranceQueue(); // API for voice preferences app.post('/api/voice-preferences', (req: Request, res: Response) => { const { voiceResponsesEnabled } = req.body; // Update preferences voicePreferences.voiceResponsesEnabled = !!voiceResponsesEnabled; res.json({ success: true, preferences: voicePreferences }); }); // API for text-to-speech app.post('/api/speak', async (req: Request, res: Response) => { const { text } = req.body; if (!text || !text.trim()) { res.status(400).json({ error: 'Text is required' }); return; } // Check if voice responses are enabled if (!voicePreferences.voiceResponsesEnabled) { res.status(400).json({ error: 'Voice responses are disabled', message: 'Cannot speak when voice responses are disabled' }); return; } try { // Always notify browser clients - they decide how to speak notifyTTSClients(text); // Mark all delivered utterances as responded const deliveredUtterances = queue.utterances.filter(u => u.status === 'delivered'); deliveredUtterances.forEach(u => { u.status = 'responded'; }); res.json({ success: true, message: 'Text spoken successfully', respondedCount: deliveredUtterances.length }); } catch (error) { res.status(500).json({ error: 'Failed to speak text', details: error instanceof Error ? error.message : String(error) }); } }); // API for system text-to-speech (always uses Mac say command) app.post('/api/speak-system', async (req: Request, res: Response) => { const { text, rate = 150 } = req.body; if (!text || !text.trim()) { res.status(400).json({ error: 'Text is required' }); return; } try { // Execute text-to-speech using macOS say command // Note: Mac say command doesn't support volume control await execAsync(`say -r ${rate} "${text.replace(/"/g, '\\"')}"`); res.json({ success: true, message: 'Text spoken successfully via system voice' }); } catch (error) { res.status(500).json({ error: 'Failed to speak text via system voice', details: error instanceof Error ? error.message : String(error) }); } }); // Server-Sent Events for TTS notifications app.get('/api/tts-events', (_req: Request, res: Response) => { res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', }); // Send initial connection message res.write('data: {"type":"connected"}\n\n'); // Add client to set ttsClients.add(res); // Remove client on disconnect res.on('close', () => { ttsClients.delete(res); }); }); return app; }

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