mcp-voice-hooks 1.0.28-beta.1 → 1.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/bin/cli.js +3 -61
- package/package.json +1 -1
- package/public/app.js +23 -3
package/README.md
CHANGED
@@ -52,7 +52,7 @@ Say something to Claude. You will need to send one message in the Claude Code CL
|
|
52
52
|
## Browser Compatibility
|
53
53
|
|
54
54
|
- ✅ **Chrome**: Full support for speech recognition, browser text-to-speech, and system text-to-speech
|
55
|
-
- ⚠️ **Safari**: Full support for speech recognition, but
|
55
|
+
- ⚠️ **Safari**: Full support for speech recognition and system text-to-speech, but browser text-to-speech cannot load high-quality voices
|
56
56
|
- ❌ **Edge**: Speech recognition not working on Apple Silicon (language-not-supported error)
|
57
57
|
|
58
58
|
## Voice responses
|
@@ -70,7 +70,7 @@ You can download high quality voices from the system voice menu: `System Setting
|
|
70
70
|
|
71
71
|
Click the info icon next to the system voice dropdown. Search for "Siri" to find the highest quality voices. You'll have to trigger a download of the voice.
|
72
72
|
|
73
|
-
Once it's downloaded, you can select it in the
|
73
|
+
Once it's downloaded, you can select it in the Browser Voice (Local) menu in Chrome.
|
74
74
|
|
75
75
|
Test it with the bash command:
|
76
76
|
|
@@ -82,6 +82,8 @@ To use Siri voices with voice-hooks, you need to set your system voice and selec
|
|
82
82
|
|
83
83
|
Other downloaded voices will show up in the voice dropdown in the voice-hooks browser interface so you can select them there directly, instead of using the "Mac System Voice" option.
|
84
84
|
|
85
|
+
There is a bug in Safari that prevents browser text-to-speech from loading high-quality voices after browser restart. This is a Safari Web Speech API limitation. To use high-quality voices in Safari you need to set your system voice to Siri and select "Mac System Voice" in the voice-hooks browser interface.
|
86
|
+
|
85
87
|
## Manual Hook Installation
|
86
88
|
|
87
89
|
The hooks are automatically installed/updated when the MCP server starts. However, if you need to manually install or reconfigure the hooks:
|
package/bin/cli.js
CHANGED
@@ -54,9 +54,6 @@ async function configureClaudeCodeSettings() {
|
|
54
54
|
const settingsPath = path.join(claudeDir, 'settings.local.json');
|
55
55
|
// This was used in versions <= v1.0.21.
|
56
56
|
const oldSettingsPath = path.join(claudeDir, 'settings.json');
|
57
|
-
|
58
|
-
// Detect if running on Windows
|
59
|
-
const isWindows = process.platform === 'win32';
|
60
57
|
|
61
58
|
console.log('⚙️ Configuring project Claude Code settings...');
|
62
59
|
|
@@ -106,62 +103,7 @@ async function configureClaudeCodeSettings() {
|
|
106
103
|
}
|
107
104
|
|
108
105
|
// Add hook configuration with inline commands
|
109
|
-
|
110
|
-
const hookConfig = isWindows ? {
|
111
|
-
// Windows-compatible commands using PowerShell environment variable syntax
|
112
|
-
"Stop": [
|
113
|
-
{
|
114
|
-
"matcher": "",
|
115
|
-
"hooks": [
|
116
|
-
{
|
117
|
-
"type": "command",
|
118
|
-
"command": "powershell -Command \"$port = if ($env:MCP_VOICE_HOOKS_PORT) { $env:MCP_VOICE_HOOKS_PORT } else { '5111' }; try { Invoke-RestMethod -Uri \\\"http://localhost:$port/api/hooks/stop\\\" -Method POST } catch { Write-Output '{\\\"decision\\\": \\\"approve\\\", \\\"reason\\\": \\\"voice-hooks unavailable\\\"}' }\""
|
119
|
-
}
|
120
|
-
]
|
121
|
-
}
|
122
|
-
],
|
123
|
-
"PreToolUse": [
|
124
|
-
{
|
125
|
-
"matcher": "^(?!mcp__voice-hooks__).*",
|
126
|
-
"hooks": [
|
127
|
-
{
|
128
|
-
"type": "command",
|
129
|
-
"command": "powershell -Command \"$port = if ($env:MCP_VOICE_HOOKS_PORT) { $env:MCP_VOICE_HOOKS_PORT } else { '5111' }; try { Invoke-RestMethod -Uri \\\"http://localhost:$port/api/hooks/pre-tool\\\" -Method POST } catch { Write-Output '{\\\"decision\\\": \\\"approve\\\", \\\"reason\\\": \\\"voice-hooks unavailable\\\"}' }\""
|
130
|
-
}
|
131
|
-
]
|
132
|
-
},
|
133
|
-
{
|
134
|
-
"matcher": "^mcp__voice-hooks__speak$",
|
135
|
-
"hooks": [
|
136
|
-
{
|
137
|
-
"type": "command",
|
138
|
-
"command": "powershell -Command \"$port = if ($env:MCP_VOICE_HOOKS_PORT) { $env:MCP_VOICE_HOOKS_PORT } else { '5111' }; try { Invoke-RestMethod -Uri \\\"http://localhost:$port/api/hooks/pre-speak\\\" -Method POST } catch { Write-Output '{\\\"decision\\\": \\\"approve\\\", \\\"reason\\\": \\\"voice-hooks unavailable\\\"}' }\""
|
139
|
-
}
|
140
|
-
]
|
141
|
-
},
|
142
|
-
{
|
143
|
-
"matcher": "^mcp__voice-hooks__wait_for_utterance$",
|
144
|
-
"hooks": [
|
145
|
-
{
|
146
|
-
"type": "command",
|
147
|
-
"command": "powershell -Command \"$port = if ($env:MCP_VOICE_HOOKS_PORT) { $env:MCP_VOICE_HOOKS_PORT } else { '5111' }; try { Invoke-RestMethod -Uri \\\"http://localhost:$port/api/hooks/pre-wait\\\" -Method POST } catch { Write-Output '{\\\"decision\\\": \\\"approve\\\", \\\"reason\\\": \\\"voice-hooks unavailable\\\"}' }\""
|
148
|
-
}
|
149
|
-
]
|
150
|
-
}
|
151
|
-
],
|
152
|
-
"PostToolUse": [
|
153
|
-
{
|
154
|
-
"matcher": "^(?!mcp__voice-hooks__).*",
|
155
|
-
"hooks": [
|
156
|
-
{
|
157
|
-
"type": "command",
|
158
|
-
"command": "powershell -Command \"$port = if ($env:MCP_VOICE_HOOKS_PORT) { $env:MCP_VOICE_HOOKS_PORT } else { '5111' }; try { Invoke-RestMethod -Uri \\\"http://localhost:$port/api/hooks/post-tool\\\" -Method POST } catch { Write-Output '{}' }\""
|
159
|
-
}
|
160
|
-
]
|
161
|
-
}
|
162
|
-
]
|
163
|
-
} : {
|
164
|
-
// Unix/Mac commands using curl with shell environment variable syntax
|
106
|
+
const hookConfig = {
|
165
107
|
"Stop": [
|
166
108
|
{
|
167
109
|
"matcher": "",
|
@@ -175,7 +117,7 @@ async function configureClaudeCodeSettings() {
|
|
175
117
|
],
|
176
118
|
"PreToolUse": [
|
177
119
|
{
|
178
|
-
"matcher": "^(?!mcp__voice-hooks__).*",
|
120
|
+
"matcher": "^(?!mcp__voice-hooks__|ExitPlanMode).*",
|
179
121
|
"hooks": [
|
180
122
|
{
|
181
123
|
"type": "command",
|
@@ -204,7 +146,7 @@ async function configureClaudeCodeSettings() {
|
|
204
146
|
],
|
205
147
|
"PostToolUse": [
|
206
148
|
{
|
207
|
-
"matcher": "^(?!mcp__voice-hooks__).*",
|
149
|
+
"matcher": "^(?!mcp__voice-hooks__|ExitPlanMode).*",
|
208
150
|
"hooks": [
|
209
151
|
{
|
210
152
|
"type": "command",
|
package/package.json
CHANGED
package/public/app.js
CHANGED
@@ -350,14 +350,33 @@ class VoiceHooksClient {
|
|
350
350
|
|
351
351
|
// Get available voices
|
352
352
|
this.voices = [];
|
353
|
+
|
354
|
+
// Enhanced voice loading with deduplication
|
353
355
|
const loadVoices = () => {
|
354
|
-
|
355
|
-
|
356
|
+
const voices = window.speechSynthesis.getVoices();
|
357
|
+
|
358
|
+
// Deduplicate voices - keep the first occurrence of each unique voice
|
359
|
+
const deduplicatedVoices = [];
|
360
|
+
const seen = new Set();
|
361
|
+
|
362
|
+
voices.forEach(voice => {
|
363
|
+
// Create a unique key based on name, language, and URI
|
364
|
+
const key = `${voice.name}-${voice.lang}-${voice.voiceURI}`;
|
365
|
+
if (!seen.has(key)) {
|
366
|
+
seen.add(key);
|
367
|
+
deduplicatedVoices.push(voice);
|
368
|
+
}
|
369
|
+
});
|
370
|
+
|
371
|
+
this.voices = deduplicatedVoices;
|
356
372
|
this.populateVoiceList();
|
357
373
|
};
|
358
374
|
|
359
|
-
// Load voices initially and
|
375
|
+
// Load voices initially and with a delayed retry for reliability
|
360
376
|
loadVoices();
|
377
|
+
setTimeout(loadVoices, 100);
|
378
|
+
|
379
|
+
// Set up voice change listener
|
361
380
|
if (window.speechSynthesis.onvoiceschanged !== undefined) {
|
362
381
|
window.speechSynthesis.onvoiceschanged = loadVoices;
|
363
382
|
}
|
@@ -438,6 +457,7 @@ class VoiceHooksClient {
|
|
438
457
|
|
439
458
|
populateVoiceList() {
|
440
459
|
if (!this.voiceSelect || !this.localVoicesGroup || !this.cloudVoicesGroup) return;
|
460
|
+
|
441
461
|
|
442
462
|
// First populate the language filter
|
443
463
|
this.populateLanguageFilter();
|