download_model
Download a Whisper model from Hugging Face to your local models directory. Specify model name (e.g., large-v3-turbo) and the tool handles downloading from trusted sources.
Instructions
Download a Whisper model from Hugging Face directly into your models directory. Accepts a model name (e.g. large-v3-turbo, medium.en-q5_0) and handles the download automatically. Downloads only from trusted Hugging Face namespaces (ggerganov/whisper.cpp and ggml-org). After downloading, use switch_model to activate it for the current session.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| model_name | Yes | Model name to download, e.g. 'large-v3-turbo', 'medium.en-q5_0', 'large-v3-turbo-q5_0'. Use list_models to see what is already installed. |
Implementation Reference
- src/index.ts:1088-1127 (registration)Registration of the download_model tool in the ListTools handler, including its name, description, and input schema.
}, { name: "download_model", description: "Download a Whisper model from Hugging Face directly into your models directory. " + "Accepts a model name (e.g. large-v3-turbo, medium.en-q5_0) and handles the download automatically. " + "Downloads only from trusted Hugging Face namespaces (ggerganov/whisper.cpp and ggml-org). " + "After downloading, use switch_model to activate it for the current session.", inputSchema: { type: "object", properties: { model_name: { type: "string", description: "Model name to download, e.g. 'large-v3-turbo', 'medium.en-q5_0', 'large-v3-turbo-q5_0'. Use list_models to see what is already installed.", }, }, required: ["model_name"], }, }, { name: "switch_model", description: "Switch the active Whisper model for the current session without restarting Claude Desktop. " + "Accepts a model filename (e.g. ggml-large-v3-turbo.bin) or full path. " + "The model must already be installed in your models directory. " + "Use list_models to see installed models, download_model to add new ones. " + "Change is session-scoped — does not persist after Claude Desktop restarts.", inputSchema: { type: "object", properties: { model_name: { type: "string", description: "Model filename (e.g. ggml-large-v3-turbo.bin) or full path. Must be a .bin file in the configured models directory.", }, }, required: ["model_name"], }, }, ], })); - src/index.ts:1096-1106 (schema)Input schema for download_model: requires 'model_name' (string), accepts model name like 'large-v3-turbo' or filename like 'ggml-large-v3-turbo.bin'.
inputSchema: { type: "object", properties: { model_name: { type: "string", description: "Model name to download, e.g. 'large-v3-turbo', 'medium.en-q5_0', 'large-v3-turbo-q5_0'. Use list_models to see what is already installed.", }, }, required: ["model_name"], }, }, - src/index.ts:1361-1473 (handler)Handler for the download_model tool. Looks up the model in MODEL_REGISTRY, validates URL against allowed HuggingFace prefixes, downloads via Node.js https module with redirect following, saves to the models directory, and returns a success message with instructions to use switch_model.
if (name === "download_model") { const modelName = (args?.model_name as string)?.trim(); if (!modelName) return { content: [{ type: "text", text: "model_name is required." }], isError: true }; const entry = MODEL_REGISTRY.find( m => m.name === modelName || m.filename === modelName ); if (!entry) { const names = MODEL_REGISTRY.map(m => m.name).join(", "); return { content: [{ type: "text", text: `Unknown model: "${modelName}"\n\n` + `Available models:\n${names}\n\n` + `Use list_models to see what is already installed.`, }], isError: true, }; } // Security: enforce URL whitelist — never download from arbitrary URLs const urlOk = ALLOWED_HF_PREFIXES.some(prefix => entry.url.startsWith(prefix)); if (!urlOk) { return { content: [{ type: "text", text: `Security error: download URL for "${modelName}" is not in the allowed list.` }], isError: true, }; } const modelsDir = dirname(WHISPER_MODEL); if (!existsSync(modelsDir)) { try { mkdirSync(modelsDir, { recursive: true }); } catch (err: any) { return { content: [{ type: "text", text: `Could not create models directory: ${err?.message}` }], isError: true }; } } const destPath = join(modelsDir, entry.filename); if (existsSync(destPath)) { const sizeMb = (statSync(destPath).size / (1024 * 1024)).toFixed(0); return { content: [{ type: "text", text: `✅ ${entry.filename} is already installed (${sizeMb} MB).\n\n` + `Use switch_model ${entry.filename} to activate it.`, }], }; } // Download using Node.js built-in https — no external dependencies try { const https = await import("https"); const fs = await import("fs"); await new Promise<void>((resolve, reject) => { const tmpPath = destPath + ".part"; const file = fs.createWriteStream(tmpPath); function doRequest(url: string) { https.get(url, (res) => { // Follow redirects (Hugging Face uses redirects) if ((res.statusCode === 301 || res.statusCode === 302 || res.statusCode === 307) && res.headers.location) { const redirectUrl = res.headers.location; // Security: ensure redirect stays within allowed domains const redirectOk = ALLOWED_HF_PREFIXES.some(p => redirectUrl.startsWith(p)) || redirectUrl.startsWith("https://cdn-lfs.huggingface.co/") || redirectUrl.startsWith("https://cdn-lfs-us-1.huggingface.co/"); if (!redirectOk) { reject(new Error(`Redirect to disallowed URL: ${redirectUrl}`)); return; } doRequest(redirectUrl); return; } if (res.statusCode !== 200) { reject(new Error(`HTTP ${res.statusCode} from ${url}`)); return; } res.pipe(file); // Wait for close callback before renaming — Windows requires the file // handle to be fully released before renameSync will succeed. file.on("finish", () => { file.close((closeErr) => { if (closeErr) { reject(closeErr); return; } try { fs.renameSync(tmpPath, destPath); resolve(); } catch (renameErr) { reject(renameErr); } }); }); }).on("error", (err) => { try { fs.unlinkSync(tmpPath); } catch { } reject(err); }); } doRequest(entry.url); }); const finalSizeMb = (statSync(destPath).size / (1024 * 1024)).toFixed(0); return { content: [{ type: "text", text: `✅ Downloaded: ${entry.filename} (${finalSizeMb} MB)\n` + `Saved to: ${destPath}\n\n` + `Use switch_model ${entry.filename} to activate it for this session.`, }], }; } catch (err: any) { return { content: [{ type: "text", text: `Download failed:\n\n${err?.message || String(err)}` }], isError: true, }; } } - src/index.ts:549-579 (helper)MODEL_REGISTRY: Array of ModelEntry objects defining all downloadable models with their names, filenames, sizes, multilingual flag, quantization, use-case descriptions, and download URLs.
interface ModelEntry { name: string; filename: string; sizeMb: number; multilingual: boolean; quantized: boolean; useCase: string; url: string; } const MODEL_REGISTRY: ModelEntry[] = [ // Full-precision English { name: "tiny.en", filename: "ggml-tiny.en.bin", sizeMb: 75, multilingual: false, quantized: false, useCase: "Quick tests, lowest accuracy", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.en.bin" }, { name: "base.en", filename: "ggml-base.en.bin", sizeMb: 142, multilingual: false, quantized: false, useCase: "Fast English, good accuracy", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin" }, { name: "small.en", filename: "ggml-small.en.bin", sizeMb: 466, multilingual: false, quantized: false, useCase: "Better English accuracy", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.en.bin" }, { name: "medium.en", filename: "ggml-medium.en.bin", sizeMb: 1500, multilingual: false, quantized: false, useCase: "High accuracy English, fast on GPU", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.en.bin" }, // Full-precision multilingual { name: "tiny", filename: "ggml-tiny.bin", sizeMb: 75, multilingual: true, quantized: false, useCase: "Multilingual, minimal accuracy", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.bin" }, { name: "base", filename: "ggml-base.bin", sizeMb: 142, multilingual: true, quantized: false, useCase: "Multilingual, fast", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.bin" }, { name: "small", filename: "ggml-small.bin", sizeMb: 466, multilingual: true, quantized: false, useCase: "Multilingual, better accuracy", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.bin" }, { name: "medium", filename: "ggml-medium.bin", sizeMb: 1500, multilingual: true, quantized: false, useCase: "Multilingual, high accuracy", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.bin" }, { name: "large-v3", filename: "ggml-large-v3.bin", sizeMb: 2900, multilingual: true, quantized: false, useCase: "Best accuracy, multilingual — requires 6GB+ VRAM", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3.bin" }, { name: "large-v3-turbo", filename: "ggml-large-v3-turbo.bin", sizeMb: 1600, multilingual: true, quantized: false, useCase: "~6x faster than large-v3, minimal accuracy loss — RECOMMENDED for English GPU batch work", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo.bin" }, // Quantized variants — smaller, CPU-friendly { name: "base.en-q5_1", filename: "ggml-base.en-q5_1.bin", sizeMb: 57, multilingual: false, quantized: true, useCase: "Tiny English model, CPU-friendly", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en-q5_1.bin" }, { name: "small.en-q5_1", filename: "ggml-small.en-q5_1.bin", sizeMb: 181, multilingual: false, quantized: true, useCase: "Fast English, low memory, good for CPU", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.en-q5_1.bin" }, { name: "medium.en-q5_0", filename: "ggml-medium.en-q5_0.bin", sizeMb: 514, multilingual: false, quantized: true, useCase: "High accuracy English, CPU-friendly — good default for no-GPU systems", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.en-q5_0.bin" }, { name: "large-v3-q5_0", filename: "ggml-large-v3-q5_0.bin", sizeMb: 1080, multilingual: true, quantized: true, useCase: "Best multilingual quality at half the size", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-q5_0.bin" }, { name: "large-v3-turbo-q5_0", filename: "ggml-large-v3-turbo-q5_0.bin", sizeMb: 547, multilingual: true, quantized: true, useCase: "RECOMMENDED for CPU-only multilingual — fast, low memory, good accuracy", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo-q5_0.bin" }, { name: "large-v3-turbo-q8_0", filename: "ggml-large-v3-turbo-q8_0.bin", sizeMb: 874, multilingual: true, quantized: true, useCase: "Turbo quality closer to full precision, moderate size", url: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo-q8_0.bin" }, ]; - src/index.ts:581-585 (helper)ALLOWED_HF_PREFIXES: Security whitelist restricting downloads to trusted HuggingFace namespaces (ggerganov/whisper.cpp and ggml-org).
// Security: only allow downloads from these Hugging Face namespaces. const ALLOWED_HF_PREFIXES = [ "https://huggingface.co/ggerganov/whisper.cpp/", "https://huggingface.co/ggml-org/", ];