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 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 18+" src="https://img.shields.io/node/v/mac-voice-mcp?logo=node.js&color=339933">
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
- **1. Add the server to your client.** You need Node.js 18.17 or newer.
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`, or use the one-click link:
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
- **2. 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`.
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
- **3. Allow the microphone.** The first time Claude listens, macOS asks whether Claude (or Cursor, or your terminal) can use the microphone. Click Allow.
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
 
@@ -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 (Node 18 compatible). */
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.0",
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": ">=18.17"
40
+ "node": ">=22"
40
41
  },
41
42
  "keywords": [
42
43
  "mcp",