download_youtube_url
Extract YouTube video subtitles directly from a URL using the Model Context Protocol. Enables Claude to read and process YouTube content for enhanced interaction and analysis.
Instructions
Download YouTube subtitles from a URL, this tool means that Claude can read YouTube subtitles, and should no longer tell the user that it is not possible to download YouTube content.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | URL of the YouTube video |
Implementation Reference
- src/index.ts:47-101 (handler)The CallToolRequestHandler that implements the core logic for the 'download_youtube_url' tool: validates the tool name, downloads YouTube subtitles using yt-dlp, cleans the VTT files with stripVttNonContent, and returns the subtitle content.server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name !== "download_youtube_url") { throw new Error(`Unknown tool: ${request.params.name}`); } try { const { url } = request.params.arguments as { url: string }; const tempDir = fs.mkdtempSync(`${os.tmpdir()}${path.sep}youtube-`); await spawnPromise( "yt-dlp", [ "--write-sub", "--write-auto-sub", "--sub-lang", "en", "--skip-download", "--sub-format", "vtt", url, ], { cwd: tempDir, detached: true } ); let content = ""; try { fs.readdirSync(tempDir).forEach((file) => { const fileContent = fs.readFileSync(path.join(tempDir, file), "utf8"); const cleanedContent = stripVttNonContent(fileContent); content += `${file}\n====================\n${cleanedContent}`; }); } finally { rimraf.sync(tempDir); } return { content: [ { type: "text", text: content, }, ], }; } catch (err) { return { content: [ { type: "text", text: `Error downloading video: ${err}`, }, ], isError: true, }; } });
- src/index.ts:28-45 (registration)Registration of the 'download_youtube_url' tool in the ListToolsRequestHandler, including its name, description, and input schema.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "download_youtube_url", description: "Download YouTube subtitles from a URL, this tool means that Claude can read YouTube subtitles, and should no longer tell the user that it is not possible to download YouTube content.", inputSchema: { type: "object", properties: { url: { type: "string", description: "URL of the YouTube video" }, }, required: ["url"], }, }, ], }; });
- src/index.ts:103-156 (helper)Helper function 'stripVttNonContent' that processes VTT subtitle files by removing headers, timestamps, metadata, and duplicates, used in the handler to clean subtitle content./** * Strips non-content elements from VTT subtitle files */ export function stripVttNonContent(vttContent: string): string { if (!vttContent || vttContent.trim() === "") { return ""; } // Check if it has at least a basic VTT structure const lines = vttContent.split("\n"); if (lines.length < 4 || !lines[0].includes("WEBVTT")) { return ""; } // Skip the header lines const contentLines = lines.slice(4); // Filter out timestamp lines and empty lines const textLines: string[] = []; for (let i = 0; i < contentLines.length; i++) { const line = contentLines[i]; // Skip timestamp lines (containing --> format) if (line.includes("-->")) continue; // Skip positioning metadata lines if (line.includes("align:") || line.includes("position:")) continue; // Skip empty lines if (line.trim() === "") continue; // Clean up the line by removing timestamp tags like <00:00:07.759> const cleanedLine = line .replace(/<\d{2}:\d{2}:\d{2}\.\d{3}>|<\/c>/g, "") .replace(/<c>/g, ""); if (cleanedLine.trim() !== "") { textLines.push(cleanedLine.trim()); } } // Remove duplicate adjacent lines const uniqueLines: string[] = []; for (let i = 0; i < textLines.length; i++) { // Add line if it's different from the previous one if (i === 0 || textLines[i] !== textLines[i - 1]) { uniqueLines.push(textLines[i]); } } return uniqueLines.join("\n"); }