mcp-walmart-ads
Servidor MCP para Walmart Connect Ads (Sponsored Search + Display) — firma automática RSA-SHA256, configuración multirregión y documentación de API incluida.
Documentación
APIs de Walmart y Sam's Club
Servidor MCP para tres familias de APIs de Walmart Inc., detrás de una única superficie de herramientas:
| Plataforma | APIs | Autenticación |
|---|---|---|
walmart:ads — Walmart Connect | Sponsored Products, Display | Firma RSA-SHA256 + token bearer |
walmart:marketplace — Walmart Marketplace | 28 dominios (pedidos, artículos, feeds, informes, …) | OAuth2 client_credentials |
samsclub:ads — Sam's Club | Sponsored Products | Firma RSA-SHA256 + token bearer |
Cuatro herramientas sobre 31 APIs y 424 operaciones: descubrimiento, un proxy genérico y un descargador. Un agente encuentra endpoints en las especificaciones OpenAPI incluidas y los invoca; el servidor firma, adquiere tokens y construye los encabezados.
walmart:ads:sponsored-products:SBAProfileUpdateV2
└─ retailer ─┘└ line ┘└─── api name ───┘└── operationId ──┘
└────────── platform ─────────┘ credentials attach here
Un id de operación por sí solo se resuelve a un host y un modelo de autenticación. Un sufijo <line>:<name>
coincidente significa la misma superficie para otro minorista: 13 ids de operación compartidos de 90.
Requisitos
- Python 3.13+
- Credenciales para las plataformas que uses. Configura solo esas: una plataforma ausente
simplemente no está configurada, y el descubrimiento funciona sin credenciales en absoluto.
- Walmart Connect / Sam's Club — ID de consumidor, par de claves RSA, token bearer
- Walmart Marketplace — ID de cliente + secreto, y los ids de anunciante (perfil de vendedor) que atienden
Inicio rápido
Configura tu configuración (consulta Configuración), luego ejecuta el servidor:
# Run directly with uvx (no clone needed)
npx -y @modelcontextprotocol/inspector@latest uvx mcp-walmart-ads
# Or run from source
git clone https://github.com/alyiox/mcp-walmart-ads.git
cd mcp-walmart-ads
uv sync
npx -y @modelcontextprotocol/inspector@latest uv run mcp-walmart-ads
Configuración
config.json DEBE vivir en ~/.config/mcp-walmart-ads/config.json. El servidor lo lee
una vez al inicio, por lo que un archivo corregido REQUIERE un reinicio.
Windows:
~se asigna a%USERPROFILE%(típicamenteC:\Users\<you>), haciendo la ruta completa%USERPROFILE%\.config\mcp-walmart-ads\config.json.
Crea el directorio y copia el ejemplo:
# Unix-like (macOS, Linux, WSL, …)
mkdir -p ~/.config/mcp-walmart-ads/keys/walmart-ads
cp config.example.json ~/.config/mcp-walmart-ads/config.json
# Windows (PowerShell)
New-Item -ItemType Directory -Force "$env:USERPROFILE\.config\mcp-walmart-ads\keys\walmart-ads"
Copy-Item config.example.json "$env:USERPROFILE\.config\mcp-walmart-ads\config.json"
Forma
platforms.<platform>.regions.<region>.<environment> = <auth block>
<platform> es el prefijo de dos segmentos con el que comienza un id de API, por lo que una clave de configuración es literalmente
el valor que pasas como parámetro de la herramienta platform — nada que traducir.
La forma del bloque de autenticación sigue el modelo de autenticación de la plataforma. Hay exactamente una forma por plataforma, por lo que no se necesita un campo discriminador.
Plataformas de firma (walmart:ads, samsclub:ads):
{
"platforms": {
"walmart:ads": {
"regions": {
"us": {
"production": {
"consumer_id": "your-consumer-id",
"private_key": "./keys/walmart-ads/us-prod.pem",
"private_key_version": "1",
"bearer_token": "your-bearer-token",
"base_urls": {
"sponsored-products": "https://developer.api.walmart.com/api-proxy/service/WPA/Api/v1",
"display": "https://developer.api.walmart.com/api-proxy/service/display/api/v1"
}
}
}
}
}
}
}
| Campo | Notas |
|---|---|
consumer_id | ID de consumidor de Partner Network |
private_key | Ruta a la clave privada RSA (PEM). Las rutas relativas se resuelven contra el directorio de configuración |
private_key_version | Cadena de versión de clave (por defecto "1") |
bearer_token | Token bearer OAuth |
base_urls.<api> | Uno por API en la superficie de descubrimiento de la plataforma. Las claves PUEDEN ser simples (sponsored-products) o completamente calificadas (walmart:ads:sponsored-products); se permiten claves adicionales para las especificaciones auxiliares alcanzadas por método+ruta sin procesar |
Los nombres de entorno son de forma libre aquí — Walmart puede emitir un tenant solo production, o
production y staging.
Plataforma OAuth2 (walmart:marketplace):
{
"platforms": {
"walmart:marketplace": {
"regions": {
"us": {
"production": {
"credentials": [
{
"client_id": "your-client-id",
"client_secret": "your-client-secret",
"advertisers": [
{ "id": 7060158, "partner_id": "10001234" },
{ "id": 7060159 }
]
}
]
}
}
}
}
}
}
environment DEBE ser production o sandbox; las URLs base están fijadas por el servidor y
ausentes del archivo. Los ids de anunciante se anidan bajo la credencial que los atiende, por lo que un
secreto aparece exactamente una vez y una referencia de anunciante colgante es estructuralmente
imposible. partner_id es por vendedor porque dos operaciones payments lo requieren como
WM_PARTNER_ID; un valor de todo ceros se lee como ausente, ya que eso es lo que una configuración generada
escribe para un vendedor sin uno, y scripts/backfill_partner_ids.py llena los vacíos desde
Walmart.
Las regiones son un espacio de nombres, no una ruta — cada región walmart:marketplace alcanza los mismos
hosts. El nivel existe porque los ids de anunciante son únicos solo dentro de una región.
Dividiendo la configuración
Un bloque walmart:marketplace poblado ocupa decenas de kilobytes de credenciales, 88% del
archivo aquí, y una coma perdida al editarlo derriba cada plataforma: un fallo de
análisis precede a la validación por plataforma. Las plataformas PUEDEN por lo tanto vivir en archivos de
inserción bajo config.d/, fusionados sobre la base:
~/.config/mcp-walmart-ads/
├── config.json # server-wide settings, and any platforms you like
├── config.d/
│ ├── walmart-marketplace.json # only a "platforms" object
│ └── samsclub-ads.json
└── keys/
- Un archivo de inserción DEBE declarar solo
platforms; la configuración de todo el servidor permanece enconfig.json. - Una plataforma declarada en dos archivos es un error que nombra ambos — nunca precedencia silenciosa.
- Solo
*.jsondirectamente enconfig.d/se lee, por lo que.baky los archivos de intercambio del editor se ignoran. - Un archivo que falla al analizarse cuesta solo sus propias plataformas; el resto sigue funcionando.
- Las rutas
private_keyrelativas se resuelven contra el directorio deconfig.jsonde cualquier manera, por lo que mover una plataforma aconfig.d/no necesita ediciones de ruta. - Sin directorio
config.d/significa sin cambio en el comportamiento.
Un bloque malformado para una plataforma NO DEBE detener la carga de las demás. Lee
wmt://platforms para ver cuáles se cargaron y qué regiones y entornos declaran: una que
falló no tiene regiones, y leer uno de sus entornos devuelve el mensaje del propio cargador —
qué archivo, qué campos, y que una corrección necesita un reinicio.
Opciones de nivel superior
| Campo | Por defecto | Notas |
|---|---|---|
response_cache_ttl | 3600 | Segundos que un cuerpo truncado o descarga permanece legible en su URI de recurso |
truncate_threshold | 2048 | Bytes de respuesta devueltos en línea antes de truncar a una vista previa |
spec_refresh | {"auto": true, "interval": 7} | Actualización de especificaciones en segundo plano. auto activa o desactiva el barrido; interval son días entre barridos, y PUEDE ser fraccionario |
Mercado → tenant (wap-tenant-id)
Pasa tenant en call_endpoint y download_file para mercados walmart:ads no estadounidenses
(WMT_CA, WMT_MX, WBD_OD, …). Omítelo para EE. UU. y para walmart:marketplace.
Herramientas
list_endpoints
Lista operaciones en cada API, con filtros opcionales.
| Parámetro | Notas |
|---|---|
query | Subcadena insensible a mayúsculas en id de operación, ruta o resumen |
api | Limitar a una API, p. ej. walmart:marketplace:order-management |
platform | Limitar a una plataforma — walmart:ads, walmart:marketplace, samsclub:ads (enumeración de esquema) |
tag | Filtrar por etiqueta OpenAPI |
method | Filtrar por verbo HTTP — GET, POST, PUT, PATCH, DELETE (enumeración de esquema) |
Los ids de operación devueltos están calificados (api:operationId) y pasan directamente a
describe_endpoint o call_endpoint.
describe_endpoint
Una operación más cada entrada components.schemas alcanzable desde ella, para que un cuerpo
de solicitud pueda construirse sin la especificación completa. Los encabezados de autenticación y QoS gestionados por el servidor se omiten.
| Parámetro | Notas |
|---|---|
operation_id | Calificado (api:operationId), o simple cuando no es ambiguo |
api | API para resolver un id simple, p. ej. walmart:ads:sponsored-products |
call_endpoint
Ejecuta una solicitud autenticada contra cualquier plataforma configurada. La firma, la adquisición de tokens con caché por credencial y actualización de vuelo único, y un reintento después de un 401 ocurren todos en el lado del servidor.
| Parámetro | Notas |
|---|---|
region, environment | Requeridos. Fuente: configuración |
operation_id | Calificado o simple. Resuelve API, plataforma, método, ruta y encabezados requeridos |
api | Requerido con method + path sin procesar; de lo contrario inferido de operation_id. Acepta las dos especificaciones walmart:ads auxiliares |
method, path | Ruta sin procesar, alcanzando endpoints alfa/beta/no publicados ausentes de las especificaciones |
path_params | Valores para {placeholders} en la ruta |
params, body | Cadena de consulta y cuerpo JSON |
file_path | Enviar el archivo como multipart/form-data — cargas de feeds de Marketplace. Combínalo con el parámetro de consulta feedType |
advertiser_id | DEBE darse en walmart:marketplace, donde selecciona la credencial. Opcional en las plataformas de anuncios, donde se envía como X-Advertiser-ID |
tenant | Tenant WAP para regiones walmart:ads no estadounidenses |
Una respuesta más grande que truncate_threshold se previsualiza en línea, con el cuerpo completo en
wmt://responses/{request_id} y un cURL reproducible en wmt://curl/{request_id} —
tokens bearer, tokens de acceso y firmas reemplazados con marcadores de posición.
download_file
Descarga un informe, etiqueta o instantánea de un endpoint autenticado. Da un url completo
(la URL details de una encuesta de instantánea de display, por ejemplo), o operation_id, o api con
method + path. platform es requerido solo para un url simple.
Con dest_path los bytes se escriben allí. Sin él, se descomprimen con gunzip cuando están comprimidos
y se almacenan en caché, y el resultado lleva cached_at — una carga útil binaria sin dest_path pide
una en su lugar. Se siguen las redirecciones, manteniendo los encabezados de autenticación en un Location relativo o del mismo host
y descartando credenciales entre hosts; el resultado incluye urls, la ruta de saltos.
Especificaciones
33 documentos OpenAPI viajan en el paquete. Una actualización escribe copias actualizadas en
~/.cache/mcp-walmart-ads/specs/, que supera al paquete en la lectura. El paquete nunca se
escribe en tiempo de ejecución, por lo que permanece como el piso al que una caché dañada o faltante recurre.
De dónde vienen
Walmart no publica archivos OpenAPI, pero cada página de referencia de ReadMe hidrata su HTML con
los UUIDs de registro de sus documentos, y https://dash.readme.com/api/v1/api-registry/<uuid>
sirve la especificación completa sin autenticación. Eso cubre Walmart Connect y los 28 dominios
de Marketplace. Sam's Club no publica ninguno, por lo que su especificación está escrita a mano a partir de la documentación
para desarrolladores: scripts/build_samsclub_spec.py regenera un candidato de esos documentos, y el
flujo de trabajo programado spec drift abre un PR cuando cambian, como una puerta de revisión humana. El
candidato nunca se envía y nunca se carga en tiempo de ejecución.
Los documentos se almacenan textualmente tal como el upstream los sirvió, por lo que un diff de actualización muestra exactamente
lo que cambió. Los ejemplos en línea sobredimensionados y los metadatos x-readme se eliminan en la carga
en lugar de en el disco, lo que mantiene esa reducción reajustable sin volver a descargar nada.
Manteniéndolas actualizadas
Las especificaciones se actualizan en segundo plano: una vez al inicio, luego cada spec_refresh.interval días
(7 por defecto). Establece auto a false para detener el barrido — el intervalo se recuerda para
cuando lo vuelvas a activar, y la actualización manual aún funciona.
Para actualizar ahora, habiendo alcanzado un endpoint que la especificación incluida no tiene:
# Installed with uvx (no clone)
uvx mcp-walmart-ads --refresh
# Or from a source checkout
uv run mcp-walmart-ads --refresh
Cualquier forma barre cada especificación independientemente del intervalo y escribe la misma caché de usuario,
imprimiendo una fila por documento: written, unchanged, o error.
Deliberadamente no hay herramienta para esto. Una especificación obsoleta es indistinguible de una actual desde dentro de una sesión — simplemente carece de un endpoint — por lo que un agente al que se le pide decidir o nunca actualizaría o actualizaría supersticiosamente después de un fallo no relacionado. El disparador pertenece fuera de la sesión.
Un documento DEBE producir al menos una operación antes de instalarse, por lo que un upstream que responda
200 con un cuerpo de error no puede envenenar la caché, y un documento byte-idéntico
se deja solo. Un archivo en caché que no se carga se descarta y la lectura recurre a
el paquete. Los servidores se coordinan a través de spec-state.json en la raíz de la caché, que registra
cuándo se intentó cada especificación por última vez y mantiene un bloqueo para que un proceso barra a la vez — cada
sesión de cliente ejecuta su propio proceso de servidor, y sin él cada uno re-descargaría los 33.
Reconstruyendo el paquete
Un paso de mantenedor, y no lo mismo que --refresh: eso actualiza tu caché, esto
actualiza las copias que viajan en el paquete.
# Registry-sourced specs only, by default
uv run python scripts/fetch_specs.py
uv run python scripts/fetch_specs.py walmart:ads:sponsored-products walmart:marketplace:order-management
# Regenerate the Sam's Club candidate spec for review
uv run --group spec-build python scripts/build_samsclub_spec.py
Recursos MCP
| URI del recurso | Descripción |
|---|---|
wmt://platforms | Cada plataforma, su modelo de autenticación, y las regiones y entornos que declara |
wmt://platforms/{platform}/apis | Los ids de api de esa plataforma |
wmt://platforms/{platform}/apis/{name} | Una api: título, versión, número de operaciones, y sus etiquetas con un conteo cada una — los valores legales de list_endpoints(tag=…) |
wmt://platforms/walmart:marketplace/regions/{region}/{environment}/advertisers | Ids de anunciante mapeados a su Walmart Partner ID (null cuando no está configurado) |
wmt://platforms/{platform}/regions/{region}/{environment}/hosts | Ids de api mapeados a la URL base que alcanza una llamada, o * para cada api cuyo host pertenece al servidor |
wmt://responses/{request_id} | Cuerpo completo de una respuesta truncada o una descarga en caché (en memoria, TTL desde la configuración) |
wmt://curl/{request_id} | cURL reproducible para una solicitud anterior, con credenciales reemplazadas por marcadores de posición |
Ejemplos de hosts MCP
Cursor
Añade a .cursor/mcp.json:
{
"mcpServers": {
"walmart": {
"command": "uvx",
"args": ["mcp-walmart-ads"]
}
}
}
Claude Code
Añade a tu configuración MCP de Claude Code:
{
"mcpServers": {
"walmart": {
"command": "uvx",
"args": ["mcp-walmart-ads"]
}
}
}
Codex
[mcp_servers.walmart]
command = "uvx"
args = ["mcp-walmart-ads"]
OpenCode
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"walmart": {
"type": "local",
"enabled": true,
"command": ["uvx", "mcp-walmart-ads"]
}
}
}
GitHub Copilot
{
"inputs": [],
"servers": {
"walmart": {
"type": "stdio",
"command": "uvx",
"args": ["mcp-walmart-ads"]
}
}
}
Desarrollo
uv sync --group dev
uv run ruff check src/ tests/ scripts/
uv run ruff format --check src/ tests/ scripts/
uv run pyright
uv run pytest tests/ -v
Contribuciones
Las incidencias y solicitudes de extracción son bienvenidas. Mantén los cambios enfocados; ruff check,
ruff format --check, pyright, y pytest DEBEN pasar todos.