audacity-mcp-server 0.1.17__tar.gz

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.
Files changed (45) hide show
  1. audacity_mcp_server-0.1.17/.github/workflows/ci.yml +30 -0
  2. audacity_mcp_server-0.1.17/.github/workflows/publish.yml +58 -0
  3. audacity_mcp_server-0.1.17/.gitignore +45 -0
  4. audacity_mcp_server-0.1.17/CHANGELOG.md +221 -0
  5. audacity_mcp_server-0.1.17/CONTRIBUTING.md +72 -0
  6. audacity_mcp_server-0.1.17/LICENSE +190 -0
  7. audacity_mcp_server-0.1.17/PKG-INFO +562 -0
  8. audacity_mcp_server-0.1.17/README.md +536 -0
  9. audacity_mcp_server-0.1.17/audacity_mcp/__init__.py +0 -0
  10. audacity_mcp_server-0.1.17/audacity_mcp/audacity_client.py +347 -0
  11. audacity_mcp_server-0.1.17/audacity_mcp/main.py +20 -0
  12. audacity_mcp_server-0.1.17/audacity_mcp/setup_transcription.py +136 -0
  13. audacity_mcp_server-0.1.17/audacity_mcp/tool_registry.py +12 -0
  14. audacity_mcp_server-0.1.17/audacity_mcp/tools/__init__.py +0 -0
  15. audacity_mcp_server-0.1.17/audacity_mcp/tools/analysis_tools.py +86 -0
  16. audacity_mcp_server-0.1.17/audacity_mcp/tools/cleanup_tools.py +1450 -0
  17. audacity_mcp_server-0.1.17/audacity_mcp/tools/edit_tools.py +74 -0
  18. audacity_mcp_server-0.1.17/audacity_mcp/tools/effects_tools.py +550 -0
  19. audacity_mcp_server-0.1.17/audacity_mcp/tools/generate_tools.py +156 -0
  20. audacity_mcp_server-0.1.17/audacity_mcp/tools/label_tools.py +137 -0
  21. audacity_mcp_server-0.1.17/audacity_mcp/tools/project_tools.py +205 -0
  22. audacity_mcp_server-0.1.17/audacity_mcp/tools/selection_tools.py +92 -0
  23. audacity_mcp_server-0.1.17/audacity_mcp/tools/track_tools.py +134 -0
  24. audacity_mcp_server-0.1.17/audacity_mcp/tools/transcription_tools.py +679 -0
  25. audacity_mcp_server-0.1.17/audacity_mcp/tools/transport_tools.py +57 -0
  26. audacity_mcp_server-0.1.17/audacity_mcp_shared/__init__.py +3 -0
  27. audacity_mcp_server-0.1.17/audacity_mcp_shared/constants.py +101 -0
  28. audacity_mcp_server-0.1.17/audacity_mcp_shared/error_codes.py +33 -0
  29. audacity_mcp_server-0.1.17/audacity_mcp_shared/pipe_protocol.py +69 -0
  30. audacity_mcp_server-0.1.17/docs/INSTALLATION.md +368 -0
  31. audacity_mcp_server-0.1.17/docs/TOOLS.md +932 -0
  32. audacity_mcp_server-0.1.17/docs/images/mod-script-pipe-enable.png +0 -0
  33. audacity_mcp_server-0.1.17/install.bat +340 -0
  34. audacity_mcp_server-0.1.17/install.sh +331 -0
  35. audacity_mcp_server-0.1.17/pyproject.toml +52 -0
  36. audacity_mcp_server-0.1.17/setup_gpu.bat +32 -0
  37. audacity_mcp_server-0.1.17/setup_gpu.sh +31 -0
  38. audacity_mcp_server-0.1.17/tests/__init__.py +0 -0
  39. audacity_mcp_server-0.1.17/tests/conftest.py +10 -0
  40. audacity_mcp_server-0.1.17/tests/test_client.py +80 -0
  41. audacity_mcp_server-0.1.17/tests/test_label_tools.py +104 -0
  42. audacity_mcp_server-0.1.17/tests/test_pipe_paths.py +100 -0
  43. audacity_mcp_server-0.1.17/tests/test_pipe_protocol.py +146 -0
  44. audacity_mcp_server-0.1.17/tests/test_tools.py +82 -0
  45. audacity_mcp_server-0.1.17/tests/test_transcription.py +468 -0
@@ -0,0 +1,30 @@
1
+ name: CI
2
+
3
+ "on":
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ os: [ubuntu-latest, windows-latest, macos-latest]
16
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
17
+
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Set up Python ${{ matrix.python-version }}
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: ${{ matrix.python-version }}
25
+
26
+ - name: Install dependencies
27
+ run: pip install -e ".[dev]"
28
+
29
+ - name: Run tests
30
+ run: pytest tests/ -x -q
@@ -0,0 +1,58 @@
1
+ name: Publish to PyPI
2
+
3
+ # Fires automatically on every version tag push (git tag vX.Y.Z && git push
4
+ # --tags), or manually via workflow_dispatch — no manual `twine upload` step,
5
+ # and no PyPI token stored anywhere in this repo. Uses PyPI's Trusted
6
+ # Publisher (OIDC) mechanism instead: PyPI is configured (once, on pypi.org)
7
+ # to trust this exact workflow in this exact repo, so GitHub can prove its
8
+ # identity to PyPI without a shared secret to store, rotate, or leak.
9
+ "on":
10
+ push:
11
+ tags:
12
+ - "v*"
13
+ workflow_dispatch:
14
+
15
+ jobs:
16
+ test:
17
+ # Gate: publishing requires the tagged commit itself to pass tests, not
18
+ # just whatever main's CI run said earlier — a tag can point anywhere.
19
+ runs-on: ${{ matrix.os }}
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ os: [ubuntu-latest, windows-latest, macos-latest]
24
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ - uses: actions/setup-python@v5
28
+ with:
29
+ python-version: ${{ matrix.python-version }}
30
+ - run: pip install -e ".[dev]"
31
+ - run: pytest tests/ -x -q
32
+
33
+ build:
34
+ needs: test
35
+ runs-on: ubuntu-latest
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+ - uses: actions/setup-python@v5
39
+ with:
40
+ python-version: "3.12"
41
+ - run: python -m pip install --upgrade build
42
+ - run: python -m build
43
+ - uses: actions/upload-artifact@v4
44
+ with:
45
+ name: dist
46
+ path: dist/
47
+
48
+ publish:
49
+ needs: build
50
+ runs-on: ubuntu-latest
51
+ permissions:
52
+ id-token: write # required for Trusted Publishing — do not remove
53
+ steps:
54
+ - uses: actions/download-artifact@v4
55
+ with:
56
+ name: dist
57
+ path: dist/
58
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,45 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ *.egg-info/
7
+ *.egg
8
+ dist/
9
+ build/
10
+ .eggs/
11
+
12
+ # Virtual environments
13
+ venv/
14
+ .venv/
15
+ env/
16
+ .env/
17
+
18
+ # IDE
19
+ .vscode/
20
+ .idea/
21
+ *.swp
22
+ *.swo
23
+ *~
24
+
25
+ # Testing
26
+ .pytest_cache/
27
+ .coverage
28
+ htmlcov/
29
+ .tox/
30
+
31
+ # OS
32
+ .DS_Store
33
+ Thumbs.db
34
+ desktop.ini
35
+
36
+ # Whisper models (large, downloaded at runtime)
37
+ *.bin
38
+ *.pt
39
+
40
+ # Claude Code
41
+ .claude/
42
+ CLAUDE.md
43
+
44
+ # Local MCP config (contains user-specific paths)
45
+ .mcp.json
@@ -0,0 +1,221 @@
1
+ # Changelog
2
+
3
+ All notable changes to AudacityMCP will be documented in this file.
4
+
5
+ ## [0.1.17] - 2026-08-01
6
+
7
+ ### PyPI Package Renamed to `audacity-mcp-server`
8
+
9
+ Setting up the new tokenless PyPI publish workflow (see 0.1.16 below) surfaced that the `audacity-mcp` project on PyPI belongs to a different PyPI account than the one publishing this repo going forward, so Trusted Publishing couldn't be linked to it. Rather than depend on access to that other account, the package is published under a new name this account owns outright.
10
+
11
+ - PyPI package renamed: `audacity-mcp` → **`audacity-mcp-server`** (`pip install audacity-mcp-server`).
12
+ - The installed CLI command is unaffected — it's still `audacity-mcp` (and `audacity-mcp-setup-gpu`), since `[project.scripts]` in `pyproject.toml` is independent of the package's PyPI name. **No existing Claude Desktop config needs to change.**
13
+ - Updated `README.md` and `docs/INSTALLATION.md` pip-install references accordingly.
14
+ - The old `audacity-mcp` PyPI project is not affiliated with this repo and will not receive further updates from here.
15
+
16
+ ## [0.1.16] - 2026-08-01
17
+
18
+ ### install.bat/install.sh: Claude Desktop Never Getting Configured on Windows, and a Redundant Reinstall
19
+
20
+ A user reported the installer didn't seem to actually connect AudacityMCP to Claude Desktop.
21
+
22
+ - **Root cause**: the Microsoft Store / MSIX build of Claude Desktop redirects its `%APPDATA%` writes into an isolated per-package folder (`%LOCALAPPDATA%\Packages\Claude_<id>\LocalCache\Roaming\Claude\`) instead of the standard `%APPDATA%\Claude\` path. `install.bat` only ever wrote to the standard path, which that build never reads — so the config step silently did nothing useful for anyone on the Store build.
23
+ - **Fix**: config writing is now a shared subroutine, called once for the standard path and once for every `Claude_*` folder found under `%LOCALAPPDATA%\Packages\`, so both install types get configured.
24
+
25
+ **The installer was also always re-fetching the package it was sitting right next to.** Both scripts ran `pip install audacity-mcp` unconditionally — fetching fresh from PyPI even when run from inside a just-cloned/downloaded copy of the repo, which is the only way you'd have `install.bat`/`install.sh` in the first place. That's a pointless redundant download, and it also meant the installer could install an older *published* version instead of whatever fixes were sitting in the local copy the user just got.
26
+
27
+ - Both scripts now always install from the local folder the script itself lives in (`pip install "<script's own folder>"`), never from PyPI or GitHub.
28
+ - If the script is ever separated from the rest of the repo (moved or downloaded standalone, no `pyproject.toml` next to it), it now refuses to run with a clear error instead of silently trying to fetch the code from somewhere else.
29
+ - Fixed a related display bug in `install.bat`'s Python check: `%PYVER%` was expanding at parse-time (before the `for /f` that sets it, inside the same `if (...)` block) so the detected version always printed blank — `Found Python - already installed`. Switched to delayed expansion (`!PYVER!`).
30
+
31
+ **Other hardening in this pass, since the whole install flow was under review:**
32
+
33
+ - Added `--dry-run` (`-n` on macOS/Linux) to both scripts — prints every action (package install, Audacity config edit, Claude Desktop config edit) without changing anything.
34
+ - Both scripts now explain what the Claude Desktop config step does and ask for an explicit y/n confirmation *before* touching that file at all, not just before overwriting it — matching the existing confirmation already in place for Audacity's config.
35
+ - Stripped a handful of non-ASCII characters (`──`, `—`) from `install.bat` that could cause cmd.exe to misparse the file under some codepages; both scripts are now pure ASCII.
36
+ - Restructured (not removed) the Python-missing detection/auto-install flow in both scripts to be more reliable about skipping the winget/brew/apt/dnf/pacman install offer when Python is already present.
37
+ - Updated `README.md` and `docs/INSTALLATION.md` to match: Quick Start / Option A now walks through "download or clone the repo, then run the installer from inside it" instead of a standalone single-file download or `curl | bash` one-liner (which no longer works now that the script requires the repo around it).
38
+
39
+ ## [0.1.15] - 2026-07-29
40
+
41
+ ### Transcription: Wrong-Language Retries, and Model Re-Downloads
42
+
43
+ Two separate reports while using transcription on real files.
44
+
45
+ **Retrying a bad language auto-detect required switching tools entirely.** A user's English audio got auto-detected and transcribed as Japanese. Fixing it meant removing the label track and starting over — but `transcribe_to_labels` and `transcribe_to_file` hardcoded `task="transcribe"` and never exposed the parameter at all, so there was no way to retry the *same* tool with `task="translate"` (forces English output) or an explicit `language`. The only way to recover was switching to `transcribe_audio` and manually reconstructing the labels from its output — exactly the tangle it took another Claude session an extra half-dozen tool calls to work around.
46
+
47
+ - Added `task` to `transcribe_to_labels` and `transcribe_to_file`, matching `transcribe_audio`/`transcribe_selection`.
48
+ - Added docstring guidance across all four transcription tools: auto-detect can occasionally misidentify the language (background music, noise, short/ambiguous clips) — if you already know the language, pass it explicitly instead of trusting auto-detect, and retry with the *same* tool rather than switching.
49
+
50
+ **A model that was already downloaded appeared to re-download on first use.** `_get_cache_dir()` hardcoded `~/.cache/huggingface/hub`, ignoring `HF_HOME`/`HUGGINGFACE_HUB_CACHE`. The manual pre-download command in the setup docs uses huggingface_hub's own default resolution, which *does* honor those variables — so anyone who has ever redirected their HF cache (common for moving model storage to a bigger/different drive) would have the pre-downloaded model in one place and the running server hardcoded to look in another, re-downloading every time. Not reproduced on this dev machine (no mismatch here), but the hardcoded-path bug is real and independently verifiable in the source regardless.
51
+
52
+ - `_get_cache_dir()` now checks `HUGGINGFACE_HUB_CACHE`, then `HF_HOME`, before falling back to the hardcoded default — the fallback still exists for its original purpose (some MCP subprocesses on Windows can't resolve huggingface_hub's own default cache path).
53
+ - Added `tests/test_transcription.py::TestGetCacheDir` and extended `TestTranscribeToLabels`/`TestTranscribeToFile` for the `task` parameter.
54
+
55
+ ## [0.1.14] - 2026-07-29
56
+
57
+ ### Long Transcriptions Getting Silently Truncated
58
+
59
+ Reported by a user: a 48-minute file only got ~23 minutes labeled, a 4.5-hour file only got ~45 minutes labeled — both cut off partway through, no error shown.
60
+
61
+ - **Root cause**: `_cleanup_stale_jobs()` killed any transcription job running longer than `_STALE_JOB_TIMEOUT` (10 minutes) measured from **job start**, regardless of whether it was still actively progressing. The label-adding loop does a `SelectTime`+`AddLabel`+`SetLabel` round trip *per segment* — a multi-hour transcript can have thousands of segments, so that loop alone can legitimately run past 10 minutes even when working correctly. The next time `check_transcription_status` got polled after the 10-minute mark, it silently cancelled the still-running task mid-loop, leaving only whatever labels had been added so far.
62
+ - **Fix**: staleness is now measured from **time since last progress**, not time since start. Every step transition and every single label added now updates a `last_progress_at` timestamp; a job only gets killed after 10 minutes with *no* forward progress (i.e. actually stuck), not just for running long on a big file. Also threaded progress reporting through the transcription step itself (`_run_transcription` now accepts an `on_progress` callback, called per-segment), so a slow CPU transcription of a very long file can't hit the same wall before labeling even starts.
63
+ - Added `tests/test_transcription.py::TestStaleJobCleanup` and `TestRunTranscriptionProgress`.
64
+
65
+ ## [0.1.13] - 2026-07-28
66
+
67
+ ### Labels Overwriting Each Other
68
+
69
+ Reported by a user: transcribing with `add_labels=True` (or adding several labels via `label_add`/`label_add_at` in a row, e.g. asking Claude to mark episode boundaries in a long track) left every label blank except the first, which ended up with the *last* label's text.
70
+
71
+ - **Root cause**: `label_add`, `label_add_at`, and the transcription auto-labeling loop all called `SetLabel(Label=0, ...)` unconditionally after `AddLabel`. `AddLabel` doesn't report back the index of the label it just created, and `Label=0` doesn't mean "the label just added" — it means "the very first label in the project," full stop. So every `SetLabel` call kept retargeting that same first label, overwriting it each time, while every other newly-created label stayed blank.
72
+ - **Fix**: added `count_existing_labels()` (`audacity_mcp/tools/label_tools.py`) — queries `GetInfo Type=Labels`, parses the JSON, and counts labels already in the project. Since `AddLabel` appends, the new label's index is exactly that count (or `count + i` for the i-th label added in a batch, in `label_add`/`label_add_at`/the transcription loop). Confirmed via a related [Audacity GitHub issue](https://github.com/audacity/audacity/issues/1577) that label indices are assigned in creation order, 0-based — the same assumption this fix relies on.
73
+ - **Known caveat, not addressed here**: that same GitHub issue shows `SetLabel` can fail past label index ~100 in some Audacity versions. Not something to work around speculatively without knowing which versions are affected, but worth knowing if a transcript has 100+ segments.
74
+ - **Not independently tested against a live Audacity instance** — verified via unit tests with mocked `GetInfo`/`SetLabel` responses and the corroborating GitHub issue, since no running Audacity was available to test against directly. Please verify against real Audacity before considering this fully closed.
75
+ - Added `tests/test_label_tools.py`.
76
+
77
+ ## [0.1.12] - 2026-07-28
78
+
79
+ ### GPU Detection False Negative
80
+
81
+ Reported by a user who ran `audacity-mcp-setup-gpu` (which confirmed GPU transcription worked) and restarted Claude Desktop, but the actual server logs still showed `Loading whisper model 'small' on CPU...` with no "GPU failed" message in between — meaning `_cuda_is_available()` returned `False` before a GPU attempt was even made.
82
+
83
+ - **Fixed `_cuda_is_available()`** (`audacity_mcp/tools/transcription_tools.py`): it trusted `torch.cuda.is_available()` exclusively when torch was importable, returning immediately on `False` without ever running the actual check that matters. faster-whisper runs on CTranslate2, not torch — a coincidentally-installed CPU-only (or mismatched-CUDA) torch build could silently mask a perfectly working `nvidia-cublas-cu12`/`nvidia-cudnn-cu12` install. Now torch is only trusted for a *positive* answer; `False` or an import failure falls through to the real `cublas64_12.dll`/`libcublas.so` load check.
84
+ - Most likely underlying cause for this specific report is still an environment mismatch (packages installed where `setup_gpu` ran vs. the Python Claude Desktop's config actually launches) — the torch issue is a separate, independently real bug found investigating the same report.
85
+ - Added `tests/test_transcription.py::TestCudaIsAvailable` covering: torch-False-falls-through, torch-True-short-circuits, no-torch-no-cublas, and the macOS no-CUDA path.
86
+
87
+ ## [0.1.11] - 2026-07-28
88
+
89
+ ### Double-Clickable GPU Setup
90
+
91
+ - **Added `setup_gpu.bat` (Windows) and `setup_gpu.sh` (macOS/Linux)** at the repo root, alongside `install.bat`/`install.sh`. Same one-click pattern: no terminal, no typed commands — just download and double-click (or run). They wrap `python -m audacity_mcp.setup_transcription` (not the bare `audacity-mcp-setup-gpu` command) so they only depend on `python` being on PATH, not pip's Scripts directory too. `setup_gpu.bat` runs via `cmd.exe`, not PowerShell, so it isn't affected by PowerShell's execution-policy restriction that blocks `.ps1` scripts by default on Windows.
92
+ - `install.bat`/`install.sh` now mention `setup_gpu.bat`/`setup_gpu.sh` in their "Next steps" so new users discover GPU setup without needing to read the docs.
93
+ - README + `docs/INSTALLATION.md` link to both scripts as an alternative to the `audacity-mcp-setup-gpu` command.
94
+
95
+ ## [0.1.10] - 2026-07-28
96
+
97
+ ### GPU Transcription Setup & Clarity
98
+
99
+ Prompted by a user report of transcription "seeming to time out / run on CPU" with no clear way to check why:
100
+
101
+ - **New `audacity-mcp-setup-gpu` command**: one-step GPU setup for transcription. Detects an NVIDIA GPU via `nvidia-smi`, installs `nvidia-cublas-cu12`/`nvidia-cudnn-cu12` into the exact Python environment `audacity-mcp` itself runs from (installing into the wrong environment was a real, common failure mode with the old manual instructions), and then actually loads a model on the GPU to confirm it works — instead of finding out later that it silently fell back to CPU. Cleanly reports "no NVIDIA GPU detected" and exits successfully (CPU is fine) rather than erroring when there's no GPU to find.
102
+ - **Fixed a real bug in `_setup_cuda_path()`** (`audacity_mcp/tools/transcription_tools.py`): it read `nvidia.cublas.__file__`/`nvidia.cudnn.__file__` to locate the DLL directories, but current `nvidia-cublas-cu12`/`nvidia-cudnn-cu12` releases are PEP 420 namespace packages with no `__init__.py`, so `__file__` is `None` — the function's own guard against that silently skipped every time, meaning it never actually added anything to PATH. Switched to `__path__`, which is always populated. (In practice this wasn't the root cause of the CPU-fallback report — testing showed `ctranslate2` finds the DLLs on its own regardless — but it was still dead, broken code worth fixing since it was found in the course of building the setup script.)
103
+ - **Documented in README + `docs/INSTALLATION.md`**: GPU acceleration is NVIDIA-only — AMD/Intel graphics and macOS aren't supported by faster-whisper's CTranslate2 backend at all, no driver update fixes that. GeForce vs. Quadro/RTX-workstation/older-GTX doesn't matter; any reasonably current NVIDIA GPU works. Added manual verification steps (Task Manager GPU usage, `nvidia-smi`, direct `WhisperModel` repro) for anyone who wants to check without the new script.
104
+ - Added `tests/test_transcription.py::TestSetupCudaPath` regression test for the namespace-package fix.
105
+
106
+ ## [0.1.9] - 2026-07-25
107
+
108
+ ### Snap / Flatpak Audacity Support (Linux)
109
+
110
+ Reported in [#7](https://github.com/xDarkzx/Audacity-MCP/issues/7): Snap-packaged Audacity on Ubuntu sandboxes `/tmp`, so the hardcoded pipe path never matched anything and the installer failed silently. Investigated the same class of issue across every OS/packaging combination we ship for (Windows installer/portable, macOS DMG/Homebrew/portable, Linux native/Snap/Flatpak/AppImage) and fixed the two real bugs found:
111
+
112
+ - **Pipe path auto-detection**: `PipePaths.resolve()` now re-detects the pipe directory on every connection instead of hardcoding `/tmp` at import time. It checks an `AUDACITY_PIPE_DIR` override first, then the plain `/tmp` path, then falls back to walking `/proc/*/comm` for a process named `audacity` and using `/proc/<pid>/root/tmp` — but only if both FIFOs actually exist there for the current uid, so a confined Audacity with mod-script-pipe disabled doesn't get mistaken for a working pipe. This isn't Snap-specific — it also fixes Flatpak, which sandboxes `/tmp` the same way via bubblewrap.
113
+ - **Installer config-path detection (`install.sh`)**: now falls back to Snap's (`~/snap/audacity/current/.config/audacity/audacity.cfg`) and Flatpak's (`~/.var/app/org.audacityteam.Audacity/config/audacity/audacity.cfg`) config locations when the standard `$XDG_CONFIG_HOME` path is missing, instead of stopping at step 3 with "config not found."
114
+ - **Documented, not fixed**: Audacity's portable mode (a `Portable Settings` folder next to the executable, on any OS) relocates `audacity.cfg` outside every path above. No user has hit this yet, and auto-detecting it requires locating Audacity's install directory, which the installer doesn't do — added a troubleshooting note (README + `docs/INSTALLATION.md`) with the manual workaround instead of building speculative detection for an unreported case.
115
+ - Added `tests/test_pipe_paths.py` covering the detection logic (override precedence, matching process found, FIFOs missing, non-`audacity` process skipped, non-Linux no-op).
116
+
117
+ ## [0.1.7] - 2026-04-10
118
+
119
+ ### macOS / Linux Compatibility
120
+
121
+ - **Fixed macOS import crash**: `ctypes.wintypes.HANDLE` type annotation was evaluated at class definition time on all platforms, causing `NameError` on macOS/Linux. Fixed with `from __future__ import annotations` for lazy evaluation.
122
+ - **Fixed cross-platform CUDA detection**: `_cuda_is_available()` hardcoded Windows DLL (`cublas64_12.dll`). Now uses `torch.cuda.is_available()` with platform-specific fallbacks (returns `False` on macOS, checks `libcublas.so` on Linux).
123
+ - **Fixed macOS pipe paths**: Merged community PR — pipes now use `os.getuid()` for correct user-specific paths instead of hardcoded UID 0.
124
+ - **Added macOS/Linux system directory protection**: `_safe_path()` now blocks `/System`, `/Library`, `/usr`, `/bin`, `/sbin`, `/etc`, `/var` on Unix systems. Previously only blocked Windows system directories.
125
+ - **Fixed path comparison**: Replaced `.lower()` with `os.path.normcase()` for correct case handling on all platforms.
126
+ - **Updated docstrings**: Export path examples changed from Windows-only (`C:\Users\Name\Music`) to platform-neutral (`~/Music`) format.
127
+
128
+ ### Memory Leaks Fixed
129
+
130
+ - **Whisper model GPU/CPU memory leak**: When switching model sizes (e.g., `large-v3` → `small`), the old model was replaced but never explicitly freed. CUDA/CTranslate2 held references preventing garbage collection. Now explicitly `del`s the old model and calls `gc.collect()` before loading a new one.
131
+ - **Job dict memory growth**: Completed job cleanup (`_cleanup_stale_jobs`) only ran when creating new jobs. If 100+ jobs completed without new ones starting, all remained in memory. Now also runs on every `check_pipeline_status` / `check_transcription_status` call.
132
+
133
+ ### Race Conditions Fixed
134
+
135
+ - **`transcription_set_model` bypassed job lock**: Created jobs and wrote to `_jobs` dict without acquiring `_job_lock`, risking corruption if called simultaneously with `_start_transcription`. Now properly acquires the lock.
136
+ - **Pipeline/transcription interleaving**: A running transcription didn't block starting a pipeline (and vice versa). Both send commands to the same Audacity pipe — interleaved commands could corrupt Audacity state. Now cross-check each other before starting.
137
+ - **Stale background tasks kept running**: `_cleanup_stale_jobs()` marked timed-out jobs as errored but the `asyncio.Task` continued executing. Now stores task references and calls `task.cancel()` on timeout.
138
+
139
+ ### Pipe Reliability
140
+
141
+ - **Handle leak on partial pipe open**: If the first pipe opened but the second failed, the first handle was leaked. Now calls `_close_pipes()` in all error paths.
142
+ - **No shutdown cleanup**: Pipe handles (especially Windows kernel handles) were never released on server exit. Added `atexit.register(client.close)`.
143
+ - **POSIX pipes could hang forever**: `readline()` blocked with no timeout. If Audacity crashed mid-response, the server thread hung permanently (even `asyncio` cancellation can't interrupt OS-level blocking reads). Now uses `select.select()` with configurable timeout.
144
+ - **Backslash escaping in pipe protocol**: `_quote_value()` escaped `"` but not `\`. A path like `C:\new\test` could have `\n` and `\t` misinterpreted. Now escapes backslashes before quotes.
145
+
146
+ ### Other Fixes
147
+
148
+ - **Temp file race on Windows**: Transcription used `NamedTemporaryFile` which on Windows creates then immediately closes a file — another process could grab the same path. Now uses UUID-based paths (matching the pattern already used in cleanup pipelines).
149
+
150
+ ## [0.1.4] - 2026-03-16
151
+
152
+ ### Easy Setup
153
+
154
+ - **One-click installer**: Added `install.bat` (Windows) and `install.sh` (macOS/Linux) that automatically install from PyPI and configure Claude Desktop — no git clone, no manual JSON editing.
155
+ - **`pip install audacity-mcp`** is now the primary install method (was previously git clone + `pip install -e .`).
156
+ - **README rewritten** to lead with one-click install and `pip install` from PyPI. Manual git clone steps moved to a collapsible section.
157
+ - **Installation guide updated**: Three clear options — one-click (easiest), pip install (recommended), from source (developers).
158
+
159
+ ### Documentation
160
+
161
+ - Fixed tool counts in README: updated from 96 to 131 tools across all categories.
162
+ - Fixed test count in project structure: updated from 40 to 60 tests.
163
+ - Updated all references from `pip install -e .` to `pip install audacity-mcp`.
164
+
165
+ ## [0.1.3] - 2026-03-15
166
+
167
+ ### Added
168
+
169
+ - Added 32 new tools (99 → 131 total) across effects, editing, tracks, selection, transcription, and labels
170
+ - Fixed pipeline settings
171
+ - Live-tested on production audio
172
+
173
+ ## [0.1.1] - 2026-03-15
174
+
175
+ ### Security
176
+
177
+ - **Path traversal protection**: All file paths are now canonicalized with `os.path.realpath()` before use, preventing `../` traversal attacks. System directories (Windows, Program Files) are blocked.
178
+ - **Command injection hardening**: Fixed `_quote_value()` in pipe protocol to properly escape embedded double quotes, preventing malformed commands from reaching Audacity.
179
+ - **File overwrite protection**: Export tools (audio, labels, sample data, transcription) now refuse to overwrite existing files, preventing accidental data loss from AI-hallucinated paths.
180
+
181
+ ### Bug Fixes
182
+
183
+ - **Memory leak**: Pipeline and transcription job stores (`_jobs` dicts) now cap at 50 completed entries and evict oldest automatically. Previously grew unbounded for the lifetime of the server process.
184
+ - **Stale job timeout**: Added 10-minute timeout to cleanup pipelines (was only in transcription). Stuck pipelines no longer block all future pipeline runs forever.
185
+ - **Race condition**: Pipeline and transcription job creation now uses `asyncio.Lock` to prevent near-simultaneous MCP calls from bypassing the concurrent-run check and starting two pipelines at once.
186
+ - **Temp file collision**: Analysis WAV files now use unique filenames (`uuid` suffix) instead of a fixed path, preventing data corruption if multiple server instances run simultaneously.
187
+ - **Removed `wma` from allowed export formats** — Audacity doesn't natively support WMA export; including it caused confusing errors.
188
+ - **`select_zero_crossing` called wrong command**: Was calling `SnapToOff` (disables snapping) instead of `ZeroCross` (find zero crossings). Users thought they were snapping to zero crossings but were actually turning snapping off.
189
+ - **`auto_analyze_audio` track info never parsed**: `GetInfo` returns JSON in the message field but code expected it in `data` dict. Track count and metadata were always empty. Now properly parses the JSON response.
190
+ - **Transcription export missing `SelAllTracks`**: `Export2` requires both track and time selection. Transcription only called `SelectAll` (time) but not `SelAllTracks`, which could cause incomplete exports on multi-track projects.
191
+ - **`parse_response` overwrote error messages**: When Audacity returned an error message followed by `BatchCommand finished: Failed!`, the batch line overwrote the actual error text. Error details are now preserved.
192
+ - **`effect_amplify` accepted ratio=0**: A ratio of 0 silences audio entirely. Now rejects values <= 0.
193
+ - **`check_pipeline_status` deleted other jobs**: Querying one completed job triggered cleanup that could delete other users' job results. Job eviction now only happens during `_create_job`.
194
+
195
+ ### Validation
196
+
197
+ - **Effect parameter validation**: Added range checks to `reverb` (7 params), `phaser` (6 params), `wahwah` (5 params), `distortion`, and `equalization`. Previously these accepted any value, potentially crashing Audacity.
198
+ - **Generator duration caps**: `generate_tone`, `generate_noise`, and `generate_chirp` now enforce a 1-hour maximum duration to prevent runaway generation.
199
+ - **Analysis parameter validation**: Added bounds checking to `analyze_find_clipping` (duty cycle 1-1000) and `analyze_sample_data_export` (limit 1-1,000,000).
200
+
201
+ ### Reliability
202
+
203
+ - **Narrowed exception handlers**: CUDA setup in transcription now catches only `ImportError`, `AttributeError`, `OSError` instead of bare `Exception`, so real bugs surface instead of being silently swallowed.
204
+ - **Thread-safe Whisper model loading**: Added `threading.Lock` around model initialization with double-checked locking pattern. Prevents concurrent transcription jobs from loading the model simultaneously and wasting memory.
205
+
206
+ ### Tests
207
+
208
+ - Added 19 new tests (41 → 60 total): pipe protocol edge cases (negative floats, Unicode, Windows paths, embedded quotes, empty strings, large numbers), path safety validation, parse_response edge cases.
209
+
210
+ ## [0.1.0] - 2025-12-01
211
+
212
+ ### Added
213
+
214
+ - Initial release with 99 MCP tools across 11 modules
215
+ - Named pipe bridge to Audacity via mod-script-pipe
216
+ - 9 automated audio pipelines (analyze, cleanup, podcast, audiobook, interview, vocal, live, music mastering, lo-fi)
217
+ - Background job system with start/poll pattern for long-running operations
218
+ - Transcription support via faster-whisper (local, offline)
219
+ - Cross-platform pipe protocol (Windows Win32 API + Unix named pipes)
220
+ - Injection detection on pipe commands
221
+ - 41 passing tests
@@ -0,0 +1,72 @@
1
+ # Contributing to AudacityMCP
2
+
3
+ Thanks for your interest in contributing! Here's how to get started.
4
+
5
+ ## Development Setup
6
+
7
+ ```bash
8
+ git clone https://github.com/xDarkzx/Audacity-MCP.git
9
+ cd AudacityMCP
10
+ pip install -e ".[dev]"
11
+ ```
12
+
13
+ ## Running Tests
14
+
15
+ ```bash
16
+ pytest tests/ -x -q
17
+ ```
18
+
19
+ All tests must pass before submitting a PR.
20
+
21
+ ## Adding a New Tool
22
+
23
+ 1. Choose the appropriate module in `audacity_mcp/tools/` (or create a new one)
24
+ 2. Add your tool inside the `register(mcp)` function using the `@mcp.tool()` decorator
25
+ 3. Validate all inputs — use `AudacityMCPError` with appropriate error codes
26
+ 4. Use `client.execute()` for fast commands or `client.execute_long()` for effects that process audio
27
+ 5. Add tests in `tests/`
28
+
29
+ Example:
30
+
31
+ ```python
32
+ @mcp.tool()
33
+ async def my_tool(param: float = 1.0) -> dict:
34
+ """Short description of what this tool does.
35
+
36
+ Args:
37
+ param: What this parameter controls. Default: 1.0
38
+ """
39
+ if param < 0:
40
+ raise AudacityMCPError(ErrorCode.VALUE_OUT_OF_RANGE, "param must be >= 0")
41
+ return await client.execute_long("AudacityCommand", Param=param)
42
+ ```
43
+
44
+ ## Code Style
45
+
46
+ - Python 3.10+
47
+ - Type hints on public functions
48
+ - No unnecessary comments or docstrings on obvious code
49
+ - Keep it simple — don't over-engineer
50
+ - No `exec`/`eval` — every operation maps to a static handler
51
+
52
+ ## Security
53
+
54
+ - Validate all parameters before sending to Audacity
55
+ - Block injection characters (`\n`, `\r`, `\x00`) in string inputs
56
+ - Validate file paths are absolute
57
+ - Range-check all numeric inputs
58
+
59
+ ## Pull Requests
60
+
61
+ - Keep PRs focused on a single change
62
+ - Include tests for new tools
63
+ - Make sure all existing tests still pass
64
+ - Describe what your change does and why
65
+
66
+ ## Reporting Issues
67
+
68
+ Open an issue on GitHub with:
69
+ - What you expected to happen
70
+ - What actually happened
71
+ - Steps to reproduce
72
+ - Your OS and Audacity version
@@ -0,0 +1,190 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to the Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by the Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding any notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ Copyright 2026 Daniel Hodgetts
179
+
180
+ Licensed under the Apache License, Version 2.0 (the "License");
181
+ you may not use this file except in compliance with the License.
182
+ You may obtain a copy of the License at
183
+
184
+ http://www.apache.org/licenses/LICENSE-2.0
185
+
186
+ Unless required by applicable law or agreed to in writing, software
187
+ distributed under the License is distributed on an "AS IS" BASIS,
188
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189
+ See the License for the specific language governing permissions and
190
+ limitations under the License.