wafle MCP server
The first MCP server for the wafle commerce platform. It enables Claude, agents, and MCP-compatible clients to manage stores, products, orders, pricing, gateways, and more through natural language.
README
@wafle/mcp — Model Context Protocol server for wafle
The first MCP server for the wafle commerce platform. Lets Claude Desktop, Claude Code, agents, and any MCP-compatible client drive a wafle tenant — stores, products, orders, pricing, gateways, audiences, system — by talking to it.
What this is
Wafle is the LLM-first multi-tenant commerce platform. Its REST API at https://wafle.click/wp-json/waffle/v1/ does the work; this MCP server exposes that work as 68 tools, 14 resources, and 5 server-defined prompts an LLM can pick from. Every wafle feature should land here as a tool first, UI second.
This is a thin, well-typed wrapper. No business logic lives in this package — bugs in pricing or order flow belong to the wafle backend.
Install
git clone <repo>
cd wafle-mcp
npm install
npm run build
Configure
Set environment variables (or copy .env.example to .env):
WAFLE_API_URL=https://wafle.click/wp-json/waffle/v1 # default
WAFLE_API_KEY=<your wafle admin key> # required (upstream)
WAFLE_TIMEOUT_MS=30000 # optional
# Only used by --transport=http:
WAFLE_MCP_HTTP_HOST=0.0.0.0
WAFLE_MCP_HTTP_PORT=7100
# Tenant-scoped JWT auth (recommended). Same value as WAFFLE_MCP_JWT_SECRET
# in the backend mu-plugin. Generate: openssl rand -hex 64
WAFLE_MCP_JWT_SECRET=<shared HS256 secret>
WAFLE_MCP_REQUIRE_JWT=1 # refuses legacy bearers in prod
# Legacy admin-tier bearers (cross-tenant). Used as fallback when REQUIRE_JWT is unset.
WAFLE_MCP_TOKENS=<comma-separated bearer tokens>
LOG_LEVEL=info
WAFLE_API_KEY is the upstream wafle key the MCP server uses to talk to the backend (master, scoped at the backend layer). Tenant isolation for the MCP itself is enforced by WAFLE_MCP_JWT_SECRET — the backend mints HS256 JWTs carrying tenant_id + tenant_slug + scope, and this server filters tools/list and rewrites every per-store call to the JWT's tenant. See src/auth/jwt.ts, src/auth/tenant.ts.
Tenant tokens
Clients obtain a tenant-scoped JWT from the wafle backend:
POST /wp-json/waffle/v1/admin/account/mcp/issue-token
X-Wafle-Admin-Key: <store key for that tenant>
{ "ttl_days": 30 }
Response includes { token, jti, tenant_slug, expires_at, mcp_url }. The client then sends Authorization: Bearer <token> to mcp.wafle.click/mcp. With this token:
tools/listonly shows the tenant's tools — admin-only tools (wafle_system_*,wafle_gateways_*,wafle_stores_list/create,wafle_auth_keys_*) are filtered out server-side.- Every per-tenant tool ignores the
slugargument and uses the JWT'stenant_sluginstead. A token for tenant A cannot read or mutate tenant B regardless of arguments passed. - Tokens can be revoked via
POST /admin/account/mcp/revoke-token({ jti }) and listed viaGET /admin/account/mcp/tokens.
Run
stdio (Claude Desktop, Claude Code local)
node dist/index.js --transport=stdio
Logs go to stderr. Stdout is reserved for the JSON-RPC framing — never console.log from a tool handler.
HTTP / Streamable HTTP (remote agents, web)
node dist/index.js --transport=http
Listens on WAFLE_MCP_HTTP_PORT (default 7100). Single endpoint at POST /mcp per the MCP Streamable HTTP spec (2025-03-26). GET /healthz for liveness.
Bearer auth on every request: Authorization: Bearer <token> where <token> is one of the values in WAFLE_MCP_TOKENS. The MCP server refuses all traffic if no tokens are configured.
Connect Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"wafle": {
"command": "node",
"args": ["/absolute/path/to/wafle-mcp/dist/index.js", "--transport=stdio"],
"env": {
"WAFLE_API_KEY": "your-wafle-admin-key",
"WAFLE_API_URL": "https://wafle.click/wp-json/waffle/v1",
"LOG_LEVEL": "info"
}
}
}
}
Restart Claude Desktop. You should see "wafle" with 63 tools in the /mcp panel.
There is also an example at examples/claude-desktop-config.json.
Connect Claude Code
Project-scoped: drop a file at <repo>/.mcp.json:
{
"mcpServers": {
"wafle": {
"type": "stdio",
"command": "node",
"args": ["/absolute/path/to/wafle-mcp/dist/index.js", "--transport=stdio"],
"env": { "WAFLE_API_KEY": "your-key" }
}
}
}
User-scoped: append to ~/.claude/.mcp.json (same shape).
For the deployed HTTP transport at mcp.wafle.click:
{
"mcpServers": {
"wafle": {
"type": "http",
"url": "https://mcp.wafle.click/mcp",
"headers": { "Authorization": "Bearer <bearer token>" }
}
}
}
Tool catalogue (68)
| Domain | Tools |
|---|---|
| auth | wafle_auth_me, wafle_auth_keys_list, wafle_auth_keys_create |
| stores | wafle_stores_list, _get, _create, _update, _settings_get, _settings_update |
| products | wafle_products_list, _search, _get, _create_manual, _update, _override, _sync_trigger, _sync_status |
| pricing | wafle_pricing_rules_list, _create, _update, _delete, wafle_pricing_compute_preview |
| orders | wafle_orders_list, _get, _create, _ship, _cancel, _refund, _add_note, _timeline |
| customers | wafle_customers_list, _get, _orders, _segments_list |
| gateways | wafle_gateways_list, _create, _update, _test, _delete |
| shipping | wafle_shipping_rates_quote, _carriers_status |
| coupons | wafle_coupons_list, _create, _update, _delete |
| abandoned | wafle_abandoned_list, _send_recovery |
| analytics | wafle_analytics_summary, _by_period |
| exports | wafle_exports_customers, _meta_audience, _google_ads |
| pixels | wafle_pixels_get, _set |
| system | wafle_system_health, _stores_health, _versions_list, _release_deploy, _release_rollback, _audit_query, _queue_stats, _queue_failed, _queue_retry |
| meta | wafle_resources_invalidate, wafle_prompts_list, wafle_products_sync_trigger_and_wait, wafle_csv_import, wafle_ads_sync_full |
Every tool has a markdown description with usage guidance, a Zod schema with .describe()-annotated fields, scope requirements, and readOnlyHint/destructiveHint/idempotentHint annotations to help the LLM pick wisely.
The long-running tools (_sync_trigger_and_wait, _csv_import, _ads_sync_full) emit notifications/progress while polling — clients that support progress (Claude Desktop) show a live progress bar.
Resources (14)
MCP Resources expose read-only context the LLM can pull in without spending a tool call. Reference them with @<uri> in Claude Desktop or by URI in any client.
| URI | TTL | Purpose |
|---|---|---|
wafle://system/health |
30s | Backend health snapshot. |
wafle://system/scopes-catalog |
1h | Catalog of every API scope (domain, tier, label). |
wafle://docs/api-conventions |
1h | REST canonical shapes. |
wafle://docs/architecture |
1h | Architecture overview. |
wafle://stores |
60s | Lean list of every store. |
wafle://stores/{slug} |
60s | Full store snapshot (settings + 7d KPIs + abandoned + last order). |
wafle://stores/{slug}/orders/recent |
30s | Last 20 orders with summary fields. |
wafle://stores/{slug}/abandoned |
60s | Top 20 abandoned carts. |
wafle://stores/{slug}/analytics/7d |
5min | 7-day analytics. |
wafle://stores/{slug}/analytics/30d |
5min | 30-day analytics. |
wafle://stores/{slug}/products/sample |
2min | Top 20 products. |
wafle://stores/{slug}/email/segments |
60s | Customer segments. |
wafle://stores/{slug}/email/recent-campaigns |
60s | Last 10 campaigns. |
wafle://stores/{slug}/ads/connections |
5min | Meta/Google/TikTok connection status. |
Cache is in-memory per session, with TTL per resource. Manual invalidation: wafle_resources_invalidate { uri_pattern }. Long-running tools auto-invalidate the affected store.
See docs/RESOURCES.md for full details and examples.
Prompts (5)
Server-defined parametric workflows for the most common BATTO operations. The client surfaces a picker; pick → fill args → conversation starts with a fully-rendered plan.
| Name | Use case |
|---|---|
onboarding_tienda_nueva |
End-to-end onboarding (store + MP + catalog + frontend API key). |
pedido_enviar |
Mark order shipped with tracking + customer email. |
segmentar_y_campania |
Build segment + create email campaign (immediate or scheduled). |
conectar_meta_y_sync |
Connect Meta to a store + sync catalog to Commerce Manager. |
debug_orden_fallida |
Diagnose failed payment + optionally retry via secondary gateway. |
See docs/PROMPTS.md for argument schemas and example invocations.
Conversational examples
See examples/prompts/:
- 01-onboarding-tienda-nueva.md — onboarding a new client end to end.
- 02-pedido-enviar.md — single-shot logistics action.
- 03-segmentar-y-campania.md — segment + campaign.
- 04-conectar-meta-y-sync.md — connect Meta + catalog sync.
- 05-debug-orden-fallida.md — payment failure diagnosis.
The 5 prompts above are converted to server-defined MCP prompts in src/prompts/.
Scopes
Each tool declares a scope (e.g. orders:write, gateways:admin). At startup the server calls wafle_auth_me to retrieve the granted scopes; if wafle's /auth/me returns type=master we grant ALL_SCOPES. If it returns neither scopes nor a known type, the server runs in warn-mode — scope checks are skipped and a startup warning is logged.
The full matrix lives in src/auth/scopes.ts.
Architecture
See ARCHITECTURE.md. TL;DR: thin TypeScript wrapper over wafle REST, Fastify for HTTP, the official @modelcontextprotocol/sdk, Zod for validation, pino for logs (stderr only — required for stdio).
Deploy
docker compose up -d
or with the systemd unit at deploy/systemd/wafle-mcp.service. The repo includes Dockerfile (multistage, alpine runtime) and a sample nginx config for mcp.wafle.click.
Troubleshooting
401 waffle_unauthorized:WAFLE_API_KEYis missing or wrong. Get the master key from the waffle container at/root/.waffle-creds.txt.- Claude Desktop says no tools: confirm the path in
claude_desktop_config.jsonis absolute anddist/index.jsexists. Runnode dist/index.js --transport=stdioin a terminal — if it doesn't start, fix the env first. - Stdout corruption: don't
console.loganywhere. All logs must go viagetLogger()(which writes to stderr). - HTTP transport refuses traffic: set
WAFLE_MCP_TOKENSand passAuthorization: Bearer <token>. - Wafle says
forbidden, invalid master key: some endpoints (e.g./system/versions) check a different key. Verify withcurl -H "X-Wafle-Admin-Key: $KEY" https://wafle.click/wp-json/waffle/v1/auth/mefirst.
Development
npm run dev # tsx-watch, stdio
npm run dev:http # tsx-watch, http
npm test # vitest
npm run test:coverage
npm run lint
npm run typecheck
License
MIT — see LICENSE.
Recommended Servers
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.
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.
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.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
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.
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.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
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.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.