Skip to main content
Glama
rogertheunissenmerge-oss

SommelierX Wine Pairing MCP

find_meals_for_wine

Input a wine type (e.g., Barolo, Chardonnay) and receive the top 10 matching dishes with pairing scores. Ideal when you have a wine and want to know what to cook.

Instructions

Find dishes that pair well with a specific wine type or style. Provide a wine name (e.g. "Barolo", "Chardonnay", "Rioja") and get the top 10 matching dishes with scores. Best for: "I have a Barolo, what should I cook?" | Auth: API key (Bearer sk_live_...) or x402 payment (USDC on Base) | Price: $0.01/call

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
wine_typeYesName of the wine type or style (e.g. "Barolo", "Chardonnay", "Rioja Reserva")
languageNoLanguage code for results (e.g. "en", "nl", "fr"). Defaults to "en".

Implementation Reference

  • Main execution function for the find_meals_for_wine tool. Takes client, config, and validated input; finds a wine type by name, then fetches meal pairings from /api/v1/pairing/by-wine/:id/meals, and formats the result.
    export async function executeFindMealsForWine(
      client: SommelierXClient,
      config: ServerConfig,
      input: FindMealsForWineInput,
    ): Promise<string> {
      const language = input.language ?? config.defaultLanguage;
    
      // Step 1: Find the wine type
      const wineType = await findWineType(client, input.wine_type, language);
    
      if (!wineType) {
        return [
          `Could not find a wine type matching "${input.wine_type}" in the SommelierX database.`,
          '',
          'Try using a more specific wine style name like "Barolo", "Chablis", or "Rioja Reserva".',
          'Common wine types include: Chardonnay, Sauvignon Blanc, Pinot Noir, Cabernet Sauvignon,',
          'Merlot, Syrah, Riesling, Champagne, Prosecco, and many regional styles.',
        ].join('\n');
      }
    
      // Step 2: Get meal recommendations
      let mealsResult: MealsByWineResult;
      try {
        mealsResult = await client.get<MealsByWineResult>(
          `/api/v1/pairing/by-wine/${wineType.id}/meals`,
          { language, perPage: '10' },
        );
      } catch (error: unknown) {
        const message = error instanceof Error ? error.message : 'Unknown error';
        return `Error finding meals for "${wineType.name}": ${message}`;
      }
    
      return formatMealsForWineResponse(wineType, mealsResult.data);
    }
  • Zod schema defining input for find_meals_for_wine: wine_type (required string, 2-200 chars) and language (optional string, 2-10 chars).
    export const findMealsForWineSchema = z.object({
      wine_type: z
        .string()
        .min(2, 'Wine type must be at least 2 characters')
        .max(200)
        .describe('Name of the wine type or style (e.g. "Barolo", "Chardonnay", "Rioja Reserva")'),
      language: z
        .string()
        .min(2)
        .max(10)
        .optional()
        .describe('Language code for results (e.g. "en", "nl", "fr"). Defaults to "en".'),
    });
    
    export type FindMealsForWineInput = z.infer<typeof findMealsForWineSchema>;
  • src/index.ts:97-106 (registration)
    Registration of the find_meals_for_wine tool with the MCP server, including description, schema shape, and handler lambda.
    server.tool(
      'find_meals_for_wine',
      'Find dishes that pair well with a specific wine type or style. Provide a wine name (e.g. "Barolo", "Chardonnay", "Rioja") and get the top 10 matching dishes with scores. Best for: "I have a Barolo, what should I cook?" | Auth: API key (Bearer sk_live_...) or x402 payment (USDC on Base) | Price: $0.01/call',
      findMealsForWineSchema.shape,
      async (input) => {
        const parsed = findMealsForWineSchema.parse(input);
        const result = await executeFindMealsForWine(client, config, parsed);
        return { content: [{ type: 'text' as const, text: result }] };
      },
    );
  • Helper function that fetches all wine types from /api/v1/wines/types and performs case-insensitive matching (exact, starts-with, contains, reverse) to find the best matching wine type item.
    async function findWineType(
      client: SommelierXClient,
      query: string,
      language: string,
    ): Promise<WineTypeItem | null> {
      try {
        const response = await client.get<WineTypesResponse>('/api/v1/wines/types', { language });
    
        // Normalize response: client may return array or object with .data
        let wineTypes: WineTypeItem[];
        if (Array.isArray(response)) {
          wineTypes = response;
        } else if ('data' in response && Array.isArray(response.data)) {
          wineTypes = response.data;
        } else {
          return null;
        }
    
        const queryLower = query.toLowerCase();
    
        // Try exact match first
        const exact = wineTypes.find((w) => w.name.toLowerCase() === queryLower);
        if (exact) return exact;
    
        // Try starts-with match
        const startsWith = wineTypes.find((w) => w.name.toLowerCase().startsWith(queryLower));
        if (startsWith) return startsWith;
    
        // Try substring match
        const contains = wineTypes.find((w) => w.name.toLowerCase().includes(queryLower));
        if (contains) return contains;
    
        // Try reverse: query contains wine name
        const reverseMatch = wineTypes.find((w) => queryLower.includes(w.name.toLowerCase()));
        if (reverseMatch) return reverseMatch;
    
        return null;
      } catch {
        return null;
      }
    }
  • Helper function that formats the meal recommendations into a human-readable string, showing rank, name, match percentage, and optional description.
    function formatMealsForWineResponse(
      wineType: WineTypeItem,
      meals: MealMatch[],
    ): string {
      const lines: string[] = [];
    
      const colorSuffix = wineType.color ? ` (${wineType.color})` : '';
      lines.push(`Dishes that pair well with ${wineType.name}${colorSuffix}:`);
      lines.push('');
    
      if (meals.length === 0) {
        lines.push('No meal recommendations found for this wine type.');
        return lines.join('\n');
      }
    
      for (let i = 0; i < meals.length; i++) {
        const meal = meals[i];
        const rank = i + 1;
    
        lines.push(`${rank}. ${meal.name}`);
        lines.push(`   Match: ${meal.score.match_percentage}%`);
    
        if (meal.description) {
          lines.push(`   ${meal.description}`);
        }
    
        lines.push('');
      }
    
      return lines.join('\n');
    }
Behavior4/5

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

Discloses auth methods (API key or USDC payment) and pricing ($0.01/call). As a read operation with no annotations, description provides useful behavioral context beyond schema.

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?

Very concise: two sentences plus auth/pricing line. Front-loaded with purpose, no redundant phrases. Every sentence adds value.

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?

Complete for a simple two-parameter tool with no output schema: explains input, output, auth, and pricing. Sibling names provide additional context.

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

Parameters3/5

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

Schema has 100% coverage with descriptions. Description adds minor context (examples, default language) but core info is already in schema. Baseline 3 due to high schema coverage.

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?

Description clearly states it finds dishes pairing with a specific wine, with examples ('Barolo', 'Chardonnay') and output details (top 10 dishes with scores). Differentiates from siblings like 'pair_wine_with_ingredients' by focusing on dishes.

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?

Includes a 'Best for' example (`I have a Barolo, what should I cook?`) indicating when to use. No explicit alternatives or when-not statements, but context implies it's for dish matching, not ingredient or meal pairing.

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/rogertheunissenmerge-oss/mcp-server'

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