@burdenoff/mcp-libs 2026.627.3 → 2026.627.5
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/cosmicintersection/index.cjs +1984 -0
- package/dist/modules/cosmicintersection/index.cjs.map +1 -0
- package/dist/modules/cosmicintersection/index.d.cts +8 -0
- package/dist/modules/cosmicintersection/index.d.ts +8 -0
- package/dist/modules/cosmicintersection/index.js +1923 -0
- package/dist/modules/cosmicintersection/index.js.map +1 -0
- package/dist/modules/ecoimpacthub/index.cjs +2042 -0
- package/dist/modules/ecoimpacthub/index.cjs.map +1 -0
- package/dist/modules/ecoimpacthub/index.d.cts +8 -0
- package/dist/modules/ecoimpacthub/index.d.ts +8 -0
- package/dist/modules/ecoimpacthub/index.js +1981 -0
- package/dist/modules/ecoimpacthub/index.js.map +1 -0
- package/dist/modules/govcitizenhub/index.cjs +1686 -0
- package/dist/modules/govcitizenhub/index.cjs.map +1 -0
- package/dist/modules/govcitizenhub/index.d.cts +8 -0
- package/dist/modules/govcitizenhub/index.d.ts +8 -0
- package/dist/modules/govcitizenhub/index.js +1625 -0
- package/dist/modules/govcitizenhub/index.js.map +1 -0
- package/dist/modules/intelwatchtower/index.cjs +1919 -0
- package/dist/modules/intelwatchtower/index.cjs.map +1 -0
- package/dist/modules/intelwatchtower/index.d.cts +8 -0
- package/dist/modules/intelwatchtower/index.d.ts +8 -0
- package/dist/modules/intelwatchtower/index.js +1858 -0
- package/dist/modules/intelwatchtower/index.js.map +1 -0
- package/dist/modules/manufacturedops/index.cjs +1884 -0
- package/dist/modules/manufacturedops/index.cjs.map +1 -0
- package/dist/modules/manufacturedops/index.d.cts +8 -0
- package/dist/modules/manufacturedops/index.d.ts +8 -0
- package/dist/modules/manufacturedops/index.js +1823 -0
- package/dist/modules/manufacturedops/index.js.map +1 -0
- package/dist/modules/mybodyshield/index.cjs +1834 -0
- package/dist/modules/mybodyshield/index.cjs.map +1 -0
- package/dist/modules/mybodyshield/index.d.cts +8 -0
- package/dist/modules/mybodyshield/index.d.ts +8 -0
- package/dist/modules/mybodyshield/index.js +1773 -0
- package/dist/modules/mybodyshield/index.js.map +1 -0
- package/dist/modules/semanticfed/index.cjs +1846 -0
- package/dist/modules/semanticfed/index.cjs.map +1 -0
- package/dist/modules/semanticfed/index.d.cts +8 -0
- package/dist/modules/semanticfed/index.d.ts +8 -0
- package/dist/modules/semanticfed/index.js +1785 -0
- package/dist/modules/semanticfed/index.js.map +1 -0
- package/dist/modules/technospam/index.cjs +1962 -0
- package/dist/modules/technospam/index.cjs.map +1 -0
- package/dist/modules/technospam/index.d.cts +8 -0
- package/dist/modules/technospam/index.d.ts +8 -0
- package/dist/modules/technospam/index.js +1901 -0
- package/dist/modules/technospam/index.js.map +1 -0
- package/dist/modules/theculturepost/index.cjs +1587 -0
- package/dist/modules/theculturepost/index.cjs.map +1 -0
- package/dist/modules/theculturepost/index.d.cts +8 -0
- package/dist/modules/theculturepost/index.d.ts +8 -0
- package/dist/modules/theculturepost/index.js +1526 -0
- package/dist/modules/theculturepost/index.js.map +1 -0
- package/dist/modules/theglobalfuel/index.cjs +1678 -0
- package/dist/modules/theglobalfuel/index.cjs.map +1 -0
- package/dist/modules/theglobalfuel/index.d.cts +8 -0
- package/dist/modules/theglobalfuel/index.d.ts +8 -0
- package/dist/modules/theglobalfuel/index.js +1617 -0
- package/dist/modules/theglobalfuel/index.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1884 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var zod = require('zod');
|
|
4
|
+
require('graphql-request');
|
|
5
|
+
require('cross-fetch');
|
|
6
|
+
require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
7
|
+
require('@modelcontextprotocol/sdk/types.js');
|
|
8
|
+
|
|
9
|
+
// src/modules/manufacturedops/tools/production.ts
|
|
10
|
+
|
|
11
|
+
// src/core/errors.ts
|
|
12
|
+
var McpInternalError = class extends Error {
|
|
13
|
+
cause;
|
|
14
|
+
constructor(message, cause) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = "McpInternalError";
|
|
17
|
+
this.cause = cause;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// src/core/result.ts
|
|
22
|
+
function jsonResult(data, structured) {
|
|
23
|
+
const result = {
|
|
24
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
25
|
+
};
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
function errorResult(message, hint) {
|
|
29
|
+
const text = hint ? `${message}
|
|
30
|
+
|
|
31
|
+
Hint: ${hint}` : message;
|
|
32
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/core/tool-name.ts
|
|
36
|
+
var MAX_TOOL_NAME_LENGTH = 63;
|
|
37
|
+
function assertSafeName(name, kind) {
|
|
38
|
+
if (name.length > MAX_TOOL_NAME_LENGTH) {
|
|
39
|
+
throw new McpInternalError(
|
|
40
|
+
`Tool name '${name}' exceeds ${MAX_TOOL_NAME_LENGTH} characters (some MCP clients truncate at 64).`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return name;
|
|
44
|
+
}
|
|
45
|
+
function mcpToolName(productName, ...parts) {
|
|
46
|
+
if (!productName) {
|
|
47
|
+
throw new McpInternalError("mcpToolName: productName is required");
|
|
48
|
+
}
|
|
49
|
+
const name = [productName, ...parts].filter(Boolean).join("_");
|
|
50
|
+
return assertSafeName(name);
|
|
51
|
+
}
|
|
52
|
+
function mcpResourceUri(productName, ...parts) {
|
|
53
|
+
if (!productName) {
|
|
54
|
+
throw new McpInternalError("mcpResourceUri: productName is required");
|
|
55
|
+
}
|
|
56
|
+
return `${productName}://${parts.filter(Boolean).join("/")}`;
|
|
57
|
+
}
|
|
58
|
+
function mcpPromptName(productName, ...parts) {
|
|
59
|
+
if (!productName) {
|
|
60
|
+
throw new McpInternalError("mcpPromptName: productName is required");
|
|
61
|
+
}
|
|
62
|
+
return [productName, ...parts].filter(Boolean).join("-");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/modules/manufacturedops/operations.ts
|
|
66
|
+
var WORK_CENTER_FIELDS = `
|
|
67
|
+
id code name description type capacityHours idealRunRate isActive tags
|
|
68
|
+
createdAt updatedAt workspaceId
|
|
69
|
+
`;
|
|
70
|
+
var PRODUCTION_OPERATION_FIELDS = `
|
|
71
|
+
id productionOrderId name sequence status workCenterId operatorId
|
|
72
|
+
setupMinutes runMinutes goodCount scrapCount startedAt completedAt
|
|
73
|
+
createdAt updatedAt workspaceId
|
|
74
|
+
`;
|
|
75
|
+
var MATERIAL_LOT_FIELDS = `
|
|
76
|
+
id lotNumber materialSku materialName quantity uom status supplier
|
|
77
|
+
receivedAt expiresAt productionOrderId parentLotId tags
|
|
78
|
+
createdAt updatedAt workspaceId
|
|
79
|
+
`;
|
|
80
|
+
var PRODUCTION_ORDER_FIELDS = `
|
|
81
|
+
id orderNumber productSku productName quantity quantityDone uom priority status
|
|
82
|
+
plannedStart plannedEnd actualStart actualEnd dueDate erpReference notes tags
|
|
83
|
+
createdAt updatedAt workspaceId
|
|
84
|
+
`;
|
|
85
|
+
var INSPECTION_PLAN_FIELDS = `
|
|
86
|
+
id code name description stage productSku characteristics isActive
|
|
87
|
+
createdAt updatedAt workspaceId
|
|
88
|
+
`;
|
|
89
|
+
var QUALITY_INSPECTION_FIELDS = `
|
|
90
|
+
id stage result sampleSize defectCount measurements notes
|
|
91
|
+
inspectionPlanId productionOrderId materialLotId workCenterId inspectorId
|
|
92
|
+
inspectedAt createdAt updatedAt workspaceId
|
|
93
|
+
`;
|
|
94
|
+
var NON_CONFORMANCE_FIELDS = `
|
|
95
|
+
id ncrNumber title description severity status disposition
|
|
96
|
+
quantityAffected rootCause correctiveAction ownerId inspectionId
|
|
97
|
+
dueDate resolvedAt tags createdAt updatedAt workspaceId
|
|
98
|
+
`;
|
|
99
|
+
var ASSET_FIELDS = `
|
|
100
|
+
id assetTag name manufacturer model serialNumber status
|
|
101
|
+
pmIntervalHours runningHours workCenterId installedAt lastServicedAt
|
|
102
|
+
tags createdAt updatedAt workspaceId
|
|
103
|
+
`;
|
|
104
|
+
var MAINTENANCE_WORK_ORDER_FIELDS = `
|
|
105
|
+
id woNumber title description type status assetId technicianId
|
|
106
|
+
scheduledFor startedAt completedAt laborMinutes downtimeMinutes
|
|
107
|
+
createdAt updatedAt workspaceId
|
|
108
|
+
`;
|
|
109
|
+
var DOWNTIME_EVENT_FIELDS = `
|
|
110
|
+
id reason description durationMinutes assetId workCenterId productionOrderId
|
|
111
|
+
startedAt endedAt createdAt updatedAt workspaceId
|
|
112
|
+
`;
|
|
113
|
+
var PAGE_INFO = `pageInfo { cursor hasMore }`;
|
|
114
|
+
var GET_WORK_CENTER = `
|
|
115
|
+
query ManGetWorkCenter($id: ID!) {
|
|
116
|
+
manWorkCenter(id: $id) {
|
|
117
|
+
${WORK_CENTER_FIELDS}
|
|
118
|
+
operations { id name sequence status productionOrderId }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
`;
|
|
122
|
+
var LIST_WORK_CENTERS = `
|
|
123
|
+
query ManListWorkCenters($isActive: Boolean) {
|
|
124
|
+
manListWorkCenters(isActive: $isActive) {
|
|
125
|
+
${WORK_CENTER_FIELDS}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
`;
|
|
129
|
+
var CREATE_WORK_CENTER = `
|
|
130
|
+
mutation ManCreateWorkCenter($input: ManCreateWorkCenterInput!) {
|
|
131
|
+
manCreateWorkCenter(input: $input) {
|
|
132
|
+
${WORK_CENTER_FIELDS}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
`;
|
|
136
|
+
var UPDATE_WORK_CENTER = `
|
|
137
|
+
mutation ManUpdateWorkCenter($id: ID!, $input: ManUpdateWorkCenterInput!) {
|
|
138
|
+
manUpdateWorkCenter(id: $id, input: $input) {
|
|
139
|
+
${WORK_CENTER_FIELDS}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
`;
|
|
143
|
+
var ARCHIVE_WORK_CENTER = `
|
|
144
|
+
mutation ManArchiveWorkCenter($id: ID!) {
|
|
145
|
+
manArchiveWorkCenter(id: $id)
|
|
146
|
+
}
|
|
147
|
+
`;
|
|
148
|
+
var GET_PRODUCTION_ORDER = `
|
|
149
|
+
query ManGetProductionOrder($id: ID!) {
|
|
150
|
+
manProductionOrder(id: $id) {
|
|
151
|
+
${PRODUCTION_ORDER_FIELDS}
|
|
152
|
+
operations { ${PRODUCTION_OPERATION_FIELDS} }
|
|
153
|
+
materialLots { ${MATERIAL_LOT_FIELDS} }
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
`;
|
|
157
|
+
var LIST_PRODUCTION_ORDERS = `
|
|
158
|
+
query ManListProductionOrders(
|
|
159
|
+
$pagination: PaginationInput
|
|
160
|
+
$productSku: String
|
|
161
|
+
$status: ManProductionOrderStatus
|
|
162
|
+
) {
|
|
163
|
+
manListProductionOrders(pagination: $pagination, productSku: $productSku, status: $status) {
|
|
164
|
+
items { ${PRODUCTION_ORDER_FIELDS} }
|
|
165
|
+
${PAGE_INFO}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
`;
|
|
169
|
+
var CREATE_PRODUCTION_ORDER = `
|
|
170
|
+
mutation ManCreateProductionOrder($input: ManCreateProductionOrderInput!) {
|
|
171
|
+
manCreateProductionOrder(input: $input) {
|
|
172
|
+
${PRODUCTION_ORDER_FIELDS}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
`;
|
|
176
|
+
var UPDATE_PRODUCTION_ORDER = `
|
|
177
|
+
mutation ManUpdateProductionOrder($id: ID!, $input: ManUpdateProductionOrderInput!) {
|
|
178
|
+
manUpdateProductionOrder(id: $id, input: $input) {
|
|
179
|
+
${PRODUCTION_ORDER_FIELDS}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
`;
|
|
183
|
+
var TRANSITION_PRODUCTION_ORDER = `
|
|
184
|
+
mutation ManTransitionProductionOrder($id: ID!, $status: ManProductionOrderStatus!) {
|
|
185
|
+
manTransitionProductionOrder(id: $id, status: $status) {
|
|
186
|
+
${PRODUCTION_ORDER_FIELDS}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
`;
|
|
190
|
+
var ADD_OPERATION = `
|
|
191
|
+
mutation ManAddOperation($input: ManCreateOperationInput!) {
|
|
192
|
+
manAddOperation(input: $input) {
|
|
193
|
+
${PRODUCTION_OPERATION_FIELDS}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
`;
|
|
197
|
+
var RECORD_OPERATION = `
|
|
198
|
+
mutation ManRecordOperation($id: ID!, $input: ManRecordOperationInput!) {
|
|
199
|
+
manRecordOperation(id: $id, input: $input) {
|
|
200
|
+
${PRODUCTION_OPERATION_FIELDS}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
`;
|
|
204
|
+
var LIST_MATERIAL_LOTS = `
|
|
205
|
+
query ManListMaterialLots(
|
|
206
|
+
$pagination: PaginationInput
|
|
207
|
+
$productionOrderId: ID
|
|
208
|
+
$status: ManMaterialLotStatus
|
|
209
|
+
) {
|
|
210
|
+
manListMaterialLots(
|
|
211
|
+
pagination: $pagination
|
|
212
|
+
productionOrderId: $productionOrderId
|
|
213
|
+
status: $status
|
|
214
|
+
) {
|
|
215
|
+
items { ${MATERIAL_LOT_FIELDS} }
|
|
216
|
+
${PAGE_INFO}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
`;
|
|
220
|
+
var CREATE_MATERIAL_LOT = `
|
|
221
|
+
mutation ManCreateMaterialLot($input: ManCreateMaterialLotInput!) {
|
|
222
|
+
manCreateMaterialLot(input: $input) {
|
|
223
|
+
${MATERIAL_LOT_FIELDS}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
`;
|
|
227
|
+
var SET_MATERIAL_LOT_STATUS = `
|
|
228
|
+
mutation ManSetMaterialLotStatus($id: ID!, $status: ManMaterialLotStatus!) {
|
|
229
|
+
manSetMaterialLotStatus(id: $id, status: $status) {
|
|
230
|
+
${MATERIAL_LOT_FIELDS}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
`;
|
|
234
|
+
var GET_INSPECTION = `
|
|
235
|
+
query ManGetInspection($id: ID!) {
|
|
236
|
+
manInspection(id: $id) {
|
|
237
|
+
${QUALITY_INSPECTION_FIELDS}
|
|
238
|
+
inspectionPlan { ${INSPECTION_PLAN_FIELDS} }
|
|
239
|
+
nonConformances { ${NON_CONFORMANCE_FIELDS} }
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
`;
|
|
243
|
+
var LIST_INSPECTIONS = `
|
|
244
|
+
query ManListInspections(
|
|
245
|
+
$pagination: PaginationInput
|
|
246
|
+
$productionOrderId: ID
|
|
247
|
+
$result: ManInspectionResult
|
|
248
|
+
) {
|
|
249
|
+
manListInspections(
|
|
250
|
+
pagination: $pagination
|
|
251
|
+
productionOrderId: $productionOrderId
|
|
252
|
+
result: $result
|
|
253
|
+
) {
|
|
254
|
+
items { ${QUALITY_INSPECTION_FIELDS} }
|
|
255
|
+
${PAGE_INFO}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
`;
|
|
259
|
+
var RECORD_INSPECTION = `
|
|
260
|
+
mutation ManRecordInspection($input: ManRecordInspectionInput!) {
|
|
261
|
+
manRecordInspection(input: $input) {
|
|
262
|
+
${QUALITY_INSPECTION_FIELDS}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
`;
|
|
266
|
+
var LIST_INSPECTION_PLANS = `
|
|
267
|
+
query ManListInspectionPlans($isActive: Boolean, $stage: ManInspectionStage) {
|
|
268
|
+
manListInspectionPlans(isActive: $isActive, stage: $stage) {
|
|
269
|
+
${INSPECTION_PLAN_FIELDS}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
`;
|
|
273
|
+
var CREATE_INSPECTION_PLAN = `
|
|
274
|
+
mutation ManCreateInspectionPlan($input: ManCreateInspectionPlanInput!) {
|
|
275
|
+
manCreateInspectionPlan(input: $input) {
|
|
276
|
+
${INSPECTION_PLAN_FIELDS}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
`;
|
|
280
|
+
var GET_NON_CONFORMANCE = `
|
|
281
|
+
query ManGetNonConformance($id: ID!) {
|
|
282
|
+
manNonConformance(id: $id) {
|
|
283
|
+
${NON_CONFORMANCE_FIELDS}
|
|
284
|
+
inspection { ${QUALITY_INSPECTION_FIELDS} }
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
`;
|
|
288
|
+
var LIST_NON_CONFORMANCES = `
|
|
289
|
+
query ManListNonConformances(
|
|
290
|
+
$pagination: PaginationInput
|
|
291
|
+
$severity: ManSeverity
|
|
292
|
+
$status: ManNonConformanceStatus
|
|
293
|
+
) {
|
|
294
|
+
manListNonConformances(pagination: $pagination, severity: $severity, status: $status) {
|
|
295
|
+
items { ${NON_CONFORMANCE_FIELDS} }
|
|
296
|
+
${PAGE_INFO}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
`;
|
|
300
|
+
var RAISE_NON_CONFORMANCE = `
|
|
301
|
+
mutation ManRaiseNonConformance($input: ManRaiseNonConformanceInput!) {
|
|
302
|
+
manRaiseNonConformance(input: $input) {
|
|
303
|
+
${NON_CONFORMANCE_FIELDS}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
`;
|
|
307
|
+
var UPDATE_NON_CONFORMANCE = `
|
|
308
|
+
mutation ManUpdateNonConformance($id: ID!, $input: ManUpdateNonConformanceInput!) {
|
|
309
|
+
manUpdateNonConformance(id: $id, input: $input) {
|
|
310
|
+
${NON_CONFORMANCE_FIELDS}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
`;
|
|
314
|
+
var TRANSITION_NON_CONFORMANCE = `
|
|
315
|
+
mutation ManTransitionNonConformance($id: ID!, $status: ManNonConformanceStatus!) {
|
|
316
|
+
manTransitionNonConformance(id: $id, status: $status) {
|
|
317
|
+
${NON_CONFORMANCE_FIELDS}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
`;
|
|
321
|
+
var GET_ASSET = `
|
|
322
|
+
query ManGetAsset($id: ID!) {
|
|
323
|
+
manAsset(id: $id) {
|
|
324
|
+
${ASSET_FIELDS}
|
|
325
|
+
workOrders { ${MAINTENANCE_WORK_ORDER_FIELDS} }
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
`;
|
|
329
|
+
var LIST_ASSETS = `
|
|
330
|
+
query ManListAssets(
|
|
331
|
+
$pagination: PaginationInput
|
|
332
|
+
$status: ManAssetStatus
|
|
333
|
+
$workCenterId: ID
|
|
334
|
+
) {
|
|
335
|
+
manListAssets(pagination: $pagination, status: $status, workCenterId: $workCenterId) {
|
|
336
|
+
items { ${ASSET_FIELDS} }
|
|
337
|
+
${PAGE_INFO}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
`;
|
|
341
|
+
var CREATE_ASSET = `
|
|
342
|
+
mutation ManCreateAsset($input: ManCreateAssetInput!) {
|
|
343
|
+
manCreateAsset(input: $input) {
|
|
344
|
+
${ASSET_FIELDS}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
`;
|
|
348
|
+
var UPDATE_ASSET = `
|
|
349
|
+
mutation ManUpdateAsset($id: ID!, $input: ManUpdateAssetInput!) {
|
|
350
|
+
manUpdateAsset(id: $id, input: $input) {
|
|
351
|
+
${ASSET_FIELDS}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
`;
|
|
355
|
+
var LIST_MAINTENANCE_WORK_ORDERS = `
|
|
356
|
+
query ManListMaintenanceWorkOrders(
|
|
357
|
+
$assetId: ID
|
|
358
|
+
$pagination: PaginationInput
|
|
359
|
+
$status: ManMaintenanceStatus
|
|
360
|
+
) {
|
|
361
|
+
manListMaintenanceWorkOrders(assetId: $assetId, pagination: $pagination, status: $status) {
|
|
362
|
+
items {
|
|
363
|
+
${MAINTENANCE_WORK_ORDER_FIELDS}
|
|
364
|
+
asset { id assetTag name status }
|
|
365
|
+
}
|
|
366
|
+
${PAGE_INFO}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
`;
|
|
370
|
+
var CREATE_MAINTENANCE_WORK_ORDER = `
|
|
371
|
+
mutation ManCreateMaintenanceWorkOrder($input: ManCreateMaintenanceWorkOrderInput!) {
|
|
372
|
+
manCreateMaintenanceWorkOrder(input: $input) {
|
|
373
|
+
${MAINTENANCE_WORK_ORDER_FIELDS}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
`;
|
|
377
|
+
var TRANSITION_MAINTENANCE_WORK_ORDER = `
|
|
378
|
+
mutation ManTransitionMaintenanceWorkOrder($id: ID!, $status: ManMaintenanceStatus!) {
|
|
379
|
+
manTransitionMaintenanceWorkOrder(id: $id, status: $status) {
|
|
380
|
+
${MAINTENANCE_WORK_ORDER_FIELDS}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
`;
|
|
384
|
+
var LIST_DOWNTIME_EVENTS = `
|
|
385
|
+
query ManListDowntimeEvents($assetId: ID, $reason: ManDowntimeReason, $workCenterId: ID) {
|
|
386
|
+
manListDowntimeEvents(assetId: $assetId, reason: $reason, workCenterId: $workCenterId) {
|
|
387
|
+
${DOWNTIME_EVENT_FIELDS}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
`;
|
|
391
|
+
var RECORD_DOWNTIME = `
|
|
392
|
+
mutation ManRecordDowntime($input: ManRecordDowntimeInput!) {
|
|
393
|
+
manRecordDowntime(input: $input) {
|
|
394
|
+
${DOWNTIME_EVENT_FIELDS}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
`;
|
|
398
|
+
var HEALTH = `
|
|
399
|
+
query ManHealth {
|
|
400
|
+
manHealth
|
|
401
|
+
}
|
|
402
|
+
`;
|
|
403
|
+
var WorkCenterTypeEnum = zod.z.enum(["ASSEMBLY", "CELL", "LINE", "MACHINE", "MANUAL"]).describe("Type of a production work center");
|
|
404
|
+
var ProductionOrderStatusEnum = zod.z.enum(["PLANNED", "RELEASED", "IN_PROGRESS", "PAUSED", "COMPLETED", "CLOSED", "CANCELLED"]).describe("Execution lifecycle status of a production order");
|
|
405
|
+
var ProductionPriorityEnum = zod.z.enum(["LOW", "NORMAL", "HIGH", "URGENT"]).describe("Scheduling priority of a production order");
|
|
406
|
+
var OperationStatusEnum = zod.z.enum(["PENDING", "SETUP", "RUNNING", "DONE", "SKIPPED"]).describe("Status of a routing/production operation");
|
|
407
|
+
var MaterialLotStatusEnum = zod.z.enum(["AVAILABLE", "ALLOCATED", "CONSUMED", "QUARANTINED", "SCRAPPED"]).describe("Inventory/quality status of a material lot");
|
|
408
|
+
var InspectionStageEnum = zod.z.enum(["INCOMING", "IN_PROCESS", "FINAL", "AUDIT"]).describe("Stage at which a quality inspection is performed");
|
|
409
|
+
var InspectionResultEnum = zod.z.enum(["PENDING", "PASS", "FAIL", "CONDITIONAL"]).describe("Outcome of a quality inspection");
|
|
410
|
+
var SeverityEnum = zod.z.enum(["MINOR", "MAJOR", "CRITICAL"]).describe("Severity of a non-conformance");
|
|
411
|
+
var NonConformanceStatusEnum = zod.z.enum(["OPEN", "INVESTIGATING", "CONTAINED", "CORRECTIVE_ACTION", "CLOSED"]).describe("Resolution lifecycle status of an NCR/CAPA");
|
|
412
|
+
var NonConformanceDispositionEnum = zod.z.enum(["OPEN", "USE_AS_IS", "REWORK", "SCRAP", "RETURN_TO_SUPPLIER"]).describe("Disposition decision for a non-conformance");
|
|
413
|
+
var AssetStatusEnum = zod.z.enum(["OPERATIONAL", "DEGRADED", "DOWN", "MAINTENANCE", "RETIRED"]).describe("Operational status of a maintainable asset");
|
|
414
|
+
var MaintenanceTypeEnum = zod.z.enum(["PREVENTIVE", "CORRECTIVE", "PREDICTIVE", "INSPECTION"]).describe("Type of a maintenance work order");
|
|
415
|
+
var MaintenanceStatusEnum = zod.z.enum(["SCHEDULED", "IN_PROGRESS", "ON_HOLD", "COMPLETED", "CANCELLED"]).describe("Lifecycle status of a maintenance work order");
|
|
416
|
+
var DowntimeReasonEnum = zod.z.enum(["PLANNED", "BREAKDOWN", "CHANGEOVER", "MATERIAL_SHORTAGE", "QUALITY", "OTHER"]).describe("Reason classification for a downtime event");
|
|
417
|
+
var IsoDateTime = zod.z.string().describe('ISO-8601 datetime, e.g. "2026-06-26T09:00:00.000Z"');
|
|
418
|
+
var JsonObject = zod.z.record(zod.z.string(), zod.z.unknown());
|
|
419
|
+
var PaginationFields = {
|
|
420
|
+
limit: zod.z.number().int().optional().describe("Page size (default 50)"),
|
|
421
|
+
offset: zod.z.number().int().optional().describe("Pagination offset (default 0)"),
|
|
422
|
+
cursor: zod.z.string().optional().describe("Opaque pagination cursor")
|
|
423
|
+
};
|
|
424
|
+
function buildPagination(args) {
|
|
425
|
+
const pagination = {};
|
|
426
|
+
if (args.limit !== void 0) pagination.limit = args.limit;
|
|
427
|
+
if (args.offset !== void 0) pagination.offset = args.offset;
|
|
428
|
+
if (args.cursor !== void 0) pagination.cursor = args.cursor;
|
|
429
|
+
return Object.keys(pagination).length > 0 ? pagination : void 0;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// src/modules/manufacturedops/tools/production.ts
|
|
433
|
+
function registerProductionTools(server, ctx, track) {
|
|
434
|
+
server.registerTool(
|
|
435
|
+
track(mcpToolName(ctx.productName, "work_centers_get")),
|
|
436
|
+
{
|
|
437
|
+
title: "Get Work Center",
|
|
438
|
+
description: "Fetch a single work center by ID, including its scheduled operations.",
|
|
439
|
+
inputSchema: { id: zod.z.string().describe("Work center ID") }
|
|
440
|
+
},
|
|
441
|
+
async ({ id }) => {
|
|
442
|
+
try {
|
|
443
|
+
const data = await ctx.gql.workspace.request(GET_WORK_CENTER, { id });
|
|
444
|
+
if (!data.manWorkCenter) return errorResult(`Work center ${id} not found`);
|
|
445
|
+
return jsonResult(data.manWorkCenter);
|
|
446
|
+
} catch (error) {
|
|
447
|
+
ctx.logger.error("manufacturedops work_centers_get failed", { error: String(error) });
|
|
448
|
+
return errorResult(
|
|
449
|
+
`Failed to get work center: ${error instanceof Error ? error.message : String(error)}`
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
);
|
|
454
|
+
server.registerTool(
|
|
455
|
+
track(mcpToolName(ctx.productName, "work_centers_list")),
|
|
456
|
+
{
|
|
457
|
+
title: "List Work Centers",
|
|
458
|
+
description: "List work centers in the bound workspace, optionally filtered by active state.",
|
|
459
|
+
inputSchema: {
|
|
460
|
+
isActive: zod.z.boolean().optional().describe("Only active (true) or archived (false) centers")
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
async ({ isActive }) => {
|
|
464
|
+
try {
|
|
465
|
+
const data = await ctx.gql.workspace.request(LIST_WORK_CENTERS, { isActive });
|
|
466
|
+
return jsonResult(data.manListWorkCenters);
|
|
467
|
+
} catch (error) {
|
|
468
|
+
ctx.logger.error("manufacturedops work_centers_list failed", { error: String(error) });
|
|
469
|
+
return errorResult(
|
|
470
|
+
`Failed to list work centers: ${error instanceof Error ? error.message : String(error)}`
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
);
|
|
475
|
+
server.registerTool(
|
|
476
|
+
track(mcpToolName(ctx.productName, "work_centers_create")),
|
|
477
|
+
{
|
|
478
|
+
title: "Create Work Center",
|
|
479
|
+
description: "Create a work center (machine/line/cell/manual station). `code` must be unique within the workspace.",
|
|
480
|
+
inputSchema: {
|
|
481
|
+
code: zod.z.string().min(1).describe("Unique work-center code"),
|
|
482
|
+
name: zod.z.string().min(1).describe("Display name"),
|
|
483
|
+
type: WorkCenterTypeEnum.optional().describe("Work-center type (default MACHINE)"),
|
|
484
|
+
description: zod.z.string().optional().describe("Description"),
|
|
485
|
+
capacityHours: zod.z.number().optional().describe("Available capacity in hours"),
|
|
486
|
+
idealRunRate: zod.z.number().optional().describe("Ideal run rate (units/hour) for OEE"),
|
|
487
|
+
tags: zod.z.array(zod.z.string()).optional().describe("Free-form tags")
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
async ({ code, name, type, description, capacityHours, idealRunRate, tags }) => {
|
|
491
|
+
try {
|
|
492
|
+
const input = { code, name };
|
|
493
|
+
if (type !== void 0) input.type = type;
|
|
494
|
+
if (description !== void 0) input.description = description;
|
|
495
|
+
if (capacityHours !== void 0) input.capacityHours = capacityHours;
|
|
496
|
+
if (idealRunRate !== void 0) input.idealRunRate = idealRunRate;
|
|
497
|
+
if (tags !== void 0) input.tags = tags;
|
|
498
|
+
const data = await ctx.gql.workspace.request(CREATE_WORK_CENTER, { input });
|
|
499
|
+
return jsonResult(data.manCreateWorkCenter);
|
|
500
|
+
} catch (error) {
|
|
501
|
+
ctx.logger.error("manufacturedops work_centers_create failed", { error: String(error) });
|
|
502
|
+
return errorResult(
|
|
503
|
+
`Failed to create work center: ${error instanceof Error ? error.message : String(error)}`
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
);
|
|
508
|
+
server.registerTool(
|
|
509
|
+
track(mcpToolName(ctx.productName, "work_centers_update")),
|
|
510
|
+
{
|
|
511
|
+
title: "Update Work Center",
|
|
512
|
+
description: "Update a work center. At least one mutable field must be provided.",
|
|
513
|
+
inputSchema: {
|
|
514
|
+
id: zod.z.string().describe("Work center ID"),
|
|
515
|
+
name: zod.z.string().optional().describe("Updated name"),
|
|
516
|
+
type: WorkCenterTypeEnum.optional().describe("Updated type"),
|
|
517
|
+
description: zod.z.string().optional().describe("Updated description"),
|
|
518
|
+
capacityHours: zod.z.number().optional().describe("Updated capacity (hours)"),
|
|
519
|
+
idealRunRate: zod.z.number().optional().describe("Updated ideal run rate"),
|
|
520
|
+
isActive: zod.z.boolean().optional().describe("Updated active state"),
|
|
521
|
+
tags: zod.z.array(zod.z.string()).optional().describe("Updated tags")
|
|
522
|
+
}
|
|
523
|
+
},
|
|
524
|
+
async ({ id, name, type, description, capacityHours, idealRunRate, isActive, tags }) => {
|
|
525
|
+
try {
|
|
526
|
+
const input = {};
|
|
527
|
+
if (name !== void 0) input.name = name;
|
|
528
|
+
if (type !== void 0) input.type = type;
|
|
529
|
+
if (description !== void 0) input.description = description;
|
|
530
|
+
if (capacityHours !== void 0) input.capacityHours = capacityHours;
|
|
531
|
+
if (idealRunRate !== void 0) input.idealRunRate = idealRunRate;
|
|
532
|
+
if (isActive !== void 0) input.isActive = isActive;
|
|
533
|
+
if (tags !== void 0) input.tags = tags;
|
|
534
|
+
if (Object.keys(input).length === 0) {
|
|
535
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field");
|
|
536
|
+
}
|
|
537
|
+
const data = await ctx.gql.workspace.request(UPDATE_WORK_CENTER, { id, input });
|
|
538
|
+
return jsonResult(data.manUpdateWorkCenter);
|
|
539
|
+
} catch (error) {
|
|
540
|
+
ctx.logger.error("manufacturedops work_centers_update failed", { error: String(error) });
|
|
541
|
+
return errorResult(
|
|
542
|
+
`Failed to update work center: ${error instanceof Error ? error.message : String(error)}`
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
);
|
|
547
|
+
server.registerTool(
|
|
548
|
+
track(mcpToolName(ctx.productName, "work_centers_archive")),
|
|
549
|
+
{
|
|
550
|
+
title: "Archive Work Center",
|
|
551
|
+
description: "Archive (soft-delete) a work center so it is no longer schedulable.",
|
|
552
|
+
inputSchema: { id: zod.z.string().describe("Work center ID") }
|
|
553
|
+
},
|
|
554
|
+
async ({ id }) => {
|
|
555
|
+
try {
|
|
556
|
+
const data = await ctx.gql.workspace.request(
|
|
557
|
+
ARCHIVE_WORK_CENTER,
|
|
558
|
+
{ id }
|
|
559
|
+
);
|
|
560
|
+
return jsonResult({ ok: data.manArchiveWorkCenter, id });
|
|
561
|
+
} catch (error) {
|
|
562
|
+
ctx.logger.error("manufacturedops work_centers_archive failed", { error: String(error) });
|
|
563
|
+
return errorResult(
|
|
564
|
+
`Failed to archive work center: ${error instanceof Error ? error.message : String(error)}`
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
);
|
|
569
|
+
server.registerTool(
|
|
570
|
+
track(mcpToolName(ctx.productName, "production_orders_get")),
|
|
571
|
+
{
|
|
572
|
+
title: "Get Production Order",
|
|
573
|
+
description: "Fetch a single production order by ID, including its operations and material lots.",
|
|
574
|
+
inputSchema: { id: zod.z.string().describe("Production order ID") }
|
|
575
|
+
},
|
|
576
|
+
async ({ id }) => {
|
|
577
|
+
try {
|
|
578
|
+
const data = await ctx.gql.workspace.request(GET_PRODUCTION_ORDER, { id });
|
|
579
|
+
if (!data.manProductionOrder) return errorResult(`Production order ${id} not found`);
|
|
580
|
+
return jsonResult(data.manProductionOrder);
|
|
581
|
+
} catch (error) {
|
|
582
|
+
ctx.logger.error("manufacturedops production_orders_get failed", { error: String(error) });
|
|
583
|
+
return errorResult(
|
|
584
|
+
`Failed to get production order: ${error instanceof Error ? error.message : String(error)}`
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
);
|
|
589
|
+
server.registerTool(
|
|
590
|
+
track(mcpToolName(ctx.productName, "production_orders_list")),
|
|
591
|
+
{
|
|
592
|
+
title: "List Production Orders",
|
|
593
|
+
description: "List production orders (paginated), optionally filtered by product SKU and/or status.",
|
|
594
|
+
inputSchema: {
|
|
595
|
+
productSku: zod.z.string().optional().describe("Filter by product SKU"),
|
|
596
|
+
status: ProductionOrderStatusEnum.optional().describe("Filter by status"),
|
|
597
|
+
...PaginationFields
|
|
598
|
+
}
|
|
599
|
+
},
|
|
600
|
+
async ({ productSku, status, limit, offset, cursor }) => {
|
|
601
|
+
try {
|
|
602
|
+
const data = await ctx.gql.workspace.request(LIST_PRODUCTION_ORDERS, {
|
|
603
|
+
productSku,
|
|
604
|
+
status,
|
|
605
|
+
pagination: buildPagination({ limit, offset, cursor })
|
|
606
|
+
});
|
|
607
|
+
return jsonResult(data.manListProductionOrders);
|
|
608
|
+
} catch (error) {
|
|
609
|
+
ctx.logger.error("manufacturedops production_orders_list failed", { error: String(error) });
|
|
610
|
+
return errorResult(
|
|
611
|
+
`Failed to list production orders: ${error instanceof Error ? error.message : String(error)}`
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
);
|
|
616
|
+
server.registerTool(
|
|
617
|
+
track(mcpToolName(ctx.productName, "production_orders_create")),
|
|
618
|
+
{
|
|
619
|
+
title: "Create Production Order",
|
|
620
|
+
description: "Create a production (manufacturing) order. `orderNumber` and `productSku` must be provided.",
|
|
621
|
+
inputSchema: {
|
|
622
|
+
orderNumber: zod.z.string().min(1).describe("Unique order number"),
|
|
623
|
+
productSku: zod.z.string().min(1).describe("Product SKU being manufactured"),
|
|
624
|
+
productName: zod.z.string().min(1).describe("Product display name"),
|
|
625
|
+
quantity: zod.z.number().optional().describe("Order quantity"),
|
|
626
|
+
uom: zod.z.string().optional().describe('Unit of measure (e.g. "ea", "kg")'),
|
|
627
|
+
priority: ProductionPriorityEnum.optional().describe("Priority (default NORMAL)"),
|
|
628
|
+
plannedStart: IsoDateTime.optional().describe("Planned start (ISO-8601)"),
|
|
629
|
+
plannedEnd: IsoDateTime.optional().describe("Planned end (ISO-8601)"),
|
|
630
|
+
dueDate: IsoDateTime.optional().describe("Due date (ISO-8601)"),
|
|
631
|
+
erpReference: zod.z.string().optional().describe("External ERP reference"),
|
|
632
|
+
notes: zod.z.string().optional().describe("Free-form notes"),
|
|
633
|
+
tags: zod.z.array(zod.z.string()).optional().describe("Free-form tags")
|
|
634
|
+
}
|
|
635
|
+
},
|
|
636
|
+
async ({
|
|
637
|
+
orderNumber,
|
|
638
|
+
productSku,
|
|
639
|
+
productName,
|
|
640
|
+
quantity,
|
|
641
|
+
uom,
|
|
642
|
+
priority,
|
|
643
|
+
plannedStart,
|
|
644
|
+
plannedEnd,
|
|
645
|
+
dueDate,
|
|
646
|
+
erpReference,
|
|
647
|
+
notes,
|
|
648
|
+
tags
|
|
649
|
+
}) => {
|
|
650
|
+
try {
|
|
651
|
+
const input = { orderNumber, productSku, productName };
|
|
652
|
+
if (quantity !== void 0) input.quantity = quantity;
|
|
653
|
+
if (uom !== void 0) input.uom = uom;
|
|
654
|
+
if (priority !== void 0) input.priority = priority;
|
|
655
|
+
if (plannedStart !== void 0) input.plannedStart = plannedStart;
|
|
656
|
+
if (plannedEnd !== void 0) input.plannedEnd = plannedEnd;
|
|
657
|
+
if (dueDate !== void 0) input.dueDate = dueDate;
|
|
658
|
+
if (erpReference !== void 0) input.erpReference = erpReference;
|
|
659
|
+
if (notes !== void 0) input.notes = notes;
|
|
660
|
+
if (tags !== void 0) input.tags = tags;
|
|
661
|
+
const data = await ctx.gql.workspace.request(CREATE_PRODUCTION_ORDER, { input });
|
|
662
|
+
return jsonResult(data.manCreateProductionOrder);
|
|
663
|
+
} catch (error) {
|
|
664
|
+
ctx.logger.error("manufacturedops production_orders_create failed", {
|
|
665
|
+
error: String(error)
|
|
666
|
+
});
|
|
667
|
+
return errorResult(
|
|
668
|
+
`Failed to create production order: ${error instanceof Error ? error.message : String(error)}`
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
);
|
|
673
|
+
server.registerTool(
|
|
674
|
+
track(mcpToolName(ctx.productName, "production_orders_update")),
|
|
675
|
+
{
|
|
676
|
+
title: "Update Production Order",
|
|
677
|
+
description: "Update a production order. At least one mutable field must be provided.",
|
|
678
|
+
inputSchema: {
|
|
679
|
+
id: zod.z.string().describe("Production order ID"),
|
|
680
|
+
productName: zod.z.string().optional().describe("Updated product name"),
|
|
681
|
+
quantity: zod.z.number().optional().describe("Updated quantity"),
|
|
682
|
+
uom: zod.z.string().optional().describe("Updated unit of measure"),
|
|
683
|
+
priority: ProductionPriorityEnum.optional().describe("Updated priority"),
|
|
684
|
+
plannedStart: IsoDateTime.optional().describe("Updated planned start"),
|
|
685
|
+
plannedEnd: IsoDateTime.optional().describe("Updated planned end"),
|
|
686
|
+
dueDate: IsoDateTime.optional().describe("Updated due date"),
|
|
687
|
+
erpReference: zod.z.string().optional().describe("Updated ERP reference"),
|
|
688
|
+
notes: zod.z.string().optional().describe("Updated notes"),
|
|
689
|
+
tags: zod.z.array(zod.z.string()).optional().describe("Updated tags")
|
|
690
|
+
}
|
|
691
|
+
},
|
|
692
|
+
async ({
|
|
693
|
+
id,
|
|
694
|
+
productName,
|
|
695
|
+
quantity,
|
|
696
|
+
uom,
|
|
697
|
+
priority,
|
|
698
|
+
plannedStart,
|
|
699
|
+
plannedEnd,
|
|
700
|
+
dueDate,
|
|
701
|
+
erpReference,
|
|
702
|
+
notes,
|
|
703
|
+
tags
|
|
704
|
+
}) => {
|
|
705
|
+
try {
|
|
706
|
+
const input = {};
|
|
707
|
+
if (productName !== void 0) input.productName = productName;
|
|
708
|
+
if (quantity !== void 0) input.quantity = quantity;
|
|
709
|
+
if (uom !== void 0) input.uom = uom;
|
|
710
|
+
if (priority !== void 0) input.priority = priority;
|
|
711
|
+
if (plannedStart !== void 0) input.plannedStart = plannedStart;
|
|
712
|
+
if (plannedEnd !== void 0) input.plannedEnd = plannedEnd;
|
|
713
|
+
if (dueDate !== void 0) input.dueDate = dueDate;
|
|
714
|
+
if (erpReference !== void 0) input.erpReference = erpReference;
|
|
715
|
+
if (notes !== void 0) input.notes = notes;
|
|
716
|
+
if (tags !== void 0) input.tags = tags;
|
|
717
|
+
if (Object.keys(input).length === 0) {
|
|
718
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field");
|
|
719
|
+
}
|
|
720
|
+
const data = await ctx.gql.workspace.request(UPDATE_PRODUCTION_ORDER, { id, input });
|
|
721
|
+
return jsonResult(data.manUpdateProductionOrder);
|
|
722
|
+
} catch (error) {
|
|
723
|
+
ctx.logger.error("manufacturedops production_orders_update failed", {
|
|
724
|
+
error: String(error)
|
|
725
|
+
});
|
|
726
|
+
return errorResult(
|
|
727
|
+
`Failed to update production order: ${error instanceof Error ? error.message : String(error)}`
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
);
|
|
732
|
+
server.registerTool(
|
|
733
|
+
track(mcpToolName(ctx.productName, "production_orders_transition")),
|
|
734
|
+
{
|
|
735
|
+
title: "Transition Production Order",
|
|
736
|
+
description: "Advance a production order through its execution lifecycle (PLANNED \u2192 RELEASED \u2192 IN_PROGRESS \u2192 COMPLETED \u2192 CLOSED, or PAUSED/CANCELLED). Transitions are validated server-side.",
|
|
737
|
+
inputSchema: {
|
|
738
|
+
id: zod.z.string().describe("Production order ID"),
|
|
739
|
+
status: ProductionOrderStatusEnum.describe("Target status")
|
|
740
|
+
}
|
|
741
|
+
},
|
|
742
|
+
async ({ id, status }) => {
|
|
743
|
+
try {
|
|
744
|
+
const data = await ctx.gql.workspace.request(TRANSITION_PRODUCTION_ORDER, { id, status });
|
|
745
|
+
return jsonResult(data.manTransitionProductionOrder);
|
|
746
|
+
} catch (error) {
|
|
747
|
+
ctx.logger.error("manufacturedops production_orders_transition failed", {
|
|
748
|
+
error: String(error)
|
|
749
|
+
});
|
|
750
|
+
return errorResult(
|
|
751
|
+
`Failed to transition production order: ${error instanceof Error ? error.message : String(error)}`
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
);
|
|
756
|
+
server.registerTool(
|
|
757
|
+
track(mcpToolName(ctx.productName, "operations_add")),
|
|
758
|
+
{
|
|
759
|
+
title: "Add Production Operation",
|
|
760
|
+
description: "Add a routing operation (step) to a production order, optionally assigned to a work center.",
|
|
761
|
+
inputSchema: {
|
|
762
|
+
productionOrderId: zod.z.string().min(1).describe("Owning production order ID"),
|
|
763
|
+
name: zod.z.string().min(1).describe('Operation name (e.g. "CNC mill", "Assembly")'),
|
|
764
|
+
sequence: zod.z.number().int().optional().describe("Ordering sequence number"),
|
|
765
|
+
workCenterId: zod.z.string().optional().describe("Assigned work center ID")
|
|
766
|
+
}
|
|
767
|
+
},
|
|
768
|
+
async ({ productionOrderId, name, sequence, workCenterId }) => {
|
|
769
|
+
try {
|
|
770
|
+
const input = { productionOrderId, name };
|
|
771
|
+
if (sequence !== void 0) input.sequence = sequence;
|
|
772
|
+
if (workCenterId !== void 0) input.workCenterId = workCenterId;
|
|
773
|
+
const data = await ctx.gql.workspace.request(ADD_OPERATION, { input });
|
|
774
|
+
return jsonResult(data.manAddOperation);
|
|
775
|
+
} catch (error) {
|
|
776
|
+
ctx.logger.error("manufacturedops operations_add failed", { error: String(error) });
|
|
777
|
+
return errorResult(
|
|
778
|
+
`Failed to add operation: ${error instanceof Error ? error.message : String(error)}`
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
);
|
|
783
|
+
server.registerTool(
|
|
784
|
+
track(mcpToolName(ctx.productName, "operations_record")),
|
|
785
|
+
{
|
|
786
|
+
title: "Record Production Operation",
|
|
787
|
+
description: "Record progress against a routing operation (setup/run minutes, good/scrap counts, status).",
|
|
788
|
+
inputSchema: {
|
|
789
|
+
id: zod.z.string().describe("Operation ID"),
|
|
790
|
+
status: OperationStatusEnum.optional().describe("Updated operation status"),
|
|
791
|
+
setupMinutes: zod.z.number().int().optional().describe("Setup minutes logged"),
|
|
792
|
+
runMinutes: zod.z.number().int().optional().describe("Run minutes logged"),
|
|
793
|
+
goodCount: zod.z.number().optional().describe("Good units produced"),
|
|
794
|
+
scrapCount: zod.z.number().optional().describe("Scrap units produced")
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
async ({ id, status, setupMinutes, runMinutes, goodCount, scrapCount }) => {
|
|
798
|
+
try {
|
|
799
|
+
const input = {};
|
|
800
|
+
if (status !== void 0) input.status = status;
|
|
801
|
+
if (setupMinutes !== void 0) input.setupMinutes = setupMinutes;
|
|
802
|
+
if (runMinutes !== void 0) input.runMinutes = runMinutes;
|
|
803
|
+
if (goodCount !== void 0) input.goodCount = goodCount;
|
|
804
|
+
if (scrapCount !== void 0) input.scrapCount = scrapCount;
|
|
805
|
+
if (Object.keys(input).length === 0) {
|
|
806
|
+
return errorResult(
|
|
807
|
+
"No updates specified \u2014 provide at least one of status/setupMinutes/runMinutes/goodCount/scrapCount"
|
|
808
|
+
);
|
|
809
|
+
}
|
|
810
|
+
const data = await ctx.gql.workspace.request(RECORD_OPERATION, { id, input });
|
|
811
|
+
return jsonResult(data.manRecordOperation);
|
|
812
|
+
} catch (error) {
|
|
813
|
+
ctx.logger.error("manufacturedops operations_record failed", { error: String(error) });
|
|
814
|
+
return errorResult(
|
|
815
|
+
`Failed to record operation: ${error instanceof Error ? error.message : String(error)}`
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
);
|
|
820
|
+
server.registerTool(
|
|
821
|
+
track(mcpToolName(ctx.productName, "material_lots_list")),
|
|
822
|
+
{
|
|
823
|
+
title: "List Material Lots",
|
|
824
|
+
description: "List material lots (paginated), optionally filtered by production order and/or status.",
|
|
825
|
+
inputSchema: {
|
|
826
|
+
productionOrderId: zod.z.string().optional().describe("Filter by production order ID"),
|
|
827
|
+
status: MaterialLotStatusEnum.optional().describe("Filter by lot status"),
|
|
828
|
+
...PaginationFields
|
|
829
|
+
}
|
|
830
|
+
},
|
|
831
|
+
async ({ productionOrderId, status, limit, offset, cursor }) => {
|
|
832
|
+
try {
|
|
833
|
+
const data = await ctx.gql.workspace.request(LIST_MATERIAL_LOTS, {
|
|
834
|
+
productionOrderId,
|
|
835
|
+
status,
|
|
836
|
+
pagination: buildPagination({ limit, offset, cursor })
|
|
837
|
+
});
|
|
838
|
+
return jsonResult(data.manListMaterialLots);
|
|
839
|
+
} catch (error) {
|
|
840
|
+
ctx.logger.error("manufacturedops material_lots_list failed", { error: String(error) });
|
|
841
|
+
return errorResult(
|
|
842
|
+
`Failed to list material lots: ${error instanceof Error ? error.message : String(error)}`
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
);
|
|
847
|
+
server.registerTool(
|
|
848
|
+
track(mcpToolName(ctx.productName, "material_lots_create")),
|
|
849
|
+
{
|
|
850
|
+
title: "Create Material Lot",
|
|
851
|
+
description: "Receive / create a traceable material lot. `lotNumber`, `materialSku`, and `materialName` are required.",
|
|
852
|
+
inputSchema: {
|
|
853
|
+
lotNumber: zod.z.string().min(1).describe("Unique lot number"),
|
|
854
|
+
materialSku: zod.z.string().min(1).describe("Material SKU"),
|
|
855
|
+
materialName: zod.z.string().min(1).describe("Material display name"),
|
|
856
|
+
quantity: zod.z.number().optional().describe("Received quantity"),
|
|
857
|
+
uom: zod.z.string().optional().describe("Unit of measure"),
|
|
858
|
+
supplier: zod.z.string().optional().describe("Supplier name"),
|
|
859
|
+
receivedAt: IsoDateTime.optional().describe("Received timestamp (ISO-8601)"),
|
|
860
|
+
expiresAt: IsoDateTime.optional().describe("Expiry timestamp (ISO-8601)"),
|
|
861
|
+
productionOrderId: zod.z.string().optional().describe("Allocated production order ID"),
|
|
862
|
+
parentLotId: zod.z.string().optional().describe("Parent lot ID (for genealogy/splits)"),
|
|
863
|
+
tags: zod.z.array(zod.z.string()).optional().describe("Free-form tags")
|
|
864
|
+
}
|
|
865
|
+
},
|
|
866
|
+
async ({
|
|
867
|
+
lotNumber,
|
|
868
|
+
materialSku,
|
|
869
|
+
materialName,
|
|
870
|
+
quantity,
|
|
871
|
+
uom,
|
|
872
|
+
supplier,
|
|
873
|
+
receivedAt,
|
|
874
|
+
expiresAt,
|
|
875
|
+
productionOrderId,
|
|
876
|
+
parentLotId,
|
|
877
|
+
tags
|
|
878
|
+
}) => {
|
|
879
|
+
try {
|
|
880
|
+
const input = { lotNumber, materialSku, materialName };
|
|
881
|
+
if (quantity !== void 0) input.quantity = quantity;
|
|
882
|
+
if (uom !== void 0) input.uom = uom;
|
|
883
|
+
if (supplier !== void 0) input.supplier = supplier;
|
|
884
|
+
if (receivedAt !== void 0) input.receivedAt = receivedAt;
|
|
885
|
+
if (expiresAt !== void 0) input.expiresAt = expiresAt;
|
|
886
|
+
if (productionOrderId !== void 0) input.productionOrderId = productionOrderId;
|
|
887
|
+
if (parentLotId !== void 0) input.parentLotId = parentLotId;
|
|
888
|
+
if (tags !== void 0) input.tags = tags;
|
|
889
|
+
const data = await ctx.gql.workspace.request(CREATE_MATERIAL_LOT, { input });
|
|
890
|
+
return jsonResult(data.manCreateMaterialLot);
|
|
891
|
+
} catch (error) {
|
|
892
|
+
ctx.logger.error("manufacturedops material_lots_create failed", { error: String(error) });
|
|
893
|
+
return errorResult(
|
|
894
|
+
`Failed to create material lot: ${error instanceof Error ? error.message : String(error)}`
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
);
|
|
899
|
+
server.registerTool(
|
|
900
|
+
track(mcpToolName(ctx.productName, "material_lots_set_status")),
|
|
901
|
+
{
|
|
902
|
+
title: "Set Material Lot Status",
|
|
903
|
+
description: "Change a material lot status (AVAILABLE/ALLOCATED/CONSUMED/QUARANTINED/SCRAPPED).",
|
|
904
|
+
inputSchema: {
|
|
905
|
+
id: zod.z.string().describe("Material lot ID"),
|
|
906
|
+
status: MaterialLotStatusEnum.describe("Target lot status")
|
|
907
|
+
}
|
|
908
|
+
},
|
|
909
|
+
async ({ id, status }) => {
|
|
910
|
+
try {
|
|
911
|
+
const data = await ctx.gql.workspace.request(SET_MATERIAL_LOT_STATUS, { id, status });
|
|
912
|
+
return jsonResult(data.manSetMaterialLotStatus);
|
|
913
|
+
} catch (error) {
|
|
914
|
+
ctx.logger.error("manufacturedops material_lots_set_status failed", {
|
|
915
|
+
error: String(error)
|
|
916
|
+
});
|
|
917
|
+
return errorResult(
|
|
918
|
+
`Failed to set material lot status: ${error instanceof Error ? error.message : String(error)}`
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
function registerQualityTools(server, ctx, track) {
|
|
925
|
+
server.registerTool(
|
|
926
|
+
track(mcpToolName(ctx.productName, "inspections_get")),
|
|
927
|
+
{
|
|
928
|
+
title: "Get Quality Inspection",
|
|
929
|
+
description: "Fetch a single quality inspection by ID, including its plan and any raised non-conformances.",
|
|
930
|
+
inputSchema: { id: zod.z.string().describe("Inspection ID") }
|
|
931
|
+
},
|
|
932
|
+
async ({ id }) => {
|
|
933
|
+
try {
|
|
934
|
+
const data = await ctx.gql.workspace.request(GET_INSPECTION, { id });
|
|
935
|
+
if (!data.manInspection) return errorResult(`Inspection ${id} not found`);
|
|
936
|
+
return jsonResult(data.manInspection);
|
|
937
|
+
} catch (error) {
|
|
938
|
+
ctx.logger.error("manufacturedops inspections_get failed", { error: String(error) });
|
|
939
|
+
return errorResult(
|
|
940
|
+
`Failed to get inspection: ${error instanceof Error ? error.message : String(error)}`
|
|
941
|
+
);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
);
|
|
945
|
+
server.registerTool(
|
|
946
|
+
track(mcpToolName(ctx.productName, "inspections_list")),
|
|
947
|
+
{
|
|
948
|
+
title: "List Quality Inspections",
|
|
949
|
+
description: "List quality inspections (paginated), optionally filtered by production order and/or result.",
|
|
950
|
+
inputSchema: {
|
|
951
|
+
productionOrderId: zod.z.string().optional().describe("Filter by production order ID"),
|
|
952
|
+
result: InspectionResultEnum.optional().describe("Filter by inspection result"),
|
|
953
|
+
...PaginationFields
|
|
954
|
+
}
|
|
955
|
+
},
|
|
956
|
+
async ({ productionOrderId, result, limit, offset, cursor }) => {
|
|
957
|
+
try {
|
|
958
|
+
const data = await ctx.gql.workspace.request(LIST_INSPECTIONS, {
|
|
959
|
+
productionOrderId,
|
|
960
|
+
result,
|
|
961
|
+
pagination: buildPagination({ limit, offset, cursor })
|
|
962
|
+
});
|
|
963
|
+
return jsonResult(data.manListInspections);
|
|
964
|
+
} catch (error) {
|
|
965
|
+
ctx.logger.error("manufacturedops inspections_list failed", { error: String(error) });
|
|
966
|
+
return errorResult(
|
|
967
|
+
`Failed to list inspections: ${error instanceof Error ? error.message : String(error)}`
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
);
|
|
972
|
+
server.registerTool(
|
|
973
|
+
track(mcpToolName(ctx.productName, "inspections_record")),
|
|
974
|
+
{
|
|
975
|
+
title: "Record Quality Inspection",
|
|
976
|
+
description: "Record a quality inspection result against a production order, material lot, or work center.",
|
|
977
|
+
inputSchema: {
|
|
978
|
+
stage: InspectionStageEnum.optional().describe("Inspection stage (default IN_PROCESS)"),
|
|
979
|
+
result: InspectionResultEnum.optional().describe("Inspection result (default PENDING)"),
|
|
980
|
+
sampleSize: zod.z.number().int().optional().describe("Number of units sampled"),
|
|
981
|
+
defectCount: zod.z.number().int().optional().describe("Number of defective units found"),
|
|
982
|
+
measurements: JsonObject.optional().describe("Measurement readings as JSON"),
|
|
983
|
+
notes: zod.z.string().optional().describe("Inspector notes"),
|
|
984
|
+
inspectionPlanId: zod.z.string().optional().describe("Inspection plan ID"),
|
|
985
|
+
productionOrderId: zod.z.string().optional().describe("Production order ID"),
|
|
986
|
+
materialLotId: zod.z.string().optional().describe("Material lot ID"),
|
|
987
|
+
workCenterId: zod.z.string().optional().describe("Work center ID"),
|
|
988
|
+
inspectedAt: IsoDateTime.optional().describe("Inspection timestamp (ISO-8601)")
|
|
989
|
+
}
|
|
990
|
+
},
|
|
991
|
+
async ({
|
|
992
|
+
stage,
|
|
993
|
+
result,
|
|
994
|
+
sampleSize,
|
|
995
|
+
defectCount,
|
|
996
|
+
measurements,
|
|
997
|
+
notes,
|
|
998
|
+
inspectionPlanId,
|
|
999
|
+
productionOrderId,
|
|
1000
|
+
materialLotId,
|
|
1001
|
+
workCenterId,
|
|
1002
|
+
inspectedAt
|
|
1003
|
+
}) => {
|
|
1004
|
+
try {
|
|
1005
|
+
const input = {};
|
|
1006
|
+
if (stage !== void 0) input.stage = stage;
|
|
1007
|
+
if (result !== void 0) input.result = result;
|
|
1008
|
+
if (sampleSize !== void 0) input.sampleSize = sampleSize;
|
|
1009
|
+
if (defectCount !== void 0) input.defectCount = defectCount;
|
|
1010
|
+
if (measurements !== void 0) input.measurements = measurements;
|
|
1011
|
+
if (notes !== void 0) input.notes = notes;
|
|
1012
|
+
if (inspectionPlanId !== void 0) input.inspectionPlanId = inspectionPlanId;
|
|
1013
|
+
if (productionOrderId !== void 0) input.productionOrderId = productionOrderId;
|
|
1014
|
+
if (materialLotId !== void 0) input.materialLotId = materialLotId;
|
|
1015
|
+
if (workCenterId !== void 0) input.workCenterId = workCenterId;
|
|
1016
|
+
if (inspectedAt !== void 0) input.inspectedAt = inspectedAt;
|
|
1017
|
+
const data = await ctx.gql.workspace.request(RECORD_INSPECTION, { input });
|
|
1018
|
+
return jsonResult(data.manRecordInspection);
|
|
1019
|
+
} catch (error) {
|
|
1020
|
+
ctx.logger.error("manufacturedops inspections_record failed", { error: String(error) });
|
|
1021
|
+
return errorResult(
|
|
1022
|
+
`Failed to record inspection: ${error instanceof Error ? error.message : String(error)}`
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
);
|
|
1027
|
+
server.registerTool(
|
|
1028
|
+
track(mcpToolName(ctx.productName, "inspection_plans_list")),
|
|
1029
|
+
{
|
|
1030
|
+
title: "List Inspection Plans",
|
|
1031
|
+
description: "List inspection plans, optionally filtered by active state and/or stage.",
|
|
1032
|
+
inputSchema: {
|
|
1033
|
+
isActive: zod.z.boolean().optional().describe("Only active (true) or inactive (false) plans"),
|
|
1034
|
+
stage: InspectionStageEnum.optional().describe("Filter by inspection stage")
|
|
1035
|
+
}
|
|
1036
|
+
},
|
|
1037
|
+
async ({ isActive, stage }) => {
|
|
1038
|
+
try {
|
|
1039
|
+
const data = await ctx.gql.workspace.request(LIST_INSPECTION_PLANS, { isActive, stage });
|
|
1040
|
+
return jsonResult(data.manListInspectionPlans);
|
|
1041
|
+
} catch (error) {
|
|
1042
|
+
ctx.logger.error("manufacturedops inspection_plans_list failed", { error: String(error) });
|
|
1043
|
+
return errorResult(
|
|
1044
|
+
`Failed to list inspection plans: ${error instanceof Error ? error.message : String(error)}`
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
);
|
|
1049
|
+
server.registerTool(
|
|
1050
|
+
track(mcpToolName(ctx.productName, "inspection_plans_create")),
|
|
1051
|
+
{
|
|
1052
|
+
title: "Create Inspection Plan",
|
|
1053
|
+
description: "Create an inspection plan (characteristics checklist). `code` and `name` are required.",
|
|
1054
|
+
inputSchema: {
|
|
1055
|
+
code: zod.z.string().min(1).describe("Unique inspection-plan code"),
|
|
1056
|
+
name: zod.z.string().min(1).describe("Plan name"),
|
|
1057
|
+
stage: InspectionStageEnum.optional().describe("Plan stage (default IN_PROCESS)"),
|
|
1058
|
+
description: zod.z.string().optional().describe("Description"),
|
|
1059
|
+
productSku: zod.z.string().optional().describe("Product SKU this plan applies to"),
|
|
1060
|
+
characteristics: JsonObject.optional().describe("Inspection characteristics as JSON")
|
|
1061
|
+
}
|
|
1062
|
+
},
|
|
1063
|
+
async ({ code, name, stage, description, productSku, characteristics }) => {
|
|
1064
|
+
try {
|
|
1065
|
+
const input = { code, name };
|
|
1066
|
+
if (stage !== void 0) input.stage = stage;
|
|
1067
|
+
if (description !== void 0) input.description = description;
|
|
1068
|
+
if (productSku !== void 0) input.productSku = productSku;
|
|
1069
|
+
if (characteristics !== void 0) input.characteristics = characteristics;
|
|
1070
|
+
const data = await ctx.gql.workspace.request(CREATE_INSPECTION_PLAN, { input });
|
|
1071
|
+
return jsonResult(data.manCreateInspectionPlan);
|
|
1072
|
+
} catch (error) {
|
|
1073
|
+
ctx.logger.error("manufacturedops inspection_plans_create failed", {
|
|
1074
|
+
error: String(error)
|
|
1075
|
+
});
|
|
1076
|
+
return errorResult(
|
|
1077
|
+
`Failed to create inspection plan: ${error instanceof Error ? error.message : String(error)}`
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
);
|
|
1082
|
+
server.registerTool(
|
|
1083
|
+
track(mcpToolName(ctx.productName, "non_conformances_get")),
|
|
1084
|
+
{
|
|
1085
|
+
title: "Get Non-Conformance",
|
|
1086
|
+
description: "Fetch a single non-conformance (NCR) by ID, including its originating inspection.",
|
|
1087
|
+
inputSchema: { id: zod.z.string().describe("Non-conformance ID") }
|
|
1088
|
+
},
|
|
1089
|
+
async ({ id }) => {
|
|
1090
|
+
try {
|
|
1091
|
+
const data = await ctx.gql.workspace.request(GET_NON_CONFORMANCE, { id });
|
|
1092
|
+
if (!data.manNonConformance) return errorResult(`Non-conformance ${id} not found`);
|
|
1093
|
+
return jsonResult(data.manNonConformance);
|
|
1094
|
+
} catch (error) {
|
|
1095
|
+
ctx.logger.error("manufacturedops non_conformances_get failed", { error: String(error) });
|
|
1096
|
+
return errorResult(
|
|
1097
|
+
`Failed to get non-conformance: ${error instanceof Error ? error.message : String(error)}`
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
);
|
|
1102
|
+
server.registerTool(
|
|
1103
|
+
track(mcpToolName(ctx.productName, "non_conformances_list")),
|
|
1104
|
+
{
|
|
1105
|
+
title: "List Non-Conformances",
|
|
1106
|
+
description: "List non-conformances (paginated), optionally filtered by severity and/or status.",
|
|
1107
|
+
inputSchema: {
|
|
1108
|
+
severity: SeverityEnum.optional().describe("Filter by severity"),
|
|
1109
|
+
status: NonConformanceStatusEnum.optional().describe("Filter by status"),
|
|
1110
|
+
...PaginationFields
|
|
1111
|
+
}
|
|
1112
|
+
},
|
|
1113
|
+
async ({ severity, status, limit, offset, cursor }) => {
|
|
1114
|
+
try {
|
|
1115
|
+
const data = await ctx.gql.workspace.request(LIST_NON_CONFORMANCES, {
|
|
1116
|
+
severity,
|
|
1117
|
+
status,
|
|
1118
|
+
pagination: buildPagination({ limit, offset, cursor })
|
|
1119
|
+
});
|
|
1120
|
+
return jsonResult(data.manListNonConformances);
|
|
1121
|
+
} catch (error) {
|
|
1122
|
+
ctx.logger.error("manufacturedops non_conformances_list failed", { error: String(error) });
|
|
1123
|
+
return errorResult(
|
|
1124
|
+
`Failed to list non-conformances: ${error instanceof Error ? error.message : String(error)}`
|
|
1125
|
+
);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
);
|
|
1129
|
+
server.registerTool(
|
|
1130
|
+
track(mcpToolName(ctx.productName, "non_conformances_raise")),
|
|
1131
|
+
{
|
|
1132
|
+
title: "Raise Non-Conformance",
|
|
1133
|
+
description: "Raise a non-conformance report (NCR). `ncrNumber` and `title` are required; optionally link an inspection.",
|
|
1134
|
+
inputSchema: {
|
|
1135
|
+
ncrNumber: zod.z.string().min(1).describe("Unique NCR number"),
|
|
1136
|
+
title: zod.z.string().min(1).describe("NCR title/summary"),
|
|
1137
|
+
description: zod.z.string().optional().describe("Detailed description"),
|
|
1138
|
+
severity: SeverityEnum.optional().describe("Severity (default MINOR)"),
|
|
1139
|
+
disposition: NonConformanceDispositionEnum.optional().describe(
|
|
1140
|
+
"Disposition (default OPEN)"
|
|
1141
|
+
),
|
|
1142
|
+
quantityAffected: zod.z.number().optional().describe("Quantity of units affected"),
|
|
1143
|
+
inspectionId: zod.z.string().optional().describe("Originating inspection ID"),
|
|
1144
|
+
ownerId: zod.z.string().optional().describe("Owner (assignee) user ID"),
|
|
1145
|
+
dueDate: IsoDateTime.optional().describe("Resolution due date (ISO-8601)"),
|
|
1146
|
+
tags: zod.z.array(zod.z.string()).optional().describe("Free-form tags")
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
async ({
|
|
1150
|
+
ncrNumber,
|
|
1151
|
+
title,
|
|
1152
|
+
description,
|
|
1153
|
+
severity,
|
|
1154
|
+
disposition,
|
|
1155
|
+
quantityAffected,
|
|
1156
|
+
inspectionId,
|
|
1157
|
+
ownerId,
|
|
1158
|
+
dueDate,
|
|
1159
|
+
tags
|
|
1160
|
+
}) => {
|
|
1161
|
+
try {
|
|
1162
|
+
const input = { ncrNumber, title };
|
|
1163
|
+
if (description !== void 0) input.description = description;
|
|
1164
|
+
if (severity !== void 0) input.severity = severity;
|
|
1165
|
+
if (disposition !== void 0) input.disposition = disposition;
|
|
1166
|
+
if (quantityAffected !== void 0) input.quantityAffected = quantityAffected;
|
|
1167
|
+
if (inspectionId !== void 0) input.inspectionId = inspectionId;
|
|
1168
|
+
if (ownerId !== void 0) input.ownerId = ownerId;
|
|
1169
|
+
if (dueDate !== void 0) input.dueDate = dueDate;
|
|
1170
|
+
if (tags !== void 0) input.tags = tags;
|
|
1171
|
+
const data = await ctx.gql.workspace.request(RAISE_NON_CONFORMANCE, { input });
|
|
1172
|
+
return jsonResult(data.manRaiseNonConformance);
|
|
1173
|
+
} catch (error) {
|
|
1174
|
+
ctx.logger.error("manufacturedops non_conformances_raise failed", { error: String(error) });
|
|
1175
|
+
return errorResult(
|
|
1176
|
+
`Failed to raise non-conformance: ${error instanceof Error ? error.message : String(error)}`
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
);
|
|
1181
|
+
server.registerTool(
|
|
1182
|
+
track(mcpToolName(ctx.productName, "non_conformances_update")),
|
|
1183
|
+
{
|
|
1184
|
+
title: "Update Non-Conformance",
|
|
1185
|
+
description: "Update a non-conformance (root cause, corrective action, disposition, owner, \u2026). At least one mutable field must be provided.",
|
|
1186
|
+
inputSchema: {
|
|
1187
|
+
id: zod.z.string().describe("Non-conformance ID"),
|
|
1188
|
+
title: zod.z.string().optional().describe("Updated title"),
|
|
1189
|
+
description: zod.z.string().optional().describe("Updated description"),
|
|
1190
|
+
severity: SeverityEnum.optional().describe("Updated severity"),
|
|
1191
|
+
disposition: NonConformanceDispositionEnum.optional().describe("Updated disposition"),
|
|
1192
|
+
quantityAffected: zod.z.number().optional().describe("Updated quantity affected"),
|
|
1193
|
+
rootCause: zod.z.string().optional().describe("Root-cause analysis"),
|
|
1194
|
+
correctiveAction: zod.z.string().optional().describe("Corrective/preventive action (CAPA)"),
|
|
1195
|
+
ownerId: zod.z.string().optional().describe("Updated owner user ID"),
|
|
1196
|
+
dueDate: IsoDateTime.optional().describe("Updated due date (ISO-8601)"),
|
|
1197
|
+
tags: zod.z.array(zod.z.string()).optional().describe("Updated tags")
|
|
1198
|
+
}
|
|
1199
|
+
},
|
|
1200
|
+
async ({
|
|
1201
|
+
id,
|
|
1202
|
+
title,
|
|
1203
|
+
description,
|
|
1204
|
+
severity,
|
|
1205
|
+
disposition,
|
|
1206
|
+
quantityAffected,
|
|
1207
|
+
rootCause,
|
|
1208
|
+
correctiveAction,
|
|
1209
|
+
ownerId,
|
|
1210
|
+
dueDate,
|
|
1211
|
+
tags
|
|
1212
|
+
}) => {
|
|
1213
|
+
try {
|
|
1214
|
+
const input = {};
|
|
1215
|
+
if (title !== void 0) input.title = title;
|
|
1216
|
+
if (description !== void 0) input.description = description;
|
|
1217
|
+
if (severity !== void 0) input.severity = severity;
|
|
1218
|
+
if (disposition !== void 0) input.disposition = disposition;
|
|
1219
|
+
if (quantityAffected !== void 0) input.quantityAffected = quantityAffected;
|
|
1220
|
+
if (rootCause !== void 0) input.rootCause = rootCause;
|
|
1221
|
+
if (correctiveAction !== void 0) input.correctiveAction = correctiveAction;
|
|
1222
|
+
if (ownerId !== void 0) input.ownerId = ownerId;
|
|
1223
|
+
if (dueDate !== void 0) input.dueDate = dueDate;
|
|
1224
|
+
if (tags !== void 0) input.tags = tags;
|
|
1225
|
+
if (Object.keys(input).length === 0) {
|
|
1226
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field");
|
|
1227
|
+
}
|
|
1228
|
+
const data = await ctx.gql.workspace.request(UPDATE_NON_CONFORMANCE, { id, input });
|
|
1229
|
+
return jsonResult(data.manUpdateNonConformance);
|
|
1230
|
+
} catch (error) {
|
|
1231
|
+
ctx.logger.error("manufacturedops non_conformances_update failed", {
|
|
1232
|
+
error: String(error)
|
|
1233
|
+
});
|
|
1234
|
+
return errorResult(
|
|
1235
|
+
`Failed to update non-conformance: ${error instanceof Error ? error.message : String(error)}`
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
);
|
|
1240
|
+
server.registerTool(
|
|
1241
|
+
track(mcpToolName(ctx.productName, "non_conformances_transition")),
|
|
1242
|
+
{
|
|
1243
|
+
title: "Transition Non-Conformance",
|
|
1244
|
+
description: "Advance an NCR/CAPA through its resolution lifecycle (OPEN \u2192 INVESTIGATING \u2192 CONTAINED \u2192 CORRECTIVE_ACTION \u2192 CLOSED). Transitions are validated server-side.",
|
|
1245
|
+
inputSchema: {
|
|
1246
|
+
id: zod.z.string().describe("Non-conformance ID"),
|
|
1247
|
+
status: NonConformanceStatusEnum.describe("Target status")
|
|
1248
|
+
}
|
|
1249
|
+
},
|
|
1250
|
+
async ({ id, status }) => {
|
|
1251
|
+
try {
|
|
1252
|
+
const data = await ctx.gql.workspace.request(TRANSITION_NON_CONFORMANCE, { id, status });
|
|
1253
|
+
return jsonResult(data.manTransitionNonConformance);
|
|
1254
|
+
} catch (error) {
|
|
1255
|
+
ctx.logger.error("manufacturedops non_conformances_transition failed", {
|
|
1256
|
+
error: String(error)
|
|
1257
|
+
});
|
|
1258
|
+
return errorResult(
|
|
1259
|
+
`Failed to transition non-conformance: ${error instanceof Error ? error.message : String(error)}`
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
);
|
|
1264
|
+
}
|
|
1265
|
+
function registerMaintenanceTools(server, ctx, track) {
|
|
1266
|
+
server.registerTool(
|
|
1267
|
+
track(mcpToolName(ctx.productName, "assets_get")),
|
|
1268
|
+
{
|
|
1269
|
+
title: "Get Asset",
|
|
1270
|
+
description: "Fetch a single maintainable asset by ID, including its maintenance work orders.",
|
|
1271
|
+
inputSchema: { id: zod.z.string().describe("Asset ID") }
|
|
1272
|
+
},
|
|
1273
|
+
async ({ id }) => {
|
|
1274
|
+
try {
|
|
1275
|
+
const data = await ctx.gql.workspace.request(GET_ASSET, { id });
|
|
1276
|
+
if (!data.manAsset) return errorResult(`Asset ${id} not found`);
|
|
1277
|
+
return jsonResult(data.manAsset);
|
|
1278
|
+
} catch (error) {
|
|
1279
|
+
ctx.logger.error("manufacturedops assets_get failed", { error: String(error) });
|
|
1280
|
+
return errorResult(
|
|
1281
|
+
`Failed to get asset: ${error instanceof Error ? error.message : String(error)}`
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
);
|
|
1286
|
+
server.registerTool(
|
|
1287
|
+
track(mcpToolName(ctx.productName, "assets_list")),
|
|
1288
|
+
{
|
|
1289
|
+
title: "List Assets",
|
|
1290
|
+
description: "List maintainable assets (paginated), optionally filtered by status and/or work center.",
|
|
1291
|
+
inputSchema: {
|
|
1292
|
+
status: AssetStatusEnum.optional().describe("Filter by asset status"),
|
|
1293
|
+
workCenterId: zod.z.string().optional().describe("Filter by work center ID"),
|
|
1294
|
+
...PaginationFields
|
|
1295
|
+
}
|
|
1296
|
+
},
|
|
1297
|
+
async ({ status, workCenterId, limit, offset, cursor }) => {
|
|
1298
|
+
try {
|
|
1299
|
+
const data = await ctx.gql.workspace.request(LIST_ASSETS, {
|
|
1300
|
+
status,
|
|
1301
|
+
workCenterId,
|
|
1302
|
+
pagination: buildPagination({ limit, offset, cursor })
|
|
1303
|
+
});
|
|
1304
|
+
return jsonResult(data.manListAssets);
|
|
1305
|
+
} catch (error) {
|
|
1306
|
+
ctx.logger.error("manufacturedops assets_list failed", { error: String(error) });
|
|
1307
|
+
return errorResult(
|
|
1308
|
+
`Failed to list assets: ${error instanceof Error ? error.message : String(error)}`
|
|
1309
|
+
);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
);
|
|
1313
|
+
server.registerTool(
|
|
1314
|
+
track(mcpToolName(ctx.productName, "assets_create")),
|
|
1315
|
+
{
|
|
1316
|
+
title: "Create Asset",
|
|
1317
|
+
description: "Register a maintainable asset (machine/equipment). `assetTag` and `name` are required.",
|
|
1318
|
+
inputSchema: {
|
|
1319
|
+
assetTag: zod.z.string().min(1).describe("Unique asset tag"),
|
|
1320
|
+
name: zod.z.string().min(1).describe("Asset display name"),
|
|
1321
|
+
manufacturer: zod.z.string().optional().describe("Manufacturer"),
|
|
1322
|
+
model: zod.z.string().optional().describe("Model"),
|
|
1323
|
+
serialNumber: zod.z.string().optional().describe("Serial number"),
|
|
1324
|
+
pmIntervalHours: zod.z.number().optional().describe("Preventive-maintenance interval (hours)"),
|
|
1325
|
+
runningHours: zod.z.number().optional().describe("Current running hours"),
|
|
1326
|
+
workCenterId: zod.z.string().optional().describe("Host work center ID"),
|
|
1327
|
+
installedAt: IsoDateTime.optional().describe("Install timestamp (ISO-8601)"),
|
|
1328
|
+
tags: zod.z.array(zod.z.string()).optional().describe("Free-form tags")
|
|
1329
|
+
}
|
|
1330
|
+
},
|
|
1331
|
+
async ({
|
|
1332
|
+
assetTag,
|
|
1333
|
+
name,
|
|
1334
|
+
manufacturer,
|
|
1335
|
+
model,
|
|
1336
|
+
serialNumber,
|
|
1337
|
+
pmIntervalHours,
|
|
1338
|
+
runningHours,
|
|
1339
|
+
workCenterId,
|
|
1340
|
+
installedAt,
|
|
1341
|
+
tags
|
|
1342
|
+
}) => {
|
|
1343
|
+
try {
|
|
1344
|
+
const input = { assetTag, name };
|
|
1345
|
+
if (manufacturer !== void 0) input.manufacturer = manufacturer;
|
|
1346
|
+
if (model !== void 0) input.model = model;
|
|
1347
|
+
if (serialNumber !== void 0) input.serialNumber = serialNumber;
|
|
1348
|
+
if (pmIntervalHours !== void 0) input.pmIntervalHours = pmIntervalHours;
|
|
1349
|
+
if (runningHours !== void 0) input.runningHours = runningHours;
|
|
1350
|
+
if (workCenterId !== void 0) input.workCenterId = workCenterId;
|
|
1351
|
+
if (installedAt !== void 0) input.installedAt = installedAt;
|
|
1352
|
+
if (tags !== void 0) input.tags = tags;
|
|
1353
|
+
const data = await ctx.gql.workspace.request(CREATE_ASSET, { input });
|
|
1354
|
+
return jsonResult(data.manCreateAsset);
|
|
1355
|
+
} catch (error) {
|
|
1356
|
+
ctx.logger.error("manufacturedops assets_create failed", { error: String(error) });
|
|
1357
|
+
return errorResult(
|
|
1358
|
+
`Failed to create asset: ${error instanceof Error ? error.message : String(error)}`
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
);
|
|
1363
|
+
server.registerTool(
|
|
1364
|
+
track(mcpToolName(ctx.productName, "assets_update")),
|
|
1365
|
+
{
|
|
1366
|
+
title: "Update Asset",
|
|
1367
|
+
description: "Update a maintainable asset (status, running hours, PM interval, \u2026). At least one mutable field must be provided.",
|
|
1368
|
+
inputSchema: {
|
|
1369
|
+
id: zod.z.string().describe("Asset ID"),
|
|
1370
|
+
name: zod.z.string().optional().describe("Updated name"),
|
|
1371
|
+
manufacturer: zod.z.string().optional().describe("Updated manufacturer"),
|
|
1372
|
+
model: zod.z.string().optional().describe("Updated model"),
|
|
1373
|
+
serialNumber: zod.z.string().optional().describe("Updated serial number"),
|
|
1374
|
+
status: AssetStatusEnum.optional().describe("Updated asset status"),
|
|
1375
|
+
pmIntervalHours: zod.z.number().optional().describe("Updated PM interval (hours)"),
|
|
1376
|
+
runningHours: zod.z.number().optional().describe("Updated running hours"),
|
|
1377
|
+
workCenterId: zod.z.string().optional().describe("Updated host work center ID"),
|
|
1378
|
+
tags: zod.z.array(zod.z.string()).optional().describe("Updated tags")
|
|
1379
|
+
}
|
|
1380
|
+
},
|
|
1381
|
+
async ({
|
|
1382
|
+
id,
|
|
1383
|
+
name,
|
|
1384
|
+
manufacturer,
|
|
1385
|
+
model,
|
|
1386
|
+
serialNumber,
|
|
1387
|
+
status,
|
|
1388
|
+
pmIntervalHours,
|
|
1389
|
+
runningHours,
|
|
1390
|
+
workCenterId,
|
|
1391
|
+
tags
|
|
1392
|
+
}) => {
|
|
1393
|
+
try {
|
|
1394
|
+
const input = {};
|
|
1395
|
+
if (name !== void 0) input.name = name;
|
|
1396
|
+
if (manufacturer !== void 0) input.manufacturer = manufacturer;
|
|
1397
|
+
if (model !== void 0) input.model = model;
|
|
1398
|
+
if (serialNumber !== void 0) input.serialNumber = serialNumber;
|
|
1399
|
+
if (status !== void 0) input.status = status;
|
|
1400
|
+
if (pmIntervalHours !== void 0) input.pmIntervalHours = pmIntervalHours;
|
|
1401
|
+
if (runningHours !== void 0) input.runningHours = runningHours;
|
|
1402
|
+
if (workCenterId !== void 0) input.workCenterId = workCenterId;
|
|
1403
|
+
if (tags !== void 0) input.tags = tags;
|
|
1404
|
+
if (Object.keys(input).length === 0) {
|
|
1405
|
+
return errorResult("No updates specified \u2014 provide at least one mutable field");
|
|
1406
|
+
}
|
|
1407
|
+
const data = await ctx.gql.workspace.request(UPDATE_ASSET, { id, input });
|
|
1408
|
+
return jsonResult(data.manUpdateAsset);
|
|
1409
|
+
} catch (error) {
|
|
1410
|
+
ctx.logger.error("manufacturedops assets_update failed", { error: String(error) });
|
|
1411
|
+
return errorResult(
|
|
1412
|
+
`Failed to update asset: ${error instanceof Error ? error.message : String(error)}`
|
|
1413
|
+
);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
);
|
|
1417
|
+
server.registerTool(
|
|
1418
|
+
track(mcpToolName(ctx.productName, "maintenance_list")),
|
|
1419
|
+
{
|
|
1420
|
+
title: "List Maintenance Work Orders",
|
|
1421
|
+
description: "List maintenance work orders (paginated), optionally filtered by asset and/or status.",
|
|
1422
|
+
inputSchema: {
|
|
1423
|
+
assetId: zod.z.string().optional().describe("Filter by asset ID"),
|
|
1424
|
+
status: MaintenanceStatusEnum.optional().describe("Filter by work-order status"),
|
|
1425
|
+
...PaginationFields
|
|
1426
|
+
}
|
|
1427
|
+
},
|
|
1428
|
+
async ({ assetId, status, limit, offset, cursor }) => {
|
|
1429
|
+
try {
|
|
1430
|
+
const data = await ctx.gql.workspace.request(LIST_MAINTENANCE_WORK_ORDERS, {
|
|
1431
|
+
assetId,
|
|
1432
|
+
status,
|
|
1433
|
+
pagination: buildPagination({ limit, offset, cursor })
|
|
1434
|
+
});
|
|
1435
|
+
return jsonResult(data.manListMaintenanceWorkOrders);
|
|
1436
|
+
} catch (error) {
|
|
1437
|
+
ctx.logger.error("manufacturedops maintenance_list failed", { error: String(error) });
|
|
1438
|
+
return errorResult(
|
|
1439
|
+
`Failed to list maintenance work orders: ${error instanceof Error ? error.message : String(error)}`
|
|
1440
|
+
);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
);
|
|
1444
|
+
server.registerTool(
|
|
1445
|
+
track(mcpToolName(ctx.productName, "maintenance_create")),
|
|
1446
|
+
{
|
|
1447
|
+
title: "Create Maintenance Work Order",
|
|
1448
|
+
description: "Create a maintenance work order against an asset. `woNumber`, `title`, and `assetId` are required.",
|
|
1449
|
+
inputSchema: {
|
|
1450
|
+
woNumber: zod.z.string().min(1).describe("Unique work-order number"),
|
|
1451
|
+
title: zod.z.string().min(1).describe("Work-order title"),
|
|
1452
|
+
assetId: zod.z.string().min(1).describe("Target asset ID"),
|
|
1453
|
+
type: MaintenanceTypeEnum.optional().describe("Maintenance type (default PREVENTIVE)"),
|
|
1454
|
+
description: zod.z.string().optional().describe("Description"),
|
|
1455
|
+
technicianId: zod.z.string().optional().describe("Assigned technician user ID"),
|
|
1456
|
+
scheduledFor: IsoDateTime.optional().describe("Scheduled timestamp (ISO-8601)")
|
|
1457
|
+
}
|
|
1458
|
+
},
|
|
1459
|
+
async ({ woNumber, title, assetId, type, description, technicianId, scheduledFor }) => {
|
|
1460
|
+
try {
|
|
1461
|
+
const input = { woNumber, title, assetId };
|
|
1462
|
+
if (type !== void 0) input.type = type;
|
|
1463
|
+
if (description !== void 0) input.description = description;
|
|
1464
|
+
if (technicianId !== void 0) input.technicianId = technicianId;
|
|
1465
|
+
if (scheduledFor !== void 0) input.scheduledFor = scheduledFor;
|
|
1466
|
+
const data = await ctx.gql.workspace.request(CREATE_MAINTENANCE_WORK_ORDER, { input });
|
|
1467
|
+
return jsonResult(data.manCreateMaintenanceWorkOrder);
|
|
1468
|
+
} catch (error) {
|
|
1469
|
+
ctx.logger.error("manufacturedops maintenance_create failed", { error: String(error) });
|
|
1470
|
+
return errorResult(
|
|
1471
|
+
`Failed to create maintenance work order: ${error instanceof Error ? error.message : String(error)}`
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
);
|
|
1476
|
+
server.registerTool(
|
|
1477
|
+
track(mcpToolName(ctx.productName, "maintenance_transition")),
|
|
1478
|
+
{
|
|
1479
|
+
title: "Transition Maintenance Work Order",
|
|
1480
|
+
description: "Advance a maintenance work order through its lifecycle (SCHEDULED \u2192 IN_PROGRESS \u2192 COMPLETED, or ON_HOLD/CANCELLED). Transitions are validated server-side.",
|
|
1481
|
+
inputSchema: {
|
|
1482
|
+
id: zod.z.string().describe("Maintenance work order ID"),
|
|
1483
|
+
status: MaintenanceStatusEnum.describe("Target status")
|
|
1484
|
+
}
|
|
1485
|
+
},
|
|
1486
|
+
async ({ id, status }) => {
|
|
1487
|
+
try {
|
|
1488
|
+
const data = await ctx.gql.workspace.request(TRANSITION_MAINTENANCE_WORK_ORDER, { id, status });
|
|
1489
|
+
return jsonResult(data.manTransitionMaintenanceWorkOrder);
|
|
1490
|
+
} catch (error) {
|
|
1491
|
+
ctx.logger.error("manufacturedops maintenance_transition failed", {
|
|
1492
|
+
error: String(error)
|
|
1493
|
+
});
|
|
1494
|
+
return errorResult(
|
|
1495
|
+
`Failed to transition maintenance work order: ${error instanceof Error ? error.message : String(error)}`
|
|
1496
|
+
);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
);
|
|
1500
|
+
server.registerTool(
|
|
1501
|
+
track(mcpToolName(ctx.productName, "downtime_list")),
|
|
1502
|
+
{
|
|
1503
|
+
title: "List Downtime Events",
|
|
1504
|
+
description: "List downtime events, optionally filtered by asset, work center, and/or reason.",
|
|
1505
|
+
inputSchema: {
|
|
1506
|
+
assetId: zod.z.string().optional().describe("Filter by asset ID"),
|
|
1507
|
+
workCenterId: zod.z.string().optional().describe("Filter by work center ID"),
|
|
1508
|
+
reason: DowntimeReasonEnum.optional().describe("Filter by downtime reason")
|
|
1509
|
+
}
|
|
1510
|
+
},
|
|
1511
|
+
async ({ assetId, workCenterId, reason }) => {
|
|
1512
|
+
try {
|
|
1513
|
+
const data = await ctx.gql.workspace.request(LIST_DOWNTIME_EVENTS, { assetId, workCenterId, reason });
|
|
1514
|
+
return jsonResult(data.manListDowntimeEvents);
|
|
1515
|
+
} catch (error) {
|
|
1516
|
+
ctx.logger.error("manufacturedops downtime_list failed", { error: String(error) });
|
|
1517
|
+
return errorResult(
|
|
1518
|
+
`Failed to list downtime events: ${error instanceof Error ? error.message : String(error)}`
|
|
1519
|
+
);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
);
|
|
1523
|
+
server.registerTool(
|
|
1524
|
+
track(mcpToolName(ctx.productName, "downtime_record")),
|
|
1525
|
+
{
|
|
1526
|
+
title: "Record Downtime Event",
|
|
1527
|
+
description: "Record a downtime event against an asset, work center, and/or production order. `startedAt` is required.",
|
|
1528
|
+
inputSchema: {
|
|
1529
|
+
startedAt: IsoDateTime.describe("Downtime start timestamp (ISO-8601)"),
|
|
1530
|
+
reason: DowntimeReasonEnum.optional().describe("Downtime reason (default BREAKDOWN)"),
|
|
1531
|
+
description: zod.z.string().optional().describe("Description"),
|
|
1532
|
+
durationMinutes: zod.z.number().int().optional().describe("Duration in minutes"),
|
|
1533
|
+
endedAt: IsoDateTime.optional().describe("Downtime end timestamp (ISO-8601)"),
|
|
1534
|
+
assetId: zod.z.string().optional().describe("Affected asset ID"),
|
|
1535
|
+
workCenterId: zod.z.string().optional().describe("Affected work center ID"),
|
|
1536
|
+
productionOrderId: zod.z.string().optional().describe("Affected production order ID")
|
|
1537
|
+
}
|
|
1538
|
+
},
|
|
1539
|
+
async ({
|
|
1540
|
+
startedAt,
|
|
1541
|
+
reason,
|
|
1542
|
+
description,
|
|
1543
|
+
durationMinutes,
|
|
1544
|
+
endedAt,
|
|
1545
|
+
assetId,
|
|
1546
|
+
workCenterId,
|
|
1547
|
+
productionOrderId
|
|
1548
|
+
}) => {
|
|
1549
|
+
try {
|
|
1550
|
+
const input = { startedAt };
|
|
1551
|
+
if (reason !== void 0) input.reason = reason;
|
|
1552
|
+
if (description !== void 0) input.description = description;
|
|
1553
|
+
if (durationMinutes !== void 0) input.durationMinutes = durationMinutes;
|
|
1554
|
+
if (endedAt !== void 0) input.endedAt = endedAt;
|
|
1555
|
+
if (assetId !== void 0) input.assetId = assetId;
|
|
1556
|
+
if (workCenterId !== void 0) input.workCenterId = workCenterId;
|
|
1557
|
+
if (productionOrderId !== void 0) input.productionOrderId = productionOrderId;
|
|
1558
|
+
const data = await ctx.gql.workspace.request(RECORD_DOWNTIME, { input });
|
|
1559
|
+
return jsonResult(data.manRecordDowntime);
|
|
1560
|
+
} catch (error) {
|
|
1561
|
+
ctx.logger.error("manufacturedops downtime_record failed", { error: String(error) });
|
|
1562
|
+
return errorResult(
|
|
1563
|
+
`Failed to record downtime: ${error instanceof Error ? error.message : String(error)}`
|
|
1564
|
+
);
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
);
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
// src/modules/manufacturedops/tools/health.ts
|
|
1571
|
+
function registerHealthTools(server, ctx, track) {
|
|
1572
|
+
server.registerTool(
|
|
1573
|
+
track(mcpToolName(ctx.productName, "health")),
|
|
1574
|
+
{
|
|
1575
|
+
title: "Manufacturedops Health Check",
|
|
1576
|
+
description: "Public liveness/identity probe for the Manufacturedops subgraph (manHealth). Requires no authentication.",
|
|
1577
|
+
inputSchema: {}
|
|
1578
|
+
},
|
|
1579
|
+
async () => {
|
|
1580
|
+
try {
|
|
1581
|
+
const data = await ctx.gql.workspace.request(HEALTH, {});
|
|
1582
|
+
return jsonResult({ ok: true, status: data.manHealth });
|
|
1583
|
+
} catch (error) {
|
|
1584
|
+
ctx.logger.error("manufacturedops health failed", { error: String(error) });
|
|
1585
|
+
return errorResult(
|
|
1586
|
+
`Manufacturedops health check failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1587
|
+
);
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
);
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
// src/modules/manufacturedops/resources.ts
|
|
1594
|
+
function registerManufacturedopsResources(server, ctx, track) {
|
|
1595
|
+
const schemaUri = track(mcpResourceUri(ctx.productName, "schema"));
|
|
1596
|
+
const ordersUri = track(mcpResourceUri(ctx.productName, "production-orders"));
|
|
1597
|
+
const workCentersUri = track(mcpResourceUri(ctx.productName, "work-centers"));
|
|
1598
|
+
const ncrUri = track(mcpResourceUri(ctx.productName, "non-conformances"));
|
|
1599
|
+
server.registerResource(
|
|
1600
|
+
"manufacturedops-schema",
|
|
1601
|
+
schemaUri,
|
|
1602
|
+
{
|
|
1603
|
+
title: "Manufacturedops Domain Schema",
|
|
1604
|
+
description: "Overview of the Manufacturedops MES domain \u2014 entities, enums, and the per-domain tool surface.",
|
|
1605
|
+
mimeType: "text/plain"
|
|
1606
|
+
},
|
|
1607
|
+
async () => {
|
|
1608
|
+
ctx.logger.debug(`Resource ${schemaUri} requested`);
|
|
1609
|
+
const schema = `# Manufacturedops Domain (Overview)
|
|
1610
|
+
|
|
1611
|
+
Manufacturing Operations Platform (MES): production planning & shop-floor
|
|
1612
|
+
execution, quality & traceability, and maintenance & OEE.
|
|
1613
|
+
|
|
1614
|
+
## Entities
|
|
1615
|
+
- ManWorkCenter \u2014 a machine/line/cell/manual station (has operations)
|
|
1616
|
+
- ManProductionOrder \u2014 a manufacturing order (has operations + material lots)
|
|
1617
|
+
- ManProductionOperation \u2014 a routing step on a production order
|
|
1618
|
+
- ManMaterialLot \u2014 a traceable material lot (genealogy via parent/child)
|
|
1619
|
+
- ManQualityInspection \u2014 a recorded inspection (links plan + non-conformances)
|
|
1620
|
+
- ManInspectionPlan \u2014 a reusable inspection characteristics checklist
|
|
1621
|
+
- ManNonConformance \u2014 an NCR/CAPA (links originating inspection)
|
|
1622
|
+
- ManAsset \u2014 a maintainable asset (has maintenance work orders)
|
|
1623
|
+
- ManMaintenanceWorkOrder \u2014 a maintenance job against an asset
|
|
1624
|
+
- ManDowntimeEvent \u2014 a recorded downtime event (asset/work-center/order)
|
|
1625
|
+
|
|
1626
|
+
## Enums
|
|
1627
|
+
- ManWorkCenterType: ASSEMBLY, CELL, LINE, MACHINE, MANUAL
|
|
1628
|
+
- ManProductionOrderStatus: PLANNED, RELEASED, IN_PROGRESS, PAUSED, COMPLETED, CLOSED, CANCELLED
|
|
1629
|
+
- ManProductionPriority: LOW, NORMAL, HIGH, URGENT
|
|
1630
|
+
- ManOperationStatus: PENDING, SETUP, RUNNING, DONE, SKIPPED
|
|
1631
|
+
- ManMaterialLotStatus: AVAILABLE, ALLOCATED, CONSUMED, QUARANTINED, SCRAPPED
|
|
1632
|
+
- ManInspectionStage: INCOMING, IN_PROCESS, FINAL, AUDIT
|
|
1633
|
+
- ManInspectionResult: PENDING, PASS, FAIL, CONDITIONAL
|
|
1634
|
+
- ManSeverity: MINOR, MAJOR, CRITICAL
|
|
1635
|
+
- ManNonConformanceStatus: OPEN, INVESTIGATING, CONTAINED, CORRECTIVE_ACTION, CLOSED
|
|
1636
|
+
- ManNonConformanceDisposition: OPEN, USE_AS_IS, REWORK, SCRAP, RETURN_TO_SUPPLIER
|
|
1637
|
+
- ManAssetStatus: OPERATIONAL, DEGRADED, DOWN, MAINTENANCE, RETIRED
|
|
1638
|
+
- ManMaintenanceType: PREVENTIVE, CORRECTIVE, PREDICTIVE, INSPECTION
|
|
1639
|
+
- ManMaintenanceStatus: SCHEDULED, IN_PROGRESS, ON_HOLD, COMPLETED, CANCELLED
|
|
1640
|
+
- ManDowntimeReason: PLANNED, BREAKDOWN, CHANGEOVER, MATERIAL_SHORTAGE, QUALITY, OTHER
|
|
1641
|
+
|
|
1642
|
+
## Tool groups
|
|
1643
|
+
production (work_centers, production_orders, operations, material_lots),
|
|
1644
|
+
quality (inspections, inspection_plans, non_conformances),
|
|
1645
|
+
maintenance (assets, maintenance work orders, downtime), plus the public health probe.
|
|
1646
|
+
All run against the WORKSPACE gateway (workspace-scoped) except the public health probe.
|
|
1647
|
+
`;
|
|
1648
|
+
return {
|
|
1649
|
+
contents: [{ uri: schemaUri, text: schema, mimeType: "text/plain" }]
|
|
1650
|
+
};
|
|
1651
|
+
}
|
|
1652
|
+
);
|
|
1653
|
+
server.registerResource(
|
|
1654
|
+
"manufacturedops-production-orders",
|
|
1655
|
+
ordersUri,
|
|
1656
|
+
{
|
|
1657
|
+
title: "Manufacturedops Production Orders",
|
|
1658
|
+
description: "The first 50 production orders in the bound workspace.",
|
|
1659
|
+
mimeType: "application/json"
|
|
1660
|
+
},
|
|
1661
|
+
async () => {
|
|
1662
|
+
ctx.logger.debug(`Resource ${ordersUri} requested`);
|
|
1663
|
+
try {
|
|
1664
|
+
const data = await ctx.gql.workspace.request(
|
|
1665
|
+
LIST_PRODUCTION_ORDERS,
|
|
1666
|
+
{ pagination: { limit: 50, offset: 0 } }
|
|
1667
|
+
);
|
|
1668
|
+
return {
|
|
1669
|
+
contents: [
|
|
1670
|
+
{
|
|
1671
|
+
uri: ordersUri,
|
|
1672
|
+
text: JSON.stringify(data.manListProductionOrders, null, 2),
|
|
1673
|
+
mimeType: "application/json"
|
|
1674
|
+
}
|
|
1675
|
+
]
|
|
1676
|
+
};
|
|
1677
|
+
} catch (error) {
|
|
1678
|
+
return {
|
|
1679
|
+
contents: [
|
|
1680
|
+
{
|
|
1681
|
+
uri: ordersUri,
|
|
1682
|
+
text: JSON.stringify({
|
|
1683
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1684
|
+
}),
|
|
1685
|
+
mimeType: "application/json"
|
|
1686
|
+
}
|
|
1687
|
+
]
|
|
1688
|
+
};
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
);
|
|
1692
|
+
server.registerResource(
|
|
1693
|
+
"manufacturedops-work-centers",
|
|
1694
|
+
workCentersUri,
|
|
1695
|
+
{
|
|
1696
|
+
title: "Manufacturedops Work Centers",
|
|
1697
|
+
description: "Active work centers in the bound workspace.",
|
|
1698
|
+
mimeType: "application/json"
|
|
1699
|
+
},
|
|
1700
|
+
async () => {
|
|
1701
|
+
ctx.logger.debug(`Resource ${workCentersUri} requested`);
|
|
1702
|
+
try {
|
|
1703
|
+
const data = await ctx.gql.workspace.request(
|
|
1704
|
+
LIST_WORK_CENTERS,
|
|
1705
|
+
{ isActive: true }
|
|
1706
|
+
);
|
|
1707
|
+
return {
|
|
1708
|
+
contents: [
|
|
1709
|
+
{
|
|
1710
|
+
uri: workCentersUri,
|
|
1711
|
+
text: JSON.stringify(data.manListWorkCenters, null, 2),
|
|
1712
|
+
mimeType: "application/json"
|
|
1713
|
+
}
|
|
1714
|
+
]
|
|
1715
|
+
};
|
|
1716
|
+
} catch (error) {
|
|
1717
|
+
return {
|
|
1718
|
+
contents: [
|
|
1719
|
+
{
|
|
1720
|
+
uri: workCentersUri,
|
|
1721
|
+
text: JSON.stringify({
|
|
1722
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1723
|
+
}),
|
|
1724
|
+
mimeType: "application/json"
|
|
1725
|
+
}
|
|
1726
|
+
]
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
);
|
|
1731
|
+
server.registerResource(
|
|
1732
|
+
"manufacturedops-non-conformances",
|
|
1733
|
+
ncrUri,
|
|
1734
|
+
{
|
|
1735
|
+
title: "Manufacturedops Non-Conformances",
|
|
1736
|
+
description: "The first 50 non-conformances (NCR/CAPA) in the bound workspace.",
|
|
1737
|
+
mimeType: "application/json"
|
|
1738
|
+
},
|
|
1739
|
+
async () => {
|
|
1740
|
+
ctx.logger.debug(`Resource ${ncrUri} requested`);
|
|
1741
|
+
try {
|
|
1742
|
+
const data = await ctx.gql.workspace.request(
|
|
1743
|
+
LIST_NON_CONFORMANCES,
|
|
1744
|
+
{ pagination: { limit: 50, offset: 0 } }
|
|
1745
|
+
);
|
|
1746
|
+
return {
|
|
1747
|
+
contents: [
|
|
1748
|
+
{
|
|
1749
|
+
uri: ncrUri,
|
|
1750
|
+
text: JSON.stringify(data.manListNonConformances, null, 2),
|
|
1751
|
+
mimeType: "application/json"
|
|
1752
|
+
}
|
|
1753
|
+
]
|
|
1754
|
+
};
|
|
1755
|
+
} catch (error) {
|
|
1756
|
+
return {
|
|
1757
|
+
contents: [
|
|
1758
|
+
{
|
|
1759
|
+
uri: ncrUri,
|
|
1760
|
+
text: JSON.stringify({
|
|
1761
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1762
|
+
}),
|
|
1763
|
+
mimeType: "application/json"
|
|
1764
|
+
}
|
|
1765
|
+
]
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
function registerManufacturedopsPrompts(server, ctx, track) {
|
|
1772
|
+
const p = ctx.productName;
|
|
1773
|
+
const orderName = track(mcpPromptName(p, "production-order-runbook"));
|
|
1774
|
+
const qualityName = track(mcpPromptName(p, "quality-maintenance-ops"));
|
|
1775
|
+
server.registerPrompt(
|
|
1776
|
+
orderName,
|
|
1777
|
+
{
|
|
1778
|
+
title: "Production Order Runbook",
|
|
1779
|
+
description: "Guide for planning and running a production order end to end: create the order, add routing operations, allocate material lots, release and progress through the shop floor, and inspect output.",
|
|
1780
|
+
argsSchema: {
|
|
1781
|
+
productSku: zod.z.string().optional().describe("Product SKU to manufacture"),
|
|
1782
|
+
orderNumber: zod.z.string().optional().describe("Order number to assign")
|
|
1783
|
+
}
|
|
1784
|
+
},
|
|
1785
|
+
({ productSku, orderNumber }) => {
|
|
1786
|
+
ctx.logger.debug(`Prompt ${orderName} called`, { productSku, orderNumber });
|
|
1787
|
+
const skuNote = productSku ? `Manufacture product SKU "${productSku}".` : "Ask the user for the product SKU.";
|
|
1788
|
+
const orderNote = orderNumber ? `Use order number "${orderNumber}".` : "Assign a unique order number.";
|
|
1789
|
+
return {
|
|
1790
|
+
messages: [
|
|
1791
|
+
{
|
|
1792
|
+
role: "user",
|
|
1793
|
+
content: {
|
|
1794
|
+
type: "text",
|
|
1795
|
+
text: `Help me plan and run a Manufacturedops production order.
|
|
1796
|
+
|
|
1797
|
+
1. **Work Centers**: Use ${p}_work_centers_list to confirm the available work centers (create any missing ones with ${p}_work_centers_create).
|
|
1798
|
+
2. **Create Order**: Use ${p}_production_orders_create. ${skuNote} ${orderNote} Set quantity, uom, priority, and planned start/end.
|
|
1799
|
+
3. **Add Routing**: Use ${p}_operations_add for each step (name + sequence + workCenterId).
|
|
1800
|
+
4. **Allocate Materials**: Use ${p}_material_lots_create for incoming lots, then ${p}_material_lots_set_status=ALLOCATED for the order.
|
|
1801
|
+
5. **Release & Run**: Use ${p}_production_orders_transition (PLANNED \u2192 RELEASED \u2192 IN_PROGRESS). Log progress with ${p}_operations_record (setup/run minutes, good/scrap counts, status).
|
|
1802
|
+
6. **Inspect**: Use ${p}_inspections_record at the relevant stage; if it FAILs, raise an NCR with ${p}_non_conformances_raise.
|
|
1803
|
+
7. **Close**: Use ${p}_production_orders_transition (COMPLETED \u2192 CLOSED) when quantityDone meets target.
|
|
1804
|
+
|
|
1805
|
+
Show results at each step and confirm before releasing and before closing.`
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
]
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
);
|
|
1812
|
+
server.registerPrompt(
|
|
1813
|
+
qualityName,
|
|
1814
|
+
{
|
|
1815
|
+
title: "Quality & Maintenance Operations",
|
|
1816
|
+
description: "Daily operations workflow: triage the NCR/CAPA queue, drive corrective actions, review asset health, schedule maintenance, and log downtime for OEE.",
|
|
1817
|
+
argsSchema: {
|
|
1818
|
+
assetId: zod.z.string().optional().describe("Asset ID to focus maintenance on")
|
|
1819
|
+
}
|
|
1820
|
+
},
|
|
1821
|
+
({ assetId }) => {
|
|
1822
|
+
ctx.logger.debug(`Prompt ${qualityName} called`, { assetId });
|
|
1823
|
+
const assetNote = assetId ? `Focus maintenance on asset "${assetId}".` : `First list assets with ${p}_assets_list and pick the ones needing attention.`;
|
|
1824
|
+
return {
|
|
1825
|
+
messages: [
|
|
1826
|
+
{
|
|
1827
|
+
role: "user",
|
|
1828
|
+
content: {
|
|
1829
|
+
type: "text",
|
|
1830
|
+
text: `Run daily Manufacturedops quality & maintenance operations. ${assetNote}
|
|
1831
|
+
|
|
1832
|
+
1. **NCR Queue**: Use ${p}_non_conformances_list filtered by status=OPEN (and severity=CRITICAL/MAJOR) to find open issues.
|
|
1833
|
+
2. **Drive CAPA**: For each, use ${p}_non_conformances_update to record rootCause + correctiveAction, then ${p}_non_conformances_transition (INVESTIGATING \u2192 CONTAINED \u2192 CORRECTIVE_ACTION \u2192 CLOSED).
|
|
1834
|
+
3. **Asset Health**: Use ${p}_assets_list filtered by status=DEGRADED/DOWN; inspect each with ${p}_assets_get to review running hours vs PM interval.
|
|
1835
|
+
4. **Schedule Maintenance**: Use ${p}_maintenance_create for due PM/corrective jobs, then ${p}_maintenance_transition (SCHEDULED \u2192 IN_PROGRESS \u2192 COMPLETED).
|
|
1836
|
+
5. **Downtime / OEE**: Use ${p}_downtime_record for any stoppages and ${p}_downtime_list to review losses by reason.
|
|
1837
|
+
6. **Inspections**: Use ${p}_inspections_list filtered by result=FAIL to catch recurring quality escapes.
|
|
1838
|
+
|
|
1839
|
+
Summarize what needs attention after each step.`
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
]
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1845
|
+
);
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
// src/modules/manufacturedops/index.ts
|
|
1849
|
+
var manufacturedopsModule = {
|
|
1850
|
+
id: "manufacturedops",
|
|
1851
|
+
version: "0.0.1",
|
|
1852
|
+
displayName: "Manufacturedops",
|
|
1853
|
+
dependsOn: ["auth"],
|
|
1854
|
+
capabilities: { tools: {}, resources: {}, prompts: {} },
|
|
1855
|
+
register(server, ctx) {
|
|
1856
|
+
const tools = [];
|
|
1857
|
+
const resources = [];
|
|
1858
|
+
const prompts = [];
|
|
1859
|
+
const trackTool = (n) => {
|
|
1860
|
+
tools.push(n);
|
|
1861
|
+
return n;
|
|
1862
|
+
};
|
|
1863
|
+
const trackResource = (uri) => {
|
|
1864
|
+
resources.push(uri);
|
|
1865
|
+
return uri;
|
|
1866
|
+
};
|
|
1867
|
+
const trackPrompt = (n) => {
|
|
1868
|
+
prompts.push(n);
|
|
1869
|
+
return n;
|
|
1870
|
+
};
|
|
1871
|
+
registerProductionTools(server, ctx, trackTool);
|
|
1872
|
+
registerQualityTools(server, ctx, trackTool);
|
|
1873
|
+
registerMaintenanceTools(server, ctx, trackTool);
|
|
1874
|
+
registerHealthTools(server, ctx, trackTool);
|
|
1875
|
+
registerManufacturedopsResources(server, ctx, trackResource);
|
|
1876
|
+
registerManufacturedopsPrompts(server, ctx, trackPrompt);
|
|
1877
|
+
return { tools, resources, prompts };
|
|
1878
|
+
}
|
|
1879
|
+
};
|
|
1880
|
+
var manufacturedops_default = manufacturedopsModule;
|
|
1881
|
+
|
|
1882
|
+
module.exports = manufacturedops_default;
|
|
1883
|
+
//# sourceMappingURL=index.cjs.map
|
|
1884
|
+
//# sourceMappingURL=index.cjs.map
|