-
Notifications
You must be signed in to change notification settings - Fork 0
integrations
VoxCtrl exposes several interfaces for external tools and services to interact with it.
Crate: crates/voxctrl-mcp/
The Model Context Protocol is an open standard for LLM agents to call tools on local servers. VoxCtrl implements an MCP server that lets AI assistants like Claude Desktop or Cursor IDE trigger voice recording and TTS.
| Platform | Socket |
|---|---|
| Linux |
/tmp/voxctrl-mcp.sock (Unix domain socket) |
| Windows |
\\.\pipe\voxctrl-mcp (named pipe) |
JSON-RPC 2.0 over the socket. Follows MCP spec v2024-11-05.
Handshake:
// Client sends:
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","clientInfo":{"name":"claude-desktop"}}}
// Server responds:
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"voxctrl","version":"1.0.0"}}}
// Client sends (notification, no id):
{"jsonrpc":"2.0","method":"notifications/initialized"}Records audio and returns the transcription. Blocks until recording ends or timeout is reached.
{
"method": "tools/call",
"params": {
"name": "transcribe_voice",
"arguments": { "timeout_seconds": 15 }
}
}timeout_seconds defaults to mcp.record_timeout (15.0 seconds). Returns "(no speech detected)" if no audio was captured.
Response:
{"content": [{"type": "text", "text": "the transcribed text here"}]}Queues text for TTS playback. Returns immediately while audio plays asynchronously.
{
"method": "tools/call",
"params": {
"name": "speak_text",
"arguments": { "text": "Recording started. Please speak now." }
}
}Returns: {"content": [{"type": "text", "text": "spoken"}]}
Returns current recording/speaking state.
{"method": "tools/call", "params": {"name": "get_status", "arguments": {}}}Response:
{"content": [{"type": "text", "text": "{\"recording\": false, \"speaking\": false}"}]}Recommended pattern — speak then record safely:
- Call
speak_textwith your question - Poll
get_statusuntilspeaking = false - Call
transcribe_voice
In config.json:
"mcp": {
"server_enabled": true,
"record_timeout": 15.0
}Add to claude_desktop_config.json:
{
"mcpServers": {
"voxctrl": {
"command": "nc",
"args": ["-U", "/tmp/voxctrl-mcp.sock"]
}
}
}Crate: crates/voxctrl-dbus/
VoxCtrl registers on the session bus as:
-
Bus name:
ai.voxctrl.Dictation -
Object path:
/ai/voxctrl/Dictation -
Interface:
ai.voxctrl.Dictation
| Method | Signature | Description |
|---|---|---|
start_recording() |
() → () |
Begin recording |
stop_recording() |
() → () |
Stop recording and process audio |
toggle_recording() |
() → () |
Toggle recording state |
get_status() |
() → s |
Returns "idle", "recording", or "transcribing"
|
get_word_count() |
() → u |
Total words dictated this session |
| Signal | Signature | Description |
|---|---|---|
status_changed |
(s) |
Emitted when recording state changes |
text_injected |
(s) |
Emitted after text is delivered to a target |
# Start recording
dbus-send --session --dest=ai.voxctrl.Dictation \
/ai/voxctrl/Dictation \
ai.voxctrl.Dictation.start_recording
# Watch for injected text
dbus-monitor --session "type='signal',interface='ai.voxctrl.Dictation'"
# Get current status ("idle", "recording", or "transcribing")
dbus-send --session --print-reply \
--dest=ai.voxctrl.Dictation \
/ai/voxctrl/Dictation \
ai.voxctrl.Dictation.get_statusThe DBus service is a stub on non-Linux platforms (compiles but does nothing).
Crate: crates/voxctrl-llm/
After Whisper transcribes speech, text can optionally be rewritten by a local LLM running in Ollama. Enabled per-hotkey binding via ollama_enabled = true in bindings.toml (or via the Hotkeys tab in the GUI settings).
"ollama": {
"enabled": false,
"endpoint": "http://localhost:11434",
"model": "llama3.2:1b",
"mode": "clean",
"custom_prompt": null,
"timeout_secs": 8
}| Mode | Prompt sent to LLM |
|---|---|
clean |
"Fix grammar and punctuation only. Return only the corrected text, no commentary." |
formal |
"Rewrite in formal professional language. Return only the result." |
casual |
"Rewrite in casual conversational language. Return only the result." |
bullet |
"Convert to a bullet-point list. Return only the list." |
concise |
"Summarize concisely in 1-2 sentences. Return only the summary." |
custom |
Uses custom_prompt field |
With mode = "custom", the custom_prompt field is used as the LLM instruction:
- If
custom_promptcontains{text}, that placeholder is replaced with the transcribed text - Otherwise, the transcribed text is appended after the prompt on a new line
OllamaClient.is_available() probes GET /api/tags on first call and caches the result. If Ollama was unreachable at startup, it will appear unreachable until the availability cache is reset (e.g. by changing the endpoint in settings).
If Ollama is unreachable, the HTTP request times out, or the response cannot be parsed, VoxCtrl logs the failure and delivers the original Whisper transcription unchanged. Text is never dropped.
Via the Settings → Ollama tab → "Test Connection" button, or via IPC:
const result = await invoke('test_ollama', {
endpoint: 'http://localhost:11434',
timeoutSecs: 5
});
// result: { success: boolean, message: string, models: string[] }POST with JSON body to http_url:
POST https://your-endpoint.com/voice
Content-Type: application/json
{"text": "transcribed text"}
Configurable: http_method, http_headers, http_json_template.
Same POST (to webhook_url) but with HMAC-SHA256 signature:
X-VoxCtrl-Signature: sha256=abc123...
When atspi.context_prompt = true, VoxCtrl uses the Linux Accessibility API (AT-SPI2) to read the surrounding text from the focused text field. This text is included in the Whisper initial prompt to improve transcription continuity and vocabulary consistency.
When atspi.auto_code_mode = true, VoxCtrl detects when the focused application is a code editor or terminal and automatically enables code-mode post-processing.
When atspi.injection = true, AT-SPI2 is used as the primary text injection method (before falling back to wtype/xdotool).
Requires the at-spi2-core package and the org.a11y.Bus DBus service to be running.