latam-market

latam-market

Enables querying item prices, offers, and market analysis for the LATAM Ragnarok Online server (FREYA) via an MCP interface.

Category
Visit Server

README

latam-market

API e servidor MCP para o mercado de jogadores do Ragnarok Online LATAM (FREYA e NIDHOGG). Os mesmos dados por dois canais: HTTP para aplicações, MCP para agentes de IA.

🔗 https://mercado.latam-tools.com.br

Interface  →  https://mercado.latam-tools.com.br
API REST   →  https://mercado.latam-tools.com.br/api/v1
MCP        →  https://mercado.latam-tools.com.br/mcp

Sem cadastro, sem chave de API, sem login. Licença MIT.

O que dá para perguntar

Conectado a um agente (Claude, por exemplo), o MCP responde coisas assim:

"Quanto custa um Elixir Dourado?" "Quero comprar uma Boina — me lista quem está vendendo e por quanto." "Vender minha Escama Invertida por 500 mil é bom negócio?" "O preço da Ração Luxuosa subiu esse mês?" "Tem alguma pechincha no mercado agora?" "Toma meu replay: quanto vale tudo que eu tenho? O que dá para vender com lucro?"

Recursos

  • Preço de qualquer item — a faixa histórica publicada pelo site e o resumo das lojas abertas na última coleta, que são coisas diferentes e vêm separadas.
  • Ofertas ativas — quem está vendendo, por quanto, em que loja e em que mapa.
  • Histórico próprio — construído acumulando nossas coletas, com retenção em camadas (anúncio cru por um dia, resumo por 30 dias, resumo diário para sempre).
  • Avaliação de preço — quantas lojas estão mais baratas, quanto pedir para ser o mais barato, e como o preço se compara à média dos últimos dias.
  • Pechinchas e maiores variações — varreduras sobre o mercado inteiro.
  • Leitura de replay .rrf — inventário, carrinho de mercador e equipamento, com preço de mercado em cada item.
  • Consulta ao vivo — quando o dado coletado não basta, consulta o site na hora. É o caminho caro, e está marcado como tal.
  • Links prontos — todo item vem com a página no Divine Pride e a busca ao vivo no site oficial.

Interface web

Em https://mercado.latam-tools.com.br dá para usar tudo isso sem escrever uma linha de comando: sobe o .rrf, e a tabela mostra item por item o que ele vale, com ícone, descrição no hover, filtro por origem (mochila, carrinho, equipado), botão para esconder os intransferíveis, exportação em CSV e links para o Divine Pride e para o mercado oficial. Tem também busca, pechinchas, maiores variações e o estado das coletas.

A interface é servida pelo mesmo domínio da API — arquivos estáticos pelo Caddy, com /api/*, /mcp e /healthz indo para o serviço Node. Código e instruções de desenvolvimento em web/.

Usando a API

Toda rota aceita ?server=FREYA|NIDHOGG. Sem o parâmetro, responde FREYA; um valor desconhecido é 400, e não o padrão em silêncio.

# buscar um item
curl 'https://mercado.latam-tools.com.br/api/v1/items?q=elixir'

# o mesmo item no outro servidor
curl 'https://mercado.latam-tools.com.br/api/v1/items/501?server=NIDHOGG'

# preço (aceita id ou nome)
curl 'https://mercado.latam-tools.com.br/api/v1/items/Elixir%20Dourado'

# quem está vendendo
curl 'https://mercado.latam-tools.com.br/api/v1/items/1100005/offers?limit=10'

# vale a pena vender a 2600?
curl 'https://mercado.latam-tools.com.br/api/v1/items/1100005/appraise?price=2600'

# avaliar um replay
curl -X POST --data-binary @replay.rrf \
  -H 'content-type: application/octet-stream' \
  https://mercado.latam-tools.com.br/api/v1/replay
Rota O que faz
GET /api/v1/items?q= Busca por nome (sem acento, sem caixa)
GET /api/v1/items/:item Preço, ofertas e resumo. :item é id ou nome
GET /api/v1/items/:item/offers Lojas vendendo agora, da mais barata para a mais cara
GET /api/v1/items/:item/history?days=30 Série histórica
GET /api/v1/items/:item/appraise?price=N Avaliação de um preço
GET /api/v1/movers?days=7&dir=up Maiores variações
GET /api/v1/deals?min_discount=25 Itens bem abaixo do usual
GET /api/v1/snapshots Coletas recentes
GET /api/v1/status Idade dos dados
POST /api/v1/replay Envie o .rrf no corpo; devolve os itens precificados
POST /api/v1/live/items/:item/refresh Consulta ao vivo (5/min por IP)
GET /healthz Saúde do serviço

Quando um nome casa com vários itens, a resposta é 300 com a lista de candidatos — o serviço não escolhe por você.

Conectando o MCP

Em clientes com suporte a MCP remoto (Claude Desktop, Claude Code):

{
  "mcpServers": {
    "mercado-ro": {
      "type": "http",
      "url": "https://mercado.latam-tools.com.br/mcp"
    }
  }
}
Ferramenta Para quê
search_items Achar o id de um item pelo nome
get_price Quanto custa
list_offers Quem está vendendo
price_history Como o preço se comportou
appraise_price Se um preço é bom
top_movers O que subiu ou caiu
find_deals Pechinchas
value_inventory Precificar um replay .rrf (envie em base64)
data_status De quando são os dados
refresh_live Consultar o site ao vivo — caro, só sob pedido explícito

⚠️ refresh_live gasta uma requisição ao site e pode ser bloqueada por limite de taxa. As descrições das ferramentas dizem isso ao agente, para ele preferir os dados já coletados — que costumam ter menos de uma hora.

Como funciona

                      ┌──────────────────────────────────┐
   Caddy ──── :8788 ──│  API REST  ─┐                    │
                      │             ├─→  core/  ─→ cache │
                      │  MCP       ─┘         ↑          │
                      └───────────────────────│──────────┘
                                              │
                          SQLite  ←──── worker do crawl
                                              │
                                    EgressPool (lanes)
                                              │
                                     ro.gnjoylatam.com

Um processo só. api/ e mcp/ são casca fina sobre core/ e não podem ler o banco direto — é isso que garante que os dois canais respondam a mesma coisa, e há um teste de paridade que quebra se alguém contornar.

O mercado inteiro cabe na memória (~5 mil itens com preço, ~20 mil anúncios), então nenhuma leitura toca o disco. O SQLite existe para o histórico e para sobreviver a reinício.

Coleta e rotação de IP

O site limita a ~50 requisições por minuto por IP e por endpoint, e o bloqueio dura mais de cinco minutos. A saída é ter várias identidades de rede.

Cada "lane" é uma conta Cloudflare WARP anônima dentro da sua própria network namespace do Linux. Isso importa porque o servidor é compartilhado: um warp-cli connect trocaria a rota da máquina inteira e derrubaria as conexões dos outros serviços a cada rotação. Dentro da namespace, a rota do host nunca é tocada.

Duas outras decisões que caem daí:

  • A rotação é fora-de-lugar. A saída nova é criada e publicada antes de a antiga ser encerrada, e o encerramento espera as requisições em andamento terminarem. Trocar no lugar mataria justamente as requisições que provocaram o bloqueio.
  • A saída direta é reservada para consultas ao vivo. O crawl roda só nas lanes de túnel, então um bloqueio na coleta nunca deixa um usuário esperando — e vice-versa.

Detalhes de medição e de operação do coletor estão em docs/scraper.md.

Rodando localmente

Precisa de Node 22.5+ (por causa do node:sqlite) e pnpm.

pnpm install
pnpm import --run-id full   # carrega os NDJSON já coletados no SQLite
pnpm dev                    # sobe em http://127.0.0.1:8788

Sem dados em data/raw/, o serviço sobe com o mercado vazio — colete primeiro com pnpm crawl (veja docs/scraper.md).

Comando O que faz
pnpm dev Servidor com recarga automática
pnpm crawl Coleta manual do mercado
pnpm import Importa NDJSON para o SQLite
pnpm report Relatório de cobertura do coletor
pnpm test Testes
pnpm typecheck Tipos
pnpm build Bundle de produção em dist/
pnpm sync:items Atualiza o catálogo a partir do latam-ro-calc

Variáveis úteis: PORT, DB_PATH, DATA_DIR, EGRESS_LANES, CRAWL_ENABLED, ALLOWED_HOSTS, ALLOWED_ORIGINS (veja src/server/config.ts).

Estrutura

src/
  scraper/   coletor: cobertura de termos, cliente HTTP, extração do payload RSC
  egress/    pool de saídas de rede, lanes e rotação de IP
  store/     SQLite, cache quente e retenção
  replay/    leitura de arquivos .rrf (inventário, carrinho, equipamento)
  core/      a lógica de mercado — a única camada que API e MCP enxergam
  api/       rotas REST
  mcp/       ferramentas MCP
  server/    processo HTTP
  worker/    coleta periódica em worker thread
  cli/       comandos de linha
web/         interface web (React + Vite), servida estática pelo Caddy
infra/       systemd, Caddy e provisionamento das lanes
docs/        documentação do coletor

Deploy

São dois fluxos, um por push na main.

Serviço (deploy.yml, sem filtro de caminho): typecheck, testes, bundle com esbuild, envio por scp para o EC2, rsync em /opt/latam-market e reinício do systemd. A verificação bate no /healthz, faz uma chamada MCP real e uma consulta REST — se qualquer uma falhar, o deploy falha.

Interface (web-deploy.yml, disparado por web/** e pelo catálogo): typecheck, testes, build do Vite e rsync em /opt/latam-market-web. Não reinicia nem recarrega nada — arquivo estático não é configuração. A verificação confere que a interface subiu e que a API, o MCP e o /healthz continuam respondendo, porque os dois dividem o mesmo bloco do Caddy.

O banco fica em /var/lib/latam-market/market.db, fora de /opt, porque o deploy usa rsync --delete e levaria o histórico junto.

Arquivos de infraestrutura em infra/, com o provisionamento manual necessário documentado no topo de cada um.

Limites conhecidos

  • Só o lado de quem está vendendo (storeType=BUY; SELL não retorna nada nesse site). FREYA e NIDHOGG são coletados na mesma cadência.
  • O histórico de NIDHOGG começa agora. FREYA acumula desde o início do projeto; o outro servidor entrou depois, então pechinchas e variações só ficam úteis lá quando houver alguns dias de coleta.
  • Refino, cartas e bônus aleatórios não são precificados. O mercado agrega por id de item, então uma arma +9 encantada aparece com o preço da arma base — as respostas avisam quando é o caso, em vez de fingir precisão.
  • O histórico começa quando começamos a coletar. O site não publica série temporal; o que ele dá é um agregado acumulado, e o resto é medição nossa.
  • O retrato de um replay é do início da gravação. Itens pegos ou gastos durante a gravação não aparecem.
  • Alguns containers de item do .rrf ainda não foram identificados (provavelmente armazém da Kafra). Eles vêm à parte, em unidentified, e não entram no total.

Créditos

Catálogo de itens em pt-BR do projeto irmão latam-ro-calc (simulador de dano). A leitura de .rrf é portada de lá, que por sua vez segue o Rrf-Parser do Tokeiburu.

Projeto não-oficial, feito para a comunidade. Sem vínculo com a Gravity ou a Gnjoy.

Licença

MIT.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured