Skip to main content
Glama
jau123

MeiGen AI Image Generation MCP

search_gallery

Read-only

Search AI image prompts using semantic understanding to find visually and conceptually similar results. Browse image URLs for inspiration and style exploration.

Instructions

Search AI image prompts with semantic understanding — finds visually and conceptually similar results, not just keyword matches. Results include image URLs — render them as markdown images () so users can visually browse and pick styles. Use when users need inspiration, want to explore styles, or say "generate an image" without a specific idea.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryNoSearch keywords (e.g., "cyberpunk", "product photo", "portrait"). Supports semantic search — natural language descriptions work well. Leave empty to browse by category or get random picks.
categoryNoFilter by category. Available: Photography, Illustration & 3D, Product & Brand, Food & Drink, Poster Design, UI & Graphic
limitNoNumber of results (1-20, default 5)
offsetNoPagination offset
sortByNoSort order when browsing without search query (default: rank)rank

Implementation Reference

  • The async handler function that executes the search_gallery tool logic. It tries semantic search via API first, then falls back to local keyword-based search in the curated prompt library. Handles three cases: random picks (no criteria), API semantic search (query without category), and local search (with category or as fallback).
    async ({ query, category, limit, offset, sortBy }) => {
      // No search criteria — return random picks from local library
      if (!query && !category && offset === 0) {
        const random = getRandomPrompts(limit)
        const stats = getLibraryStats()
        const header = `Curated Prompt Library: ${stats.total} trending prompts\nCategories: ${Object.entries(stats.categories).map(([k, v]) => `${k} (${v})`).join(', ')}\n\nHere are ${limit} random picks — show the preview images to the user:\n`
        return {
          content: [{
            type: 'text' as const,
            text: header + formatLocalResults(random),
          }],
        }
      }
    
      // Has query and no category filter → try semantic search via API
      if (query && query.trim() && !category) {
        const apiResults = await apiSearchPosts(config.meigenBaseUrl, query, limit, offset)
        if (apiResults && apiResults.length > 0) {
          const text = `Found ${apiResults.length} results for "${query}" (semantic search):\n\n${formatApiResults(apiResults)}\n\nShow the preview images above to the user so they can visually browse. Use get_inspiration(imageId) to get the full prompt and all images for any entry the user likes.`
          return {
            content: [{
              type: 'text' as const,
              text,
            }],
          }
        }
        // API failed or no results — fall through to local search
      }
    
      // Local search (keyword-based): with category filter, or as API fallback
      const results = searchPrompts({ query, category, limit, offset, sortBy })
    
      if (results.length === 0) {
        const suggestion = category
          ? `No results for "${query || ''}" in category "${category}". Try a different keyword or remove the category filter.`
          : `No results for "${query}". Try broader keywords like "portrait", "landscape", "product", "anime".`
        return {
          content: [{
            type: 'text' as const,
            text: suggestion,
          }],
        }
      }
    
      const searchDesc = [
        query ? `"${query}"` : null,
        category ? `category: ${category}` : null,
      ].filter(Boolean).join(', ')
    
      const text = `Found ${results.length} results${searchDesc ? ` for ${searchDesc}` : ''}:\n\n${formatLocalResults(results)}\n\nShow the preview images above to the user so they can visually browse. Use get_inspiration(imageId) to get the full prompt and all images for any entry the user likes.`
    
      return {
        content: [{
          type: 'text' as const,
          text,
        }],
      }
    }
  • Input schema for search_gallery tool using Zod. Defines fields: query (optional string), category (enum of 6 categories), limit (1-20, default 5), offset (min 0, default 0), sortBy (rank/likes/views/date, default rank).
    export const searchGallerySchema = {
      query: z.string().optional()
        .describe('Search keywords (e.g., "cyberpunk", "product photo", "portrait"). Supports semantic search — natural language descriptions work well. Leave empty to browse by category or get random picks.'),
      category: z.enum(['Photography', 'Illustration & 3D', 'Product & Brand', 'Food & Drink', 'Poster Design', 'UI & Graphic']).optional()
        .describe('Filter by category. Available: Photography, Illustration & 3D, Product & Brand, Food & Drink, Poster Design, UI & Graphic'),
      limit: z.number().min(1).max(20).optional().default(5)
        .describe('Number of results (1-20, default 5)'),
      offset: z.number().min(0).optional().default(0)
        .describe('Pagination offset'),
      sortBy: z.enum(['rank', 'likes', 'views', 'date']).optional().default('rank')
        .describe('Sort order when browsing without search query (default: rank)'),
    }
  • The registerSearchGallery function that registers the 'search_gallery' tool with the MCP server using server.tool(), including the schema, readOnlyHint, and the handler.
    export function registerSearchGallery(server: McpServer, config: MeiGenConfig) {
      server.tool(
        'search_gallery',
        'Search AI image prompts with semantic understanding — finds visually and conceptually similar results, not just keyword matches. Results include image URLs — render them as markdown images (![](url)) so users can visually browse and pick styles. Use when users need inspiration, want to explore styles, or say "generate an image" without a specific idea.',
        searchGallerySchema,
        { readOnlyHint: true },
        async ({ query, category, limit, offset, sortBy }) => {
          // No search criteria — return random picks from local library
          if (!query && !category && offset === 0) {
            const random = getRandomPrompts(limit)
            const stats = getLibraryStats()
            const header = `Curated Prompt Library: ${stats.total} trending prompts\nCategories: ${Object.entries(stats.categories).map(([k, v]) => `${k} (${v})`).join(', ')}\n\nHere are ${limit} random picks — show the preview images to the user:\n`
            return {
              content: [{
                type: 'text' as const,
                text: header + formatLocalResults(random),
              }],
            }
          }
    
          // Has query and no category filter → try semantic search via API
          if (query && query.trim() && !category) {
            const apiResults = await apiSearchPosts(config.meigenBaseUrl, query, limit, offset)
            if (apiResults && apiResults.length > 0) {
              const text = `Found ${apiResults.length} results for "${query}" (semantic search):\n\n${formatApiResults(apiResults)}\n\nShow the preview images above to the user so they can visually browse. Use get_inspiration(imageId) to get the full prompt and all images for any entry the user likes.`
              return {
                content: [{
                  type: 'text' as const,
                  text,
                }],
              }
            }
            // API failed or no results — fall through to local search
          }
    
          // Local search (keyword-based): with category filter, or as API fallback
          const results = searchPrompts({ query, category, limit, offset, sortBy })
    
          if (results.length === 0) {
            const suggestion = category
              ? `No results for "${query || ''}" in category "${category}". Try a different keyword or remove the category filter.`
              : `No results for "${query}". Try broader keywords like "portrait", "landscape", "product", "anime".`
            return {
              content: [{
                type: 'text' as const,
                text: suggestion,
              }],
            }
          }
    
          const searchDesc = [
            query ? `"${query}"` : null,
            category ? `category: ${category}` : null,
          ].filter(Boolean).join(', ')
    
          const text = `Found ${results.length} results${searchDesc ? ` for ${searchDesc}` : ''}:\n\n${formatLocalResults(results)}\n\nShow the preview images above to the user so they can visually browse. Use get_inspiration(imageId) to get the full prompt and all images for any entry the user likes.`
    
          return {
            content: [{
              type: 'text' as const,
              text,
            }],
          }
        }
      )
    }
  • src/server.ts:264-267 (registration)
    Registration call that wires registerSearchGallery into the server setup, alongside other free features (enhance_prompt, list_models, get_inspiration, manage_preferences).
    // Free features (no configuration required)
    registerEnhancePrompt(server)
    registerSearchGallery(server, config)
    registerListModels(server, apiClient, config)
  • The searchPrompts helper function that performs local keyword-based search and category filtering on the curated prompt library (data/trending-prompts.json). Used by the handler as a fallback when API search fails or is not applicable.
    export function searchPrompts(options: SearchOptions): TrendingPrompt[] {
      const prompts = loadPrompts()
      const { query, category, limit = 10, offset = 0, sortBy = 'rank' } = options
    
      let filtered = prompts
    
      // Category filter
      if (category) {
        const cat = category.toLowerCase()
        filtered = filtered.filter(p =>
          p.categories.some(c => c.toLowerCase() === cat)
        )
      }
    
      // Keyword search
      if (query && query.trim()) {
        const keywords = query.toLowerCase().split(/\s+/).filter(Boolean)
        filtered = filtered.map(p => {
          const searchText = [
            p.prompt,
            p.author_name,
            p.author,
            ...p.categories,
          ].join(' ').toLowerCase()
    
          // Calculate match score
          let score = 0
          for (const kw of keywords) {
            if (searchText.includes(kw)) {
              score += 1
              // Higher score for prompt text match
              if (p.prompt.toLowerCase().includes(kw)) score += 2
              // Higher score for category match
              if (p.categories.some(c => c.toLowerCase().includes(kw))) score += 3
            }
          }
          return { prompt: p, score }
        })
        .filter(r => r.score > 0)
        .sort((a, b) => b.score - a.score || a.prompt.rank - b.prompt.rank)
        .map(r => r.prompt)
      } else {
        // No keyword — sort by specified field
        filtered = [...filtered].sort((a, b) => {
          switch (sortBy) {
            case 'likes': return b.likes - a.likes
            case 'views': return b.views - a.views
            case 'date': return b.date.localeCompare(a.date)
            case 'rank':
            default: return a.rank - b.rank
          }
        })
      }
    
      return filtered.slice(offset, offset + limit)
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate readOnlyHint=true. The description adds behavioral context by mentioning that results include image URLs and should be rendered as markdown images, which helps the agent understand the expected interaction. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very concise at three sentences, all of which are meaningful. The main purpose is front-loaded, followed by usage guidance and display instructions. No unnecessary words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a search tool with no output schema, the description covers the main functionality, when to use, how to query (including empty query behavior), and how to display results. It lacks explicit pagination details, but given the schema's parameter descriptions, it is fairly complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds value by providing usage tips for the query parameter (e.g., natural language works well, leave empty to browse) and implies category usage. This exceeds the bare schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool performs semantic search for AI image prompts, distinguishing itself from keyword searches. It also specifies that results include image URLs and how to render them, making the purpose very clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use the tool: for inspiration, exploring styles, or when users say 'generate an image' without a specific idea. While it doesn't explicitly list alternatives, the context and sibling tool names allow the agent to infer when not to use it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jau123/mei-gen-ai-design-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server