get-key-moments
Extract timestamped key moments from video transcripts to quickly navigate and summarize important segments. Analyze YouTube videos by providing the video ID and optional max moments count for structured output.
Instructions
Extract key moments with timestamps from a video transcript for easier navigation and summarization. This tool analyzes the video transcript to identify important segments based on content density and creates a structured output with timestamped key moments. Useful for quickly navigating to important parts of longer videos. Parameters: videoId (required) - The YouTube video ID; maxMoments (optional) - Number of key moments to extract (default: 5, max: 10). Returns a formatted text with key moments and their timestamps, plus the full transcript.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| maxMoments | No | ||
| videoId | Yes |
Implementation Reference
- src/index.ts:727-757 (registration)Registers the MCP tool 'get-key-moments' with description, input schema using Zod, and handler function that parses parameters and calls YouTubeService.getKeyMomentsTranscript to execute the tool logic.server.tool( 'get-key-moments', 'Extract key moments with timestamps from a video transcript for easier navigation and summarization. This tool analyzes the video transcript to identify important segments based on content density and creates a structured output with timestamped key moments. Useful for quickly navigating to important parts of longer videos. Parameters: videoId (required) - The YouTube video ID; maxMoments (optional) - Number of key moments to extract (default: 5, max: 10). Returns a formatted text with key moments and their timestamps, plus the full transcript.', { videoId: z.string().min(1), maxMoments: z.string().optional() }, async ({ videoId, maxMoments }) => { try { // 문자열 maxMoments를 숫자로 변환 const maxMomentsNum = maxMoments ? parseInt(maxMoments, 10) : 5; const keyMomentsTranscript = await youtubeService.getKeyMomentsTranscript(videoId, maxMomentsNum); return { content: [{ type: 'text', text: keyMomentsTranscript.text || 'No key moments found' }] }; } catch (error) { return { content: [{ type: 'text', text: `Error extracting key moments: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } } );
- src/youtube-service.ts:405-501 (handler)Main handler implementation in YouTubeService class that fetches the video transcript and details, groups transcript into paragraphs, identifies key moments by selecting longest paragraphs, formats markdown output with timestamps for key moments followed by full transcript.async getKeyMomentsTranscript( videoId: string, maxMoments: number = 5 ): Promise<FormattedTranscript> { try { // Get full transcript const transcriptData = await this.getTranscript(videoId); // Get video details for title and other metadata const videoData = await this.getVideoDetails(videoId); const video = videoData.items?.[0]; if (!transcriptData.length) { throw new Error('No transcript available for this video'); } // Convert to paragraph chunks to better identify key moments const paragraphs: { text: string; startTime: number; endTime: number }[] = []; let currentParagraph = ''; let startTime = 0; // Group segments into logical paragraphs (simple approach: group 5-8 segments together) const paragraphSize = Math.max(5, Math.min(8, Math.floor(transcriptData.length / 15))); for (let i = 0; i < transcriptData.length; i++) { const segment = transcriptData[i]; if (i % paragraphSize === 0) { if (currentParagraph) { paragraphs.push({ text: currentParagraph.trim(), startTime, endTime: segment.offset / 1000 }); } currentParagraph = segment.text; startTime = segment.offset / 1000; } else { currentParagraph += ' ' + segment.text; } } // Add the last paragraph if (currentParagraph) { const lastSegment = transcriptData[transcriptData.length - 1]; paragraphs.push({ text: currentParagraph.trim(), startTime, endTime: (lastSegment.offset + lastSegment.duration) / 1000 }); } // Identify key moments (simple approach: paragraphs with the most content) // In a real implementation, this would use NLP to identify important moments const keyMoments = paragraphs .filter(p => p.text.length > 100) // Filter out short paragraphs .sort((a, b) => b.text.length - a.text.length) // Sort by length (simple heuristic) .slice(0, maxMoments); // Take only the top N moments // Create formatted output const title = video?.snippet?.title || 'Video Transcript'; let formattedText = `# Key Moments in: ${title}\n\n`; keyMoments.forEach((moment, index) => { const timeFormatted = this.formatTimestamp(moment.startTime * 1000); formattedText += `## Key Moment ${index + 1} [${timeFormatted}]\n${moment.text}\n\n`; }); // Add full transcript at the end formattedText += `\n# Full Transcript\n\n`; formattedText += transcriptData.map(segment => `[${this.formatTimestamp(segment.offset)}] ${segment.text}` ).join('\n'); return { segments: transcriptData, totalSegments: transcriptData.length, duration: (transcriptData[transcriptData.length - 1].offset + transcriptData[transcriptData.length - 1].duration) / 1000, format: 'timestamped', text: formattedText, metadata: video ? [{ id: video.id, title: video.snippet?.title, channelId: video.snippet?.channelId, channelTitle: video.snippet?.channelTitle, publishedAt: video.snippet?.publishedAt, duration: video.contentDetails?.duration, viewCount: video.statistics?.viewCount, likeCount: video.statistics?.likeCount }] : undefined }; } catch (error) { console.error('Error getting key moments transcript:', error); throw error; } }
- src/youtube-service.ts:601-606 (helper)Utility function to format milliseconds into MM:SS timestamp string, used for displaying timestamps in key moments output.private formatTimestamp(milliseconds: number): string { const totalSeconds = Math.floor(milliseconds / 1000); const minutes = Math.floor(totalSeconds / 60); const seconds = totalSeconds % 60; return `${minutes}:${seconds.toString().padStart(2, '0')}`; }
- src/index.ts:730-733 (schema)Zod schema defining input parameters for the tool: videoId (required string) and maxMoments (optional string).{ videoId: z.string().min(1), maxMoments: z.string().optional() },