whisper-windows-mcp 1.5.0 → 1.6.0
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/dist/index.js +171 -2
- package/package.json +1 -1
- package/whisper-windows-mcp_ROADMAP.md +0 -193
package/dist/index.js
CHANGED
|
@@ -95,6 +95,60 @@ function hasVulkanDll() {
|
|
|
95
95
|
const whisperDir = dirname(WHISPER_CLI_PATH);
|
|
96
96
|
return existsSync(join(whisperDir, "ggml-vulkan.dll"));
|
|
97
97
|
}
|
|
98
|
+
async function probeFile(filePath) {
|
|
99
|
+
try {
|
|
100
|
+
const { stdout } = await execFileAsync(FFMPEG_PATH.replace(/ffmpeg(\.exe)?$/i, "ffprobe$1").replace(/ffmpeg$/i, "ffprobe"), [
|
|
101
|
+
"-v", "quiet",
|
|
102
|
+
"-print_format", "json",
|
|
103
|
+
"-show_format",
|
|
104
|
+
"-show_streams",
|
|
105
|
+
filePath,
|
|
106
|
+
], { windowsHide: true, maxBuffer: 10 * 1024 * 1024 });
|
|
107
|
+
const data = JSON.parse(stdout);
|
|
108
|
+
const fmt = data.format ?? {};
|
|
109
|
+
const streams = data.streams ?? [];
|
|
110
|
+
const audioStream = streams.find((s) => s.codec_type === "audio");
|
|
111
|
+
const durationSec = parseFloat(fmt.duration ?? "0") || 0;
|
|
112
|
+
const sizeMb = parseInt(fmt.size ?? "0", 10) / (1024 * 1024);
|
|
113
|
+
const bitrate = Math.round(parseInt(fmt.bit_rate ?? "0", 10) / 1000);
|
|
114
|
+
const codec = audioStream?.codec_name ?? fmt.format_name?.split(",")[0] ?? "unknown";
|
|
115
|
+
return {
|
|
116
|
+
filePath,
|
|
117
|
+
fileName: basename(filePath),
|
|
118
|
+
durationSec,
|
|
119
|
+
sizeMb,
|
|
120
|
+
codec,
|
|
121
|
+
bitrate,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function formatDuration(sec) {
|
|
129
|
+
if (!sec)
|
|
130
|
+
return "?:??";
|
|
131
|
+
const h = Math.floor(sec / 3600);
|
|
132
|
+
const m = Math.floor((sec % 3600) / 60);
|
|
133
|
+
const s = Math.floor(sec % 60);
|
|
134
|
+
if (h > 0)
|
|
135
|
+
return `${h}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
|
|
136
|
+
return `${m}:${String(s).padStart(2, "0")}`;
|
|
137
|
+
}
|
|
138
|
+
function estimateTime(durationSec, gpu) {
|
|
139
|
+
if (!durationSec)
|
|
140
|
+
return "?";
|
|
141
|
+
// CPU: ~1.5x realtime on Ryzen 7 2700x with medium.en
|
|
142
|
+
// GPU: ~0.12x realtime on Vega 56 via Vulkan with medium.en (~8x faster than CPU)
|
|
143
|
+
const ratio = gpu ? 0.12 : 1.5;
|
|
144
|
+
const estSec = Math.round(durationSec * ratio);
|
|
145
|
+
if (estSec < 60)
|
|
146
|
+
return `~${estSec}s`;
|
|
147
|
+
return `~${Math.round(estSec / 60)}m`;
|
|
148
|
+
}
|
|
149
|
+
function padEnd(str, len) {
|
|
150
|
+
return str.length >= len ? str.slice(0, len) : str + " ".repeat(len - str.length);
|
|
151
|
+
}
|
|
98
152
|
function needsConversion(filePath) {
|
|
99
153
|
return !NATIVE_EXTENSIONS.includes(extname(filePath).toLowerCase());
|
|
100
154
|
}
|
|
@@ -186,7 +240,7 @@ function getFiles(dir, recursive) {
|
|
|
186
240
|
// ---------------------------------------------------------------------------
|
|
187
241
|
// MCP Server
|
|
188
242
|
// ---------------------------------------------------------------------------
|
|
189
|
-
const server = new Server({ name: "whisper-windows-mcp", version: "1.
|
|
243
|
+
const server = new Server({ name: "whisper-windows-mcp", version: "1.6.0" }, { capabilities: { tools: {} } });
|
|
190
244
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
191
245
|
tools: [
|
|
192
246
|
{
|
|
@@ -258,6 +312,29 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
258
312
|
description: "Verify whisper-cli.exe, model, and FFmpeg are all available. Run this first if anything fails.",
|
|
259
313
|
inputSchema: { type: "object", properties: {} },
|
|
260
314
|
},
|
|
315
|
+
{
|
|
316
|
+
name: "analyze_media",
|
|
317
|
+
description: "Analyze one or more media files using FFprobe before transcribing. " +
|
|
318
|
+
"For a single file: returns duration, size, codec, and estimated transcription time on CPU and GPU. " +
|
|
319
|
+
"For a folder: scans all supported media files and returns a sorted table with the same info for each. " +
|
|
320
|
+
"Use this to plan batch work, estimate how long transcription will take, or check what's already been transcribed.",
|
|
321
|
+
inputSchema: {
|
|
322
|
+
type: "object",
|
|
323
|
+
properties: {
|
|
324
|
+
path: {
|
|
325
|
+
type: "string",
|
|
326
|
+
description: "Absolute Windows path to a single file or a folder.",
|
|
327
|
+
},
|
|
328
|
+
sort_by: {
|
|
329
|
+
type: "string",
|
|
330
|
+
enum: ["duration", "name", "size"],
|
|
331
|
+
description: "For folder scans: sort order. Defaults to duration (shortest first).",
|
|
332
|
+
default: "duration",
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
required: ["path"],
|
|
336
|
+
},
|
|
337
|
+
},
|
|
261
338
|
{
|
|
262
339
|
name: "check_system",
|
|
263
340
|
description: "Detect GPU hardware and verify Vulkan acceleration is available. " +
|
|
@@ -297,6 +374,98 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
297
374
|
};
|
|
298
375
|
}
|
|
299
376
|
// -------------------------------------------------------------------------
|
|
377
|
+
// analyze_media
|
|
378
|
+
// -------------------------------------------------------------------------
|
|
379
|
+
if (name === "analyze_media") {
|
|
380
|
+
const targetPath = args?.path;
|
|
381
|
+
const sortBy = args?.sort_by || "duration";
|
|
382
|
+
if (!targetPath)
|
|
383
|
+
return { content: [{ type: "text", text: "path is required." }], isError: true };
|
|
384
|
+
if (!existsSync(targetPath))
|
|
385
|
+
return { content: [{ type: "text", text: `Path not found: ${targetPath}` }], isError: true };
|
|
386
|
+
// Check ffprobe is available
|
|
387
|
+
const ffprobePath = FFMPEG_PATH.replace(/ffmpeg(\.exe)?$/i, "ffprobe$1").replace(/ffmpeg$/i, "ffprobe");
|
|
388
|
+
try {
|
|
389
|
+
await execFileAsync(ffprobePath, ["-version"], { windowsHide: true });
|
|
390
|
+
}
|
|
391
|
+
catch {
|
|
392
|
+
return {
|
|
393
|
+
content: [{ type: "text", text: "ffprobe not found. FFprobe is bundled with FFmpeg — make sure FFmpeg is installed and in your PATH." }],
|
|
394
|
+
isError: true,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
const vulkan = hasVulkanDll();
|
|
398
|
+
// Single file
|
|
399
|
+
const { statSync } = await import("fs");
|
|
400
|
+
const stat = statSync(targetPath);
|
|
401
|
+
if (stat.isFile()) {
|
|
402
|
+
const info = await probeFile(targetPath);
|
|
403
|
+
if (!info) {
|
|
404
|
+
return { content: [{ type: "text", text: `Could not probe file: ${targetPath}\nMake sure it is a supported media file.` }], isError: true };
|
|
405
|
+
}
|
|
406
|
+
const txtPath = targetPath.replace(/\.[^.]+$/, ".txt");
|
|
407
|
+
const transcribed = existsSync(txtPath) ? "✅ already transcribed" : "⬜ not yet transcribed";
|
|
408
|
+
return {
|
|
409
|
+
content: [{
|
|
410
|
+
type: "text",
|
|
411
|
+
text: `📄 ${info.fileName}\n` +
|
|
412
|
+
`${"─".repeat(40)}\n` +
|
|
413
|
+
`Duration: ${formatDuration(info.durationSec)}\n` +
|
|
414
|
+
`Size: ${info.sizeMb.toFixed(1)} MB\n` +
|
|
415
|
+
`Codec: ${info.codec}\n` +
|
|
416
|
+
`Bitrate: ${info.bitrate} kbps\n` +
|
|
417
|
+
`Status: ${transcribed}\n\n` +
|
|
418
|
+
`Estimated transcription time:\n` +
|
|
419
|
+
` CPU: ${estimateTime(info.durationSec, false)}\n` +
|
|
420
|
+
` GPU: ${estimateTime(info.durationSec, true)}${vulkan ? "" : " (⚠️ Vulkan not detected — GPU estimate may not apply)"}`,
|
|
421
|
+
}],
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
// Folder scan
|
|
425
|
+
const files = getFiles(targetPath, false);
|
|
426
|
+
if (files.length === 0) {
|
|
427
|
+
return { content: [{ type: "text", text: `No supported media files found in: ${targetPath}` }], isError: true };
|
|
428
|
+
}
|
|
429
|
+
const results = [];
|
|
430
|
+
for (const f of files) {
|
|
431
|
+
const info = await probeFile(f);
|
|
432
|
+
if (info)
|
|
433
|
+
results.push(info);
|
|
434
|
+
}
|
|
435
|
+
// Sort
|
|
436
|
+
if (sortBy === "name")
|
|
437
|
+
results.sort((a, b) => a.fileName.localeCompare(b.fileName));
|
|
438
|
+
else if (sortBy === "size")
|
|
439
|
+
results.sort((a, b) => a.sizeMb - b.sizeMb);
|
|
440
|
+
else
|
|
441
|
+
results.sort((a, b) => a.durationSec - b.durationSec); // duration default
|
|
442
|
+
const totalDuration = results.reduce((acc, r) => acc + r.durationSec, 0);
|
|
443
|
+
const totalSize = results.reduce((acc, r) => acc + r.sizeMb, 0);
|
|
444
|
+
const transcribedCount = results.filter(r => existsSync(r.filePath.replace(/\.[^.]+$/, ".txt"))).length;
|
|
445
|
+
const header = `${padEnd("File", 36)} ${padEnd("Duration", 8)} ${padEnd("Size", 8)} ${padEnd("CPU est", 8)} ${padEnd("GPU est", 8)} Status\n` +
|
|
446
|
+
`${"─".repeat(90)}\n`;
|
|
447
|
+
const rows = results.map(r => {
|
|
448
|
+
const done = existsSync(r.filePath.replace(/\.[^.]+$/, ".txt")) ? "✅" : "⬜";
|
|
449
|
+
return (`${padEnd(r.fileName, 36)} ` +
|
|
450
|
+
`${padEnd(formatDuration(r.durationSec), 8)} ` +
|
|
451
|
+
`${padEnd(r.sizeMb.toFixed(1) + " MB", 8)} ` +
|
|
452
|
+
`${padEnd(estimateTime(r.durationSec, false), 8)} ` +
|
|
453
|
+
`${padEnd(estimateTime(r.durationSec, true), 8)} ` +
|
|
454
|
+
done);
|
|
455
|
+
}).join("\n");
|
|
456
|
+
const summary = `\n${"─".repeat(90)}\n` +
|
|
457
|
+
`${results.length} files | Total duration: ${formatDuration(totalDuration)} | Total size: ${totalSize.toFixed(1)} MB\n` +
|
|
458
|
+
`Transcribed: ${transcribedCount}/${results.length}\n` +
|
|
459
|
+
`Total est. CPU time: ${estimateTime(totalDuration, false)} | Total est. GPU time: ${estimateTime(totalDuration, true)}` +
|
|
460
|
+
(vulkan ? "" : "\n⚠️ ggml-vulkan.dll not detected — GPU estimates may not apply");
|
|
461
|
+
return {
|
|
462
|
+
content: [{
|
|
463
|
+
type: "text",
|
|
464
|
+
text: `Media analysis: ${targetPath}\n\n${header}${rows}${summary}`,
|
|
465
|
+
}],
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
// -------------------------------------------------------------------------
|
|
300
469
|
// check_system
|
|
301
470
|
// -------------------------------------------------------------------------
|
|
302
471
|
if (name === "check_system") {
|
|
@@ -476,7 +645,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
476
645
|
async function main() {
|
|
477
646
|
const transport = new StdioServerTransport();
|
|
478
647
|
await server.connect(transport);
|
|
479
|
-
console.error(`whisper-windows-mcp v1.
|
|
648
|
+
console.error(`whisper-windows-mcp v1.6.0 running | threads: ${WHISPER_THREADS}/${SYSTEM_THREADS}`);
|
|
480
649
|
}
|
|
481
650
|
main().catch((err) => {
|
|
482
651
|
console.error("Fatal error:", err);
|
package/package.json
CHANGED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
# whisper-windows-mcp — Roadmap
|
|
2
|
-
|
|
3
|
-
Current version is a functional early release. It works, but it has known limitations that make it unsuitable for production use with long files or batch workflows. This document tracks what needs to be fixed and what's planned.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Known Issues (Current Version)
|
|
8
|
-
|
|
9
|
-
### 1. No Process Lock
|
|
10
|
-
Multiple `whisper-cli.exe` instances can be spawned concurrently if Claude retries a timed-out request. This maxes CPU and degrades or breaks both transcriptions. **This is the most critical bug.**
|
|
11
|
-
|
|
12
|
-
### 2. No Progress Visibility
|
|
13
|
-
The user has no indicator that transcription is happening or how far along it is, short of watching Task Manager or listening to their CPU fan. This is not acceptable UX for a public tool.
|
|
14
|
-
|
|
15
|
-
### 3. 4-Minute Claude MCP Timeout
|
|
16
|
-
The Claude web client cuts MCP connections after ~4 minutes. Whisper continues running in the background after the timeout fires, but Claude can't confirm completion. For files longer than ~2-3 minutes of audio on CPU, this timeout is frequently hit.
|
|
17
|
-
|
|
18
|
-
### 4. No GPU Autodetection
|
|
19
|
-
The bundled `whisper-cli.exe` binary is CPU-only. AMD GPUs are not detected or used. NVIDIA GPUs may work depending on the binary source, but there's no verification at startup.
|
|
20
|
-
|
|
21
|
-
### 5. High CPU Load
|
|
22
|
-
Sustained 50-55% CPU utilization during transcription over multi-hour sessions puts significant strain on hardware. GPU acceleration is the primary fix, but binary selection matters too.
|
|
23
|
-
|
|
24
|
-
### 6. Background Batch Non-Functional
|
|
25
|
-
The background batch mode (intended to open a CMD window with progress) does not work as designed and was cut from the current release.
|
|
26
|
-
|
|
27
|
-
### 7. No File Pre-Analysis
|
|
28
|
-
No way to know file duration or size before processing starts. Can't sort a queue by length or estimate how long a job will take.
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
## Roadmap
|
|
33
|
-
|
|
34
|
-
### Priority 1 — GPU Acceleration
|
|
35
|
-
|
|
36
|
-
**Problem:** The current `whisper-cli.exe` binary is CPU-only.
|
|
37
|
-
|
|
38
|
-
**Solution options (in order of recommendation):**
|
|
39
|
-
|
|
40
|
-
- **Vulkan backend** — Compile whisper.cpp with Vulkan support. Works with AMD, NVIDIA, and Intel GPUs on Windows without vendor-specific SDKs. Best cross-compatibility.
|
|
41
|
-
- **ROCm backend** — AMD-native but Windows ROCm support is limited and complex to install.
|
|
42
|
-
- **Switch to faster-whisper** — Python-based implementation with GPU support via CuBLAS/ROCm. Often faster than whisper.cpp for equivalent accuracy. Requires Python environment.
|
|
43
|
-
|
|
44
|
-
**GPU autodetection:** On startup, check for available GPU via `dxdiag` or `wmic path win32_VideoController` and report to the user. If a supported GPU is found but the binary doesn't use it, surface a warning with instructions.
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
### Priority 2 — Process Lock
|
|
49
|
-
|
|
50
|
-
**Problem:** Retrying a timed-out transcription spawns a second `whisper-cli.exe` while the first is still running.
|
|
51
|
-
|
|
52
|
-
**Solution:** Before spawning any process, check for existing instances:
|
|
53
|
-
|
|
54
|
-
```batch
|
|
55
|
-
tasklist /FI "IMAGENAME eq whisper-cli.exe" /NH
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
If found, return an error: `"Transcription already in progress. Wait for the current job to complete before starting another."` Do not spawn. Period.
|
|
59
|
-
|
|
60
|
-
This is a small code change with major impact.
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
### Priority 3 — File Pre-Analysis Tool
|
|
65
|
-
|
|
66
|
-
**Problem:** No duration/size info before processing. Can't sort intelligently or estimate time.
|
|
67
|
-
|
|
68
|
-
**Solution:** New tool `analyze_media` using FFprobe (already bundled with FFmpeg):
|
|
69
|
-
|
|
70
|
-
```
|
|
71
|
-
ffprobe -v quiet -print_format json -show_format -show_streams <file>
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
Returns: duration in seconds, file size, codec, bitrate.
|
|
75
|
-
|
|
76
|
-
For a folder scan, returns a sorted table:
|
|
77
|
-
|
|
78
|
-
```
|
|
79
|
-
filename | duration | size | est. time (CPU) | est. time (GPU)
|
|
80
|
-
2026-01-11 10-11-42.mp4 | 0:35 | 42 MB | ~1 min | ~10 sec
|
|
81
|
-
2026-03-16 06-56-43.mp4 | 1:17 | 98 MB | ~3 min | ~20 sec
|
|
82
|
-
...
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Throughput estimates are calibrated from actual completed jobs (logged internally) and refined over time.
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
### Priority 4 — Progress Visibility
|
|
90
|
-
|
|
91
|
-
**Problem:** No way to know how far along transcription is without Task Manager.
|
|
92
|
-
|
|
93
|
-
**Solution:** `whisper-cli.exe` outputs segment timestamps to stderr as it processes (e.g. `[00:01:30 --> 00:01:35]`). The MCP should:
|
|
94
|
-
|
|
95
|
-
1. Pipe stderr from the child process to a log file during processing
|
|
96
|
-
2. Expose a `check_progress` tool that reads the log and returns:
|
|
97
|
-
- Last completed timestamp
|
|
98
|
-
- Percentage complete (last timestamp / total duration)
|
|
99
|
-
- Estimated time remaining
|
|
100
|
-
- Whether the process is still running (check PID)
|
|
101
|
-
|
|
102
|
-
No changes needed to `whisper-cli` itself — only changes to how the MCP monitors it.
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
### Priority 5 — Timeout Workaround (Detached Process Architecture)
|
|
107
|
-
|
|
108
|
-
**Problem:** The 4-minute Claude MCP timeout kills the connection before long transcriptions finish.
|
|
109
|
-
|
|
110
|
-
**Solution:** Rearchitect transcription to use fully detached background processes instead of blocking child processes.
|
|
111
|
-
|
|
112
|
-
Flow:
|
|
113
|
-
1. `transcribe_audio` called → spawn whisper as **detached process** → write `job.json` (PID, source file, start time, expected duration, output path, log path) → **return immediately** with job ID
|
|
114
|
-
2. `check_progress` called → read `job.json` → check if PID is still alive → read last N lines of log → return status + percentage
|
|
115
|
-
3. When `check_progress` returns "complete" → read and return transcript
|
|
116
|
-
|
|
117
|
-
The transcribe call **never blocks**. It always returns in under a second. The timeout problem disappears entirely.
|
|
118
|
-
|
|
119
|
-
This is the correct architecture for any long-running MCP tool.
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
### Priority 6 — Sequential Batch with Validation
|
|
124
|
-
|
|
125
|
-
**Problem:** Background batch mode is non-functional. No post-processing validation.
|
|
126
|
-
|
|
127
|
-
**Solution:** Rebuild batch mode using the detached process architecture (Priority 5):
|
|
128
|
-
|
|
129
|
-
1. `transcribe_batch` → runs `analyze_media` on folder → sorts by duration ascending → processes one file at a time using detached process
|
|
130
|
-
2. After each file completes, validate: .txt exists + is non-empty + line count is proportional to duration (blank = likely failed)
|
|
131
|
-
3. Flag any suspect outputs for re-run
|
|
132
|
-
4. Write progress to `batch_progress.log`
|
|
133
|
-
5. `check_batch_progress` returns: files done, files remaining, current file, overall ETA, any failed files
|
|
134
|
-
|
|
135
|
-
---
|
|
136
|
-
|
|
137
|
-
### Priority 7 — Multi-Language Support and Translation
|
|
138
|
-
|
|
139
|
-
`whisper-cli` already supports `--language` and `--translate` natively. Expose these properly in the MCP:
|
|
140
|
-
|
|
141
|
-
- `language` parameter: auto-detect (default) or specify (e.g. `ja`, `es`, `de`)
|
|
142
|
-
- `translate_to_english`: boolean flag — uses whisper's built-in translation model
|
|
143
|
-
- For dual output (native + translated): two whisper passes, two output files
|
|
144
|
-
|
|
145
|
-
**Example use case:** Japanese DVD rip → Japanese SRT + English SRT simultaneously. Whisper handles abbreviated subject-drop Japanese better than literal translators because it was trained on natural speech.
|
|
146
|
-
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
### Priority 8 — Filename-Based References Throughout
|
|
150
|
-
|
|
151
|
-
**Problem:** When Claude references transcripts by positional index ("file 2", "file 11"), the user has no frame of reference. The index means nothing outside the tool call context.
|
|
152
|
-
|
|
153
|
-
**Solution:** All tool outputs — status messages, batch listings, progress reports, analysis results — must reference the **full source filename** at all times. Never use positional indices as the primary identifier.
|
|
154
|
-
|
|
155
|
-
Bad: `"File 11 is complete."`
|
|
156
|
-
Good: `"2025-09-01 03-15-13.mp4 — complete. Transcript saved to 2025-09-01 03-15-13.txt"`
|
|
157
|
-
|
|
158
|
-
When Claude ingests transcripts for analysis, each content block should be tagged with its source filename so quotes can always be traced back to their origin without ambiguity.
|
|
159
|
-
|
|
160
|
-
---
|
|
161
|
-
|
|
162
|
-
### Priority 9 — HWID / System Diagnostics Tool
|
|
163
|
-
|
|
164
|
-
New tool: `check_system`
|
|
165
|
-
|
|
166
|
-
Returns:
|
|
167
|
-
- GPU vendor and model
|
|
168
|
-
- VRAM available
|
|
169
|
-
- Whether a GPU-accelerated whisper binary is available and configured
|
|
170
|
-
- Recommended whisper model size for available hardware
|
|
171
|
-
- Estimated throughput (tokens/sec) based on hardware profile
|
|
172
|
-
|
|
173
|
-
This makes setup easier for new users and helps diagnose configuration problems without needing to open Task Manager or Device Manager.
|
|
174
|
-
|
|
175
|
-
---
|
|
176
|
-
|
|
177
|
-
## Design Principles
|
|
178
|
-
|
|
179
|
-
**Minimize Claude API usage.** Every MCP tool call consumes from the user's usage limit. Free-tier users have a hard cap. The entire transcription workflow — scan, analyze, queue, run, validate — should require fewer than 20 Claude interactions for a 60-file batch.
|
|
180
|
-
|
|
181
|
-
**One whisper instance at all times.** Never spawn a second process while one is running. Enforce this unconditionally.
|
|
182
|
-
|
|
183
|
-
**Local-first, private by default.** No audio leaves the machine. No cloud APIs required. This is a feature, not a limitation.
|
|
184
|
-
|
|
185
|
-
**Works for free-tier users.** Courtroom transcription, documentary research, foreign film subtitling — these are real use cases for people who can't afford cloud transcription services. The tool should serve them.
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
|
-
## Contributing
|
|
190
|
-
|
|
191
|
-
If you've worked out GPU acceleration for AMD (ROCm or Vulkan) or NVIDIA on Windows, please open an issue or PR — it's the most wanted feature and the implementation details for Windows are non-trivial.
|
|
192
|
-
|
|
193
|
-
Pull requests welcome for any of the above priorities. Check existing issues before starting work.
|