mattrguerra/meridian-orders-mcpmainPrivate on GitHub, published here in full

PLINTH-API.md

Language Markdown4.2 KB98 lines

docs/PLINTH-API.md

  1. # Plinth API reference
  2. Plinth is Meridian Display Co.'s order system: orders, customers, the status
  3. catalog, split shipments, products and stock. Like the company, it is
  4. fictional. This is the reference the server in `mcp-server/` is written
  5. against, and the one its code comments point to.
  6. ## Transport
  7. | Item | Value |
  8. |---|---|
  9. | Endpoint | `https://orders.meridiandisplay.example/api/v2/dispatch` |
  10. | Style | One endpoint; the action is chosen by the `method` query parameter |
  11. | HTTP method | GET for reads |
  12. | Format | JSON |
  13. | Environment | **Production only.** There is no sandbox; every call reads live data. |
  14. | Auth | Static key in the `api_key` query parameter on every call, with global **read** scope |
  15. | Rate limit | 10 requests per 10 seconds, across every caller sharing the key |
  16. The key reads every customer's full record, so the API has no per-customer
  17. scoping of its own. Anything that exposes its data to someone other than staff
  18. has to verify identity first (see `mcp-server/src/lib/classification.ts`).
  19. ## Methods
  20. | Method | Kind | What it returns |
  21. |---|---|---|
  22. | `orders.fetch` | read | One order or a batch |
  23. | `statuses.list` | read | The status catalog (a lookup table) |
  24. | `customers.fetch` | read | Customer records |
  25. | `orders.splitShipments` | read | Per-destination breakdown of a multi-address order |
  26. | `products.fetch` | read | Product specs and turnaround (schema not yet probed) |
  27. | `inventory.fetch` | read | Stock levels (schema not yet probed) |
  28. | `orders.setStatus` | **write** | Changes an order's status and can email the customer |
  29. | `inventory.adjust` | **write** | Changes stock levels |
  30. The server never dispatches a write method; see the four-layer guard in
  31. `mcp-server/src/lib/guard.ts`.
  32. ## Rules that hold across methods
  33. - **Request dates are `yyyymmdd`**, with no separators. ISO dates are rejected
  34. with `{"error": "Bad since date"}`.
  35. - **Response dates differ by method:** `orders.fetch` returns MySQL
  36. `YYYY-MM-DD HH:MM:SS`; `orders.splitShipments` returns plain `YYYY-MM-DD`.
  37. - **Two error shapes**, both on HTTP 200: `{"error": "…"}` and
  38. `{"notice": "No match"}`. Success bodies never carry either key.
  39. - **Collections switch type:** an empty one arrives as `[]`, a populated one
  40. as an object keyed `"1".."N"`.
  41. - **Numbers arrive as strings** in some records and as numbers in others.
  42. - **Fail-open filters:** a parameter the method doesn't know is ignored, not
  43. rejected, so a typo returns the unfiltered set.
  44. - **One misspelled key**, `carier_name`, is part of the contract. Map it once.
  45. ## Method notes
  46. **`orders.fetch`**
  47. - Parameters:
  48. - `order_ref` (a raw integer);
  49. - `ref_cmp` (`below` or `above`, relative to `order_ref`);
  50. - `customer_ref`, which errors unless paired with both dates;
  51. - `shop` and `subshops` (`1` to include child shops);
  52. - `status_code`;
  53. - `since` and `until`;
  54. - `offset` and `limit` (cap 100).
  55. - Response: `order_map`, with orders keyed `ord_{order_ref}`. `lines` and
  56. `shipments` are ordinal-keyed.
  57. - `closed_on` holds the date the order was **placed**, despite its name.
  58. **`statuses.list`**
  59. - Ignores every filter.
  60. - `status_code` is the stable identifier and is what orders carry.
  61. - The `row_N` keys are **not** stable: the catalog gained an entry and
  62. reshuffled between two pulls. Map by `status_code`.
  63. **`customers.fetch`**
  64. - Filters on exact `email`, or on a `since`/`until` range over
  65. `date_field` = `signup` (default) or `last_login`.
  66. - Records are keyed `cust_{n}`.
  67. - **There is no phone filter.** A phone parameter is silently dropped and the
  68. full customer list comes back.
  69. **`orders.splitShipments`**
  70. - The only method whose top level is a bare array, one entry per destination.
  71. - `{"notice": "No match"}` for a valid single-address order means "not a
  72. multi-address order", not a failure.
  73. - `split_lines` can be an array or an ordinal-keyed object within one
  74. response.
  75. ## Order numbers
  76. Customers and staff quote the **display** number, which the API neither
  77. accepts nor returns:
  78. - **Current:** `MD` + the order's two-digit year + `order_ref`. Order 39582
  79. placed in 2026 is `MD2639582`.
  80. - **Legacy:** `M-` + six digits, from before Plinth. These don't map to an
  81. `order_ref` by rule, so route them to a person.