@burdenoff/mcp-libs 2026.626.1 → 2026.627.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/modules/assethandler/index.cjs +1601 -0
- package/dist/modules/assethandler/index.cjs.map +1 -0
- package/dist/modules/assethandler/index.d.cts +8 -0
- package/dist/modules/assethandler/index.d.ts +8 -0
- package/dist/modules/assethandler/index.js +1540 -0
- package/dist/modules/assethandler/index.js.map +1 -0
- package/dist/modules/crewfoundry/index.cjs +1598 -0
- package/dist/modules/crewfoundry/index.cjs.map +1 -0
- package/dist/modules/crewfoundry/index.d.cts +8 -0
- package/dist/modules/crewfoundry/index.d.ts +8 -0
- package/dist/modules/crewfoundry/index.js +1537 -0
- package/dist/modules/crewfoundry/index.js.map +1 -0
- package/dist/modules/eventfullymanaged/index.cjs +3364 -0
- package/dist/modules/eventfullymanaged/index.cjs.map +1 -0
- package/dist/modules/eventfullymanaged/index.d.cts +8 -0
- package/dist/modules/eventfullymanaged/index.d.ts +8 -0
- package/dist/modules/eventfullymanaged/index.js +3303 -0
- package/dist/modules/eventfullymanaged/index.js.map +1 -0
- package/dist/modules/ggnomad/index.cjs +1068 -0
- package/dist/modules/ggnomad/index.cjs.map +1 -0
- package/dist/modules/ggnomad/index.d.cts +8 -0
- package/dist/modules/ggnomad/index.d.ts +8 -0
- package/dist/modules/ggnomad/index.js +1007 -0
- package/dist/modules/ggnomad/index.js.map +1 -0
- package/dist/modules/headshotmarketing/index.cjs +5031 -0
- package/dist/modules/headshotmarketing/index.cjs.map +1 -0
- package/dist/modules/headshotmarketing/index.d.cts +8 -0
- package/dist/modules/headshotmarketing/index.d.ts +8 -0
- package/dist/modules/headshotmarketing/index.js +4970 -0
- package/dist/modules/headshotmarketing/index.js.map +1 -0
- package/dist/modules/housingvista/index.cjs +1092 -0
- package/dist/modules/housingvista/index.cjs.map +1 -0
- package/dist/modules/housingvista/index.d.cts +8 -0
- package/dist/modules/housingvista/index.d.ts +8 -0
- package/dist/modules/housingvista/index.js +1031 -0
- package/dist/modules/housingvista/index.js.map +1 -0
- package/dist/modules/movethewheels/index.cjs +3915 -0
- package/dist/modules/movethewheels/index.cjs.map +1 -0
- package/dist/modules/movethewheels/index.d.cts +8 -0
- package/dist/modules/movethewheels/index.d.ts +8 -0
- package/dist/modules/movethewheels/index.js +3854 -0
- package/dist/modules/movethewheels/index.js.map +1 -0
- package/dist/modules/planmagnet/index.cjs +6171 -0
- package/dist/modules/planmagnet/index.cjs.map +1 -0
- package/dist/modules/planmagnet/index.d.cts +8 -0
- package/dist/modules/planmagnet/index.d.ts +8 -0
- package/dist/modules/planmagnet/index.js +6110 -0
- package/dist/modules/planmagnet/index.js.map +1 -0
- package/dist/modules/proserviceworld/index.cjs +1404 -0
- package/dist/modules/proserviceworld/index.cjs.map +1 -0
- package/dist/modules/proserviceworld/index.d.cts +8 -0
- package/dist/modules/proserviceworld/index.d.ts +8 -0
- package/dist/modules/proserviceworld/index.js +1343 -0
- package/dist/modules/proserviceworld/index.js.map +1 -0
- package/dist/modules/timecampus/index.cjs +6482 -0
- package/dist/modules/timecampus/index.cjs.map +1 -0
- package/dist/modules/timecampus/index.d.cts +8 -0
- package/dist/modules/timecampus/index.d.ts +8 -0
- package/dist/modules/timecampus/index.js +6421 -0
- package/dist/modules/timecampus/index.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,3854 @@
|
|
|
1
|
+
import { mcpToolName, errorResult, jsonResult, mcpResourceUri, mcpPromptName } from '../../chunk-H2OYWLOA.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
// src/modules/movethewheels/operations.ts
|
|
5
|
+
var CONTEXT_FIELDS = `context { appId projectId userId workspaceId custom }`;
|
|
6
|
+
var AUDIT_FIELDS = `workspaceId createdBy onBehalfOf updatedBy createdAt updatedAt deletedAt`;
|
|
7
|
+
var CUSTOMER_FIELDS = `
|
|
8
|
+
id name email phone company type address taxId businessLicense rating
|
|
9
|
+
${AUDIT_FIELDS} ${CONTEXT_FIELDS}
|
|
10
|
+
`;
|
|
11
|
+
var DRIVER_FIELDS = `
|
|
12
|
+
id firstName lastName email phone employeeId userId vehicleId status
|
|
13
|
+
licenseNumber licenseClass licenseExpiry certifications currentLocation
|
|
14
|
+
performanceMetrics rating ${AUDIT_FIELDS} ${CONTEXT_FIELDS}
|
|
15
|
+
`;
|
|
16
|
+
var VEHICLE_FIELDS = `
|
|
17
|
+
id vehicleNumber type status make model year licensePlate capacity
|
|
18
|
+
currentLocation fuelLevel mileage ownershipType ${AUDIT_FIELDS} ${CONTEXT_FIELDS}
|
|
19
|
+
`;
|
|
20
|
+
var PRODUCT_FIELDS = `
|
|
21
|
+
id name sku description category brand weight value dimensions fragile
|
|
22
|
+
hazardous temperatureControlled ${AUDIT_FIELDS} ${CONTEXT_FIELDS}
|
|
23
|
+
`;
|
|
24
|
+
var ORDER_STATUS_HISTORY_FIELDS = `
|
|
25
|
+
id orderId status previousStatus notes location createdAt
|
|
26
|
+
`;
|
|
27
|
+
var ORDER_FIELDS = `
|
|
28
|
+
id orderNumber customerId driverId vehicleId orderType status priority
|
|
29
|
+
pickupAddress deliveryAddress items pricing trackingNumber scheduledDelivery
|
|
30
|
+
specialInstructions ${AUDIT_FIELDS} ${CONTEXT_FIELDS}
|
|
31
|
+
`;
|
|
32
|
+
var SHIPMENT_FIELDS = `
|
|
33
|
+
id shipmentNumber orderId customerId carrierId origin destination status
|
|
34
|
+
trackingNumber cargoDetails estimatedDelivery actualDelivery
|
|
35
|
+
${AUDIT_FIELDS} ${CONTEXT_FIELDS}
|
|
36
|
+
`;
|
|
37
|
+
var TRACKING_EVENT_FIELDS = `
|
|
38
|
+
id shipmentId eventType details location timestamp
|
|
39
|
+
workspaceId createdBy onBehalfOf updatedBy createdAt updatedAt deletedAt
|
|
40
|
+
${CONTEXT_FIELDS}
|
|
41
|
+
`;
|
|
42
|
+
var TRACKING_ALERT_FIELDS = `
|
|
43
|
+
shipmentId shipmentNumber alertType message
|
|
44
|
+
`;
|
|
45
|
+
var ROUTE_FIELDS = `
|
|
46
|
+
id name status shipmentId driverId vehicleId waypoints estimatedDistance
|
|
47
|
+
actualDistance estimatedDuration actualDuration fuelCost
|
|
48
|
+
${AUDIT_FIELDS} ${CONTEXT_FIELDS}
|
|
49
|
+
`;
|
|
50
|
+
var WAREHOUSE_LOCATION_FIELDS = `
|
|
51
|
+
id warehouseId locationCode locationType status zone aisle rack shelf bin
|
|
52
|
+
capacity ${AUDIT_FIELDS} ${CONTEXT_FIELDS}
|
|
53
|
+
`;
|
|
54
|
+
var WAREHOUSE_FIELDS = `
|
|
55
|
+
id name code type status address contactInfo operatingHours capabilities
|
|
56
|
+
totalCapacity usedCapacity ${AUDIT_FIELDS} ${CONTEXT_FIELDS}
|
|
57
|
+
`;
|
|
58
|
+
var INVENTORY_ITEM_FIELDS = `
|
|
59
|
+
id productId warehouseId locationId onHand available allocated damaged
|
|
60
|
+
reorderPoint safetyStock unitCost ${AUDIT_FIELDS} ${CONTEXT_FIELDS}
|
|
61
|
+
`;
|
|
62
|
+
var INVENTORY_TRANSACTION_FIELDS = `
|
|
63
|
+
id transactionNumber type productId warehouseId inventoryItemId quantity
|
|
64
|
+
unitCost referenceType referenceId notes ${AUDIT_FIELDS} ${CONTEXT_FIELDS}
|
|
65
|
+
`;
|
|
66
|
+
var SERVICE_OFFERING_FIELDS = `
|
|
67
|
+
id name providerId category description price currency availability
|
|
68
|
+
${AUDIT_FIELDS} ${CONTEXT_FIELDS}
|
|
69
|
+
`;
|
|
70
|
+
var SERVICE_PROVIDER_FIELDS = `
|
|
71
|
+
id name type description isActive isVerified rating services contactInfo
|
|
72
|
+
${AUDIT_FIELDS} ${CONTEXT_FIELDS}
|
|
73
|
+
`;
|
|
74
|
+
var REPORT_FIELDS = `
|
|
75
|
+
id name type status description configuration schedule lastGenerated
|
|
76
|
+
${AUDIT_FIELDS} ${CONTEXT_FIELDS}
|
|
77
|
+
`;
|
|
78
|
+
var GET_CUSTOMER = `
|
|
79
|
+
query MovethewheelsGetCustomer($id: ID!) {
|
|
80
|
+
customer(id: $id) { ${CUSTOMER_FIELDS} }
|
|
81
|
+
}
|
|
82
|
+
`;
|
|
83
|
+
var LIST_CUSTOMERS = `
|
|
84
|
+
query MovethewheelsListCustomers($filter: CustomerFilterInput, $pagination: PaginationInput) {
|
|
85
|
+
customers(filter: $filter, pagination: $pagination) { ${CUSTOMER_FIELDS} }
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
var CREATE_CUSTOMER = `
|
|
89
|
+
mutation MovethewheelsCreateCustomer($input: CreateCustomerInput!) {
|
|
90
|
+
createCustomer(input: $input) { ${CUSTOMER_FIELDS} }
|
|
91
|
+
}
|
|
92
|
+
`;
|
|
93
|
+
var UPDATE_CUSTOMER = `
|
|
94
|
+
mutation MovethewheelsUpdateCustomer($id: ID!, $input: UpdateCustomerInput!) {
|
|
95
|
+
updateCustomer(id: $id, input: $input) { ${CUSTOMER_FIELDS} }
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
var DELETE_CUSTOMER = `
|
|
99
|
+
mutation MovethewheelsDeleteCustomer($id: ID!) {
|
|
100
|
+
deleteCustomer(id: $id)
|
|
101
|
+
}
|
|
102
|
+
`;
|
|
103
|
+
var GET_DRIVER = `
|
|
104
|
+
query MovethewheelsGetDriver($id: ID!) {
|
|
105
|
+
driver(id: $id) { ${DRIVER_FIELDS} }
|
|
106
|
+
}
|
|
107
|
+
`;
|
|
108
|
+
var LIST_DRIVERS = `
|
|
109
|
+
query MovethewheelsListDrivers($filter: DriverFilterInput, $pagination: PaginationInput) {
|
|
110
|
+
drivers(filter: $filter, pagination: $pagination) { ${DRIVER_FIELDS} }
|
|
111
|
+
}
|
|
112
|
+
`;
|
|
113
|
+
var CREATE_DRIVER = `
|
|
114
|
+
mutation MovethewheelsCreateDriver($input: CreateDriverInput!) {
|
|
115
|
+
createDriver(input: $input) { ${DRIVER_FIELDS} }
|
|
116
|
+
}
|
|
117
|
+
`;
|
|
118
|
+
var UPDATE_DRIVER = `
|
|
119
|
+
mutation MovethewheelsUpdateDriver($id: ID!, $input: UpdateDriverInput!) {
|
|
120
|
+
updateDriver(id: $id, input: $input) { ${DRIVER_FIELDS} }
|
|
121
|
+
}
|
|
122
|
+
`;
|
|
123
|
+
var UPDATE_DRIVER_STATUS = `
|
|
124
|
+
mutation MovethewheelsUpdateDriverStatus($id: ID!, $status: DriverStatus!) {
|
|
125
|
+
updateDriverStatus(id: $id, status: $status) { ${DRIVER_FIELDS} }
|
|
126
|
+
}
|
|
127
|
+
`;
|
|
128
|
+
var ASSIGN_DRIVER_VEHICLE = `
|
|
129
|
+
mutation MovethewheelsAssignDriverVehicle($id: ID!, $vehicleId: ID) {
|
|
130
|
+
assignDriverVehicle(id: $id, vehicleId: $vehicleId) { ${DRIVER_FIELDS} }
|
|
131
|
+
}
|
|
132
|
+
`;
|
|
133
|
+
var DELETE_DRIVER = `
|
|
134
|
+
mutation MovethewheelsDeleteDriver($id: ID!) {
|
|
135
|
+
deleteDriver(id: $id)
|
|
136
|
+
}
|
|
137
|
+
`;
|
|
138
|
+
var GET_VEHICLE = `
|
|
139
|
+
query MovethewheelsGetVehicle($id: ID!) {
|
|
140
|
+
vehicle(id: $id) { ${VEHICLE_FIELDS} }
|
|
141
|
+
}
|
|
142
|
+
`;
|
|
143
|
+
var LIST_VEHICLES = `
|
|
144
|
+
query MovethewheelsListVehicles($filter: VehicleFilterInput, $pagination: PaginationInput) {
|
|
145
|
+
vehicles(filter: $filter, pagination: $pagination) { ${VEHICLE_FIELDS} }
|
|
146
|
+
}
|
|
147
|
+
`;
|
|
148
|
+
var CREATE_VEHICLE = `
|
|
149
|
+
mutation MovethewheelsCreateVehicle($input: CreateVehicleInput!) {
|
|
150
|
+
createVehicle(input: $input) { ${VEHICLE_FIELDS} }
|
|
151
|
+
}
|
|
152
|
+
`;
|
|
153
|
+
var UPDATE_VEHICLE = `
|
|
154
|
+
mutation MovethewheelsUpdateVehicle($id: ID!, $input: UpdateVehicleInput!) {
|
|
155
|
+
updateVehicle(id: $id, input: $input) { ${VEHICLE_FIELDS} }
|
|
156
|
+
}
|
|
157
|
+
`;
|
|
158
|
+
var UPDATE_VEHICLE_STATUS = `
|
|
159
|
+
mutation MovethewheelsUpdateVehicleStatus($id: ID!, $status: VehicleStatus!) {
|
|
160
|
+
updateVehicleStatus(id: $id, status: $status) { ${VEHICLE_FIELDS} }
|
|
161
|
+
}
|
|
162
|
+
`;
|
|
163
|
+
var UPDATE_VEHICLE_LOCATION = `
|
|
164
|
+
mutation MovethewheelsUpdateVehicleLocation($id: ID!, $location: JSON!) {
|
|
165
|
+
updateVehicleLocation(id: $id, location: $location) { ${VEHICLE_FIELDS} }
|
|
166
|
+
}
|
|
167
|
+
`;
|
|
168
|
+
var DELETE_VEHICLE = `
|
|
169
|
+
mutation MovethewheelsDeleteVehicle($id: ID!) {
|
|
170
|
+
deleteVehicle(id: $id)
|
|
171
|
+
}
|
|
172
|
+
`;
|
|
173
|
+
var GET_PRODUCT = `
|
|
174
|
+
query MovethewheelsGetProduct($id: ID!) {
|
|
175
|
+
product(id: $id) { ${PRODUCT_FIELDS} }
|
|
176
|
+
}
|
|
177
|
+
`;
|
|
178
|
+
var LIST_PRODUCTS = `
|
|
179
|
+
query MovethewheelsListProducts($filter: ProductFilterInput, $pagination: PaginationInput) {
|
|
180
|
+
products(filter: $filter, pagination: $pagination) { ${PRODUCT_FIELDS} }
|
|
181
|
+
}
|
|
182
|
+
`;
|
|
183
|
+
var CREATE_PRODUCT = `
|
|
184
|
+
mutation MovethewheelsCreateProduct($input: CreateProductInput!) {
|
|
185
|
+
createProduct(input: $input) { ${PRODUCT_FIELDS} }
|
|
186
|
+
}
|
|
187
|
+
`;
|
|
188
|
+
var UPDATE_PRODUCT = `
|
|
189
|
+
mutation MovethewheelsUpdateProduct($id: ID!, $input: UpdateProductInput!) {
|
|
190
|
+
updateProduct(id: $id, input: $input) { ${PRODUCT_FIELDS} }
|
|
191
|
+
}
|
|
192
|
+
`;
|
|
193
|
+
var DELETE_PRODUCT = `
|
|
194
|
+
mutation MovethewheelsDeleteProduct($id: ID!) {
|
|
195
|
+
deleteProduct(id: $id)
|
|
196
|
+
}
|
|
197
|
+
`;
|
|
198
|
+
var GET_ORDER = `
|
|
199
|
+
query MovethewheelsGetOrder($id: ID!) {
|
|
200
|
+
movethewheelsOrder(id: $id) {
|
|
201
|
+
${ORDER_FIELDS}
|
|
202
|
+
customer { id name email phone }
|
|
203
|
+
driver { id firstName lastName status }
|
|
204
|
+
vehicle { id vehicleNumber type status }
|
|
205
|
+
shipment { id shipmentNumber status }
|
|
206
|
+
statusHistory { ${ORDER_STATUS_HISTORY_FIELDS} }
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
`;
|
|
210
|
+
var LIST_ORDERS = `
|
|
211
|
+
query MovethewheelsListOrders($filter: OrderFilterInput, $pagination: PaginationInput) {
|
|
212
|
+
movethewheelsOrders(filter: $filter, pagination: $pagination) { ${ORDER_FIELDS} }
|
|
213
|
+
}
|
|
214
|
+
`;
|
|
215
|
+
var CREATE_ORDER = `
|
|
216
|
+
mutation MovethewheelsCreateOrder($input: CreateOrderInput!) {
|
|
217
|
+
createMovethewheelsOrder(input: $input) { ${ORDER_FIELDS} }
|
|
218
|
+
}
|
|
219
|
+
`;
|
|
220
|
+
var UPDATE_ORDER = `
|
|
221
|
+
mutation MovethewheelsUpdateOrder($id: ID!, $input: UpdateOrderInput!) {
|
|
222
|
+
updateMovethewheelsOrder(id: $id, input: $input) { ${ORDER_FIELDS} }
|
|
223
|
+
}
|
|
224
|
+
`;
|
|
225
|
+
var UPDATE_ORDER_STATUS = `
|
|
226
|
+
mutation MovethewheelsUpdateOrderStatus($id: ID!, $input: UpdateOrderStatusInput!) {
|
|
227
|
+
updateMovethewheelsOrderStatus(id: $id, input: $input) {
|
|
228
|
+
${ORDER_FIELDS}
|
|
229
|
+
statusHistory { ${ORDER_STATUS_HISTORY_FIELDS} }
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
`;
|
|
233
|
+
var DELETE_ORDER = `
|
|
234
|
+
mutation MovethewheelsDeleteOrder($id: ID!) {
|
|
235
|
+
deleteMovethewheelsOrder(id: $id)
|
|
236
|
+
}
|
|
237
|
+
`;
|
|
238
|
+
var GET_SHIPMENT = `
|
|
239
|
+
query MovethewheelsGetShipment($id: ID!) {
|
|
240
|
+
shipment(id: $id) {
|
|
241
|
+
${SHIPMENT_FIELDS}
|
|
242
|
+
order { id orderNumber status }
|
|
243
|
+
trackingEvents { ${TRACKING_EVENT_FIELDS} }
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
`;
|
|
247
|
+
var LIST_SHIPMENTS = `
|
|
248
|
+
query MovethewheelsListShipments($filter: ShipmentFilterInput, $pagination: PaginationInput) {
|
|
249
|
+
shipments(filter: $filter, pagination: $pagination) { ${SHIPMENT_FIELDS} }
|
|
250
|
+
}
|
|
251
|
+
`;
|
|
252
|
+
var LIST_ACTIVE_DELIVERIES = `
|
|
253
|
+
query MovethewheelsActiveDeliveries($pagination: PaginationInput) {
|
|
254
|
+
activeDeliveries(pagination: $pagination) { ${SHIPMENT_FIELDS} }
|
|
255
|
+
}
|
|
256
|
+
`;
|
|
257
|
+
var CREATE_SHIPMENT = `
|
|
258
|
+
mutation MovethewheelsCreateShipment($input: CreateShipmentInput!) {
|
|
259
|
+
createShipment(input: $input) { ${SHIPMENT_FIELDS} }
|
|
260
|
+
}
|
|
261
|
+
`;
|
|
262
|
+
var UPDATE_SHIPMENT = `
|
|
263
|
+
mutation MovethewheelsUpdateShipment($id: ID!, $input: UpdateShipmentInput!) {
|
|
264
|
+
updateShipment(id: $id, input: $input) { ${SHIPMENT_FIELDS} }
|
|
265
|
+
}
|
|
266
|
+
`;
|
|
267
|
+
var DELETE_SHIPMENT = `
|
|
268
|
+
mutation MovethewheelsDeleteShipment($id: ID!) {
|
|
269
|
+
deleteShipment(id: $id)
|
|
270
|
+
}
|
|
271
|
+
`;
|
|
272
|
+
var GET_ORDER_TRACKING = `
|
|
273
|
+
query MovethewheelsOrderTracking($orderId: ID!, $pagination: PaginationInput) {
|
|
274
|
+
orderTracking(orderId: $orderId, pagination: $pagination) { ${TRACKING_EVENT_FIELDS} }
|
|
275
|
+
}
|
|
276
|
+
`;
|
|
277
|
+
var GET_SHIPMENT_TRACKING = `
|
|
278
|
+
query MovethewheelsShipmentTracking($shipmentId: ID!, $pagination: PaginationInput) {
|
|
279
|
+
shipmentTracking(shipmentId: $shipmentId, pagination: $pagination) { ${TRACKING_EVENT_FIELDS} }
|
|
280
|
+
}
|
|
281
|
+
`;
|
|
282
|
+
var LIST_TRACKING_ALERTS = `
|
|
283
|
+
query MovethewheelsTrackingAlerts($pagination: PaginationInput) {
|
|
284
|
+
trackingAlerts(pagination: $pagination) { ${TRACKING_ALERT_FIELDS} }
|
|
285
|
+
}
|
|
286
|
+
`;
|
|
287
|
+
var ADD_TRACKING_EVENT = `
|
|
288
|
+
mutation MovethewheelsAddTrackingEvent($input: AddTrackingEventInput!) {
|
|
289
|
+
addTrackingEvent(input: $input) { ${TRACKING_EVENT_FIELDS} }
|
|
290
|
+
}
|
|
291
|
+
`;
|
|
292
|
+
var GET_ROUTE = `
|
|
293
|
+
query MovethewheelsGetRoute($id: ID!) {
|
|
294
|
+
route(id: $id) {
|
|
295
|
+
${ROUTE_FIELDS}
|
|
296
|
+
driver { id firstName lastName }
|
|
297
|
+
vehicle { id vehicleNumber type }
|
|
298
|
+
shipment { id shipmentNumber status }
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
`;
|
|
302
|
+
var LIST_ROUTES = `
|
|
303
|
+
query MovethewheelsListRoutes($filter: RouteFilterInput, $pagination: PaginationInput) {
|
|
304
|
+
routes(filter: $filter, pagination: $pagination) { ${ROUTE_FIELDS} }
|
|
305
|
+
}
|
|
306
|
+
`;
|
|
307
|
+
var CREATE_ROUTE = `
|
|
308
|
+
mutation MovethewheelsCreateRoute($input: CreateRouteInput!) {
|
|
309
|
+
createRoute(input: $input) { ${ROUTE_FIELDS} }
|
|
310
|
+
}
|
|
311
|
+
`;
|
|
312
|
+
var UPDATE_ROUTE = `
|
|
313
|
+
mutation MovethewheelsUpdateRoute($id: ID!, $input: UpdateRouteInput!) {
|
|
314
|
+
updateRoute(id: $id, input: $input) { ${ROUTE_FIELDS} }
|
|
315
|
+
}
|
|
316
|
+
`;
|
|
317
|
+
var OPTIMIZE_ROUTE = `
|
|
318
|
+
mutation MovethewheelsOptimizeRoute($id: ID!) {
|
|
319
|
+
optimizeRoute(id: $id) { ${ROUTE_FIELDS} }
|
|
320
|
+
}
|
|
321
|
+
`;
|
|
322
|
+
var DELETE_ROUTE = `
|
|
323
|
+
mutation MovethewheelsDeleteRoute($id: ID!) {
|
|
324
|
+
deleteRoute(id: $id)
|
|
325
|
+
}
|
|
326
|
+
`;
|
|
327
|
+
var GET_WAREHOUSE = `
|
|
328
|
+
query MovethewheelsGetWarehouse($id: ID!) {
|
|
329
|
+
warehouse(id: $id) {
|
|
330
|
+
${WAREHOUSE_FIELDS}
|
|
331
|
+
locations { ${WAREHOUSE_LOCATION_FIELDS} }
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
`;
|
|
335
|
+
var LIST_WAREHOUSES = `
|
|
336
|
+
query MovethewheelsListWarehouses($filter: WarehouseFilterInput, $pagination: PaginationInput) {
|
|
337
|
+
warehouses(filter: $filter, pagination: $pagination) { ${WAREHOUSE_FIELDS} }
|
|
338
|
+
}
|
|
339
|
+
`;
|
|
340
|
+
var CREATE_WAREHOUSE = `
|
|
341
|
+
mutation MovethewheelsCreateWarehouse($input: CreateWarehouseInput!) {
|
|
342
|
+
createWarehouse(input: $input) { ${WAREHOUSE_FIELDS} }
|
|
343
|
+
}
|
|
344
|
+
`;
|
|
345
|
+
var UPDATE_WAREHOUSE = `
|
|
346
|
+
mutation MovethewheelsUpdateWarehouse($id: ID!, $input: UpdateWarehouseInput!) {
|
|
347
|
+
updateWarehouse(id: $id, input: $input) { ${WAREHOUSE_FIELDS} }
|
|
348
|
+
}
|
|
349
|
+
`;
|
|
350
|
+
var DELETE_WAREHOUSE = `
|
|
351
|
+
mutation MovethewheelsDeleteWarehouse($id: ID!) {
|
|
352
|
+
deleteWarehouse(id: $id)
|
|
353
|
+
}
|
|
354
|
+
`;
|
|
355
|
+
var CREATE_WAREHOUSE_LOCATION = `
|
|
356
|
+
mutation MovethewheelsCreateWarehouseLocation($input: CreateWarehouseLocationInput!) {
|
|
357
|
+
createWarehouseLocation(input: $input) { ${WAREHOUSE_LOCATION_FIELDS} }
|
|
358
|
+
}
|
|
359
|
+
`;
|
|
360
|
+
var UPDATE_WAREHOUSE_LOCATION = `
|
|
361
|
+
mutation MovethewheelsUpdateWarehouseLocation($id: ID!, $input: UpdateWarehouseLocationInput!) {
|
|
362
|
+
updateWarehouseLocation(id: $id, input: $input) { ${WAREHOUSE_LOCATION_FIELDS} }
|
|
363
|
+
}
|
|
364
|
+
`;
|
|
365
|
+
var GET_INVENTORY_ITEM = `
|
|
366
|
+
query MovethewheelsGetInventoryItem($id: ID!) {
|
|
367
|
+
inventoryItem(id: $id) {
|
|
368
|
+
${INVENTORY_ITEM_FIELDS}
|
|
369
|
+
product { id name sku }
|
|
370
|
+
warehouse { id name code }
|
|
371
|
+
location { id locationCode }
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
`;
|
|
375
|
+
var LIST_INVENTORY_ITEMS = `
|
|
376
|
+
query MovethewheelsListInventoryItems($filter: InventoryFilterInput, $pagination: PaginationInput) {
|
|
377
|
+
inventoryItems(filter: $filter, pagination: $pagination) { ${INVENTORY_ITEM_FIELDS} }
|
|
378
|
+
}
|
|
379
|
+
`;
|
|
380
|
+
var GET_INVENTORY_STATS = `
|
|
381
|
+
query MovethewheelsInventoryStats {
|
|
382
|
+
inventoryStats {
|
|
383
|
+
lowStockCount
|
|
384
|
+
totalAllocated
|
|
385
|
+
totalAvailable
|
|
386
|
+
totalDamaged
|
|
387
|
+
totalOnHand
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
`;
|
|
391
|
+
var LIST_INVENTORY_TRANSACTIONS = `
|
|
392
|
+
query MovethewheelsListInventoryTransactions(
|
|
393
|
+
$filter: InventoryFilterInput
|
|
394
|
+
$pagination: PaginationInput
|
|
395
|
+
) {
|
|
396
|
+
inventoryTransactions(filter: $filter, pagination: $pagination) {
|
|
397
|
+
${INVENTORY_TRANSACTION_FIELDS}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
`;
|
|
401
|
+
var CREATE_INVENTORY_ITEM = `
|
|
402
|
+
mutation MovethewheelsCreateInventoryItem($input: CreateInventoryItemInput!) {
|
|
403
|
+
createInventoryItem(input: $input) { ${INVENTORY_ITEM_FIELDS} }
|
|
404
|
+
}
|
|
405
|
+
`;
|
|
406
|
+
var UPDATE_INVENTORY_ITEM = `
|
|
407
|
+
mutation MovethewheelsUpdateInventoryItem($id: ID!, $input: UpdateInventoryItemInput!) {
|
|
408
|
+
updateInventoryItem(id: $id, input: $input) { ${INVENTORY_ITEM_FIELDS} }
|
|
409
|
+
}
|
|
410
|
+
`;
|
|
411
|
+
var ADJUST_INVENTORY_STOCK = `
|
|
412
|
+
mutation MovethewheelsAdjustInventoryStock($input: AdjustInventoryStockInput!) {
|
|
413
|
+
adjustInventoryStock(input: $input) { ${INVENTORY_ITEM_FIELDS} }
|
|
414
|
+
}
|
|
415
|
+
`;
|
|
416
|
+
var DELETE_INVENTORY_ITEM = `
|
|
417
|
+
mutation MovethewheelsDeleteInventoryItem($id: ID!) {
|
|
418
|
+
deleteInventoryItem(id: $id)
|
|
419
|
+
}
|
|
420
|
+
`;
|
|
421
|
+
var CREATE_INVENTORY_TRANSACTION = `
|
|
422
|
+
mutation MovethewheelsCreateInventoryTransaction($input: CreateInventoryTransactionInput!) {
|
|
423
|
+
createInventoryTransaction(input: $input) { ${INVENTORY_TRANSACTION_FIELDS} }
|
|
424
|
+
}
|
|
425
|
+
`;
|
|
426
|
+
var GET_SERVICE_PROVIDER = `
|
|
427
|
+
query MovethewheelsGetServiceProvider($id: ID!) {
|
|
428
|
+
serviceProvider(id: $id) {
|
|
429
|
+
${SERVICE_PROVIDER_FIELDS}
|
|
430
|
+
offerings { ${SERVICE_OFFERING_FIELDS} }
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
`;
|
|
434
|
+
var LIST_SERVICE_PROVIDERS = `
|
|
435
|
+
query MovethewheelsListServiceProviders(
|
|
436
|
+
$filter: ServiceProviderFilterInput
|
|
437
|
+
$pagination: PaginationInput
|
|
438
|
+
) {
|
|
439
|
+
serviceProviders(filter: $filter, pagination: $pagination) { ${SERVICE_PROVIDER_FIELDS} }
|
|
440
|
+
}
|
|
441
|
+
`;
|
|
442
|
+
var CREATE_SERVICE_PROVIDER = `
|
|
443
|
+
mutation MovethewheelsCreateServiceProvider($input: CreateServiceProviderInput!) {
|
|
444
|
+
createServiceProvider(input: $input) { ${SERVICE_PROVIDER_FIELDS} }
|
|
445
|
+
}
|
|
446
|
+
`;
|
|
447
|
+
var UPDATE_SERVICE_PROVIDER = `
|
|
448
|
+
mutation MovethewheelsUpdateServiceProvider($id: ID!, $input: UpdateServiceProviderInput!) {
|
|
449
|
+
updateServiceProvider(id: $id, input: $input) { ${SERVICE_PROVIDER_FIELDS} }
|
|
450
|
+
}
|
|
451
|
+
`;
|
|
452
|
+
var DELETE_SERVICE_PROVIDER = `
|
|
453
|
+
mutation MovethewheelsDeleteServiceProvider($id: ID!) {
|
|
454
|
+
deleteServiceProvider(id: $id)
|
|
455
|
+
}
|
|
456
|
+
`;
|
|
457
|
+
var GET_SERVICE_OFFERING = `
|
|
458
|
+
query MovethewheelsGetServiceOffering($id: ID!) {
|
|
459
|
+
serviceOffering(id: $id) {
|
|
460
|
+
${SERVICE_OFFERING_FIELDS}
|
|
461
|
+
provider { id name type }
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
`;
|
|
465
|
+
var LIST_SERVICE_OFFERINGS = `
|
|
466
|
+
query MovethewheelsListServiceOfferings(
|
|
467
|
+
$filter: ServiceOfferingFilterInput
|
|
468
|
+
$pagination: PaginationInput
|
|
469
|
+
) {
|
|
470
|
+
serviceOfferings(filter: $filter, pagination: $pagination) { ${SERVICE_OFFERING_FIELDS} }
|
|
471
|
+
}
|
|
472
|
+
`;
|
|
473
|
+
var CREATE_SERVICE_OFFERING = `
|
|
474
|
+
mutation MovethewheelsCreateServiceOffering($input: CreateServiceOfferingInput!) {
|
|
475
|
+
createServiceOffering(input: $input) { ${SERVICE_OFFERING_FIELDS} }
|
|
476
|
+
}
|
|
477
|
+
`;
|
|
478
|
+
var UPDATE_SERVICE_OFFERING = `
|
|
479
|
+
mutation MovethewheelsUpdateServiceOffering($id: ID!, $input: UpdateServiceOfferingInput!) {
|
|
480
|
+
updateServiceOffering(id: $id, input: $input) { ${SERVICE_OFFERING_FIELDS} }
|
|
481
|
+
}
|
|
482
|
+
`;
|
|
483
|
+
var DELETE_SERVICE_OFFERING = `
|
|
484
|
+
mutation MovethewheelsDeleteServiceOffering($id: ID!) {
|
|
485
|
+
deleteServiceOffering(id: $id)
|
|
486
|
+
}
|
|
487
|
+
`;
|
|
488
|
+
var GET_REPORT = `
|
|
489
|
+
query MovethewheelsGetReport($id: ID!) {
|
|
490
|
+
movethewheelsReport(id: $id) { ${REPORT_FIELDS} }
|
|
491
|
+
}
|
|
492
|
+
`;
|
|
493
|
+
var LIST_REPORTS = `
|
|
494
|
+
query MovethewheelsListReports(
|
|
495
|
+
$filter: MovethewheelsReportFilterInput
|
|
496
|
+
$pagination: PaginationInput
|
|
497
|
+
) {
|
|
498
|
+
movethewheelsReports(filter: $filter, pagination: $pagination) { ${REPORT_FIELDS} }
|
|
499
|
+
}
|
|
500
|
+
`;
|
|
501
|
+
var CREATE_REPORT = `
|
|
502
|
+
mutation MovethewheelsCreateReport($input: CreateMovethewheelsReportInput!) {
|
|
503
|
+
createMovethewheelsReport(input: $input) { ${REPORT_FIELDS} }
|
|
504
|
+
}
|
|
505
|
+
`;
|
|
506
|
+
var UPDATE_REPORT = `
|
|
507
|
+
mutation MovethewheelsUpdateReport($id: ID!, $input: UpdateMovethewheelsReportInput!) {
|
|
508
|
+
updateMovethewheelsReport(id: $id, input: $input) { ${REPORT_FIELDS} }
|
|
509
|
+
}
|
|
510
|
+
`;
|
|
511
|
+
var DELETE_REPORT = `
|
|
512
|
+
mutation MovethewheelsDeleteReport($id: ID!) {
|
|
513
|
+
deleteMovethewheelsReport(id: $id)
|
|
514
|
+
}
|
|
515
|
+
`;
|
|
516
|
+
var GENERATE_REPORT = `
|
|
517
|
+
mutation MovethewheelsGenerateReport($input: GenerateMovethewheelsReportInput!) {
|
|
518
|
+
generateMovethewheelsReport(input: $input) {
|
|
519
|
+
generatedAt
|
|
520
|
+
result
|
|
521
|
+
report { ${REPORT_FIELDS} }
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
`;
|
|
525
|
+
var GET_MODULE_SETTINGS = `
|
|
526
|
+
query MovethewheelsModuleSettings {
|
|
527
|
+
moduleSettings { workspaceId preferences updatedAt }
|
|
528
|
+
}
|
|
529
|
+
`;
|
|
530
|
+
var UPDATE_MODULE_SETTINGS = `
|
|
531
|
+
mutation MovethewheelsUpdateModuleSettings(
|
|
532
|
+
$input: UpdateMovethewheelsModuleSettingsInput!
|
|
533
|
+
) {
|
|
534
|
+
updateModuleSettings(input: $input) { workspaceId preferences updatedAt }
|
|
535
|
+
}
|
|
536
|
+
`;
|
|
537
|
+
var EXPORT_DATA = `
|
|
538
|
+
mutation MovethewheelsExportData($input: MovethewheelsExportDataInput!) {
|
|
539
|
+
exportData(input: $input) { success message payload }
|
|
540
|
+
}
|
|
541
|
+
`;
|
|
542
|
+
var IMPORT_DATA = `
|
|
543
|
+
mutation MovethewheelsImportData($input: MovethewheelsImportDataInput!) {
|
|
544
|
+
importData(input: $input) { success message payload }
|
|
545
|
+
}
|
|
546
|
+
`;
|
|
547
|
+
var GET_ANALYTICS = `
|
|
548
|
+
query MovethewheelsAnalytics {
|
|
549
|
+
analytics {
|
|
550
|
+
orderTrend { label value }
|
|
551
|
+
deliveryTrend { label value }
|
|
552
|
+
revenueTrend { label value }
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
`;
|
|
556
|
+
var GET_DASHBOARD_METRICS = `
|
|
557
|
+
query MovethewheelsDashboardMetrics {
|
|
558
|
+
dashboardMetrics {
|
|
559
|
+
totalOrders
|
|
560
|
+
activeDeliveries
|
|
561
|
+
activeVehicles
|
|
562
|
+
availableDrivers
|
|
563
|
+
lowStockItems
|
|
564
|
+
totalRevenue
|
|
565
|
+
recentOrders { id orderNumber status priority createdAt }
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
`;
|
|
569
|
+
var GET_ORDER_STATS = `
|
|
570
|
+
query MovethewheelsOrderStats {
|
|
571
|
+
orderStats { total pending inTransit delivered cancelled failed }
|
|
572
|
+
}
|
|
573
|
+
`;
|
|
574
|
+
var GET_FLEET_STATS = `
|
|
575
|
+
query MovethewheelsFleetStats {
|
|
576
|
+
fleetStats {
|
|
577
|
+
totalVehicles
|
|
578
|
+
activeVehicles
|
|
579
|
+
inTransitVehicles
|
|
580
|
+
maintenanceVehicles
|
|
581
|
+
totalDrivers
|
|
582
|
+
availableDrivers
|
|
583
|
+
onDeliveryDrivers
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
`;
|
|
587
|
+
var GET_FINANCE_METRICS = `
|
|
588
|
+
query MovethewheelsFinanceMetrics {
|
|
589
|
+
financeMetrics {
|
|
590
|
+
revenue { totalRevenue deliveredRevenue averageOrderValue }
|
|
591
|
+
fuelCost
|
|
592
|
+
openInvoices
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
`;
|
|
596
|
+
var GET_ASSISTANT_INSIGHTS = `
|
|
597
|
+
query MovethewheelsAssistantInsights($question: String!) {
|
|
598
|
+
assistantInsights(question: $question) { question answer generatedAt }
|
|
599
|
+
}
|
|
600
|
+
`;
|
|
601
|
+
var DriverStatusEnum = z.enum(["AVAILABLE", "INACTIVE", "OFF_DUTY", "ON_BREAK", "ON_DELIVERY"]).describe("Availability/duty status of a driver");
|
|
602
|
+
var InventoryTransactionTypeEnum = z.enum(["ADJUSTMENT", "ISSUE", "RECEIPT", "RETURN", "TRANSFER"]).describe("Type of inventory ledger movement");
|
|
603
|
+
var OrderPriorityEnum = z.enum(["CRITICAL", "HIGH", "LOW", "MEDIUM", "URGENT"]).describe("Fulfilment priority of an order");
|
|
604
|
+
var OrderStatusEnum = z.enum([
|
|
605
|
+
"ASSIGNED",
|
|
606
|
+
"CANCELLED",
|
|
607
|
+
"CONFIRMED",
|
|
608
|
+
"DELIVERED",
|
|
609
|
+
"FAILED",
|
|
610
|
+
"IN_TRANSIT",
|
|
611
|
+
"OUT_FOR_DELIVERY",
|
|
612
|
+
"PENDING",
|
|
613
|
+
"PICKED_UP",
|
|
614
|
+
"RETURNED"
|
|
615
|
+
]).describe("Fulfilment status of an order");
|
|
616
|
+
var OrderTypeEnum = z.enum(["B2B", "B2C", "MARKETPLACE", "SUBSCRIPTION"]).describe("Commercial type of an order");
|
|
617
|
+
var RouteStatusEnum = z.enum(["CANCELLED", "COMPLETED", "IN_PROGRESS", "PLANNED"]).describe("Lifecycle status of a route");
|
|
618
|
+
var ShipmentStatusEnum = z.enum(["DELIVERED", "FAILED", "IN_TRANSIT", "PICKED_UP", "PLANNED", "RETURNED"]).describe("Lifecycle status of a shipment");
|
|
619
|
+
var TrackingEventTypeEnum = z.enum([
|
|
620
|
+
"ARRIVED",
|
|
621
|
+
"CREATED",
|
|
622
|
+
"DELIVERED",
|
|
623
|
+
"DEPARTED",
|
|
624
|
+
"FAILED",
|
|
625
|
+
"IN_TRANSIT",
|
|
626
|
+
"OUT_FOR_DELIVERY",
|
|
627
|
+
"PICKED_UP",
|
|
628
|
+
"RETURNED"
|
|
629
|
+
]).describe("Type of shipment tracking event");
|
|
630
|
+
var VehicleStatusEnum = z.enum(["ACTIVE", "DECOMMISSIONED", "INACTIVE", "IN_TRANSIT", "MAINTENANCE"]).describe("Operational status of a vehicle");
|
|
631
|
+
var VehicleTypeEnum = z.enum([
|
|
632
|
+
"BICYCLE",
|
|
633
|
+
"DRONE",
|
|
634
|
+
"FLATBED",
|
|
635
|
+
"MOTORCYCLE",
|
|
636
|
+
"REFRIGERATED",
|
|
637
|
+
"SEMI_TRAILER",
|
|
638
|
+
"TRUCK",
|
|
639
|
+
"VAN"
|
|
640
|
+
]).describe("Class of vehicle in the fleet");
|
|
641
|
+
var WarehouseTypeEnum = z.enum(["COLD_STORAGE", "CROSS_DOCK", "DISTRIBUTION", "FULFILLMENT", "RETAIL"]).describe("Type of warehouse facility");
|
|
642
|
+
var JsonObject = z.record(z.string(), z.unknown());
|
|
643
|
+
var JsonValue = z.union([JsonObject, z.array(z.unknown())]);
|
|
644
|
+
|
|
645
|
+
// src/modules/movethewheels/tools/customers.ts
|
|
646
|
+
function registerCustomerTools(server, ctx, track) {
|
|
647
|
+
server.registerTool(
|
|
648
|
+
track(mcpToolName(ctx.productName, "customers_get")),
|
|
649
|
+
{
|
|
650
|
+
title: "Get Customer",
|
|
651
|
+
description: "Fetch a single Movethewheels customer by ID.",
|
|
652
|
+
inputSchema: { id: z.string().describe("Customer ID") }
|
|
653
|
+
},
|
|
654
|
+
async ({ id }) => {
|
|
655
|
+
try {
|
|
656
|
+
const data = await ctx.gql.workspace.request(GET_CUSTOMER, { id });
|
|
657
|
+
if (!data.customer) return errorResult(`Customer ${id} not found`);
|
|
658
|
+
return jsonResult(data.customer);
|
|
659
|
+
} catch (error) {
|
|
660
|
+
ctx.logger.error("movethewheels customers_get failed", { error: String(error) });
|
|
661
|
+
return errorResult(
|
|
662
|
+
`Failed to get customer: ${error instanceof Error ? error.message : String(error)}`
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
);
|
|
667
|
+
server.registerTool(
|
|
668
|
+
track(mcpToolName(ctx.productName, "customers_list")),
|
|
669
|
+
{
|
|
670
|
+
title: "List Customers",
|
|
671
|
+
description: "List customers in the bound workspace with optional type/text filters and pagination.",
|
|
672
|
+
inputSchema: {
|
|
673
|
+
search: z.string().optional().describe("Free-text search across name/company/email"),
|
|
674
|
+
type: z.string().optional().describe('Filter by customer type, e.g. "b2c" or "b2b"'),
|
|
675
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
676
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
async ({ search, type, limit, offset }) => {
|
|
680
|
+
try {
|
|
681
|
+
const filter = {};
|
|
682
|
+
if (search !== void 0) filter.search = search;
|
|
683
|
+
if (type !== void 0) filter.type = type;
|
|
684
|
+
const data = await ctx.gql.workspace.request(LIST_CUSTOMERS, {
|
|
685
|
+
filter: Object.keys(filter).length > 0 ? filter : void 0,
|
|
686
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
687
|
+
});
|
|
688
|
+
return jsonResult(data.customers);
|
|
689
|
+
} catch (error) {
|
|
690
|
+
ctx.logger.error("movethewheels customers_list failed", { error: String(error) });
|
|
691
|
+
return errorResult(
|
|
692
|
+
`Failed to list customers: ${error instanceof Error ? error.message : String(error)}`
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
);
|
|
697
|
+
server.registerTool(
|
|
698
|
+
track(mcpToolName(ctx.productName, "customers_create")),
|
|
699
|
+
{
|
|
700
|
+
title: "Create Customer",
|
|
701
|
+
description: 'Onboard a new customer. `name` is required; `type` defaults to "b2c".',
|
|
702
|
+
inputSchema: {
|
|
703
|
+
name: z.string().min(1).describe("Customer display name"),
|
|
704
|
+
email: z.string().optional().describe("Contact email"),
|
|
705
|
+
phone: z.string().optional().describe("Contact phone"),
|
|
706
|
+
company: z.string().optional().describe("Company name (B2B)"),
|
|
707
|
+
type: z.string().optional().describe('Customer type, e.g. "b2c" or "b2b" (default "b2c")'),
|
|
708
|
+
address: JsonObject.optional().describe("Address JSON"),
|
|
709
|
+
taxId: z.string().optional().describe("Tax identifier"),
|
|
710
|
+
businessLicense: z.string().optional().describe("Business license identifier"),
|
|
711
|
+
rating: z.number().optional().describe("Customer rating")
|
|
712
|
+
}
|
|
713
|
+
},
|
|
714
|
+
async ({ name, email, phone, company, type, address, taxId, businessLicense, rating }) => {
|
|
715
|
+
try {
|
|
716
|
+
const input = { name };
|
|
717
|
+
if (email !== void 0) input.email = email;
|
|
718
|
+
if (phone !== void 0) input.phone = phone;
|
|
719
|
+
if (company !== void 0) input.company = company;
|
|
720
|
+
if (type !== void 0) input.type = type;
|
|
721
|
+
if (address !== void 0) input.address = address;
|
|
722
|
+
if (taxId !== void 0) input.taxId = taxId;
|
|
723
|
+
if (businessLicense !== void 0) input.businessLicense = businessLicense;
|
|
724
|
+
if (rating !== void 0) input.rating = rating;
|
|
725
|
+
const data = await ctx.gql.workspace.request(CREATE_CUSTOMER, { input });
|
|
726
|
+
return jsonResult(data.createCustomer);
|
|
727
|
+
} catch (error) {
|
|
728
|
+
ctx.logger.error("movethewheels customers_create failed", { error: String(error) });
|
|
729
|
+
return errorResult(
|
|
730
|
+
`Failed to create customer: ${error instanceof Error ? error.message : String(error)}`
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
);
|
|
735
|
+
server.registerTool(
|
|
736
|
+
track(mcpToolName(ctx.productName, "customers_update")),
|
|
737
|
+
{
|
|
738
|
+
title: "Update Customer",
|
|
739
|
+
description: "Update a customer. At least one mutable field must be provided.",
|
|
740
|
+
inputSchema: {
|
|
741
|
+
id: z.string().describe("Customer ID"),
|
|
742
|
+
name: z.string().optional().describe("Updated display name"),
|
|
743
|
+
email: z.string().optional().describe("Updated email"),
|
|
744
|
+
phone: z.string().optional().describe("Updated phone"),
|
|
745
|
+
company: z.string().optional().describe("Updated company"),
|
|
746
|
+
type: z.string().optional().describe("Updated customer type"),
|
|
747
|
+
address: JsonObject.optional().describe("Updated address JSON"),
|
|
748
|
+
taxId: z.string().optional().describe("Updated tax identifier"),
|
|
749
|
+
businessLicense: z.string().optional().describe("Updated business license"),
|
|
750
|
+
rating: z.number().optional().describe("Updated rating")
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
async ({ id, name, email, phone, company, type, address, taxId, businessLicense, rating }) => {
|
|
754
|
+
try {
|
|
755
|
+
const input = {};
|
|
756
|
+
if (name !== void 0) input.name = name;
|
|
757
|
+
if (email !== void 0) input.email = email;
|
|
758
|
+
if (phone !== void 0) input.phone = phone;
|
|
759
|
+
if (company !== void 0) input.company = company;
|
|
760
|
+
if (type !== void 0) input.type = type;
|
|
761
|
+
if (address !== void 0) input.address = address;
|
|
762
|
+
if (taxId !== void 0) input.taxId = taxId;
|
|
763
|
+
if (businessLicense !== void 0) input.businessLicense = businessLicense;
|
|
764
|
+
if (rating !== void 0) input.rating = rating;
|
|
765
|
+
if (Object.keys(input).length === 0) {
|
|
766
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field.");
|
|
767
|
+
}
|
|
768
|
+
const data = await ctx.gql.workspace.request(UPDATE_CUSTOMER, { id, input });
|
|
769
|
+
return jsonResult(data.updateCustomer);
|
|
770
|
+
} catch (error) {
|
|
771
|
+
ctx.logger.error("movethewheels customers_update failed", { error: String(error) });
|
|
772
|
+
return errorResult(
|
|
773
|
+
`Failed to update customer: ${error instanceof Error ? error.message : String(error)}`
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
);
|
|
778
|
+
server.registerTool(
|
|
779
|
+
track(mcpToolName(ctx.productName, "customers_delete")),
|
|
780
|
+
{
|
|
781
|
+
title: "Delete Customer",
|
|
782
|
+
description: "Soft-delete a customer by ID.",
|
|
783
|
+
inputSchema: { id: z.string().describe("Customer ID") }
|
|
784
|
+
},
|
|
785
|
+
async ({ id }) => {
|
|
786
|
+
try {
|
|
787
|
+
const data = await ctx.gql.workspace.request(
|
|
788
|
+
DELETE_CUSTOMER,
|
|
789
|
+
{ id }
|
|
790
|
+
);
|
|
791
|
+
return jsonResult({ deleted: data.deleteCustomer, id });
|
|
792
|
+
} catch (error) {
|
|
793
|
+
ctx.logger.error("movethewheels customers_delete failed", { error: String(error) });
|
|
794
|
+
return errorResult(
|
|
795
|
+
`Failed to delete customer: ${error instanceof Error ? error.message : String(error)}`
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
);
|
|
800
|
+
}
|
|
801
|
+
function registerDriverTools(server, ctx, track) {
|
|
802
|
+
server.registerTool(
|
|
803
|
+
track(mcpToolName(ctx.productName, "drivers_get")),
|
|
804
|
+
{
|
|
805
|
+
title: "Get Driver",
|
|
806
|
+
description: "Fetch a single driver by ID.",
|
|
807
|
+
inputSchema: { id: z.string().describe("Driver ID") }
|
|
808
|
+
},
|
|
809
|
+
async ({ id }) => {
|
|
810
|
+
try {
|
|
811
|
+
const data = await ctx.gql.workspace.request(GET_DRIVER, { id });
|
|
812
|
+
if (!data.driver) return errorResult(`Driver ${id} not found`);
|
|
813
|
+
return jsonResult(data.driver);
|
|
814
|
+
} catch (error) {
|
|
815
|
+
ctx.logger.error("movethewheels drivers_get failed", { error: String(error) });
|
|
816
|
+
return errorResult(
|
|
817
|
+
`Failed to get driver: ${error instanceof Error ? error.message : String(error)}`
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
);
|
|
822
|
+
server.registerTool(
|
|
823
|
+
track(mcpToolName(ctx.productName, "drivers_list")),
|
|
824
|
+
{
|
|
825
|
+
title: "List Drivers",
|
|
826
|
+
description: "List drivers with optional status/vehicle/text filters and pagination.",
|
|
827
|
+
inputSchema: {
|
|
828
|
+
search: z.string().optional().describe("Free-text search across name/employeeId"),
|
|
829
|
+
status: DriverStatusEnum.optional().describe("Filter by driver status"),
|
|
830
|
+
vehicleId: z.string().optional().describe("Filter by assigned vehicle ID"),
|
|
831
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
832
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
833
|
+
}
|
|
834
|
+
},
|
|
835
|
+
async ({ search, status, vehicleId, limit, offset }) => {
|
|
836
|
+
try {
|
|
837
|
+
const filter = {};
|
|
838
|
+
if (search !== void 0) filter.search = search;
|
|
839
|
+
if (status !== void 0) filter.status = status;
|
|
840
|
+
if (vehicleId !== void 0) filter.vehicleId = vehicleId;
|
|
841
|
+
const data = await ctx.gql.workspace.request(LIST_DRIVERS, {
|
|
842
|
+
filter: Object.keys(filter).length > 0 ? filter : void 0,
|
|
843
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
844
|
+
});
|
|
845
|
+
return jsonResult(data.drivers);
|
|
846
|
+
} catch (error) {
|
|
847
|
+
ctx.logger.error("movethewheels drivers_list failed", { error: String(error) });
|
|
848
|
+
return errorResult(
|
|
849
|
+
`Failed to list drivers: ${error instanceof Error ? error.message : String(error)}`
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
);
|
|
854
|
+
server.registerTool(
|
|
855
|
+
track(mcpToolName(ctx.productName, "drivers_create")),
|
|
856
|
+
{
|
|
857
|
+
title: "Create Driver",
|
|
858
|
+
description: "Add a driver to the fleet roster. `employeeId`, `firstName`, and `lastName` are required; `status` defaults to AVAILABLE.",
|
|
859
|
+
inputSchema: {
|
|
860
|
+
employeeId: z.string().min(1).describe("Internal employee identifier"),
|
|
861
|
+
firstName: z.string().min(1).describe("Driver first name"),
|
|
862
|
+
lastName: z.string().min(1).describe("Driver last name"),
|
|
863
|
+
email: z.string().optional().describe("Contact email"),
|
|
864
|
+
phone: z.string().optional().describe("Contact phone"),
|
|
865
|
+
userId: z.string().optional().describe("Linked platform user ID"),
|
|
866
|
+
vehicleId: z.string().optional().describe("Assigned vehicle ID"),
|
|
867
|
+
status: DriverStatusEnum.optional().describe("Initial status (default AVAILABLE)"),
|
|
868
|
+
licenseNumber: z.string().optional().describe("Driving license number"),
|
|
869
|
+
licenseClass: z.string().optional().describe("Driving license class"),
|
|
870
|
+
licenseExpiry: z.string().optional().describe("License expiry (ISO 8601 DateTime)"),
|
|
871
|
+
certifications: JsonObject.optional().describe("Certifications JSON"),
|
|
872
|
+
currentLocation: JsonObject.optional().describe("Current location JSON ({ lat, lng })"),
|
|
873
|
+
performanceMetrics: JsonObject.optional().describe("Performance metrics JSON"),
|
|
874
|
+
rating: z.number().optional().describe("Driver rating")
|
|
875
|
+
}
|
|
876
|
+
},
|
|
877
|
+
async ({
|
|
878
|
+
employeeId,
|
|
879
|
+
firstName,
|
|
880
|
+
lastName,
|
|
881
|
+
email,
|
|
882
|
+
phone,
|
|
883
|
+
userId,
|
|
884
|
+
vehicleId,
|
|
885
|
+
status,
|
|
886
|
+
licenseNumber,
|
|
887
|
+
licenseClass,
|
|
888
|
+
licenseExpiry,
|
|
889
|
+
certifications,
|
|
890
|
+
currentLocation,
|
|
891
|
+
performanceMetrics,
|
|
892
|
+
rating
|
|
893
|
+
}) => {
|
|
894
|
+
try {
|
|
895
|
+
const input = { employeeId, firstName, lastName };
|
|
896
|
+
if (email !== void 0) input.email = email;
|
|
897
|
+
if (phone !== void 0) input.phone = phone;
|
|
898
|
+
if (userId !== void 0) input.userId = userId;
|
|
899
|
+
if (vehicleId !== void 0) input.vehicleId = vehicleId;
|
|
900
|
+
if (status !== void 0) input.status = status;
|
|
901
|
+
if (licenseNumber !== void 0) input.licenseNumber = licenseNumber;
|
|
902
|
+
if (licenseClass !== void 0) input.licenseClass = licenseClass;
|
|
903
|
+
if (licenseExpiry !== void 0) input.licenseExpiry = licenseExpiry;
|
|
904
|
+
if (certifications !== void 0) input.certifications = certifications;
|
|
905
|
+
if (currentLocation !== void 0) input.currentLocation = currentLocation;
|
|
906
|
+
if (performanceMetrics !== void 0) input.performanceMetrics = performanceMetrics;
|
|
907
|
+
if (rating !== void 0) input.rating = rating;
|
|
908
|
+
const data = await ctx.gql.workspace.request(CREATE_DRIVER, { input });
|
|
909
|
+
return jsonResult(data.createDriver);
|
|
910
|
+
} catch (error) {
|
|
911
|
+
ctx.logger.error("movethewheels drivers_create failed", { error: String(error) });
|
|
912
|
+
return errorResult(
|
|
913
|
+
`Failed to create driver: ${error instanceof Error ? error.message : String(error)}`
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
);
|
|
918
|
+
server.registerTool(
|
|
919
|
+
track(mcpToolName(ctx.productName, "drivers_update")),
|
|
920
|
+
{
|
|
921
|
+
title: "Update Driver",
|
|
922
|
+
description: "Update a driver. At least one mutable field must be provided.",
|
|
923
|
+
inputSchema: {
|
|
924
|
+
id: z.string().describe("Driver ID"),
|
|
925
|
+
firstName: z.string().optional().describe("Updated first name"),
|
|
926
|
+
lastName: z.string().optional().describe("Updated last name"),
|
|
927
|
+
email: z.string().optional().describe("Updated email"),
|
|
928
|
+
phone: z.string().optional().describe("Updated phone"),
|
|
929
|
+
userId: z.string().optional().describe("Updated linked user ID"),
|
|
930
|
+
vehicleId: z.string().optional().describe("Updated assigned vehicle ID"),
|
|
931
|
+
status: DriverStatusEnum.optional().describe("Updated status"),
|
|
932
|
+
licenseNumber: z.string().optional().describe("Updated license number"),
|
|
933
|
+
licenseClass: z.string().optional().describe("Updated license class"),
|
|
934
|
+
licenseExpiry: z.string().optional().describe("Updated license expiry (ISO 8601)"),
|
|
935
|
+
certifications: JsonObject.optional().describe("Updated certifications JSON"),
|
|
936
|
+
currentLocation: JsonObject.optional().describe("Updated location JSON"),
|
|
937
|
+
performanceMetrics: JsonObject.optional().describe("Updated performance metrics JSON"),
|
|
938
|
+
rating: z.number().optional().describe("Updated rating")
|
|
939
|
+
}
|
|
940
|
+
},
|
|
941
|
+
async ({
|
|
942
|
+
id,
|
|
943
|
+
firstName,
|
|
944
|
+
lastName,
|
|
945
|
+
email,
|
|
946
|
+
phone,
|
|
947
|
+
userId,
|
|
948
|
+
vehicleId,
|
|
949
|
+
status,
|
|
950
|
+
licenseNumber,
|
|
951
|
+
licenseClass,
|
|
952
|
+
licenseExpiry,
|
|
953
|
+
certifications,
|
|
954
|
+
currentLocation,
|
|
955
|
+
performanceMetrics,
|
|
956
|
+
rating
|
|
957
|
+
}) => {
|
|
958
|
+
try {
|
|
959
|
+
const input = {};
|
|
960
|
+
if (firstName !== void 0) input.firstName = firstName;
|
|
961
|
+
if (lastName !== void 0) input.lastName = lastName;
|
|
962
|
+
if (email !== void 0) input.email = email;
|
|
963
|
+
if (phone !== void 0) input.phone = phone;
|
|
964
|
+
if (userId !== void 0) input.userId = userId;
|
|
965
|
+
if (vehicleId !== void 0) input.vehicleId = vehicleId;
|
|
966
|
+
if (status !== void 0) input.status = status;
|
|
967
|
+
if (licenseNumber !== void 0) input.licenseNumber = licenseNumber;
|
|
968
|
+
if (licenseClass !== void 0) input.licenseClass = licenseClass;
|
|
969
|
+
if (licenseExpiry !== void 0) input.licenseExpiry = licenseExpiry;
|
|
970
|
+
if (certifications !== void 0) input.certifications = certifications;
|
|
971
|
+
if (currentLocation !== void 0) input.currentLocation = currentLocation;
|
|
972
|
+
if (performanceMetrics !== void 0) input.performanceMetrics = performanceMetrics;
|
|
973
|
+
if (rating !== void 0) input.rating = rating;
|
|
974
|
+
if (Object.keys(input).length === 0) {
|
|
975
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field.");
|
|
976
|
+
}
|
|
977
|
+
const data = await ctx.gql.workspace.request(UPDATE_DRIVER, { id, input });
|
|
978
|
+
return jsonResult(data.updateDriver);
|
|
979
|
+
} catch (error) {
|
|
980
|
+
ctx.logger.error("movethewheels drivers_update failed", { error: String(error) });
|
|
981
|
+
return errorResult(
|
|
982
|
+
`Failed to update driver: ${error instanceof Error ? error.message : String(error)}`
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
);
|
|
987
|
+
server.registerTool(
|
|
988
|
+
track(mcpToolName(ctx.productName, "drivers_update_status")),
|
|
989
|
+
{
|
|
990
|
+
title: "Update Driver Status",
|
|
991
|
+
description: "Set a driver\u2019s duty/availability status.",
|
|
992
|
+
inputSchema: {
|
|
993
|
+
id: z.string().describe("Driver ID"),
|
|
994
|
+
status: DriverStatusEnum.describe("New driver status")
|
|
995
|
+
}
|
|
996
|
+
},
|
|
997
|
+
async ({ id, status }) => {
|
|
998
|
+
try {
|
|
999
|
+
const data = await ctx.gql.workspace.request(UPDATE_DRIVER_STATUS, { id, status });
|
|
1000
|
+
return jsonResult(data.updateDriverStatus);
|
|
1001
|
+
} catch (error) {
|
|
1002
|
+
ctx.logger.error("movethewheels drivers_update_status failed", { error: String(error) });
|
|
1003
|
+
return errorResult(
|
|
1004
|
+
`Failed to update driver status: ${error instanceof Error ? error.message : String(error)}`
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
);
|
|
1009
|
+
server.registerTool(
|
|
1010
|
+
track(mcpToolName(ctx.productName, "drivers_assign_vehicle")),
|
|
1011
|
+
{
|
|
1012
|
+
title: "Assign Driver Vehicle",
|
|
1013
|
+
description: "Assign (or, with no vehicleId, unassign) a vehicle to a driver. Pass `vehicleId` to assign; omit it to clear the assignment.",
|
|
1014
|
+
inputSchema: {
|
|
1015
|
+
id: z.string().describe("Driver ID"),
|
|
1016
|
+
vehicleId: z.string().optional().describe("Vehicle ID to assign (omit to unassign)")
|
|
1017
|
+
}
|
|
1018
|
+
},
|
|
1019
|
+
async ({ id, vehicleId }) => {
|
|
1020
|
+
try {
|
|
1021
|
+
const data = await ctx.gql.workspace.request(ASSIGN_DRIVER_VEHICLE, { id, vehicleId: vehicleId ?? null });
|
|
1022
|
+
return jsonResult(data.assignDriverVehicle);
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
ctx.logger.error("movethewheels drivers_assign_vehicle failed", { error: String(error) });
|
|
1025
|
+
return errorResult(
|
|
1026
|
+
`Failed to assign vehicle: ${error instanceof Error ? error.message : String(error)}`
|
|
1027
|
+
);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
);
|
|
1031
|
+
server.registerTool(
|
|
1032
|
+
track(mcpToolName(ctx.productName, "drivers_delete")),
|
|
1033
|
+
{
|
|
1034
|
+
title: "Delete Driver",
|
|
1035
|
+
description: "Soft-delete a driver by ID.",
|
|
1036
|
+
inputSchema: { id: z.string().describe("Driver ID") }
|
|
1037
|
+
},
|
|
1038
|
+
async ({ id }) => {
|
|
1039
|
+
try {
|
|
1040
|
+
const data = await ctx.gql.workspace.request(DELETE_DRIVER, {
|
|
1041
|
+
id
|
|
1042
|
+
});
|
|
1043
|
+
return jsonResult({ deleted: data.deleteDriver, id });
|
|
1044
|
+
} catch (error) {
|
|
1045
|
+
ctx.logger.error("movethewheels drivers_delete failed", { error: String(error) });
|
|
1046
|
+
return errorResult(
|
|
1047
|
+
`Failed to delete driver: ${error instanceof Error ? error.message : String(error)}`
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
function registerVehicleTools(server, ctx, track) {
|
|
1054
|
+
server.registerTool(
|
|
1055
|
+
track(mcpToolName(ctx.productName, "vehicles_get")),
|
|
1056
|
+
{
|
|
1057
|
+
title: "Get Vehicle",
|
|
1058
|
+
description: "Fetch a single vehicle by ID.",
|
|
1059
|
+
inputSchema: { id: z.string().describe("Vehicle ID") }
|
|
1060
|
+
},
|
|
1061
|
+
async ({ id }) => {
|
|
1062
|
+
try {
|
|
1063
|
+
const data = await ctx.gql.workspace.request(GET_VEHICLE, { id });
|
|
1064
|
+
if (!data.vehicle) return errorResult(`Vehicle ${id} not found`);
|
|
1065
|
+
return jsonResult(data.vehicle);
|
|
1066
|
+
} catch (error) {
|
|
1067
|
+
ctx.logger.error("movethewheels vehicles_get failed", { error: String(error) });
|
|
1068
|
+
return errorResult(
|
|
1069
|
+
`Failed to get vehicle: ${error instanceof Error ? error.message : String(error)}`
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
);
|
|
1074
|
+
server.registerTool(
|
|
1075
|
+
track(mcpToolName(ctx.productName, "vehicles_list")),
|
|
1076
|
+
{
|
|
1077
|
+
title: "List Vehicles",
|
|
1078
|
+
description: "List fleet vehicles with optional status/type/text filters and pagination.",
|
|
1079
|
+
inputSchema: {
|
|
1080
|
+
search: z.string().optional().describe("Free-text search across vehicleNumber/make/model"),
|
|
1081
|
+
status: VehicleStatusEnum.optional().describe("Filter by vehicle status"),
|
|
1082
|
+
type: VehicleTypeEnum.optional().describe("Filter by vehicle type"),
|
|
1083
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
1084
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
1085
|
+
}
|
|
1086
|
+
},
|
|
1087
|
+
async ({ search, status, type, limit, offset }) => {
|
|
1088
|
+
try {
|
|
1089
|
+
const filter = {};
|
|
1090
|
+
if (search !== void 0) filter.search = search;
|
|
1091
|
+
if (status !== void 0) filter.status = status;
|
|
1092
|
+
if (type !== void 0) filter.type = type;
|
|
1093
|
+
const data = await ctx.gql.workspace.request(LIST_VEHICLES, {
|
|
1094
|
+
filter: Object.keys(filter).length > 0 ? filter : void 0,
|
|
1095
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
1096
|
+
});
|
|
1097
|
+
return jsonResult(data.vehicles);
|
|
1098
|
+
} catch (error) {
|
|
1099
|
+
ctx.logger.error("movethewheels vehicles_list failed", { error: String(error) });
|
|
1100
|
+
return errorResult(
|
|
1101
|
+
`Failed to list vehicles: ${error instanceof Error ? error.message : String(error)}`
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
);
|
|
1106
|
+
server.registerTool(
|
|
1107
|
+
track(mcpToolName(ctx.productName, "vehicles_create")),
|
|
1108
|
+
{
|
|
1109
|
+
title: "Create Vehicle",
|
|
1110
|
+
description: "Register a vehicle in the fleet. `vehicleNumber` and `type` are required; `status` defaults to ACTIVE.",
|
|
1111
|
+
inputSchema: {
|
|
1112
|
+
vehicleNumber: z.string().min(1).describe("Fleet vehicle number / registration"),
|
|
1113
|
+
type: VehicleTypeEnum.describe("Vehicle class"),
|
|
1114
|
+
status: VehicleStatusEnum.optional().describe("Initial status (default ACTIVE)"),
|
|
1115
|
+
make: z.string().optional().describe("Manufacturer"),
|
|
1116
|
+
model: z.string().optional().describe("Model"),
|
|
1117
|
+
year: z.number().int().optional().describe("Model year"),
|
|
1118
|
+
licensePlate: z.string().optional().describe("License plate"),
|
|
1119
|
+
capacity: JsonObject.optional().describe("Capacity JSON ({ weight, volume, ... })"),
|
|
1120
|
+
currentLocation: JsonObject.optional().describe("Current location JSON ({ lat, lng })"),
|
|
1121
|
+
fuelLevel: z.number().optional().describe("Fuel level (0-100)"),
|
|
1122
|
+
mileage: z.number().optional().describe("Odometer mileage"),
|
|
1123
|
+
ownershipType: z.string().optional().describe('Ownership type, e.g. "owned", "leased"')
|
|
1124
|
+
}
|
|
1125
|
+
},
|
|
1126
|
+
async ({
|
|
1127
|
+
vehicleNumber,
|
|
1128
|
+
type,
|
|
1129
|
+
status,
|
|
1130
|
+
make,
|
|
1131
|
+
model,
|
|
1132
|
+
year,
|
|
1133
|
+
licensePlate,
|
|
1134
|
+
capacity,
|
|
1135
|
+
currentLocation,
|
|
1136
|
+
fuelLevel,
|
|
1137
|
+
mileage,
|
|
1138
|
+
ownershipType
|
|
1139
|
+
}) => {
|
|
1140
|
+
try {
|
|
1141
|
+
const input = { vehicleNumber, type };
|
|
1142
|
+
if (status !== void 0) input.status = status;
|
|
1143
|
+
if (make !== void 0) input.make = make;
|
|
1144
|
+
if (model !== void 0) input.model = model;
|
|
1145
|
+
if (year !== void 0) input.year = year;
|
|
1146
|
+
if (licensePlate !== void 0) input.licensePlate = licensePlate;
|
|
1147
|
+
if (capacity !== void 0) input.capacity = capacity;
|
|
1148
|
+
if (currentLocation !== void 0) input.currentLocation = currentLocation;
|
|
1149
|
+
if (fuelLevel !== void 0) input.fuelLevel = fuelLevel;
|
|
1150
|
+
if (mileage !== void 0) input.mileage = mileage;
|
|
1151
|
+
if (ownershipType !== void 0) input.ownershipType = ownershipType;
|
|
1152
|
+
const data = await ctx.gql.workspace.request(CREATE_VEHICLE, { input });
|
|
1153
|
+
return jsonResult(data.createVehicle);
|
|
1154
|
+
} catch (error) {
|
|
1155
|
+
ctx.logger.error("movethewheels vehicles_create failed", { error: String(error) });
|
|
1156
|
+
return errorResult(
|
|
1157
|
+
`Failed to create vehicle: ${error instanceof Error ? error.message : String(error)}`
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
);
|
|
1162
|
+
server.registerTool(
|
|
1163
|
+
track(mcpToolName(ctx.productName, "vehicles_update")),
|
|
1164
|
+
{
|
|
1165
|
+
title: "Update Vehicle",
|
|
1166
|
+
description: "Update a vehicle. At least one mutable field must be provided.",
|
|
1167
|
+
inputSchema: {
|
|
1168
|
+
id: z.string().describe("Vehicle ID"),
|
|
1169
|
+
vehicleNumber: z.string().optional().describe("Updated vehicle number"),
|
|
1170
|
+
type: VehicleTypeEnum.optional().describe("Updated vehicle type"),
|
|
1171
|
+
status: VehicleStatusEnum.optional().describe("Updated status"),
|
|
1172
|
+
make: z.string().optional().describe("Updated manufacturer"),
|
|
1173
|
+
model: z.string().optional().describe("Updated model"),
|
|
1174
|
+
year: z.number().int().optional().describe("Updated model year"),
|
|
1175
|
+
licensePlate: z.string().optional().describe("Updated license plate"),
|
|
1176
|
+
capacity: JsonObject.optional().describe("Updated capacity JSON"),
|
|
1177
|
+
currentLocation: JsonObject.optional().describe("Updated location JSON"),
|
|
1178
|
+
fuelLevel: z.number().optional().describe("Updated fuel level"),
|
|
1179
|
+
mileage: z.number().optional().describe("Updated mileage"),
|
|
1180
|
+
ownershipType: z.string().optional().describe("Updated ownership type")
|
|
1181
|
+
}
|
|
1182
|
+
},
|
|
1183
|
+
async ({
|
|
1184
|
+
id,
|
|
1185
|
+
vehicleNumber,
|
|
1186
|
+
type,
|
|
1187
|
+
status,
|
|
1188
|
+
make,
|
|
1189
|
+
model,
|
|
1190
|
+
year,
|
|
1191
|
+
licensePlate,
|
|
1192
|
+
capacity,
|
|
1193
|
+
currentLocation,
|
|
1194
|
+
fuelLevel,
|
|
1195
|
+
mileage,
|
|
1196
|
+
ownershipType
|
|
1197
|
+
}) => {
|
|
1198
|
+
try {
|
|
1199
|
+
const input = {};
|
|
1200
|
+
if (vehicleNumber !== void 0) input.vehicleNumber = vehicleNumber;
|
|
1201
|
+
if (type !== void 0) input.type = type;
|
|
1202
|
+
if (status !== void 0) input.status = status;
|
|
1203
|
+
if (make !== void 0) input.make = make;
|
|
1204
|
+
if (model !== void 0) input.model = model;
|
|
1205
|
+
if (year !== void 0) input.year = year;
|
|
1206
|
+
if (licensePlate !== void 0) input.licensePlate = licensePlate;
|
|
1207
|
+
if (capacity !== void 0) input.capacity = capacity;
|
|
1208
|
+
if (currentLocation !== void 0) input.currentLocation = currentLocation;
|
|
1209
|
+
if (fuelLevel !== void 0) input.fuelLevel = fuelLevel;
|
|
1210
|
+
if (mileage !== void 0) input.mileage = mileage;
|
|
1211
|
+
if (ownershipType !== void 0) input.ownershipType = ownershipType;
|
|
1212
|
+
if (Object.keys(input).length === 0) {
|
|
1213
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field.");
|
|
1214
|
+
}
|
|
1215
|
+
const data = await ctx.gql.workspace.request(UPDATE_VEHICLE, { id, input });
|
|
1216
|
+
return jsonResult(data.updateVehicle);
|
|
1217
|
+
} catch (error) {
|
|
1218
|
+
ctx.logger.error("movethewheels vehicles_update failed", { error: String(error) });
|
|
1219
|
+
return errorResult(
|
|
1220
|
+
`Failed to update vehicle: ${error instanceof Error ? error.message : String(error)}`
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
);
|
|
1225
|
+
server.registerTool(
|
|
1226
|
+
track(mcpToolName(ctx.productName, "vehicles_update_status")),
|
|
1227
|
+
{
|
|
1228
|
+
title: "Update Vehicle Status",
|
|
1229
|
+
description: "Set a vehicle\u2019s operational status.",
|
|
1230
|
+
inputSchema: {
|
|
1231
|
+
id: z.string().describe("Vehicle ID"),
|
|
1232
|
+
status: VehicleStatusEnum.describe("New vehicle status")
|
|
1233
|
+
}
|
|
1234
|
+
},
|
|
1235
|
+
async ({ id, status }) => {
|
|
1236
|
+
try {
|
|
1237
|
+
const data = await ctx.gql.workspace.request(UPDATE_VEHICLE_STATUS, { id, status });
|
|
1238
|
+
return jsonResult(data.updateVehicleStatus);
|
|
1239
|
+
} catch (error) {
|
|
1240
|
+
ctx.logger.error("movethewheels vehicles_update_status failed", { error: String(error) });
|
|
1241
|
+
return errorResult(
|
|
1242
|
+
`Failed to update vehicle status: ${error instanceof Error ? error.message : String(error)}`
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
);
|
|
1247
|
+
server.registerTool(
|
|
1248
|
+
track(mcpToolName(ctx.productName, "vehicles_update_location")),
|
|
1249
|
+
{
|
|
1250
|
+
title: "Update Vehicle Location",
|
|
1251
|
+
description: "Push a new GPS/telemetry location for a vehicle (location JSON, e.g. { lat, lng }).",
|
|
1252
|
+
inputSchema: {
|
|
1253
|
+
id: z.string().describe("Vehicle ID"),
|
|
1254
|
+
location: JsonObject.describe("Location JSON ({ lat, lng, ... })")
|
|
1255
|
+
}
|
|
1256
|
+
},
|
|
1257
|
+
async ({ id, location }) => {
|
|
1258
|
+
try {
|
|
1259
|
+
const data = await ctx.gql.workspace.request(UPDATE_VEHICLE_LOCATION, { id, location });
|
|
1260
|
+
return jsonResult(data.updateVehicleLocation);
|
|
1261
|
+
} catch (error) {
|
|
1262
|
+
ctx.logger.error("movethewheels vehicles_update_location failed", { error: String(error) });
|
|
1263
|
+
return errorResult(
|
|
1264
|
+
`Failed to update vehicle location: ${error instanceof Error ? error.message : String(error)}`
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
);
|
|
1269
|
+
server.registerTool(
|
|
1270
|
+
track(mcpToolName(ctx.productName, "vehicles_delete")),
|
|
1271
|
+
{
|
|
1272
|
+
title: "Delete Vehicle",
|
|
1273
|
+
description: "Soft-delete a vehicle by ID.",
|
|
1274
|
+
inputSchema: { id: z.string().describe("Vehicle ID") }
|
|
1275
|
+
},
|
|
1276
|
+
async ({ id }) => {
|
|
1277
|
+
try {
|
|
1278
|
+
const data = await ctx.gql.workspace.request(
|
|
1279
|
+
DELETE_VEHICLE,
|
|
1280
|
+
{ id }
|
|
1281
|
+
);
|
|
1282
|
+
return jsonResult({ deleted: data.deleteVehicle, id });
|
|
1283
|
+
} catch (error) {
|
|
1284
|
+
ctx.logger.error("movethewheels vehicles_delete failed", { error: String(error) });
|
|
1285
|
+
return errorResult(
|
|
1286
|
+
`Failed to delete vehicle: ${error instanceof Error ? error.message : String(error)}`
|
|
1287
|
+
);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
);
|
|
1291
|
+
}
|
|
1292
|
+
function registerProductTools(server, ctx, track) {
|
|
1293
|
+
server.registerTool(
|
|
1294
|
+
track(mcpToolName(ctx.productName, "products_get")),
|
|
1295
|
+
{
|
|
1296
|
+
title: "Get Product",
|
|
1297
|
+
description: "Fetch a single product (cargo SKU) by ID.",
|
|
1298
|
+
inputSchema: { id: z.string().describe("Product ID") }
|
|
1299
|
+
},
|
|
1300
|
+
async ({ id }) => {
|
|
1301
|
+
try {
|
|
1302
|
+
const data = await ctx.gql.workspace.request(GET_PRODUCT, { id });
|
|
1303
|
+
if (!data.product) return errorResult(`Product ${id} not found`);
|
|
1304
|
+
return jsonResult(data.product);
|
|
1305
|
+
} catch (error) {
|
|
1306
|
+
ctx.logger.error("movethewheels products_get failed", { error: String(error) });
|
|
1307
|
+
return errorResult(
|
|
1308
|
+
`Failed to get product: ${error instanceof Error ? error.message : String(error)}`
|
|
1309
|
+
);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
);
|
|
1313
|
+
server.registerTool(
|
|
1314
|
+
track(mcpToolName(ctx.productName, "products_list")),
|
|
1315
|
+
{
|
|
1316
|
+
title: "List Products",
|
|
1317
|
+
description: "List products with optional category/sku/text filters and pagination.",
|
|
1318
|
+
inputSchema: {
|
|
1319
|
+
search: z.string().optional().describe("Free-text search across name/description"),
|
|
1320
|
+
category: z.string().optional().describe("Filter by category"),
|
|
1321
|
+
sku: z.string().optional().describe("Filter by SKU"),
|
|
1322
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
1323
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
1324
|
+
}
|
|
1325
|
+
},
|
|
1326
|
+
async ({ search, category, sku, limit, offset }) => {
|
|
1327
|
+
try {
|
|
1328
|
+
const filter = {};
|
|
1329
|
+
if (search !== void 0) filter.search = search;
|
|
1330
|
+
if (category !== void 0) filter.category = category;
|
|
1331
|
+
if (sku !== void 0) filter.sku = sku;
|
|
1332
|
+
const data = await ctx.gql.workspace.request(LIST_PRODUCTS, {
|
|
1333
|
+
filter: Object.keys(filter).length > 0 ? filter : void 0,
|
|
1334
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
1335
|
+
});
|
|
1336
|
+
return jsonResult(data.products);
|
|
1337
|
+
} catch (error) {
|
|
1338
|
+
ctx.logger.error("movethewheels products_list failed", { error: String(error) });
|
|
1339
|
+
return errorResult(
|
|
1340
|
+
`Failed to list products: ${error instanceof Error ? error.message : String(error)}`
|
|
1341
|
+
);
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
);
|
|
1345
|
+
server.registerTool(
|
|
1346
|
+
track(mcpToolName(ctx.productName, "products_create")),
|
|
1347
|
+
{
|
|
1348
|
+
title: "Create Product",
|
|
1349
|
+
description: "Create a product (cargo SKU). `name` and `sku` are required.",
|
|
1350
|
+
inputSchema: {
|
|
1351
|
+
name: z.string().min(1).describe("Product name"),
|
|
1352
|
+
sku: z.string().min(1).describe("Stock-keeping unit (unique)"),
|
|
1353
|
+
description: z.string().optional().describe("Description"),
|
|
1354
|
+
category: z.string().optional().describe("Category"),
|
|
1355
|
+
brand: z.string().optional().describe("Brand"),
|
|
1356
|
+
weight: z.number().optional().describe("Unit weight"),
|
|
1357
|
+
value: z.number().optional().describe("Declared value"),
|
|
1358
|
+
dimensions: JsonObject.optional().describe("Dimensions JSON ({ length, width, height })"),
|
|
1359
|
+
fragile: z.boolean().optional().describe("Whether the item is fragile (default false)"),
|
|
1360
|
+
hazardous: z.boolean().optional().describe("Whether the item is hazardous (default false)"),
|
|
1361
|
+
temperatureControlled: z.boolean().optional().describe("Whether the item needs temperature control (default false)")
|
|
1362
|
+
}
|
|
1363
|
+
},
|
|
1364
|
+
async ({
|
|
1365
|
+
name,
|
|
1366
|
+
sku,
|
|
1367
|
+
description,
|
|
1368
|
+
category,
|
|
1369
|
+
brand,
|
|
1370
|
+
weight,
|
|
1371
|
+
value,
|
|
1372
|
+
dimensions,
|
|
1373
|
+
fragile,
|
|
1374
|
+
hazardous,
|
|
1375
|
+
temperatureControlled
|
|
1376
|
+
}) => {
|
|
1377
|
+
try {
|
|
1378
|
+
const input = { name, sku };
|
|
1379
|
+
if (description !== void 0) input.description = description;
|
|
1380
|
+
if (category !== void 0) input.category = category;
|
|
1381
|
+
if (brand !== void 0) input.brand = brand;
|
|
1382
|
+
if (weight !== void 0) input.weight = weight;
|
|
1383
|
+
if (value !== void 0) input.value = value;
|
|
1384
|
+
if (dimensions !== void 0) input.dimensions = dimensions;
|
|
1385
|
+
if (fragile !== void 0) input.fragile = fragile;
|
|
1386
|
+
if (hazardous !== void 0) input.hazardous = hazardous;
|
|
1387
|
+
if (temperatureControlled !== void 0) {
|
|
1388
|
+
input.temperatureControlled = temperatureControlled;
|
|
1389
|
+
}
|
|
1390
|
+
const data = await ctx.gql.workspace.request(CREATE_PRODUCT, { input });
|
|
1391
|
+
return jsonResult(data.createProduct);
|
|
1392
|
+
} catch (error) {
|
|
1393
|
+
ctx.logger.error("movethewheels products_create failed", { error: String(error) });
|
|
1394
|
+
return errorResult(
|
|
1395
|
+
`Failed to create product: ${error instanceof Error ? error.message : String(error)}`
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
);
|
|
1400
|
+
server.registerTool(
|
|
1401
|
+
track(mcpToolName(ctx.productName, "products_update")),
|
|
1402
|
+
{
|
|
1403
|
+
title: "Update Product",
|
|
1404
|
+
description: "Update a product. At least one mutable field must be provided.",
|
|
1405
|
+
inputSchema: {
|
|
1406
|
+
id: z.string().describe("Product ID"),
|
|
1407
|
+
name: z.string().optional().describe("Updated name"),
|
|
1408
|
+
sku: z.string().optional().describe("Updated SKU"),
|
|
1409
|
+
description: z.string().optional().describe("Updated description"),
|
|
1410
|
+
category: z.string().optional().describe("Updated category"),
|
|
1411
|
+
brand: z.string().optional().describe("Updated brand"),
|
|
1412
|
+
weight: z.number().optional().describe("Updated weight"),
|
|
1413
|
+
value: z.number().optional().describe("Updated value"),
|
|
1414
|
+
dimensions: JsonObject.optional().describe("Updated dimensions JSON"),
|
|
1415
|
+
fragile: z.boolean().optional().describe("Updated fragile flag"),
|
|
1416
|
+
hazardous: z.boolean().optional().describe("Updated hazardous flag"),
|
|
1417
|
+
temperatureControlled: z.boolean().optional().describe("Updated temperature-control flag")
|
|
1418
|
+
}
|
|
1419
|
+
},
|
|
1420
|
+
async ({
|
|
1421
|
+
id,
|
|
1422
|
+
name,
|
|
1423
|
+
sku,
|
|
1424
|
+
description,
|
|
1425
|
+
category,
|
|
1426
|
+
brand,
|
|
1427
|
+
weight,
|
|
1428
|
+
value,
|
|
1429
|
+
dimensions,
|
|
1430
|
+
fragile,
|
|
1431
|
+
hazardous,
|
|
1432
|
+
temperatureControlled
|
|
1433
|
+
}) => {
|
|
1434
|
+
try {
|
|
1435
|
+
const input = {};
|
|
1436
|
+
if (name !== void 0) input.name = name;
|
|
1437
|
+
if (sku !== void 0) input.sku = sku;
|
|
1438
|
+
if (description !== void 0) input.description = description;
|
|
1439
|
+
if (category !== void 0) input.category = category;
|
|
1440
|
+
if (brand !== void 0) input.brand = brand;
|
|
1441
|
+
if (weight !== void 0) input.weight = weight;
|
|
1442
|
+
if (value !== void 0) input.value = value;
|
|
1443
|
+
if (dimensions !== void 0) input.dimensions = dimensions;
|
|
1444
|
+
if (fragile !== void 0) input.fragile = fragile;
|
|
1445
|
+
if (hazardous !== void 0) input.hazardous = hazardous;
|
|
1446
|
+
if (temperatureControlled !== void 0) {
|
|
1447
|
+
input.temperatureControlled = temperatureControlled;
|
|
1448
|
+
}
|
|
1449
|
+
if (Object.keys(input).length === 0) {
|
|
1450
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field.");
|
|
1451
|
+
}
|
|
1452
|
+
const data = await ctx.gql.workspace.request(UPDATE_PRODUCT, { id, input });
|
|
1453
|
+
return jsonResult(data.updateProduct);
|
|
1454
|
+
} catch (error) {
|
|
1455
|
+
ctx.logger.error("movethewheels products_update failed", { error: String(error) });
|
|
1456
|
+
return errorResult(
|
|
1457
|
+
`Failed to update product: ${error instanceof Error ? error.message : String(error)}`
|
|
1458
|
+
);
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
);
|
|
1462
|
+
server.registerTool(
|
|
1463
|
+
track(mcpToolName(ctx.productName, "products_delete")),
|
|
1464
|
+
{
|
|
1465
|
+
title: "Delete Product",
|
|
1466
|
+
description: "Soft-delete a product by ID.",
|
|
1467
|
+
inputSchema: { id: z.string().describe("Product ID") }
|
|
1468
|
+
},
|
|
1469
|
+
async ({ id }) => {
|
|
1470
|
+
try {
|
|
1471
|
+
const data = await ctx.gql.workspace.request(
|
|
1472
|
+
DELETE_PRODUCT,
|
|
1473
|
+
{ id }
|
|
1474
|
+
);
|
|
1475
|
+
return jsonResult({ deleted: data.deleteProduct, id });
|
|
1476
|
+
} catch (error) {
|
|
1477
|
+
ctx.logger.error("movethewheels products_delete failed", { error: String(error) });
|
|
1478
|
+
return errorResult(
|
|
1479
|
+
`Failed to delete product: ${error instanceof Error ? error.message : String(error)}`
|
|
1480
|
+
);
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
);
|
|
1484
|
+
}
|
|
1485
|
+
function registerOrderTools(server, ctx, track) {
|
|
1486
|
+
server.registerTool(
|
|
1487
|
+
track(mcpToolName(ctx.productName, "orders_get")),
|
|
1488
|
+
{
|
|
1489
|
+
title: "Get Order",
|
|
1490
|
+
description: "Fetch a single delivery order by ID, including customer/driver/vehicle/shipment links and status history.",
|
|
1491
|
+
inputSchema: { id: z.string().describe("Order ID") }
|
|
1492
|
+
},
|
|
1493
|
+
async ({ id }) => {
|
|
1494
|
+
try {
|
|
1495
|
+
const data = await ctx.gql.workspace.request(GET_ORDER, { id });
|
|
1496
|
+
if (!data.movethewheelsOrder) return errorResult(`Order ${id} not found`);
|
|
1497
|
+
return jsonResult(data.movethewheelsOrder);
|
|
1498
|
+
} catch (error) {
|
|
1499
|
+
ctx.logger.error("movethewheels orders_get failed", { error: String(error) });
|
|
1500
|
+
return errorResult(
|
|
1501
|
+
`Failed to get order: ${error instanceof Error ? error.message : String(error)}`
|
|
1502
|
+
);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
);
|
|
1506
|
+
server.registerTool(
|
|
1507
|
+
track(mcpToolName(ctx.productName, "orders_list")),
|
|
1508
|
+
{
|
|
1509
|
+
title: "List Orders",
|
|
1510
|
+
description: "List delivery orders with optional customer/driver/vehicle/type/priority/status/text filters and pagination.",
|
|
1511
|
+
inputSchema: {
|
|
1512
|
+
search: z.string().optional().describe("Free-text search across orderNumber"),
|
|
1513
|
+
customerId: z.string().optional().describe("Filter by customer ID"),
|
|
1514
|
+
driverId: z.string().optional().describe("Filter by driver ID"),
|
|
1515
|
+
vehicleId: z.string().optional().describe("Filter by vehicle ID"),
|
|
1516
|
+
orderType: OrderTypeEnum.optional().describe("Filter by order type"),
|
|
1517
|
+
priority: OrderPriorityEnum.optional().describe("Filter by priority"),
|
|
1518
|
+
status: OrderStatusEnum.optional().describe("Filter by status"),
|
|
1519
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
1520
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
1521
|
+
}
|
|
1522
|
+
},
|
|
1523
|
+
async ({
|
|
1524
|
+
search,
|
|
1525
|
+
customerId,
|
|
1526
|
+
driverId,
|
|
1527
|
+
vehicleId,
|
|
1528
|
+
orderType,
|
|
1529
|
+
priority,
|
|
1530
|
+
status,
|
|
1531
|
+
limit,
|
|
1532
|
+
offset
|
|
1533
|
+
}) => {
|
|
1534
|
+
try {
|
|
1535
|
+
const filter = {};
|
|
1536
|
+
if (search !== void 0) filter.search = search;
|
|
1537
|
+
if (customerId !== void 0) filter.customerId = customerId;
|
|
1538
|
+
if (driverId !== void 0) filter.driverId = driverId;
|
|
1539
|
+
if (vehicleId !== void 0) filter.vehicleId = vehicleId;
|
|
1540
|
+
if (orderType !== void 0) filter.orderType = orderType;
|
|
1541
|
+
if (priority !== void 0) filter.priority = priority;
|
|
1542
|
+
if (status !== void 0) filter.status = status;
|
|
1543
|
+
const data = await ctx.gql.workspace.request(LIST_ORDERS, {
|
|
1544
|
+
filter: Object.keys(filter).length > 0 ? filter : void 0,
|
|
1545
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
1546
|
+
});
|
|
1547
|
+
return jsonResult(data.movethewheelsOrders);
|
|
1548
|
+
} catch (error) {
|
|
1549
|
+
ctx.logger.error("movethewheels orders_list failed", { error: String(error) });
|
|
1550
|
+
return errorResult(
|
|
1551
|
+
`Failed to list orders: ${error instanceof Error ? error.message : String(error)}`
|
|
1552
|
+
);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
);
|
|
1556
|
+
server.registerTool(
|
|
1557
|
+
track(mcpToolName(ctx.productName, "orders_create")),
|
|
1558
|
+
{
|
|
1559
|
+
title: "Create Order",
|
|
1560
|
+
description: "Create a delivery order. `customerId`, `orderNumber`, `pickupAddress`, `deliveryAddress`, and `items` are required. `orderType` defaults to B2C, `priority` to MEDIUM, `status` to PENDING.",
|
|
1561
|
+
inputSchema: {
|
|
1562
|
+
customerId: z.string().min(1).describe("Ordering customer ID"),
|
|
1563
|
+
orderNumber: z.string().min(1).describe("Human-readable order number (unique)"),
|
|
1564
|
+
pickupAddress: JsonObject.describe("Pickup address JSON"),
|
|
1565
|
+
deliveryAddress: JsonObject.describe("Delivery address JSON"),
|
|
1566
|
+
items: JsonValue.describe("Order items JSON (array of line items)"),
|
|
1567
|
+
orderType: OrderTypeEnum.optional().describe("Order type (default B2C)"),
|
|
1568
|
+
priority: OrderPriorityEnum.optional().describe("Priority (default MEDIUM)"),
|
|
1569
|
+
status: OrderStatusEnum.optional().describe("Initial status (default PENDING)"),
|
|
1570
|
+
driverId: z.string().optional().describe("Assigned driver ID"),
|
|
1571
|
+
vehicleId: z.string().optional().describe("Assigned vehicle ID"),
|
|
1572
|
+
pricing: JsonObject.optional().describe("Pricing breakdown JSON"),
|
|
1573
|
+
scheduledDelivery: z.string().optional().describe("Scheduled delivery (ISO 8601 DateTime)"),
|
|
1574
|
+
specialInstructions: z.string().optional().describe("Special handling instructions"),
|
|
1575
|
+
trackingNumber: z.string().optional().describe("Tracking number")
|
|
1576
|
+
}
|
|
1577
|
+
},
|
|
1578
|
+
async ({
|
|
1579
|
+
customerId,
|
|
1580
|
+
orderNumber,
|
|
1581
|
+
pickupAddress,
|
|
1582
|
+
deliveryAddress,
|
|
1583
|
+
items,
|
|
1584
|
+
orderType,
|
|
1585
|
+
priority,
|
|
1586
|
+
status,
|
|
1587
|
+
driverId,
|
|
1588
|
+
vehicleId,
|
|
1589
|
+
pricing,
|
|
1590
|
+
scheduledDelivery,
|
|
1591
|
+
specialInstructions,
|
|
1592
|
+
trackingNumber
|
|
1593
|
+
}) => {
|
|
1594
|
+
try {
|
|
1595
|
+
const input = {
|
|
1596
|
+
customerId,
|
|
1597
|
+
orderNumber,
|
|
1598
|
+
pickupAddress,
|
|
1599
|
+
deliveryAddress,
|
|
1600
|
+
items
|
|
1601
|
+
};
|
|
1602
|
+
if (orderType !== void 0) input.orderType = orderType;
|
|
1603
|
+
if (priority !== void 0) input.priority = priority;
|
|
1604
|
+
if (status !== void 0) input.status = status;
|
|
1605
|
+
if (driverId !== void 0) input.driverId = driverId;
|
|
1606
|
+
if (vehicleId !== void 0) input.vehicleId = vehicleId;
|
|
1607
|
+
if (pricing !== void 0) input.pricing = pricing;
|
|
1608
|
+
if (scheduledDelivery !== void 0) input.scheduledDelivery = scheduledDelivery;
|
|
1609
|
+
if (specialInstructions !== void 0) input.specialInstructions = specialInstructions;
|
|
1610
|
+
if (trackingNumber !== void 0) input.trackingNumber = trackingNumber;
|
|
1611
|
+
const data = await ctx.gql.workspace.request(CREATE_ORDER, { input });
|
|
1612
|
+
return jsonResult(data.createMovethewheelsOrder);
|
|
1613
|
+
} catch (error) {
|
|
1614
|
+
ctx.logger.error("movethewheels orders_create failed", { error: String(error) });
|
|
1615
|
+
return errorResult(
|
|
1616
|
+
`Failed to create order: ${error instanceof Error ? error.message : String(error)}`
|
|
1617
|
+
);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
);
|
|
1621
|
+
server.registerTool(
|
|
1622
|
+
track(mcpToolName(ctx.productName, "orders_update")),
|
|
1623
|
+
{
|
|
1624
|
+
title: "Update Order",
|
|
1625
|
+
description: "Update a delivery order. At least one mutable field must be provided.",
|
|
1626
|
+
inputSchema: {
|
|
1627
|
+
id: z.string().describe("Order ID"),
|
|
1628
|
+
customerId: z.string().optional().describe("Updated customer ID"),
|
|
1629
|
+
driverId: z.string().optional().describe("Updated driver ID"),
|
|
1630
|
+
vehicleId: z.string().optional().describe("Updated vehicle ID"),
|
|
1631
|
+
orderType: OrderTypeEnum.optional().describe("Updated order type"),
|
|
1632
|
+
priority: OrderPriorityEnum.optional().describe("Updated priority"),
|
|
1633
|
+
status: OrderStatusEnum.optional().describe("Updated status"),
|
|
1634
|
+
pickupAddress: JsonObject.optional().describe("Updated pickup address JSON"),
|
|
1635
|
+
deliveryAddress: JsonObject.optional().describe("Updated delivery address JSON"),
|
|
1636
|
+
items: JsonValue.optional().describe("Updated items JSON"),
|
|
1637
|
+
pricing: JsonObject.optional().describe("Updated pricing JSON"),
|
|
1638
|
+
scheduledDelivery: z.string().optional().describe("Updated scheduled delivery (ISO 8601)"),
|
|
1639
|
+
specialInstructions: z.string().optional().describe("Updated special instructions"),
|
|
1640
|
+
trackingNumber: z.string().optional().describe("Updated tracking number")
|
|
1641
|
+
}
|
|
1642
|
+
},
|
|
1643
|
+
async ({
|
|
1644
|
+
id,
|
|
1645
|
+
customerId,
|
|
1646
|
+
driverId,
|
|
1647
|
+
vehicleId,
|
|
1648
|
+
orderType,
|
|
1649
|
+
priority,
|
|
1650
|
+
status,
|
|
1651
|
+
pickupAddress,
|
|
1652
|
+
deliveryAddress,
|
|
1653
|
+
items,
|
|
1654
|
+
pricing,
|
|
1655
|
+
scheduledDelivery,
|
|
1656
|
+
specialInstructions,
|
|
1657
|
+
trackingNumber
|
|
1658
|
+
}) => {
|
|
1659
|
+
try {
|
|
1660
|
+
const input = {};
|
|
1661
|
+
if (customerId !== void 0) input.customerId = customerId;
|
|
1662
|
+
if (driverId !== void 0) input.driverId = driverId;
|
|
1663
|
+
if (vehicleId !== void 0) input.vehicleId = vehicleId;
|
|
1664
|
+
if (orderType !== void 0) input.orderType = orderType;
|
|
1665
|
+
if (priority !== void 0) input.priority = priority;
|
|
1666
|
+
if (status !== void 0) input.status = status;
|
|
1667
|
+
if (pickupAddress !== void 0) input.pickupAddress = pickupAddress;
|
|
1668
|
+
if (deliveryAddress !== void 0) input.deliveryAddress = deliveryAddress;
|
|
1669
|
+
if (items !== void 0) input.items = items;
|
|
1670
|
+
if (pricing !== void 0) input.pricing = pricing;
|
|
1671
|
+
if (scheduledDelivery !== void 0) input.scheduledDelivery = scheduledDelivery;
|
|
1672
|
+
if (specialInstructions !== void 0) input.specialInstructions = specialInstructions;
|
|
1673
|
+
if (trackingNumber !== void 0) input.trackingNumber = trackingNumber;
|
|
1674
|
+
if (Object.keys(input).length === 0) {
|
|
1675
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field.");
|
|
1676
|
+
}
|
|
1677
|
+
const data = await ctx.gql.workspace.request(UPDATE_ORDER, { id, input });
|
|
1678
|
+
return jsonResult(data.updateMovethewheelsOrder);
|
|
1679
|
+
} catch (error) {
|
|
1680
|
+
ctx.logger.error("movethewheels orders_update failed", { error: String(error) });
|
|
1681
|
+
return errorResult(
|
|
1682
|
+
`Failed to update order: ${error instanceof Error ? error.message : String(error)}`
|
|
1683
|
+
);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
);
|
|
1687
|
+
server.registerTool(
|
|
1688
|
+
track(mcpToolName(ctx.productName, "orders_update_status")),
|
|
1689
|
+
{
|
|
1690
|
+
title: "Update Order Status",
|
|
1691
|
+
description: "Advance an order through its delivery lifecycle (e.g. PENDING \u2192 CONFIRMED \u2192 ASSIGNED \u2192 PICKED_UP \u2192 IN_TRANSIT \u2192 OUT_FOR_DELIVERY \u2192 DELIVERED, or CANCELLED/FAILED/RETURNED). Records a status-history entry.",
|
|
1692
|
+
inputSchema: {
|
|
1693
|
+
id: z.string().describe("Order ID"),
|
|
1694
|
+
status: OrderStatusEnum.describe("New order status"),
|
|
1695
|
+
notes: z.string().optional().describe("Status-change notes"),
|
|
1696
|
+
location: JsonObject.optional().describe("Location JSON at time of status change")
|
|
1697
|
+
}
|
|
1698
|
+
},
|
|
1699
|
+
async ({ id, status, notes, location }) => {
|
|
1700
|
+
try {
|
|
1701
|
+
const input = { status };
|
|
1702
|
+
if (notes !== void 0) input.notes = notes;
|
|
1703
|
+
if (location !== void 0) input.location = location;
|
|
1704
|
+
const data = await ctx.gql.workspace.request(UPDATE_ORDER_STATUS, { id, input });
|
|
1705
|
+
return jsonResult(data.updateMovethewheelsOrderStatus);
|
|
1706
|
+
} catch (error) {
|
|
1707
|
+
ctx.logger.error("movethewheels orders_update_status failed", { error: String(error) });
|
|
1708
|
+
return errorResult(
|
|
1709
|
+
`Failed to update order status: ${error instanceof Error ? error.message : String(error)}`
|
|
1710
|
+
);
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
);
|
|
1714
|
+
server.registerTool(
|
|
1715
|
+
track(mcpToolName(ctx.productName, "orders_delete")),
|
|
1716
|
+
{
|
|
1717
|
+
title: "Delete Order",
|
|
1718
|
+
description: "Soft-delete a delivery order by ID.",
|
|
1719
|
+
inputSchema: { id: z.string().describe("Order ID") }
|
|
1720
|
+
},
|
|
1721
|
+
async ({ id }) => {
|
|
1722
|
+
try {
|
|
1723
|
+
const data = await ctx.gql.workspace.request(
|
|
1724
|
+
DELETE_ORDER,
|
|
1725
|
+
{ id }
|
|
1726
|
+
);
|
|
1727
|
+
return jsonResult({ deleted: data.deleteMovethewheelsOrder, id });
|
|
1728
|
+
} catch (error) {
|
|
1729
|
+
ctx.logger.error("movethewheels orders_delete failed", { error: String(error) });
|
|
1730
|
+
return errorResult(
|
|
1731
|
+
`Failed to delete order: ${error instanceof Error ? error.message : String(error)}`
|
|
1732
|
+
);
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
);
|
|
1736
|
+
}
|
|
1737
|
+
function registerShipmentTools(server, ctx, track) {
|
|
1738
|
+
server.registerTool(
|
|
1739
|
+
track(mcpToolName(ctx.productName, "shipments_get")),
|
|
1740
|
+
{
|
|
1741
|
+
title: "Get Shipment",
|
|
1742
|
+
description: "Fetch a single shipment by ID, including its linked order and tracking events.",
|
|
1743
|
+
inputSchema: { id: z.string().describe("Shipment ID") }
|
|
1744
|
+
},
|
|
1745
|
+
async ({ id }) => {
|
|
1746
|
+
try {
|
|
1747
|
+
const data = await ctx.gql.workspace.request(GET_SHIPMENT, { id });
|
|
1748
|
+
if (!data.shipment) return errorResult(`Shipment ${id} not found`);
|
|
1749
|
+
return jsonResult(data.shipment);
|
|
1750
|
+
} catch (error) {
|
|
1751
|
+
ctx.logger.error("movethewheels shipments_get failed", { error: String(error) });
|
|
1752
|
+
return errorResult(
|
|
1753
|
+
`Failed to get shipment: ${error instanceof Error ? error.message : String(error)}`
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
);
|
|
1758
|
+
server.registerTool(
|
|
1759
|
+
track(mcpToolName(ctx.productName, "shipments_list")),
|
|
1760
|
+
{
|
|
1761
|
+
title: "List Shipments",
|
|
1762
|
+
description: "List shipments with optional order/customer/carrier/status/text filters and pagination.",
|
|
1763
|
+
inputSchema: {
|
|
1764
|
+
search: z.string().optional().describe("Free-text search across shipmentNumber"),
|
|
1765
|
+
orderId: z.string().optional().describe("Filter by order ID"),
|
|
1766
|
+
customerId: z.string().optional().describe("Filter by customer ID"),
|
|
1767
|
+
carrierId: z.string().optional().describe("Filter by carrier ID"),
|
|
1768
|
+
status: ShipmentStatusEnum.optional().describe("Filter by shipment status"),
|
|
1769
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
1770
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
1771
|
+
}
|
|
1772
|
+
},
|
|
1773
|
+
async ({ search, orderId, customerId, carrierId, status, limit, offset }) => {
|
|
1774
|
+
try {
|
|
1775
|
+
const filter = {};
|
|
1776
|
+
if (search !== void 0) filter.search = search;
|
|
1777
|
+
if (orderId !== void 0) filter.orderId = orderId;
|
|
1778
|
+
if (customerId !== void 0) filter.customerId = customerId;
|
|
1779
|
+
if (carrierId !== void 0) filter.carrierId = carrierId;
|
|
1780
|
+
if (status !== void 0) filter.status = status;
|
|
1781
|
+
const data = await ctx.gql.workspace.request(LIST_SHIPMENTS, {
|
|
1782
|
+
filter: Object.keys(filter).length > 0 ? filter : void 0,
|
|
1783
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
1784
|
+
});
|
|
1785
|
+
return jsonResult(data.shipments);
|
|
1786
|
+
} catch (error) {
|
|
1787
|
+
ctx.logger.error("movethewheels shipments_list failed", { error: String(error) });
|
|
1788
|
+
return errorResult(
|
|
1789
|
+
`Failed to list shipments: ${error instanceof Error ? error.message : String(error)}`
|
|
1790
|
+
);
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
);
|
|
1794
|
+
server.registerTool(
|
|
1795
|
+
track(mcpToolName(ctx.productName, "shipments_active_deliveries")),
|
|
1796
|
+
{
|
|
1797
|
+
title: "List Active Deliveries",
|
|
1798
|
+
description: "List shipments currently in active delivery (in-transit / out-for-delivery).",
|
|
1799
|
+
inputSchema: {
|
|
1800
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
1801
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
1802
|
+
}
|
|
1803
|
+
},
|
|
1804
|
+
async ({ limit, offset }) => {
|
|
1805
|
+
try {
|
|
1806
|
+
const data = await ctx.gql.workspace.request(LIST_ACTIVE_DELIVERIES, {
|
|
1807
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
1808
|
+
});
|
|
1809
|
+
return jsonResult(data.activeDeliveries);
|
|
1810
|
+
} catch (error) {
|
|
1811
|
+
ctx.logger.error("movethewheels shipments_active_deliveries failed", {
|
|
1812
|
+
error: String(error)
|
|
1813
|
+
});
|
|
1814
|
+
return errorResult(
|
|
1815
|
+
`Failed to list active deliveries: ${error instanceof Error ? error.message : String(error)}`
|
|
1816
|
+
);
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
);
|
|
1820
|
+
server.registerTool(
|
|
1821
|
+
track(mcpToolName(ctx.productName, "shipments_create")),
|
|
1822
|
+
{
|
|
1823
|
+
title: "Create Shipment",
|
|
1824
|
+
description: "Create a shipment for an order. `orderId`, `shipmentNumber`, `origin`, and `destination` are required. `status` defaults to PLANNED.",
|
|
1825
|
+
inputSchema: {
|
|
1826
|
+
orderId: z.string().min(1).describe("Source order ID"),
|
|
1827
|
+
shipmentNumber: z.string().min(1).describe("Human-readable shipment number (unique)"),
|
|
1828
|
+
origin: JsonObject.describe("Origin address JSON"),
|
|
1829
|
+
destination: JsonObject.describe("Destination address JSON"),
|
|
1830
|
+
status: ShipmentStatusEnum.optional().describe("Initial status (default PLANNED)"),
|
|
1831
|
+
customerId: z.string().optional().describe("Customer ID"),
|
|
1832
|
+
carrierId: z.string().optional().describe("Carrier ID"),
|
|
1833
|
+
trackingNumber: z.string().optional().describe("Carrier tracking number"),
|
|
1834
|
+
cargoDetails: JsonObject.optional().describe("Cargo details JSON"),
|
|
1835
|
+
estimatedDelivery: z.string().optional().describe("Estimated delivery (ISO 8601 DateTime)"),
|
|
1836
|
+
actualDelivery: z.string().optional().describe("Actual delivery (ISO 8601 DateTime)")
|
|
1837
|
+
}
|
|
1838
|
+
},
|
|
1839
|
+
async ({
|
|
1840
|
+
orderId,
|
|
1841
|
+
shipmentNumber,
|
|
1842
|
+
origin,
|
|
1843
|
+
destination,
|
|
1844
|
+
status,
|
|
1845
|
+
customerId,
|
|
1846
|
+
carrierId,
|
|
1847
|
+
trackingNumber,
|
|
1848
|
+
cargoDetails,
|
|
1849
|
+
estimatedDelivery,
|
|
1850
|
+
actualDelivery
|
|
1851
|
+
}) => {
|
|
1852
|
+
try {
|
|
1853
|
+
const input = { orderId, shipmentNumber, origin, destination };
|
|
1854
|
+
if (status !== void 0) input.status = status;
|
|
1855
|
+
if (customerId !== void 0) input.customerId = customerId;
|
|
1856
|
+
if (carrierId !== void 0) input.carrierId = carrierId;
|
|
1857
|
+
if (trackingNumber !== void 0) input.trackingNumber = trackingNumber;
|
|
1858
|
+
if (cargoDetails !== void 0) input.cargoDetails = cargoDetails;
|
|
1859
|
+
if (estimatedDelivery !== void 0) input.estimatedDelivery = estimatedDelivery;
|
|
1860
|
+
if (actualDelivery !== void 0) input.actualDelivery = actualDelivery;
|
|
1861
|
+
const data = await ctx.gql.workspace.request(CREATE_SHIPMENT, { input });
|
|
1862
|
+
return jsonResult(data.createShipment);
|
|
1863
|
+
} catch (error) {
|
|
1864
|
+
ctx.logger.error("movethewheels shipments_create failed", { error: String(error) });
|
|
1865
|
+
return errorResult(
|
|
1866
|
+
`Failed to create shipment: ${error instanceof Error ? error.message : String(error)}`
|
|
1867
|
+
);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
);
|
|
1871
|
+
server.registerTool(
|
|
1872
|
+
track(mcpToolName(ctx.productName, "shipments_update")),
|
|
1873
|
+
{
|
|
1874
|
+
title: "Update Shipment",
|
|
1875
|
+
description: "Update a shipment. At least one mutable field must be provided.",
|
|
1876
|
+
inputSchema: {
|
|
1877
|
+
id: z.string().describe("Shipment ID"),
|
|
1878
|
+
status: ShipmentStatusEnum.optional().describe("Updated status"),
|
|
1879
|
+
customerId: z.string().optional().describe("Updated customer ID"),
|
|
1880
|
+
carrierId: z.string().optional().describe("Updated carrier ID"),
|
|
1881
|
+
origin: JsonObject.optional().describe("Updated origin JSON"),
|
|
1882
|
+
destination: JsonObject.optional().describe("Updated destination JSON"),
|
|
1883
|
+
trackingNumber: z.string().optional().describe("Updated tracking number"),
|
|
1884
|
+
cargoDetails: JsonObject.optional().describe("Updated cargo details JSON"),
|
|
1885
|
+
estimatedDelivery: z.string().optional().describe("Updated estimated delivery (ISO 8601)"),
|
|
1886
|
+
actualDelivery: z.string().optional().describe("Updated actual delivery (ISO 8601)")
|
|
1887
|
+
}
|
|
1888
|
+
},
|
|
1889
|
+
async ({
|
|
1890
|
+
id,
|
|
1891
|
+
status,
|
|
1892
|
+
customerId,
|
|
1893
|
+
carrierId,
|
|
1894
|
+
origin,
|
|
1895
|
+
destination,
|
|
1896
|
+
trackingNumber,
|
|
1897
|
+
cargoDetails,
|
|
1898
|
+
estimatedDelivery,
|
|
1899
|
+
actualDelivery
|
|
1900
|
+
}) => {
|
|
1901
|
+
try {
|
|
1902
|
+
const input = {};
|
|
1903
|
+
if (status !== void 0) input.status = status;
|
|
1904
|
+
if (customerId !== void 0) input.customerId = customerId;
|
|
1905
|
+
if (carrierId !== void 0) input.carrierId = carrierId;
|
|
1906
|
+
if (origin !== void 0) input.origin = origin;
|
|
1907
|
+
if (destination !== void 0) input.destination = destination;
|
|
1908
|
+
if (trackingNumber !== void 0) input.trackingNumber = trackingNumber;
|
|
1909
|
+
if (cargoDetails !== void 0) input.cargoDetails = cargoDetails;
|
|
1910
|
+
if (estimatedDelivery !== void 0) input.estimatedDelivery = estimatedDelivery;
|
|
1911
|
+
if (actualDelivery !== void 0) input.actualDelivery = actualDelivery;
|
|
1912
|
+
if (Object.keys(input).length === 0) {
|
|
1913
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field.");
|
|
1914
|
+
}
|
|
1915
|
+
const data = await ctx.gql.workspace.request(UPDATE_SHIPMENT, { id, input });
|
|
1916
|
+
return jsonResult(data.updateShipment);
|
|
1917
|
+
} catch (error) {
|
|
1918
|
+
ctx.logger.error("movethewheels shipments_update failed", { error: String(error) });
|
|
1919
|
+
return errorResult(
|
|
1920
|
+
`Failed to update shipment: ${error instanceof Error ? error.message : String(error)}`
|
|
1921
|
+
);
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
);
|
|
1925
|
+
server.registerTool(
|
|
1926
|
+
track(mcpToolName(ctx.productName, "shipments_delete")),
|
|
1927
|
+
{
|
|
1928
|
+
title: "Delete Shipment",
|
|
1929
|
+
description: "Soft-delete a shipment by ID.",
|
|
1930
|
+
inputSchema: { id: z.string().describe("Shipment ID") }
|
|
1931
|
+
},
|
|
1932
|
+
async ({ id }) => {
|
|
1933
|
+
try {
|
|
1934
|
+
const data = await ctx.gql.workspace.request(
|
|
1935
|
+
DELETE_SHIPMENT,
|
|
1936
|
+
{ id }
|
|
1937
|
+
);
|
|
1938
|
+
return jsonResult({ deleted: data.deleteShipment, id });
|
|
1939
|
+
} catch (error) {
|
|
1940
|
+
ctx.logger.error("movethewheels shipments_delete failed", { error: String(error) });
|
|
1941
|
+
return errorResult(
|
|
1942
|
+
`Failed to delete shipment: ${error instanceof Error ? error.message : String(error)}`
|
|
1943
|
+
);
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
);
|
|
1947
|
+
}
|
|
1948
|
+
function registerTrackingTools(server, ctx, track) {
|
|
1949
|
+
server.registerTool(
|
|
1950
|
+
track(mcpToolName(ctx.productName, "tracking_order_events")),
|
|
1951
|
+
{
|
|
1952
|
+
title: "Order Tracking Timeline",
|
|
1953
|
+
description: "List tracking events for an order (across its shipments), most recent first.",
|
|
1954
|
+
inputSchema: {
|
|
1955
|
+
orderId: z.string().describe("Order ID"),
|
|
1956
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
1957
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
1958
|
+
}
|
|
1959
|
+
},
|
|
1960
|
+
async ({ orderId, limit, offset }) => {
|
|
1961
|
+
try {
|
|
1962
|
+
const data = await ctx.gql.workspace.request(GET_ORDER_TRACKING, {
|
|
1963
|
+
orderId,
|
|
1964
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
1965
|
+
});
|
|
1966
|
+
return jsonResult(data.orderTracking);
|
|
1967
|
+
} catch (error) {
|
|
1968
|
+
ctx.logger.error("movethewheels tracking_order_events failed", { error: String(error) });
|
|
1969
|
+
return errorResult(
|
|
1970
|
+
`Failed to get order tracking: ${error instanceof Error ? error.message : String(error)}`
|
|
1971
|
+
);
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
);
|
|
1975
|
+
server.registerTool(
|
|
1976
|
+
track(mcpToolName(ctx.productName, "tracking_shipment_events")),
|
|
1977
|
+
{
|
|
1978
|
+
title: "Shipment Tracking Timeline",
|
|
1979
|
+
description: "List tracking events for a single shipment, most recent first.",
|
|
1980
|
+
inputSchema: {
|
|
1981
|
+
shipmentId: z.string().describe("Shipment ID"),
|
|
1982
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
1983
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
1984
|
+
}
|
|
1985
|
+
},
|
|
1986
|
+
async ({ shipmentId, limit, offset }) => {
|
|
1987
|
+
try {
|
|
1988
|
+
const data = await ctx.gql.workspace.request(GET_SHIPMENT_TRACKING, {
|
|
1989
|
+
shipmentId,
|
|
1990
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
1991
|
+
});
|
|
1992
|
+
return jsonResult(data.shipmentTracking);
|
|
1993
|
+
} catch (error) {
|
|
1994
|
+
ctx.logger.error("movethewheels tracking_shipment_events failed", { error: String(error) });
|
|
1995
|
+
return errorResult(
|
|
1996
|
+
`Failed to get shipment tracking: ${error instanceof Error ? error.message : String(error)}`
|
|
1997
|
+
);
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
);
|
|
2001
|
+
server.registerTool(
|
|
2002
|
+
track(mcpToolName(ctx.productName, "tracking_alerts")),
|
|
2003
|
+
{
|
|
2004
|
+
title: "Tracking Alerts",
|
|
2005
|
+
description: "List active tracking alerts (delayed / exception shipments needing attention).",
|
|
2006
|
+
inputSchema: {
|
|
2007
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
2008
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
2009
|
+
}
|
|
2010
|
+
},
|
|
2011
|
+
async ({ limit, offset }) => {
|
|
2012
|
+
try {
|
|
2013
|
+
const data = await ctx.gql.workspace.request(LIST_TRACKING_ALERTS, {
|
|
2014
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
2015
|
+
});
|
|
2016
|
+
return jsonResult(data.trackingAlerts);
|
|
2017
|
+
} catch (error) {
|
|
2018
|
+
ctx.logger.error("movethewheels tracking_alerts failed", { error: String(error) });
|
|
2019
|
+
return errorResult(
|
|
2020
|
+
`Failed to list tracking alerts: ${error instanceof Error ? error.message : String(error)}`
|
|
2021
|
+
);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
);
|
|
2025
|
+
server.registerTool(
|
|
2026
|
+
track(mcpToolName(ctx.productName, "tracking_add_event")),
|
|
2027
|
+
{
|
|
2028
|
+
title: "Add Tracking Event",
|
|
2029
|
+
description: "Append a tracking event to a shipment timeline. `shipmentId` and `eventType` are required.",
|
|
2030
|
+
inputSchema: {
|
|
2031
|
+
shipmentId: z.string().min(1).describe("Shipment ID"),
|
|
2032
|
+
eventType: TrackingEventTypeEnum.describe("Type of tracking event"),
|
|
2033
|
+
details: z.string().optional().describe("Free-text event details"),
|
|
2034
|
+
location: JsonObject.optional().describe("Location JSON ({ lat, lng, address })"),
|
|
2035
|
+
timestamp: z.string().optional().describe("Event timestamp (ISO 8601; defaults to now)")
|
|
2036
|
+
}
|
|
2037
|
+
},
|
|
2038
|
+
async ({ shipmentId, eventType, details, location, timestamp }) => {
|
|
2039
|
+
try {
|
|
2040
|
+
const input = { shipmentId, eventType };
|
|
2041
|
+
if (details !== void 0) input.details = details;
|
|
2042
|
+
if (location !== void 0) input.location = location;
|
|
2043
|
+
if (timestamp !== void 0) input.timestamp = timestamp;
|
|
2044
|
+
const data = await ctx.gql.workspace.request(ADD_TRACKING_EVENT, { input });
|
|
2045
|
+
return jsonResult(data.addTrackingEvent);
|
|
2046
|
+
} catch (error) {
|
|
2047
|
+
ctx.logger.error("movethewheels tracking_add_event failed", { error: String(error) });
|
|
2048
|
+
return errorResult(
|
|
2049
|
+
`Failed to add tracking event: ${error instanceof Error ? error.message : String(error)}`
|
|
2050
|
+
);
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
);
|
|
2054
|
+
}
|
|
2055
|
+
function registerRouteTools(server, ctx, track) {
|
|
2056
|
+
server.registerTool(
|
|
2057
|
+
track(mcpToolName(ctx.productName, "routes_get")),
|
|
2058
|
+
{
|
|
2059
|
+
title: "Get Route",
|
|
2060
|
+
description: "Fetch a single route by ID, including its linked driver, vehicle, and shipment.",
|
|
2061
|
+
inputSchema: { id: z.string().describe("Route ID") }
|
|
2062
|
+
},
|
|
2063
|
+
async ({ id }) => {
|
|
2064
|
+
try {
|
|
2065
|
+
const data = await ctx.gql.workspace.request(GET_ROUTE, { id });
|
|
2066
|
+
if (!data.route) return errorResult(`Route ${id} not found`);
|
|
2067
|
+
return jsonResult(data.route);
|
|
2068
|
+
} catch (error) {
|
|
2069
|
+
ctx.logger.error("movethewheels routes_get failed", { error: String(error) });
|
|
2070
|
+
return errorResult(
|
|
2071
|
+
`Failed to get route: ${error instanceof Error ? error.message : String(error)}`
|
|
2072
|
+
);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
);
|
|
2076
|
+
server.registerTool(
|
|
2077
|
+
track(mcpToolName(ctx.productName, "routes_list")),
|
|
2078
|
+
{
|
|
2079
|
+
title: "List Routes",
|
|
2080
|
+
description: "List routes with optional driver/vehicle/shipment/status/text filters and pagination.",
|
|
2081
|
+
inputSchema: {
|
|
2082
|
+
search: z.string().optional().describe("Free-text search across route name"),
|
|
2083
|
+
driverId: z.string().optional().describe("Filter by driver ID"),
|
|
2084
|
+
vehicleId: z.string().optional().describe("Filter by vehicle ID"),
|
|
2085
|
+
shipmentId: z.string().optional().describe("Filter by shipment ID"),
|
|
2086
|
+
status: RouteStatusEnum.optional().describe("Filter by route status"),
|
|
2087
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
2088
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
2089
|
+
}
|
|
2090
|
+
},
|
|
2091
|
+
async ({ search, driverId, vehicleId, shipmentId, status, limit, offset }) => {
|
|
2092
|
+
try {
|
|
2093
|
+
const filter = {};
|
|
2094
|
+
if (search !== void 0) filter.search = search;
|
|
2095
|
+
if (driverId !== void 0) filter.driverId = driverId;
|
|
2096
|
+
if (vehicleId !== void 0) filter.vehicleId = vehicleId;
|
|
2097
|
+
if (shipmentId !== void 0) filter.shipmentId = shipmentId;
|
|
2098
|
+
if (status !== void 0) filter.status = status;
|
|
2099
|
+
const data = await ctx.gql.workspace.request(LIST_ROUTES, {
|
|
2100
|
+
filter: Object.keys(filter).length > 0 ? filter : void 0,
|
|
2101
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
2102
|
+
});
|
|
2103
|
+
return jsonResult(data.routes);
|
|
2104
|
+
} catch (error) {
|
|
2105
|
+
ctx.logger.error("movethewheels routes_list failed", { error: String(error) });
|
|
2106
|
+
return errorResult(
|
|
2107
|
+
`Failed to list routes: ${error instanceof Error ? error.message : String(error)}`
|
|
2108
|
+
);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
);
|
|
2112
|
+
server.registerTool(
|
|
2113
|
+
track(mcpToolName(ctx.productName, "routes_create")),
|
|
2114
|
+
{
|
|
2115
|
+
title: "Create Route",
|
|
2116
|
+
description: "Plan a route. `name` and `waypoints` are required; `status` defaults to PLANNED.",
|
|
2117
|
+
inputSchema: {
|
|
2118
|
+
name: z.string().min(1).describe("Route name"),
|
|
2119
|
+
waypoints: JsonValue.describe("Waypoints JSON (ordered array of stops)"),
|
|
2120
|
+
status: RouteStatusEnum.optional().describe("Initial status (default PLANNED)"),
|
|
2121
|
+
shipmentId: z.string().optional().describe("Linked shipment ID"),
|
|
2122
|
+
driverId: z.string().optional().describe("Assigned driver ID"),
|
|
2123
|
+
vehicleId: z.string().optional().describe("Assigned vehicle ID"),
|
|
2124
|
+
estimatedDistance: z.number().optional().describe("Estimated distance"),
|
|
2125
|
+
actualDistance: z.number().optional().describe("Actual distance"),
|
|
2126
|
+
estimatedDuration: z.number().optional().describe("Estimated duration"),
|
|
2127
|
+
actualDuration: z.number().optional().describe("Actual duration"),
|
|
2128
|
+
fuelCost: z.number().optional().describe("Estimated/actual fuel cost")
|
|
2129
|
+
}
|
|
2130
|
+
},
|
|
2131
|
+
async ({
|
|
2132
|
+
name,
|
|
2133
|
+
waypoints,
|
|
2134
|
+
status,
|
|
2135
|
+
shipmentId,
|
|
2136
|
+
driverId,
|
|
2137
|
+
vehicleId,
|
|
2138
|
+
estimatedDistance,
|
|
2139
|
+
actualDistance,
|
|
2140
|
+
estimatedDuration,
|
|
2141
|
+
actualDuration,
|
|
2142
|
+
fuelCost
|
|
2143
|
+
}) => {
|
|
2144
|
+
try {
|
|
2145
|
+
const input = { name, waypoints };
|
|
2146
|
+
if (status !== void 0) input.status = status;
|
|
2147
|
+
if (shipmentId !== void 0) input.shipmentId = shipmentId;
|
|
2148
|
+
if (driverId !== void 0) input.driverId = driverId;
|
|
2149
|
+
if (vehicleId !== void 0) input.vehicleId = vehicleId;
|
|
2150
|
+
if (estimatedDistance !== void 0) input.estimatedDistance = estimatedDistance;
|
|
2151
|
+
if (actualDistance !== void 0) input.actualDistance = actualDistance;
|
|
2152
|
+
if (estimatedDuration !== void 0) input.estimatedDuration = estimatedDuration;
|
|
2153
|
+
if (actualDuration !== void 0) input.actualDuration = actualDuration;
|
|
2154
|
+
if (fuelCost !== void 0) input.fuelCost = fuelCost;
|
|
2155
|
+
const data = await ctx.gql.workspace.request(CREATE_ROUTE, { input });
|
|
2156
|
+
return jsonResult(data.createRoute);
|
|
2157
|
+
} catch (error) {
|
|
2158
|
+
ctx.logger.error("movethewheels routes_create failed", { error: String(error) });
|
|
2159
|
+
return errorResult(
|
|
2160
|
+
`Failed to create route: ${error instanceof Error ? error.message : String(error)}`
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
);
|
|
2165
|
+
server.registerTool(
|
|
2166
|
+
track(mcpToolName(ctx.productName, "routes_update")),
|
|
2167
|
+
{
|
|
2168
|
+
title: "Update Route",
|
|
2169
|
+
description: "Update a route. At least one mutable field must be provided.",
|
|
2170
|
+
inputSchema: {
|
|
2171
|
+
id: z.string().describe("Route ID"),
|
|
2172
|
+
name: z.string().optional().describe("Updated route name"),
|
|
2173
|
+
waypoints: JsonValue.optional().describe("Updated waypoints JSON"),
|
|
2174
|
+
status: RouteStatusEnum.optional().describe("Updated status"),
|
|
2175
|
+
shipmentId: z.string().optional().describe("Updated shipment ID"),
|
|
2176
|
+
driverId: z.string().optional().describe("Updated driver ID"),
|
|
2177
|
+
vehicleId: z.string().optional().describe("Updated vehicle ID"),
|
|
2178
|
+
estimatedDistance: z.number().optional().describe("Updated estimated distance"),
|
|
2179
|
+
actualDistance: z.number().optional().describe("Updated actual distance"),
|
|
2180
|
+
estimatedDuration: z.number().optional().describe("Updated estimated duration"),
|
|
2181
|
+
actualDuration: z.number().optional().describe("Updated actual duration"),
|
|
2182
|
+
fuelCost: z.number().optional().describe("Updated fuel cost")
|
|
2183
|
+
}
|
|
2184
|
+
},
|
|
2185
|
+
async ({
|
|
2186
|
+
id,
|
|
2187
|
+
name,
|
|
2188
|
+
waypoints,
|
|
2189
|
+
status,
|
|
2190
|
+
shipmentId,
|
|
2191
|
+
driverId,
|
|
2192
|
+
vehicleId,
|
|
2193
|
+
estimatedDistance,
|
|
2194
|
+
actualDistance,
|
|
2195
|
+
estimatedDuration,
|
|
2196
|
+
actualDuration,
|
|
2197
|
+
fuelCost
|
|
2198
|
+
}) => {
|
|
2199
|
+
try {
|
|
2200
|
+
const input = {};
|
|
2201
|
+
if (name !== void 0) input.name = name;
|
|
2202
|
+
if (waypoints !== void 0) input.waypoints = waypoints;
|
|
2203
|
+
if (status !== void 0) input.status = status;
|
|
2204
|
+
if (shipmentId !== void 0) input.shipmentId = shipmentId;
|
|
2205
|
+
if (driverId !== void 0) input.driverId = driverId;
|
|
2206
|
+
if (vehicleId !== void 0) input.vehicleId = vehicleId;
|
|
2207
|
+
if (estimatedDistance !== void 0) input.estimatedDistance = estimatedDistance;
|
|
2208
|
+
if (actualDistance !== void 0) input.actualDistance = actualDistance;
|
|
2209
|
+
if (estimatedDuration !== void 0) input.estimatedDuration = estimatedDuration;
|
|
2210
|
+
if (actualDuration !== void 0) input.actualDuration = actualDuration;
|
|
2211
|
+
if (fuelCost !== void 0) input.fuelCost = fuelCost;
|
|
2212
|
+
if (Object.keys(input).length === 0) {
|
|
2213
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field.");
|
|
2214
|
+
}
|
|
2215
|
+
const data = await ctx.gql.workspace.request(UPDATE_ROUTE, { id, input });
|
|
2216
|
+
return jsonResult(data.updateRoute);
|
|
2217
|
+
} catch (error) {
|
|
2218
|
+
ctx.logger.error("movethewheels routes_update failed", { error: String(error) });
|
|
2219
|
+
return errorResult(
|
|
2220
|
+
`Failed to update route: ${error instanceof Error ? error.message : String(error)}`
|
|
2221
|
+
);
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
);
|
|
2225
|
+
server.registerTool(
|
|
2226
|
+
track(mcpToolName(ctx.productName, "routes_optimize")),
|
|
2227
|
+
{
|
|
2228
|
+
title: "Optimize Route",
|
|
2229
|
+
description: "Run route optimization for a route (reorders waypoints / recomputes distance & duration).",
|
|
2230
|
+
inputSchema: { id: z.string().describe("Route ID") }
|
|
2231
|
+
},
|
|
2232
|
+
async ({ id }) => {
|
|
2233
|
+
try {
|
|
2234
|
+
const data = await ctx.gql.workspace.request(OPTIMIZE_ROUTE, { id });
|
|
2235
|
+
return jsonResult(data.optimizeRoute);
|
|
2236
|
+
} catch (error) {
|
|
2237
|
+
ctx.logger.error("movethewheels routes_optimize failed", { error: String(error) });
|
|
2238
|
+
return errorResult(
|
|
2239
|
+
`Failed to optimize route: ${error instanceof Error ? error.message : String(error)}`
|
|
2240
|
+
);
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
);
|
|
2244
|
+
server.registerTool(
|
|
2245
|
+
track(mcpToolName(ctx.productName, "routes_delete")),
|
|
2246
|
+
{
|
|
2247
|
+
title: "Delete Route",
|
|
2248
|
+
description: "Soft-delete a route by ID.",
|
|
2249
|
+
inputSchema: { id: z.string().describe("Route ID") }
|
|
2250
|
+
},
|
|
2251
|
+
async ({ id }) => {
|
|
2252
|
+
try {
|
|
2253
|
+
const data = await ctx.gql.workspace.request(DELETE_ROUTE, {
|
|
2254
|
+
id
|
|
2255
|
+
});
|
|
2256
|
+
return jsonResult({ deleted: data.deleteRoute, id });
|
|
2257
|
+
} catch (error) {
|
|
2258
|
+
ctx.logger.error("movethewheels routes_delete failed", { error: String(error) });
|
|
2259
|
+
return errorResult(
|
|
2260
|
+
`Failed to delete route: ${error instanceof Error ? error.message : String(error)}`
|
|
2261
|
+
);
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
);
|
|
2265
|
+
}
|
|
2266
|
+
function registerWarehouseTools(server, ctx, track) {
|
|
2267
|
+
server.registerTool(
|
|
2268
|
+
track(mcpToolName(ctx.productName, "warehouses_get")),
|
|
2269
|
+
{
|
|
2270
|
+
title: "Get Warehouse",
|
|
2271
|
+
description: "Fetch a single warehouse by ID, including its storage locations.",
|
|
2272
|
+
inputSchema: { id: z.string().describe("Warehouse ID") }
|
|
2273
|
+
},
|
|
2274
|
+
async ({ id }) => {
|
|
2275
|
+
try {
|
|
2276
|
+
const data = await ctx.gql.workspace.request(GET_WAREHOUSE, { id });
|
|
2277
|
+
if (!data.warehouse) return errorResult(`Warehouse ${id} not found`);
|
|
2278
|
+
return jsonResult(data.warehouse);
|
|
2279
|
+
} catch (error) {
|
|
2280
|
+
ctx.logger.error("movethewheels warehouses_get failed", { error: String(error) });
|
|
2281
|
+
return errorResult(
|
|
2282
|
+
`Failed to get warehouse: ${error instanceof Error ? error.message : String(error)}`
|
|
2283
|
+
);
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
);
|
|
2287
|
+
server.registerTool(
|
|
2288
|
+
track(mcpToolName(ctx.productName, "warehouses_list")),
|
|
2289
|
+
{
|
|
2290
|
+
title: "List Warehouses",
|
|
2291
|
+
description: "List warehouses with optional type/status/text filters and pagination.",
|
|
2292
|
+
inputSchema: {
|
|
2293
|
+
search: z.string().optional().describe("Free-text search across name/code"),
|
|
2294
|
+
status: z.string().optional().describe('Filter by status, e.g. "ACTIVE"'),
|
|
2295
|
+
type: WarehouseTypeEnum.optional().describe("Filter by warehouse type"),
|
|
2296
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
2297
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
2298
|
+
}
|
|
2299
|
+
},
|
|
2300
|
+
async ({ search, status, type, limit, offset }) => {
|
|
2301
|
+
try {
|
|
2302
|
+
const filter = {};
|
|
2303
|
+
if (search !== void 0) filter.search = search;
|
|
2304
|
+
if (status !== void 0) filter.status = status;
|
|
2305
|
+
if (type !== void 0) filter.type = type;
|
|
2306
|
+
const data = await ctx.gql.workspace.request(LIST_WAREHOUSES, {
|
|
2307
|
+
filter: Object.keys(filter).length > 0 ? filter : void 0,
|
|
2308
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
2309
|
+
});
|
|
2310
|
+
return jsonResult(data.warehouses);
|
|
2311
|
+
} catch (error) {
|
|
2312
|
+
ctx.logger.error("movethewheels warehouses_list failed", { error: String(error) });
|
|
2313
|
+
return errorResult(
|
|
2314
|
+
`Failed to list warehouses: ${error instanceof Error ? error.message : String(error)}`
|
|
2315
|
+
);
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
);
|
|
2319
|
+
server.registerTool(
|
|
2320
|
+
track(mcpToolName(ctx.productName, "warehouses_create")),
|
|
2321
|
+
{
|
|
2322
|
+
title: "Create Warehouse",
|
|
2323
|
+
description: 'Create a warehouse facility. `name`, `code`, `type`, and `address` are required; `status` defaults to "ACTIVE".',
|
|
2324
|
+
inputSchema: {
|
|
2325
|
+
name: z.string().min(1).describe("Warehouse name"),
|
|
2326
|
+
code: z.string().min(1).describe("Warehouse code (unique)"),
|
|
2327
|
+
type: WarehouseTypeEnum.describe("Warehouse type"),
|
|
2328
|
+
address: JsonObject.describe("Address JSON"),
|
|
2329
|
+
status: z.string().optional().describe('Status (default "ACTIVE")'),
|
|
2330
|
+
contactInfo: JsonObject.optional().describe("Contact info JSON"),
|
|
2331
|
+
operatingHours: JsonObject.optional().describe("Operating hours JSON"),
|
|
2332
|
+
capabilities: JsonObject.optional().describe("Capabilities JSON"),
|
|
2333
|
+
totalCapacity: z.number().optional().describe("Total capacity"),
|
|
2334
|
+
usedCapacity: z.number().optional().describe("Used capacity")
|
|
2335
|
+
}
|
|
2336
|
+
},
|
|
2337
|
+
async ({
|
|
2338
|
+
name,
|
|
2339
|
+
code,
|
|
2340
|
+
type,
|
|
2341
|
+
address,
|
|
2342
|
+
status,
|
|
2343
|
+
contactInfo,
|
|
2344
|
+
operatingHours,
|
|
2345
|
+
capabilities,
|
|
2346
|
+
totalCapacity,
|
|
2347
|
+
usedCapacity
|
|
2348
|
+
}) => {
|
|
2349
|
+
try {
|
|
2350
|
+
const input = { name, code, type, address };
|
|
2351
|
+
if (status !== void 0) input.status = status;
|
|
2352
|
+
if (contactInfo !== void 0) input.contactInfo = contactInfo;
|
|
2353
|
+
if (operatingHours !== void 0) input.operatingHours = operatingHours;
|
|
2354
|
+
if (capabilities !== void 0) input.capabilities = capabilities;
|
|
2355
|
+
if (totalCapacity !== void 0) input.totalCapacity = totalCapacity;
|
|
2356
|
+
if (usedCapacity !== void 0) input.usedCapacity = usedCapacity;
|
|
2357
|
+
const data = await ctx.gql.workspace.request(CREATE_WAREHOUSE, { input });
|
|
2358
|
+
return jsonResult(data.createWarehouse);
|
|
2359
|
+
} catch (error) {
|
|
2360
|
+
ctx.logger.error("movethewheels warehouses_create failed", { error: String(error) });
|
|
2361
|
+
return errorResult(
|
|
2362
|
+
`Failed to create warehouse: ${error instanceof Error ? error.message : String(error)}`
|
|
2363
|
+
);
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
);
|
|
2367
|
+
server.registerTool(
|
|
2368
|
+
track(mcpToolName(ctx.productName, "warehouses_update")),
|
|
2369
|
+
{
|
|
2370
|
+
title: "Update Warehouse",
|
|
2371
|
+
description: "Update a warehouse. At least one mutable field must be provided.",
|
|
2372
|
+
inputSchema: {
|
|
2373
|
+
id: z.string().describe("Warehouse ID"),
|
|
2374
|
+
name: z.string().optional().describe("Updated name"),
|
|
2375
|
+
code: z.string().optional().describe("Updated code"),
|
|
2376
|
+
type: WarehouseTypeEnum.optional().describe("Updated type"),
|
|
2377
|
+
status: z.string().optional().describe("Updated status"),
|
|
2378
|
+
address: JsonObject.optional().describe("Updated address JSON"),
|
|
2379
|
+
contactInfo: JsonObject.optional().describe("Updated contact info JSON"),
|
|
2380
|
+
operatingHours: JsonObject.optional().describe("Updated operating hours JSON"),
|
|
2381
|
+
capabilities: JsonObject.optional().describe("Updated capabilities JSON"),
|
|
2382
|
+
totalCapacity: z.number().optional().describe("Updated total capacity"),
|
|
2383
|
+
usedCapacity: z.number().optional().describe("Updated used capacity")
|
|
2384
|
+
}
|
|
2385
|
+
},
|
|
2386
|
+
async ({
|
|
2387
|
+
id,
|
|
2388
|
+
name,
|
|
2389
|
+
code,
|
|
2390
|
+
type,
|
|
2391
|
+
status,
|
|
2392
|
+
address,
|
|
2393
|
+
contactInfo,
|
|
2394
|
+
operatingHours,
|
|
2395
|
+
capabilities,
|
|
2396
|
+
totalCapacity,
|
|
2397
|
+
usedCapacity
|
|
2398
|
+
}) => {
|
|
2399
|
+
try {
|
|
2400
|
+
const input = {};
|
|
2401
|
+
if (name !== void 0) input.name = name;
|
|
2402
|
+
if (code !== void 0) input.code = code;
|
|
2403
|
+
if (type !== void 0) input.type = type;
|
|
2404
|
+
if (status !== void 0) input.status = status;
|
|
2405
|
+
if (address !== void 0) input.address = address;
|
|
2406
|
+
if (contactInfo !== void 0) input.contactInfo = contactInfo;
|
|
2407
|
+
if (operatingHours !== void 0) input.operatingHours = operatingHours;
|
|
2408
|
+
if (capabilities !== void 0) input.capabilities = capabilities;
|
|
2409
|
+
if (totalCapacity !== void 0) input.totalCapacity = totalCapacity;
|
|
2410
|
+
if (usedCapacity !== void 0) input.usedCapacity = usedCapacity;
|
|
2411
|
+
if (Object.keys(input).length === 0) {
|
|
2412
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field.");
|
|
2413
|
+
}
|
|
2414
|
+
const data = await ctx.gql.workspace.request(UPDATE_WAREHOUSE, { id, input });
|
|
2415
|
+
return jsonResult(data.updateWarehouse);
|
|
2416
|
+
} catch (error) {
|
|
2417
|
+
ctx.logger.error("movethewheels warehouses_update failed", { error: String(error) });
|
|
2418
|
+
return errorResult(
|
|
2419
|
+
`Failed to update warehouse: ${error instanceof Error ? error.message : String(error)}`
|
|
2420
|
+
);
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
);
|
|
2424
|
+
server.registerTool(
|
|
2425
|
+
track(mcpToolName(ctx.productName, "warehouses_delete")),
|
|
2426
|
+
{
|
|
2427
|
+
title: "Delete Warehouse",
|
|
2428
|
+
description: "Soft-delete a warehouse by ID.",
|
|
2429
|
+
inputSchema: { id: z.string().describe("Warehouse ID") }
|
|
2430
|
+
},
|
|
2431
|
+
async ({ id }) => {
|
|
2432
|
+
try {
|
|
2433
|
+
const data = await ctx.gql.workspace.request(
|
|
2434
|
+
DELETE_WAREHOUSE,
|
|
2435
|
+
{ id }
|
|
2436
|
+
);
|
|
2437
|
+
return jsonResult({ deleted: data.deleteWarehouse, id });
|
|
2438
|
+
} catch (error) {
|
|
2439
|
+
ctx.logger.error("movethewheels warehouses_delete failed", { error: String(error) });
|
|
2440
|
+
return errorResult(
|
|
2441
|
+
`Failed to delete warehouse: ${error instanceof Error ? error.message : String(error)}`
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
);
|
|
2446
|
+
server.registerTool(
|
|
2447
|
+
track(mcpToolName(ctx.productName, "warehouses_create_location")),
|
|
2448
|
+
{
|
|
2449
|
+
title: "Create Warehouse Location",
|
|
2450
|
+
description: 'Create a storage location (bin/shelf/zone) inside a warehouse. `warehouseId` and `locationCode` are required; `status` defaults to "ACTIVE".',
|
|
2451
|
+
inputSchema: {
|
|
2452
|
+
warehouseId: z.string().min(1).describe("Parent warehouse ID"),
|
|
2453
|
+
locationCode: z.string().min(1).describe("Location code (unique within warehouse)"),
|
|
2454
|
+
locationType: z.string().optional().describe('Location type, e.g. "bin", "shelf"'),
|
|
2455
|
+
status: z.string().optional().describe('Status (default "ACTIVE")'),
|
|
2456
|
+
zone: z.string().optional().describe("Zone"),
|
|
2457
|
+
aisle: z.string().optional().describe("Aisle"),
|
|
2458
|
+
rack: z.string().optional().describe("Rack"),
|
|
2459
|
+
shelf: z.string().optional().describe("Shelf"),
|
|
2460
|
+
bin: z.string().optional().describe("Bin"),
|
|
2461
|
+
capacity: z.number().optional().describe("Capacity")
|
|
2462
|
+
}
|
|
2463
|
+
},
|
|
2464
|
+
async ({
|
|
2465
|
+
warehouseId,
|
|
2466
|
+
locationCode,
|
|
2467
|
+
locationType,
|
|
2468
|
+
status,
|
|
2469
|
+
zone,
|
|
2470
|
+
aisle,
|
|
2471
|
+
rack,
|
|
2472
|
+
shelf,
|
|
2473
|
+
bin,
|
|
2474
|
+
capacity
|
|
2475
|
+
}) => {
|
|
2476
|
+
try {
|
|
2477
|
+
const input = { warehouseId, locationCode };
|
|
2478
|
+
if (locationType !== void 0) input.locationType = locationType;
|
|
2479
|
+
if (status !== void 0) input.status = status;
|
|
2480
|
+
if (zone !== void 0) input.zone = zone;
|
|
2481
|
+
if (aisle !== void 0) input.aisle = aisle;
|
|
2482
|
+
if (rack !== void 0) input.rack = rack;
|
|
2483
|
+
if (shelf !== void 0) input.shelf = shelf;
|
|
2484
|
+
if (bin !== void 0) input.bin = bin;
|
|
2485
|
+
if (capacity !== void 0) input.capacity = capacity;
|
|
2486
|
+
const data = await ctx.gql.workspace.request(CREATE_WAREHOUSE_LOCATION, { input });
|
|
2487
|
+
return jsonResult(data.createWarehouseLocation);
|
|
2488
|
+
} catch (error) {
|
|
2489
|
+
ctx.logger.error("movethewheels warehouses_create_location failed", {
|
|
2490
|
+
error: String(error)
|
|
2491
|
+
});
|
|
2492
|
+
return errorResult(
|
|
2493
|
+
`Failed to create warehouse location: ${error instanceof Error ? error.message : String(error)}`
|
|
2494
|
+
);
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
);
|
|
2498
|
+
server.registerTool(
|
|
2499
|
+
track(mcpToolName(ctx.productName, "warehouses_update_location")),
|
|
2500
|
+
{
|
|
2501
|
+
title: "Update Warehouse Location",
|
|
2502
|
+
description: "Update a warehouse storage location. At least one mutable field must be provided.",
|
|
2503
|
+
inputSchema: {
|
|
2504
|
+
id: z.string().describe("Warehouse location ID"),
|
|
2505
|
+
locationCode: z.string().optional().describe("Updated location code"),
|
|
2506
|
+
locationType: z.string().optional().describe("Updated location type"),
|
|
2507
|
+
status: z.string().optional().describe("Updated status"),
|
|
2508
|
+
zone: z.string().optional().describe("Updated zone"),
|
|
2509
|
+
aisle: z.string().optional().describe("Updated aisle"),
|
|
2510
|
+
rack: z.string().optional().describe("Updated rack"),
|
|
2511
|
+
shelf: z.string().optional().describe("Updated shelf"),
|
|
2512
|
+
bin: z.string().optional().describe("Updated bin"),
|
|
2513
|
+
capacity: z.number().optional().describe("Updated capacity")
|
|
2514
|
+
}
|
|
2515
|
+
},
|
|
2516
|
+
async ({ id, locationCode, locationType, status, zone, aisle, rack, shelf, bin, capacity }) => {
|
|
2517
|
+
try {
|
|
2518
|
+
const input = {};
|
|
2519
|
+
if (locationCode !== void 0) input.locationCode = locationCode;
|
|
2520
|
+
if (locationType !== void 0) input.locationType = locationType;
|
|
2521
|
+
if (status !== void 0) input.status = status;
|
|
2522
|
+
if (zone !== void 0) input.zone = zone;
|
|
2523
|
+
if (aisle !== void 0) input.aisle = aisle;
|
|
2524
|
+
if (rack !== void 0) input.rack = rack;
|
|
2525
|
+
if (shelf !== void 0) input.shelf = shelf;
|
|
2526
|
+
if (bin !== void 0) input.bin = bin;
|
|
2527
|
+
if (capacity !== void 0) input.capacity = capacity;
|
|
2528
|
+
if (Object.keys(input).length === 0) {
|
|
2529
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field.");
|
|
2530
|
+
}
|
|
2531
|
+
const data = await ctx.gql.workspace.request(UPDATE_WAREHOUSE_LOCATION, { id, input });
|
|
2532
|
+
return jsonResult(data.updateWarehouseLocation);
|
|
2533
|
+
} catch (error) {
|
|
2534
|
+
ctx.logger.error("movethewheels warehouses_update_location failed", {
|
|
2535
|
+
error: String(error)
|
|
2536
|
+
});
|
|
2537
|
+
return errorResult(
|
|
2538
|
+
`Failed to update warehouse location: ${error instanceof Error ? error.message : String(error)}`
|
|
2539
|
+
);
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
);
|
|
2543
|
+
}
|
|
2544
|
+
function registerInventoryTools(server, ctx, track) {
|
|
2545
|
+
server.registerTool(
|
|
2546
|
+
track(mcpToolName(ctx.productName, "inventory_get_item")),
|
|
2547
|
+
{
|
|
2548
|
+
title: "Get Inventory Item",
|
|
2549
|
+
description: "Fetch a single inventory item by ID, including its product, warehouse, and location.",
|
|
2550
|
+
inputSchema: { id: z.string().describe("Inventory item ID") }
|
|
2551
|
+
},
|
|
2552
|
+
async ({ id }) => {
|
|
2553
|
+
try {
|
|
2554
|
+
const data = await ctx.gql.workspace.request(GET_INVENTORY_ITEM, { id });
|
|
2555
|
+
if (!data.inventoryItem) return errorResult(`Inventory item ${id} not found`);
|
|
2556
|
+
return jsonResult(data.inventoryItem);
|
|
2557
|
+
} catch (error) {
|
|
2558
|
+
ctx.logger.error("movethewheels inventory_get_item failed", { error: String(error) });
|
|
2559
|
+
return errorResult(
|
|
2560
|
+
`Failed to get inventory item: ${error instanceof Error ? error.message : String(error)}`
|
|
2561
|
+
);
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
);
|
|
2565
|
+
server.registerTool(
|
|
2566
|
+
track(mcpToolName(ctx.productName, "inventory_list_items")),
|
|
2567
|
+
{
|
|
2568
|
+
title: "List Inventory Items",
|
|
2569
|
+
description: "List inventory items with optional product/warehouse/location/text filters and pagination.",
|
|
2570
|
+
inputSchema: {
|
|
2571
|
+
search: z.string().optional().describe("Free-text search"),
|
|
2572
|
+
productId: z.string().optional().describe("Filter by product ID"),
|
|
2573
|
+
warehouseId: z.string().optional().describe("Filter by warehouse ID"),
|
|
2574
|
+
locationId: z.string().optional().describe("Filter by location ID"),
|
|
2575
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
2576
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
2577
|
+
}
|
|
2578
|
+
},
|
|
2579
|
+
async ({ search, productId, warehouseId, locationId, limit, offset }) => {
|
|
2580
|
+
try {
|
|
2581
|
+
const filter = {};
|
|
2582
|
+
if (search !== void 0) filter.search = search;
|
|
2583
|
+
if (productId !== void 0) filter.productId = productId;
|
|
2584
|
+
if (warehouseId !== void 0) filter.warehouseId = warehouseId;
|
|
2585
|
+
if (locationId !== void 0) filter.locationId = locationId;
|
|
2586
|
+
const data = await ctx.gql.workspace.request(LIST_INVENTORY_ITEMS, {
|
|
2587
|
+
filter: Object.keys(filter).length > 0 ? filter : void 0,
|
|
2588
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
2589
|
+
});
|
|
2590
|
+
return jsonResult(data.inventoryItems);
|
|
2591
|
+
} catch (error) {
|
|
2592
|
+
ctx.logger.error("movethewheels inventory_list_items failed", { error: String(error) });
|
|
2593
|
+
return errorResult(
|
|
2594
|
+
`Failed to list inventory items: ${error instanceof Error ? error.message : String(error)}`
|
|
2595
|
+
);
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
);
|
|
2599
|
+
server.registerTool(
|
|
2600
|
+
track(mcpToolName(ctx.productName, "inventory_stats")),
|
|
2601
|
+
{
|
|
2602
|
+
title: "Inventory Stats",
|
|
2603
|
+
description: "Aggregate inventory KPIs: total on-hand / available / allocated / damaged and low-stock count.",
|
|
2604
|
+
inputSchema: {}
|
|
2605
|
+
},
|
|
2606
|
+
async () => {
|
|
2607
|
+
try {
|
|
2608
|
+
const data = await ctx.gql.workspace.request(GET_INVENTORY_STATS, {});
|
|
2609
|
+
return jsonResult(data.inventoryStats);
|
|
2610
|
+
} catch (error) {
|
|
2611
|
+
ctx.logger.error("movethewheels inventory_stats failed", { error: String(error) });
|
|
2612
|
+
return errorResult(
|
|
2613
|
+
`Failed to get inventory stats: ${error instanceof Error ? error.message : String(error)}`
|
|
2614
|
+
);
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
);
|
|
2618
|
+
server.registerTool(
|
|
2619
|
+
track(mcpToolName(ctx.productName, "inventory_list_transactions")),
|
|
2620
|
+
{
|
|
2621
|
+
title: "List Inventory Transactions",
|
|
2622
|
+
description: "List inventory ledger transactions (receipts/issues/transfers/adjustments/returns) with optional product/warehouse/location/text filters.",
|
|
2623
|
+
inputSchema: {
|
|
2624
|
+
search: z.string().optional().describe("Free-text search"),
|
|
2625
|
+
productId: z.string().optional().describe("Filter by product ID"),
|
|
2626
|
+
warehouseId: z.string().optional().describe("Filter by warehouse ID"),
|
|
2627
|
+
locationId: z.string().optional().describe("Filter by location ID"),
|
|
2628
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
2629
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
2630
|
+
}
|
|
2631
|
+
},
|
|
2632
|
+
async ({ search, productId, warehouseId, locationId, limit, offset }) => {
|
|
2633
|
+
try {
|
|
2634
|
+
const filter = {};
|
|
2635
|
+
if (search !== void 0) filter.search = search;
|
|
2636
|
+
if (productId !== void 0) filter.productId = productId;
|
|
2637
|
+
if (warehouseId !== void 0) filter.warehouseId = warehouseId;
|
|
2638
|
+
if (locationId !== void 0) filter.locationId = locationId;
|
|
2639
|
+
const data = await ctx.gql.workspace.request(LIST_INVENTORY_TRANSACTIONS, {
|
|
2640
|
+
filter: Object.keys(filter).length > 0 ? filter : void 0,
|
|
2641
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
2642
|
+
});
|
|
2643
|
+
return jsonResult(data.inventoryTransactions);
|
|
2644
|
+
} catch (error) {
|
|
2645
|
+
ctx.logger.error("movethewheels inventory_list_transactions failed", {
|
|
2646
|
+
error: String(error)
|
|
2647
|
+
});
|
|
2648
|
+
return errorResult(
|
|
2649
|
+
`Failed to list inventory transactions: ${error instanceof Error ? error.message : String(error)}`
|
|
2650
|
+
);
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
);
|
|
2654
|
+
server.registerTool(
|
|
2655
|
+
track(mcpToolName(ctx.productName, "inventory_create_item")),
|
|
2656
|
+
{
|
|
2657
|
+
title: "Create Inventory Item",
|
|
2658
|
+
description: "Create an inventory item for a product at a warehouse. `productId` and `warehouseId` are required; stock counts default to 0.",
|
|
2659
|
+
inputSchema: {
|
|
2660
|
+
productId: z.string().min(1).describe("Product ID"),
|
|
2661
|
+
warehouseId: z.string().min(1).describe("Warehouse ID"),
|
|
2662
|
+
locationId: z.string().optional().describe("Storage location ID"),
|
|
2663
|
+
onHand: z.number().int().optional().describe("On-hand quantity (default 0)"),
|
|
2664
|
+
available: z.number().int().optional().describe("Available quantity (default 0)"),
|
|
2665
|
+
allocated: z.number().int().optional().describe("Allocated quantity (default 0)"),
|
|
2666
|
+
damaged: z.number().int().optional().describe("Damaged quantity (default 0)"),
|
|
2667
|
+
reorderPoint: z.number().int().optional().describe("Reorder point"),
|
|
2668
|
+
safetyStock: z.number().int().optional().describe("Safety stock"),
|
|
2669
|
+
unitCost: z.number().optional().describe("Unit cost")
|
|
2670
|
+
}
|
|
2671
|
+
},
|
|
2672
|
+
async ({
|
|
2673
|
+
productId,
|
|
2674
|
+
warehouseId,
|
|
2675
|
+
locationId,
|
|
2676
|
+
onHand,
|
|
2677
|
+
available,
|
|
2678
|
+
allocated,
|
|
2679
|
+
damaged,
|
|
2680
|
+
reorderPoint,
|
|
2681
|
+
safetyStock,
|
|
2682
|
+
unitCost
|
|
2683
|
+
}) => {
|
|
2684
|
+
try {
|
|
2685
|
+
const input = { productId, warehouseId };
|
|
2686
|
+
if (locationId !== void 0) input.locationId = locationId;
|
|
2687
|
+
if (onHand !== void 0) input.onHand = onHand;
|
|
2688
|
+
if (available !== void 0) input.available = available;
|
|
2689
|
+
if (allocated !== void 0) input.allocated = allocated;
|
|
2690
|
+
if (damaged !== void 0) input.damaged = damaged;
|
|
2691
|
+
if (reorderPoint !== void 0) input.reorderPoint = reorderPoint;
|
|
2692
|
+
if (safetyStock !== void 0) input.safetyStock = safetyStock;
|
|
2693
|
+
if (unitCost !== void 0) input.unitCost = unitCost;
|
|
2694
|
+
const data = await ctx.gql.workspace.request(CREATE_INVENTORY_ITEM, { input });
|
|
2695
|
+
return jsonResult(data.createInventoryItem);
|
|
2696
|
+
} catch (error) {
|
|
2697
|
+
ctx.logger.error("movethewheels inventory_create_item failed", { error: String(error) });
|
|
2698
|
+
return errorResult(
|
|
2699
|
+
`Failed to create inventory item: ${error instanceof Error ? error.message : String(error)}`
|
|
2700
|
+
);
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
);
|
|
2704
|
+
server.registerTool(
|
|
2705
|
+
track(mcpToolName(ctx.productName, "inventory_update_item")),
|
|
2706
|
+
{
|
|
2707
|
+
title: "Update Inventory Item",
|
|
2708
|
+
description: "Update an inventory item. At least one mutable field must be provided.",
|
|
2709
|
+
inputSchema: {
|
|
2710
|
+
id: z.string().describe("Inventory item ID"),
|
|
2711
|
+
locationId: z.string().optional().describe("Updated location ID"),
|
|
2712
|
+
onHand: z.number().int().optional().describe("Updated on-hand quantity"),
|
|
2713
|
+
available: z.number().int().optional().describe("Updated available quantity"),
|
|
2714
|
+
allocated: z.number().int().optional().describe("Updated allocated quantity"),
|
|
2715
|
+
damaged: z.number().int().optional().describe("Updated damaged quantity"),
|
|
2716
|
+
reorderPoint: z.number().int().optional().describe("Updated reorder point"),
|
|
2717
|
+
safetyStock: z.number().int().optional().describe("Updated safety stock"),
|
|
2718
|
+
unitCost: z.number().optional().describe("Updated unit cost")
|
|
2719
|
+
}
|
|
2720
|
+
},
|
|
2721
|
+
async ({
|
|
2722
|
+
id,
|
|
2723
|
+
locationId,
|
|
2724
|
+
onHand,
|
|
2725
|
+
available,
|
|
2726
|
+
allocated,
|
|
2727
|
+
damaged,
|
|
2728
|
+
reorderPoint,
|
|
2729
|
+
safetyStock,
|
|
2730
|
+
unitCost
|
|
2731
|
+
}) => {
|
|
2732
|
+
try {
|
|
2733
|
+
const input = {};
|
|
2734
|
+
if (locationId !== void 0) input.locationId = locationId;
|
|
2735
|
+
if (onHand !== void 0) input.onHand = onHand;
|
|
2736
|
+
if (available !== void 0) input.available = available;
|
|
2737
|
+
if (allocated !== void 0) input.allocated = allocated;
|
|
2738
|
+
if (damaged !== void 0) input.damaged = damaged;
|
|
2739
|
+
if (reorderPoint !== void 0) input.reorderPoint = reorderPoint;
|
|
2740
|
+
if (safetyStock !== void 0) input.safetyStock = safetyStock;
|
|
2741
|
+
if (unitCost !== void 0) input.unitCost = unitCost;
|
|
2742
|
+
if (Object.keys(input).length === 0) {
|
|
2743
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field.");
|
|
2744
|
+
}
|
|
2745
|
+
const data = await ctx.gql.workspace.request(UPDATE_INVENTORY_ITEM, { id, input });
|
|
2746
|
+
return jsonResult(data.updateInventoryItem);
|
|
2747
|
+
} catch (error) {
|
|
2748
|
+
ctx.logger.error("movethewheels inventory_update_item failed", { error: String(error) });
|
|
2749
|
+
return errorResult(
|
|
2750
|
+
`Failed to update inventory item: ${error instanceof Error ? error.message : String(error)}`
|
|
2751
|
+
);
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
);
|
|
2755
|
+
server.registerTool(
|
|
2756
|
+
track(mcpToolName(ctx.productName, "inventory_adjust_stock")),
|
|
2757
|
+
{
|
|
2758
|
+
title: "Adjust Inventory Stock",
|
|
2759
|
+
description: "Directly set one or more stock counts (onHand/available/allocated/damaged) for an inventory item. Provide the item id and at least one count.",
|
|
2760
|
+
inputSchema: {
|
|
2761
|
+
id: z.string().describe("Inventory item ID"),
|
|
2762
|
+
onHand: z.number().int().optional().describe("New on-hand quantity"),
|
|
2763
|
+
available: z.number().int().optional().describe("New available quantity"),
|
|
2764
|
+
allocated: z.number().int().optional().describe("New allocated quantity"),
|
|
2765
|
+
damaged: z.number().int().optional().describe("New damaged quantity")
|
|
2766
|
+
}
|
|
2767
|
+
},
|
|
2768
|
+
async ({ id, onHand, available, allocated, damaged }) => {
|
|
2769
|
+
try {
|
|
2770
|
+
const input = { id };
|
|
2771
|
+
if (onHand !== void 0) input.onHand = onHand;
|
|
2772
|
+
if (available !== void 0) input.available = available;
|
|
2773
|
+
if (allocated !== void 0) input.allocated = allocated;
|
|
2774
|
+
if (damaged !== void 0) input.damaged = damaged;
|
|
2775
|
+
if (Object.keys(input).length === 1) {
|
|
2776
|
+
return errorResult(
|
|
2777
|
+
"No stock counts specified \u2014 provide at least one of onHand/available/allocated/damaged."
|
|
2778
|
+
);
|
|
2779
|
+
}
|
|
2780
|
+
const data = await ctx.gql.workspace.request(ADJUST_INVENTORY_STOCK, { input });
|
|
2781
|
+
return jsonResult(data.adjustInventoryStock);
|
|
2782
|
+
} catch (error) {
|
|
2783
|
+
ctx.logger.error("movethewheels inventory_adjust_stock failed", { error: String(error) });
|
|
2784
|
+
return errorResult(
|
|
2785
|
+
`Failed to adjust inventory stock: ${error instanceof Error ? error.message : String(error)}`
|
|
2786
|
+
);
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
);
|
|
2790
|
+
server.registerTool(
|
|
2791
|
+
track(mcpToolName(ctx.productName, "inventory_create_transaction")),
|
|
2792
|
+
{
|
|
2793
|
+
title: "Create Inventory Transaction",
|
|
2794
|
+
description: "Record an inventory ledger transaction (RECEIPT/ISSUE/TRANSFER/ADJUSTMENT/RETURN). `transactionNumber`, `type`, `productId`, `warehouseId`, and `quantity` are required.",
|
|
2795
|
+
inputSchema: {
|
|
2796
|
+
transactionNumber: z.string().min(1).describe("Transaction number (unique)"),
|
|
2797
|
+
type: InventoryTransactionTypeEnum.describe("Transaction type"),
|
|
2798
|
+
productId: z.string().min(1).describe("Product ID"),
|
|
2799
|
+
warehouseId: z.string().min(1).describe("Warehouse ID"),
|
|
2800
|
+
quantity: z.number().int().describe("Quantity moved (sign per transaction semantics)"),
|
|
2801
|
+
inventoryItemId: z.string().optional().describe("Affected inventory item ID"),
|
|
2802
|
+
unitCost: z.number().optional().describe("Unit cost"),
|
|
2803
|
+
referenceType: z.string().optional().describe('Reference type, e.g. "order", "shipment"'),
|
|
2804
|
+
referenceId: z.string().optional().describe("Reference ID"),
|
|
2805
|
+
notes: z.string().optional().describe("Notes")
|
|
2806
|
+
}
|
|
2807
|
+
},
|
|
2808
|
+
async ({
|
|
2809
|
+
transactionNumber,
|
|
2810
|
+
type,
|
|
2811
|
+
productId,
|
|
2812
|
+
warehouseId,
|
|
2813
|
+
quantity,
|
|
2814
|
+
inventoryItemId,
|
|
2815
|
+
unitCost,
|
|
2816
|
+
referenceType,
|
|
2817
|
+
referenceId,
|
|
2818
|
+
notes
|
|
2819
|
+
}) => {
|
|
2820
|
+
try {
|
|
2821
|
+
const input = {
|
|
2822
|
+
transactionNumber,
|
|
2823
|
+
type,
|
|
2824
|
+
productId,
|
|
2825
|
+
warehouseId,
|
|
2826
|
+
quantity
|
|
2827
|
+
};
|
|
2828
|
+
if (inventoryItemId !== void 0) input.inventoryItemId = inventoryItemId;
|
|
2829
|
+
if (unitCost !== void 0) input.unitCost = unitCost;
|
|
2830
|
+
if (referenceType !== void 0) input.referenceType = referenceType;
|
|
2831
|
+
if (referenceId !== void 0) input.referenceId = referenceId;
|
|
2832
|
+
if (notes !== void 0) input.notes = notes;
|
|
2833
|
+
const data = await ctx.gql.workspace.request(CREATE_INVENTORY_TRANSACTION, { input });
|
|
2834
|
+
return jsonResult(data.createInventoryTransaction);
|
|
2835
|
+
} catch (error) {
|
|
2836
|
+
ctx.logger.error("movethewheels inventory_create_transaction failed", {
|
|
2837
|
+
error: String(error)
|
|
2838
|
+
});
|
|
2839
|
+
return errorResult(
|
|
2840
|
+
`Failed to create inventory transaction: ${error instanceof Error ? error.message : String(error)}`
|
|
2841
|
+
);
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
);
|
|
2845
|
+
server.registerTool(
|
|
2846
|
+
track(mcpToolName(ctx.productName, "inventory_delete_item")),
|
|
2847
|
+
{
|
|
2848
|
+
title: "Delete Inventory Item",
|
|
2849
|
+
description: "Soft-delete an inventory item by ID.",
|
|
2850
|
+
inputSchema: { id: z.string().describe("Inventory item ID") }
|
|
2851
|
+
},
|
|
2852
|
+
async ({ id }) => {
|
|
2853
|
+
try {
|
|
2854
|
+
const data = await ctx.gql.workspace.request(
|
|
2855
|
+
DELETE_INVENTORY_ITEM,
|
|
2856
|
+
{ id }
|
|
2857
|
+
);
|
|
2858
|
+
return jsonResult({ deleted: data.deleteInventoryItem, id });
|
|
2859
|
+
} catch (error) {
|
|
2860
|
+
ctx.logger.error("movethewheels inventory_delete_item failed", { error: String(error) });
|
|
2861
|
+
return errorResult(
|
|
2862
|
+
`Failed to delete inventory item: ${error instanceof Error ? error.message : String(error)}`
|
|
2863
|
+
);
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
);
|
|
2867
|
+
}
|
|
2868
|
+
function registerMarketplaceTools(server, ctx, track) {
|
|
2869
|
+
server.registerTool(
|
|
2870
|
+
track(mcpToolName(ctx.productName, "marketplace_get_provider")),
|
|
2871
|
+
{
|
|
2872
|
+
title: "Get Service Provider",
|
|
2873
|
+
description: "Fetch a single logistics service provider by ID, including its offerings.",
|
|
2874
|
+
inputSchema: { id: z.string().describe("Service provider ID") }
|
|
2875
|
+
},
|
|
2876
|
+
async ({ id }) => {
|
|
2877
|
+
try {
|
|
2878
|
+
const data = await ctx.gql.workspace.request(GET_SERVICE_PROVIDER, { id });
|
|
2879
|
+
if (!data.serviceProvider) return errorResult(`Service provider ${id} not found`);
|
|
2880
|
+
return jsonResult(data.serviceProvider);
|
|
2881
|
+
} catch (error) {
|
|
2882
|
+
ctx.logger.error("movethewheels marketplace_get_provider failed", { error: String(error) });
|
|
2883
|
+
return errorResult(
|
|
2884
|
+
`Failed to get service provider: ${error instanceof Error ? error.message : String(error)}`
|
|
2885
|
+
);
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
);
|
|
2889
|
+
server.registerTool(
|
|
2890
|
+
track(mcpToolName(ctx.productName, "marketplace_list_providers")),
|
|
2891
|
+
{
|
|
2892
|
+
title: "List Service Providers",
|
|
2893
|
+
description: "List logistics service providers with optional type/active/text filters and pagination.",
|
|
2894
|
+
inputSchema: {
|
|
2895
|
+
search: z.string().optional().describe("Free-text search across name/description"),
|
|
2896
|
+
type: z.string().optional().describe("Filter by provider type"),
|
|
2897
|
+
isActive: z.boolean().optional().describe("Filter by active flag"),
|
|
2898
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
2899
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
2900
|
+
}
|
|
2901
|
+
},
|
|
2902
|
+
async ({ search, type, isActive, limit, offset }) => {
|
|
2903
|
+
try {
|
|
2904
|
+
const filter = {};
|
|
2905
|
+
if (search !== void 0) filter.search = search;
|
|
2906
|
+
if (type !== void 0) filter.type = type;
|
|
2907
|
+
if (isActive !== void 0) filter.isActive = isActive;
|
|
2908
|
+
const data = await ctx.gql.workspace.request(LIST_SERVICE_PROVIDERS, {
|
|
2909
|
+
filter: Object.keys(filter).length > 0 ? filter : void 0,
|
|
2910
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
2911
|
+
});
|
|
2912
|
+
return jsonResult(data.serviceProviders);
|
|
2913
|
+
} catch (error) {
|
|
2914
|
+
ctx.logger.error("movethewheels marketplace_list_providers failed", {
|
|
2915
|
+
error: String(error)
|
|
2916
|
+
});
|
|
2917
|
+
return errorResult(
|
|
2918
|
+
`Failed to list service providers: ${error instanceof Error ? error.message : String(error)}`
|
|
2919
|
+
);
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
);
|
|
2923
|
+
server.registerTool(
|
|
2924
|
+
track(mcpToolName(ctx.productName, "marketplace_create_provider")),
|
|
2925
|
+
{
|
|
2926
|
+
title: "Create Service Provider",
|
|
2927
|
+
description: "Onboard a logistics service provider. `name` and `type` are required; `isActive` defaults to true, `isVerified` to false.",
|
|
2928
|
+
inputSchema: {
|
|
2929
|
+
name: z.string().min(1).describe("Provider name"),
|
|
2930
|
+
type: z.string().min(1).describe('Provider type, e.g. "carrier", "broker"'),
|
|
2931
|
+
description: z.string().optional().describe("Description"),
|
|
2932
|
+
isActive: z.boolean().optional().describe("Active flag (default true)"),
|
|
2933
|
+
isVerified: z.boolean().optional().describe("Verified flag (default false)"),
|
|
2934
|
+
rating: z.number().optional().describe("Rating"),
|
|
2935
|
+
services: JsonObject.optional().describe("Services JSON"),
|
|
2936
|
+
contactInfo: JsonObject.optional().describe("Contact info JSON")
|
|
2937
|
+
}
|
|
2938
|
+
},
|
|
2939
|
+
async ({ name, type, description, isActive, isVerified, rating, services, contactInfo }) => {
|
|
2940
|
+
try {
|
|
2941
|
+
const input = { name, type };
|
|
2942
|
+
if (description !== void 0) input.description = description;
|
|
2943
|
+
if (isActive !== void 0) input.isActive = isActive;
|
|
2944
|
+
if (isVerified !== void 0) input.isVerified = isVerified;
|
|
2945
|
+
if (rating !== void 0) input.rating = rating;
|
|
2946
|
+
if (services !== void 0) input.services = services;
|
|
2947
|
+
if (contactInfo !== void 0) input.contactInfo = contactInfo;
|
|
2948
|
+
const data = await ctx.gql.workspace.request(CREATE_SERVICE_PROVIDER, { input });
|
|
2949
|
+
return jsonResult(data.createServiceProvider);
|
|
2950
|
+
} catch (error) {
|
|
2951
|
+
ctx.logger.error("movethewheels marketplace_create_provider failed", {
|
|
2952
|
+
error: String(error)
|
|
2953
|
+
});
|
|
2954
|
+
return errorResult(
|
|
2955
|
+
`Failed to create service provider: ${error instanceof Error ? error.message : String(error)}`
|
|
2956
|
+
);
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
);
|
|
2960
|
+
server.registerTool(
|
|
2961
|
+
track(mcpToolName(ctx.productName, "marketplace_update_provider")),
|
|
2962
|
+
{
|
|
2963
|
+
title: "Update Service Provider",
|
|
2964
|
+
description: "Update a service provider. At least one mutable field must be provided.",
|
|
2965
|
+
inputSchema: {
|
|
2966
|
+
id: z.string().describe("Service provider ID"),
|
|
2967
|
+
name: z.string().optional().describe("Updated name"),
|
|
2968
|
+
type: z.string().optional().describe("Updated type"),
|
|
2969
|
+
description: z.string().optional().describe("Updated description"),
|
|
2970
|
+
isActive: z.boolean().optional().describe("Updated active flag"),
|
|
2971
|
+
isVerified: z.boolean().optional().describe("Updated verified flag"),
|
|
2972
|
+
rating: z.number().optional().describe("Updated rating"),
|
|
2973
|
+
services: JsonObject.optional().describe("Updated services JSON"),
|
|
2974
|
+
contactInfo: JsonObject.optional().describe("Updated contact info JSON")
|
|
2975
|
+
}
|
|
2976
|
+
},
|
|
2977
|
+
async ({
|
|
2978
|
+
id,
|
|
2979
|
+
name,
|
|
2980
|
+
type,
|
|
2981
|
+
description,
|
|
2982
|
+
isActive,
|
|
2983
|
+
isVerified,
|
|
2984
|
+
rating,
|
|
2985
|
+
services,
|
|
2986
|
+
contactInfo
|
|
2987
|
+
}) => {
|
|
2988
|
+
try {
|
|
2989
|
+
const input = {};
|
|
2990
|
+
if (name !== void 0) input.name = name;
|
|
2991
|
+
if (type !== void 0) input.type = type;
|
|
2992
|
+
if (description !== void 0) input.description = description;
|
|
2993
|
+
if (isActive !== void 0) input.isActive = isActive;
|
|
2994
|
+
if (isVerified !== void 0) input.isVerified = isVerified;
|
|
2995
|
+
if (rating !== void 0) input.rating = rating;
|
|
2996
|
+
if (services !== void 0) input.services = services;
|
|
2997
|
+
if (contactInfo !== void 0) input.contactInfo = contactInfo;
|
|
2998
|
+
if (Object.keys(input).length === 0) {
|
|
2999
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field.");
|
|
3000
|
+
}
|
|
3001
|
+
const data = await ctx.gql.workspace.request(UPDATE_SERVICE_PROVIDER, { id, input });
|
|
3002
|
+
return jsonResult(data.updateServiceProvider);
|
|
3003
|
+
} catch (error) {
|
|
3004
|
+
ctx.logger.error("movethewheels marketplace_update_provider failed", {
|
|
3005
|
+
error: String(error)
|
|
3006
|
+
});
|
|
3007
|
+
return errorResult(
|
|
3008
|
+
`Failed to update service provider: ${error instanceof Error ? error.message : String(error)}`
|
|
3009
|
+
);
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
);
|
|
3013
|
+
server.registerTool(
|
|
3014
|
+
track(mcpToolName(ctx.productName, "marketplace_delete_provider")),
|
|
3015
|
+
{
|
|
3016
|
+
title: "Delete Service Provider",
|
|
3017
|
+
description: "Soft-delete a service provider by ID.",
|
|
3018
|
+
inputSchema: { id: z.string().describe("Service provider ID") }
|
|
3019
|
+
},
|
|
3020
|
+
async ({ id }) => {
|
|
3021
|
+
try {
|
|
3022
|
+
const data = await ctx.gql.workspace.request(
|
|
3023
|
+
DELETE_SERVICE_PROVIDER,
|
|
3024
|
+
{ id }
|
|
3025
|
+
);
|
|
3026
|
+
return jsonResult({ deleted: data.deleteServiceProvider, id });
|
|
3027
|
+
} catch (error) {
|
|
3028
|
+
ctx.logger.error("movethewheels marketplace_delete_provider failed", {
|
|
3029
|
+
error: String(error)
|
|
3030
|
+
});
|
|
3031
|
+
return errorResult(
|
|
3032
|
+
`Failed to delete service provider: ${error instanceof Error ? error.message : String(error)}`
|
|
3033
|
+
);
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
);
|
|
3037
|
+
server.registerTool(
|
|
3038
|
+
track(mcpToolName(ctx.productName, "marketplace_get_offering")),
|
|
3039
|
+
{
|
|
3040
|
+
title: "Get Service Offering",
|
|
3041
|
+
description: "Fetch a single service offering by ID, including its provider.",
|
|
3042
|
+
inputSchema: { id: z.string().describe("Service offering ID") }
|
|
3043
|
+
},
|
|
3044
|
+
async ({ id }) => {
|
|
3045
|
+
try {
|
|
3046
|
+
const data = await ctx.gql.workspace.request(GET_SERVICE_OFFERING, { id });
|
|
3047
|
+
if (!data.serviceOffering) return errorResult(`Service offering ${id} not found`);
|
|
3048
|
+
return jsonResult(data.serviceOffering);
|
|
3049
|
+
} catch (error) {
|
|
3050
|
+
ctx.logger.error("movethewheels marketplace_get_offering failed", { error: String(error) });
|
|
3051
|
+
return errorResult(
|
|
3052
|
+
`Failed to get service offering: ${error instanceof Error ? error.message : String(error)}`
|
|
3053
|
+
);
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
);
|
|
3057
|
+
server.registerTool(
|
|
3058
|
+
track(mcpToolName(ctx.productName, "marketplace_list_offerings")),
|
|
3059
|
+
{
|
|
3060
|
+
title: "List Service Offerings",
|
|
3061
|
+
description: "List service offerings with optional provider/category/text filters and pagination.",
|
|
3062
|
+
inputSchema: {
|
|
3063
|
+
search: z.string().optional().describe("Free-text search across name/description"),
|
|
3064
|
+
providerId: z.string().optional().describe("Filter by provider ID"),
|
|
3065
|
+
category: z.string().optional().describe("Filter by category"),
|
|
3066
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
3067
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
3068
|
+
}
|
|
3069
|
+
},
|
|
3070
|
+
async ({ search, providerId, category, limit, offset }) => {
|
|
3071
|
+
try {
|
|
3072
|
+
const filter = {};
|
|
3073
|
+
if (search !== void 0) filter.search = search;
|
|
3074
|
+
if (providerId !== void 0) filter.providerId = providerId;
|
|
3075
|
+
if (category !== void 0) filter.category = category;
|
|
3076
|
+
const data = await ctx.gql.workspace.request(LIST_SERVICE_OFFERINGS, {
|
|
3077
|
+
filter: Object.keys(filter).length > 0 ? filter : void 0,
|
|
3078
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
3079
|
+
});
|
|
3080
|
+
return jsonResult(data.serviceOfferings);
|
|
3081
|
+
} catch (error) {
|
|
3082
|
+
ctx.logger.error("movethewheels marketplace_list_offerings failed", {
|
|
3083
|
+
error: String(error)
|
|
3084
|
+
});
|
|
3085
|
+
return errorResult(
|
|
3086
|
+
`Failed to list service offerings: ${error instanceof Error ? error.message : String(error)}`
|
|
3087
|
+
);
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
);
|
|
3091
|
+
server.registerTool(
|
|
3092
|
+
track(mcpToolName(ctx.productName, "marketplace_create_offering")),
|
|
3093
|
+
{
|
|
3094
|
+
title: "Create Service Offering",
|
|
3095
|
+
description: 'Create a service offering for a provider. `name`, `price`, and `providerId` are required; `currency` defaults to "USD".',
|
|
3096
|
+
inputSchema: {
|
|
3097
|
+
name: z.string().min(1).describe("Offering name"),
|
|
3098
|
+
price: z.number().describe("Price"),
|
|
3099
|
+
providerId: z.string().min(1).describe("Owning provider ID"),
|
|
3100
|
+
category: z.string().optional().describe("Category"),
|
|
3101
|
+
description: z.string().optional().describe("Description"),
|
|
3102
|
+
currency: z.string().optional().describe('Currency (default "USD")'),
|
|
3103
|
+
availability: JsonObject.optional().describe("Availability JSON")
|
|
3104
|
+
}
|
|
3105
|
+
},
|
|
3106
|
+
async ({ name, price, providerId, category, description, currency, availability }) => {
|
|
3107
|
+
try {
|
|
3108
|
+
const input = { name, price, providerId };
|
|
3109
|
+
if (category !== void 0) input.category = category;
|
|
3110
|
+
if (description !== void 0) input.description = description;
|
|
3111
|
+
if (currency !== void 0) input.currency = currency;
|
|
3112
|
+
if (availability !== void 0) input.availability = availability;
|
|
3113
|
+
const data = await ctx.gql.workspace.request(CREATE_SERVICE_OFFERING, { input });
|
|
3114
|
+
return jsonResult(data.createServiceOffering);
|
|
3115
|
+
} catch (error) {
|
|
3116
|
+
ctx.logger.error("movethewheels marketplace_create_offering failed", {
|
|
3117
|
+
error: String(error)
|
|
3118
|
+
});
|
|
3119
|
+
return errorResult(
|
|
3120
|
+
`Failed to create service offering: ${error instanceof Error ? error.message : String(error)}`
|
|
3121
|
+
);
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
);
|
|
3125
|
+
server.registerTool(
|
|
3126
|
+
track(mcpToolName(ctx.productName, "marketplace_update_offering")),
|
|
3127
|
+
{
|
|
3128
|
+
title: "Update Service Offering",
|
|
3129
|
+
description: "Update a service offering. At least one mutable field must be provided.",
|
|
3130
|
+
inputSchema: {
|
|
3131
|
+
id: z.string().describe("Service offering ID"),
|
|
3132
|
+
name: z.string().optional().describe("Updated name"),
|
|
3133
|
+
price: z.number().optional().describe("Updated price"),
|
|
3134
|
+
providerId: z.string().optional().describe("Updated provider ID"),
|
|
3135
|
+
category: z.string().optional().describe("Updated category"),
|
|
3136
|
+
description: z.string().optional().describe("Updated description"),
|
|
3137
|
+
currency: z.string().optional().describe("Updated currency"),
|
|
3138
|
+
availability: JsonObject.optional().describe("Updated availability JSON")
|
|
3139
|
+
}
|
|
3140
|
+
},
|
|
3141
|
+
async ({ id, name, price, providerId, category, description, currency, availability }) => {
|
|
3142
|
+
try {
|
|
3143
|
+
const input = {};
|
|
3144
|
+
if (name !== void 0) input.name = name;
|
|
3145
|
+
if (price !== void 0) input.price = price;
|
|
3146
|
+
if (providerId !== void 0) input.providerId = providerId;
|
|
3147
|
+
if (category !== void 0) input.category = category;
|
|
3148
|
+
if (description !== void 0) input.description = description;
|
|
3149
|
+
if (currency !== void 0) input.currency = currency;
|
|
3150
|
+
if (availability !== void 0) input.availability = availability;
|
|
3151
|
+
if (Object.keys(input).length === 0) {
|
|
3152
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field.");
|
|
3153
|
+
}
|
|
3154
|
+
const data = await ctx.gql.workspace.request(UPDATE_SERVICE_OFFERING, { id, input });
|
|
3155
|
+
return jsonResult(data.updateServiceOffering);
|
|
3156
|
+
} catch (error) {
|
|
3157
|
+
ctx.logger.error("movethewheels marketplace_update_offering failed", {
|
|
3158
|
+
error: String(error)
|
|
3159
|
+
});
|
|
3160
|
+
return errorResult(
|
|
3161
|
+
`Failed to update service offering: ${error instanceof Error ? error.message : String(error)}`
|
|
3162
|
+
);
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
);
|
|
3166
|
+
server.registerTool(
|
|
3167
|
+
track(mcpToolName(ctx.productName, "marketplace_delete_offering")),
|
|
3168
|
+
{
|
|
3169
|
+
title: "Delete Service Offering",
|
|
3170
|
+
description: "Soft-delete a service offering by ID.",
|
|
3171
|
+
inputSchema: { id: z.string().describe("Service offering ID") }
|
|
3172
|
+
},
|
|
3173
|
+
async ({ id }) => {
|
|
3174
|
+
try {
|
|
3175
|
+
const data = await ctx.gql.workspace.request(
|
|
3176
|
+
DELETE_SERVICE_OFFERING,
|
|
3177
|
+
{ id }
|
|
3178
|
+
);
|
|
3179
|
+
return jsonResult({ deleted: data.deleteServiceOffering, id });
|
|
3180
|
+
} catch (error) {
|
|
3181
|
+
ctx.logger.error("movethewheels marketplace_delete_offering failed", {
|
|
3182
|
+
error: String(error)
|
|
3183
|
+
});
|
|
3184
|
+
return errorResult(
|
|
3185
|
+
`Failed to delete service offering: ${error instanceof Error ? error.message : String(error)}`
|
|
3186
|
+
);
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
);
|
|
3190
|
+
}
|
|
3191
|
+
function registerReportTools(server, ctx, track) {
|
|
3192
|
+
server.registerTool(
|
|
3193
|
+
track(mcpToolName(ctx.productName, "reports_get")),
|
|
3194
|
+
{
|
|
3195
|
+
title: "Get Report",
|
|
3196
|
+
description: "Fetch a single report definition by ID.",
|
|
3197
|
+
inputSchema: { id: z.string().describe("Report ID") }
|
|
3198
|
+
},
|
|
3199
|
+
async ({ id }) => {
|
|
3200
|
+
try {
|
|
3201
|
+
const data = await ctx.gql.workspace.request(GET_REPORT, { id });
|
|
3202
|
+
if (!data.movethewheelsReport) return errorResult(`Report ${id} not found`);
|
|
3203
|
+
return jsonResult(data.movethewheelsReport);
|
|
3204
|
+
} catch (error) {
|
|
3205
|
+
ctx.logger.error("movethewheels reports_get failed", { error: String(error) });
|
|
3206
|
+
return errorResult(
|
|
3207
|
+
`Failed to get report: ${error instanceof Error ? error.message : String(error)}`
|
|
3208
|
+
);
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3211
|
+
);
|
|
3212
|
+
server.registerTool(
|
|
3213
|
+
track(mcpToolName(ctx.productName, "reports_list")),
|
|
3214
|
+
{
|
|
3215
|
+
title: "List Reports",
|
|
3216
|
+
description: "List report definitions with optional type/status/text filters and pagination.",
|
|
3217
|
+
inputSchema: {
|
|
3218
|
+
search: z.string().optional().describe("Free-text search across name"),
|
|
3219
|
+
type: z.string().optional().describe("Filter by report type"),
|
|
3220
|
+
status: z.string().optional().describe('Filter by status, e.g. "DRAFT", "ACTIVE"'),
|
|
3221
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
3222
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
3223
|
+
}
|
|
3224
|
+
},
|
|
3225
|
+
async ({ search, type, status, limit, offset }) => {
|
|
3226
|
+
try {
|
|
3227
|
+
const filter = {};
|
|
3228
|
+
if (search !== void 0) filter.search = search;
|
|
3229
|
+
if (type !== void 0) filter.type = type;
|
|
3230
|
+
if (status !== void 0) filter.status = status;
|
|
3231
|
+
const data = await ctx.gql.workspace.request(LIST_REPORTS, {
|
|
3232
|
+
filter: Object.keys(filter).length > 0 ? filter : void 0,
|
|
3233
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 }
|
|
3234
|
+
});
|
|
3235
|
+
return jsonResult(data.movethewheelsReports);
|
|
3236
|
+
} catch (error) {
|
|
3237
|
+
ctx.logger.error("movethewheels reports_list failed", { error: String(error) });
|
|
3238
|
+
return errorResult(
|
|
3239
|
+
`Failed to list reports: ${error instanceof Error ? error.message : String(error)}`
|
|
3240
|
+
);
|
|
3241
|
+
}
|
|
3242
|
+
}
|
|
3243
|
+
);
|
|
3244
|
+
server.registerTool(
|
|
3245
|
+
track(mcpToolName(ctx.productName, "reports_create")),
|
|
3246
|
+
{
|
|
3247
|
+
title: "Create Report",
|
|
3248
|
+
description: 'Create a report definition. `name` and `type` are required; `status` defaults to "DRAFT".',
|
|
3249
|
+
inputSchema: {
|
|
3250
|
+
name: z.string().min(1).describe("Report name"),
|
|
3251
|
+
type: z.string().min(1).describe("Report type"),
|
|
3252
|
+
status: z.string().optional().describe('Status (default "DRAFT")'),
|
|
3253
|
+
description: z.string().optional().describe("Description"),
|
|
3254
|
+
configuration: JsonObject.optional().describe("Report configuration JSON"),
|
|
3255
|
+
schedule: z.string().optional().describe("Schedule (e.g. cron string)")
|
|
3256
|
+
}
|
|
3257
|
+
},
|
|
3258
|
+
async ({ name, type, status, description, configuration, schedule }) => {
|
|
3259
|
+
try {
|
|
3260
|
+
const input = { name, type };
|
|
3261
|
+
if (status !== void 0) input.status = status;
|
|
3262
|
+
if (description !== void 0) input.description = description;
|
|
3263
|
+
if (configuration !== void 0) input.configuration = configuration;
|
|
3264
|
+
if (schedule !== void 0) input.schedule = schedule;
|
|
3265
|
+
const data = await ctx.gql.workspace.request(CREATE_REPORT, { input });
|
|
3266
|
+
return jsonResult(data.createMovethewheelsReport);
|
|
3267
|
+
} catch (error) {
|
|
3268
|
+
ctx.logger.error("movethewheels reports_create failed", { error: String(error) });
|
|
3269
|
+
return errorResult(
|
|
3270
|
+
`Failed to create report: ${error instanceof Error ? error.message : String(error)}`
|
|
3271
|
+
);
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
);
|
|
3275
|
+
server.registerTool(
|
|
3276
|
+
track(mcpToolName(ctx.productName, "reports_update")),
|
|
3277
|
+
{
|
|
3278
|
+
title: "Update Report",
|
|
3279
|
+
description: "Update a report definition. At least one mutable field must be provided.",
|
|
3280
|
+
inputSchema: {
|
|
3281
|
+
id: z.string().describe("Report ID"),
|
|
3282
|
+
name: z.string().optional().describe("Updated name"),
|
|
3283
|
+
type: z.string().optional().describe("Updated type"),
|
|
3284
|
+
status: z.string().optional().describe("Updated status"),
|
|
3285
|
+
description: z.string().optional().describe("Updated description"),
|
|
3286
|
+
configuration: JsonObject.optional().describe("Updated configuration JSON"),
|
|
3287
|
+
schedule: z.string().optional().describe("Updated schedule")
|
|
3288
|
+
}
|
|
3289
|
+
},
|
|
3290
|
+
async ({ id, name, type, status, description, configuration, schedule }) => {
|
|
3291
|
+
try {
|
|
3292
|
+
const input = {};
|
|
3293
|
+
if (name !== void 0) input.name = name;
|
|
3294
|
+
if (type !== void 0) input.type = type;
|
|
3295
|
+
if (status !== void 0) input.status = status;
|
|
3296
|
+
if (description !== void 0) input.description = description;
|
|
3297
|
+
if (configuration !== void 0) input.configuration = configuration;
|
|
3298
|
+
if (schedule !== void 0) input.schedule = schedule;
|
|
3299
|
+
if (Object.keys(input).length === 0) {
|
|
3300
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field.");
|
|
3301
|
+
}
|
|
3302
|
+
const data = await ctx.gql.workspace.request(UPDATE_REPORT, { id, input });
|
|
3303
|
+
return jsonResult(data.updateMovethewheelsReport);
|
|
3304
|
+
} catch (error) {
|
|
3305
|
+
ctx.logger.error("movethewheels reports_update failed", { error: String(error) });
|
|
3306
|
+
return errorResult(
|
|
3307
|
+
`Failed to update report: ${error instanceof Error ? error.message : String(error)}`
|
|
3308
|
+
);
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
);
|
|
3312
|
+
server.registerTool(
|
|
3313
|
+
track(mcpToolName(ctx.productName, "reports_generate")),
|
|
3314
|
+
{
|
|
3315
|
+
title: "Generate Report",
|
|
3316
|
+
description: "Run a report definition and return the generated result payload.",
|
|
3317
|
+
inputSchema: {
|
|
3318
|
+
reportId: z.string().describe("Report ID to generate"),
|
|
3319
|
+
parameters: JsonObject.optional().describe("Runtime parameters JSON")
|
|
3320
|
+
}
|
|
3321
|
+
},
|
|
3322
|
+
async ({ reportId, parameters }) => {
|
|
3323
|
+
try {
|
|
3324
|
+
const input = { reportId };
|
|
3325
|
+
if (parameters !== void 0) input.parameters = parameters;
|
|
3326
|
+
const data = await ctx.gql.workspace.request(GENERATE_REPORT, { input });
|
|
3327
|
+
return jsonResult(data.generateMovethewheelsReport);
|
|
3328
|
+
} catch (error) {
|
|
3329
|
+
ctx.logger.error("movethewheels reports_generate failed", { error: String(error) });
|
|
3330
|
+
return errorResult(
|
|
3331
|
+
`Failed to generate report: ${error instanceof Error ? error.message : String(error)}`
|
|
3332
|
+
);
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
);
|
|
3336
|
+
server.registerTool(
|
|
3337
|
+
track(mcpToolName(ctx.productName, "reports_delete")),
|
|
3338
|
+
{
|
|
3339
|
+
title: "Delete Report",
|
|
3340
|
+
description: "Soft-delete a report definition by ID.",
|
|
3341
|
+
inputSchema: { id: z.string().describe("Report ID") }
|
|
3342
|
+
},
|
|
3343
|
+
async ({ id }) => {
|
|
3344
|
+
try {
|
|
3345
|
+
const data = await ctx.gql.workspace.request(
|
|
3346
|
+
DELETE_REPORT,
|
|
3347
|
+
{ id }
|
|
3348
|
+
);
|
|
3349
|
+
return jsonResult({ deleted: data.deleteMovethewheelsReport, id });
|
|
3350
|
+
} catch (error) {
|
|
3351
|
+
ctx.logger.error("movethewheels reports_delete failed", { error: String(error) });
|
|
3352
|
+
return errorResult(
|
|
3353
|
+
`Failed to delete report: ${error instanceof Error ? error.message : String(error)}`
|
|
3354
|
+
);
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
);
|
|
3358
|
+
server.registerTool(
|
|
3359
|
+
track(mcpToolName(ctx.productName, "settings_get")),
|
|
3360
|
+
{
|
|
3361
|
+
title: "Get Module Settings",
|
|
3362
|
+
description: "Fetch the Movethewheels module settings (preferences) for the bound workspace.",
|
|
3363
|
+
inputSchema: {}
|
|
3364
|
+
},
|
|
3365
|
+
async () => {
|
|
3366
|
+
try {
|
|
3367
|
+
const data = await ctx.gql.workspace.request(GET_MODULE_SETTINGS, {});
|
|
3368
|
+
return jsonResult(data.moduleSettings);
|
|
3369
|
+
} catch (error) {
|
|
3370
|
+
ctx.logger.error("movethewheels settings_get failed", { error: String(error) });
|
|
3371
|
+
return errorResult(
|
|
3372
|
+
`Failed to get module settings: ${error instanceof Error ? error.message : String(error)}`
|
|
3373
|
+
);
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
);
|
|
3377
|
+
server.registerTool(
|
|
3378
|
+
track(mcpToolName(ctx.productName, "settings_update")),
|
|
3379
|
+
{
|
|
3380
|
+
title: "Update Module Settings",
|
|
3381
|
+
description: "Replace the Movethewheels module preferences JSON for the bound workspace.",
|
|
3382
|
+
inputSchema: {
|
|
3383
|
+
preferences: JsonObject.describe("Module preferences JSON (full replacement)")
|
|
3384
|
+
}
|
|
3385
|
+
},
|
|
3386
|
+
async ({ preferences }) => {
|
|
3387
|
+
try {
|
|
3388
|
+
const data = await ctx.gql.workspace.request(UPDATE_MODULE_SETTINGS, { input: { preferences } });
|
|
3389
|
+
return jsonResult(data.updateModuleSettings);
|
|
3390
|
+
} catch (error) {
|
|
3391
|
+
ctx.logger.error("movethewheels settings_update failed", { error: String(error) });
|
|
3392
|
+
return errorResult(
|
|
3393
|
+
`Failed to update module settings: ${error instanceof Error ? error.message : String(error)}`
|
|
3394
|
+
);
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
);
|
|
3398
|
+
server.registerTool(
|
|
3399
|
+
track(mcpToolName(ctx.productName, "data_export")),
|
|
3400
|
+
{
|
|
3401
|
+
title: "Export Data",
|
|
3402
|
+
description: 'Export a Movethewheels resource (e.g. "orders", "shipments", "inventory") as a payload. `format` defaults to "json".',
|
|
3403
|
+
inputSchema: {
|
|
3404
|
+
resource: z.string().min(1).describe('Resource to export, e.g. "orders"'),
|
|
3405
|
+
format: z.string().optional().describe('Export format (default "json")')
|
|
3406
|
+
}
|
|
3407
|
+
},
|
|
3408
|
+
async ({ resource, format }) => {
|
|
3409
|
+
try {
|
|
3410
|
+
const input = { resource };
|
|
3411
|
+
if (format !== void 0) input.format = format;
|
|
3412
|
+
const data = await ctx.gql.workspace.request(EXPORT_DATA, { input });
|
|
3413
|
+
return jsonResult(data.exportData);
|
|
3414
|
+
} catch (error) {
|
|
3415
|
+
ctx.logger.error("movethewheels data_export failed", { error: String(error) });
|
|
3416
|
+
return errorResult(
|
|
3417
|
+
`Failed to export data: ${error instanceof Error ? error.message : String(error)}`
|
|
3418
|
+
);
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
);
|
|
3422
|
+
server.registerTool(
|
|
3423
|
+
track(mcpToolName(ctx.productName, "data_import")),
|
|
3424
|
+
{
|
|
3425
|
+
title: "Import Data",
|
|
3426
|
+
description: 'Import a payload into a Movethewheels resource (e.g. "orders", "customers"). `resource` and `payload` are required.',
|
|
3427
|
+
inputSchema: {
|
|
3428
|
+
resource: z.string().min(1).describe('Resource to import into, e.g. "customers"'),
|
|
3429
|
+
payload: JsonObject.describe("Payload JSON to import")
|
|
3430
|
+
}
|
|
3431
|
+
},
|
|
3432
|
+
async ({ resource, payload }) => {
|
|
3433
|
+
try {
|
|
3434
|
+
const data = await ctx.gql.workspace.request(IMPORT_DATA, { input: { resource, payload } });
|
|
3435
|
+
return jsonResult(data.importData);
|
|
3436
|
+
} catch (error) {
|
|
3437
|
+
ctx.logger.error("movethewheels data_import failed", { error: String(error) });
|
|
3438
|
+
return errorResult(
|
|
3439
|
+
`Failed to import data: ${error instanceof Error ? error.message : String(error)}`
|
|
3440
|
+
);
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
);
|
|
3444
|
+
}
|
|
3445
|
+
function registerAnalyticsTools(server, ctx, track) {
|
|
3446
|
+
server.registerTool(
|
|
3447
|
+
track(mcpToolName(ctx.productName, "analytics_series")),
|
|
3448
|
+
{
|
|
3449
|
+
title: "Analytics Time Series",
|
|
3450
|
+
description: "Time-series trends for orders, deliveries, and revenue.",
|
|
3451
|
+
inputSchema: {}
|
|
3452
|
+
},
|
|
3453
|
+
async () => {
|
|
3454
|
+
try {
|
|
3455
|
+
const data = await ctx.gql.workspace.request(GET_ANALYTICS, {});
|
|
3456
|
+
return jsonResult(data.analytics);
|
|
3457
|
+
} catch (error) {
|
|
3458
|
+
ctx.logger.error("movethewheels analytics_series failed", { error: String(error) });
|
|
3459
|
+
return errorResult(
|
|
3460
|
+
`Failed to get analytics: ${error instanceof Error ? error.message : String(error)}`
|
|
3461
|
+
);
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
);
|
|
3465
|
+
server.registerTool(
|
|
3466
|
+
track(mcpToolName(ctx.productName, "analytics_dashboard")),
|
|
3467
|
+
{
|
|
3468
|
+
title: "Dashboard Metrics",
|
|
3469
|
+
description: "Headline operations KPIs: total orders, active deliveries, active vehicles, available drivers, low-stock items, total revenue, and recent orders.",
|
|
3470
|
+
inputSchema: {}
|
|
3471
|
+
},
|
|
3472
|
+
async () => {
|
|
3473
|
+
try {
|
|
3474
|
+
const data = await ctx.gql.workspace.request(GET_DASHBOARD_METRICS, {});
|
|
3475
|
+
return jsonResult(data.dashboardMetrics);
|
|
3476
|
+
} catch (error) {
|
|
3477
|
+
ctx.logger.error("movethewheels analytics_dashboard failed", { error: String(error) });
|
|
3478
|
+
return errorResult(
|
|
3479
|
+
`Failed to get dashboard metrics: ${error instanceof Error ? error.message : String(error)}`
|
|
3480
|
+
);
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
);
|
|
3484
|
+
server.registerTool(
|
|
3485
|
+
track(mcpToolName(ctx.productName, "analytics_order_stats")),
|
|
3486
|
+
{
|
|
3487
|
+
title: "Order Stats",
|
|
3488
|
+
description: "Order counts by lifecycle bucket: total, pending, in-transit, delivered, cancelled, failed.",
|
|
3489
|
+
inputSchema: {}
|
|
3490
|
+
},
|
|
3491
|
+
async () => {
|
|
3492
|
+
try {
|
|
3493
|
+
const data = await ctx.gql.workspace.request(GET_ORDER_STATS, {});
|
|
3494
|
+
return jsonResult(data.orderStats);
|
|
3495
|
+
} catch (error) {
|
|
3496
|
+
ctx.logger.error("movethewheels analytics_order_stats failed", { error: String(error) });
|
|
3497
|
+
return errorResult(
|
|
3498
|
+
`Failed to get order stats: ${error instanceof Error ? error.message : String(error)}`
|
|
3499
|
+
);
|
|
3500
|
+
}
|
|
3501
|
+
}
|
|
3502
|
+
);
|
|
3503
|
+
server.registerTool(
|
|
3504
|
+
track(mcpToolName(ctx.productName, "analytics_fleet")),
|
|
3505
|
+
{
|
|
3506
|
+
title: "Fleet Stats",
|
|
3507
|
+
description: "Fleet KPIs: total/active/in-transit/maintenance vehicles and total/available/on-delivery drivers.",
|
|
3508
|
+
inputSchema: {}
|
|
3509
|
+
},
|
|
3510
|
+
async () => {
|
|
3511
|
+
try {
|
|
3512
|
+
const data = await ctx.gql.workspace.request(GET_FLEET_STATS, {});
|
|
3513
|
+
return jsonResult(data.fleetStats);
|
|
3514
|
+
} catch (error) {
|
|
3515
|
+
ctx.logger.error("movethewheels analytics_fleet failed", { error: String(error) });
|
|
3516
|
+
return errorResult(
|
|
3517
|
+
`Failed to get fleet stats: ${error instanceof Error ? error.message : String(error)}`
|
|
3518
|
+
);
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
);
|
|
3522
|
+
server.registerTool(
|
|
3523
|
+
track(mcpToolName(ctx.productName, "analytics_finance")),
|
|
3524
|
+
{
|
|
3525
|
+
title: "Finance Metrics",
|
|
3526
|
+
description: "Finance KPIs: revenue breakdown, fuel cost, and open invoice count.",
|
|
3527
|
+
inputSchema: {}
|
|
3528
|
+
},
|
|
3529
|
+
async () => {
|
|
3530
|
+
try {
|
|
3531
|
+
const data = await ctx.gql.workspace.request(GET_FINANCE_METRICS, {});
|
|
3532
|
+
return jsonResult(data.financeMetrics);
|
|
3533
|
+
} catch (error) {
|
|
3534
|
+
ctx.logger.error("movethewheels analytics_finance failed", { error: String(error) });
|
|
3535
|
+
return errorResult(
|
|
3536
|
+
`Failed to get finance metrics: ${error instanceof Error ? error.message : String(error)}`
|
|
3537
|
+
);
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
);
|
|
3541
|
+
server.registerTool(
|
|
3542
|
+
track(mcpToolName(ctx.productName, "analytics_insights")),
|
|
3543
|
+
{
|
|
3544
|
+
title: "Assistant Insights",
|
|
3545
|
+
description: "Ask the Movethewheels operations assistant a natural-language question and get a generated insight answer.",
|
|
3546
|
+
inputSchema: {
|
|
3547
|
+
question: z.string().min(1).describe("Natural-language operations question")
|
|
3548
|
+
}
|
|
3549
|
+
},
|
|
3550
|
+
async ({ question }) => {
|
|
3551
|
+
try {
|
|
3552
|
+
const data = await ctx.gql.workspace.request(GET_ASSISTANT_INSIGHTS, { question });
|
|
3553
|
+
return jsonResult(data.assistantInsights);
|
|
3554
|
+
} catch (error) {
|
|
3555
|
+
ctx.logger.error("movethewheels analytics_insights failed", {
|
|
3556
|
+
error: String(error)
|
|
3557
|
+
});
|
|
3558
|
+
return errorResult(
|
|
3559
|
+
`Failed to get assistant insights: ${error instanceof Error ? error.message : String(error)}`
|
|
3560
|
+
);
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3563
|
+
);
|
|
3564
|
+
}
|
|
3565
|
+
|
|
3566
|
+
// src/modules/movethewheels/resources.ts
|
|
3567
|
+
function registerMovethewheelsResources(server, ctx, track) {
|
|
3568
|
+
const schemaUri = track(mcpResourceUri(ctx.productName, "schema"));
|
|
3569
|
+
const dashboardUri = track(mcpResourceUri(ctx.productName, "dashboard"));
|
|
3570
|
+
const fleetUri = track(mcpResourceUri(ctx.productName, "fleet"));
|
|
3571
|
+
const activeUri = track(mcpResourceUri(ctx.productName, "active-deliveries"));
|
|
3572
|
+
server.registerResource(
|
|
3573
|
+
"movethewheels-schema",
|
|
3574
|
+
schemaUri,
|
|
3575
|
+
{
|
|
3576
|
+
title: "Movethewheels Domain Schema",
|
|
3577
|
+
description: "Overview of the Movethewheels logistics domain \u2014 entities, enums, and the per-group tool surface.",
|
|
3578
|
+
mimeType: "text/plain"
|
|
3579
|
+
},
|
|
3580
|
+
async () => {
|
|
3581
|
+
ctx.logger.debug(`Resource ${schemaUri} requested`);
|
|
3582
|
+
const schema = `# Movethewheels Domain (Overview)
|
|
3583
|
+
|
|
3584
|
+
Logistics / fleet / warehouse / last-mile-delivery operations platform.
|
|
3585
|
+
|
|
3586
|
+
## Entities
|
|
3587
|
+
- Customer \u2014 shipper/consignee (B2B/B2C)
|
|
3588
|
+
- Driver \u2014 fleet driver (status, license, telemetry)
|
|
3589
|
+
- Vehicle \u2014 fleet vehicle (type, status, capacity, location)
|
|
3590
|
+
- Product \u2014 cargo SKU master (weight, dimensions, hazardous/fragile/temp)
|
|
3591
|
+
- MovethewheelsOrder (+OrderStatusHistory) \u2014 delivery order + status timeline
|
|
3592
|
+
- Shipment (+TrackingEvent) \u2014 freight shipment + tracking timeline
|
|
3593
|
+
- Route \u2014 planned/optimized delivery route
|
|
3594
|
+
- Warehouse (+WarehouseLocation) \u2014 facilities + storage locations
|
|
3595
|
+
- InventoryItem (+InventoryTransaction) \u2014 stock at a warehouse + ledger
|
|
3596
|
+
- ServiceProvider (+ServiceOffering) \u2014 3PL marketplace providers + offerings
|
|
3597
|
+
- MovethewheelsReport \u2014 report definitions
|
|
3598
|
+
|
|
3599
|
+
## Enums
|
|
3600
|
+
- DriverStatus: AVAILABLE, INACTIVE, OFF_DUTY, ON_BREAK, ON_DELIVERY
|
|
3601
|
+
- VehicleStatus: ACTIVE, DECOMMISSIONED, INACTIVE, IN_TRANSIT, MAINTENANCE
|
|
3602
|
+
- VehicleType: BICYCLE, DRONE, FLATBED, MOTORCYCLE, REFRIGERATED, SEMI_TRAILER, TRUCK, VAN
|
|
3603
|
+
- OrderType: B2B, B2C, MARKETPLACE, SUBSCRIPTION
|
|
3604
|
+
- OrderPriority: CRITICAL, HIGH, LOW, MEDIUM, URGENT
|
|
3605
|
+
- OrderStatus: PENDING, CONFIRMED, ASSIGNED, PICKED_UP, IN_TRANSIT, OUT_FOR_DELIVERY, DELIVERED, CANCELLED, FAILED, RETURNED
|
|
3606
|
+
- ShipmentStatus: PLANNED, PICKED_UP, IN_TRANSIT, DELIVERED, FAILED, RETURNED
|
|
3607
|
+
- TrackingEventType: CREATED, PICKED_UP, DEPARTED, ARRIVED, IN_TRANSIT, OUT_FOR_DELIVERY, DELIVERED, FAILED, RETURNED
|
|
3608
|
+
- RouteStatus: PLANNED, IN_PROGRESS, COMPLETED, CANCELLED
|
|
3609
|
+
- WarehouseType: COLD_STORAGE, CROSS_DOCK, DISTRIBUTION, FULFILLMENT, RETAIL
|
|
3610
|
+
- InventoryTransactionType: RECEIPT, ISSUE, TRANSFER, ADJUSTMENT, RETURN
|
|
3611
|
+
|
|
3612
|
+
## Tool groups
|
|
3613
|
+
customers, drivers, vehicles, products, orders, shipments, tracking, routes,
|
|
3614
|
+
warehouses, inventory, marketplace (service providers + offerings), reports
|
|
3615
|
+
(+ settings + import/export), analytics.
|
|
3616
|
+
All run against the WORKSPACE gateway (workspace-scoped). The subgraph has no
|
|
3617
|
+
@public health field, so there is no health tool.
|
|
3618
|
+
`;
|
|
3619
|
+
return {
|
|
3620
|
+
contents: [{ uri: schemaUri, text: schema, mimeType: "text/plain" }]
|
|
3621
|
+
};
|
|
3622
|
+
}
|
|
3623
|
+
);
|
|
3624
|
+
server.registerResource(
|
|
3625
|
+
"movethewheels-dashboard",
|
|
3626
|
+
dashboardUri,
|
|
3627
|
+
{
|
|
3628
|
+
title: "Movethewheels Dashboard Metrics",
|
|
3629
|
+
description: "Live headline operations KPIs (orders, active deliveries, vehicles, drivers, low-stock, revenue).",
|
|
3630
|
+
mimeType: "application/json"
|
|
3631
|
+
},
|
|
3632
|
+
async () => {
|
|
3633
|
+
ctx.logger.debug(`Resource ${dashboardUri} requested`);
|
|
3634
|
+
try {
|
|
3635
|
+
const data = await ctx.gql.workspace.request(
|
|
3636
|
+
GET_DASHBOARD_METRICS,
|
|
3637
|
+
{}
|
|
3638
|
+
);
|
|
3639
|
+
return {
|
|
3640
|
+
contents: [
|
|
3641
|
+
{
|
|
3642
|
+
uri: dashboardUri,
|
|
3643
|
+
text: JSON.stringify(data.dashboardMetrics, null, 2),
|
|
3644
|
+
mimeType: "application/json"
|
|
3645
|
+
}
|
|
3646
|
+
]
|
|
3647
|
+
};
|
|
3648
|
+
} catch (error) {
|
|
3649
|
+
return {
|
|
3650
|
+
contents: [
|
|
3651
|
+
{
|
|
3652
|
+
uri: dashboardUri,
|
|
3653
|
+
text: JSON.stringify({
|
|
3654
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3655
|
+
}),
|
|
3656
|
+
mimeType: "application/json"
|
|
3657
|
+
}
|
|
3658
|
+
]
|
|
3659
|
+
};
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
);
|
|
3663
|
+
server.registerResource(
|
|
3664
|
+
"movethewheels-fleet",
|
|
3665
|
+
fleetUri,
|
|
3666
|
+
{
|
|
3667
|
+
title: "Movethewheels Fleet Stats",
|
|
3668
|
+
description: "Fleet KPIs: vehicle and driver counts by status.",
|
|
3669
|
+
mimeType: "application/json"
|
|
3670
|
+
},
|
|
3671
|
+
async () => {
|
|
3672
|
+
ctx.logger.debug(`Resource ${fleetUri} requested`);
|
|
3673
|
+
try {
|
|
3674
|
+
const data = await ctx.gql.workspace.request(GET_FLEET_STATS, {});
|
|
3675
|
+
return {
|
|
3676
|
+
contents: [
|
|
3677
|
+
{
|
|
3678
|
+
uri: fleetUri,
|
|
3679
|
+
text: JSON.stringify(data.fleetStats, null, 2),
|
|
3680
|
+
mimeType: "application/json"
|
|
3681
|
+
}
|
|
3682
|
+
]
|
|
3683
|
+
};
|
|
3684
|
+
} catch (error) {
|
|
3685
|
+
return {
|
|
3686
|
+
contents: [
|
|
3687
|
+
{
|
|
3688
|
+
uri: fleetUri,
|
|
3689
|
+
text: JSON.stringify({
|
|
3690
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3691
|
+
}),
|
|
3692
|
+
mimeType: "application/json"
|
|
3693
|
+
}
|
|
3694
|
+
]
|
|
3695
|
+
};
|
|
3696
|
+
}
|
|
3697
|
+
}
|
|
3698
|
+
);
|
|
3699
|
+
server.registerResource(
|
|
3700
|
+
"movethewheels-active-deliveries",
|
|
3701
|
+
activeUri,
|
|
3702
|
+
{
|
|
3703
|
+
title: "Movethewheels Active Deliveries",
|
|
3704
|
+
description: "The first 50 shipments currently in active delivery in the bound workspace.",
|
|
3705
|
+
mimeType: "application/json"
|
|
3706
|
+
},
|
|
3707
|
+
async () => {
|
|
3708
|
+
ctx.logger.debug(`Resource ${activeUri} requested`);
|
|
3709
|
+
try {
|
|
3710
|
+
const data = await ctx.gql.workspace.request(
|
|
3711
|
+
LIST_ACTIVE_DELIVERIES,
|
|
3712
|
+
{ pagination: { limit: 50, offset: 0 } }
|
|
3713
|
+
);
|
|
3714
|
+
return {
|
|
3715
|
+
contents: [
|
|
3716
|
+
{
|
|
3717
|
+
uri: activeUri,
|
|
3718
|
+
text: JSON.stringify(data.activeDeliveries, null, 2),
|
|
3719
|
+
mimeType: "application/json"
|
|
3720
|
+
}
|
|
3721
|
+
]
|
|
3722
|
+
};
|
|
3723
|
+
} catch (error) {
|
|
3724
|
+
return {
|
|
3725
|
+
contents: [
|
|
3726
|
+
{
|
|
3727
|
+
uri: activeUri,
|
|
3728
|
+
text: JSON.stringify({
|
|
3729
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3730
|
+
}),
|
|
3731
|
+
mimeType: "application/json"
|
|
3732
|
+
}
|
|
3733
|
+
]
|
|
3734
|
+
};
|
|
3735
|
+
}
|
|
3736
|
+
}
|
|
3737
|
+
);
|
|
3738
|
+
}
|
|
3739
|
+
function registerMovethewheelsPrompts(server, ctx, track) {
|
|
3740
|
+
const p = ctx.productName;
|
|
3741
|
+
const fulfilmentName = track(mcpPromptName(p, "delivery-fulfilment"));
|
|
3742
|
+
const fleetName = track(mcpPromptName(p, "fleet-ops"));
|
|
3743
|
+
server.registerPrompt(
|
|
3744
|
+
fulfilmentName,
|
|
3745
|
+
{
|
|
3746
|
+
title: "Delivery Fulfilment",
|
|
3747
|
+
description: "Guide for fulfilling a delivery order end to end: create the order, create a shipment, assign a driver/vehicle, and advance it through tracking to delivery.",
|
|
3748
|
+
argsSchema: {
|
|
3749
|
+
customerId: z.string().optional().describe("Customer placing the order"),
|
|
3750
|
+
orderId: z.string().optional().describe("Existing order ID to fulfil")
|
|
3751
|
+
}
|
|
3752
|
+
},
|
|
3753
|
+
({ customerId, orderId }) => {
|
|
3754
|
+
ctx.logger.debug(`Prompt ${fulfilmentName} called`, { customerId, orderId });
|
|
3755
|
+
const orderNote = orderId ? `Operate the existing order "${orderId}".` : customerId ? `Create a new order for customer "${customerId}".` : `First pick a customer with ${p}_customers_list (or create one with ${p}_customers_create).`;
|
|
3756
|
+
return {
|
|
3757
|
+
messages: [
|
|
3758
|
+
{
|
|
3759
|
+
role: "user",
|
|
3760
|
+
content: {
|
|
3761
|
+
type: "text",
|
|
3762
|
+
text: `Help me fulfil a Movethewheels delivery order. ${orderNote}
|
|
3763
|
+
|
|
3764
|
+
1. **Order**: If needed, use ${p}_orders_create with a unique orderNumber, pickupAddress, deliveryAddress, and items.
|
|
3765
|
+
2. **Assign Capacity**: Use ${p}_drivers_list (status=AVAILABLE) and ${p}_vehicles_list (status=ACTIVE), then ${p}_drivers_assign_vehicle and ${p}_orders_update to attach driverId/vehicleId.
|
|
3766
|
+
3. **Shipment**: Use ${p}_shipments_create with the orderId, a unique shipmentNumber, origin, and destination.
|
|
3767
|
+
4. **Track**: As the shipment moves, use ${p}_tracking_add_event (PICKED_UP \u2192 IN_TRANSIT \u2192 OUT_FOR_DELIVERY \u2192 DELIVERED) and mirror order state with ${p}_orders_update_status.
|
|
3768
|
+
5. **Verify**: Use ${p}_tracking_order_events to confirm the timeline and ${p}_orders_get for the final state.
|
|
3769
|
+
|
|
3770
|
+
Show results at each step and confirm before marking DELIVERED.`
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
]
|
|
3774
|
+
};
|
|
3775
|
+
}
|
|
3776
|
+
);
|
|
3777
|
+
server.registerPrompt(
|
|
3778
|
+
fleetName,
|
|
3779
|
+
{
|
|
3780
|
+
title: "Fleet & Warehouse Operations",
|
|
3781
|
+
description: "Daily operations workflow: review KPIs, triage the order queue and tracking alerts, balance fleet status, and watch low inventory.",
|
|
3782
|
+
argsSchema: {}
|
|
3783
|
+
},
|
|
3784
|
+
() => {
|
|
3785
|
+
ctx.logger.debug(`Prompt ${fleetName} called`);
|
|
3786
|
+
return {
|
|
3787
|
+
messages: [
|
|
3788
|
+
{
|
|
3789
|
+
role: "user",
|
|
3790
|
+
content: {
|
|
3791
|
+
type: "text",
|
|
3792
|
+
text: `Run daily Movethewheels operations.
|
|
3793
|
+
|
|
3794
|
+
1. **KPIs**: Use ${p}_analytics_dashboard for headline numbers, ${p}_analytics_order_stats and ${p}_analytics_fleet for status breakdowns, and ${p}_analytics_finance for revenue/fuel/invoices.
|
|
3795
|
+
2. **Order Queue**: Use ${p}_orders_list filtered by status=PENDING (and CONFIRMED) to find orders needing assignment; advance them with ${p}_orders_update_status.
|
|
3796
|
+
3. **Tracking Alerts**: Use ${p}_tracking_alerts to find delayed/exception shipments and ${p}_shipments_get to investigate.
|
|
3797
|
+
4. **Fleet Balance**: Use ${p}_vehicles_list and ${p}_drivers_list to spot MAINTENANCE/OFF_DUTY capacity; update with ${p}_vehicles_update_status / ${p}_drivers_update_status.
|
|
3798
|
+
5. **Inventory**: Use ${p}_inventory_stats and ${p}_inventory_list_items to find low-stock items; record movements with ${p}_inventory_create_transaction.
|
|
3799
|
+
|
|
3800
|
+
Summarize what needs attention after each step.`
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
]
|
|
3804
|
+
};
|
|
3805
|
+
}
|
|
3806
|
+
);
|
|
3807
|
+
}
|
|
3808
|
+
|
|
3809
|
+
// src/modules/movethewheels/index.ts
|
|
3810
|
+
var movethewheelsModule = {
|
|
3811
|
+
id: "movethewheels",
|
|
3812
|
+
version: "0.0.1",
|
|
3813
|
+
displayName: "Movethewheels",
|
|
3814
|
+
dependsOn: ["auth"],
|
|
3815
|
+
capabilities: { tools: {}, resources: {}, prompts: {} },
|
|
3816
|
+
register(server, ctx) {
|
|
3817
|
+
const tools = [];
|
|
3818
|
+
const resources = [];
|
|
3819
|
+
const prompts = [];
|
|
3820
|
+
const trackTool = (n) => {
|
|
3821
|
+
tools.push(n);
|
|
3822
|
+
return n;
|
|
3823
|
+
};
|
|
3824
|
+
const trackResource = (uri) => {
|
|
3825
|
+
resources.push(uri);
|
|
3826
|
+
return uri;
|
|
3827
|
+
};
|
|
3828
|
+
const trackPrompt = (n) => {
|
|
3829
|
+
prompts.push(n);
|
|
3830
|
+
return n;
|
|
3831
|
+
};
|
|
3832
|
+
registerCustomerTools(server, ctx, trackTool);
|
|
3833
|
+
registerDriverTools(server, ctx, trackTool);
|
|
3834
|
+
registerVehicleTools(server, ctx, trackTool);
|
|
3835
|
+
registerProductTools(server, ctx, trackTool);
|
|
3836
|
+
registerOrderTools(server, ctx, trackTool);
|
|
3837
|
+
registerShipmentTools(server, ctx, trackTool);
|
|
3838
|
+
registerTrackingTools(server, ctx, trackTool);
|
|
3839
|
+
registerRouteTools(server, ctx, trackTool);
|
|
3840
|
+
registerWarehouseTools(server, ctx, trackTool);
|
|
3841
|
+
registerInventoryTools(server, ctx, trackTool);
|
|
3842
|
+
registerMarketplaceTools(server, ctx, trackTool);
|
|
3843
|
+
registerReportTools(server, ctx, trackTool);
|
|
3844
|
+
registerAnalyticsTools(server, ctx, trackTool);
|
|
3845
|
+
registerMovethewheelsResources(server, ctx, trackResource);
|
|
3846
|
+
registerMovethewheelsPrompts(server, ctx, trackPrompt);
|
|
3847
|
+
return { tools, resources, prompts };
|
|
3848
|
+
}
|
|
3849
|
+
};
|
|
3850
|
+
var movethewheels_default = movethewheelsModule;
|
|
3851
|
+
|
|
3852
|
+
export { movethewheels_default as default };
|
|
3853
|
+
//# sourceMappingURL=index.js.map
|
|
3854
|
+
//# sourceMappingURL=index.js.map
|