getTranscripts
Retrieve key segments or full text of YouTube video transcripts to analyze messaging.
Instructions
Retrieves specific, meaningful segments of a video's transcript. By default, it returns the intro 'hook' and the final 'outro' or call to action. It can also return the full transcript text. Use this to efficiently analyze a video's key messaging.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| videoIds | Yes | Array of YouTube video IDs to get transcripts for | |
| lang | No | Language code for transcripts (e.g., 'en', 'ko', 'es'). Defaults to environment setting or 'en' | en |
| format | No | The desired transcript format. 'full_text': Returns the entire transcript as a single string. 'key_segments': (Default) Returns only the video's intro hook and final call to action. | key_segments |
Implementation Reference
- src/tools/video/getTranscripts.ts:29-52 (handler)The GetTranscriptsTool class - the handler that executes the 'getTranscripts' tool logic. It maps over videoIds, calls transcriptService.getTranscriptSegments for each, then formats results via formatVideoMap.
export class GetTranscriptsTool extends BaseTool<typeof getTranscriptsSchema> { name = "getTranscripts"; description = "Retrieves specific, meaningful segments of a video's transcript. By default, it returns the intro 'hook' and the final 'outro' or call to action. It can also return the full transcript text. Use this to efficiently analyze a video's key messaging."; schema = getTranscriptsSchema; protected async executeImpl( params: z.infer<typeof getTranscriptsSchema> ): Promise<CallToolResult> { const { videoIds, lang, format } = params; const transcriptPromises = videoIds.map((videoId) => this.container.transcriptService.getTranscriptSegments( videoId, lang, format ) ); const transcripts = await Promise.all(transcriptPromises); const result = formatVideoMap(videoIds, transcripts); return formatSuccess(result); } } - The Zod schema for getTranscripts: accepts videoIds (array of video IDs), lang (default 'en'), and format ('full_text' or 'key_segments', default 'key_segments').
export const getTranscriptsSchema = z.object({ videoIds: z .array(videoIdSchema) .describe("Array of YouTube video IDs to get transcripts for"), lang: languageSchema .default("en") .describe( "Language code for transcripts (e.g., 'en', 'ko', 'es'). Defaults to environment setting or 'en'" ), format: z .enum(["full_text", "key_segments"]) .default("key_segments") .describe( "The desired transcript format. " + "'full_text': Returns the entire transcript as a single string. " + "'key_segments': (Default) Returns only the video's intro hook and final call to action." ), }); - src/tools/index.ts:10-54 (registration)GetTranscriptsTool is imported and included in TOOL_CLASSES array (line 31) and also conditionally registered as a standalone tool when no YouTube API key is present (line 53). The actual MCP registration via server.registerTool happens in the loop (lines 65-87).
import { GetTranscriptsTool } from "./video/getTranscripts.js"; import { GetVideoCommentsTool } from "./video/getVideoComments.js"; import { GetChannelStatisticsTool } from "./channel/getChannelStatistics.js"; import { GetChannelTopVideosTool } from "./channel/getChannelTopVideos.js"; import { GetTrendingVideosTool } from "./general/getTrendingVideos.js"; import { GetVideoCategoriesTool } from "./general/getVideoCategories.js"; import { FindConsistentOutlierChannelsTool } from "./general/findConsistentOutlierChannels.js"; interface ITool { readonly name: string; readonly description: string; readonly schema: z.ZodObject<z.ZodRawShape>; execute(args: z.infer<this["schema"]>): Promise<CallToolResult>; } type ToolConstructor = new (container: IServiceContainer) => ITool; // 1. Maintain a list of Constructors const TOOL_CLASSES = [ GetVideoDetailsTool, SearchVideosTool, GetTranscriptsTool, GetVideoCommentsTool, GetChannelStatisticsTool, GetChannelTopVideosTool, GetTrendingVideosTool, GetVideoCategoriesTool, ]; export function registerTools(server: McpServer, container: IServiceContainer) { const hasYoutubeKey = !!process.env.YOUTUBE_API_KEY; const toolsToRegister: ToolConstructor[] = []; if (hasYoutubeKey) { // Register all standard YouTube tools toolsToRegister.push(...(TOOL_CLASSES as ToolConstructor[])); // Register analytics tools if connection string is present if (process.env.MDB_MCP_CONNECTION_STRING) { toolsToRegister.push(FindConsistentOutlierChannelsTool); } } else { // Only register tools that don't require the API key toolsToRegister.push(GetTranscriptsTool); } - TranscriptService.getTranscriptSegments - the core helper service that fetches and processes transcript data. Fetches via youtube-transcript-plus, caches results, and returns either full_text (joined string) or key_segments (extracted hook and outro).
public async getTranscriptSegments( videoId: string, lang: string = "en", format: "full_text" | "key_segments" = "key_segments" ): Promise<object | null> { const rawSubtitles = await this.fetchAndCacheRawTranscript(videoId, lang); if (!rawSubtitles || rawSubtitles.length === 0) { return null; } if (format === "full_text") { return { transcript: rawSubtitles.map((sub) => sub.text).join(" "), }; } const hook = this.extractHook(rawSubtitles); const outro = this.extractOutro(rawSubtitles); return { hook, outro }; } private async fetchAndCacheRawTranscript( videoId: string, lang: string = "en" ): Promise<Subtitle[]> { const cacheKey = this.cacheService.createOperationKey("getTranscript", { videoId, lang, }); const operation = async (): Promise<Subtitle[]> => { try { const transcript = (await fetchTranscript(videoId, { lang, })) as TranscriptResult[]; // Map youtube-transcript-plus format to the Subtitle shape used internally return transcript.map( (item: { offset: number; duration: number; text: string }) => ({ start: String(item.offset), dur: String(item.duration), text: item.text, }) ); } catch (error) { console.error( `[TranscriptService] Failed to fetch transcript for ${videoId} (lang: ${lang}):`, error instanceof Error ? error.message : error ); return []; } }; return this.cacheService.getOrSet<Subtitle[]>( cacheKey, operation, CACHE_TTLS.STATIC, CACHE_COLLECTIONS.TRANSCRIPTS, { videoId, lang } ); } private extractHook(subtitles: Subtitle[]): string { const HOOK_DURATION_SECONDS = 40; let hookText = ""; for (const sub of subtitles) { const startTime = parseFloat(sub.start); if (startTime < HOOK_DURATION_SECONDS) { hookText += sub.text + " "; } else { break; } } return hookText.trim(); } private extractOutro(subtitles: Subtitle[]): string { if (subtitles.length === 0) return ""; const OUTRO_DURATION_SECONDS = 30; const lastTimestamp = parseFloat(subtitles[subtitles.length - 1].start); const outroStartTime = Math.max(0, lastTimestamp - OUTRO_DURATION_SECONDS); const startIndex = subtitles.findIndex( (sub) => parseFloat(sub.start) >= outroStartTime ); if (startIndex === -1) { return subtitles .map((s) => s.text) .join(" ") .trim(); } return subtitles .slice(startIndex) .map((s) => s.text) .join(" ") .trim(); } }