searchVideos
Find YouTube videos by query with filters for duration, recency, channel, and region to get relevant results with metadata.
Instructions
Searches for videos based on a query string. Returns a list of videos matching the search criteria, including titles, descriptions, and metadata. Use this when you need to find videos related to specific topics or keywords.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| channelId | No | Restrict search to specific channel ID | |
| maxResults | No | Maximum number of results to return (1-500, default: 10) | |
| order | No | Sort order for results (default: relevance) | |
| query | Yes | Search query string to find videos | |
| recency | No | Filter by recency. Possible values: 'any', 'pastHour', 'pastDay', 'pastWeek', 'pastMonth', 'pastQuarter', 'pastYear'. | |
| regionCode | No | 2-letter country code to restrict results | |
| type | No | Type of content to search for (default: video) | |
| videoDuration | No | Filter by video duration. 'any' (default): no duration filter. 'short': videos less than 4 minutes. 'medium': videos 4 to 20 minutes. 'long': videos longer than 20 minutes. |
Implementation Reference
- src/tools/video/searchVideos.ts:63-87 (handler)The main handler function for the 'searchVideos' MCP tool. It validates input parameters using the schema, calls the youtubeService to perform the search, maps the results to a lean format, and formats the response.export const searchVideosHandler = async ( params: SearchParams, youtubeService: YoutubeService ): Promise<CallToolResult> => { try { const validatedParams = searchVideosSchema.parse(params); const rawResults = await youtubeService.searchVideos(validatedParams); const leanResults: LeanVideoSearchResult[] = rawResults.map( (result: youtube_v3.Schema$SearchResult) => ({ videoId: result.id?.videoId ?? null, title: result.snippet?.title ?? null, descriptionSnippet: result.snippet?.description ?? null, channelId: result.snippet?.channelId ?? null, channelTitle: result.snippet?.channelTitle ?? null, publishedAt: result.snippet?.publishedAt ?? null, }) ); return formatSuccess(leanResults); } catch (error: unknown) { return formatError(error); } };
- Zod schema defining the input parameters for the searchVideos tool, including query, maxResults, order, type, filters etc.export const searchVideosSchema = z.object({ query: querySchema.describe("Search query string to find videos"), maxResults: maxResultsSchema .optional() .default(10) .describe("Maximum number of results to return (1-500, default: 10)"), order: z .enum(["relevance", "date", "viewCount"]) .optional() .describe("Sort order for results (default: relevance)"), type: z .enum(["video", "channel"]) .optional() .describe("Type of content to search for (default: video)"), channelId: z .string() .optional() .describe("Restrict search to specific channel ID"), videoDuration: z .enum(["any", "short", "medium", "long"]) .optional() .describe( "Filter by video duration. 'any' (default): no duration filter. 'short': videos less than 4 minutes. 'medium': videos 4 to 20 minutes. 'long': videos longer than 20 minutes." ), recency: z .enum([ "any", "pastHour", "pastDay", "pastWeek", "pastMonth", "pastQuarter", "pastYear", ]) .optional() .describe( "Filter by recency. Possible values: 'any', 'pastHour', 'pastDay', 'pastWeek', 'pastMonth', 'pastQuarter', 'pastYear'." ), regionCode: z .string() .length(2) .optional() .describe("2-letter country code to restrict results"), });
- src/tools/index.ts:84-88 (registration)Registration of the searchVideos tool in the allTools function, using the exported config and handler, injecting youtubeService.{ config: searchVideosConfig, handler: (params) => searchVideosHandler(params as unknown as SearchParams, youtubeService), },
- Supporting method in YoutubeService that performs the actual paginated YouTube API search.list calls, handles caching, pagination, and parameter mapping for recency filters.async searchVideos( options: SearchOptions ): Promise<youtube_v3.Schema$SearchResult[]> { const cacheKey = this.cacheService.createOperationKey( "searchVideos", options ); const operation = async (): Promise<youtube_v3.Schema$SearchResult[]> => { try { const { query, maxResults = 10, order = "relevance", type = "video", channelId, videoDuration, publishedAfter, recency, regionCode, } = options; const results: youtube_v3.Schema$SearchResult[] = []; let nextPageToken: string | undefined = undefined; const targetResults = Math.min(maxResults, this.ABSOLUTE_MAX_RESULTS); let calculatedPublishedAfter = publishedAfter; if (recency && recency !== "any") { calculatedPublishedAfter = this.calculatePublishedAfter(recency); } while (results.length < targetResults) { const searchParams: youtube_v3.Params$Resource$Search$List = { part: ["snippet"], q: query, maxResults: Math.min( this.MAX_RESULTS_PER_PAGE, targetResults - results.length ), type: [type], order: order, pageToken: nextPageToken, }; if (channelId) { searchParams.channelId = channelId; } if (videoDuration && videoDuration !== "any") { searchParams.videoDuration = videoDuration; } if (calculatedPublishedAfter) { searchParams.publishedAfter = calculatedPublishedAfter; } if (regionCode) { searchParams.regionCode = regionCode; } const response = await this.trackCost( () => this.youtube.search.list(searchParams), API_COSTS["search.list"] ); const searchResponse: youtube_v3.Schema$SearchListResponse = response.data; if (!searchResponse.items?.length) { break; } results.push(...searchResponse.items); nextPageToken = searchResponse.nextPageToken || undefined; if (!nextPageToken) { break; } } return results.slice(0, targetResults); } catch (error) { throw new YouTubeApiError( `YouTube API call for searchVideos failed`, error ); } }; return this.cacheService.getOrSet( cacheKey, operation, CACHE_TTLS.STANDARD, CACHE_COLLECTIONS.VIDEO_DETAILS, options, ["snippet.thumbnails"] ); }