Final POS – Custom POS Builder
REMOTE · AI.FINALPOS.COM
Build custom POS apps with any AI agent: checkout flows, kiosks, payment terminals. Plus store ops.
Available components
Last scanned (14 hours ago)
Scores higher than 92% of remote servers
Reachable. Last reached 7 Nov 2026.
Category E-commerce
How this component scores in each security and reliability category. Every signal is checked automatically against the live server, and we only credit what we can confirm. How we score → Why this is hard to score →
Endpoint Security94
- The endpoint's TLS certificate is valid, in date, and uses a strong key. View diagnostics → Pass
- Authorisation is enforced on tool calls, advertised via RFC 9728 protected-resource metadata. Discovery is public, which costs nothing: no tool can be invoked without a token. View diagnostics → Pass
- HTTPS is enforced; there's no plaintext access path. View diagnostics → Pass
- The HSTS (Strict-Transport-Security) header is present. View diagnostics → Pass
- DNSSEC check failed: this domain isn't protected by DNSSEC. See how to fix → View diagnostics → Fail
- The authorisation server offers only Dynamic Client Registration (RFC 7591), which MCP 2026-07-28 deprecated in favour of Client ID Metadata Documents. View diagnostics → Partial
Transport & Reachability100
- Verified streamable-http transport via a live MCP handshake. View diagnostics → Pass
Schema Quality & AI Usability65
- AI-judged instruction clarity (excellent).Pass
- Context-footprint check failed: tool/resource definitions use about 36159 tokens (~204/item across 177 items; 177 tools + 0 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management67
- Stability observed for 20 of 30 days with no destabilising changes; credit accrues until the full window elapses.Partial
Tool Coverage92
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 77% of tool parameters carry a description.Partial
Tool Safety98
- No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
- 24 of 26 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "hub_get_refunds" implies "refund" and declares readOnlyHint instead, contradicting what its own name says it does. See how to fix → Partial
- An AI judge read all 178 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
- Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
Checks we ran (13)
- TLS certificate
- Authorization
- HTTPS
- HSTS header
- DNSSEC
- Transport
- Schema quality
- Stability
- Tool coverage
- Injection markers
- Destructive annotations
- Judged manipulation
- MCP protocol
Does not apply to this hosted endpoint (2)
- Supply Chain SecurityScored on package channels only. This is a hosted endpoint, so there is nothing to install or inspect.
- Provenance & TransparencyScored on package channels only. This is a hosted endpoint, so there is nothing to install or inspect.
The score does not measure everything. What the score does not measure →
Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 7 Nov 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 63 to 67. That category is still filling its 30-day observation window: 19 days of observed history at the previous scan, 20 at this one. The score rises as the window fills, whether or not the server changes.
- 6 Nov 26 0
- We updated how we score, so this day's move reflects our rubric, not a change to the server See what changed → functional
- 4 Nov 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 53 to 57. That category is still filling its 30-day observation window: 16 days of observed history at the previous scan, 17 at this one. The score rises as the window fills, whether or not the server changes.
- 2 Nov 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 47 to 50. That category is still filling its 30-day observation window: 14 days of observed history at the previous scan, 15 at this one. The score rises as the window fills, whether or not the server changes.
- 31 Oct 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 40 to 43. That category is still filling its 30-day observation window: 12 days of observed history at the previous scan, 13 at this one. The score rises as the window fills, whether or not the server changes.
- 29 Oct 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 33 to 37. That category is still filling its 30-day observation window: 10 days of observed history at the previous scan, 11 at this one. The score rises as the window fills, whether or not the server changes.
- 27 Oct 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 27 to 30. That category is still filling its 30-day observation window: 8 days of observed history at the previous scan, 9 at this one. The score rises as the window fills, whether or not the server changes.
- 25 Oct 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 20 to 23. That category is still filling its 30-day observation window: 6 days of observed history at the previous scan, 7 at this one. The score rises as the window fills, whether or not the server changes.
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 7 Nov 2026 · Probed https://ai.finalpos.com/mcp
TLS valid
Negotiated TLS 1.3 with TLS_AES_128_GCM_SHA256 .
| Subject | Issuer | Valid from | Valid until | Key | Signature | Serial |
|---|---|---|---|---|---|---|
| CN=ai.finalpos.com | CN=WR3,O=Google Trust Services,C=US | 29 Oct 2026 | 27 Jan 2027 | RSA 2048 | SHA256-RSA | 7400e20ba493be190946bf1402d60490 |
| SANs: ai.finalpos.com | ||||||
| CN=WR3,O=Google Trust Services,C=US (CA) | CN=GTS Root R1,O=Google Trust Services LLC,C=US | 13 Dec 2023 | 20 Feb 2029 | RSA 2048 | SHA256-RSA | 7ff005a91568d63abc22861684aa4b5a |
| CN=GTS Root R1,O=Google Trust Services LLC,C=US (CA) | CN=GlobalSign Root CA,OU=Root CA,O=GlobalSign nv-sa,C=BE | 19 Jun 2020 | 28 Jan 2028 | RSA 4096 | SHA256-RSA | 77bd0d6cdb36f91aea210fc4f058d30d |
Background: What to check on a remote MCP endpoint →
DNSSEC insecure
Validation of ai.finalpos.com. — Not signed
| Zone | DS | Keys | Algorithms | Outcome |
|---|---|---|---|---|
| . | Trust anchor | 20326, 38696 | 8, 8 | Verified |
| com. | DS published | 19718 | 13 | Verified |
| finalpos.com. | No DS | Unsigned (proven) parent-signed NSEC/NSEC3 proves an unsigned delegation |
Authentication Enforced and verified
The endpoint asked for a token and published valid RFC 9728 metadata describing how to get one.
| Result | Enforced and verified |
|---|---|
| Enforced | On tool calls |
| HTTP status | 200 |
WWW-Authenticate challenge Bearer error="invalid_token", error_description="Sign in with Final through OAuth, or create a connection key at https://ai.finalpos.com/connections.", resource_metadata="https://ai.finalpos.com/.well-known/oauth-protected-resource/mcp"
Bearer error="invalid_token", error_description="Sign in with Final through OAuth, or create a connection key at https://ai.finalpos.com/connections.", resource_metadata="https://ai.finalpos.com/.well-known/oauth-protected-resource/mcp" | Header | Value |
|---|---|
| strict-transport-security | max-age=63072000; includeSubDomains; preload |
| content-security-policy | frame-ancestors 'self' https://app.finalpos.com |
| x-content-type-options | nosniff |
| referrer-policy | strict-origin-when-cross-origin |
Protected resource metadata
| Document | https://ai.finalpos.com/.well-known/oauth-protected-resource/mcp |
|---|---|
| Retrieved | Yes |
| Resource | https://ai.finalpos.com/mcp |
| Authorisation server | https://ai.finalpos.com |
Background: How OAuth 2.1 works in the 2026 MCP spec →
Transports 2 probes
| Transport | URL | Outcome | Status | Location |
|---|---|---|---|---|
| streamable-http | https://ai.finalpos.com/mcp | Verified | 200 | |
| http (plaintext) | http://ai.finalpos.com/mcp | HTTPS enforced | 302 | https://ai.finalpos.com/mcp |
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →
hub_get_modifier Get modifier ~105
One modifier in full: its choices with prices and defaults, and every category it is linked to with that category’s own rules. Read this before editing — update_modifier REPLACES the whole choice list, so you need the current one to send back. Each choice carries price (formatted, for reading out) and priceAmount (plain, what the write tools accept back). Pass priceAmount, never price.
| Name | Type | Req | Description |
|---|---|---|---|
| modifierId | string | yes | Modifier id from get_modifiers |
No output schema declared.
No examples provided.
hub_get_modifiers List modifiers ~149
Every modifier the merchant has — the questions the till can ask about an item, with their choices and prices. Start here for anything about options, add-ons, extras, toppings, sizes, milk, cooking temperature or "how do I let them choose X". Each row says how it is answered: "Pick one" (radio), "Pick many" (checkboxes), "Pick many · qty" or "stepper per choice" (the last two count UNITS, so one choice taken three times counts as 3). usedIn is how many categories it is linked to — a modifier with usedIn 0 is invisible at the till no matter how well it is set up.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
hub_get_order Get order details ~212
Everything inside ONE order: line items with quantity, unit price and per-line tax, custom sales, gift-card and other non-revenue lines, the totals block, every payment taken (method, processor, card type and last 4, tip, change), every refund already made against it, plus the customer, outlet, station, employee and order notes. Answers 'what was in order 1042', 'how did they pay', 'was this one refunded', 'why is this total different'. Takes the order ID from get_orders — a receipt number will NOT work, search for it with get_orders first. READ-ONLY, and there is no sibling tool that writes: an order's status cannot be changed, an order cannot be cancelled or completed, and a refund cannot be created from this API — refunds are taken on the POS terminal against the original payment. Say that plainly instead of hunting for another tool.
| Name | Type | Req | Description |
|---|---|---|---|
| orderId | string | yes | Order id from get_orders — not a receipt number |
No output schema declared.
No examples provided.
hub_get_order_count Count orders ~116
How many orders the company has, in total or at given outlets — 'how many orders do we have', 'how busy is the King Street shop'. Cheap: it counts, it loads no order data. It counts EVERY order ever recorded and takes no date, status or source filter. For anything time-boxed or filtered, call get_orders and read its `matching` count, or use get_financial_summary for a period.
| Name | Type | Req | Description |
|---|---|---|---|
| outlets | array | – | Outlet ids to restrict to. Omit to count every outlet |
No output schema declared.
No examples provided.
hub_get_orders List orders ~509
Find orders — the same rows as Manage → Orders. Answers 'show me order 1042', 'what did we sell on Saturday', 'are there parked orders', 'orders rung up by Maria', 'orders over $500 at the King Street shop'. Filter by receipt number, status, source, employee, customer, product, date window, order total, outlet or station. Results are ALWAYS newest first — there is no other sorting, so a "biggest order" question needs the amount filter, not paging. Search matches the receipt number and the order id ONLY, never customer names or product names: for those, pass customerIds from get_customers or productIds from get_products. One row per order with its total; for what is INSIDE an order — line items, payments, refunds, notes — call get_order with the id from here. For how much the business took over a period use get_financial_summary or get_sales_breakdown instead of adding these rows up.
| Name | Type | Req | Description |
|---|---|---|---|
| customerIds | array | – | Customer ids from get_customers, not names |
| employeeIds | array | – | Employee ids — from get_employee_sales, not names |
| endDate | string | – | Only orders created at or before this ISO 8601 moment |
| maxTotal | number | – | Maximum order total in MINOR units (cents) |
| minTotal | number | – | Minimum order total in MINOR units (cents), e.g. 50000 for $500 |
| outlets | array | – | Outlet ids. Combined with stations by OR |
| page | integer | – | Page number, default 1 |
| pageSize | integer | – | Orders per page, default 20 |
| productIds | array | – | Product ids from get_products — matches orders containing them |
| search | string | – | Receipt number or order id. Does NOT match customer or product names |
| source | array | – | e.g. ["in-store"], ["woo-commerce"] |
| startDate | string | – | Only orders created at or after this ISO 8601 moment |
| stations | array | – | Station ids. Combined with outlets by OR |
| status | array | – | Final POS statuses: "completed", "refunded", "partial-refund", "parked". WooCommerce orders keep their own ("processing", "on-hold", "pending", "cancelled", "failed") — pass "other" to match every st… |
No output schema declared.
No examples provided.
hub_get_outlets List outlets ~207
Every point of sale (store, location, branch) the merchant has — the same list as Settings → Outlets. Answers 'how many stores do I have', 'what is the address of the King Street shop', 'what is that location’s phone number'. ALSO THE WAY TO TURN A STORE NAME INTO AN ID: the reports, inventory and station tools filter by outlet ID and never by name, so when the merchant says 'sales for King Street' or 'stock at the warehouse', call this FIRST and pass the id you find here. Returns every outlet in one call — the list is short, there is no paging and no server-side search, so match the name yourself. Deleted outlets are not returned. Outlets carry NO opening hours, timezone or currency: store hours are not stored anywhere on this platform and currency is a company-wide setting, so say that plainly instead of guessing. Per-outlet tax percentages are not here either — those belong to the tax tools.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
hub_get_payment_methods_breakdown Payment methods breakdown ~195
How customers paid over a date range, netted of refunds — 'how much came in on card vs cash', 'what did processing fees cost me', 'how much hits my bank account'. Card and each custom processor also report their processing fees and the resulting deposit; cash has no fees. The same aggregation the Financial Summary settlement section uses, so the two always agree.
| Name | Type | Req | Description |
|---|---|---|---|
| endDate | string | yes | End of the window, ISO 8601. Inclusive of the day when a bare date is given |
| outlets | array | – | Outlet ids to restrict to. Omit for all outlets |
| startDate | string | yes | Start of the window, ISO 8601, e.g. "2026-08-01" or "2026-08-01T00:00:00Z" |
| stations | array | – | Station ids to restrict to. Omit for all stations |
No output schema declared.
No examples provided.
hub_get_permissions List permissions ~133
The catalogue of permissions a role can be given — every switch on the Roles screen, with the id create_role and update_role need. Call this to turn a request like 'let her see the reports' into the right permission id; match on the label and the permission path, and ask the merchant if two of them could both be meant. Permissions are granted exactly as named — there is no parent-grants-the-children cascade here — so pass every line the merchant actually needs. The list is the same for every merchant; it is not their current setup. For what a role currently grants, use get_roles.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
hub_get_product_booking_setup Read a product booking setup ~70
Which resources serve this bookable product. Use it to check a product is actually ready to take bookings before quoting availability — an empty resource list means every hold will be refused, however correct the rule set looks.
| Name | Type | Req | Description |
|---|---|---|---|
| productId | string | yes | Product id from get_products — not a name |
No output schema declared.
No examples provided.
hub_get_product_options List product options ~70
List the variation axes the merchant already has — e.g. 'Size' with Small/Medium/Large. Call this BEFORE creating a product with variants and offer the existing axes instead of inventing new ones. Only call create_product_option when the merchant needs an axis that is not in this list.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
hub_get_products List products ~225
List products in the merchant's catalogue. Answers 'what products do I have', 'find the Coca-Cola product', 'how much is X'. Pass categoryIds to answer 'what is in the Drinks category' — take the ids from get_categories, which does not return products itself. LIMITATION — there is no sorting: results come back in catalogue order and cannot be ordered by price, name or date. So 'the most expensive products' CANNOT be answered from this tool, and you must NOT page through the whole catalogue trying. Say plainly that sorting is not supported yet, then offer what does work: search by a name or SKU, filter by category, or ask the merchant to narrow it down.
| Name | Type | Req | Description |
|---|---|---|---|
| categoryIds | array | – | Only products in these categories. Ids come from get_categories; a product matching any of them is returned |
| page | integer | – | Page number, default 1 |
| pageSize | integer | – | Results per page, default 20 |
| search | string | – | Filter by product name or SKU (partial match) |
No output schema declared.
No examples provided.
hub_get_purchase_order Get purchase order ~223
One purchase order in full: supplier, outlet, status, and every line — what was ORDERED and what has actually ARRIVED, per item, across each delivery. Answers 'what is on this order', 'what is still outstanding', 'did the price change from what we agreed'. Money appears twice on every writable field: the formatted one (CA$12.50) is for reading out to the merchant, the *Amount twin (12.50) is what update_purchase_order accepts back. The `ordered` block is the agreed order; each entry in `receipts` is one delivery booked against it, with its invoice number. Compare an item’s ordered quantity against the sum of its received quantities to see what is still owed — receiving more than ordered is allowed, so an overage is real, not an error. ALWAYS CALL THIS AFTER receive_purchase_order instead of retrying the receive: it is the way to confirm what was booked.
| Name | Type | Req | Description |
|---|---|---|---|
| purchaseOrderId | string | yes | Purchase order id from get_purchase_orders — not a PO number |
No output schema declared.
No examples provided.
hub_get_purchase_orders List purchase orders ~403
Stock the merchant has ordered from suppliers — the same list as Purchasing → Purchase Orders, and THE tool for any purchasing question over a period: 'what did I spend on stock last month', 'what is still on order', 'what have I ordered from this supplier', 'what is arriving this week'. Filter by status, supplier, outlet and a created-date range. READ totalCost CAREFULLY: it is what the order was placed FOR, not what was paid, and it sits on suggested and draft rows too — summing the whole list answers 'what have I proposed', not 'what did I spend'. For spend, filter status to ['sent','partially_received','fully_received']; for what a delivery actually cost, read the receipts block of get_purchase_order. Statuses, in order: suggested (auto-proposed by a reorder rule, not ordered), draft (being written), sent (with the supplier), partially_received (some arrived), fully_received (all in). "Open" means anything not fully_received. Cancelled orders are gone from this list entirely. The date range filters on when the order was CREATED, not when it was sent or received. Costs here are what the merchant pays suppliers — for sales and revenue use the report tools instead.
| Name | Type | Req | Description |
|---|---|---|---|
| endDate | string | – | ISO date — orders created up to and including this day |
| outletIds | array | – | Outlet ids from get_outlets |
| page | integer | – | 1-based page, default 1 |
| pageSize | integer | – | Rows per page, default 20 |
| search | string | – | Match the PO number |
| sortBy | string | – | Default createdAt |
| sortOrder | string | – | Default desc |
| startDate | string | – | ISO date — orders created on or after this day |
| status | array | – | Keep only these statuses |
| supplierIds | array | – | Supplier ids from get_suppliers |
No output schema declared.
No examples provided.
hub_get_receive_progress Progress of a delivery being booked in ~156
How far a delivery has got in being applied to stock. A large delivery is applied item by item in the background, so its order keeps its old status until the work finishes — this is the only place that shows the real progress. Call it with the receiptId that receive_purchase_order returned, or the one get_purchase_order reports as still being received. Read `failures` seriously: those items did NOT get their stock and say why (the item was deleted, the outlet no longer exists, the supplier link was removed). Everything else on the delivery still landed, so the fix is per item, not a re-receive of the whole thing.
| Name | Type | Req | Description |
|---|---|---|---|
| receiptId | string | yes | Receipt id from receive_purchase_order |
No output schema declared.
No examples provided.
hub_get_refunds List refunds ~199
Refunds recorded against orders, newest first: what was refunded, why, by whom, on which order and for which customer. Answers 'what did we refund lately', 'show the refunds on order 1042', 'who processed that refund'. Filter by order id, or search by the REFUND's own receipt number. LIMITATIONS: no date filter and no total count — for refunds in a date range use get_sales_breakdown (one row per refund) or get_financial_summary (refund totals). Refunds CANNOT be created here: they are taken on the POS terminal against the original payment. This tool only reads.
| Name | Type | Req | Description |
|---|---|---|---|
| orderId | string | – | Only refunds against this order — id from get_orders |
| page | integer | – | Page number, default 1 |
| pageSize | integer | – | Refunds per page, default 20 |
| search | string | – | Search by the refund's receipt number |
No output schema declared.
No examples provided.
hub_get_reorder_suggestions What needs reordering ~168
What the platform thinks should be reordered: rescans stock against the merchant's auto-reorder rules and returns the suggested orders it left behind, grouped by supplier and outlet. Answers 'what am I running low on', 'what should I order this week'. A suggestion is NOT an order — nothing has been sent and no supplier knows about it. Turn one into a real order with update_purchase_order (which accepts it as a draft) and then send_purchase_order. Only items with an auto-reorder rule can appear here, so an empty result usually means no rules are set rather than nothing being low — say that, and offer set_reorder_rule. The rescan is skipped and the stored suggestions returned when one ran moments ago; `rescanned: false` says so.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
hub_get_roles List roles ~109
The roles this merchant has defined and exactly what each one is allowed to do — Settings → Roles. Answers 'what can a cashier do', 'which roles exist', 'who is on the Manager role'. A role is the ONLY place permissions live: people do not carry their own. Read this before changing anything, and take permission ids from the granted list when revoking. Owners and organisation-level accounts hold no role and are never restricted by one — a role change cannot take anything away from them.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
hub_get_sales_breakdown Sales breakdown ~290
Order-by-order breakdown for a date range — the same rows as Reports → Sales Breakdown. One row per order AND per refund. Answers 'show me the orders from Saturday', 'which order had that big discount', 'how many refunds last month', 'sales per day'. The `totals` and `dailyTrend` in the reply cover the WHOLE date range, not just the returned page — quote them directly instead of adding up rows. For headline figures alone use get_financial_summary; this tool is for when the merchant wants to see the actual orders.
| Name | Type | Req | Description |
|---|---|---|---|
| endDate | string | yes | End of the window, ISO 8601. Inclusive of the day when a bare date is given |
| outlets | array | – | Outlet ids to restrict to. Omit for all outlets |
| page | integer | – | Page number, default 1 |
| pageSize | integer | – | Rows per page, default 50 |
| startDate | string | yes | Start of the window, ISO 8601, e.g. "2026-08-01" or "2026-08-01T00:00:00Z" |
| stations | array | – | Station ids to restrict to. Omit for all stations |
| timezone | string | – | IANA timezone for the daily grouping, e.g. "America/Toronto". Defaults to the server timezone |
No output schema declared.
No examples provided.
hub_get_session_report End of session report ~114
The full End of Session report for ONE shift: sales, taxes, settlement, refunds, staff sales, tips and the cash drawer. Answers 'does the drawer balance', 'was the till short last night', 'what did that shift take'. Takes the session id from get_sessions, never a station or a person. The cash drawer section is what tells you over / short — report it plainly, and do not compute it yourself from the other sections.
| Name | Type | Req | Description |
|---|---|---|---|
| sessionId | string | yes | Session id from get_sessions |
No output schema declared.
No examples provided.
hub_get_sessions Register sessions ~206
Register sessions (shifts) opened and closed in a date range — the list behind Reports → End of Session. Answers 'what shifts ran yesterday', 'who closed the register last night', 'is a session still open'. Returns who opened and closed each one, the opening float and the declared closing amounts. A session with no closeTime is still open. For the full money report of one shift, pass its id to get_session_report.
| Name | Type | Req | Description |
|---|---|---|---|
| endDate | string | yes | End of the window, ISO 8601. Inclusive of the day when a bare date is given |
| outlets | array | – | Outlet ids to restrict to. Omit for all outlets |
| startDate | string | yes | Start of the window, ISO 8601, e.g. "2026-08-01" or "2026-08-01T00:00:00Z" |
| stations | array | – | Station ids to restrict to. Omit for all stations |
No output schema declared.
No examples provided.
hub_get_station Till details ~314
Everything about ONE station, including what its device last reported about itself — app version, OS, network, printer, battery and whatever else that device sends. THIS IS THE TOOL FOR 'the till will not connect', 'it will not print', 'the card reader is not working', 'this register is acting up'. Takes the station id from get_stations, never a name. How to read it when troubleshooting: `kind` tells you whether it is a physical device or a browser till (a browser till has no device to blame — check the share link and the flow instead); an empty `deviceId` on a native station means no device ever enrolled under it; `enrolledBy` and `createdAt` say who set it up and when; an empty `deviceEnvironment` means the device has never reported anything, which is itself the finding. The values carry NO timestamp — they are the last ones received, and there is no way to tell how old they are. `recordUpdatedAt` is the last time the station RECORD changed (mostly a shift opening or closing, or a rename) — it is NOT a heartbeat: the platform stores no online/offline ping for a till, so never report a station as "online" or "last seen". Card readers shown here are read-only: pairing and unpairing a reader happens on the Terminals screen of the cabinet, not through any tool.
| Name | Type | Req | Description |
|---|---|---|---|
| stationId | string | yes | Station id from get_stations — not a name |
No output schema declared.
No examples provided.
hub_get_stations List tills ~420
Every POS station (till, register, checkout lane, cash desk) the merchant has — the same list as Run → Stations. Answers 'how many tills do I have', 'which register is busy right now', 'which store is this till at', 'which of them are browser tills'. Each station belongs to exactly ONE outlet and the outlet name is resolved here, so you do not need get_outlets to answer where a till stands — call get_outlets only to turn a store NAME the merchant said into the outletId filter of this tool. WHAT status MEANS, EXACTLY: the server does not compute it. The POS app on the till writes it — IN_USE when a cashier opens a register shift on that station, AVAILABLE when the shift is closed. It is best-effort: the update is lost while the device is offline and only flushed when it reconnects, and older app versions never sent it at all, so a station can sit at AVAILABLE with a shift open on it or at IN_USE hours after one was closed. Read it as "what this till last told us", never as proof. NEVER answer a shift or money question from it: for shifts that are actually open, who opened them and what they took, use get_sessions (a session with no closeTime is open) and get_session_report. If those disagree with this flag, they are right and the flag is stale. The whole list comes back in one call — it is short and plan-capped, there is no paging; search matches the station NAME only. Deleted stations are not returned.
| Name | Type | Req | Description |
|---|---|---|---|
| kind | string | – | native = a physical device running the POS app; virtual = a browser till |
| outletId | string | – | Only stations at this outlet — id from get_outlets, not a store name |
| search | string | – | Filter by station name, e.g. "POS 2" |
| status | string | – | IN_USE = the till last reported an open register shift; AVAILABLE = it did not |
No output schema declared.
No examples provided.
hub_get_stock Check stock ~244
How many of ONE product are on hand, broken down by variant and by outlet. Answers 'how many of these do I have', 'how many are left at the King Street shop', 'where is this product in stock'. Takes the product id from get_products — get_products itself returns prices, never quantities. Pass outletId (from get_outlets) to narrow it to a single shop. CRITICAL — "tracked: false" means the merchant switched stock counting OFF for that variant at that outlet, so there is NO count at all. It is NOT "0 in stock". Never say "you have 0" or "it is out of stock" about an untracked line; say stock is not being tracked there. Only lines with tracked: true carry a real quantity. Outlets where the product is hidden do not appear at all. For 'what is running out' across the whole catalogue use find_low_stock instead — do not page through products with this tool.
| Name | Type | Req | Description |
|---|---|---|---|
| outletId | string | – | Outlet id from get_outlets. Omit for every outlet |
| productId | string | yes | Product id from get_products — not a name |
No output schema declared.
No examples provided.
hub_get_stock_history Stock history ~242
Every recorded stock movement for ONE product, newest first — the audit trail behind a count. Answers 'why is the number wrong', 'who changed this', 'when did we last receive these'. Each entry says what happened (SALE, STOCK_RECEIVED, DAMAGE, THEFT, LOSS, RESTOCK_RETURN, INVENTORY_RECOUNT, TRANSFER, BULK_RECOUNT), the signed quantity, the resulting on-hand, the outlet and the employee. Use this when the merchant disputes a number: state what the log shows rather than guessing at a cause. Only changes that were actually applied appear — an adjustment on an untracked line writes no history at all, which is itself the explanation when the log looks empty.
| Name | Type | Req | Description |
|---|---|---|---|
| actions | array | – | Limit to these action names, e.g. ["DAMAGE","THEFT"] |
| endDate | string | – | ISO date. Pass together with startDate or neither |
| outletIds | array | – | Limit to these outlets (ids from get_outlets) |
| productId | string | yes | Product id from get_products |
| startDate | string | – | ISO date. Pass together with endDate or neither |
No output schema declared.
No examples provided.
hub_get_stock_transfer View stock transfer ~164
ONE transfer in full: both outlets, the employee, the invoice, the notes and the line items with quantities. Takes the transactionId AND the type exactly as get_stock_transfers returned them — the type decides which leg is read, and a wrong one fails. For send-transfer and receive-transfer this also returns the sibling leg, so you can name where the stock came from and where it is going, plus stockTransferId, which is what receive_stock_transfer needs. Line quantities are the amounts MOVED, recorded when the transfer was created — they are not current stock. Use get_stock for that.
| Name | Type | Req | Description |
|---|---|---|---|
| transactionId | string | yes | transactionId from get_stock_transfers |
| type | string | yes | The same type get_stock_transfers reported for that row |
No output schema declared.
No examples provided.
hub_get_stock_transfers List stock transfers ~235
The Stock Transfers list. Answers 'what transfers are open', 'what is still in transit', 'did the warehouse send those yet'. Each row is ONE LEG: a move between outlets produces a send-transfer (already completed — the stock has left the source) and a receive-transfer (in-transit until the destination confirms). receive-stock is stock booked in from a supplier; bulk-edit is a recount. To find work that is still open, filter status "in-transit". Search matches the transfer number and the invoice only — not product names. Rows carry no line items: open one with get_stock_transfer to see what was actually moved.
| Name | Type | Req | Description |
|---|---|---|---|
| outletIds | array | – | Only legs at these outlets (ids from get_outlets) |
| page | integer | – | Page number, default 1 |
| pageSize | integer | – | Rows per page, default 20 |
| search | string | – | Matches the transfer number or the invoice number |
| status | string | – | "in-transit" = waiting to be received |
| type | string | – | Only this leg type. Omit for all |
No output schema declared.
No examples provided.
hub_get_supplier Get supplier ~85
One supplier in full: contact details, address, notes, how many catalogue items it is linked to and how many of its purchase orders are still open. Does NOT list the items themselves or the orders — those are get_supplier_items and get_purchase_orders (filter it by this supplier id).
| Name | Type | Req | Description |
|---|---|---|---|
| supplierId | string | yes | Supplier id from get_suppliers — not a name |
No output schema declared.
No examples provided.
hub_get_supplier_items List a supplier’s items ~273
The catalogue items one supplier carries, with the cost the merchant pays and the supplier’s own part number. Answers 'what do I buy from this supplier', 'what does this supplier charge me for X'. CALL THIS BEFORE create_purchase_order: an order may only contain items the supplier is linked to, and the variantId + unitCost you need for each line come from here. PASS outletId when you are about to build an order: that narrows the list to items whose stock is actually tracked at that outlet — the only ones a receive can add stock to — and adds onHand, the quantity there right now, so you can see what genuinely needs reordering. Search and paging work the same either way. An item the merchant expects but does not see here is simply not linked yet — get_linkable_items then link_supplier_items.
| Name | Type | Req | Description |
|---|---|---|---|
| outletId | string | – | Outlet id from get_outlets — narrows to stock-managed items there and adds onHand |
| page | integer | – | 1-based page, default 1 |
| pageSize | integer | – | Rows per page, default 20 |
| search | string | – | Match product name, item name, SKU, barcode or part number |
| supplierId | string | yes | Supplier id from get_suppliers — not a name |
No output schema declared.
No examples provided.
hub_get_suppliers List suppliers ~175
The businesses this merchant BUYS stock from — the same list as Purchasing → Suppliers. Answers 'who do I order from', 'what is my supplier’s email', 'which suppliers have open orders'. ALSO THE WAY TO TURN A SUPPLIER NAME INTO AN ID: every purchase-order and item-linking tool takes a supplier id and never a name. Each row carries openPoCount — purchase orders not yet fully received — and itemCount, how many catalogue items that supplier is linked to. This is the buying side. For what the merchant SELLS and at what price, use get_products.
| Name | Type | Req | Description |
|---|---|---|---|
| page | integer | – | 1-based page, default 1 |
| pageSize | integer | – | Rows per page, default 20 |
| search | string | – | Match supplier name, contact name or email |
No output schema declared.
No examples provided.
hub_get_suppliers_for_product Who supplies a product ~106
The other direction: given a product from get_products, which suppliers carry its items and at what cost. Answers 'who can I reorder this from', 'who is cheapest for this', 'am I buying this from more than one place'. Nothing comes back when the product is linked to no supplier — that is a real answer, not an error: it cannot be purchase-ordered until it is linked.
| Name | Type | Req | Description |
|---|---|---|---|
| productId | string | yes | Product id from get_products |
No output schema declared.
No examples provided.
hub_get_taxes Tax settings overview ~139
The whole Settings → Taxes screen: every tax group, the rates inside it in charging order, and each rate’s percentage at each outlet. Answers 'what sales tax do I charge', 'what tax applies in the King Street store', 'which tax group is the standard one', 'do I charge GST and PST separately'. ALWAYS call this before any other tax tool — every other tool takes ids from here (tax group ids, tax rate ids, outlet ids) and none of them accept names. It also reports which rates are shared across groups and which belong to no group, both of which change what an edit will do. Read-only.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
hub_get_team List team members ~125
Everyone who works for this merchant — Manage → Team → Users. Answers 'who works here', 'what role does Maria have', 'who can see reports', 'what is Tom's phone number'. This is about who works here and what they are allowed to do — for how much anyone SOLD, use get_employee_sales instead. Returns the whole team at once: there is no search and no paging, so filter the rows yourself instead of asking the merchant to narrow down. Removed people are not listed at all. Every other tool in this domain takes its userId from here.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
hub_get_transactions Transactions ~304
Individual payments and refunds — Reports → Transactions. This is the PAYMENT level, not the order level: one order can hold several transactions. Answers 'find the payment for receipt 1042', 'show every Amex refund last week', 'which transactions failed'. Filter by payment method, processor, card provider or status, or search by order number. For sales figures use get_financial_summary — this tool is for locating a specific payment.
| Name | Type | Req | Description |
|---|---|---|---|
| cardProviders | array | – | e.g. ["Visa", "Mastercard", "Amex"] |
| endDate | string | yes | End of the window, ISO 8601. Inclusive of the day when a bare date is given |
| outlets | array | – | Outlet ids to restrict to. Omit for all outlets |
| page | integer | – | Page number, default 1 |
| pageSize | integer | – | Rows per page, default 50 |
| paymentMethods | array | – | e.g. ["card", "cash"] |
| processors | array | – | Processor ids |
| search | string | – | Search by order / receipt number |
| startDate | string | yes | Start of the window, ISO 8601, e.g. "2026-08-01" or "2026-08-01T00:00:00Z" |
| stations | array | – | Station ids to restrict to. Omit for all stations |
| status | string | – | Transaction status to filter by |
No output schema declared.
No examples provided.
hub_hold_booking Hold a booking window ~217
Claim a window on one resource. The hold is what makes the window unavailable to everybody else, and it expires by itself if nobody pays — so holding is safe, and a hold nobody confirms costs nothing. `startAt` must be exactly a slot start from get_booking_availability, and `endAt` exactly where the last slot ends; anything else is refused rather than rounded. A refusal with 409 means somebody took it first: re-read availability and offer what is left, do not retry the same window.
| Name | Type | Req | Description |
|---|---|---|---|
| customerId | string | – | Who it is for, id from get_customers. Omit for a walk-in nobody identified |
| endAt | string | yes | ISO instant, exactly where the booked window ends |
| outletId | string | – | The shop the sale happens at |
| productId | string | yes | Product id |
| resourceId | string | yes | Which resource — the barber, room or asset being booked |
| startAt | string | yes | ISO instant, exactly a slot start from availability |
No output schema declared.
No examples provided.
hub_import_categories Import categories ~396
Load a category tree in one call — a whole department structure, a migration. For one or two categories use create_category. Parents and children may sit in the SAME batch: `parentRef` takes a parent NAME (matched inside this batch first, then against existing categories) or an existing id, which is what makes a flat spreadsheet importable. Loops and unresolvable parents fail those rows and their descendants, not the batch. This tool does NOT match rows against what already exists — the cabinet does that in the browser, not on the server. Every row in `creates` becomes a NEW record even if an identical one is already there. To change existing records, read them with export_records and pass their ids in `updates`. Two rows named the same thing in one batch, or a name that already exists, will produce two categories with that name. On `updates` this is the only import with true merge-patch semantics: omit a field to leave it, pass null to CLEAR it, and pass `parentId: null` to move a category back to the top level (which create_category and update_category cannot do). Products are untouched — importing categories neither assigns nor removes products; that is the products side. Rows are independent and per-row failures come back in the reply. Max 500 rows per call. BEFORE CALLING: show the merchant a preview of the first few rows, state the EXACT number of rows and which resource they hit, and get an explicit yes. There is NO undo and no dry-run — nothing here validates without writing. If you are unsure a row is right, ask; a wrong bulk import is far more expensive to repair than a question. Afterwards, tell the merchant what landed: an import done here does NOT appear in the cabinet's Recent Imports list, which only records uploads made through the wizard.
| Name | Type | Req | Description |
|---|---|---|---|
| creates | array | – | – |
| updates | array | – | – |
No output schema declared.
No examples provided.
hub_import_customers Import customers ~325
Load a customer list in one call — a mailing list, a migration from another system. For one person use create_customer. This is the ONE import that recognises records it already has: pass `rows` and the server looks each one up BY EMAIL, updating that customer instead of duplicating them. A row with no email is always created new and can never be matched again, so push for emails before importing. Fields you pass overwrite the stored ones; fields you omit are left alone — but an address you pass replaces the stored address whole, and `tags` replaces the whole tag list. ALL-OR-NOTHING: unlike the other imports this one runs in a single transaction, so one bad row rolls the entire batch back and nothing is written. There is no per-row failure list. Max 500 rows per call. BEFORE CALLING: show the merchant a preview of the first few rows, state the EXACT number of rows and which resource they hit, and get an explicit yes. There is NO undo and no dry-run — nothing here validates without writing. If you are unsure a row is right, ask; a wrong bulk import is far more expensive to repair than a question. Afterwards, tell the merchant what landed: an import done here does NOT appear in the cabinet's Recent Imports list, which only records uploads made through the wizard.
| Name | Type | Req | Description |
|---|---|---|---|
| rows | array | – | Spreadsheet rows — matched by email, then created or updated by the server |
| updates | array | – | Use only when you already hold the id and do not want email matching |
No output schema declared.
No examples provided.
hub_import_options Import product attributes ~303
Load product attributes and their values in one call — "Size: S,M,L", "Colour: Red,Blue,Green" — the axes variants are built from. For one axis use create_option. Attribute names are unique per company: a `creates` row whose name already exists is REJECTED (that row only), so read get_options first and send existing ones through `updates`. On `updates`, values are APPEND-ONLY: the list you pass is merged into what is there and existing values are never renamed or removed, because variants reference them by name and would be orphaned. Renaming the attribute itself does cascade to its variants. To remove a value, use the cabinet. This creates no products and no variants — it only defines the axes. Rows are independent and per-row failures come back in the reply. Max 500 rows per call. BEFORE CALLING: show the merchant a preview of the first few rows, state the EXACT number of rows and which resource they hit, and get an explicit yes. There is NO undo and no dry-run — nothing here validates without writing. If you are unsure a row is right, ask; a wrong bulk import is far more expensive to repair than a question. Afterwards, tell the merchant what landed: an import done here does NOT appear in the cabinet's Recent Imports list, which only records uploads made through the wizard.
| Name | Type | Req | Description |
|---|---|---|---|
| creates | array | – | – |
| updates | array | – | – |
No output schema declared.
No examples provided.
hub_import_outlets Import outlets ~398
Load a list of store locations in one call — opening a chain, migrating from another system. For one shop use create_outlet. Name, street address, city, state and postal code are ALL required on new rows, exactly as in the cabinet form; ask the merchant rather than inventing an address. Country is not settable: every outlet takes the company country, and the import fails outright if the company has none. This tool does NOT match rows against what already exists — the cabinet does that in the browser, not on the server. Every row in `creates` becomes a NEW record even if an identical one is already there. To change existing records, read them with export_records and pass their ids in `updates`. Bulk-created outlets differ from ones added in the cabinet in two ways worth telling the merchant: no Stripe location is wired up (payments must be linked per outlet afterwards) and stock rows are seeded in the background, so a fresh outlet can look empty for a while. This path also does NOT check the plan allowance, so it can push the company past the number of outlets it pays for — say so before importing more than a couple. Updates only set what you pass; alias and country cannot be changed here. Rows are independent and per-row failures come back in the reply. Max 500 rows per call. BEFORE CALLING: show the merchant a preview of the first few rows, state the EXACT number of rows and which resource they hit, and get an explicit yes. There is NO undo and no dry-run — nothing here validates without writing. If you are unsure a row is right, ask; a wrong bulk import is far more expensive to repair than a question. Afterwards, tell the merchant what landed: an import done here does NOT appear in the cabinet's Recent Imports list, which only records uploads made through the wizard.
| Name | Type | Req | Description |
|---|---|---|---|
| creates | array | – | – |
| updates | array | – | – |
No output schema declared.
No examples provided.
hub_import_products Import products ~425
Load a spreadsheet of products in one call — "here are my 200 items", a catalogue from another POS, a price list. For one or two products use create_product instead: it can build variants from options, this cannot (every created row is a SIMPLE product with a single variant). This tool does NOT match rows against what already exists — the cabinet does that in the browser, not on the server. Every row in `creates` becomes a NEW record even if an identical one is already there. To change existing records, read them with export_records and pass their ids in `updates`. An update row is found by productId OR by sku (the SKU of its variant — what a spreadsheet has); a SKU that matches no product or several fails that row. price, costPrice, barcode and sku in an update go to the product's single variant; a product with several variants fails that row — use update_product for it. An update cannot clear categories; omit the field to leave them as they are. Prices are written as the merchant says them ("19.99"), in the company currency. Category entries may be names or ids: an unknown NAME is created as a new top-level category, so check the spelling against get_categories first or the import will litter the catalogue. Rows are independent — the reply lists per-row failures alongside what landed, so a bad row does not stop the rest. Max 500 rows per call. BEFORE CALLING: show the merchant a preview of the first few rows, state the EXACT number of rows and which resource they hit, and get an explicit yes. There is NO undo and no dry-run — nothing here validates without writing. If you are unsure a row is right, ask; a wrong bulk import is far more expensive to repair than a question. Afterwards, tell the merchant what landed: an import done here does NOT appear in the cabinet's Recent Imports list, which only records uploads made through the wizard.
| Name | Type | Req | Description |
|---|---|---|---|
| creates | array | – | New products, one per spreadsheet row |
| updates | array | – | – |
No output schema declared.
No examples provided.
hub_import_stock_csv Import stock CSV ~231
Write stock counts for ONE outlet from the CSV that export_stock_csv returns — the cabinet's stock upload. Edit only the New Qty column; each row SETS that variant's quantity at the outlet (it is not a +/- adjustment). Keep the header and the Variation ID column as exported. Rows whose Variation ID is not in this company are skipped. For one or two variants use adjust_stock instead. BEFORE CALLING: show the merchant a preview of the first few rows, state the EXACT number of rows and which resource they hit, and get an explicit yes. There is NO undo and no dry-run — nothing here validates without writing. If you are unsure a row is right, ask; a wrong bulk import is far more expensive to repair than a question. Afterwards, tell the merchant what landed: an import done here does NOT appear in the cabinet's Recent Imports list, which only records uploads made through the wizard.
| Name | Type | Req | Description |
|---|---|---|---|
| csv | string | yes | The CSV text, header row included |
| outletId | string | yes | Outlet id from get_outlets — the one the CSV was exported for |
No output schema declared.
No examples provided.
hub_install_extension Install extension ~235
Install an extension for the whole company. Takes the `extensionId` from get_extension_catalog, never a name. CONFIRM WITH THE MERCHANT BEFORE CALLING, naming the extension: this is not a personal setting — it changes the cabinet for EVERY user of the company, and several extensions do real work on install (WooCommerce connects the store and starts syncing, TIMEmSYSTEM writes default settings, Custom Payment schedules Stripe billing). MONEY: an extension with a non-zero `price` starts costing the merchant from the moment it is installed — quote the stored price string and let them decide. Installing is not enough to finish a setup: most extensions then need their own settings page in the cabinet (a WooCommerce store URL, a payment gateway key), which this agent cannot fill in — say that plainly after installing. Refused when the plan excludes the extension, when the caller lacks the Extensions permission, or when it is already installed (installing twice is not a no-op — it would run the side effects again).
| Name | Type | Req | Description |
|---|---|---|---|
| extensionId | string | yes | extensionId from get_extension_catalog — not a name, not a guid |
No output schema declared.
No examples provided.
hub_link_modifier_to_category Ask a modifier on a category ~262
Start asking a modifier on every product in a category — THIS is what puts it on the till. The rules are per category, so the same modifier can be required here and optional elsewhere. required means the cashier cannot finish the line until they answer. min/max count distinct choices, or UNITS when the modifier uses quantities. Refused when the category already inherits this modifier from a parent category (link it to the parent or the child, not both), when it is already linked, and when the rules could not be satisfied — for example asking for at least 3 when only 2 choices are sold at every outlet. Linking a modifier to a PARENT category detaches it from any subcategories that already had it, since they now inherit it.
| Name | Type | Req | Description |
|---|---|---|---|
| categoryId | string | yes | Category id from get_categories |
| max | integer | – | Most that may be chosen. Omit for no cap |
| min | integer | – | Fewest that must be chosen. Setting this makes it required |
| modifierId | string | yes | Modifier id from get_modifiers |
| required | boolean | – | The cashier must answer before ringing up. Default false |
| sortOrder | integer | – | Position among this category’s modifiers. Omit to append |
No output schema declared.
No examples provided.
hub_link_supplier_items Link, re-price or unlink supplier items ~322
Change what a supplier carries and what it charges, in one call: `add` new items, `update` the cost or part number of items already linked, `remove` items it no longer sells. variantId comes from get_linkable_items for `add` and from get_supplier_items for `update` / `remove`. unitCost is what the merchant PAYS per unit, as a human amount like "12.50" in the company currency — never the selling price, and never cents. It is only the DEFAULT that prefills new order lines: changing it does not re-price purchase orders already placed. Adding an item that is already linked is refused, so read the current list first rather than re-adding blindly. REMOVING is the destructive half — confirm it with the merchant, and tell them it also EDITS ORDERS THAT ARE ALREADY WRITTEN: the item’s line is dropped from every suggested and draft order from this supplier, which changes their totals. Orders already sent keep their lines, the item stays in the catalogue, but it can no longer be added to a new order from this supplier, and any auto-reorder rule pointing at this supplier for it stops producing suggestions.
| Name | Type | Req | Description |
|---|---|---|---|
| add | array | – | Items to start buying from this supplier |
| remove | array | – | Item ids from get_supplier_items to stop buying from this supplier |
| supplierId | string | yes | Supplier id from get_suppliers — not a name |
| update | array | – | Already-linked items to re-price or re-code |
No output schema declared.
No examples provided.
hub_receive_purchase_order Receive a delivery ~494
Book a delivery in against an order that was sent: ADDS THE RECEIVED QUANTITIES TO REAL STOCK at the order’s outlet and updates the item cost book. This is not a note — it changes inventory. CONFIRM THE QUANTITIES WITH THE MERCHANT FIRST, item by item, and record what genuinely ARRIVED, not what was ordered — they differ, which is the whole point of receiving. Only `sent` and `partially_received` orders can be received; each item must be one that was ordered; receiving more than ordered is allowed. A short delivery leaves the order `partially_received` — receive again when the rest turns up. A CALL THAT RETURNS IS NOT PROOF EVERY ITEM LANDED — check `partialFailure`, and `stockApplied.complete` on a large delivery. IF IT FAILS, DO NOT RETRY BLINDLY: "still being received" means a previous delivery is being applied right now (get_receive_progress), and a wrong-status error means it was already fully received. Call get_purchase_order to see what actually landed before deciding anything. unitCost is what this delivery actually cost per unit — pass the invoice price if it differs from the order. When it does differ, the supplier’s stored cost is NOT updated unless you list that item in updateSupplierCostVariantIds, so ask the merchant whether the new price is the going rate or a one-off.
| Name | Type | Req | Description |
|---|---|---|---|
| customFees | array | – | Any other named charge on the order, e.g. "Handling" |
| discount | string | – | Flat discount on the whole order, e.g. "25.00". Not with discountPercent |
| discountPercent | number | – | Discount as a percentage of the items subtotal |
| dutyFees | string | – | Duty / customs charged on the order |
| invoiceDate | string | – | ISO date on the invoice |
| invoiceNumber | string | – | Supplier invoice number for this delivery |
| lines | array | yes | What actually arrived |
| purchaseOrderId | string | yes | Purchase order id from get_purchase_orders — not a PO number |
| receivedAt | string | – | ISO date the goods arrived — defaults to now |
| shippingFee | string | – | Shipping charged on the order, e.g. "18.00" |
| updateSupplierCostVariantIds | array | – | Items whose new unitCost should become the supplier’s stored cost from now on |
No output schema declared.
No examples provided.
hub_receive_stock_transfer Receive stock transfer ~129
Confirm that a transfer arrived: the destination outlet gains the units and the transfer closes. CONFIRM WITH THE MERCHANT FIRST — this adds stock and cannot be undone from here. Only do it when they say the goods are physically there; confirming a transfer that never arrived hides a loss instead of recording one. Takes stockTransferId (from get_stock_transfer), NOT the transactionId. Only applies to a receive-transfer leg still in-transit; a transfer already completed has nothing left to receive.
| Name | Type | Req | Description |
|---|---|---|---|
| stockTransferId | string | yes | stockTransferId from get_stock_transfer — not the transactionId |
No output schema declared.
No examples provided.
hub_record_received_goods Record goods received with no order ~364
Stock that arrived with no purchase order behind it — the merchant bought it on the spot, or it was ordered outside the platform. Creates the order already fully received and ADDS THE QUANTITIES TO REAL STOCK at the outlet, in one step. Use this ONLY when no order exists. If the goods match an order that was sent, use receive_purchase_order instead — otherwise the same delivery is counted twice, once here and once when someone receives the real order. Check get_purchase_orders for an open order from this supplier before calling. The items must already be linked to the supplier (get_supplier_items) and stock-managed at the outlet. There is no undo: correcting a mistake means a stock adjustment afterwards.
| Name | Type | Req | Description |
|---|---|---|---|
| customFees | array | – | Any other named charge on the order, e.g. "Handling" |
| discount | string | – | Flat discount on the whole order, e.g. "25.00". Not with discountPercent |
| discountPercent | number | – | Discount as a percentage of the items subtotal |
| dutyFees | string | – | Duty / customs charged on the order |
| invoiceDate | string | – | ISO date on the invoice |
| invoiceNumber | string | – | – |
| lines | array | yes | What arrived |
| notes | string | – | – |
| outletId | string | yes | Outlet id from get_outlets — where the goods physically arrived |
| receivedAt | string | – | ISO date the goods arrived — defaults to now |
| shippingFee | string | – | Shipping charged on the order, e.g. "18.00" |
| supplierId | string | yes | Supplier id from get_suppliers |
| updateSupplierCostVariantIds | array | – | Items whose unitCost should become the supplier’s stored cost from now on |
No output schema declared.
No examples provided.
hub_remove_reorder_rule Turn off auto-reorder for an item ~80
Stop watching one item: it will no longer be suggested for reordering however low it runs. Its thresholds are forgotten, so turning it back on means setting them again. Suggestions already produced for this item are not withdrawn — cancel those orders separately if the merchant wants them gone.
| Name | Type | Req | Description |
|---|---|---|---|
| variantId | string | yes | Item id from get_supplier_items |
No output schema declared.
No examples provided.
hub_remove_tax_rate_from_group Remove rate from group ~140
Stop ONE tax group charging a rate. The rate itself, its percentages and its use by other groups are untouched — this only drops the link. This is what the merchant means by "remove the city tax from Food". Use delete_tax_rate ONLY when they want the rate gone from the whole company. CONFIRM WITH THE MERCHANT: every product in this group immediately stops being charged this tax. Sales already rung up are NOT recalculated — this only affects sales from now on.
| Name | Type | Req | Description |
|---|---|---|---|
| taxGroupId | string | yes | Tax group id from get_taxes |
| taxRateId | string | yes | Tax rate id from get_taxes |
No output schema declared.
No examples provided.
hub_remove_user Remove team member ~144
Remove someone from the merchant's team — the tool for 'she left', 'take the leaver off'. Takes the userId from get_team ONLY, never a name. CONFIRM WITH THE MERCHANT BEFORE CALLING, naming the person: there is no undo from here. If two people share a name, list them and ask which. They stop being able to sign in immediately; their past orders and sales figures are kept, so reports about them still work. You cannot remove the account this agent key belongs to — the server refuses, so that an administrator cannot lock themselves out.
| Name | Type | Req | Description |
|---|---|---|---|
| userId | string | yes | User id from get_team — not a name |
No output schema declared.
No examples provided.
hub_rename_station Rename till ~146
Rename one station. Takes the station id from get_stations. Renaming is safe and takes effect everywhere — past orders, sessions and reports store the id and look the name up. The name is the ONLY thing the cabinet lets anyone change on an existing station, and the only thing this tool changes. A station cannot be moved to another outlet, and its device, card reader and POS layout are not editable from here — moving a till to another store means creating a new station there and deleting this one, which loses its id.
| Name | Type | Req | Description |
|---|---|---|---|
| name | string | yes | The new station name |
| stationId | string | yes | Station id from get_stations — not a name |
No output schema declared.
No examples provided.
How do I install the Final POS – Custom POS Builder MCP server?
Final POS – Custom POS Builder is a hosted endpoint at https://ai.finalpos.com/mcp, so there is nothing to install locally. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.
remote · ai.finalpos.com
claude mcp add --transport http com-finalpos-final 'https://ai.finalpos.com/mcp'
What is the Final POS – Custom POS Builder MCP server?
Final POS – Custom POS Builder is an MCP server listed in the public MCP registry as com.finalpos/final. Build custom POS apps with any AI agent: checkout flows, kiosks, payment terminals. Plus store ops. This page covers its hosted endpoint (https://ai.finalpos.com/mcp).
Is the Final POS – Custom POS Builder MCP server safe to use?
Final POS – Custom POS Builder scores 86 out of 100 on VerifyMCP. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.
What tools does the Final POS – Custom POS Builder MCP server expose?
Final POS – Custom POS Builder exposes 177 tools: get_connection, list_packs, find_tools, call_tool, call_write_tool, and 172 more. Their descriptions and schemas cost roughly 35,433 tokens of context every time the server is loaded.
Does the Final POS – Custom POS Builder MCP server require authentication?
Yes. Final POS – Custom POS Builder asked us for credentials when we connected, so you will need to authorise it in your MCP client before it can do anything.
Is the Final POS – Custom POS Builder MCP server still maintained?
Final POS – Custom POS Builder is still listed as active in the MCP registry. We last reached this channel on 7 November 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.
What did VerifyMCP test on the Final POS – Custom POS Builder MCP server?
VerifyMCP ran 13 checks on Final POS – Custom POS Builder, covering TLS certificate, Authorization, HTTPS, HSTS header and 9 more. Supply Chain Security and Provenance & Transparency do not apply to a hosted endpoint, so they were not tested. The score does not measure everything, and the scoring documentation lists what it leaves out.