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

getOrders.ts

Language TypeScript3.3 KB77 lines

mcp-server/src/tools/getOrders.ts

  1. import { z } from "zod";
  2. import type { ToolModule } from "./types.js";
  3. import { plinthRequest, type PlinthParams } from "../lib/http.js";
  4. import { toRequestDate } from "../lib/dates.js";
  5. /**
  6. * get_orders: retrieve one order or a batch (Plinth method `orders.fetch`).
  7. *
  8. * Wire notes:
  9. * - `order_ref` is a RAW integer, not the MD-prefixed display number (resolve
  10. * that via lib/orderNumber.ts first).
  11. * - `customer_ref` ALONE errors ({"error":"Invalid request"}); it must be paired
  12. * with since/until dates.
  13. * - Request dates are yyyymmdd (lib/dates.ts:toRequestDate); ISO is rejected.
  14. * - Response container is `order_map`, orders keyed `ord_{order_ref}`; `lines`
  15. * and `shipments` are ordinal-keyed (lib/collections.ts:normalizeCollection).
  16. */
  17. export const getOrders: ToolModule = {
  18. name: "get_orders",
  19. config: {
  20. title: "Get orders",
  21. description:
  22. "Retrieve one order or a batch, filterable by order ref, customer, shop, status, or date range. Read-only.",
  23. inputSchema: {
  24. orderId: z.number().int().positive().optional().describe("Raw integer order_ref (not the MD display number)."),
  25. compare: z.enum(["below", "above"]).optional().describe("With orderId: orders below/above that ref."),
  26. customerId: z.number().int().positive().optional().describe("Customer ref; MUST be paired with fromDate + toDate."),
  27. shop: z.string().optional().describe("Shop id, e.g. '2'."),
  28. includeSubshops: z.boolean().optional().describe("With shop: include its child shops (default true)."),
  29. statusCode: z.string().optional().describe("Status code from get_status_catalog."),
  30. fromDate: z.string().optional().describe("ISO YYYY-MM-DD; converted to yyyymmdd on the wire."),
  31. toDate: z.string().optional().describe("ISO YYYY-MM-DD; converted to yyyymmdd on the wire."),
  32. offset: z.number().int().min(0).optional(),
  33. limit: z.number().int().min(1).max(100).optional().describe("Documented cap 100; observed default may be 50."),
  34. },
  35. },
  36. async handler(args, ctx) {
  37. const { orderId, compare, customerId, shop, includeSubshops, statusCode, fromDate, toDate, offset, limit } =
  38. args as {
  39. orderId?: number;
  40. compare?: "below" | "above";
  41. customerId?: number;
  42. shop?: string;
  43. includeSubshops?: boolean;
  44. statusCode?: string;
  45. fromDate?: string;
  46. toDate?: string;
  47. offset?: number;
  48. limit?: number;
  49. };
  50. if (customerId !== undefined && (fromDate === undefined || toDate === undefined)) {
  51. return {
  52. content: [
  53. { type: "text", text: "customerId must be combined with both fromDate and toDate (the API rejects it otherwise)." },
  54. ],
  55. isError: true,
  56. };
  57. }
  58. const params: PlinthParams = {
  59. order_ref: orderId,
  60. ref_cmp: compare,
  61. customer_ref: customerId,
  62. shop,
  63. subshops: shop !== undefined && includeSubshops !== undefined ? (includeSubshops ? 1 : 0) : undefined,
  64. status_code: statusCode,
  65. since: fromDate !== undefined ? toRequestDate(fromDate) : undefined,
  66. until: toDate !== undefined ? toRequestDate(toDate) : undefined,
  67. offset,
  68. limit,
  69. };
  70. const body = await plinthRequest(ctx, "orders.fetch", params);
  71. return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
  72. },
  73. };