@audiolabtools/mcp-server 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/LICENSE +21 -0
- package/README.md +50 -76
- package/hosted-server.mjs +28 -12
- package/package.json +2 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nathan Renting
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,107 +1,81 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @audiolabtools/mcp-server
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
MCP ([Model Context Protocol](https://modelcontextprotocol.io)) server that gives any
|
|
4
|
+
MCP-capable AI — Claude Desktop, Claude Code, Cursor, and others — eight audio-analysis
|
|
5
|
+
tools, backed by the hosted **AudioLab** API. It is a thin HTTP client: **no local audio
|
|
6
|
+
engine, no ffmpeg, nothing to compile.**
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
- **Public URL** (`{url: "https://..."}`): fetched and decoded server-side
|
|
8
|
-
- **Local file path** (`{path: "./song.wav"}`): read server-side from disk
|
|
8
|
+
## Install
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
same threat model as a CLI the user installs themselves.
|
|
12
|
-
|
|
13
|
-
Nothing is uploaded by the caller in either case. Responses are **curated** JSON
|
|
14
|
-
(per-sample waveform/spectrum arrays are dropped so the response is useful to an
|
|
15
|
-
LLM, not overwhelming).
|
|
16
|
-
|
|
17
|
-
## Two modes (pick by who runs it)
|
|
18
|
-
|
|
19
|
-
| | `mcp/server.mjs` (local) | `mcp/hosted-server.mjs` (hosted) |
|
|
20
|
-
|---|---|---|
|
|
21
|
-
| Runs | the proprietary engine locally (bundled ffmpeg) | HTTP calls to the hosted API `audiolab.tools/v1/*` |
|
|
22
|
-
| Audio source | `{url}` **or** `{path}` (on-device, never leaves the machine) | `{url}` only |
|
|
23
|
-
| Needs | the repo + engine (private) | an `AUDIOLAB_API_KEY` |
|
|
24
|
-
| Distribute? | **No** (it contains the proprietary engine; keep it repo-only) | **Yes** (no engine code, safe to hand to Claude & other AIs) |
|
|
25
|
-
|
|
26
|
-
Give **hosted mode** to Claude Desktop / Claude Code / Cursor / any MCP client:
|
|
10
|
+
Point your MCP client at the package via `npx` (nothing to install globally):
|
|
27
11
|
|
|
28
12
|
```json
|
|
29
13
|
{
|
|
30
14
|
"mcpServers": {
|
|
31
15
|
"audiolab": {
|
|
32
|
-
"command": "
|
|
33
|
-
"args": ["/
|
|
16
|
+
"command": "npx",
|
|
17
|
+
"args": ["-y", "@audiolabtools/mcp-server"],
|
|
34
18
|
"env": { "AUDIOLAB_API_KEY": "al_live_yourkey" }
|
|
35
19
|
}
|
|
36
20
|
}
|
|
37
21
|
}
|
|
38
22
|
```
|
|
39
23
|
|
|
40
|
-
Get a key
|
|
41
|
-
Both modes expose the identical eight tools below.
|
|
24
|
+
Get a key: sign in at **https://audiolab.tools/account** and generate one (free tier available).
|
|
42
25
|
|
|
43
|
-
##
|
|
26
|
+
## Requirements
|
|
44
27
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
| `analyze_loudness` | MixLab | `{url}` or `{path}` | Integrated LUFS (EBU R128 / BS.1770-4), true-peak, loudness range, crest factor, stereo correlation, tonal balance, harshness/muddiness labels |
|
|
48
|
-
| `check_target` | MixLab | `{url\|path, target, lufs?, tp?}` | Pass/fail vs Spotify -14 / Apple-Music -16 / YouTube -14 / TIDAL -14 / Amazon -14 / podcast -16 / EBU-broadcast -23 / ATSC-broadcast -24 (or `"custom"` + lufs+tp). Includes deltas, issue messages, ffmpeg loudnorm fix command |
|
|
49
|
-
| `analyze_timeseries` | MixLab | `{url\|path, waveformPoints?}` | Short-term LUFS samples + downsampled waveform peaks over time (for graphing/UI) |
|
|
50
|
-
| `get_spectrum` | MixLab | `{url}` or `{path}` | FFT bins (freqs+mags) + 7-band energies + centroid/rolloff/flatness |
|
|
51
|
-
| `analyze_voice` | VoiceLab | `{url}` or `{path}` | Speech/silence ratio, speaking rate, SNR, noise floor, room echo, sibilance & clipping risk |
|
|
52
|
-
| `get_speech_segments` | VoiceLab | `{url}` or `{path}` | Voiced regions with start/end timestamps + per-segment RMS (for auto-trim, chapters, speaker-turn detection) |
|
|
53
|
-
| `index_signal` | SignalLab | `{url}` or `{path}` | Content-type guess, tags, clipping/silence regions, brightness & dynamics buckets, file facts |
|
|
54
|
-
| `compare_loudness` | MixLab A/B | `{a: {url\|path}, b: {url\|path}}` | Run MixLab on both, return both results + per-metric deltas with verdict |
|
|
28
|
+
- **Node ≥ 18** — uses the built-in global `fetch` + `AbortSignal.timeout`.
|
|
29
|
+
- An `AUDIOLAB_API_KEY`. No ffmpeg, no native dependencies.
|
|
55
30
|
|
|
56
|
-
|
|
31
|
+
## Tools
|
|
57
32
|
|
|
58
|
-
|
|
33
|
+
Every tool takes a **public https URL** to the audio; the API fetches and analyses it
|
|
34
|
+
server-side and returns numbers only (nothing is uploaded from your machine).
|
|
59
35
|
|
|
60
|
-
|
|
61
|
-
|
|
36
|
+
| Tool | Input | Returns |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `analyze_loudness` | `{ url }` | Integrated LUFS (EBU R128 / BS.1770-4), true-peak (dBTP), LRA, crest factor, stereo correlation, mono compatibility, tonal balance |
|
|
39
|
+
| `check_target` | `{ url, target, lufs?, tp? }` | Pass/fail vs a delivery target (`spotify` / `apple-music` / `youtube` / `tidal` / `amazon-music` / `podcast` / `ebu-broadcast` / `atsc-broadcast`, or `target:"custom"` + `lufs`+`tp`), with per-metric deltas and an ffmpeg loudnorm fix command |
|
|
40
|
+
| `analyze_timeseries` | `{ url, waveformPoints? }` | Short-term LUFS over time + downsampled waveform peaks (for graphs/meters) |
|
|
41
|
+
| `get_spectrum` | `{ url }` | FFT magnitude data + 7-band energies + dominant band |
|
|
42
|
+
| `analyze_voice` | `{ url }` | Voice QA: speech/silence ratio, speaking rate, SNR, noise floor, room echo, sibilance & clipping risk |
|
|
43
|
+
| `get_speech_segments` | `{ url }` | Voiced regions with start/end + per-segment RMS (auto-trim, chapters) |
|
|
44
|
+
| `index_signal` | `{ url }` | Content-type guess, tags, clipping/silence regions, brightness & dynamics buckets |
|
|
45
|
+
| `compare_loudness` | `{ urlA, urlB }` | Runs loudness on both and returns both results for an A/B comparison |
|
|
62
46
|
|
|
63
|
-
|
|
47
|
+
Example asks to your AI:
|
|
64
48
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
```
|
|
49
|
+
- *“Analyze the loudness of https://example.com/track.wav”* → `analyze_loudness`
|
|
50
|
+
- *“Does https://example.com/mix.wav pass Spotify?”* → `check_target` with `target:"spotify"`
|
|
51
|
+
- *“A/B-compare masterA.wav vs masterB.wav for loudness”* → `compare_loudness`
|
|
69
52
|
|
|
70
|
-
|
|
71
|
-
for a remote, keyed server is a later transport addition; deploy, auth keys,
|
|
72
|
-
and billing are gated steps (see [`docs/PDD.md`](../docs/PDD.md) §0/§8).
|
|
53
|
+
## Configuration (env)
|
|
73
54
|
|
|
74
|
-
|
|
55
|
+
| Var | Default | Purpose |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| `AUDIOLAB_API_KEY` | — (required) | Your API key. |
|
|
58
|
+
| `AUDIOLAB_API_BASE` | `https://audiolab.tools/v1` | Override the API base (must be `https://`). |
|
|
59
|
+
| `AUDIOLAB_TIMEOUT_MS` | `60000` | Per-request timeout in milliseconds. |
|
|
75
60
|
|
|
76
|
-
|
|
77
|
-
{
|
|
78
|
-
"mcpServers": {
|
|
79
|
-
"audiolab": {
|
|
80
|
-
"command": "node",
|
|
81
|
-
"args": ["E:/New audiolab.dev/mcp/server.mjs"]
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
```
|
|
61
|
+
## Privacy
|
|
86
62
|
|
|
87
|
-
|
|
88
|
-
|
|
63
|
+
The tools send the **audio URL** and your **API key** to the AudioLab API (`audiolab.tools`),
|
|
64
|
+
which fetches and analyses the audio server-side and returns numbers only — your audio is not
|
|
65
|
+
stored (see https://audiolab.tools/privacy). This package has no telemetry and writes nothing
|
|
66
|
+
to disk.
|
|
89
67
|
|
|
90
|
-
|
|
68
|
+
## Limits
|
|
91
69
|
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
- *"A/B compare ./master.wav against ./reference.wav for loudness"* → calls `compare_loudness`
|
|
70
|
+
- One file per call (agents loop for many); one-shot (no streaming/realtime).
|
|
71
|
+
- File-size and rate limits are enforced by the API, per key.
|
|
95
72
|
|
|
96
|
-
##
|
|
73
|
+
## Smoke test
|
|
97
74
|
|
|
98
|
-
|
|
99
|
-
-
|
|
100
|
-
|
|
101
|
-
- One-shot only (no streaming / realtime)
|
|
75
|
+
```sh
|
|
76
|
+
node hosted-server.mjs --selftest # verifies the 8 tools register + the missing-key guard; no network
|
|
77
|
+
```
|
|
102
78
|
|
|
103
|
-
##
|
|
79
|
+
## License
|
|
104
80
|
|
|
105
|
-
|
|
106
|
-
invoice them. No Stripe, no key DB, no metering platform required to start.
|
|
107
|
-
Metering/credits/self-serve come later, behind the billing gate.
|
|
81
|
+
MIT © Nathan Renting
|
package/hosted-server.mjs
CHANGED
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
// calls, so it is safe to distribute publicly without exposing the proprietary engine.
|
|
6
6
|
//
|
|
7
7
|
// Two consumers share buildServer():
|
|
8
|
-
// 1. This file run directly = a stdio server (the npm package @
|
|
8
|
+
// 1. This file run directly = a stdio server (the npm package @audiolabtools/mcp-server),
|
|
9
9
|
// reading the key from AUDIOLAB_API_KEY.
|
|
10
10
|
// 2. api/mcp.mjs = the remote streamable-HTTP endpoint at /mcp, which passes the
|
|
11
11
|
// per-request Bearer key via buildServer({ apiKey }).
|
|
12
12
|
//
|
|
13
|
-
// Requires an API key (
|
|
13
|
+
// Requires an API key (self-serve: sign in at https://audiolab.tools/account and generate one).
|
|
14
14
|
// Config in Claude Desktop's claude_desktop_config.json:
|
|
15
15
|
// { "mcpServers": { "audiolab": {
|
|
16
|
-
// "command": "npx", "args": ["-y", "@
|
|
16
|
+
// "command": "npx", "args": ["-y", "@audiolabtools/mcp-server"],
|
|
17
17
|
// "env": { "AUDIOLAB_API_KEY": "al_live_yourkey" } } } }
|
|
18
18
|
//
|
|
19
19
|
// Env: AUDIOLAB_API_KEY (required at call time for stdio mode), AUDIOLAB_API_BASE
|
|
@@ -26,19 +26,35 @@ import { z } from 'zod';
|
|
|
26
26
|
|
|
27
27
|
// Read env at CALL time (not module load) so the key can be injected by the MCP host
|
|
28
28
|
// and so the missing-key guard is testable.
|
|
29
|
-
const apiBase = () =>
|
|
29
|
+
const apiBase = () => {
|
|
30
|
+
const base = (process.env.AUDIOLAB_API_BASE || 'https://audiolab.tools/v1').replace(/\/$/, '');
|
|
31
|
+
// Defense in depth: never send the Bearer key over a non-https (or attacker-injected) base.
|
|
32
|
+
if (!/^https:\/\//i.test(base)) throw new Error('AUDIOLAB_API_BASE must be an https:// URL.');
|
|
33
|
+
return base;
|
|
34
|
+
};
|
|
30
35
|
|
|
31
36
|
// key defaults to the env var (stdio mode); the remote endpoint passes a per-request key.
|
|
32
37
|
export async function call(route, body, key = process.env.AUDIOLAB_API_KEY) {
|
|
33
|
-
if (!key) throw new Error('AUDIOLAB_API_KEY is not set. Get a key
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
if (!key) throw new Error('AUDIOLAB_API_KEY is not set. Get a key at https://audiolab.tools/account.');
|
|
39
|
+
// Time-box the request so a hung/slow API can't stall the calling agent forever.
|
|
40
|
+
const timeoutMs = Number(process.env.AUDIOLAB_TIMEOUT_MS) || 60_000;
|
|
41
|
+
let res;
|
|
42
|
+
try {
|
|
43
|
+
res = await fetch(`${apiBase()}/${route}`, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: { 'content-type': 'application/json', authorization: `Bearer ${key}` },
|
|
46
|
+
body: JSON.stringify(body),
|
|
47
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
48
|
+
});
|
|
49
|
+
} catch (e) {
|
|
50
|
+
if (e?.name === 'TimeoutError') throw new Error(`AudioLab API timed out after ${timeoutMs} ms.`);
|
|
51
|
+
throw new Error(`Could not reach the AudioLab API: ${String(e?.message || e)}`);
|
|
52
|
+
}
|
|
39
53
|
const text = await res.text();
|
|
40
54
|
let json;
|
|
41
|
-
|
|
55
|
+
// Parse JSON only; never echo an arbitrary upstream body back to the agent.
|
|
56
|
+
try { json = JSON.parse(text); }
|
|
57
|
+
catch { throw new Error(res.ok ? 'AudioLab API returned a non-JSON response.' : `AudioLab API error ${res.status}.`); }
|
|
42
58
|
if (!res.ok) throw new Error(String(json?.error?.message || json?.error || `API returned ${res.status}`));
|
|
43
59
|
return json;
|
|
44
60
|
}
|
|
@@ -52,7 +68,7 @@ const wrap = (fn) => async (input) => {
|
|
|
52
68
|
const URL_IN = { url: z.string().url().describe('Public https URL to the audio file.') };
|
|
53
69
|
|
|
54
70
|
export function buildServer({ apiKey } = {}) {
|
|
55
|
-
const server = new McpServer({ name: 'audiolab', version: '0.
|
|
71
|
+
const server = new McpServer({ name: 'audiolab', version: '0.1.1' }); // keep in sync with package.json
|
|
56
72
|
const toolNames = [];
|
|
57
73
|
const tool = (name, def, handler) => { server.registerTool(name, def, handler); toolNames.push(name); };
|
|
58
74
|
// Per-request API key threaded from the transport, so we never race on process.env
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@audiolabtools/mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "MCP server for AudioLab — loudness (EBU R128 / BS.1770-4), true-peak, voice-quality, and signal analysis for AI agents (Claude, Cursor, any MCP client), via the AudioLab hosted API. No local audio engine required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"audiolab"
|
|
23
23
|
],
|
|
24
24
|
"homepage": "https://audiolab.tools/api",
|
|
25
|
+
"bugs": { "url": "https://audiolab.tools/api" },
|
|
25
26
|
"repository": {
|
|
26
27
|
"type": "git",
|
|
27
28
|
"url": "git+https://github.com/Audio-Launch/audiolab-tools.git",
|