yampi-mcp

yampi-mcp

An MCP server that lets you manage a Yampi e-commerce store through natural language, enabling operations like looking up orders, creating products, adjusting stock, and building coupons and offers.

Category
访问服务器

README

<p align="center"> <img src="assets/yampi-icon.png" alt="Yampi" height="88"> </p>

<h1 align="center">yampi-mcp</h1>

<p align="center"> <a href="https://github.com/Eduardo-Orsi/yampi-mcp/actions/workflows/ci.yml"><img src="https://github.com/Eduardo-Orsi/yampi-mcp/actions/workflows/ci.yml/badge.svg" alt="CI"></a> <a href="https://workers.cloudflare.com"><img src="https://img.shields.io/badge/Cloudflare_Workers-F38020?logo=cloudflare&logoColor=white" alt="Cloudflare Workers"></a> <a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-2026--07--28-black" alt="MCP"></a> <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue" alt="MIT"></a> </p>

An MCP server that lets you talk to your Yampi store from Claude — look up orders, create products, adjust stock, build coupons and offers.

Every merchant hosts their own copy on Cloudflare. This is not a service: nobody holds your credentials but you. Unofficial, and not affiliated with Yampi.

How it works

A Yampi credential belongs to the user, not the store: if you run four stores under one login, all four show up. You connect once and pick the store on each command.

Setup

You need a Cloudflare account (the free plan is enough) and Node installed.

git clone https://github.com/Eduardo-Orsi/yampi-mcp && cd yampi-mcp
npm install
cp wrangler.example.jsonc wrangler.jsonc
npx wrangler kv namespace create OAUTH_KV   # paste the returned id into wrangler.jsonc
npx wrangler deploy

In your Claude client (claude.ai, Desktop or Code), add a custom connector pointing at https://yampi-mcp.<your-subdomain>.workers.dev/mcp.

On connect, a screen asks for your User-Token and User-Secret-Key. You'll find them in the Yampi dashboard under Perfil › Credenciais de API (Profile › API Credentials). That's it — there's no password to create.

Using it

Once connected, it's plain conversation:

"How many paid orders did store X get between June 1st and 15th?" "Create a product called Black T-Shirt, brand Acme, SKU TS-BLACK-M, R$ 79.90, 20 in stock." "SKU TS-BLACK-M is priced wrong — change it to R$ 89.90 and drop stock to 5." "Which carts were abandoned this week and what do they add up to?" "Create a 15% coupon valid through month end, R$ 100 minimum, 50 uses."

With more than one store on the account, say which one — the tools require it explicitly so nothing gets written to the wrong store.

What it does

Tool What it does
describe_store Stores, order statuses, categories and brands — the map, so the model stops guessing ids
search_orders Orders filtered by status, period and free text
get_order One order with items, customer, payments, address and history
search_products Catalog with SKUs, prices and images
get_product One product with variations, stock, brand and categories
search_customers Customers and addresses
customer_history A customer and all their orders
abandoned_carts Carts that never became orders
create_product Creates a product with its SKUs
update_product Edits product fields
manage_sku Creates a SKU, or updates price and stock
create_coupon Discount coupon
advance_order_status ⚠️ Moves an order to another status
add_order_comment ⚠️ Internal note on an order
manage_offers Cashback, order bump, upsell and free gift

⚠️ Not validated against the live API. The other thirteen were run end to end against a real store — creating a product, changing a price, writing stock, issuing a coupon — and their field names came out of that process corrected. These two need an existing order, and the test store had none. The endpoints are right; the request body comes from the documentation, which turned out to be missing at least one required field in every one of the other five writes. Expect a 422 on first call — the message will name the missing field.

What it deliberately does not do

It does not cancel orders, refund purchases, or switch payment gateways. Not a feature behind an environment variable: the code does not exist. These are the irreversible operations in the API, and neither Claude Desktop nor claude.ai supports elicitation — meaning the server has no way to genuinely ask for confirmation. Absence is the only guarantee that doesn't depend on someone paying attention.

The ban is enforced in two places, both covered by tests: on the status alias (tools/write.ts) and at the seam every request passes through (yampi.ts). Rationale in docs/adr/0002.

Order tracking is also out: Yampi caps that route at 3 requests per hour, which makes the tool useless in practice — two calls and the agent is stuck for 20 minutes.

Your credentials

  • Stored encrypted (AES-GCM) in the OAuth grant props, inside your KV.
  • The key encrypting them is wrapped by a key derived from the access token, and KV only holds the token's hash. A KV leak alone does not open the credentials.
  • Claude never receives them: it only ever sees an opaque token.
  • Revoking means deleting the grant — other connections keep working.

/authorize is public and validates credentials, which technically makes it an oracle for testing stolen keys. Hence the limit of 5 attempts per IP per minute.

To restrict the instance to specific stores:

npx wrangler secret put ALLOWED_STORES   # e.g. my-store,other-store

API limits

Yampi limits per route per minute: 30 req/min on products and SKUs, 120 on order reads, 30 on writes, 60 in general. The server uses include= to pull relationships in a single call instead of N+1, reads X-RateLimit-Remaining off every response, and warns the model when the quota is running out — rather than letting it find out through a 429.

When something goes wrong

403 on everything, reads included. The store is active: false in the Yampi dashboard. Inactive stores reject every route. Reactivate it, then reconnect the connector.

422 on a write. The message names the exact field Yampi rejected — the server forwards the whole errors object. Claude usually corrects itself on the next attempt.

"Grant without credential". The grant lost its props. Remove the connector and add it again.

Switching credentials. Just reconnect: a new grant replaces the old one. To cut access without reconnecting, delete the KV namespace.

A store is missing from the list. Either it's inactive, or the credential doesn't reach it. Run describe_store to see what the server can see.

Yampi API quirks

Found by testing against the live API. All of them can burn hours, and none are clear from the documentation:

  • Filters need array syntax. ?status_id=4 is silently ignored and returns the entire dataset; ?status_id[]=4 filters. Same for active[]. A filter that doesn't filter is worse than no filter: the agent summarizes 55,000 orders believing it saw July's.
  • Dates use a bespoke format: ?date=created_at:2026-06-01|2026-06-30. Anything else returns 500 or is ignored.
  • filters[...] does not filter. It only switches the response to scroll_id pagination.
  • /auth/me is POST, not GET, and returns every store on the credential — because the credential belongs to the user, not the store.
  • Order include has a closed enum: items, customer, marketplace, status, statuses, shipping_address, promocode, transactions, comments, files, discounts, seller, labels. There is no payments.
  • GET responses are cached for 30 minutes on Yampi's side. In an agent context that lies: create a product, ask to read it back, and you get the previous state. This server sends ?skipCache=true on every read.
  • Stock is not a SKU field. quantity on a SKU is always null — including on the real SKUs of a live store. Stock lives in /logistics/stocks (the stock location) joined to the SKU at /catalog/skus/{id}/stocks. And stock_id is not the id from /logistics/warehouses, which is a different resource entirely.
  • Coupon discount_type accepts only p or v, not percentage/fixed.
  • Coupon dates require Y-m-d H:i:s. Date alone returns 422.
  • PUT /catalog/skus/{id} requires product_id and price_cost even for a partial update.
  • Creating a product requires simple, brand_id and skus.*.blocked_sale, none of them obvious.
  • A store with active: false returns 403 on everything, reads included. This server filters those stores out at connect time, so the model is never offered an option that can only fail.
  • 422 responses carry an errors object naming the exact field that failed. Worth forwarding to the model instead of showing only the status code — it's what lets it correct itself.

Development

npm test              # 32 unit tests, no network
npm run typecheck
npm run dev           # wrangler dev

Testing against your own store

The unit suite uses a fake fetch and proves the server's logic. It cannot notice Yampi changing an endpoint, a field name or a filter syntax — and that happened repeatedly while this project was built. That other half is covered by an integration suite that hits the live API, read-only, creating and changing nothing:

cp .env.example .env    # fill in the alias and credentials of YOUR store
npm run test:integration

It checks that store discovery works, that status aliases exist, that filtering by status actually filters, that the date format is accepted, that include expands relationships, and that quota headers arrive. If one fails, the API changed and the server will start lying before it starts breaking.

The architecture has one rule: no tool speaks HTTP. Everything goes through src/yampi.ts. That's what makes the "does not reach the banned routes" promise auditable — the entire surface fits in one file.

Project vocabulary in CONTEXT.md. Decisions in docs/adr/.

Known limitations

  • No order tracking (Yampi's 3 req/h cap makes it unusable).
  • No banners, free shipping rules, progressive discounts or combos.
  • advance_order_status and add_order_comment were never run against the live API.
  • Stock is written to the store's first registered stock location. Anyone using multiple locations needs to adjust defaultStockId() in src/tools/write.ts.

Contributing

Pull requests are welcome. Fork it, open a PR against main, and CI runs typecheck and the unit tests. For anything larger than a bug fix, open an issue first.

One thing will not be merged regardless of patch quality: anything that cancels an order, refunds a purchase, or switches payment gateway, including indirect routes. That absence is the point of the project — reasoning in ADR 0002.

Details in CONTRIBUTING.md. Found a security issue? Do not open a public issue — see SECURITY.md.

License

MIT — see LICENSE.

The Yampi logo in assets/ is Yampi's trademark, used here only to identify which platform this server talks to. It is not covered by the MIT license and this project is not affiliated with or endorsed by Yampi.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选