Search Pexels Videos
pexels_search_videosSearch for stock videos by query with optional orientation, size, and locale filters. Returns HD-quality .mp4 links with mandatory attribution.
Instructions
Search for stock videos by query with optional filters. Returns HD-quality .mp4 links with mandatory attribution.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| orientation | No | ||
| size | No | ||
| locale | No | ||
| per_page | No | ||
| force_refresh | No |
Implementation Reference
- src/tools/video-search.ts:48-87 (handler)The main handler function `handleVideoSearch` that executes the pexels_search_videos tool logic. It accepts a query and optional filters (orientation, size, locale, per_page), checks cache, calls the Pexels API via fetchVideoSearch, formats results, and returns content blocks with video metadata, attribution, and resource links.
export async function handleVideoSearch( args: z.infer<typeof videoSearchSchema>, ): Promise<CallToolResult> { const start = Date.now(); const forceRefresh = args.force_refresh ?? false; const params = { query: args.query, orientation: args.orientation, size: args.size, locale: args.locale, per_page: args.per_page || 5, }; logDebug('tool: pexels_search_videos', 'params:', JSON.stringify(params)); const cacheKey = makeCacheKey(params); if (!forceRefresh) { const cached = getFromCache<ContentBlock[]>(cacheKey); if (cached) { logDebug('cache: HIT', 'key:', cacheKey, `${Date.now() - start}ms`); return { content: cached }; } logDebug('cache: MISS', 'key:', cacheKey); } try { const response = await fetchVideoSearch(params); const videos = response.videos?.slice(0, params.per_page) || []; const contentBlocks = videos.flatMap(formatVideoResult); const structured = { results: videos.map(buildVideoStructuredData) }; contentBlocks.push({ type: 'text', text: JSON.stringify(structured) }); setCache(cacheKey, contentBlocks, 600); logDebug('result:', 'count:', videos.length, `${Date.now() - start}ms`); return { content: contentBlocks }; } catch (error) { logDebug('error:', error instanceof Error ? error.message : String(error)); return formatApiError(error); } } - src/tools/video-search.ts:89-104 (registration)The registration function `registerVideoSearch` that registers the tool 'pexels_search_videos' with the MCP server, including its title, description, input schema (videoSearchSchema), and annotations (readOnlyHint, idempotentHint).
export function registerVideoSearch(server: McpServer): void { server.registerTool( 'pexels_search_videos', { title: 'Search Pexels Videos', description: 'Search for stock videos by query with optional filters. Returns HD-quality .mp4 links with mandatory attribution.', inputSchema: videoSearchSchema, annotations: { readOnlyHint: true, idempotentHint: true, }, }, (args) => handleVideoSearch(args), ); } - src/utils/validation.ts:23-30 (schema)The `videoSearchSchema` Zod schema defining the input validation for pexels_search_videos: query (string trimmed to 100 chars), optional orientation, size, locale, per_page (1-10), and force_refresh boolean.
export const videoSearchSchema = z.object({ query: z.string().trim().transform((val) => val.slice(0, 100)), orientation: z.enum(['landscape', 'portrait', 'square']).optional(), size: z.enum(['large', 'medium', 'small']).optional(), locale: z.string().optional(), per_page: z.number().min(1).max(10).optional(), force_refresh: z.boolean().optional(), }); - src/tools/video-search.ts:12-27 (helper)Helper function `buildVideoStructuredData` that transforms a PexelsVideo into structured output data with id, creator info, URLs, dimensions, and duration.
export function buildVideoStructuredData(video: PexelsVideo): z.infer<typeof videoOutputSchema> { const bestFile = chooseBestVideo(video.video_files); const validMp4 = bestFile?.file_type === 'video/mp4' ? bestFile : undefined; return { id: video.id, kind: 'video', creatorName: video.user.name, creatorUrl: video.user.url, pageUrl: video.url, previewUrl: video.image, mediaUrl: validMp4?.link || video.image, mediaMimeType: validMp4 ? 'video/mp4' : 'image/jpeg', dimensions: { width: video.width, height: video.height }, durationSeconds: video.duration, }; } - src/tools/video-search.ts:29-46 (helper)Helper function `formatVideoResult` that formats a single PexelsVideo into ContentBlock array containing a text block with markdown attribution and a resource_link block pointing to the video file or preview image.
export function formatVideoResult(video: PexelsVideo): ContentBlock[] { const bestFile = chooseBestVideo(video.video_files); const text = `Video by [${video.user.name}](${video.user.url}) on Pexels **ID:** ${video.id} **Dimensions:** ${video.width}x${video.height} **Duration:** ${video.duration}s **Link:** ${video.url} `; const validMp4 = bestFile?.file_type === 'video/mp4' ? bestFile : undefined; const mediaBlock: ContentBlock = validMp4 ? { type: 'resource_link', uri: validMp4.link, name: 'Video', mimeType: 'video/mp4' } : { type: 'resource_link', uri: video.image, name: 'Preview', mimeType: 'image/jpeg' }; return [{ type: 'text', text }, mediaBlock]; }