mac-voice-mcp 0.1.0 → 0.1.1
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/CHANGELOG.md +18 -0
- package/README.md +32 -14
- package/dist/speech-text.js +20 -0
- package/dist/stt.js +1 -1
- package/dist/texts.js +2 -0
- package/package.json +4 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- **Node.js 22 or newer is now required.** Node 18 and 20 no longer receive security fixes. CI tests on Node 22, 24 and 26.
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
- **"It's on screen" when it isn't** ([#3](https://github.com/jeet0007/mac-voice-mcp/issues/3)). When the spoken text sends you to look at something, the model gets a reminder that only its own message text reaches your screen, not the speech and not Bash/tool output. The speaking rules now say the same. Thanks to Copilot for the first version ([#4](https://github.com/jeet0007/mac-voice-mcp/pull/4)). The trigger phrases were then narrowed so that ordinary sentences like "above thirty degrees" or "see the doctor" don't set it off.
|
|
10
|
+
|
|
11
|
+
### Docs and tooling
|
|
12
|
+
- README: one-click install buttons for Cursor and VS Code, and install steps for the Claude Code plugin marketplace and the official MCP Registry.
|
|
13
|
+
- Security: TruffleHog secret scanning, CodeQL, dependency review, `npm audit` in CI, and Dependabot.
|
|
14
|
+
- Releases publish through npm trusted publishing, with provenance.
|
|
15
|
+
|
|
16
|
+
## 0.1.0
|
|
17
|
+
|
|
18
|
+
First release: `speak_and_listen` (natural turn-taking, on-device whisper.cpp with a warm server), `voice_setup` (check first, install only what's missing, only after you agree), and the `setup` and `voice_mode` prompts.
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<p align="center">
|
|
17
17
|
<img alt="Platform: macOS, Apple Silicon" src="https://img.shields.io/badge/platform-macOS%20%C2%B7%20Apple%20Silicon-000000?logo=apple">
|
|
18
18
|
<a href="https://modelcontextprotocol.io"><img alt="MCP server" src="https://img.shields.io/badge/MCP-server-6f42c1"></a>
|
|
19
|
-
<img alt="Node.js
|
|
19
|
+
<img alt="Node.js 22+" src="https://img.shields.io/node/v/mac-voice-mcp?logo=node.js&color=339933">
|
|
20
20
|
<a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue"></a>
|
|
21
21
|
<a href="SECURITY.md"><img alt="Dependabot enabled" src="https://img.shields.io/badge/Dependabot-enabled-025e8c?logo=dependabot"></a>
|
|
22
22
|
<a href="#-vibe-coded"><img alt="Vibe-coded with Claude" src="https://img.shields.io/badge/vibe--coded-with%20Claude-d97757"></a>
|
|
@@ -59,7 +59,26 @@ The server has **two tools and two prompts**:
|
|
|
59
59
|
|
|
60
60
|
## Install
|
|
61
61
|
|
|
62
|
-
**
|
|
62
|
+
You need **Node.js 22 or newer**. Whichever way you install, run setup once afterwards (see *Then*, below).
|
|
63
|
+
|
|
64
|
+
### One click
|
|
65
|
+
|
|
66
|
+
<p>
|
|
67
|
+
<a href="https://cursor.com/en/install-mcp?name=voice-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIm1hYy12b2ljZS1tY3AiXX0%3D"><img alt="Add to Cursor" src="https://cursor.com/deeplink/mcp-install-dark.svg" height="32"></a>
|
|
68
|
+
<a href="https://insiders.vscode.dev/redirect/mcp/install?name=voice-mcp&config=%7B%22type%22%3A%22stdio%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22mac-voice-mcp%22%5D%7D"><img alt="Install in VS Code" src="https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=for-the-badge&logo=visualstudiocode&logoColor=white" height="32"></a>
|
|
69
|
+
<a href="https://insiders.vscode.dev/redirect/mcp/install?name=voice-mcp&config=%7B%22type%22%3A%22stdio%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22mac-voice-mcp%22%5D%7D&quality=insiders"><img alt="Install in VS Code Insiders" src="https://img.shields.io/badge/VS_Code_Insiders-Install_Server-24bfa5?style=for-the-badge&logo=visualstudiocode&logoColor=white" height="32"></a>
|
|
70
|
+
</p>
|
|
71
|
+
|
|
72
|
+
### From a marketplace
|
|
73
|
+
|
|
74
|
+
- **Claude Code plugin marketplace.** This repo is its own marketplace:
|
|
75
|
+
```
|
|
76
|
+
/plugin marketplace add jeet0007/mac-voice-mcp
|
|
77
|
+
/plugin install mac-voice-mcp@mac-voice-mcp
|
|
78
|
+
```
|
|
79
|
+
- **The official MCP Registry.** It's listed as [`io.github.jeet0007/mac-voice-mcp`](https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.jeet0007/mac-voice-mcp). Apps and directories that read the registry pick it up from there. In VS Code, open the Extensions view (⇧⌘X), search `@mcp mac-voice`, and click **Install**. Smithery, Glama, PulseMCP and mcp.so copy the registry, so it shows up there too.
|
|
80
|
+
|
|
81
|
+
### By hand
|
|
63
82
|
|
|
64
83
|
**Claude Code**
|
|
65
84
|
|
|
@@ -67,13 +86,6 @@ The server has **two tools and two prompts**:
|
|
|
67
86
|
claude mcp add voice-mcp -s user -- npx -y mac-voice-mcp
|
|
68
87
|
```
|
|
69
88
|
|
|
70
|
-
Or install it as a Claude Code plugin:
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
/plugin marketplace add jeet0007/mac-voice-mcp
|
|
74
|
-
/plugin install mac-voice-mcp@mac-voice-mcp
|
|
75
|
-
```
|
|
76
|
-
|
|
77
89
|
**Claude Desktop.** Add this to `~/Library/Application Support/Claude/claude_desktop_config.json`, then quit (⌘Q) and reopen the app:
|
|
78
90
|
|
|
79
91
|
```json
|
|
@@ -89,13 +101,19 @@ Or install it as a Claude Code plugin:
|
|
|
89
101
|
|
|
90
102
|
If you get `spawn npx ENOENT`, use the full path from `which npx`, e.g. `"command": "/opt/homebrew/bin/npx"`.
|
|
91
103
|
|
|
92
|
-
**Cursor.** Add the same `mcpServers` block to `~/.cursor/mcp.json
|
|
104
|
+
**Cursor.** Add the same `mcpServers` block to `~/.cursor/mcp.json`.
|
|
93
105
|
|
|
106
|
+
**VS Code.** Run **MCP: Add Server** from the Command Palette, or:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
code --add-mcp '{"name":"voice-mcp","command":"npx","args":["-y","mac-voice-mcp"]}'
|
|
94
110
|
```
|
|
95
|
-
cursor://anysphere.cursor-deeplink/mcp/install?name=voice-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIm1hYy12b2ljZS1tY3AiXX0=
|
|
96
|
-
```
|
|
97
111
|
|
|
98
|
-
**
|
|
112
|
+
**Any other MCP client.** Run `npx -y mac-voice-mcp` as a stdio server.
|
|
113
|
+
|
|
114
|
+
### Then: set up and allow the mic
|
|
115
|
+
|
|
116
|
+
**Run setup once.** Ask Claude to *"set up voice"*. In Claude Code you can also run `/mcp__voice-mcp__setup`, or from a terminal run `npx -y mac-voice-mcp setup`.
|
|
99
117
|
|
|
100
118
|
Setup checks what's already there before it changes anything:
|
|
101
119
|
|
|
@@ -110,7 +128,7 @@ Setup checks what's already there before it changes anything:
|
|
|
110
128
|
- **Nothing happens without your OK.** Claude calls `voice_setup` to check first, shows you the checklist, and asks before calling it with `install=true`.
|
|
111
129
|
- **Slow installs don't time out.** If `brew install whisper-cpp` takes a while, setup reports INSTALLING. The install carries on in the background, and the next check picks up the result.
|
|
112
130
|
|
|
113
|
-
**
|
|
131
|
+
**Allow the microphone.** The first time Claude listens, macOS asks whether Claude (or Cursor, or your terminal) can use the microphone. Click Allow.
|
|
114
132
|
|
|
115
133
|
### Installing from a clone
|
|
116
134
|
|
package/dist/speech-text.js
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
* - prepareSpeech: turn "screen text" into "ear text" before it's spoken
|
|
4
4
|
* - cleanTranscript: strip whisper.cpp's non-speech markers from a transcript
|
|
5
5
|
*/
|
|
6
|
+
/**
|
|
7
|
+
* Phrases that send the user to look at something. Deliberately specific: bare words like
|
|
8
|
+
* "above", "below" or "see the" also occur in ordinary speech ("above thirty degrees",
|
|
9
|
+
* "see the doctor") and must not trigger the reminder.
|
|
10
|
+
*/
|
|
11
|
+
const ON_SCREEN_PATTERN = new RegExp([
|
|
12
|
+
String.raw `\bon[\s-]?screen\b`,
|
|
13
|
+
String.raw `\bI(?:['’]ve| have)\s+(?:printed|pasted|put|posted|listed|written|shown|added)\b`,
|
|
14
|
+
String.raw `\b(?:printed|pasted|posted|listed|shown|written)\s+(?:it\s+|them\s+)?(?:below|above|here)\b`,
|
|
15
|
+
String.raw `\b(?:see|check|look at|scroll to)\s+(?:the\s+)?(?:screen|chat|reply|message|details|output|table|diff|log)\b`,
|
|
16
|
+
String.raw `\bin (?:the|my) (?:reply|message|chat)\b`,
|
|
17
|
+
].join("|"), "i");
|
|
6
18
|
/**
|
|
7
19
|
* Safety net for speakability. The model is asked (tool description, server
|
|
8
20
|
* instructions, voice_mode prompt) to send plain spoken sentences; this catches
|
|
@@ -89,6 +101,14 @@ export function prepareSpeech(input, opts = {}) {
|
|
|
89
101
|
"Next time send only short, plain spoken sentences — keep code, paths, links and tables in your on-screen reply.",
|
|
90
102
|
]
|
|
91
103
|
: [];
|
|
104
|
+
// When the spoken text points the user at something to look at, remind the model that only
|
|
105
|
+
// its own assistant message renders — not the speech, and not Bash/tool output (issue #3).
|
|
106
|
+
// Checked on the original input, so the "The rest is on screen." added above doesn't count.
|
|
107
|
+
if (ON_SCREEN_PATTERN.test(input)) {
|
|
108
|
+
noteList.push("voice-mcp reminder: you told the user to look at something. Spoken text and Bash/tool output are " +
|
|
109
|
+
"NOT visible to them — only your own assistant message is. Make sure those details are written in " +
|
|
110
|
+
"your reply for this turn.");
|
|
111
|
+
}
|
|
92
112
|
return { text: s, notes: noteList };
|
|
93
113
|
}
|
|
94
114
|
/** Remove timestamps and non-speech markers like [BLANK_AUDIO] from whisper output. */
|
package/dist/stt.js
CHANGED
|
@@ -71,7 +71,7 @@ function freePort() {
|
|
|
71
71
|
});
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
|
-
/** fetch() with a timeout that also honours the caller's abort signal (
|
|
74
|
+
/** fetch() with a timeout that also honours the caller's abort signal (works without AbortSignal.any). */
|
|
75
75
|
async function fetchWithin(url, init, timeoutMs, signal) {
|
|
76
76
|
const ctl = new AbortController();
|
|
77
77
|
const timer = setTimeout(() => ctl.abort(), timeoutMs);
|
package/dist/texts.js
CHANGED
|
@@ -12,6 +12,8 @@ export const SPEECH_RULES = [
|
|
|
12
12
|
' not paths ("in index.ts"), round numbers ("about two hundred ms"), spell out symbols.',
|
|
13
13
|
'- Ask one question at a time, answerable in a few words ("Should I deploy it — yes or no?").',
|
|
14
14
|
"- Put the details (diffs, logs, links) in your normal on-screen reply, and say so out loud.",
|
|
15
|
+
"- Tool output (Bash stdout, file writes) is NOT the on-screen reply. Only your own assistant message text renders.",
|
|
16
|
+
"- A spoken turn with no assistant message text shows the user nothing — never skip the on-screen reply.",
|
|
15
17
|
].join("\n");
|
|
16
18
|
/** Claude Code reads these (truncated at 2 KB) — keep under that. */
|
|
17
19
|
export const SERVER_INSTRUCTIONS = [
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mac-voice-mcp",
|
|
3
3
|
"mcpName": "io.github.jeet0007/mac-voice-mcp",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.1",
|
|
5
5
|
"description": "Talk with Claude out loud on your Mac: speaks with macOS `say`, listens for one natural conversational turn, and transcribes on-device with whisper.cpp. An MCP server.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"dist",
|
|
14
14
|
"examples",
|
|
15
15
|
"README.md",
|
|
16
|
-
"LICENSE"
|
|
16
|
+
"LICENSE",
|
|
17
|
+
"CHANGELOG.md"
|
|
17
18
|
],
|
|
18
19
|
"scripts": {
|
|
19
20
|
"build": "tsc && node -e \"require('fs').chmodSync('dist/index.js', 0o755)\"",
|
|
@@ -36,7 +37,7 @@
|
|
|
36
37
|
"typescript": "^7.0.2"
|
|
37
38
|
},
|
|
38
39
|
"engines": {
|
|
39
|
-
"node": ">=
|
|
40
|
+
"node": ">=22"
|
|
40
41
|
},
|
|
41
42
|
"keywords": [
|
|
42
43
|
"mcp",
|