get_transcript
Retrieve transcript text from any YouTube video. Supports auto-detection or selection of language, and optional timestamps. Get clean text for accessibility, analysis, or content creation.
Instructions
Get transcript text from a YouTube video.
Args: url: YouTube URL or video ID language: Language code (default: auto-detect) with_timestamps: Include timestamps in output
Returns: Dict containing video_id, language, and transcript text
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | ||
| language | No | auto | |
| with_timestamps | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/youtube_mcp/transcript.py:21-85 (handler)The get_transcript function: extracts a YouTube video transcript using youtube-transcript-api. Accepts a URL/video ID, optional language (default auto), and with_timestamps flag. Returns a dict with video_id, language, and transcript text.
def get_transcript( url: str, language: str = "auto", with_timestamps: bool = False, ) -> dict[str, Any]: """Get transcript text from a YouTube video. Args: url: YouTube URL or video ID language: Language code (default: auto-detect) with_timestamps: Include timestamps in output Returns: Dict containing video_id, language, and transcript text """ video_id = extract_video_id(url) api = YouTubeTranscriptApi() # Get available transcripts transcript_list = api.list(video_id) # Determine language priority (English first, then Japanese for auto; otherwise specified + English) preferred_languages = ["en", "ja"] if language == "auto" else [language, "en"] # Find transcript transcript = None used_language = None # Try manual transcripts first for lang in preferred_languages: try: transcript = transcript_list.find_transcript([lang]) used_language = lang break except NoTranscriptFound: continue # Fall back to any available transcript if transcript is None: for t in transcript_list: transcript = t used_language = t.language_code break if transcript is None: return { "video_id": video_id, "language": None, "transcript": None, "error": "No transcript found", } # Fetch and format transcript entries = transcript.fetch() if with_timestamps: lines = [f"{format_timestamp(entry.start)} {entry.text}" for entry in entries] else: lines = [entry.text for entry in entries] return { "video_id": video_id, "language": used_language, "transcript": "\n".join(lines), } - src/youtube_mcp/transcript.py:21-25 (schema)Function signature defines the input schema: url (str), language (str, default 'auto'), with_timestamps (bool, default False). Return type is dict[str, Any].
def get_transcript( url: str, language: str = "auto", with_timestamps: bool = False, ) -> dict[str, Any]: - src/youtube_mcp/server.py:10-10 (registration)Tool registration: mcp.tool()(get_transcript) registers get_transcript as an MCP tool on the FastMCP 'youtube' server.
mcp.tool()(get_transcript) - src/youtube_mcp/transcript.py:11-18 (helper)Helper function format_timestamp: converts seconds to [H:MM:SS] or [M:SS] format, used when with_timestamps is True.
def format_timestamp(seconds: float) -> str: """Format seconds as [H:MM:SS] or [M:SS].""" hours = int(seconds // 3600) minutes = int((seconds % 3600) // 60) secs = int(seconds % 60) if hours > 0: return f"[{hours}:{minutes:02d}:{secs:02d}]" return f"[{minutes}:{secs:02d}]" - src/youtube_mcp/url.py:7-66 (helper)Helper function extract_video_id: parses various YouTube URL formats (standard, shortened, embed, shorts, live, mobile) to extract the 11-character video ID.
def extract_video_id(url: str) -> str: """Extract video ID from various forms of YouTube URLs. Handles standard, shortened, embed, shorts, live URLs, as well as regional domains and mobile versions. Args: url: YouTube URL or video ID Returns: The extracted video ID Raises: ValueError: If the video ID cannot be extracted """ if not url: raise ValueError("URL is required") # Handle case where input is just the 11-character video ID if re.match(r"^[a-zA-Z0-9_-]{11}$", url): return url # Prepend scheme if not present if not re.match(r"https?://", url): url = "https://" + url parsed = urlparse(url) hostname = parsed.hostname if not hostname: raise ValueError(f"Could not parse URL: {url}") # youtu.be shortlinks if hostname == "youtu.be": video_id = parsed.path.lstrip("/").split("/")[0].split("?")[0] if video_id: return video_id raise ValueError(f"Could not extract video ID from URL: {url}") # youtube.com variants youtube_pattern = r"^(?:www\.|m\.|music\.)?youtube\.com$" if not re.match(youtube_pattern, hostname): raise ValueError(f"Not a YouTube URL: {url}") # /watch?v=VIDEO_ID if parsed.path == "/watch": query_params = parse_qs(parsed.query) video_ids = query_params.get("v") if video_ids: return video_ids[0] raise ValueError(f"Could not extract video ID from URL: {url}") # /embed/VIDEO_ID, /shorts/VIDEO_ID, /live/VIDEO_ID, /v/VIDEO_ID path_parts = parsed.path.split("/") if len(path_parts) >= 3 and path_parts[1] in ("embed", "shorts", "live", "v"): video_id = path_parts[2].split("?")[0] if video_id: return video_id raise ValueError(f"Could not extract video ID from URL: {url}")