You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+67-7Lines changed: 67 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -91,7 +91,7 @@ Intel Mac (darwin-x64) is not supported: Apple has discontinued the platform and
91
91
92
92
## Tool Reference
93
93
94
-
Arguments are validated before anything is recorded or written. An argument a tool does not declare is rejected with an error naming it, rather than silently ignored. `duration_ms` must be an integer between 100 and 30000; `device` must be a non-negative integer index or a non-empty string id from `list_audio_devices`. A rejected call writes nothing to disk.
94
+
Arguments are validated before anything is recorded or written. An argument a tool does not declare is rejected with an error naming it, rather than silently ignored. `duration_ms` must be an integer between 100 and 30000; `silence_ms` an integer between 100 and 10000; `stop_on_silence` a boolean; `device` a non-negative integer index or a non-empty string id from `list_audio_devices`. A `silence_ms` that cannot take effect (passed without silence-stopping active) is rejected for the same reason unknown arguments are. A rejected call writes nothing to disk.
95
95
96
96
### list_audio_devices
97
97
@@ -112,16 +112,18 @@ The `id` is stable across reboots and device changes. The `index` is positional
112
112
113
113
### capture_audio
114
114
115
-
Records audio from the microphone and saves as a WAV file.
115
+
Records audio from the microphone and saves as a WAV file. Records for exactly `duration_ms` by default, or until the speaker stops talking with `stop_on_silence: true`.
|`duration_ms`| number | 5000 | Recording duration in milliseconds (100-30000) |
121
+
|`duration_ms`| number | 5000 | Recording duration in milliseconds (100-30000). A maximum, not an exact length, when `stop_on_silence` is true|
122
122
|`device`| number or string | system default | Device index or stable device `id` from `list_audio_devices`|
123
+
|`stop_on_silence`| boolean | false | Stop when the speaker stops talking, detected with on-device voice activity detection (Silero VAD, bundled, no download) |
124
+
|`silence_ms`| number | 1000 | Continuous silence in milliseconds that ends a `stop_on_silence` recording (100-10000). Requires `stop_on_silence: true`|
123
125
124
-
**Example response:**
126
+
**Example response (fixed duration):**
125
127
126
128
```json
127
129
{
@@ -133,16 +135,34 @@ Records audio from the microphone and saves as a WAV file.
133
135
}
134
136
```
135
137
138
+
**Example response (`stop_on_silence: true`):**
139
+
140
+
```json
141
+
{
142
+
"path": "/tmp/mcp-listen-1712345678901.wav",
143
+
"duration_ms": 2600,
144
+
"sample_rate": 16000,
145
+
"channels": 1,
146
+
"size_bytes": 83244,
147
+
"stopped_by": "silence",
148
+
"speech_detected": true
149
+
}
150
+
```
151
+
152
+
With `stop_on_silence`, `duration_ms` in the response is the actual captured length, and `stopped_by` says how the recording ended: `"silence"` (the speaker finished), `"ceiling"` (the `duration_ms` maximum was reached), or `"no_speech_timeout"` (nobody spoke for 10 seconds; the WAV is still returned, with `speech_detected: false`, so silence is a reported outcome rather than an error). Detection runs per ~100ms audio buffer, so the effective hangover rounds up to the next buffer, and the recording keeps everything from the start of the call through the stop decision: nothing is gated or clipped at speech boundaries, and the audio itself is byte-identical to a fixed-duration capture of the same sounds.
153
+
136
154
### voice_query
137
155
138
-
Full voice pipeline: capture audio, transcribe with whisper.cpp, send to Ollama, return the response. Entirely offline.
156
+
Full voice pipeline: capture audio, transcribe with whisper.cpp, send to Ollama, return the response. Entirely offline. Recording stops automatically when the speaker stops talking; pass `stop_on_silence: false` for a fixed-length recording.
|`duration_ms`| number | 5000 |Recording duration in milliseconds (100-30000) |
162
+
|`duration_ms`| number |15000 / 5000 |Maximum recording duration in milliseconds (100-30000). Default 15000 while silence-stopping is active, 5000 with `stop_on_silence: false`|
145
163
|`device`| number or string | system default | Device index or stable device `id` from `list_audio_devices`|
164
+
|`stop_on_silence`| boolean | true | Stop recording when the speaker stops talking. Pass false to record for exactly `duration_ms`|
165
+
|`silence_ms`| number | 1000 | Continuous silence in milliseconds that ends the recording (100-10000) |
146
166
|`whisper_model`| string | ggml-base.en.bin | Path or filename of Whisper GGML model |
147
167
|`language`| string | en | Language code for transcription |
148
168
|`model`| string | llama3.2 | Ollama model name |
@@ -158,12 +178,52 @@ Full voice pipeline: capture audio, transcribe with whisper.cpp, send to Ollama,
158
178
}
159
179
```
160
180
181
+
**Result outcomes.**`voice_query` reports five distinct outcomes. The **structured fields are the contract** (`isError`, `speech_detected`, `transcription`); any `message` is a human-readable hint whose wording is not part of the contract, so a caller branches on the fields, never on the prose. The rule is simple: **if the pipeline ran, the result is a success (even when it found no words); if a dependency broke, the result is an error.**
A caller distinguishes "the user was silent" from "the user spoke but produced no words" by `speech_detected` (`false` vs `true`), both carrying `transcription: null`. Non-speech audio never reaches the language model: whisper's non-speech markers (`[BLANK_AUDIO]`, `[MUSIC]`, `(silence)`, and similar) are treated as no usable words rather than sent on as a query.
192
+
193
+
**No speech** (`speech_detected: false`):
194
+
195
+
```json
196
+
{
197
+
"speech_detected": false,
198
+
"stopped_by": "no_speech_timeout",
199
+
"transcription": null,
200
+
"response": null,
201
+
"message": "No speech was detected. Ask the user to repeat, or check that the correct microphone is selected."
202
+
}
203
+
```
204
+
205
+
**Speech, but no transcribable words** (`speech_detected: true`, `transcription: null`):
206
+
207
+
```json
208
+
{
209
+
"speech_detected": true,
210
+
"stopped_by": "silence",
211
+
"transcription": null,
212
+
"response": null,
213
+
"message": "Speech was detected but could not be transcribed. It may have been too quiet, too brief, or unclear. Ask the user to repeat, a little louder and closer to the microphone."
214
+
}
215
+
```
216
+
217
+
Transcription and dependency failures return `isError: true` with the real cause (a missing model, a whisper load failure, Ollama not running, a timeout, or an empty model response), so a caller debugging can tell whether the failure was in capture, transcription, or the language model.
218
+
161
219
## How It Works
162
220
163
221
mcp-listen uses [decibri](https://decibri.com) for cross-platform microphone capture. No ffmpeg, no SoX, no system audio tools required. Pre-built native binaries with zero setup.
164
222
165
223
Audio is captured as 16-bit PCM at 16kHz mono, the standard format for speech-to-text engines.
166
224
225
+
Silence-stopping uses the Silero voice activity detection model that ships inside decibri, running on-device through the bundled ONNX Runtime. Nothing extra is downloaded and no audio leaves the machine. The stop decision is measured in captured audio, not wall-clock time, and the VAD only decides when to stop: it never gates or alters the recorded samples.
226
+
167
227
The `voice_query` tool runs the full pipeline locally: capture audio, transcribe with whisper.cpp, and send to a local Ollama LLM. Fully offline, nothing leaves your machine.
168
228
169
229
## Whisper Model Setup
@@ -194,7 +254,7 @@ The model is ~150MB and downloads once. You can also set the `WHISPER_MODEL_PATH
194
254
195
255
## Known Limitations
196
256
197
-
1.**Fixed recording duration.**You specify how long to record. There is no "stop when I stop talking" mode yet.
257
+
1.**A loud transient can register as speech.**Silence-stopping decides "speech has started" from the VAD score, so a door slam or a cough can start the countdown and end the recording after `silence_ms` of quiet, yielding a short capture of mostly silence. The outcome is visible, not silent: the result reports the actual duration, and `voice_query` reports an empty transcription rather than inventing one. A minimum-speech-duration guard is a candidate refinement.
198
258
2.**`voice_query` requires Ollama running.** If Ollama isn't running, the tool returns a clear error message.
199
259
3.**Whisper model must be downloaded before first use.**`voice_query` does not download the model itself; the first call requires a pre-downloaded model (~150MB). See [Whisper Model Setup](#whisper-model-setup).
200
260
4.**No streaming.** MCP's request/response pattern means the entire recording is captured, then transcribed, then sent to the LLM. No real-time partial results.
0 commit comments