Skip to content

Releases: docdyhr/macwhisper-mcp-server

v1.2.0

Choose a tag to compare

@docdyhr docdyhr released this 13 Aug 07:27

Added

  • transcribe_audio accepts an optional language argument (ISO 639-1 code,
    or "auto"), passed through as --language to mw.
  • MACWHISPER_LANGUAGE_DEFAULTS env var maps directories to a default
    language (e.g. ~/Desktop/DK=da) — files in a matching directory (or its
    subdirectories) get --language automatically, most-specific match wins.
    An explicit language argument always overrides the directory default.
    Applies to both transcribe_audio and watch-folder transcriptions.
  • New engine argument on transcribe_audio ("macwhisper" default, or
    "whisper-cpp") — an independent transcription backend using a standalone
    whisper-cli binary that does not go through MacWhisper at all. New
    MACWHISPER_WHISPERCPP_BINARY / MACWHISPER_WHISPERCPP_MODEL_DIR env vars.
    v1 supports .wav/.mp3/.flac input only; see README for setup and
    limitations. list_models() now also lists whisper-cpp models when
    MACWHISPER_WHISPERCPP_MODEL_DIR is configured.

Changed

  • transcribe.py now validates the request and dispatches to a new
    engines.py module (MacWhisper and whisper-cpp backends); behavior of the
    MacWhisper path is unchanged. Test mock target for subprocess.Popen moved
    from macwhisper_mcp.transcribe to macwhisper_mcp.engines accordingly.
  • The 10 MB output cap is now a single shared MAX_OUTPUT_BYTES constant in
    config.py, imported by transcribe.py and watcher.py so the two paths
    can never enforce different limits.

Fixed

  • cancel_transcription no longer raises IndexError (surfaced to the client
    as an error) when the running transcription finishes and clears the proc
    list in the gap between cancel's non-empty check and its subscript. Cancel
    now uses a single atomic subscript and degrades to "no transcription
    running" instead.
  • __version__ is now read from the installed distribution metadata instead of
    a hardcoded literal, so it can no longer drift from pyproject.toml on
    semantic-release bumps.
  • start_watch now rejects a watch session when the "done" directory falls
    outside the configured allow-list (previously it silently wrote there). The
    done directory is overridable via the new MACWHISPER_WATCH_DONE_DIR env
    var; the default remains <incoming>/../done.

v1.1.1

Choose a tag to compare

@docdyhr docdyhr released this 22 Jun 22:09

What's changed

Changed

  • fastmcp bumped 3.2.4 → 3.4.2. No API changes affect this server; the bump picks up upstream bug fixes and performance improvements.

Fixed

  • Access-denied error messages now include the allow-listed paths so the LLM (and user) understand why a path was rejected without requiring a separate `list_allowed_paths()` call.
  • `transcribe_audio` tool description clarifies that files must be on the local filesystem, not in Claude's container — suppresses a common LLM fallback.
  • Disabled FastMCP's built-in update-check nag on server startup (`FASTMCP_CHECK_FOR_UPDATES=off` is no longer required in the Claude Desktop config).
  • CodeQL CI workflow now triggers on the correct branch (`main`).

Documentation

  • Added Homebrew tap as the recommended install method:
    ```bash
    brew tap docdyhr/tap
    brew install docdyhr/tap/macwhisper-mcp-server
    ```
  • Claude Desktop config simplified: `"command": "macwhisper-mcp"` (no hardcoded venv path) when installed via Homebrew or `pip install`.
  • MIT `LICENSE` file added to repo root.

Full changelog: https://github.com/docdyhr/macwhisper-mcp-server/blob/main/CHANGELOG.md

v1.1.0

Choose a tag to compare

@docdyhr docdyhr released this 25 Apr 19:11

Added

  • list_models() MCP tool — runs mw models list and returns installed
    MacWhisper models with display names; the active model is marked [active].
    Model IDs returned can be passed directly to transcribe_audio(model=…).
  • persist parameter on transcribe_audio — pass persist=true to save the
    transcription to MacWhisper's history database (mw --persist).
  • tests/test_server.py — 13 tests covering all 8 MCP tools including list_models
    and persist, concurrency lock, lock-release-on-exception, and cancel.

Fixed

  • watcher.py: apply 10 MB output size cap to watcher transcriptions, matching
    the existing cap in transcribe.py.
  • publish.yml: publish job now requires tests to pass (new test job that build
    depends on), preventing broken releases via workflow_dispatch.
  • ci.yml: corrected cache-dependency-path from stale requirements*.txt to
    pyproject.toml.
  • pyproject.toml: narrowed requires-python from >=3.10 to >=3.13 to match
    the Python version actually tested and supported.
  • .gitignore: added server.json (MCP Registry publish artifact).

Documentation

  • README: added Homebrew install path, mw version verify command, updated tools
    table with list_models and persist.
  • CLAUDE.md: updated status, layout, known quirks, and mock path conventions to
    reflect current codebase.

v1.0.0 — First stable release

Choose a tag to compare

@docdyhr docdyhr released this 25 Apr 04:45

First stable public release. All five development phases complete: MVP → hardening
→ structured output → ergonomics → security. 7 MCP tools, 33 tests, fully local.

Security

  • Resolve-before-validate in transcribe(). Path symlinks are now fully resolved
    before extension and allow-list checks, closing a TOCTOU race where a symlink could
    change between validation and CLI invocation.
  • Null-byte rejection. Paths containing \x00 are rejected immediately in
    transcribe() before any filesystem access.
  • Model identifier sanitization. The model parameter is now validated against
    [a-zA-Z0-9_:.-]+ before being passed to the CLI, preventing argument injection.
  • No allow-list leakage in errors. "Access denied" messages no longer include the
    full allow-list; this was an information-disclosure issue.
  • start_watch() folder validation. The folder path is now checked against the
    allow-list before starting the watcher; previously any directory could be watched.
  • Symlink rejection in watcher. FolderWatcher._scan() skips symbolic links;
    a symlink inside incoming/ pointing outside the allow-list could otherwise cause
    MacWhisper to read arbitrary files.
  • MACWHISPER_LOG_PATH must be under $HOME. Config.from_env() rejects log
    paths outside the user's home directory to prevent log-file hijacking.
  • Output size cap. transcribe() raises TranscribeError if mw stdout exceeds
    10 MB, guarding against runaway output consuming memory.