search_images_for_essay
Find openly-licensed images tailored for essays by specifying your topic, key concepts, and preferred style. Ideal for enhancing academic or research content with relevant visuals.
Instructions
Search for images suitable for illustrating an essay
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| concepts | Yes | List of key concepts to find images for | |
| essay_topic | Yes | Main topic/title of the essay | |
| max_images | No | Maximum images to return (default: 10) | |
| style | No | Preferred image style (default: any) |
Input Schema (JSON Schema)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"properties": {
"concepts": {
"description": "List of key concepts to find images for",
"items": {
"type": "string"
},
"type": "array"
},
"essay_topic": {
"description": "Main topic/title of the essay",
"type": "string"
},
"max_images": {
"description": "Maximum images to return (default: 10)",
"type": "number"
},
"style": {
"description": "Preferred image style (default: any)",
"enum": [
"photo",
"illustration",
"any"
],
"type": "string"
}
},
"required": [
"essay_topic",
"concepts"
],
"type": "object"
}
Implementation Reference
- src/index.ts:184-285 (handler)The core handler function (execute) for the 'search_images_for_essay' tool. It destructures input args, defines an internal search helper, performs searches for the essay topic and each concept, compiles featured images and concept-specific images, and returns structured JSON results or error.execute: async (args) => { const { essay_topic, concepts = [], style = 'any', max_images = 10 } = args; const results = { topic: essay_topic, images_by_concept: {} as Record<string, any[]>, featured_images: [] as any[], total_images: 0 }; // Helper function to search images const searchImages = async (query: string, pageSize: number, filters: Record<string, string> = {}): Promise<{results: any[]}> => { const params: Record<string, string> = { q: query, page_size: String(pageSize), mature: 'false', ...filters }; if (style === 'photo') { params.extension = 'jpg,png'; } const queryParams = new URLSearchParams(params); try { const response = await fetch(`${OPENVERSE_API_BASE}/images/?${queryParams}`, { headers: { 'User-Agent': 'MCP-Openverse/1.0' } }); if (!response.ok) { return { results: [] }; } return await response.json() as {results: any[]}; } catch { return { results: [] }; } }; try { // Search for main topic const mainSearch = await searchImages(essay_topic, Math.min(5, max_images)); if (mainSearch.results) { results.featured_images = mainSearch.results.slice(0, 3).map((img: any) => ({ id: img.id, title: img.title || '', url: img.url, thumbnail: img.thumbnail || '', creator: img.creator || 'Unknown', license: img.license || '', attribution: img.attribution || '', source: img.source || '' })); results.total_images += results.featured_images.length; } // Search for each concept const imagesPerConcept = concepts.length ? Math.max(1, Math.floor(max_images / concepts.length)) : max_images; for (const concept of concepts) { if (results.total_images >= max_images) break; const conceptSearch = await searchImages( `${concept} ${essay_topic}`, imagesPerConcept ); if (conceptSearch.results && conceptSearch.results.length > 0) { results.images_by_concept[concept] = conceptSearch.results .slice(0, imagesPerConcept) .map((img: any) => ({ id: img.id, title: img.title || '', url: img.url, thumbnail: img.thumbnail || '', creator: img.creator || 'Unknown', license: img.license || '', attribution: img.attribution || '', source: img.source || '' })); results.total_images += results.images_by_concept[concept].length; } } return JSON.stringify(results, null, 2); } catch (error) { return JSON.stringify({ error: error instanceof Error ? error.message : 'Failed to search for essay images', topic: essay_topic }); } } });
- src/index.ts:39-44 (schema)Zod schema defining the input parameters for the search_images_for_essay tool, including essay_topic (required), concepts (array), style (enum), and max_images (optional).const essayImagesSchema = z.object({ essay_topic: z.string().describe('Main topic/title of the essay'), concepts: z.array(z.string()).describe('List of key concepts to find images for'), style: z.enum(['photo', 'illustration', 'any']).optional().describe('Preferred image style (default: any)'), max_images: z.number().optional().describe('Maximum images to return (default: 10)') });
- src/index.ts:179-285 (registration)The server.addTool call registering the 'search_images_for_essay' tool, specifying name, description, parameters schema, and inline execute handler.// Tool: search_images_for_essay server.addTool({ name: 'search_images_for_essay', description: 'Search for images suitable for illustrating an essay', parameters: essayImagesSchema, execute: async (args) => { const { essay_topic, concepts = [], style = 'any', max_images = 10 } = args; const results = { topic: essay_topic, images_by_concept: {} as Record<string, any[]>, featured_images: [] as any[], total_images: 0 }; // Helper function to search images const searchImages = async (query: string, pageSize: number, filters: Record<string, string> = {}): Promise<{results: any[]}> => { const params: Record<string, string> = { q: query, page_size: String(pageSize), mature: 'false', ...filters }; if (style === 'photo') { params.extension = 'jpg,png'; } const queryParams = new URLSearchParams(params); try { const response = await fetch(`${OPENVERSE_API_BASE}/images/?${queryParams}`, { headers: { 'User-Agent': 'MCP-Openverse/1.0' } }); if (!response.ok) { return { results: [] }; } return await response.json() as {results: any[]}; } catch { return { results: [] }; } }; try { // Search for main topic const mainSearch = await searchImages(essay_topic, Math.min(5, max_images)); if (mainSearch.results) { results.featured_images = mainSearch.results.slice(0, 3).map((img: any) => ({ id: img.id, title: img.title || '', url: img.url, thumbnail: img.thumbnail || '', creator: img.creator || 'Unknown', license: img.license || '', attribution: img.attribution || '', source: img.source || '' })); results.total_images += results.featured_images.length; } // Search for each concept const imagesPerConcept = concepts.length ? Math.max(1, Math.floor(max_images / concepts.length)) : max_images; for (const concept of concepts) { if (results.total_images >= max_images) break; const conceptSearch = await searchImages( `${concept} ${essay_topic}`, imagesPerConcept ); if (conceptSearch.results && conceptSearch.results.length > 0) { results.images_by_concept[concept] = conceptSearch.results .slice(0, imagesPerConcept) .map((img: any) => ({ id: img.id, title: img.title || '', url: img.url, thumbnail: img.thumbnail || '', creator: img.creator || 'Unknown', license: img.license || '', attribution: img.attribution || '', source: img.source || '' })); results.total_images += results.images_by_concept[concept].length; } } return JSON.stringify(results, null, 2); } catch (error) { return JSON.stringify({ error: error instanceof Error ? error.message : 'Failed to search for essay images', topic: essay_topic }); } } });
- src/index.ts:199-229 (helper)Internal helper function used within the handler to perform API searches on Openverse with query, page size, filters, and style-specific adjustments.// Helper function to search images const searchImages = async (query: string, pageSize: number, filters: Record<string, string> = {}): Promise<{results: any[]}> => { const params: Record<string, string> = { q: query, page_size: String(pageSize), mature: 'false', ...filters }; if (style === 'photo') { params.extension = 'jpg,png'; } const queryParams = new URLSearchParams(params); try { const response = await fetch(`${OPENVERSE_API_BASE}/images/?${queryParams}`, { headers: { 'User-Agent': 'MCP-Openverse/1.0' } }); if (!response.ok) { return { results: [] }; } return await response.json() as {results: any[]}; } catch { return { results: [] }; } };