mcp-samsclub-ads
MCP server for Sam's Club Sponsored Ads APIs — spec-driven endpoint discovery and a generic authenticated proxy with automatic RSA-SHA256 request signing.
Documentation
Sam's Club Sponsored Ads APIs
MCP server for Sam's Club Sponsored Ads APIs.
Exposes spec-driven discovery (list_endpoints, describe_endpoint), a generic API proxy (call_endpoint), and a runtime spec refresher (refresh_specs). The AI agent discovers endpoints from a bundled OpenAPI spec then calls them; the server handles RSA-SHA256 signing and auth headers automatically.
Forked from mcp-walmart-ads: Sam's Club runs on the same Walmart api-proxy backend and uses the same WM_* signing headers, so the auth, HTTP client, discovery, caching, and tool surface carry over. The differences are a single sponsored ad family (vs Walmart's search/display), the Sam's Club base URLs, and a hand-authored OpenAPI spec.
Features
- Spec-driven discovery — list/describe endpoints from a bundled OpenAPI spec, refreshable at runtime
- Any endpoint — call by operation id or raw method+path (raw path reaches endpoints not yet in the spec); no code changes when APIs evolve
- Multi-region, multi-environment (production + sandbox) via config file
- Per-request RSA-SHA256 signing with automatic header construction
- Large responses truncated with full data available via MCP resource URI
Requirements
- Python 3.13+
- Sam's Club Partner credentials (consumer ID, RSA key pair, bearer token) — see Status & blocker
Quick start
Set up your config (see Configuration), then run the server:
# Run directly with uvx (no clone needed)
npx -y @modelcontextprotocol/inspector uvx mcp-samsclub-ads
# Or run from source
git clone https://github.com/alyiox/mcp-samsclub-ads.git
cd mcp-samsclub-ads
uv sync
npx -y @modelcontextprotocol/inspector uv run mcp-samsclub-ads
The server speaks MCP over stdio.
Configuration
The config file lives under your home directory at ~/.config/mcp-samsclub-ads/config.json.
Windows note:
~maps to%USERPROFILE%(typicallyC:\Users\<you>), so the full path is%USERPROFILE%\.config\mcp-samsclub-ads\config.json.
1. Create the config directory and copy the example
# Unix-like (macOS, Linux, WSL, …)
mkdir -p ~/.config/mcp-samsclub-ads/keys/us
cp config.example.json ~/.config/mcp-samsclub-ads/config.json
# Windows (PowerShell)
New-Item -ItemType Directory -Force "$env:USERPROFILE\.config\mcp-samsclub-ads\keys\us"
Copy-Item config.example.json "$env:USERPROFILE\.config\mcp-samsclub-ads\config.json"
2. Edit ~/.config/mcp-samsclub-ads/config.json
{
"response_cache_ttl": 3600,
"truncate_threshold": 51200,
"regions": {
"US": {
"production": {
"consumer_id": "your-consumer-id",
"private_key": "./keys/us/prod.pem",
"private_key_version": "1",
"bearer_token": "your-bearer-token",
"base_urls": {
"sponsored": "https://developer.api.us.walmart.com/api-proxy/service/sp/api-sams/v1"
}
},
"sandbox": {
"consumer_id": "your-sandbox-consumer-id",
"private_key": "./keys/us/sandbox.pem",
"private_key_version": "1",
"bearer_token": "your-sandbox-bearer-token",
"base_urls": {
"sponsored": "https://developer.api.us.stg.walmart.com/api-proxy/service/sp/api-sams/v1"
}
}
}
}
}
3. Place your RSA private key PEM files in ~/.config/mcp-samsclub-ads/keys/
Key paths in the config are resolved relative to the config directory, so ./keys/us/prod.pem resolves to ~/.config/mcp-samsclub-ads/keys/us/prod.pem.
| Config field | Description |
|---|---|
response_cache_ttl | Seconds to keep truncated responses in memory (default 3600) |
truncate_threshold | Response byte limit before truncation (default 51200) |
regions.<R>.<E>.consumer_id | Your Sam's Club consumer ID |
regions.<R>.<E>.private_key | Path to RSA private key PEM (relative to config dir or absolute) |
regions.<R>.<E>.private_key_version | Key version string (default "1") |
regions.<R>.<E>.bearer_token | OAuth bearer token |
regions.<R>.<E>.base_urls.sponsored | Sponsored Ads API base URL |
Each request is signed with WM_CONSUMER.ID, WM_SEC.AUTH_SIGNATURE (Base64 RSA-SHA256), WM_SEC.KEY_VERSION, and WM_CONSUMER.INTIMESTAMP (Unix epoch ms), plus Authorization: Bearer <token> — all injected automatically by the client.
Base URLs
- Production:
https://developer.api.us.walmart.com/api-proxy/service/sp/api-sams/v1 - Sandbox:
https://developer.api.us.stg.walmart.com/api-proxy/service/sp/api-sams/v1
Paths in the spec begin /api/v1/... (or /api/v2/...), so the effective URL is <base_url>/api/v1/<resource>.
Tools
list_endpoints
List operations from the bundled OpenAPI spec for an ad_type, with optional filters.
| Parameter | Required | Description |
|---|---|---|
ad_type | yes | sponsored |
query | no | Case-insensitive substring match on operationId, path, or summary |
tag | no | Filter to operations whose OpenAPI tags include this value |
method | no | Filter by HTTP verb |
describe_endpoint
Return one operation plus the components.schemas reachable from it (its $ref closure), so request bodies and responses can be built without the full spec.
| Parameter | Required | Description |
|---|---|---|
ad_type | yes | sponsored |
operation_id | yes | Spec operation id (from list_endpoints) |
call_endpoint
Execute any Sam's Club Sponsored Ads API endpoint. Identify it by operation_id, or by raw method + path. Raw method+path also reaches endpoints not yet in the bundled spec. The server handles RSA-SHA256 signing.
| Parameter | Required | Description |
|---|---|---|
region | yes | e.g. US |
env | yes | production or sandbox |
ad_type | yes | sponsored |
operation_id | no* | Spec operation id; resolves method+path |
method | no* | GET, POST, PUT, PATCH, or DELETE |
path | no* | e.g. /api/v1/campaigns |
params | no | Query string parameters (JSON object) |
body | no | JSON request body for POST/PUT (object or array) |
* Provide either operation_id, or both method and path.
refresh_specs
Re-fetch the bundled OpenAPI spec from its committed source_url into a user cache (~/.cache/mcp-samsclub-ads/specs/) that takes precedence over the bundled copy — ship fixes without a release.
| Parameter | Required | Description |
|---|---|---|
spec_id | no | One spec to refresh (e.g. sponsored/sponsored-ads); omit to refresh all |
MCP resources
Endpoint schemas come from the bundled OpenAPI spec via list_endpoints / describe_endpoint (see Tools), not from static resources.
Dynamic resources
| Resource URI | Description |
|---|---|
sca://config | Available regions, environments, and ad types from your config |
sca://responses/{request_id} | Full body of a truncated API response (cached in memory, TTL from config) |
sca://curl/{request_id} | Reproducible cURL command for a previous API request |
Spec coverage
The bundled OpenAPI spec is hand-authored from the
developer.samsclub.com docs and is partial by
design. It covers the core ads surface: Campaigns, Statistics, Ad Groups, Ad Items, Catalog
Item Search, Keywords (+ suggestions/analytics), Negative Keywords, Placements, Bid
Multipliers, and Snapshot Reports v1/v2 + Latest Report Date. Endpoints not yet modeled (SBA
profiles, media, reviews, api-usage, entity/audit snapshots, alerts & recommendations) are
still reachable via call_endpoint with a raw method+path.
Status & blocker
Partner credentials are the long pole. Access requires emailing an RSA public key to
partner-support@samsclub.com and receiving a consumer id + auth token + key version.
Without sandbox credentials the server runs and its discovery/signing are verified offline,
but calls cannot be exercised end-to-end.
Spec drift detection
scripts/build_spec.py scrapes the docs into a candidate spec at
spec-candidate/ — offline, never at runtime. The
Spec drift workflow runs it on a schedule and opens a
PR when the docs change; a reviewer then ports real changes into the hand-authored canonical
spec. The candidate is intentionally flat and is never shipped or loaded.
uv run --group spec-build python scripts/build_spec.py # write candidate
uv run --group spec-build python scripts/build_spec.py --check # exit 1 on drift
Still deferred: flipping a spec's auth flag to fetch the authenticated live Swagger backend
once partner credentials exist.
MCP host examples
Cursor
Add to .cursor/mcp.json:
{
"mcpServers": {
"samsclub-ads": {
"command": "uvx",
"args": ["mcp-samsclub-ads"]
}
}
}
Claude Code
Add to your Claude Code MCP config:
{
"mcpServers": {
"samsclub-ads": {
"command": "uvx",
"args": ["mcp-samsclub-ads"]
}
}
}
Codex
[mcp_servers.samsclub-ads]
command = "uvx"
args = ["mcp-samsclub-ads"]
OpenCode
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"samsclub-ads": {
"type": "local",
"enabled": true,
"command": ["uvx", "mcp-samsclub-ads"]
}
}
}
GitHub Copilot
{
"inputs": [],
"servers": {
"samsclub-ads": {
"type": "stdio",
"command": "uvx",
"args": ["mcp-samsclub-ads"]
}
}
}
Development
uv sync --group dev # install deps
uv run pytest # run tests
uv run ruff check . # lint
uv run ruff format . # format
uv run pyright # type check
Reference
- API docs: https://developer.samsclub.com/API/overview/
- Authentication: https://developer.samsclub.com/API/authentication/
- Base project:
mcp-walmart-ads
Contributing
Open issues or PRs. Follow existing style and add tests where appropriate.
License
MIT. See LICENSE.