Skip to main content
Glama
findmine

FindMine Shopping Stylist

Official
by findmine

get_complete_the_look

Generate personalized outfit recommendations for a specific product based on customer preferences, product details, and availability, enhancing the shopping experience with tailored styling suggestions.

Instructions

Get outfit recommendations for a product

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
api_versionNoAPI version to use (overrides FINDMINE_API_VERSION env var)
customer_genderNoCustomer gender (M = Men, W = Women, U = Unknown)
customer_idNoCustomer ID for personalized recommendations
in_stockNoWhether the product is in stock
on_saleNoWhether the product is on sale
product_color_idNoColor ID of the product (if applicable)
product_idYesID of the product
return_pdp_itemNoWhether to return the original product in the response
session_idNoSession ID for tracking and personalization

Implementation Reference

  • MCP tool handler for get_complete_the_look: validates input parameters, calls findMineService.getCompleteTheLook, processes the response by mapping looks and products to URIs, and returns structured JSON content in MCP format.
    case "get_complete_the_look": {
      try {
        const args = request.params.arguments as any;
        
        // Extract and validate required parameters
        const productId = args.product_id;
        if (!productId) {
          logger.error('Missing required parameter: product_id');
          return {
            error: {
              message: "Product ID is required",
              code: "VALIDATION_ERROR"
            }
          };
        }
        
        // Set defaults for optional parameters with explicit declaration
        const inStock = args.in_stock ?? true; // Using nullish coalescing
        const onSale = args.on_sale ?? false;
        const returnPdpItem = args.return_pdp_item ?? true;
        const colorId = args.product_color_id;
        const sessionId = args.session_id;
        const customerId = args.customer_id;
        // Don't pass gender by default - this parameter should be explicitly set only when needed
        const gender = args.customer_gender !== undefined ? args.customer_gender : undefined;
        const apiVersion = args.api_version;
        
        logger.error(`Getting complete the look for product ${productId}${colorId ? ` (color: ${colorId})` : ''}`);
        
        // Get complete the look recommendations
        const result = await findMineService.getCompleteTheLook(
          productId,
          inStock,
          onSale,
          {
            colorId,
            sessionId,
            customerId,
            returnPdpItem,
            gender,
            apiVersion,
          }
        );
    
        // Safety check for result.looks
        if (!result.looks || !Array.isArray(result.looks)) {
          return {
            error: {
              message: "Received invalid response from FindMine service: looks array missing",
              code: "INVALID_RESPONSE"
            }
          };
        }
          
        // Process looks data with null safety
        const lookItems = result.looks.map(look => {
          if (!look || !look.id) {
            return null; // Skip invalid looks
          }
          
          const lookUri = createLookUri(look.id);
          
          return {
            look_id: look.id,
            title: look.title || "",
            description: look.description || "",
            image_url: look.imageUrl || "",
            products: Array.isArray(look.productIds) ? look.productIds.map(pid => {
              if (!pid) return null;
              const product = findMineService.getProduct(pid);
              const productUri = product ? createProductUri(product.id, product.colorId) : null;
              
              return {
                product_id: pid,
                name: product?.name || "",
                uri: productUri,
              };
            }).filter(Boolean) : [], // Remove null products and handle missing productIds
            uri: lookUri,
          };
        }).filter(Boolean); // Remove null looks
    
        // Include the product if requested
        let productInfo = null;
        if (result.product) {
          const productUri = createProductUri(result.product.id, result.product.colorId);
          
          productInfo = {
            product_id: result.product.id,
            name: result.product.name,
            uri: productUri,
          };
        }
    
        // Return success response with structured data
        return {
          success: true,
          content: [{
            type: "text",
            text: JSON.stringify({
              product: productInfo,
              looks: lookItems,
              total_looks: lookItems.length,
            }, null, 2)
          }]
        };
      } catch (error) {
        logger.error(`Error getting complete the look: ${error instanceof Error ? error.message : String(error)}`, error);
        return {
          error: {
            message: error instanceof Error ? error.message : 'Unknown error occurred while getting complete the look',
            code: 'COMPLETE_THE_LOOK_ERROR'
          }
        };
      }
    }
  • Input schema and description for the get_complete_the_look tool, returned by ListToolsRequestSchema handler.
    {
      name: "get_complete_the_look",
      description: "Get outfit recommendations for a product",
      inputSchema: {
        type: "object",
        properties: {
          product_id: {
            type: "string",
            description: "ID of the product"
          },
          product_color_id: {
            type: "string",
            description: "Color ID of the product (if applicable)"
          },
          in_stock: {
            type: "boolean",
            description: "Whether the product is in stock",
            default: true
          },
          on_sale: {
            type: "boolean",
            description: "Whether the product is on sale",
            default: false
          },
          customer_id: {
            type: "string",
            description: "Customer ID for personalized recommendations"
          },
          customer_gender: {
            type: "string",
            enum: ["M", "W", "U"],
            description: "Customer gender (M = Men, W = Women, U = Unknown)"
          },
          return_pdp_item: {
            type: "boolean",
            description: "Whether to return the original product in the response",
            default: true
          },
          session_id: {
            type: "string",
            description: "Session ID for tracking and personalization"
          },
          api_version: {
            type: "string",
            description: "API version to use (overrides FINDMINE_API_VERSION env var)"
          }
        },
        required: ["product_id"]
      }
    },
  • FindMineService.getCompleteTheLook: Implements caching, calls the API client, maps API responses to ProductResource and LookResource objects, and stores them in the resource store.
    async getCompleteTheLook(
      productId: string,
      inStock: boolean,
      onSale: boolean,
      options: {
        colorId?: string;
        sessionId?: string;
        customerId?: string;
        returnPdpItem?: boolean;
        gender?: 'M' | 'W' | 'U';
        useCache?: boolean;
        apiVersion?: string;
      } = {}
    ): Promise<{
      product?: ProductResource;
      looks: LookResource[];
    }> {
      const sessionId = options.sessionId || config.session.defaultSessionId;
      const useCache = options.useCache !== false && config.cache.enabled;
      
      // Create cache key
      const cacheKey = Cache.createKey([
        'completeTheLook',
        productId,
        options.colorId || 'default',
        String(inStock),
        String(onSale),
        String(options.returnPdpItem)
      ]);
    
      // Try to get from cache
      let response: CompleteTheLookResponse;
      if (useCache) {
        const cached = this.completeTheLookCache.get(cacheKey);
        if (cached) {
          response = cached;
        } else {
          response = await this.client.getCompleteTheLook(
            productId,
            inStock,
            onSale,
            sessionId,
            {
              colorId: options.colorId,
              customerId: options.customerId,
              returnPdpItem: options.returnPdpItem,
              gender: options.gender,
              apiVersion: options.apiVersion,
            }
          );
          
          // Store in cache
          this.completeTheLookCache.set(cacheKey, response);
        }
      } else {
        response = await this.client.getCompleteTheLook(
          productId,
          inStock,
          onSale,
          sessionId,
          {
            colorId: options.colorId,
            customerId: options.customerId,
            returnPdpItem: options.returnPdpItem,
            gender: options.gender,
            apiVersion: options.apiVersion,
          }
        );
      }
    
      // Convert to internal resources
      let product: ProductResource | undefined;
      if (response.pdp_item) {
        product = mapProductToResource(response.pdp_item);
        this.resources.products[product.id] = product;
      }
    
      const looks: LookResource[] = [];
      for (const look of response.looks) {
        try {
          const lookResource = mapLookToResource(look);
          this.resources.looks[lookResource.id] = lookResource;
          looks.push(lookResource);
    
          // Store all products from the look - handle different API formats
          const products = look.products || look.items || [];
          if (Array.isArray(products)) {
            for (const product of products) {
              try {
                if (product && product.product_id) {
                  const productResource = mapProductToResource(product);
                  this.resources.products[productResource.id] = productResource;
                }
              } catch (productError) {
                if (process.env.FINDMINE_DEBUG === 'true') {
                  console.error(`[FindMineService] Error mapping product in look ${lookResource.id}:`, productError);
                }
                // Continue with next product even if one fails
              }
            }
          }
        } catch (lookError) {
          if (process.env.FINDMINE_DEBUG === 'true') {
            console.error(`[FindMineService] Error mapping look:`, lookError);
          }
          // Continue with next look even if one fails
        }
      }
    
      return {
        product,
        looks,
      };
    }
  • FindMineClient.getCompleteTheLook: Constructs the API request parameters and makes HTTP GET request to the FindMine complete-the-look endpoint with retry logic.
    async getCompleteTheLook(
      productId: string,
      inStock: boolean,
      onSale: boolean,
      sessionId: string,
      options: {
        colorId?: string;
        customerId?: string;
        returnPdpItem?: boolean;
        gender?: 'M' | 'W' | 'U';
        apiVersion?: string;
      } = {}
    ): Promise<CompleteTheLookResponse> {
      const apiVersion = options.apiVersion || this.config.apiVersion;
      // Create request parameters, only including gender if explicitly provided
      const params: CompleteTheLookRequest = {
        ...this.createBaseRequest(sessionId, options.customerId),
        product_id: productId,
        product_color_id: options.colorId,
        product_in_stock: inStock,
        product_on_sale: onSale,
        return_pdp_item: options.returnPdpItem,
      };
      
      // Only add gender param if explicitly provided
      if (options.gender !== undefined) {
        params.customer_gender = options.gender;
      }
    
      return this.makeRequest<CompleteTheLookResponse>(
        `/api/${apiVersion}/complete-the-look`,
        'GET',
        params
      );
    }
Install Server

Other Tools

Related 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/findmine/findmine-mcp'

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