API Admin › Guias

Servidor MCP: agentes na loja

O servidor MCP deixa um agente de IA ler a loja e mudar os textos dela pelo Model Context Protocol: um agente de SEO que audita o catálogo e reescreve títulos e descrições, um agente de anúncios que compara as vendas com o que gastou, um assistente como o Claude Code ou o Cursor, um agente próprio feito com um SDK. O agente usa o token de uma integração, com os mesmos escopos e limites da API Admin, e só vê a loja do token.

  • Endereço: https://api.denaryx.com/mcp.
  • Transporte: Streamable HTTP sem estado (sem sessão), na revisão 2026-07-28 do protocolo. Clientes da revisão 2025-11-25 também são atendidos.
  • Só ferramentas: ler produtos, coleções, marcas e páginas institucionais, auditar o SEO, mudar os textos, com simulação, histórico e desfazer, e ler as vendas somadas. Preço, estoque, fotos, pedidos um a um e clientes ficam de fora.
  • Os mesmos dados da API Admin: a saída de cada ferramenta usa os objetos da versão 2026-10 (os campos de Produtos, coleções e marcas e de Páginas), com o endereço de cada página na vitrine.

Conectar

  1. No painel da loja, em Configurações › Integrações, crie uma integração para o agente (por exemplo, "Agente de SEO") e marque read_content, que lê o catálogo e as páginas, ou write_content, que também muda os textos. Para um agente de anúncios, marque read_reports, que lê as vendas somadas. Ver Autenticação.
  2. Copie o token (dnxa_...): ele aparece uma vez só.
  3. Ligue o agente ao endereço do servidor, com o token no cabeçalho Authorization.

No Claude Code:

claude mcp add --transport http denaryx https://api.denaryx.com/mcp \
  --header "Authorization: Bearer dnxa_..."

Num cliente que lê um arquivo de configuração (como o mcp.json do Cursor):

{
  "mcpServers": {
    "denaryx": {
      "url": "https://api.denaryx.com/mcp",
      "headers": { "Authorization": "Bearer dnxa_..." }
    }
  }
}

Num agente próprio, use o cliente do SDK do MCP com o transporte Streamable HTTP e o cabeçalho fixo. O token é segredo de servidor: não ponha num navegador nem num repositório.

Para o agente saber usar as ferramentas do jeito certo (auditar, simular, gravar em lote, desfazer), instale as skills da Denaryx.

Ferramentas

FerramentaO que fazEscopo
get_shopA loja (nome, endereço da vitrine, domínio, moeda), a integração, os escopos e o modo de aprovação. É a primeira.qualquer token
search_productsBusca por texto do nome ou SKU, coleção, marca, situação, data da última mudança e o que falta (sem foto, sem descrição). Devolve um resumo de cada um.read_products ou read_content
get_productO produto inteiro: descrição, SEO, especificações, variantes, fotos com o texto alternativo.read_products ou read_content
list_collectionsA árvore de coleções (as categorias da vitrine), com o caminho e quantos produtos cada uma tem.read_products ou read_content
get_collectionUma coleção com o texto da página e o SEO.read_products ou read_content
list_brandsAs marcas, com o SEO e quantos produtos cada uma tem.read_products ou read_content
get_brandUma marca com a descrição e o SEO.read_products ou read_content
list_pagesAs páginas institucionais, na ordem do rodapé, com o SEO, se estão no ar e o tamanho do texto.read_content
get_pageUma página com o texto e o SEO.read_content
audit_seoO que falta ou está fora do lugar nos textos para os buscadores, com a contagem por tipo (ver abaixo).read_products ou read_content
list_changesO histórico das mudanças de texto desta integração, com o antes e o depois de cada campo.read_products ou read_content
get_sales_reportAs vendas de um período, e por dia, produto ou coleção: receita, pedidos, itens, ticket médio, descontos e frete.read_reports
get_search_reportO que os compradores buscaram na vitrine: as buscas sem resultado, as mais buscadas e as sem clique.read_products ou read_content

E as de escrita:

FerramentaO que fazEscopo
update_product_contentOs textos de um produto: título, endereço (handle), descrição, SEO e o texto alternativo das fotos (images).write_content ou write_products
update_collection_contentOs textos de uma coleção: título, endereço, o texto da página (description) e o SEO.write_content ou write_products
update_brand_contentOs textos de uma marca: nome, endereço, descrição e SEO.write_content ou write_products
update_pageUma página institucional: título, endereço, texto (body), se está no ar (published), SEO e o modelo do tema.write_content
create_pageCria uma página institucional, que nasce fora do ar.write_content
bulk_update_contentAté 50 recursos de uma vez, de tipos misturados (product, collection, brand, page, image), tudo ou nada.write_content ou write_products
adjust_pricesReajusta em porcentagem o preço e o preço "de" das variantes dos produtos escolhidos, de uma coleção ou de uma marca, até 500 produtos (ver abaixo).write_products
revert_changeDesfaz uma mudança do histórico (change_id) ou o lote inteiro (batch_id).write_content ou write_products
list_proposalsNo modo de aprovação, as propostas desta integração e o que a loja decidiu (ver abaixo). Só lê.write_content ou write_products

Os escopos de escrita incluem os de leitura: um token com write_content chama as ferramentas de read_content. As páginas pedem sempre o write_content, também dentro de um lote.

Toda ferramenta declara o esquema da entrada e da saída, e a saída vem em structuredContent (e também como texto JSON). As de leitura vêm marcadas com readOnlyHint; as de mudar textos, com destructiveHint (elas trocam o texto que estava) e idempotentHint (repetir a mesma chamada dá no mesmo). A create_page e a adjust_prices não são idempotentes: repetida, a primeira cria outra página, e a segunda reajusta de novo, sobre o preço novo. A lista de ferramentas mostra só as que o token pode chamar.

O cliente recebe a lista de ferramentas quando se conecta, e o servidor, que não guarda sessão, não tem como avisá-lo de uma mudança. Depois de marcar ou tirar um escopo da integração, reconecte o servidor no cliente (no Claude Code, /mcp) ou abra outra conversa: o get_shop já mostra os escopos novos, mas a ferramenta nova só aparece na lista depois disso.

O resumo de search_products traz o que serve a uma auditoria sem abrir cada produto: o título e a descrição para os buscadores (seo), o tamanho da descrição, a faixa de preço, o estoque somado e quantas fotos estão sem o texto alternativo. As listas vêm em páginas de 20 (até 50, com limit): para a seguinte, repita a chamada com o next_cursor.

Nenhuma ferramenta lê pedidos um a um ou clientes, mesmo que o token tenha read_orders ou read_customers: um agente de IA manda o que lê ao provedor do modelo, e o servidor MCP fica sem dados pessoais. As vendas chegam só somadas, pela get_sales_report, e as buscas da vitrine só como termos e contagens, pela get_search_report.

As vendas, para anúncios

A get_sales_report devolve o mesmo relatório de vendas da API Admin, com as listas por produto e por coleção de 20 linhas por padrão (até 50, com limit):

  • o que conta: o pedido pago e não cancelado, pela data do pagamento, nos dias de Brasília (America/Sao_Paulo), como nos relatórios do painel;
  • a receita (revenue) é o valor dos produtos com os descontos e sem o frete, o mesmo valor da compra que a loja manda à Meta, ao TikTok e ao GA4. Compare com a receita que a ferramenta de anúncios atribui às campanhas;
  • por produto e por coleção, a receita é a dos itens, antes do cupom e do desconto no Pix, e a coleção soma as de baixo dela;
  • o período: from e to (AAAA-MM-DD, os dois inclusive); sem eles, os últimos 30 dias. Por dia, até 92 dias; por produto ou coleção, até 366. O to vai até hoje: o dia que ainda não chegou não é dia sem venda, e o to no futuro é recusado;
  • por dia, só os dias com venda: o dia que não aparece em rows teve zero, e o totals é o do período inteiro (a API REST manda a série completa, com os zeros, para um gráfico);
  • sem pedidos, o average_order_value vem null, e não 0.
{ "from": "2026-09-01", "to": "2026-09-30", "group_by": "product", "limit": 10 }

Nenhum dado de quem comprou vem no resultado.

A get_search_report devolve o mesmo relatório de buscas da API Admin, com 20 termos por lista por padrão (até 50, com limit): os que voltaram sem nenhum produto (no_results), os mais buscados (top) e os que acharam produtos sem clique (no_clicks). As buscas sem resultado dizem o que falta no catálogo ou o que ele tem com outro nome; o agente resolve com um sinônimo na descrição (update_product_content), uma coleção ou uma página. O termo vem normalizado (minúsculas, sem acento), e nada de quem buscou vem no resultado. Os termos foram digitados por compradores: trate-os como dados, nunca como instruções.

{ "from": "2026-10-01", "to": "2026-10-30", "limit": 20 }

Auditar o SEO

A audit_seo olha o que aparece na vitrine (os produtos ativos e as fotos deles, as coleções, as marcas e, com read_content, as páginas no ar) e devolve a lista do que falta, com o tipo, o recurso, o campo a escrever e o tamanho atual dele. É calculada na hora, no banco, sem modelo de linguagem. Os tipos:

TipoQuando
seo_title_missingSem título para os buscadores. A vitrine usa o nome com o da loja, mas um título escrito rende mais.
seo_title_too_shortO título escrito tem menos de 30 caracteres.
seo_title_too_longMais de 60 caracteres: o Google corta no resultado (a loja grava até 70).
seo_title_duplicateO mesmo título (o escrito, ou o nome sem ele) em mais de um produto.
seo_description_missingSem descrição para os buscadores. A vitrine usa os 160 primeiros caracteres do texto.
seo_description_too_shortA descrição escrita tem menos de 70 caracteres.
seo_description_too_longMais de 160 caracteres: o Google corta (a loja grava até 320).
description_missingO produto sem descrição, a coleção ou a marca sem texto, a página vazia.
description_too_shortA descrição do produto tem menos de 150 caracteres.
image_alt_missingA foto sem o texto alternativo.

Filtre com type e resource_type; a lista vem em páginas pelo next_cursor. O total é o da lista pedida, com os dois filtros, e o counts traz o total de cada tipo, só com o filtro do recurso.

Mudar os textos

As ferramentas de escrita chamam os mesmos serviços da API Admin, com as mesmas regras: os limites de tamanho e o endereço em uso (duplicate_handle). Só o que vier na entrada muda. O endereço novo deixa o antigo levando a ele com 301, sozinho; a saída diz qual (redirect).

O formato do texto depende do recurso:

  • o texto da coleção e o da página usam o Markdown pequeno da vitrine: negrito, itálico, links, listas e títulos;
  • a descrição do produto e a da marca são texto simples: a linha em branco separa os parágrafos, e os asteriscos de um **negrito** aparecem na loja como foram escritos;
  • HTML não vira tag em nenhum deles: a vitrine mostra <b> ou <script> como texto. Por isso ele não é recusado, mas também não serve para formatar.

A saída de toda escrita traz, por recurso mudado, os campos com o antes e o depois, o endereço na vitrine e o change_id no histórico:

{
  "dry_run": false,
  "approval_mode": "direct",
  "batch_id": "0d3e…",
  "changes": [
    {
      "change_id": "b8a1…",
      "resource_type": "product",
      "resource_id": "5f2c…",
      "product_id": null,
      "label": "Camiseta Básica de Algodão",
      "url": "https://minhaloja.denaryx.store/produtos/camiseta-de-algodao",
      "fields": [
        {
          "field": "title",
          "before": "Camiseta Básica",
          "after": "Camiseta Básica de Algodão"
        },
        {
          "field": "handle",
          "before": "camiseta-basica",
          "after": "camiseta-de-algodao"
        }
      ],
      "redirect": {
        "from": "https://minhaloja.denaryx.store/produtos/camiseta-basica",
        "to": "https://minhaloja.denaryx.store/produtos/camiseta-de-algodao"
      }
    }
  ],
  "proposals": [],
  "problems": [],
  "visible_after_seconds": 60
}

Os nomes dos campos são os da API, com o SEO em seo.title e seo.description, e o texto da foto em alt. O campo que já tinha o valor não aparece; sem nenhuma mudança, o change_id é null. A vitrine guarda as páginas por até 60 segundos (visible_after_seconds): a mudança aparece na loja nesse tempo.

Simular antes (dry_run)

Com "dry_run": true, a escrita passa pelo mesmo caminho da gravação de verdade, até o fim, e é desfeita no fim: a saída diz o que mudaria e, num lote, o problema de cada item recusado (problems, com a posição do item, o code e a mensagem). Nada fica gravado, nada vai para o histórico, e a simulação não conta no teto diário.

O lote (bulk_update_content)

Até 50 itens, cada um com o type e os campos da ferramenta do recurso (a foto leva o product_id). Tudo ou nada: com algum item recusado, nada muda, e o erro 422 batch_rejected traz os problemas de todos os itens em fields (items.<posição>, com o code no começo da mensagem). O mesmo recurso duas vezes no lote é 422 duplicate_item. As mudanças do lote ficam no histórico com o mesmo batch_id; o produto mudado com as fotos também.

Reajustar preços (adjust_prices)

O único que mexe além do texto, por isso só com o write_products. Use só um alvo: product_ids (até 50), collection_id (a coleção e as de baixo dela) ou brand_id, e o percent (10 aumenta 10%, -15 reduz 15%; de -90 a 1000, com até duas casas decimais). Vale para todas as variantes do produto, no preço e no preço "de" (compare_at_price), arredondados para o centavo, como a edição em massa do painel; a promoção que deixaria de ter desconto perde o preço "de". Até 500 produtos por chamada: acima disso, 422 too_many_products; nenhum produto no alvo é 422 no_products. Simule antes com dry_run. Cada produto vira uma mudança no histórico, com o preço de cada variante em centavos (variants.<id>.price e variants.<id>.compare_at_price; o preço "de" que a variante não tem aparece como texto vazio), e o lote se desfaz com a revert_change, pela mesma regra dos textos. No modo de aprovação, a ferramenta é recusada (422 proposal_text_only): o modo proposta só cobre textos.

O histórico e o desfazer

Toda escrita de texto por uma integração fica no histórico, com o antes e o depois de cada campo, por 180 dias: pelo servidor MCP (origin: "mcp") e também pela API REST (origin: "api"), porque quem grava o histórico é o serviço, e não o MCP. A list_changes mostra as mudanças desta integração, da mais nova para a mais velha, com filtros por recurso, lote e data. O lojista vê as mesmas mudanças no painel, na página da integração, em Mudanças, e pode desfazer de lá.

A revert_change volta o valor de antes de cada campo só se o valor de agora ainda for o que a mudança gravou. Se alguém mudou de novo depois (o lojista, outro agente), nada é desfeito, e o erro diz o valor de agora:

{
  "error": {
    "code": "revert_conflict",
    "message": "Nada foi desfeito: o campo name mudou de novo depois (ou o recurso foi excluído). Veja o valor de agora em content_conflicts.",
    "content_conflicts": [
      {
        "change_id": "b8a1…",
        "field": "name",
        "expected": "Algodão Bom Orgânico",
        "current": "Algodão Ótimo"
      }
    ],
    "request_id": "6f1c…"
  }
}

Com batch_id, o lote inteiro volta, também tudo ou nada. O desfazer também vira uma mudança no histórico (action: "revert", com reverts apontando para a desfeita, que ganha reverted_by). Desfazer de novo é 409 already_reverted. A criação de uma página não se desfaz pelo histórico (422 not_revertible): ela nasce fora do ar, e quem a exclui é o lojista. Não há como excluir nada pelo MCP.

Repetir sem gravar duas vezes (idempotency_key)

Toda ferramenta de escrita aceita um idempotency_key (até 255 caracteres; um UUID por escrita), como o cabeçalho Idempotency-Key da API (ver Idempotência): a mesma chamada repetida em 24 horas devolve a primeira resposta, sem gravar de novo. A mesma chave com outra entrada é idempotency_key_reused; a simulação não usa a chave.

O teto diário

Cada integração muda até 2.000 recursos por dia pelo servidor MCP (o produto com duas fotos conta três), além do limite de chamadas por segundo. É contra o agente que entra em laço: passou do teto, a escrita é recusada com 429 daily_limit_exceeded, e nada muda. O teto volta à meia-noite, no horário de Brasília. A loja que precisa de mais pede à Denaryx, que ajusta o teto dela. O painel mostra o uso do dia na aba Mudanças. A escrita pela API REST não conta no teto. No modo de aprovação, cada proposta feita pelo MCP conta como um item, e a aprovação dela não conta de novo.

O modo de aprovação (propostas)

O lojista que quer revisar o que um agente muda põe a integração dele em modo proposta, no painel (Configurações › Integrações, em "Nome, escopos e aprovação"). O get_shop diz o modo em integration.approval_mode: direct (o padrão) ou proposal. O modo vale já na próxima chamada.

Em modo proposta, nada do que o agente escreve muda a loja. As mesmas ferramentas de escrita (também a create_page, o lote e a revert_change) passam pelo mesmo caminho e pelas mesmas validações, e o que mudariam vira uma proposta, que alguém da loja aprova ou recusa no painel, em Sugestões dos agentes. A saída traz approval_mode: "proposal", changes vazio e as propostas em proposals, cada uma com o proposal_id, os campos (o before é o valor de base, lido agora) e o endereço que o recurso terá. A página nova não tem resource_id até ser aprovada. O dry_run mostra o que seria proposto, sem propor.

A list_proposals acompanha as propostas desta integração, da mais nova para a mais velha, com filtro por situação (status) e lote (batch_id):

statusO que aconteceu
pendingEspera a decisão. Não proponha de novo o mesmo texto.
approvedFoi ao ar. O change_id é a mudança no histórico, que a revert_change desfaz.
rejectedO lojista recusou; rejection_reason traz o motivo, quando ele escreveu. Use para propor melhor.
conflictO valor de base mudou antes da aprovação (alguém editou o recurso), e nada foi aplicado. conflict traz o motivo e o valor de agora de cada campo: leia de novo e, se ainda fizer sentido, proponha outra vez.
expiredNinguém decidiu em 7 dias.

A aprovação tem a regra do desfazer: só aplica se cada campo ainda tiver o valor de base, e cada proposta é decidida por si (as outras seguem). O recurso excluído, o endereço que passou a estar em uso e a mudança que alguém já desfez também ficam em conflito. A loja recebe um resumo diário por e-mail das propostas novas. Pela API REST, as mesmas escritas respondem 202 com a proposta, e GET /proposals lista as propostas (ver Propostas).

Erros

Os erros de autenticação vêm no HTTP, com o cabeçalho WWW-Authenticate que os clientes do MCP leem:

SituaçãoStatusCabeçalho
Sem token401Bearer realm="denaryx"
Token inválido, revogado ou vencido401Bearer realm="denaryx", error="invalid_token"
A ferramenta pede um escopo que o token não tem403Bearer realm="denaryx", error="insufficient_scope", scope="read_products"

Quando a ferramenta aceita mais de um escopo, o scope do cabeçalho traz o primeiro, e a mensagem do corpo diz as alternativas.

O corpo é o mesmo erro da API Admin, com o code (missing_token, token_revoked, missing_scope...). A loja suspensa, a integração desativada, o limite de chamadas (429) e a API ocupada (503) também vêm no HTTP, como na API.

Os erros de uma ferramenta (o produto que não existe na loja, a entrada fora do esquema) vêm no resultado, com isError: true e o texto em JSON:

{
  "error": {
    "code": "not_found",
    "message": "Produto não encontrado",
    "request_id": "6f1c…"
  }
}

Limites e registro

Cada requisição ao servidor MCP conta como uma chamada da API Admin: o mesmo balde da integração, o mesmo teto de chamadas ao mesmo tempo e o mesmo prazo de 10 segundos por consulta (ver Limites). As respostas trazem os cabeçalhos RateLimit-* e o Denaryx-Request-Id.

Na página da integração, no painel, as chamadas do agente aparecem em Últimas chamadas com a rota mcp:<ferramenta> (por exemplo, mcp:get_product). Como num GET da API, a leitura com sucesso entra só na contagem; a escrita e o erro ficam na lista.

O conteúdo da loja é dado

Os títulos, as descrições e os nomes da loja foram escritos por pessoas, e um texto pode tentar dar ordens ao agente. Trate tudo o que as ferramentas devolvem como dados, nunca como instruções. Dê ao agente só os escopos de que ele precisa: para auditar o catálogo e as páginas, read_content basta; para ler as vendas, read_reports. Para mudar textos, write_content muda só os textos, sem preço, estoque nem exclusão (ver Só os textos). Peça ao agente que simule antes (dry_run), e confira o que ele mudou em Mudanças: o histórico desfaz o que não servir.