youtube-to-markdown
Convert YouTube videos to markdown format with transcripts for easy documentation and content creation.
Instructions
Convert a YouTube video to markdown, including transcript if available
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | URL of the YouTube video |
Implementation Reference
- src/server.ts:49-70 (handler)Handler switch case for youtube-to-markdown (shared with webpage tools): validates URL, security checks, calls Markdownify.toMarkdown with URLcase tools.YouTubeToMarkdownTool.name: case tools.BingSearchResultToMarkdownTool.name: case tools.WebpageToMarkdownTool.name: if (!validatedArgs.url) { throw new Error("URL is required for this tool"); } const parsedUrl = new URL(validatedArgs.url); if (!["http:", "https:"].includes(parsedUrl.protocol)) { throw new Error("Only http: and https: schemes are allowed."); } if (is_ip_private(parsedUrl.hostname)) { throw new Error(`Fetching ${validatedArgs.url} is potentially dangerous, aborting.`); } result = await Markdownify.toMarkdown({ url: validatedArgs.url, projectRoot: validatedArgs.projectRoot, uvPath: validatedArgs.uvPath || process.env.UV_PATH, }); break;
- src/tools.ts:3-17 (schema)Tool schema definition including name, description, and input schema requiring 'url'export const YouTubeToMarkdownTool = ToolSchema.parse({ name: "youtube-to-markdown", description: "Convert a YouTube video to markdown, including transcript if available", inputSchema: { type: "object", properties: { url: { type: "string", description: "URL of the YouTube video", }, }, required: ["url"], }, });
- src/server.ts:33-37 (registration)Tool registration via ListTools handler returning all exported tool schemas from tools.tsserver.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: Object.values(tools), }; });
- src/Markdownify.ts:74-124 (helper)Core conversion helper: for URL input (used by youtube-to-markdown), fetches content, saves to temp file, runs markitdown via uv, saves output markdownstatic async toMarkdown({ filePath, url, projectRoot = path.resolve(__dirname, ".."), uvPath = "~/.local/bin/uv", }: { filePath?: string; url?: string; projectRoot?: string; uvPath?: string; }): Promise<MarkdownResult> { try { let inputPath: string; let isTemporary = false; if (url) { const response = await fetch(url); let extension = null; if (url.endsWith(".pdf")) { extension = "pdf"; } const arrayBuffer = await response.arrayBuffer(); const content = Buffer.from(arrayBuffer); inputPath = await this.saveToTempFile(content, extension); isTemporary = true; } else if (filePath) { inputPath = filePath; } else { throw new Error("Either filePath or url must be provided"); } const text = await this._markitdown(inputPath, projectRoot, uvPath); const outputPath = await this.saveToTempFile(text); if (isTemporary) { fs.unlinkSync(inputPath); } return { path: outputPath, text }; } catch (e: unknown) { if (e instanceof Error) { throw new Error(`Error processing to Markdown: ${e.message}`); } else { throw new Error("Error processing to Markdown: Unknown error occurred"); } } }
- src/Markdownify.ts:20-51 (helper)Helper that executes the external 'markitdown' tool (which handles YouTube conversion) via uv run on the input file pathprivate static async _markitdown( filePath: string, projectRoot: string, uvPath: string, ): Promise<string> { const venvPath = path.join(projectRoot, ".venv"); const markitdownPath = path.join( venvPath, process.platform === "win32" ? "Scripts" : "bin", `markitdown${process.platform === "win32" ? ".exe" : ""}`, ); if (!fs.existsSync(markitdownPath)) { throw new Error("markitdown executable not found"); } // Expand tilde in uvPath if present const expandedUvPath = expandHome(uvPath); // Use execFile to prevent command injection const { stdout, stderr } = await execFileAsync(expandedUvPath, [ "run", markitdownPath, filePath, ]); if (stderr) { throw new Error(`Error executing command: ${stderr}`); } return stdout; }