Skip to content
Back to skills

Openwhispr Mcp Setup

CSecurity

Stand up the `openwhispr` voice-notes MCP on a new machine and wire it into both Claude clients — the OpenWhispr add-on alongside mcp-bridge-setup (like whatsapp-mcp-setup). It exposes the external OpenWhispr desktop app's local transcript store (a SQLite DB + `.webm` recordings under `~/Library/Application Support/open-whispr/`) as a stdio MCP, fronted by a supergateway + launchd bridge on $OPENWHISPR_BRIDGE_PORT/:8002 for Claude Code and a direct stdio command for Cowork, and owns the water...

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 2, 2026
ai-agentsgoshellsqlnodegitdatabase

Works with

  • claude code
  • claude desktop
  • cli
  • mcp

Security analysis

C63/100
  • criticalModifies startup scripts or system services for persistence
  • mediumInstalls packages at runtime which could introduce malicious dependencies
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned September 2, 2026

npx -y skills add philipyaz/cos --skill openwhispr-mcp-setup --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Openwhispr Mcp Setup?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Openwhispr Mcp Setup
[![Security: C — Skills Directory](https://www.skillsdirectory.com/api/skills/philipyaz-openwhispr-mcp-setup/badge)](https://www.skillsdirectory.com/skills/philipyaz-openwhispr-mcp-setup)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: openwhispr-mcp-setup
description: Stand up the `openwhispr` voice-notes MCP on a new machine and wire it into both Claude clients — the OpenWhispr add-on alongside mcp-bridge-setup (like whatsapp-mcp-setup). It exposes the external OpenWhispr desktop app's local transcript store (a SQLite DB + `.webm` recordings under `~/Library/Application Support/open-whispr/`) as a stdio MCP, fronted by a supergateway + launchd bridge on $OPENWHISPR_BRIDGE_PORT/:8002 for Claude Code and a direct stdio command for Cowork, and owns the watermark that makes voice ingestion idempotent. Use when setting up OpenWhispr/voice on a new machine, when Cowork or Code can't see the `openwhispr` server, when the openwhispr bridge (:8002) is down, when the voice ingest loop can't find notes, or when list_transcripts is stuck on fixtures instead of the real store.
---

# OpenWhispr MCP setup (voice-notes add-on — stdio MCP bridge :8002)

## Why this exists / architecture
`openwhispr` is the **optional voice add-on**. Unlike the core Cos servers (board, calendar,
guard, vault), it has an **external-app dependency**: it does nothing but expose the
[OpenWhispr](https://openwhispr.com) desktop app's **local transcript store**, so it only makes
sense on a machine where you actually use OpenWhispr. That's why it lives in its own skill — the
same shape as **whatsapp-mcp-setup** (which integrates an external app + needs pairing). board /
calendar / guard / vault stay in **mcp-bridge-setup**; this skill adds the one optional voice
server on top.

It is a **single process** — `mcp/openwhispr-server/server.mjs`, a Node **stdio** MCP server — and
it registers into Cos the same two ways the core servers do:

- **Claude Code** reaches it over **HTTP** via a **supergateway + launchd BRIDGE on `:8002`**
  (`$OPENWHISPR_BRIDGE_PORT`, label `com.chiefofstaff.mcp-openwhispr`, IN `.mcp.json`).
- **Claude Cowork Desktop** spawns it as a **direct stdio `command` entry** in
  `claude_desktop_config.json` — identical to how mcp-bridge-setup wires board/calendar/etc.

```
Claude Code   ──HTTP──> localhost:8002/mcp ──supergateway(launchd)──> node openwhispr-server (stdio)
                                                                          │ reads READ-ONLY (sqlite3 CLI)
                                                                          └──> ~/Library/Application Support/open-whispr/
                                                                               transcriptions.db + audio/*.webm
Cowork Desktop ──spawns stdio directly──────────────────────────────────> node openwhispr-server   (§4)
```

The server reads the store **read-only** (via the `sqlite3` CLI; WAL lets it read safely alongside a
running OpenWhispr — though a *clean* shutdown can transiently remove the `-shm` file a read-only open
needs, see **Gotchas**) and owns the **watermark** (`state/watermark.json`) that
makes the voice ingest loop idempotent — `list_transcripts → route → mark_processed` never
re-emits a routed note. The board-side routing is owned by `/second-brain-ingest` + the voice
recipe; this skill only stands up the plumbing.

This add-on's one process (alongside mcp-bridge-setup's four core servers + two sidecars):
| process | what runs | env | bridge port | launchd label | in `.mcp.json`? |
|---|---|---|---|---|---|
| openwhispr (MCP bridge) | `node mcp/openwhispr-server/server.mjs` via supergateway | `OPENWHISPR_DB` + `OPENWHISPR_AUDIO_DIR` (the **real** store) | 8002 | `com.chiefofstaff.mcp-openwhispr` | **yes** (`http://localhost:8002/mcp`) |

**Tools (4):** `list_transcripts` (unprocessed-by-default, watermark-filtered), `get_transcript`
(full text + `audio_path` metadata), `get_watermark`, `mark_processed` (advances the watermark —
the idempotency primitive). See `mcp/openwhispr-server/README.md` for the full contract.

> Machine config comes from the loader (run the preamble in §1): it exports `$REPO_ROOT`,
> `$BREW_PREFIX`, `$NODE_BIN`, `$SUPERGATEWAY_BIN`, `$LAUNCH_AGENTS_DIR`, `$COWORK_CONFIG`,
> `$OPENWHISPR_DB`, `$OPENWHISPR_AUDIO_DIR`, and `$OPENWHISPR_BRIDGE_PORT` (=`8002`) /
> `$OPENWHISPR_BRIDGE_URL` (=`http://localhost:8002`) — use those instead of hardcoding paths, the
> Homebrew prefix, your username, or the port.

## Prerequisites
- **Node + npm** (Homebrew) and the server's deps: `(cd mcp/openwhispr-server && npm i)`.
- **`supergateway`** (`npm install -g supergateway`) — the stdio→HTTP bridge for Claude Code,
  exactly as in mcp-bridge-setup.
- **The OpenWhispr desktop app installed, with at least one recorded note** — this is the external
  dependency (the analog of whatsapp's phone pairing / guard's HuggingFace login). Without it there
  is no `transcriptions.db` to read; you can still wire everything against the bundled **fixtures**
  for a dry run (§1), but production needs the real store.

Run the loader preamble as the first line of every shell block below — it exports `$REPO_ROOT`,
`$NODE_BIN`, `$BREW_PREFIX`, `$SUPERGATEWAY_BIN`, `$LAUNCH_AGENTS_DIR`, `$COWORK_CONFIG`,
`$OPENWHISPR_DB`, `$OPENWHISPR_AUDIO_DIR`, the bridge port/URL, etc., so nothing below is
hardcoded. `$U=$(id -u)` is derived inline where `launchctl` needs it.
```sh
source "$(git rev-parse --show-toplevel)/config/load-config.sh"
```

## Steps

### 1. Confirm the OpenWhispr store (the external dependency)
The server reads OpenWhispr's Electron `userData` dir — `transcriptions.db` (transcript text) +
`audio/*.webm` (recordings, mapped to each note by the trailing `-<id>`). Check it exists:
```sh
source "$(git rev-parse --show-toplevel)/config/load-config.sh"
[ -f "$OPENWHISPR_DB" ] && echo "OpenWhispr store found: $OPENWHISPR_DB" \
                       || echo "No store at $OPENWHISPR_DB — install OpenWhispr + record a note, or use fixtures (below)"
ls -d "$OPENWHISPR_AUDIO_DIR" 2>/dev/null && echo "audio dir OK"
```
- **No store yet?** Either install OpenWhispr and record one note, or wire everything against the
  bundled **fixtures** for a dry run by setting `OPENWHISPR_FIXTURES="$REPO_ROOT/mcp/openwhispr-server/fixtures"`
  in §2–4 instead of `OPENWHISPR_DB` — but **never leave `OPENWHISPR_FIXTURES` set in the real
  plist/Cowork config** (that's the classic "stuck on fixtures" bug — see Gotchas).
- **CHECKPOINT** — `$OPENWHISPR_DB` exists (or you've consciously chosen the fixtures path).

### 2. Verify the stdio server runs
```sh
source "$(git rev-parse --show-toplevel)/config/load-config.sh"
(cd "$REPO_ROOT/mcp/openwhispr-server" && npm i >/dev/null 2>&1)
OPENWHISPR_DB="$OPENWHISPR_DB" OPENWHISPR_AUDIO_DIR="$OPENWHISPR_AUDIO_DIR" \
  "$NODE_BIN" "$REPO_ROOT/mcp/openwhispr-server/server.mjs"   # Ctrl-C; prints "openwhispr MCP server ready (tools: …)"
```
- **CHECKPOINT** — the ready line prints the four tool names. (To prove it sees the **real** store,
  §6's `list_transcripts` will report `Source: sqlite`.)

### 3. Install the launchd BRIDGE (`:8002`)
The plist is generated from `mcp/openwhispr-server/openwhispr.service.json` by
`scripts/gen-launchd.mjs` (see `mcp/CLAUDE.md`) — it renders the `:8002` supergateway bridge with
env `OPENWHISPR_DB` + `OPENWHISPR_AUDIO_DIR` (the **real** voice store), the idle-exit opt-in, and
`PATH` led by `$BREW_PREFIX/bin`, then bootout→bootstrap→kickstart in one step on macOS. Name
`openwhispr` explicitly so it installs only this add-on's plist (a bare `--install` would render just
the core services):
```sh
source "$(git rev-parse --show-toplevel)/config/load-config.sh"
mkdir -p "$REPO_ROOT/mcp/logs"
node "$REPO_ROOT/scripts/gen-launchd.mjs" --install openwhispr
```
- **CHECKPOINT** — an MCP `initialize` on `:8002` returns `serverInfo.name == "openwhispr"`:
  ```sh
  source "$(git rev-parse --show-toplevel)/config/load-config.sh"
  curl -s -X POST "$OPENWHISPR_BRIDGE_URL/mcp" \
    -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
    -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"c","version":"0"}}}' \
    | grep -o '"name":"openwhispr"' && echo "openwhispr bridge OK"
  ```

### 4. Register BOTH clients (each registers differently!)
As in mcp-bridge-setup, the two clients register **differently** — getting this wrong is the usual
failure.

**Claude Code — HTTP via the bridge.** `.mcp.json` is **generated** (CI-checked) from the manifest,
so don't hand-edit it — regenerate it and the `openwhispr` entry (`http://localhost:8002/mcp`, from
the descriptor's `clients`) lands alongside the core `board`/`calendar`/`guard`/`vault` entries:
```sh
source "$(git rev-parse --show-toplevel)/config/load-config.sh"
node "$REPO_ROOT/scripts/gen-mcp-json.mjs"
```

**Claude Cowork Desktop — direct stdio.** `claude_desktop_config.json` (`$COWORK_CONFIG`) accepts
**stdio `command` servers only** (an HTTP `url` is rejected — same validated rule as
mcp-bridge-setup §5). `gen-cowork-config.mjs` does a **backup-first, additive merge** — it preserves
the other servers + `preferences` and refreshes only the named `openwhispr` entry from the descriptor
(absolute `$NODE_BIN`, the **real** `OPENWHISPR_DB`/`OPENWHISPR_AUDIO_DIR`, never `OPENWHISPR_FIXTURES`):
```sh
source "$(git rev-parse --show-toplevel)/config/load-config.sh"
node "$REPO_ROOT/scripts/gen-cowork-config.mjs" openwhispr
```
- **Quit + reopen Claude Desktop (⌘Q)** — it reads this file only at launch; then `openwhispr`
  appears in Cowork's tools.
- **CHECKPOINT** — both registrations present:
  ```sh
  source "$(git rev-parse --show-toplevel)/config/load-config.sh"
  grep -q '"openwhispr"' "$REPO_ROOT/.mcp.json" && echo ".mcp.json OK"
  "$NODE_BIN" -e 'const c=require(process.env.COWORK_CONFIG);process.exit(c.mcpServers&&c.mcpServers.openwhispr?0:1)' \
    && echo "Cowork config OK"
  ```

### 5. Confirm `mcp/ensure-bridges.sh` picks it up (automatic — no edit)
`ensure-bridges.sh` is a thin consumer of the manifest: its service + probe list comes from
`node mcp/service-manifest.mjs --probe-list`, so the committed `openwhispr.service.json` descriptor
(`probe: httpListen`) is bootstrapped + probed with **no edit** to the script (see `mcp/CLAUDE.md`).
A machine without the add-on installed (no plist) is skipped silently; an installed-but-down one
WARNs.
- **CHECKPOINT** — the script bootstraps it and reports it up:
  ```sh
  source "$(git rev-parse --show-toplevel)/config/load-config.sh"
  "$REPO_ROOT/mcp/ensure-bridges.sh" | grep -i openwhispr   # expect: "[mcp] openwhispr up on :8002"
  ```

### 6. End-to-end verify (proves it reads the REAL store)
```sh
source "$(git rev-parse --show-toplevel)/config/load-config.sh"
curl -s -X POST "$OPENWHISPR_BRIDGE_URL/mcp" \
  -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_transcripts","arguments":{"limit":3}}}'
# expect the result text to begin "Source: sqlite | watermark: …" — NOT "Source: fixtures"
```
- **CHECKPOINT** — `list_transcripts` returns `Source: sqlite` (your live notes) and both clients
  see `openwhispr` after a ⌘Q reopen. The voice ingest loop (`list_transcripts → get_transcript →
  route to vault/board → mark_processed`) is then exercised by **`/voice-operations`** (the reads +
  watermark) + **`/second-brain-ingest`** (the routing); this skill only proves the plumbing.

## Manage
```sh
launchctl kickstart -k gui/$(id -u)/com.chiefofstaff.mcp-openwhispr   # restart the bridge
launchctl bootout      gui/$(id -u)/com.chiefofstaff.mcp-openwhispr   # stop the bridge (e.g. on a machine without OpenWhispr)
tail -f mcp/logs/openwhispr.err.log                                   # supergateway / server log
```
**Remove the add-on entirely** (machine without OpenWhispr): `launchctl bootout` the agent, delete
`~/Library/LaunchAgents/com.chiefofstaff.mcp-openwhispr.plist`, and delete `cfg.mcpServers.openwhispr`
from the Cowork config by hand (`gen-cowork-config openwhispr` is **additive** — rerunning it would
re-add the entry, so don't). `.mcp.json` and `ensure-bridges.sh` are committed/manifest-driven, so
leave them as-is — once the plist is gone, `ensure-bridges.sh` skips the un-installed agent silently
and nothing answers on `:8002`.

## Gotchas (read before editing)
- **Never leave `OPENWHISPR_FIXTURES` set in the real plist/Cowork config.** It is the
  highest-precedence source and **wins over the real DB** — so a stray `OPENWHISPR_FIXTURES` (handy
  for a dry run in §1–2) silently serves the three bundled test notes instead of your voice notes.
  The tell: `list_transcripts` reports `Source: fixtures` instead of `Source: sqlite`. Production
  sets only `OPENWHISPR_DB` + `OPENWHISPR_AUDIO_DIR`.
- **The bridge reads the store directly — the OpenWhispr app need not be running.** It uses the
  `sqlite3` CLI read-only (WAL allows concurrent readers), and maps each transcript to its `.webm`
  by the trailing `-<id>`. OpenWhispr's own loopback "CLI bridge" (random port 8200–8219) exposes
  **no read endpoint for audio** and vanishes when the app quits, so this server never depends on it.
  (One caveat — next bullet — a *clean* OpenWhispr shutdown can transiently remove the WAL `-shm` file a
  read-only open needs.)
- **WAL + read-only: a missing `-shm` can fail the open (SQLite error 14).** A read-only open needs the
  WAL `-shm` shared-memory file, and a `-readonly` connection **cannot create** it — so after OpenWhispr
  does a clean shutdown (which checkpoints and removes `-wal`/`-shm`), `list_transcripts` can fail with
  `unable to open database file`. It recovers as soon as any writer recreates `-shm`: **open the
  OpenWhispr app once** (it self-heals on its next write). The containing dir must also be writable — it
  is by default (`~/Library/Application Support/open-whispr`, `drwx------`, yours). To make the server
  immune, harden its `sqliteJson` open to retry with `immutable=1` when the plain `-readonly` open fails
  — that fallback only triggers when `-shm` is absent (i.e. the WAL was already checkpointed), so it sees
  complete data with no staleness risk.
- **The watermark is this server's only writable state** — `state/watermark.json` (override
  `OPENWHISPR_STATE`). It's how `mark_processed` keeps ingestion idempotent; it is **not** covered
  by Cos's encrypted backup (which protects the board/guard/config/vault stores), and it's safely
  reconstructible — at worst the loop re-emits already-routed notes, which dedupe on the board.
- **Port `8002` must be free** (`lsof -nP -iTCP:8002 -sTCP:LISTEN`). It sits between the core
  bridges (`8001` board, `8003` calendar, `8004` guard, `8005` vault). A foreign process on it
  breaks §3 — pick another free port via `OPENWHISPR_BRIDGE_PORT` in `cos.env` and re-render.
- **launchd cannot expand `$VARS`.** As with every Cos plist, the **rendered** plist in
  `~/Library/LaunchAgents` carries **literal absolute paths** and a `PATH` that starts with
  `$BREW_PREFIX/bin` — launchd never inherits your login shell and can't see an nvm/asdf shim.
- **The node/simdjson + pm2 gotchas in mcp-bridge-setup apply here too** — same Node, same
  supergateway, same launchd supervisor. If the bridge dies with a `libsimdjson` dyld error after a
  `brew install`, `brew reinstall node`. Don't reintroduce pm2.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.