search_channels
Find YouTube channels matching specific topics or criteria, with options to filter results and include channel statistics for analysis.
Instructions
Find and analyze YouTube channels
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search query for YouTube channels | |
| maxResults | No | Maximum number of channels to return | |
| includeStats | No | Whether to include channel statistics | |
| order | No | Sort order for channels | relevance |
Implementation Reference
- src/tools/channels.ts:20-127 (handler)Main handler implementation in ChannelSearchTool.execute(). Handles input validation, caching, quota checks, calls YouTube client, enhances and analyzes results.async execute(args: unknown): Promise<ChannelSearchResult & { metadata?: { quotaUsed: number; cached: boolean; analysis?: { categoryDistribution: Record<string, number>; subscriberRanges: Record<string, number>; avgVideosPerChannel: number; topPerformers: Array<{ name: string; subscribers: number; videos: number; }>; }; }; }> { // Validate input parameters const params = SearchChannelsSchema.parse(args); this.logger.info(`Searching channels for: "${params.query}"`); // Generate cache key const cacheKey = `channels:${Buffer.from(JSON.stringify(params)).toString('base64')}`; // Check cache first const cached = await this.cache.get<ChannelSearchResult>(cacheKey); if (cached) { this.logger.info(`Returning cached channel search results for: "${params.query}"`); return { ...cached, metadata: { quotaUsed: 0, cached: true } }; } // Check quota before making API call const operationCost = QuotaManager.getOperationCost('channel_search'); if (!this.quotaManager.canPerformOperation(operationCost)) { throw new QuotaExceededError('Insufficient quota for channel search operation'); } // Optimize operation based on quota availability const optimizedMaxResults = this.quotaManager.optimizeOperation('channel_search', params.maxResults); if (optimizedMaxResults === 0) { throw new QuotaExceededError('Cannot perform channel search due to quota constraints'); } const optimizedParams: SearchChannelsParams = { ...params, maxResults: optimizedMaxResults }; try { // Perform the channel search const result = await this.youtubeClient.searchChannels(optimizedParams); // Record quota usage await this.quotaManager.recordUsage(operationCost, 'channel_search'); // Add additional cost if we fetched detailed stats if (params.includeStats) { const additionalCost = Math.ceil(result.channels.length / 50); // 1 unit per 50 channels await this.quotaManager.recordUsage(additionalCost, 'channel_details'); } // Enhance the result with analysis const enhancedResult = await this.enhanceChannelResults(result); // Cache the result await this.cache.set(cacheKey, enhancedResult); this.logger.info( `Channel search completed for "${params.query}": ${result.channels.length} channels found` ); return { ...enhancedResult, metadata: { quotaUsed: operationCost + (params.includeStats ? Math.ceil(result.channels.length / 50) : 0), cached: false, analysis: this.analyzeChannelData(result.channels) } }; } catch (error) { this.logger.error(`Channel search failed for "${params.query}":`, error); // Try fallback strategies if quota exceeded if (error instanceof QuotaExceededError) { const fallbackResults = await this.getFallbackChannelData(params.query); if (fallbackResults) { this.logger.warn('Returning fallback channel data due to quota limits'); return { ...fallbackResults, metadata: { quotaUsed: 0, cached: true, fallback: true } } as any; } } throw error; } }
- src/types.ts:92-97 (schema)Zod schema definition for search_channels input parameters.export const SearchChannelsSchema = z.object({ query: z.string().describe('Search query for YouTube channels'), maxResults: z.number().min(1).max(50).default(10).describe('Maximum number of channels to return'), includeStats: z.boolean().default(true).describe('Whether to include channel statistics'), order: z.enum(['relevance', 'date', 'viewCount', 'videoCount']).default('relevance').describe('Sort order for channels'), });
- src/index.ts:350-380 (registration)Tool registration in listToolsRequestHandler: name, description, and inputSchema for search_channels.name: 'search_channels', description: 'Find and analyze YouTube channels', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query for YouTube channels' }, maxResults: { type: 'number', minimum: 1, maximum: 50, default: 10, description: 'Maximum number of channels to return' }, includeStats: { type: 'boolean', default: true, description: 'Whether to include channel statistics' }, order: { type: 'string', enum: ['relevance', 'date', 'viewCount', 'videoCount'], default: 'relevance', description: 'Sort order for channels' } }, required: ['query'] } },
- src/index.ts:575-577 (registration)Dispatch in CallToolRequestHandler switch statement routes search_channels calls to channelTool.execute().case 'search_channels': result = await this.channelTool.execute(args); break;
- src/youtube-client.ts:182-236 (helper)Low-level YouTube API searchChannels method called by the tool handler, performs actual API calls for channel search.async searchChannels(params: SearchChannelsParams): Promise<ChannelSearchResult> { try { this.checkQuota(100); // Search costs 100 quota units const searchResponse = await this.youtube.search.list({ part: ['snippet'], q: params.query, type: ['channel'], maxResults: params.maxResults, order: params.order }); this.quotaUsed += 100; if (!searchResponse.data.items) { return { channels: [], totalResults: 0 }; } const channelIds = searchResponse.data.items .map(item => item.id?.channelId) .filter(Boolean) as string[]; let channels: YouTubeChannel[] = []; if (params.includeStats && channelIds.length > 0) { this.checkQuota(1); // Channels.list costs 1 quota unit const channelsResponse = await this.youtube.channels.list({ part: ['snippet', 'statistics'], id: channelIds }); this.quotaUsed += 1; channels = this.mapChannelsResponse(channelsResponse.data.items || []); } else { channels = searchResponse.data.items.map(item => ({ id: item.id?.channelId || '', title: item.snippet?.title || '', description: item.snippet?.description || '', publishedAt: item.snippet?.publishedAt || '', thumbnails: item.snippet?.thumbnails || {} })); } return { channels, totalResults: searchResponse.data.pageInfo?.totalResults || 0, nextPageToken: searchResponse.data.nextPageToken }; } catch (error) { this.handleError(error); throw error; } }