Search Pexels Photos
pexels_search_photosSearch for stock photos by query with optional filters for orientation, size, color, and locale. Returns results with mandatory photographer attribution.
Instructions
Search for stock photos by query with optional filters for orientation, size, color, and locale. Returns 3-5 results with mandatory photographer attribution.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| orientation | No | ||
| size | No | ||
| color | No | ||
| locale | No | ||
| per_page | No | ||
| force_refresh | No |
Implementation Reference
- src/tools/photo-search.ts:44-84 (handler)The main handler function `handlePhotoSearch` that executes the pexels_search_photos tool logic. It extracts query params (query, orientation, size, color, locale, per_page), checks cache, calls fetchPhotoSearch, formats results with attribution via formatPhotoResult and buildPhotoStructuredData, and returns content blocks.
export async function handlePhotoSearch( args: z.infer<typeof photoSearchSchema>, ): Promise<CallToolResult> { const start = Date.now(); const forceRefresh = args.force_refresh ?? false; const params = { query: args.query, orientation: args.orientation, size: args.size, color: args.color, locale: args.locale, per_page: args.per_page || 5, }; logDebug('tool: pexels_search_photos', '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 fetchPhotoSearch(params); const photos = response.photos?.slice(0, params.per_page) || []; const contentBlocks = photos.flatMap(formatPhotoResult); const structured = { results: photos.map(buildPhotoStructuredData) }; contentBlocks.push({ type: 'text', text: JSON.stringify(structured) }); setCache(cacheKey, contentBlocks, 600); logDebug('result:', 'count:', photos.length, `${Date.now() - start}ms`); return { content: contentBlocks }; } catch (error) { logDebug('error:', error instanceof Error ? error.message : String(error)); return formatApiError(error); } } - src/tools/photo-search.ts:86-101 (registration)Registers the tool 'pexels_search_photos' on the McpServer with title, description, input schema, annotations (readOnlyHint, idempotentHint), and wires it to handlePhotoSearch.
export function registerPhotoSearch(server: McpServer): void { server.registerTool( 'pexels_search_photos', { title: 'Search Pexels Photos', description: 'Search for stock photos by query with optional filters for orientation, size, color, and locale. Returns 3-5 results with mandatory photographer attribution.', inputSchema: photoSearchSchema, annotations: { readOnlyHint: true, idempotentHint: true, }, }, (args) => handlePhotoSearch(args), ); } - src/utils/validation.ts:13-21 (schema)The `photoSearchSchema` Zod schema used for input validation of pexels_search_photos. Defines fields: query (trimmed + max 100), orientation, size, color, locale, per_page (1-10), force_refresh.
export const photoSearchSchema = 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(), color: colorSchema.optional(), locale: z.string().optional(), per_page: z.number().min(1).max(10).optional(), force_refresh: z.boolean().optional(), }); - src/tools/photo-search.ts:11-26 (helper)Helper function `buildPhotoStructuredData` that converts a PexelsPhoto into structured output data conforming to photoOutputSchema.
export function buildPhotoStructuredData(photo: PexelsPhoto): z.infer<typeof photoOutputSchema> { const bestSrc = photo.src.medium; return { id: photo.id, kind: 'photo', creatorName: photo.photographer, creatorUrl: photo.photographer_url, pageUrl: photo.url, previewUrl: bestSrc, mediaUrl: photo.src.large2x || bestSrc, mediaMimeType: 'image/jpeg', dimensions: { width: photo.width, height: photo.height }, avgColor: photo.avg_color, alt: photo.alt || '', }; } - src/tools/photo-search.ts:28-42 (helper)Helper function `formatPhotoResult` that formats a PexelsPhoto into a text block with markdown (photographer attribution, dimensions, color, alt text, image preview) and a resource_link block.
export function formatPhotoResult(photo: PexelsPhoto): ContentBlock[] { const text = `Photo by [${photo.photographer}](${photo.photographer_url}) on Pexels **ID:** ${photo.id} **Dimensions:** ${photo.width}x${photo.height} **Color:** ${photo.avg_color} **Alt:** ${photo.alt || 'No description'} **Link:** ${photo.url} `; return [ { type: 'text', text }, { type: 'resource_link', uri: photo.src.medium, name: 'Preview', mimeType: 'image/jpeg' }, ]; }