Construir um servidor MCP para WooCommerce: guia prático
O Model Context Protocol dá a um agente de IA uma superfície tipada e introspetável para agir sobre a loja. O WooCommerce já disponibiliza uma REST API em /wp-json/wc/v3/. Colocar o MCP à frente dela transforma essa superfície em algo que um LLM consegue chamar sem adivinhar nomes de parâmetros. É esta a arquitetura que implemento para clientes que querem o catálogo e a intenção de encomenda acessíveis a partir do Claude, do ChatGPT ou do seu próprio runtime de agentes.
O artigo integra-se no serviço de desenvolvimento de servidores MCP e no pilar Universal Commerce Protocol.
TL;DR
- O MCP fica à frente do REST do WooCommerce como uma superfície JSON-RPC tipada.
- Três ferramentas cobrem a maior parte das necessidades do agente:
catalogue.list,product.detail,order.intent. - Os esquemas Zod definem cada entrada e saída e correm em cada chamada.
- Mapeie os campos do WooCommerce para
schema.orgProduct e Offer, para que as respostas do agente sejam coerentes entre superfícies. - O Cloudflare Workers é o meu destino de implementação por defeito para servidores MCP dominados por leituras.
O que é, afinal, o MCP
A Anthropic anunciou o Model Context Protocol a 25 de novembro de 2024 (anthropic.com/news/model-context-protocol). É um protocolo aberto baseado em JSON-RPC 2.0 que permite a um cliente (um host LLM como o Claude Desktop, um IDE ou o seu próprio runtime de agentes) falar com um servidor (a sua implementação MCP) por stdio ou HTTP com Server-Sent Events.
Três primitivas:
- Tools. Funções invocáveis com entrada e saída tipadas. O agente escolhe uma e chama-a.
- Resources. Referências legíveis a documentos ou registos que o agente pode trazer para o contexto.
- Prompts. Modelos de prompt fornecidos pelo servidor, que o host pode invocar.
Numa loja WooCommerce, são as tools que carregam o grosso do trabalho. Percorrer o catálogo é uma chamada de ferramenta. Os detalhes de um produto são uma chamada de ferramenta. Propor uma encomenda é uma chamada de ferramenta. Os resources são úteis quando o agente precisa do texto integral da política de devoluções ou de uma descrição longa de produto; os prompts são úteis quando quer fornecer interações prontas (“propõe três produtos para este perfil de cliente”).
De que ferramentas MCP precisa uma loja WooCommerce
Antes de escrever uma linha de código, listo as intenções que o agente deve conseguir exprimir perante a loja. Para uma loja WooCommerce típica, a lista é curta:
- Percorrer o catálogo com filtros (
catalogue.list). - Detalhes completos de um produto (
product.detail). - Propor uma encomenda em nome do cliente, devolvida como rascunho para confirmação humana (
order.intent). - Verificar o estado de uma encomenda existente pelo número e pelo endereço de e-mail (
order.status). - Consultar o stock de um SKU (
inventory.check).
Cada intenção corresponde exatamente a uma ferramenta. Resista à tentação de expor wc.products.get, wc.products.list, wc.orders.create como um wrapper um-para-um do REST. O agente não ganha nada com verbos REST; ganha com verbos de intenção.
Como definir ferramentas MCP em Zod
O SDK TypeScript em @modelcontextprotocol/sdk usa Zod (zod.dev) para os esquemas de entrada e saída. Este é o contrato para catalogue.list:
import { z } from "zod";
export const catalogueListInput = z.object({
query: z.string().min(2).max(200).optional(),
category: z.string().optional(),
in_stock: z.boolean().optional(),
price_min: z.number().nonnegative().optional(),
price_max: z.number().nonnegative().optional(),
page: z.number().int().min(1).max(100).default(1),
per_page: z.number().int().min(1).max(50).default(12),
});
export const catalogueListOutput = z.object({
results: z.array(
z.object({
sku: z.string(),
name: z.string(),
url: z.string().url(),
price: z.object({
amount: z.number().nonnegative(),
currency: z.string().length(3),
}),
availability: z.enum(["InStock", "OutOfStock", "PreOrder"]),
image: z.string().url().optional(),
})
),
total: z.number().int().nonnegative(),
page: z.number().int(),
});Há aqui duas coisas importantes. Primeiro, a saída usa o vocabulário schema.org (InStock, OutOfStock, PreOrder são valores tirados diretamente de ItemAvailability). Segundo, a entrada rejeita lixo na fronteira do protocolo; o handler nunca verá query: "" nem um price_min negativo.
Declaro todas as ferramentas com o mesmo padrão: esquema de entrada, esquema de saída, handler. O handler é um adaptador fino sobre /wp-json/wc/v3/. O Zod corre duas vezes por chamada: uma na entrada, outra na saída. A verificação da saída apanha erros do handler que, de outro modo, chegariam ao agente como dados malformados.
Como ligar o servidor MCP à REST API do WooCommerce
O handler de catalogue.list lê de /wp-json/wc/v3/products:
async function handleCatalogueList(input: z.infer<typeof catalogueListInput>) {
const params = new URLSearchParams();
if (input.query) params.set("search", input.query);
if (input.category) params.set("category", input.category);
if (input.in_stock) params.set("stock_status", "instock");
if (input.price_min !== undefined) params.set("min_price", String(input.price_min));
if (input.price_max !== undefined) params.set("max_price", String(input.price_max));
params.set("page", String(input.page));
params.set("per_page", String(input.per_page));
const response = await fetch(
`${WC_BASE}/wp-json/wc/v3/products?${params.toString()}`,
{ headers: { Authorization: `Basic ${WC_AUTH}` } }
);
const products = await response.json();
const total = Number(response.headers.get("X-WP-Total") ?? 0);
return catalogueListOutput.parse({
results: products.map(mapWooProductToSchemaOrg),
total,
page: input.page,
});
}O helper mapWooProductToSchemaOrg traduz os nomes de campos do WooCommerce (stock_status, regular_price, permalink) para a forma compatível com schema.org da declaração de saída. O mapeamento de campos fica num só sítio, chamado a partir de uma ferramenta, chamada a partir de um handler. Os desvios entre atualizações do WooCommerce e o contrato do agente são apanhados pela chamada parse na saída, não pela produção.
Em order.intent, nunca chamo os endpoints de escrita do WooCommerce a partir do handler. A ferramenta devolve um rascunho de encomenda que o host mostra ao utilizador, o utilizador confirma, e só então um caminho separado e autenticado chama POST /wp-json/wc/v3/orders. Escrever às cegas com base num LLM é um mau padrão. Isto encaixa também no RGPD art. 25 (proteção de dados desde a conceção); o rascunho permite ajustar o botão de compra e os consentimentos de marketing antes de a operação ser executada.
Como mapear campos do WooCommerce para schema.org
Duas razões para manter as saídas das ferramentas no vocabulário schema.org:
Os agentes usam as mesmas palavras entre fontes. Quando um agente responde a “este produto está disponível?”, junta uma entidade Product do seu servidor MCP, uma Offer com availability e eventualmente uma Review. Se o seu servidor MCP devolver { "stock": "sim" } e outra fonte availability: "https://schema.org/InStock", o agente tem de conciliar dois vocabulários. Com o alinhamento a schema.org do seu lado, não precisa.
Os dados estruturados da sua loja já falam schema.org. O seu /wp-content/themes/<theme>/single-product.php (ou o equivalente headless) emite JSON-LD do tipo Product. Manter a saída do MCP com a mesma forma faz com que a mesma página de produto sirva os mesmos dados ao crawler da Google, ao crawler da OpenAI por URL e ao agente que chama diretamente a sua ferramenta MCP.
O mapeamento de um produto WooCommerce típico é este:
| Campo WooCommerce | Campo schema.org |
|---|---|
sku | sku |
name | name |
permalink | url |
regular_price + currency | offers.price + priceCurrency |
stock_status: "instock" | availability: InStock |
stock_status: "outofstock" | availability: OutOfStock |
images[0].src | image |
description | description |
As variações exigem uma camada adicional (uma lista hasVariant do tipo Product), mas a forma base é a mesma.
Como tornar as encomendas idempotentes no servidor MCP
As ferramentas de leitura (catalogue.list, product.detail, inventory.check, order.status) são naturalmente idempotentes. Três chamadas devolvem a mesma resposta.
order.intent é a armadilha. O agente pode chamar duas vezes se a rede entre a resposta e o host falhar por instantes. O contrato que implemento:
order.intentaceita umaidempotency_keyopcional (UUID fornecido pelo host).- O handler guarda a chave no Workers KV com TTL de 24 horas, junto com o ID do rascunho de encomenda criado.
- Uma nova chamada com a mesma chave devolve o mesmo rascunho, em vez de criar um novo.
O padrão corresponde à idempotência da Stripe em POST /v1/charges. É bem conhecido; agentes e pessoas beneficiam por igual.
Como implementar o servidor MCP no Cloudflare Workers
O SDK TypeScript do MCP exporta a classe Server e adaptadores de transporte. No Workers uso o transporte HTTP em streaming. A entrada do Worker é esta:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
export default {
async fetch(request: Request, env: Env): Promise<Response> {
if (request.method !== "POST") {
return new Response("Method not allowed", { status: 405 });
}
const auth = request.headers.get("Authorization");
if (!isValidAgentToken(auth, env)) {
return new Response("Unauthorized", { status: 401 });
}
const server = createWooCommerceMcpServer(env);
const transport = new StreamableHTTPServerTransport(request);
return server.connect(transport);
},
};isValidAgentToken verifica tokens com âmbito limitado no Workers KV. As ferramentas só de leitura passam com um token de permissões mais baixas; order.intent exige um token com o âmbito orders:write. A estratégia de autenticação é um de dois padrões de autenticação MCP.
O wrangler.toml declara o URL de origem do WordPress, a chave de consumidor e o segredo do WooCommerce (como segredos do Worker, nunca no código) e um namespace KV para a idempotência. A implementação faz-se com wrangler deploy. O Worker mantém-se pequeno (bem abaixo do limite de 1 MB de bundle comprimido), porque é apenas encaminhamento JSON-RPC mais adaptadores REST.
Como registar e monitorizar chamadas de ferramentas MCP
Registo cada chamada de ferramenta com:
- O nome da ferramenta.
- Um hash SHA-256 da entrada (não a entrada em si; risco de dados pessoais nos termos do RGPD art. 32).
- A latência.
- Se a validação da saída passou ou não.
- O âmbito do token do agente.
Os registos seguem pelo Cloudflare Logpush para armazenamento de longo prazo. Dois hábitos operacionais que isto torna possíveis:
Apertar os esquemas. Seis semanas após o arranque, reviso as falhas de validação. Cada falha de saída é um bug do handler ou um campo do WooCommerce por mapear. Cada falha de entrada é um agente com defeito ou um esquema demasiado restritivo. Ambos entram na versão seguinte.
Atribuição de custos. As chamadas de ferramentas são carga mensurável sobre a origem do WooCommerce. O registo mostra que token de agente gera a carga e com que ferramenta. Um único agente com mau comportamento, preso em ciclo em catalogue.list, fica a uma consulta ao registo de ser identificado.
Outros guias sobre MCP e WooCommerce
Este artigo mostra o percurso de implementação. Uma arquitetura resistente a falhas para sincronização com ERP, filas Redis e transações de base de dados está descrita no nosso guia de arquitetura de integração WooCommerce com ERP 2026. A página do serviço é desenvolvimento de servidores MCP. A estratégia mais ampla está no Universal Commerce Protocol.
O orçamento é individual, porque o âmbito depende do número de ferramentas necessárias, da complexidade das extensões do WooCommerce e dos runtimes de agentes que pretende suportar.







