getChannelTopVideos
Retrieve a YouTube channel's most popular videos using its channel ID. Optionally set result count, include tags, or select description detail to manage token cost.
Instructions
Retrieves a channel's most popular videos. CRITICAL: Requires a valid channelId (starting with 'UC...'), NOT a channel handle or name. Use searchVideos (type='channel') first to find the channelId if you only have a name.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| channelId | Yes | YouTube channel ID to get top videos from | |
| maxResults | No | Maximum number of top videos to return (1-500, default: 10) | |
| includeTags | No | Specify 'true' to include the video's 'tags' array in the response, which is useful for extracting niche keywords. The 'tags' are omitted by default to conserve tokens. | |
| descriptionDetail | No | Controls video description detail to manage token cost. Options: 'NONE' (default, no text), 'SNIPPET' (a brief preview for broad scans), 'LONG' (a 500-char text for deep analysis of specific targets). | NONE |
Implementation Reference
- src/services/youtube.service.ts:452-602 (handler)Core handler: Calls YouTube Search API (order: viewCount) to find channel's top videos, then fetches details via Videos API. Caches results. Returns LeanChannelTopVideo[] with engagement ratios.
async getChannelTopVideos( options: ChannelOptions ): Promise<LeanChannelTopVideo[]> { const cacheKey = this.cacheService.createOperationKey( "getChannelTopVideos", options ); const operation = async (): Promise<LeanChannelTopVideo[]> => { try { const { channelId, maxResults = 10, includeTags = false, descriptionDetail = "NONE", } = options; const searchResults: youtube_v3.Schema$SearchResult[] = []; let nextPageToken: string | undefined = undefined; const targetResults = Math.min(maxResults, this.ABSOLUTE_MAX_RESULTS); while (searchResults.length < targetResults) { const response = await this.trackCost( () => this.youtube.search.list({ part: ["id"], channelId: channelId, maxResults: Math.min( this.MAX_RESULTS_PER_PAGE, targetResults - searchResults.length ), order: "viewCount", type: ["video"], pageToken: nextPageToken, }), API_COSTS["search.list"] ); const searchResponse: youtube_v3.Schema$SearchListResponse = response.data; if (!searchResponse.items?.length) { break; } searchResults.push(...searchResponse.items); nextPageToken = searchResponse.nextPageToken || undefined; if (!nextPageToken) { break; } } if (!searchResults.length) { throw new Error("No videos found."); } const videoIds = searchResults .map((item) => item.id?.videoId) .filter((id): id is string => id !== undefined); const videoDetails: youtube_v3.Schema$Video[] = []; const detailPromises = []; for (let i = 0; i < videoIds.length; i += this.MAX_RESULTS_PER_PAGE) { const batch = videoIds.slice(i, i + this.MAX_RESULTS_PER_PAGE); const promise = this.trackCost( () => this.youtube.videos.list({ part: ["snippet", "statistics", "contentDetails"], id: batch, }), API_COSTS["videos.list"] ); detailPromises.push(promise); } const detailOutcomes = await Promise.allSettled(detailPromises); for (const outcome of detailOutcomes) { if (outcome.status === "fulfilled") { const response = outcome.value; if (response.data.items) { videoDetails.push(...response.data.items); } } else { if (outcome.reason instanceof AppError) throw outcome.reason; console.error( "A video details batch in getChannelTopVideos failed:", outcome.reason ); } } // If we had IDs to fetch but got no details, it means all batches failed. if (videoDetails.length === 0 && videoIds.length > 0) { throw new Error("All batches failed to retrieve video details."); } return videoDetails.slice(0, targetResults).map((video) => { const viewCount = parseYouTubeNumber(video.statistics?.viewCount); const likeCount = parseYouTubeNumber(video.statistics?.likeCount); const commentCount = parseYouTubeNumber( video.statistics?.commentCount ); const formattedDescription = formatDescription( video.snippet?.description, descriptionDetail ); const baseVideo = { id: video.id, title: video.snippet?.title, publishedAt: video.snippet?.publishedAt, duration: video.contentDetails?.duration, viewCount: viewCount, likeCount: likeCount, commentCount: commentCount, likeToViewRatio: calculateLikeToViewRatio(viewCount, likeCount), commentToViewRatio: calculateCommentToViewRatio( viewCount, commentCount ), categoryId: video.snippet?.categoryId ?? null, defaultLanguage: video.snippet?.defaultLanguage ?? null, }; const videoWithDescription = formattedDescription !== undefined ? { ...baseVideo, description: formattedDescription } : baseVideo; return includeTags ? { ...videoWithDescription, tags: video.snippet?.tags ?? [] } : videoWithDescription; }); } catch (error) { if (error instanceof AppError) throw error; throw new YouTubeApiError( `YouTube API call for getChannelTopVideos failed for channelId: ${options.channelId}`, error ); } }; return this.cacheService.getOrSet( cacheKey, operation, CACHE_TTLS.SEMI_STATIC, CACHE_COLLECTIONS.CHANNEL_TOP_VIDEOS, options ); } - Zod schema defining input parameters: channelId (required), maxResults (default 10), includeTags (default false), descriptionDetail (enum NONE/SNIPPET/LONG).
export const getChannelTopVideosSchema = z.object({ channelId: channelIdSchema.describe( "YouTube channel ID to get top videos from" ), maxResults: maxResultsSchema .optional() .default(10) .describe("Maximum number of top videos to return (1-500, default: 10)"), includeTags: z .boolean() .optional() .default(false) .describe( "Specify 'true' to include the video's 'tags' array in the response, which is useful for extracting niche keywords. The 'tags' are omitted by default to conserve tokens." ), descriptionDetail: z .enum(["NONE", "SNIPPET", "LONG"]) .optional() .default("NONE") .describe( "Controls video description detail to manage token cost. Options: 'NONE' (default, no text), 'SNIPPET' (a brief preview for broad scans), 'LONG' (a 500-char text for deep analysis of specific targets)." ), }); - src/tools/index.ts:34-34 (registration)Tool class registered in TOOL_CLASSES array for MCP server registration.
GetChannelTopVideosTool, - src/types/youtube.ts:19-33 (helper)Type definition for the returned video data structure (LeanChannelTopVideo).
export interface LeanChannelTopVideo { id: string | null | undefined; title: string | null | undefined; description?: string | null | undefined; publishedAt: string | null | undefined; duration: string | null | undefined; viewCount: number | null | undefined; likeCount: number | null | undefined; commentCount: number | null | undefined; likeToViewRatio: number | null | undefined; commentToViewRatio: number | null | undefined; tags?: string[] | null | undefined; categoryId: string | null | undefined; defaultLanguage: string | null | undefined; } - ChannelOptions interface for the handler.s parameters.
export interface ChannelOptions { channelId: string; maxResults?: number; includeTags?: boolean; descriptionDetail?: "NONE" | "SNIPPET" | "LONG"; }