Lightspeed X-Series MCP
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Lightspeed X-Series MCP How did each store do yesterday?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Ask Claude about your Lightspeed Retail stores. Sales, stock, products and customers, through an MCP server that cannot write to your account.
Get started · What you can ask · Tools · Read-only · Hosting for a team · Security
Lightspeed Retail X-Series (formerly Vend) holds a store's whole day: every sale, every stock movement, every customer. This server puts that data in front of Claude, or any other MCP client, so you can ask a question in plain words and get the number back: "How did each store do yesterday?", "Where do we still have the large one in navy?", "Which promotions start next week?"
It is read-only in code. Lightspeed's personal tokens carry no scopes, so a token that can read your sales can also void them. This server never asks the API to change anything, and a test fails the build if a write ever creeps in.
What you can ask
Claude picks the tools; you just ask. Things that work well:
Revenue: "Sales by outlet for last week, excluding tax." · "Daily takings in March as a table." · "Top 20 products by units this month."
Stock: "How many of SKU 1234-NV are on hand, by store?" · "Show every size and color of the Weekender with stock per outlet." · "What's low at the warehouse?"
Sales lookups: "Find invoice 10482." · "Which sales did Sam ring up on Saturday?" · "Show parked and on-account sales from this week."
Customers and loyalty: "Look up jane@example.com: spend this year, store credit, address." · "Check gift card 6006 1234 5678."
Merchandising: "Which promotions run this month and which products do they cover?" · "Prices in the wholesale price book for the Weekender."
Operations: "Open stock transfers between stores." · "Purchase orders still waiting on delivery." · "Payments by type on register 2 today."
Dates are local store dates: "yesterday" means yesterday in the retailer's time zone, not UTC, and the server converts for you.
Related MCP server: VortexIQ MCP Connector
Features
35 tools covering sales, revenue summaries, products, variant families, inventory, transfers, purchase orders, customers, store credit, gift cards, promotions and price books, plus a guarded
api_getfor anything else.Answers, not just endpoints.
sales_summarygroups revenue by outlet, day, staff member or product in one call.search_productsmatches any mix of name, variant, SKU and barcode words.get_inventoryrolls a whole variant family up into one stock table.Readable output. Outlet, staff and product ids come back with their names, and sales come back as compact rows unless you ask for the full object.
Sized for a chat window. Variant families, price books and promotion product lists can run past a megabyte. Those tools return summaries by default, with
full=Trueor a filter when you need everything.Local time done right. Date ranges are converted from the retailer's time zone to the UTC bounds the API wants, so day totals match the Lightspeed reports.
Polite to the API. Reference data is cached (outlets and staff for an hour, the product catalog for 15 minutes), and a
429is retried once afterRetry-After.Standard library HTTP. The only dependency is the MCP SDK.
Get started
1. Make a personal token
In Lightspeed Retail, open Setup → Personal tokens and add one. Use an account that can see every outlet you want to query, because the token sees exactly what that user sees. Lightspeed lists personal tokens as a plan feature, so check your plan if the menu is missing.
Your domain prefix is the first part of your store address:
mystore for mystore.retail.lightspeed.app.
2. Add it to your MCP client
The server runs over stdio. With uv
installed, uvx fetches and runs it without a separate install step.
Claude Code
claude mcp add lightspeed \
-e LIGHTSPEED_DOMAIN_PREFIX=mystore \
-e LIGHTSPEED_TOKEN=your-token \
-- uvx --from git+https://github.com/davidboulay/lightspeed-xseries-mcp lightspeed-xseries-mcpClaude Desktop (claude_desktop_config.json), Cursor and other
JSON-configured clients:
{
"mcpServers": {
"lightspeed": {
"command": "uvx",
"args": ["--from", "git+https://github.com/davidboulay/lightspeed-xseries-mcp", "lightspeed-xseries-mcp"],
"env": {
"LIGHTSPEED_DOMAIN_PREFIX": "mystore",
"LIGHTSPEED_TOKEN": "your-token"
}
}
}
}Codex
codex mcp add lightspeed \
--env LIGHTSPEED_DOMAIN_PREFIX=mystore --env LIGHTSPEED_TOKEN=your-token \
-- uvx --from git+https://github.com/davidboulay/lightspeed-xseries-mcp lightspeed-xseries-mcpThen ask "What's the retailer name and currency on Lightspeed?". If you get an answer, you're connected.
The token sits in your client's config file in plain text. That's normal for stdio MCP servers, but it means the file deserves the same care as the token. To share access without handing the token around, see Hosting for a team.
Several stores
One process serves one retailer account. For several, add the server once per
store under different names (lightspeed-us, lightspeed-uk, …), each with its
own prefix and token. Claude sees them as separate tool sets and can compare
them.
Configuration
Variable | Required | Default | |
| yes |
| |
| yes | personal token from Setup → Personal tokens | |
| no |
| X-Series date-based API version. Each version is supported for about a year, so bump it yearly. |
Tools
Area | Tools |
Account |
|
Products |
|
Stock |
|
Sales |
|
Customers |
|
Pricing |
|
Anything else |
|
The ones to know:
sales_summary: revenue for a local date range grouped byoutlet,day,user,productornone, with totals excluding and including tax, the average sale and, per product, units. It counts closed sales by default, includes returns as negatives, and stops at 20,000 sales per call, settingtruncatedwhen you should narrow the range.search_products: every word must match somewhere in the name, variant, SKU or barcodes, so "navy large weekender" finds the right variant. Inactive products are hidden unless you ask.get_inventory: on-hand stock per outlet for one variant, or withinclude_variants=Truefor the whole family, totaled per variant.
Read-only by construction
A personal token can do anything its user can, including deleting products and voiding sales. So read-only can't be a setting. It has to be the code:
Every tool sends
GET. The two exceptions are/inventoryand/inventory_levels, which are reads that take their filters as aPOSTbody.There are no write tools. Every tool carries MCP's
readOnlyHint, so clients that ask before running non-read tools never need to ask.api_getaccepts plain resource paths only (no.., no query string in the path) and refuses integration plumbing that can carry credentials or partner data:/webhooks,/partner,/channel_requests,/security_events.tests/test_read_only.pyparses the source and fails if any request uses a method other thanGETor one of the two read-onlyPOSTs. It also checks every tool's annotations and theapi_getdeny list. CI runs it on every push.
Read-only still means readable: whoever can call these tools can see customer contact details, staff names, and cost fields such as average cost and supplier price. Give access accordingly.
Hosting for a team
stdio keeps the token on one machine. To give colleagues access without giving
them the token, run the server on a host you control behind an
authenticating proxy that speaks remote MCP. For example,
mcp-auth-proxy adds OAuth sign-in
(Google, GitHub, …) with a domain allow-list:
mcp-auth-proxy --external-url https://lightspeed.example.com \
--google-client-id … --google-client-secret … --google-allowed-workspaces example.com \
-- lightspeed-xseries-mcpPeople then add https://lightspeed.example.com/mcp as a custom connector
(claude.ai → Settings → Connectors), sign in with their work account, and never
see the token.
Never expose the server without authentication. Don't publish a bare HTTP
port or a tunnel to it: anyone who finds the URL could read your sales and
customers. Pin dependencies when you deploy. requirements.lock
is a full, tested freeze.
API quirks it handles
Found against live data on API version 2026-10, and worth knowing if you call
api_get yourself:
Two sale shapes.
/search?type=salesreturns the older 2.x shape (total_price,total_tax,sale_date,outlet_id, lineprice_total), while/sales/{id}returns the date-based one (totals,source). The server reads both.Product names live on the family. Fetch products with
includes[]=families.variant_namealready contains the product name.Family stock.
/inventorywithvariants: truewants the root product id and returns nothing for a variant id, so family stock goes through/inventory_levelswithproduct_ids./product_categoriesnests its list underdata.categories./promotions/searchdate filters mean "starts after". Usescope=current_upcomingfor what's running now.Retailer settings.
currencyis an object (usecurrency.code), and the time zone may come back astimezoneortime_zone.
Limitations
Read-only, permanently. There's no plan to add writes. Use Lightspeed itself for changes.
Personal tokens only. OAuth would need a registered Lightspeed developer app. A token is simpler for a single retailer, but it's tied to one user's access.
Big ranges are capped.
sales_summarystops at 20,000 sales andsearch_salesat itslimit. Split long periods by month.Caches are per process. A product added in the last 15 minutes may not appear in
search_productsyet.get_productby SKU is always live.One
429retry. Under heavy load a call can still fail with Lightspeed's rate-limit error. Ask again a moment later.
Project layout
src/lightspeed_xseries_mcp/
server.py the whole server: HTTP client, caches, 35 tools
tests/
test_read_only.py read-only guarantee, no network or token needed
requirements.lock full tested dependency freeze, for deployments
docs/icon.svgDevelopment
git clone https://github.com/davidboulay/lightspeed-xseries-mcp.git && cd lightspeed-xseries-mcp
uv run --with-requirements requirements.lock --with-editable . python -m unittest discover -s tests -vTo try it against a real store, run it under the MCP Inspector:
LIGHTSPEED_DOMAIN_PREFIX=mystore LIGHTSPEED_TOKEN=… \
npx @modelcontextprotocol/inspector uv run lightspeed-xseries-mcpWhen you add a tool, keep it a read. The test will tell you if it isn't.
License
MIT. An independent project, not affiliated with or endorsed by Lightspeed Commerce Inc. "Lightspeed" and "X-Series" are its trademarks.
Available Tools
35 toolsapi_getARead-onlyIdempotent
Read any other X-Series endpoint with GET, e.g. /shifts, /services, /serialnumbers, /auditlog_events, /customer_taxes, /product_images/{id}. path is relative to /api/ and must start with '/'. params are query parameters (lists repeat the key). Reference: https://x-series-api.lightspeedhq.com/llms.txt
Inputs: path (required), params. Returns: result
find_gift_cardBRead-onlyIdempotent
A gift card by number, with balance, status and transactions.
Inputs: card_number (required). Returns: result
get_consignmentBRead-onlyIdempotent
One consignment with its totals and (by default) its product lines.
Inputs: consignment_id (required), include_products. Returns: result
get_customerARead-onlyIdempotent
One customer (contact details, group, loyalty balance, year-to-date spend) with addresses and, by default, store credit balance.
Inputs: customer_id (required), include_store_credit. Returns: result
get_customer_group_customersCRead-onlyIdempotent
Customers in a customer group (version-cursor paged).
Inputs: customer_group_id (required), after, page_size. Returns: result
get_inventoryARead-onlyIdempotent
Stock on hand per outlet for one product (variant), with reorder point/amount and average cost. include_variants=True covers its whole family (every colour and size; can be hundreds of rows). outlet filters by outlet name (substring). hide_zero drops rows with nothing on hand.
Inputs: product_id (required), outlet, hide_zero, include_variants. Returns: result
get_price_book_productsARead-onlyIdempotent
Prices in a price book (tax-exclusive): name, product_id, price, tax, markup. Filter by product_ids or query (name substring); capped at limit.
Inputs: price_book_id (required), limit, query, product_ids. Returns: result
get_productBRead-onlyIdempotent
One product with its family, brand, category, supplier, tags and variant attributes. Pass product_id, or sku to look it up by SKU.
Inputs: sku, product_id. Returns: result
get_product_familyARead-onlyIdempotent
The variant family a product belongs to (every colour/size of one item): family details, brand/category/tag names and one compact line per variant (id, variant name, SKU, retail price, active). full=True returns the raw API object (can be very large).
Inputs: product_id (required), full. Returns: result
get_promotionARead-onlyIdempotent
One promotion with its rules, promo codes and applicable products (counts plus the first product_limit of each product list).
Inputs: promotion_id (required), product_limit, include_products. Returns: result
get_purchase_orderBRead-onlyIdempotent
One purchase order with its line items and deliveries.
Inputs: purchase_order_id (required), include_lines. Returns: result
get_register_payments_summaryCRead-onlyIdempotent
Payment totals by payment type for a register's current/last session.
Inputs: register_id (required). Returns: result
get_retailerARead-onlyIdempotent
Retailer account details: name, currency, time zone, settings. Call this first if you need the currency or local time zone.
Inputs: no named parameters. Returns: result
get_saleBRead-onlyIdempotent
One sale in full: line items, payments, taxes, customer, totals.
Inputs: sale_id (required). Returns: result
get_store_credit_reportARead-onlyIdempotent
Store credit report across all customers.
Inputs: no named parameters. Returns: result
get_user_sale_totalsCRead-onlyIdempotent
A staff user's sales totals (as reported by Lightspeed).
Inputs: user_id (required). Returns: result
list_consignmentsARead-onlyIdempotent
Stock orders/transfers/returns/stocktakes. type: SUPPLIER, OUTLET (transfer), RETURN or STOCKTAKE. status e.g. OPEN, SENT, DISPATCHED, RECEIVED, CANCELLED, STOCKTAKE_IN_PROGRESS. Version-cursor paged.
Inputs: type, after, status, outlet_id, +1 more. Returns: result
list_fulfillmentsBRead-onlyIdempotent
Fulfillments (pickups/deliveries/shipping) with state, optionally by outlet or sale.
Inputs: sale_id, outlet_id, page_size, page_number, +2 more. Returns: result
list_gift_cardsCRead-onlyIdempotent
Gift cards, optionally by status (e.g. ACTIVE, VOIDED).
Inputs: before, status, page_size. Returns: result
list_inventory_levelsARead-onlyIdempotent
Inventory levels across the catalog, optionally filtered by outlets and products (on hand, available, allocated, on order where provided). Page with offset/size (size max 10000). Use for 'what's in stock at outlet X' or low-stock reviews.
Inputs: size, offset, outlet_ids, product_ids, +3 more. Returns: result
list_outletsARead-onlyIdempotent
All outlets (stores/warehouses) with id, name, address and time zone.
Inputs: no named parameters. Returns: result
list_payment_typesBRead-onlyIdempotent
Payment types configured (cash, card, gift card, store credit, ...).
Inputs: no named parameters. Returns: result
list_productsARead-onlyIdempotent
Raw product collection page (version cursor). Pass the previous response's
version.max as after for the next page. Prefer search_products to find items.
Inputs: after, page_size. Returns: result
list_promotionsCRead-onlyIdempotent
Promotions. scope: current_upcoming (default), past or all. name matches part of the promotion name; outlet_ids keeps those linked to the given outlets.
Inputs: name, scope, page_size, outlet_ids. Returns: result
list_purchase_ordersCRead-onlyIdempotent
Purchase orders (version-cursor paged).
Inputs: after, page_size. Returns: result
list_quotesCRead-onlyIdempotent
Quotes (sales:read), cursor paged with after.
Inputs: after, limit. Returns: result
list_referenceARead-onlyIdempotent
List a reference collection. kind is one of: brands, suppliers, tags, product_categories, product_types, variant_attributes, customer_groups, price_books, outlet_taxes, button_layouts.
Inputs: kind (required). Returns: result
list_registersBRead-onlyIdempotent
All registers (tills) with their outlet and open/closed state.
Inputs: no named parameters. Returns: result
list_stock_adjustmentsCRead-onlyIdempotent
Stock adjustments (manual corrections, damages, ...), version-cursor paged.
Inputs: after, page_size. Returns: result
list_taxesCRead-onlyIdempotent
Sales taxes configured, with rates.
Inputs: no named parameters. Returns: result
list_usersARead-onlyIdempotent
Staff users (id, name, account type, outlets). Use ids with get_user_sale_totals or search_sales(user_id=...).
Inputs: no named parameters. Returns: result
sales_summaryARead-onlyIdempotent
Revenue totals for a local date range (YYYY-MM-DD, inclusive; default today).
group_by: outlet, day, user, product, or none. Counts closed sales by default
(pass states to change). Returns sales count, totals excl/incl tax, tax, units
(product grouping) and average sale value. Returns are included as negatives.
Capped at 20,000 sales per call: narrow the range if truncated is true.
Inputs: top, states, date_to, group_by, +2 more. Returns: result
search_customersCRead-onlyIdempotent
Find customers by email, name, company, phone or customer code.
Inputs: email, limit, phone, mobile, +4 more. Returns: result
search_productsARead-onlyIdempotent
Find products by name, variant name, SKU or barcode (case-insensitive; every word must match). Returns product_id, name, variant, SKU and retail prices. Follow with get_inventory(product_id) for stock or get_product for full detail.
Inputs: query (required), limit, include_inactive. Returns: result
search_salesARead-onlyIdempotent
Find sales. date_from/date_to are local store dates YYYY-MM-DD, inclusive (default: today). states: closed, pending (on account / layby), parked, voided (default: all). Compact rows by default; set compact=False for full line items, or call get_sale(id).
Inputs: limit, states, compact, date_to, +7 more. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 35 tools
Most tools target a distinct resource/action, but api_get is a catch-all GET that overlaps with every specific read tool. Product and inventory tools (search_products, list_products, get_product, get_inventory, list_inventory_levels) also require careful selection, though descriptions help.
The set is mostly consistent snake_case verb_noun naming (list_*, get_*, search_*). Minor deviations like api_get, sales_summary, and find_gift_card keep it from being perfectly uniform.
35 tools is heavy for an MCP surface, even for a broad retail API. Many read/list/get variants could be consolidated or handled through fewer well-scoped tools plus the generic api_get.
Read coverage is broad across products, inventory, sales, customers, gift cards, and promotions, but the entire write/update/delete side is absent because every tool is a GET-style read. This is a notable gap for full lifecycle coverage.
Related MCP Connectors
Ask about a Lightspeed Retail (R-Series) shop's sales, stock, customers, and work orders.
Read-only Recharge store analytics: metrics, dimensions, and governed queries in plain language.
Ask your databases questions in plain English, run read-only SQL and set up data alerts.
Read Lightspeed X-Series products, stock, sales, customers, stock orders; one-call sales reports.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables e-commerce clients to query their own analytics data in plain English with strict tenant isolation enforced by the database.MIT
- AlicenseNot gradedqualityDmaintenanceEnables read-only access to VortexIQ ecommerce AI data, including store audits, KPIs, alerts, brand DNA, and reports, through natural language queries.MIT
- AlicenseAqualityBmaintenanceEnables read-only access to Lightspeed X retail data (sales, inventory, products, customers) with aggregated reporting on revenue, COGS, profit, and other metrics for MCP clients like Claude.1MIT
- AlicenseNot gradedqualityCmaintenanceEnables natural language, read-only access to your Cakto payment gateway data, including orders, revenue analytics, subscriptions, churn, customers, and products.MIT