Skip to main content
Glama
newerton

Mercado Livre MCP Server

MercadoLivreService.ts7.11 kB
import * as cheerio from 'cheerio'; import type { Element } from 'domhandler'; import voca from 'voca'; import { MercadoLivreCrawlerOutput } from '../../domain/models/MercadoLivreServiceModel.js'; import { wordsMatching } from '../../domain/utils/crawler/words-matching.js'; import { MercadoLivreApiService } from '../../infrastructure/services/MercadoLivreApiService.js'; export class MercadoLivreService { url: string | null = null; pageContent: cheerio.CheerioAPI | null = null; products: MercadoLivreCrawlerOutput[] = []; page: number = 1; constructor(private apiService: MercadoLivreApiService) {} async getProducts(products: string[]) { // const data = []; // for (const product of products) { this.page = 1; this.products = []; await this.getItems(products[0]); const lowestValueItem = this.getLowestValueItem(); // data.push(lowestValueItem); // } return lowestValueItem; } private getLowestValueItem = (): MercadoLivreCrawlerOutput[] => { const items: MercadoLivreCrawlerOutput[] = []; this.getFreeShipping(); for (const product of this.products) { items.push({ name: product.name, price: product.price, queryMatchRatio: product.queryMatchRatio, url: product.url, shippings: product.shippings, }); } items.sort((a, b) => a.price - b.price); return items; }; private getItems = async (product: string): Promise<void> => { const body = await this.request(product); if (body) { const hasItems = this.hasItems(); if (hasItems) { this.parseHTML(product); const hasPagination = this.hasPagination(); if (hasPagination) { await new Promise((resolve) => setTimeout(resolve, 2000)); await this.getItems(product); } } } }; private getFreeShipping = (): void => { const products = []; for (const product of this.products) { if (product.shippings && product.shippings.length > 0) { products.push(product); } } this.products = products; }; private hasItems = (): boolean => { const $ = this.pageContent; if (!$) { return false; } return $('.ui-search-layout__item').length > 0; }; private hasPagination = (): boolean => { const $ = this.pageContent; if (!$) { return false; } const buttonNext = $('[class="andes-pagination__button--next"]').length > 0; if (buttonNext) { this.page++; } if (this.page > 2) { return false; } return buttonNext; }; private request = async (query: string): Promise<any> => { const search = voca.slugify(query); const limit = 50; const page = this.page > 1 ? `_Desde_${limit * (this.page - 1) + 1}` : ''; const html = await this.apiService .getProduct({ product: search, page: Number(page), }) .then((response) => { if (response) { this.pageContent = cheerio.load(response); return response; } return null; }) .catch((error) => { console.error('Error fetching product:', error); return null; }); if (!html) { console.error('Error fetching product:', html); return null; } return html; }; private parseHTML = (product: string): void => { const $ = this.pageContent; if (!$) { console.error('Error parsing HTML: No content available'); return; } const uiSearchLayoutItem = this.layoutItems(); if (uiSearchLayoutItem) { uiSearchLayoutItem.each((_, element) => { const uiSearchItemTitle = $(element) .find('div[class="poly-card__content"] h3') .text(); const productOriginalName = voca(uiSearchItemTitle).trim().value(); const price = this.getPrice(element); const matching = wordsMatching({ originalName: productOriginalName, querySearchByFilter: product, price, debug: false, }); if (matching.queryMatchRatio >= 0.8) { if (price) { const productUrl = this.getUrl(element); const shippings = this.getShippings(element); this.products.push({ name: productOriginalName, price, queryMatchRatio: matching.queryMatchRatio, url: productUrl, shippings, }); } } }); } }; private layoutItems = () => { const $ = this.pageContent; if (!$) { return null; } const uiSearchLayoutItem = $('.ui-search-layout__item'); const hasItems = uiSearchLayoutItem.length > 0; if (hasItems) { return uiSearchLayoutItem; } return null; }; private getPrice = (element: Element): number | undefined => { const $ = this.pageContent; if (!$) { return undefined; } const productPrice = $(element) .find('div[class="poly-price__current"] span:eq(0)') .text(); if (productPrice === '') { return undefined; } return parseFloat( productPrice .replace('R$', '') .replace('.', '') .replace(',', '.') .replace(/\s/g, ''), ); }; private getUrl = (element: Element): string => { const $ = this.pageContent; if (!$) { return ''; } const productUrl = $(element) .find('a[class="poly-component__title"]') .attr('href'); if (!productUrl) { return ''; } return productUrl; }; private getShippings = ( element: Element, ): Array<{ name: string; price: number; deliveryDays: number; originalDescription: string; }> => { const $ = this.pageContent; if (!$) { return []; } const productShipping: { name: string; price: number; deliveryDays: number; originalDescription: string; }[] = []; const shippingFreeText = $(element) .find('div[class="poly-component__shipping"]') .text(); if (shippingFreeText) { productShipping.push({ name: 'Frete grátis', price: 0, deliveryDays: 0, originalDescription: shippingFreeText, }); } const shippingFullText = $(element) .find('span[class="poly-component__shipped-from"]') .text(); if (shippingFullText) { const isFull = $(element).find('span[class="poly-component__shipped-from"] svg') .length > 0; productShipping.push({ name: isFull ? 'Enviado pelo Full' : shippingFullText, price: 0, deliveryDays: 0, originalDescription: shippingFullText, }); } const shippingInternationalText = $(element) .find('div[class="poly-component__cbt"]') .text(); if (shippingInternationalText) { productShipping.push({ name: 'Compra Internacional', price: 0, deliveryDays: 0, originalDescription: shippingInternationalText, }); } return productShipping; }; }

Implementation Reference

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/newerton/mcp-mercado-livre'

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