@assetlab/mcp-server 1.19.6 → 1.20.0
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/README.md +1 -20
- package/dist/client.js +34 -13
- package/dist/client.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/oauth.d.ts +52 -15
- package/dist/oauth.js +548 -205
- package/dist/oauth.js.map +1 -1
- package/dist/response-shaping.d.ts +28 -0
- package/dist/response-shaping.js +53 -0
- package/dist/response-shaping.js.map +1 -0
- package/dist/tools-write.js +1150 -255
- package/dist/tools-write.js.map +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +383 -222
- package/dist/tools.js.map +1 -1
- package/dist/worker.d.ts +11 -0
- package/dist/worker.js +83 -28
- package/dist/worker.js.map +1 -1
- package/package.json +44 -41
package/dist/tools-write.js
CHANGED
|
@@ -6,16 +6,10 @@
|
|
|
6
6
|
* Write tools require API keys with the appropriate :write scope.
|
|
7
7
|
*/
|
|
8
8
|
import { z } from 'zod';
|
|
9
|
+
import { formatError, formatResult } from './response-shaping.js';
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
10
11
|
// Helpers
|
|
11
12
|
// ---------------------------------------------------------------------------
|
|
12
|
-
function formatResult(data) {
|
|
13
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
14
|
-
}
|
|
15
|
-
function formatError(err) {
|
|
16
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
17
|
-
return { content: [{ type: 'text', text: `Error: ${message}` }], isError: true };
|
|
18
|
-
}
|
|
19
13
|
/** Strip undefined values so the API only receives explicitly-set fields. */
|
|
20
14
|
function buildBody(params) {
|
|
21
15
|
const body = {};
|
|
@@ -48,8 +42,8 @@ function coerceNumber(value) {
|
|
|
48
42
|
function normalizePmTemplateBody(params) {
|
|
49
43
|
const out = { ...params };
|
|
50
44
|
if (Array.isArray(out.tasks)) {
|
|
51
|
-
out.tasks = out.tasks.map(
|
|
52
|
-
const t =
|
|
45
|
+
out.tasks = out.tasks.map(raw => {
|
|
46
|
+
const t = raw && typeof raw === 'object' ? raw : {};
|
|
53
47
|
const id = typeof t.id === 'string' && t.id.trim() !== '' ? t.id : randomTaskId();
|
|
54
48
|
const description = typeof t.description === 'string' ? t.description : '';
|
|
55
49
|
const completed = typeof t.completed === 'boolean' ? t.completed : false;
|
|
@@ -57,8 +51,8 @@ function normalizePmTemplateBody(params) {
|
|
|
57
51
|
});
|
|
58
52
|
}
|
|
59
53
|
if (Array.isArray(out.resources)) {
|
|
60
|
-
out.resources = out.resources.map(
|
|
61
|
-
const r =
|
|
54
|
+
out.resources = out.resources.map(raw => {
|
|
55
|
+
const r = raw && typeof raw === 'object' ? raw : {};
|
|
62
56
|
const res = {};
|
|
63
57
|
if (typeof r.name === 'string')
|
|
64
58
|
res.name = r.name;
|
|
@@ -89,11 +83,22 @@ export function registerWriteTools(server, client) {
|
|
|
89
83
|
title: z.string().min(1).max(500).describe('Work order title (required)'),
|
|
90
84
|
description: z.string().optional().describe('Detailed description'),
|
|
91
85
|
priority: z.enum(['LOW', 'MEDIUM', 'HIGH', 'URGENT']).optional().describe('Priority level'),
|
|
92
|
-
status: z
|
|
86
|
+
status: z
|
|
87
|
+
.enum(['NEW', 'IN_PROGRESS', 'ON_HOLD', 'REJECTED', 'COMPLETED', 'CANCELLED'])
|
|
88
|
+
.optional()
|
|
89
|
+
.describe('Status'),
|
|
93
90
|
type: z.enum(['PM', 'REACTIVE']).optional().describe('Work order type'),
|
|
94
91
|
site_id: z.string().uuid().optional().describe('Site ID — resolve first via list_sites'),
|
|
95
|
-
building_id: z
|
|
96
|
-
|
|
92
|
+
building_id: z
|
|
93
|
+
.string()
|
|
94
|
+
.uuid()
|
|
95
|
+
.optional()
|
|
96
|
+
.describe('Building ID — resolve second via list_buildings filtered by site_id'),
|
|
97
|
+
location_id: z
|
|
98
|
+
.string()
|
|
99
|
+
.uuid()
|
|
100
|
+
.optional()
|
|
101
|
+
.describe('Location ID — resolve last via list_locations filtered by building_id'),
|
|
97
102
|
asset_id: z.string().uuid().optional().describe('Asset ID'),
|
|
98
103
|
start_date: z.string().optional().describe('Start date (ISO 8601)'),
|
|
99
104
|
due_date: z.string().optional().describe('Due date (ISO 8601)'),
|
|
@@ -101,9 +106,20 @@ export function registerWriteTools(server, client) {
|
|
|
101
106
|
estimated_cost: z.number().min(0).optional().describe('Estimated cost'),
|
|
102
107
|
work_category_id: z.string().uuid().optional().describe('Work category ID'),
|
|
103
108
|
assigned_to: z.string().optional().describe('Assigned user ID (mapped to assignees array)'),
|
|
104
|
-
assignees: z
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
assignees: z
|
|
110
|
+
.array(z.string())
|
|
111
|
+
.optional()
|
|
112
|
+
.describe('Array of assigned user IDs (alternative to assigned_to for multiple assignees)'),
|
|
113
|
+
image_url: z
|
|
114
|
+
.string()
|
|
115
|
+
.max(2000)
|
|
116
|
+
.optional()
|
|
117
|
+
.describe('Image URL (upload via create_upload_url with bucket "attachments", then set this to the public_url)'),
|
|
118
|
+
meter_reading: z
|
|
119
|
+
.number()
|
|
120
|
+
.min(0)
|
|
121
|
+
.optional()
|
|
122
|
+
.describe('Meter/odometer reading at time of service'),
|
|
107
123
|
meter_unit: z.string().max(50).optional().describe('Meter unit (km, miles, hours, cycles)'),
|
|
108
124
|
}, async (params) => {
|
|
109
125
|
try {
|
|
@@ -119,11 +135,22 @@ export function registerWriteTools(server, client) {
|
|
|
119
135
|
title: z.string().min(1).max(500).optional().describe('Work order title'),
|
|
120
136
|
description: z.string().optional().describe('Detailed description'),
|
|
121
137
|
priority: z.enum(['LOW', 'MEDIUM', 'HIGH', 'URGENT']).optional().describe('Priority level'),
|
|
122
|
-
status: z
|
|
138
|
+
status: z
|
|
139
|
+
.enum(['NEW', 'IN_PROGRESS', 'ON_HOLD', 'REJECTED', 'COMPLETED', 'CANCELLED'])
|
|
140
|
+
.optional()
|
|
141
|
+
.describe('Status'),
|
|
123
142
|
type: z.enum(['PM', 'REACTIVE']).optional().describe('Work order type'),
|
|
124
143
|
site_id: z.string().uuid().optional().describe('Site ID — resolve first via list_sites'),
|
|
125
|
-
building_id: z
|
|
126
|
-
|
|
144
|
+
building_id: z
|
|
145
|
+
.string()
|
|
146
|
+
.uuid()
|
|
147
|
+
.optional()
|
|
148
|
+
.describe('Building ID — resolve second via list_buildings filtered by site_id'),
|
|
149
|
+
location_id: z
|
|
150
|
+
.string()
|
|
151
|
+
.uuid()
|
|
152
|
+
.optional()
|
|
153
|
+
.describe('Location ID — resolve last via list_locations filtered by building_id'),
|
|
127
154
|
asset_id: z.string().uuid().optional().describe('Asset ID'),
|
|
128
155
|
start_date: z.string().optional().describe('Start date (ISO 8601)'),
|
|
129
156
|
due_date: z.string().optional().describe('Due date (ISO 8601)'),
|
|
@@ -131,9 +158,20 @@ export function registerWriteTools(server, client) {
|
|
|
131
158
|
estimated_cost: z.number().min(0).optional().describe('Estimated cost'),
|
|
132
159
|
work_category_id: z.string().uuid().optional().describe('Work category ID'),
|
|
133
160
|
assigned_to: z.string().optional().describe('Assigned user ID (mapped to assignees array)'),
|
|
134
|
-
assignees: z
|
|
135
|
-
|
|
136
|
-
|
|
161
|
+
assignees: z
|
|
162
|
+
.array(z.string())
|
|
163
|
+
.optional()
|
|
164
|
+
.describe('Array of assigned user IDs (alternative to assigned_to for multiple assignees)'),
|
|
165
|
+
image_url: z
|
|
166
|
+
.string()
|
|
167
|
+
.max(2000)
|
|
168
|
+
.optional()
|
|
169
|
+
.describe('Image URL (upload via create_upload_url with bucket "attachments", then set this to the public_url)'),
|
|
170
|
+
meter_reading: z
|
|
171
|
+
.number()
|
|
172
|
+
.min(0)
|
|
173
|
+
.optional()
|
|
174
|
+
.describe('Meter/odometer reading at time of service'),
|
|
137
175
|
meter_unit: z.string().max(50).optional().describe('Meter unit (km, miles, hours, cycles)'),
|
|
138
176
|
}, async ({ id, ...rest }) => {
|
|
139
177
|
try {
|
|
@@ -159,23 +197,54 @@ export function registerWriteTools(server, client) {
|
|
|
159
197
|
server.tool('create_asset', 'Create a new asset. Requires assets:write scope. IMPORTANT — Location hierarchy: always resolve top-down by calling list_sites first, then list_buildings filtered by site_id, then list_locations filtered by building_id. Provide all three IDs (site_id, building_id, location_id) explicitly. System hierarchy: similarly resolve via list_system_classes → list_system_groups → list_systems.', {
|
|
160
198
|
name: z.string().min(1).max(500).describe('Asset name (required)'),
|
|
161
199
|
description: z.string().optional().describe('Description'),
|
|
162
|
-
asset_id: z
|
|
200
|
+
asset_id: z
|
|
201
|
+
.string()
|
|
202
|
+
.max(100)
|
|
203
|
+
.optional()
|
|
204
|
+
.describe('Custom asset identifier (unique per tenant)'),
|
|
163
205
|
asset_type_id: z.string().uuid().optional().describe('Asset type ID (from asset_types)'),
|
|
164
|
-
manufacturer_id: z
|
|
206
|
+
manufacturer_id: z
|
|
207
|
+
.string()
|
|
208
|
+
.uuid()
|
|
209
|
+
.optional()
|
|
210
|
+
.describe('Manufacturer ID (from manufacturers)'),
|
|
165
211
|
model: z.string().max(500).optional().describe('Model name/number'),
|
|
166
212
|
serial_number: z.string().max(200).optional().describe('Serial number'),
|
|
167
213
|
purchase_cost: z.number().min(0).optional().describe('Purchase cost'),
|
|
168
214
|
purchase_date: z.string().optional().describe('Purchase date (ISO 8601)'),
|
|
169
215
|
expected_lifetime_years: z.number().min(0).optional().describe('Expected lifetime in years'),
|
|
170
216
|
condition_score: z.number().min(0).max(100).optional().describe('Condition score (0-100)'),
|
|
171
|
-
risk_factor: z
|
|
217
|
+
risk_factor: z
|
|
218
|
+
.enum(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'])
|
|
219
|
+
.optional()
|
|
220
|
+
.describe('Risk factor (CRITICAL, HIGH, MEDIUM, LOW)'),
|
|
172
221
|
status_id: z.string().max(100).optional().describe('Status identifier'),
|
|
173
222
|
site_id: z.string().uuid().optional().describe('Site ID — resolve first via list_sites'),
|
|
174
|
-
building_id: z
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
223
|
+
building_id: z
|
|
224
|
+
.string()
|
|
225
|
+
.uuid()
|
|
226
|
+
.optional()
|
|
227
|
+
.describe('Building ID — resolve second via list_buildings filtered by site_id'),
|
|
228
|
+
location_id: z
|
|
229
|
+
.string()
|
|
230
|
+
.uuid()
|
|
231
|
+
.optional()
|
|
232
|
+
.describe('Location ID — resolve last via list_locations filtered by building_id'),
|
|
233
|
+
system_class_id: z
|
|
234
|
+
.string()
|
|
235
|
+
.uuid()
|
|
236
|
+
.optional()
|
|
237
|
+
.describe('System class ID — resolve first via list_system_classes'),
|
|
238
|
+
system_group_id: z
|
|
239
|
+
.string()
|
|
240
|
+
.uuid()
|
|
241
|
+
.optional()
|
|
242
|
+
.describe('System group ID — resolve second via list_system_groups filtered by system_class_id'),
|
|
243
|
+
system_id: z
|
|
244
|
+
.string()
|
|
245
|
+
.uuid()
|
|
246
|
+
.optional()
|
|
247
|
+
.describe('System ID — resolve last via list_systems filtered by system_group_id'),
|
|
179
248
|
image_url: z.string().max(2000).optional().describe('Image URL'),
|
|
180
249
|
last_maintenance_date: z.string().optional().describe('Last maintenance date (ISO 8601)'),
|
|
181
250
|
quantity: z.number().min(0).optional().describe('Quantity'),
|
|
@@ -183,15 +252,49 @@ export function registerWriteTools(server, client) {
|
|
|
183
252
|
unit_replacement_value: z.number().min(0).optional().describe('Unit replacement value'),
|
|
184
253
|
cost_per_sq_ft: z.number().min(0).optional().describe('Cost per square foot'),
|
|
185
254
|
salvage_value: z.number().min(0).optional().describe('Salvage value'),
|
|
186
|
-
salvage_value_percentage: z
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
255
|
+
salvage_value_percentage: z
|
|
256
|
+
.number()
|
|
257
|
+
.min(0)
|
|
258
|
+
.max(100)
|
|
259
|
+
.optional()
|
|
260
|
+
.describe('Salvage value percentage (0-100)'),
|
|
261
|
+
consequence_of_failure_score: z
|
|
262
|
+
.number()
|
|
263
|
+
.int()
|
|
264
|
+
.min(0)
|
|
265
|
+
.optional()
|
|
266
|
+
.describe('Consequence of failure score'),
|
|
267
|
+
likelihood_of_failure_score: z
|
|
268
|
+
.number()
|
|
269
|
+
.int()
|
|
270
|
+
.min(0)
|
|
271
|
+
.optional()
|
|
272
|
+
.describe('Likelihood of failure score'),
|
|
273
|
+
safety_impact: z
|
|
274
|
+
.enum(['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'])
|
|
275
|
+
.optional()
|
|
276
|
+
.describe('Safety impact level (LOW, MEDIUM, HIGH, CRITICAL)'),
|
|
277
|
+
service_impact: z
|
|
278
|
+
.enum(['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'])
|
|
279
|
+
.optional()
|
|
280
|
+
.describe('Service impact level (LOW, MEDIUM, HIGH, CRITICAL)'),
|
|
281
|
+
environmental_impact: z
|
|
282
|
+
.enum(['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'])
|
|
283
|
+
.optional()
|
|
284
|
+
.describe('Environmental impact level (LOW, MEDIUM, HIGH, CRITICAL)'),
|
|
285
|
+
regulatory_impact: z
|
|
286
|
+
.enum(['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'])
|
|
287
|
+
.optional()
|
|
288
|
+
.describe('Regulatory impact level (LOW, MEDIUM, HIGH, CRITICAL)'),
|
|
289
|
+
reputation_impact: z
|
|
290
|
+
.enum(['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'])
|
|
291
|
+
.optional()
|
|
292
|
+
.describe('Reputation impact level (LOW, MEDIUM, HIGH, CRITICAL)'),
|
|
293
|
+
current_meter_reading: z
|
|
294
|
+
.number()
|
|
295
|
+
.min(0)
|
|
296
|
+
.optional()
|
|
297
|
+
.describe('Current meter/odometer reading'),
|
|
195
298
|
meter_unit: z.string().max(50).optional().describe('Meter unit (km, miles, hours, cycles)'),
|
|
196
299
|
}, async (params) => {
|
|
197
300
|
try {
|
|
@@ -206,40 +309,105 @@ export function registerWriteTools(server, client) {
|
|
|
206
309
|
id: z.string().uuid().describe('Asset ID'),
|
|
207
310
|
name: z.string().min(1).max(500).optional().describe('Asset name'),
|
|
208
311
|
description: z.string().optional().describe('Description'),
|
|
209
|
-
asset_id: z
|
|
312
|
+
asset_id: z
|
|
313
|
+
.string()
|
|
314
|
+
.max(100)
|
|
315
|
+
.optional()
|
|
316
|
+
.describe('Custom asset identifier (unique per tenant)'),
|
|
210
317
|
asset_type_id: z.string().uuid().optional().describe('Asset type ID (from asset_types)'),
|
|
211
|
-
manufacturer_id: z
|
|
318
|
+
manufacturer_id: z
|
|
319
|
+
.string()
|
|
320
|
+
.uuid()
|
|
321
|
+
.optional()
|
|
322
|
+
.describe('Manufacturer ID (from manufacturers)'),
|
|
212
323
|
model: z.string().max(500).optional().describe('Model name/number'),
|
|
213
324
|
serial_number: z.string().max(200).optional().describe('Serial number'),
|
|
214
325
|
purchase_cost: z.number().min(0).optional().describe('Purchase cost'),
|
|
215
326
|
purchase_date: z.string().optional().describe('Purchase date (ISO 8601)'),
|
|
216
327
|
expected_lifetime_years: z.number().min(0).optional().describe('Expected lifetime in years'),
|
|
217
328
|
condition_score: z.number().min(0).max(100).optional().describe('Condition score (0-100)'),
|
|
218
|
-
risk_factor: z
|
|
329
|
+
risk_factor: z
|
|
330
|
+
.enum(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'])
|
|
331
|
+
.optional()
|
|
332
|
+
.describe('Risk factor (CRITICAL, HIGH, MEDIUM, LOW)'),
|
|
219
333
|
status_id: z.string().max(100).optional().describe('Status identifier'),
|
|
220
334
|
site_id: z.string().uuid().optional().describe('Site ID — resolve first via list_sites'),
|
|
221
|
-
building_id: z
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
335
|
+
building_id: z
|
|
336
|
+
.string()
|
|
337
|
+
.uuid()
|
|
338
|
+
.optional()
|
|
339
|
+
.describe('Building ID — resolve second via list_buildings filtered by site_id'),
|
|
340
|
+
location_id: z
|
|
341
|
+
.string()
|
|
342
|
+
.uuid()
|
|
343
|
+
.optional()
|
|
344
|
+
.describe('Location ID — resolve last via list_locations filtered by building_id'),
|
|
345
|
+
system_class_id: z
|
|
346
|
+
.string()
|
|
347
|
+
.uuid()
|
|
348
|
+
.optional()
|
|
349
|
+
.describe('System class ID — resolve first via list_system_classes'),
|
|
350
|
+
system_group_id: z
|
|
351
|
+
.string()
|
|
352
|
+
.uuid()
|
|
353
|
+
.optional()
|
|
354
|
+
.describe('System group ID — resolve second via list_system_groups filtered by system_class_id'),
|
|
355
|
+
system_id: z
|
|
356
|
+
.string()
|
|
357
|
+
.uuid()
|
|
358
|
+
.optional()
|
|
359
|
+
.describe('System ID — resolve last via list_systems filtered by system_group_id'),
|
|
226
360
|
image_url: z.string().max(2000).optional().describe('Image URL'),
|
|
227
361
|
last_maintenance_date: z.string().optional().describe('Last maintenance date (ISO 8601)'),
|
|
228
|
-
current_meter_reading: z
|
|
362
|
+
current_meter_reading: z
|
|
363
|
+
.number()
|
|
364
|
+
.min(0)
|
|
365
|
+
.optional()
|
|
366
|
+
.describe('Current meter/odometer reading'),
|
|
229
367
|
meter_unit: z.string().max(50).optional().describe('Meter unit (km, miles, hours, cycles)'),
|
|
230
368
|
quantity: z.number().min(0).optional().describe('Quantity'),
|
|
231
369
|
unit_of_measure: z.string().max(100).optional().describe('Unit of measure'),
|
|
232
370
|
unit_replacement_value: z.number().min(0).optional().describe('Unit replacement value'),
|
|
233
371
|
cost_per_sq_ft: z.number().min(0).optional().describe('Cost per square foot'),
|
|
234
372
|
salvage_value: z.number().min(0).optional().describe('Salvage value'),
|
|
235
|
-
salvage_value_percentage: z
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
373
|
+
salvage_value_percentage: z
|
|
374
|
+
.number()
|
|
375
|
+
.min(0)
|
|
376
|
+
.max(100)
|
|
377
|
+
.optional()
|
|
378
|
+
.describe('Salvage value percentage (0-100)'),
|
|
379
|
+
consequence_of_failure_score: z
|
|
380
|
+
.number()
|
|
381
|
+
.int()
|
|
382
|
+
.min(0)
|
|
383
|
+
.optional()
|
|
384
|
+
.describe('Consequence of failure score'),
|
|
385
|
+
likelihood_of_failure_score: z
|
|
386
|
+
.number()
|
|
387
|
+
.int()
|
|
388
|
+
.min(0)
|
|
389
|
+
.optional()
|
|
390
|
+
.describe('Likelihood of failure score'),
|
|
391
|
+
safety_impact: z
|
|
392
|
+
.enum(['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'])
|
|
393
|
+
.optional()
|
|
394
|
+
.describe('Safety impact level (LOW, MEDIUM, HIGH, CRITICAL)'),
|
|
395
|
+
service_impact: z
|
|
396
|
+
.enum(['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'])
|
|
397
|
+
.optional()
|
|
398
|
+
.describe('Service impact level (LOW, MEDIUM, HIGH, CRITICAL)'),
|
|
399
|
+
environmental_impact: z
|
|
400
|
+
.enum(['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'])
|
|
401
|
+
.optional()
|
|
402
|
+
.describe('Environmental impact level (LOW, MEDIUM, HIGH, CRITICAL)'),
|
|
403
|
+
regulatory_impact: z
|
|
404
|
+
.enum(['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'])
|
|
405
|
+
.optional()
|
|
406
|
+
.describe('Regulatory impact level (LOW, MEDIUM, HIGH, CRITICAL)'),
|
|
407
|
+
reputation_impact: z
|
|
408
|
+
.enum(['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'])
|
|
409
|
+
.optional()
|
|
410
|
+
.describe('Reputation impact level (LOW, MEDIUM, HIGH, CRITICAL)'),
|
|
243
411
|
}, async ({ id, ...rest }) => {
|
|
244
412
|
try {
|
|
245
413
|
const result = await client.update('assets', id, buildBody(rest));
|
|
@@ -265,10 +433,21 @@ export function registerWriteTools(server, client) {
|
|
|
265
433
|
title: z.string().min(1).max(500).describe('Work request title (required)'),
|
|
266
434
|
description: z.string().optional().describe('Description'),
|
|
267
435
|
priority: z.enum(['LOW', 'MEDIUM', 'HIGH', 'CRITICAL']).optional().describe('Priority level'),
|
|
268
|
-
status: z
|
|
436
|
+
status: z
|
|
437
|
+
.enum(['SUBMITTED', 'APPROVED', 'REJECTED', 'CONVERTED'])
|
|
438
|
+
.optional()
|
|
439
|
+
.describe('Status'),
|
|
269
440
|
site_id: z.string().uuid().optional().describe('Site ID — resolve first via list_sites'),
|
|
270
|
-
building_id: z
|
|
271
|
-
|
|
441
|
+
building_id: z
|
|
442
|
+
.string()
|
|
443
|
+
.uuid()
|
|
444
|
+
.optional()
|
|
445
|
+
.describe('Building ID — resolve second via list_buildings filtered by site_id'),
|
|
446
|
+
location_id: z
|
|
447
|
+
.string()
|
|
448
|
+
.uuid()
|
|
449
|
+
.optional()
|
|
450
|
+
.describe('Location ID — resolve last via list_locations filtered by building_id'),
|
|
272
451
|
asset_id: z.string().uuid().optional().describe('Asset ID'),
|
|
273
452
|
system_id: z.string().uuid().optional().describe('System ID'),
|
|
274
453
|
work_category_id: z.string().uuid().optional().describe('Work category ID'),
|
|
@@ -286,10 +465,21 @@ export function registerWriteTools(server, client) {
|
|
|
286
465
|
title: z.string().min(1).max(500).optional().describe('Work request title'),
|
|
287
466
|
description: z.string().optional().describe('Description'),
|
|
288
467
|
priority: z.enum(['LOW', 'MEDIUM', 'HIGH', 'CRITICAL']).optional().describe('Priority level'),
|
|
289
|
-
status: z
|
|
468
|
+
status: z
|
|
469
|
+
.enum(['SUBMITTED', 'APPROVED', 'REJECTED', 'CONVERTED'])
|
|
470
|
+
.optional()
|
|
471
|
+
.describe('Status'),
|
|
290
472
|
site_id: z.string().uuid().optional().describe('Site ID — resolve first via list_sites'),
|
|
291
|
-
building_id: z
|
|
292
|
-
|
|
473
|
+
building_id: z
|
|
474
|
+
.string()
|
|
475
|
+
.uuid()
|
|
476
|
+
.optional()
|
|
477
|
+
.describe('Building ID — resolve second via list_buildings filtered by site_id'),
|
|
478
|
+
location_id: z
|
|
479
|
+
.string()
|
|
480
|
+
.uuid()
|
|
481
|
+
.optional()
|
|
482
|
+
.describe('Location ID — resolve last via list_locations filtered by building_id'),
|
|
293
483
|
asset_id: z.string().uuid().optional().describe('Asset ID'),
|
|
294
484
|
system_id: z.string().uuid().optional().describe('System ID'),
|
|
295
485
|
work_category_id: z.string().uuid().optional().describe('Work category ID'),
|
|
@@ -324,8 +514,15 @@ export function registerWriteTools(server, client) {
|
|
|
324
514
|
state: z.string().max(100).optional().describe('State/province'),
|
|
325
515
|
country: z.string().max(100).optional().describe('Country'),
|
|
326
516
|
status: z.string().max(50).optional().describe('Vendor status'),
|
|
327
|
-
categories: z
|
|
328
|
-
|
|
517
|
+
categories: z
|
|
518
|
+
.array(z.string().max(100))
|
|
519
|
+
.optional()
|
|
520
|
+
.describe('Vendor categories (e.g. ["HVAC", "Plumbing"])'),
|
|
521
|
+
website: z
|
|
522
|
+
.string()
|
|
523
|
+
.max(500)
|
|
524
|
+
.optional()
|
|
525
|
+
.describe('Website URL (protocol and www prefix are stripped automatically)'),
|
|
329
526
|
description: z.string().optional().describe('Description'),
|
|
330
527
|
}, async (params) => {
|
|
331
528
|
try {
|
|
@@ -347,8 +544,15 @@ export function registerWriteTools(server, client) {
|
|
|
347
544
|
state: z.string().max(100).optional().describe('State/province'),
|
|
348
545
|
country: z.string().max(100).optional().describe('Country'),
|
|
349
546
|
status: z.string().max(50).optional().describe('Vendor status'),
|
|
350
|
-
categories: z
|
|
351
|
-
|
|
547
|
+
categories: z
|
|
548
|
+
.array(z.string().max(100))
|
|
549
|
+
.optional()
|
|
550
|
+
.describe('Vendor categories (e.g. ["HVAC", "Plumbing"])'),
|
|
551
|
+
website: z
|
|
552
|
+
.string()
|
|
553
|
+
.max(500)
|
|
554
|
+
.optional()
|
|
555
|
+
.describe('Website URL (protocol and www prefix are stripped automatically)'),
|
|
352
556
|
description: z.string().optional().describe('Description'),
|
|
353
557
|
}, async ({ id, ...rest }) => {
|
|
354
558
|
try {
|
|
@@ -440,8 +644,18 @@ export function registerWriteTools(server, client) {
|
|
|
440
644
|
floors: z.number().int().optional().describe('Number of floors'),
|
|
441
645
|
area_sqft: z.number().min(0).optional().describe('Area in square feet'),
|
|
442
646
|
type: z.string().max(100).optional().describe('Building type label'),
|
|
443
|
-
building_type_id: z
|
|
444
|
-
|
|
647
|
+
building_type_id: z
|
|
648
|
+
.string()
|
|
649
|
+
.uuid()
|
|
650
|
+
.optional()
|
|
651
|
+
.describe('Building type ID (from building_types)'),
|
|
652
|
+
year_built: z
|
|
653
|
+
.number()
|
|
654
|
+
.int()
|
|
655
|
+
.min(1800)
|
|
656
|
+
.max(2100)
|
|
657
|
+
.optional()
|
|
658
|
+
.describe('Year the building was constructed'),
|
|
445
659
|
}, async (params) => {
|
|
446
660
|
try {
|
|
447
661
|
const result = await client.create('buildings', buildBody(params));
|
|
@@ -458,8 +672,18 @@ export function registerWriteTools(server, client) {
|
|
|
458
672
|
floors: z.number().int().optional().describe('Number of floors'),
|
|
459
673
|
area_sqft: z.number().min(0).optional().describe('Area in square feet'),
|
|
460
674
|
type: z.string().max(100).optional().describe('Building type label'),
|
|
461
|
-
building_type_id: z
|
|
462
|
-
|
|
675
|
+
building_type_id: z
|
|
676
|
+
.string()
|
|
677
|
+
.uuid()
|
|
678
|
+
.optional()
|
|
679
|
+
.describe('Building type ID (from building_types)'),
|
|
680
|
+
year_built: z
|
|
681
|
+
.number()
|
|
682
|
+
.int()
|
|
683
|
+
.min(1800)
|
|
684
|
+
.max(2100)
|
|
685
|
+
.optional()
|
|
686
|
+
.describe('Year the building was constructed'),
|
|
463
687
|
}, async ({ id, ...rest }) => {
|
|
464
688
|
try {
|
|
465
689
|
const result = await client.update('buildings', id, buildBody(rest));
|
|
@@ -487,7 +711,11 @@ export function registerWriteTools(server, client) {
|
|
|
487
711
|
floor: z.string().max(50).optional().describe('Floor identifier'),
|
|
488
712
|
area: z.number().min(0).optional().describe('Area (sq ft or sq m)'),
|
|
489
713
|
type: z.string().max(100).optional().describe('Location type label'),
|
|
490
|
-
location_type_id: z
|
|
714
|
+
location_type_id: z
|
|
715
|
+
.string()
|
|
716
|
+
.uuid()
|
|
717
|
+
.optional()
|
|
718
|
+
.describe('Location type ID (from location_types)'),
|
|
491
719
|
}, async (params) => {
|
|
492
720
|
try {
|
|
493
721
|
const result = await client.create('locations', buildBody(params));
|
|
@@ -504,7 +732,11 @@ export function registerWriteTools(server, client) {
|
|
|
504
732
|
floor: z.string().max(50).optional().describe('Floor identifier'),
|
|
505
733
|
area: z.number().min(0).optional().describe('Area (sq ft or sq m)'),
|
|
506
734
|
type: z.string().max(100).optional().describe('Location type label'),
|
|
507
|
-
location_type_id: z
|
|
735
|
+
location_type_id: z
|
|
736
|
+
.string()
|
|
737
|
+
.uuid()
|
|
738
|
+
.optional()
|
|
739
|
+
.describe('Location type ID (from location_types)'),
|
|
508
740
|
}, async ({ id, ...rest }) => {
|
|
509
741
|
try {
|
|
510
742
|
const result = await client.update('locations', id, buildBody(rest));
|
|
@@ -529,14 +761,39 @@ export function registerWriteTools(server, client) {
|
|
|
529
761
|
server.tool('create_pm_schedule', 'Create a new preventive maintenance schedule. Requires pm_schedules:write scope. IMPORTANT — Location hierarchy: always resolve top-down by calling list_sites first, then list_buildings filtered by site_id, then list_locations filtered by building_id. Provide all three IDs explicitly.', {
|
|
530
762
|
title: z.string().min(1).max(500).describe('PM schedule title (required)'),
|
|
531
763
|
description: z.string().optional().describe('Description'),
|
|
532
|
-
frequency: z
|
|
533
|
-
|
|
764
|
+
frequency: z
|
|
765
|
+
.enum([
|
|
766
|
+
'DAILY',
|
|
767
|
+
'WEEKLY',
|
|
768
|
+
'MONTHLY',
|
|
769
|
+
'QUARTERLY',
|
|
770
|
+
'SEMI_ANNUAL',
|
|
771
|
+
'ANNUAL',
|
|
772
|
+
'FIVE_YEARLY',
|
|
773
|
+
'CUSTOM',
|
|
774
|
+
])
|
|
775
|
+
.optional()
|
|
776
|
+
.describe('Frequency'),
|
|
777
|
+
custom_interval_weeks: z
|
|
778
|
+
.number()
|
|
779
|
+
.int()
|
|
780
|
+
.min(1)
|
|
781
|
+
.optional()
|
|
782
|
+
.describe('Custom interval in weeks (when frequency is CUSTOM)'),
|
|
534
783
|
next_due: z.string().optional().describe('Next due date (ISO 8601)'),
|
|
535
784
|
estimated_hours: z.number().min(0).optional().describe('Estimated hours'),
|
|
536
785
|
estimated_cost: z.number().min(0).optional().describe('Estimated cost'),
|
|
537
786
|
site_id: z.string().uuid().optional().describe('Site ID — resolve first via list_sites'),
|
|
538
|
-
building_id: z
|
|
539
|
-
|
|
787
|
+
building_id: z
|
|
788
|
+
.string()
|
|
789
|
+
.uuid()
|
|
790
|
+
.optional()
|
|
791
|
+
.describe('Building ID — resolve second via list_buildings filtered by site_id'),
|
|
792
|
+
location_id: z
|
|
793
|
+
.string()
|
|
794
|
+
.uuid()
|
|
795
|
+
.optional()
|
|
796
|
+
.describe('Location ID — resolve last via list_locations filtered by building_id'),
|
|
540
797
|
asset_id: z.string().uuid().optional().describe('Asset ID'),
|
|
541
798
|
work_category: z.string().max(200).optional().describe('Work category label'),
|
|
542
799
|
schedule_type: z.string().max(100).optional().describe('Schedule type'),
|
|
@@ -546,18 +803,28 @@ export function registerWriteTools(server, client) {
|
|
|
546
803
|
status: z.enum(['active', 'inactive']).optional().describe('Schedule status'),
|
|
547
804
|
auto_generate_wo: z.boolean().optional().describe('Auto-generate work orders'),
|
|
548
805
|
floating: z.boolean().optional().describe('Floating schedule (due date based on completion)'),
|
|
549
|
-
meter_based: z
|
|
550
|
-
|
|
806
|
+
meter_based: z
|
|
807
|
+
.boolean()
|
|
808
|
+
.optional()
|
|
809
|
+
.describe('Whether this PM triggers at meter intervals (e.g. every 5000 km)'),
|
|
810
|
+
meter_interval: z
|
|
811
|
+
.number()
|
|
812
|
+
.min(0)
|
|
813
|
+
.optional()
|
|
814
|
+
.describe('Meter interval — trigger every N units'),
|
|
551
815
|
meter_unit: z.string().max(50).optional().describe('Meter unit (km, miles, hours, cycles)'),
|
|
552
816
|
start_date: z.string().optional().describe('Start date (ISO 8601)'),
|
|
553
817
|
asset_ids: z.array(z.string().uuid()).optional().describe('Array of asset IDs'),
|
|
554
818
|
system_ids: z.array(z.string().uuid()).optional().describe('Array of system IDs'),
|
|
555
819
|
location_ids: z.array(z.string().uuid()).optional().describe('Array of location IDs'),
|
|
556
|
-
tasks: z
|
|
820
|
+
tasks: z
|
|
821
|
+
.array(z.object({
|
|
557
822
|
id: z.string().describe('Unique task ID (use a random string)'),
|
|
558
823
|
description: z.string().describe('Task description'),
|
|
559
824
|
completed: z.boolean().describe('Whether the task is completed'),
|
|
560
|
-
}))
|
|
825
|
+
}))
|
|
826
|
+
.optional()
|
|
827
|
+
.describe('Checklist of tasks for this PM schedule'),
|
|
561
828
|
}, async (params) => {
|
|
562
829
|
try {
|
|
563
830
|
const result = await client.create('pm-schedules', buildBody(params));
|
|
@@ -571,14 +838,39 @@ export function registerWriteTools(server, client) {
|
|
|
571
838
|
id: z.string().uuid().describe('PM schedule ID'),
|
|
572
839
|
title: z.string().min(1).max(500).optional().describe('PM schedule title'),
|
|
573
840
|
description: z.string().optional().describe('Description'),
|
|
574
|
-
frequency: z
|
|
575
|
-
|
|
841
|
+
frequency: z
|
|
842
|
+
.enum([
|
|
843
|
+
'DAILY',
|
|
844
|
+
'WEEKLY',
|
|
845
|
+
'MONTHLY',
|
|
846
|
+
'QUARTERLY',
|
|
847
|
+
'SEMI_ANNUAL',
|
|
848
|
+
'ANNUAL',
|
|
849
|
+
'FIVE_YEARLY',
|
|
850
|
+
'CUSTOM',
|
|
851
|
+
])
|
|
852
|
+
.optional()
|
|
853
|
+
.describe('Frequency'),
|
|
854
|
+
custom_interval_weeks: z
|
|
855
|
+
.number()
|
|
856
|
+
.int()
|
|
857
|
+
.min(1)
|
|
858
|
+
.optional()
|
|
859
|
+
.describe('Custom interval in weeks (when frequency is CUSTOM)'),
|
|
576
860
|
next_due: z.string().optional().describe('Next due date (ISO 8601)'),
|
|
577
861
|
estimated_hours: z.number().min(0).optional().describe('Estimated hours'),
|
|
578
862
|
estimated_cost: z.number().min(0).optional().describe('Estimated cost'),
|
|
579
863
|
site_id: z.string().uuid().optional().describe('Site ID — resolve first via list_sites'),
|
|
580
|
-
building_id: z
|
|
581
|
-
|
|
864
|
+
building_id: z
|
|
865
|
+
.string()
|
|
866
|
+
.uuid()
|
|
867
|
+
.optional()
|
|
868
|
+
.describe('Building ID — resolve second via list_buildings filtered by site_id'),
|
|
869
|
+
location_id: z
|
|
870
|
+
.string()
|
|
871
|
+
.uuid()
|
|
872
|
+
.optional()
|
|
873
|
+
.describe('Location ID — resolve last via list_locations filtered by building_id'),
|
|
582
874
|
asset_id: z.string().uuid().optional().describe('Asset ID'),
|
|
583
875
|
work_category: z.string().max(200).optional().describe('Work category label'),
|
|
584
876
|
schedule_type: z.string().max(100).optional().describe('Schedule type'),
|
|
@@ -589,17 +881,24 @@ export function registerWriteTools(server, client) {
|
|
|
589
881
|
auto_generate_wo: z.boolean().optional().describe('Auto-generate work orders'),
|
|
590
882
|
floating: z.boolean().optional().describe('Floating schedule (due date based on completion)'),
|
|
591
883
|
meter_based: z.boolean().optional().describe('Whether this PM triggers at meter intervals'),
|
|
592
|
-
meter_interval: z
|
|
884
|
+
meter_interval: z
|
|
885
|
+
.number()
|
|
886
|
+
.min(0)
|
|
887
|
+
.optional()
|
|
888
|
+
.describe('Meter interval — trigger every N units'),
|
|
593
889
|
meter_unit: z.string().max(50).optional().describe('Meter unit (km, miles, hours, cycles)'),
|
|
594
890
|
start_date: z.string().optional().describe('Start date (ISO 8601)'),
|
|
595
891
|
asset_ids: z.array(z.string().uuid()).optional().describe('Array of asset IDs'),
|
|
596
892
|
system_ids: z.array(z.string().uuid()).optional().describe('Array of system IDs'),
|
|
597
893
|
location_ids: z.array(z.string().uuid()).optional().describe('Array of location IDs'),
|
|
598
|
-
tasks: z
|
|
894
|
+
tasks: z
|
|
895
|
+
.array(z.object({
|
|
599
896
|
id: z.string().describe('Unique task ID'),
|
|
600
897
|
description: z.string().describe('Task description'),
|
|
601
898
|
completed: z.boolean().describe('Whether the task is completed'),
|
|
602
|
-
}))
|
|
899
|
+
}))
|
|
900
|
+
.optional()
|
|
901
|
+
.describe('Checklist of tasks for this PM schedule'),
|
|
603
902
|
}, async ({ id, ...rest }) => {
|
|
604
903
|
try {
|
|
605
904
|
const result = await client.update('pm-schedules', id, buildBody(rest));
|
|
@@ -624,27 +923,66 @@ export function registerWriteTools(server, client) {
|
|
|
624
923
|
server.tool('create_pm_template', 'Create a new PM template. Templates are reusable PM definitions (not linked to a site/asset) that can seed new PM schedules. Requires pm_templates:write scope.', {
|
|
625
924
|
title: z.string().min(1).max(500).describe('PM template title (required, unique per tenant)'),
|
|
626
925
|
description: z.string().optional().describe('Description'),
|
|
627
|
-
frequency: z
|
|
628
|
-
|
|
926
|
+
frequency: z
|
|
927
|
+
.enum([
|
|
928
|
+
'DAILY',
|
|
929
|
+
'WEEKLY',
|
|
930
|
+
'MONTHLY',
|
|
931
|
+
'QUARTERLY',
|
|
932
|
+
'SEMI_ANNUAL',
|
|
933
|
+
'ANNUAL',
|
|
934
|
+
'FIVE_YEARLY',
|
|
935
|
+
'CUSTOM',
|
|
936
|
+
])
|
|
937
|
+
.optional()
|
|
938
|
+
.describe('Suggested maintenance frequency'),
|
|
939
|
+
custom_interval_weeks: z
|
|
940
|
+
.number()
|
|
941
|
+
.int()
|
|
942
|
+
.min(1)
|
|
943
|
+
.optional()
|
|
944
|
+
.describe('Custom interval in weeks (when frequency is CUSTOM)'),
|
|
629
945
|
work_category: z.string().max(200).optional().describe('Work category label (free text)'),
|
|
630
|
-
work_category_id: z
|
|
946
|
+
work_category_id: z
|
|
947
|
+
.string()
|
|
948
|
+
.uuid()
|
|
949
|
+
.optional()
|
|
950
|
+
.describe('Work category ID — resolve via list_work_categories'),
|
|
631
951
|
estimated_hours: z.number().min(0).optional().describe('Estimated hours'),
|
|
632
952
|
estimated_cost: z.number().min(0).optional().describe('Estimated cost'),
|
|
633
953
|
safety_requirements: z.string().optional().describe('Safety requirements'),
|
|
634
|
-
tasks: z
|
|
954
|
+
tasks: z
|
|
955
|
+
.array(z.object({
|
|
635
956
|
id: z.string().optional().describe('Unique task ID (auto-generated if omitted)'),
|
|
636
957
|
description: z.string().describe('Task description'),
|
|
637
|
-
completed: z
|
|
638
|
-
|
|
639
|
-
|
|
958
|
+
completed: z
|
|
959
|
+
.boolean()
|
|
960
|
+
.optional()
|
|
961
|
+
.describe('Whether the task is completed (defaults to false)'),
|
|
962
|
+
}))
|
|
963
|
+
.optional()
|
|
964
|
+
.describe('Checklist of tasks baked into this template'),
|
|
965
|
+
resources: z
|
|
966
|
+
.array(z.object({
|
|
640
967
|
name: z.string().optional().describe('Resource name'),
|
|
641
|
-
type: z
|
|
968
|
+
type: z
|
|
969
|
+
.enum(PM_RESOURCE_TYPES)
|
|
970
|
+
.optional()
|
|
971
|
+
.describe('Resource type (TOOL, PART, MATERIAL, or EQUIPMENT)'),
|
|
642
972
|
quantity: z.number().optional().describe('Quantity required'),
|
|
643
973
|
cost: z.number().optional().describe('Unit cost'),
|
|
644
|
-
}))
|
|
974
|
+
}))
|
|
975
|
+
.optional()
|
|
976
|
+
.describe('Resource references (parts, tools, materials, equipment)'),
|
|
645
977
|
documents: z.array(z.record(z.unknown())).optional().describe('Document references'),
|
|
646
|
-
asset_ids: z
|
|
647
|
-
|
|
978
|
+
asset_ids: z
|
|
979
|
+
.array(z.string().uuid())
|
|
980
|
+
.optional()
|
|
981
|
+
.describe('Default asset IDs to seed on derived schedules'),
|
|
982
|
+
location_ids: z
|
|
983
|
+
.array(z.string().uuid())
|
|
984
|
+
.optional()
|
|
985
|
+
.describe('Default location IDs to seed on derived schedules'),
|
|
648
986
|
}, async (params) => {
|
|
649
987
|
try {
|
|
650
988
|
const result = await client.create('pm-templates', buildBody(normalizePmTemplateBody(params)));
|
|
@@ -658,24 +996,53 @@ export function registerWriteTools(server, client) {
|
|
|
658
996
|
id: z.string().uuid().describe('PM template ID'),
|
|
659
997
|
title: z.string().min(1).max(500).optional().describe('PM template title'),
|
|
660
998
|
description: z.string().optional().describe('Description'),
|
|
661
|
-
frequency: z
|
|
662
|
-
|
|
999
|
+
frequency: z
|
|
1000
|
+
.enum([
|
|
1001
|
+
'DAILY',
|
|
1002
|
+
'WEEKLY',
|
|
1003
|
+
'MONTHLY',
|
|
1004
|
+
'QUARTERLY',
|
|
1005
|
+
'SEMI_ANNUAL',
|
|
1006
|
+
'ANNUAL',
|
|
1007
|
+
'FIVE_YEARLY',
|
|
1008
|
+
'CUSTOM',
|
|
1009
|
+
])
|
|
1010
|
+
.optional()
|
|
1011
|
+
.describe('Suggested maintenance frequency'),
|
|
1012
|
+
custom_interval_weeks: z
|
|
1013
|
+
.number()
|
|
1014
|
+
.int()
|
|
1015
|
+
.min(1)
|
|
1016
|
+
.optional()
|
|
1017
|
+
.describe('Custom interval in weeks (when frequency is CUSTOM)'),
|
|
663
1018
|
work_category: z.string().max(200).optional().describe('Work category label (free text)'),
|
|
664
1019
|
work_category_id: z.string().uuid().optional().describe('Work category ID'),
|
|
665
1020
|
estimated_hours: z.number().min(0).optional().describe('Estimated hours'),
|
|
666
1021
|
estimated_cost: z.number().min(0).optional().describe('Estimated cost'),
|
|
667
1022
|
safety_requirements: z.string().optional().describe('Safety requirements'),
|
|
668
|
-
tasks: z
|
|
1023
|
+
tasks: z
|
|
1024
|
+
.array(z.object({
|
|
669
1025
|
id: z.string().optional().describe('Unique task ID (auto-generated if omitted)'),
|
|
670
1026
|
description: z.string().describe('Task description'),
|
|
671
|
-
completed: z
|
|
672
|
-
|
|
673
|
-
|
|
1027
|
+
completed: z
|
|
1028
|
+
.boolean()
|
|
1029
|
+
.optional()
|
|
1030
|
+
.describe('Whether the task is completed (defaults to false)'),
|
|
1031
|
+
}))
|
|
1032
|
+
.optional()
|
|
1033
|
+
.describe('Checklist of tasks baked into this template'),
|
|
1034
|
+
resources: z
|
|
1035
|
+
.array(z.object({
|
|
674
1036
|
name: z.string().optional().describe('Resource name'),
|
|
675
|
-
type: z
|
|
1037
|
+
type: z
|
|
1038
|
+
.enum(PM_RESOURCE_TYPES)
|
|
1039
|
+
.optional()
|
|
1040
|
+
.describe('Resource type (TOOL, PART, MATERIAL, or EQUIPMENT)'),
|
|
676
1041
|
quantity: z.number().optional().describe('Quantity required'),
|
|
677
1042
|
cost: z.number().optional().describe('Unit cost'),
|
|
678
|
-
}))
|
|
1043
|
+
}))
|
|
1044
|
+
.optional()
|
|
1045
|
+
.describe('Resource references (parts, tools, materials, equipment)'),
|
|
679
1046
|
documents: z.array(z.record(z.unknown())).optional().describe('Document references'),
|
|
680
1047
|
asset_ids: z.array(z.string().uuid()).optional().describe('Default asset IDs'),
|
|
681
1048
|
location_ids: z.array(z.string().uuid()).optional().describe('Default location IDs'),
|
|
@@ -705,16 +1072,42 @@ export function registerWriteTools(server, client) {
|
|
|
705
1072
|
status: z.string().max(100).describe('Project status (required)'),
|
|
706
1073
|
start_date: z.string().describe('Start date (ISO 8601, required)'),
|
|
707
1074
|
project_code: z.string().max(100).optional().describe('Project code'),
|
|
708
|
-
project_type: z
|
|
1075
|
+
project_type: z
|
|
1076
|
+
.enum([
|
|
1077
|
+
'capital',
|
|
1078
|
+
'maintenance',
|
|
1079
|
+
'repair',
|
|
1080
|
+
'upgrade',
|
|
1081
|
+
'new_construction',
|
|
1082
|
+
'renovation',
|
|
1083
|
+
'deferred_maintenance',
|
|
1084
|
+
'other',
|
|
1085
|
+
])
|
|
1086
|
+
.optional()
|
|
1087
|
+
.describe('Project type'),
|
|
709
1088
|
current_phase: z.string().max(100).optional().describe('Current phase'),
|
|
710
1089
|
description: z.string().optional().describe('Description'),
|
|
711
1090
|
end_date: z.string().optional().describe('End date (ISO 8601)'),
|
|
712
1091
|
budget: z.number().min(0).optional().describe('Total budget'),
|
|
713
1092
|
project_manager: z.string().max(200).optional().describe('Project manager name'),
|
|
714
|
-
progress_percentage: z
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
1093
|
+
progress_percentage: z
|
|
1094
|
+
.number()
|
|
1095
|
+
.min(0)
|
|
1096
|
+
.max(100)
|
|
1097
|
+
.optional()
|
|
1098
|
+
.describe('Progress percentage (0-100)'),
|
|
1099
|
+
health_status: z
|
|
1100
|
+
.enum(['on_track', 'at_risk', 'delayed', 'critical'])
|
|
1101
|
+
.optional()
|
|
1102
|
+
.describe('Health status'),
|
|
1103
|
+
budget_status: z
|
|
1104
|
+
.enum(['off_track', 'on_track', 'not_set', 'monitor'])
|
|
1105
|
+
.optional()
|
|
1106
|
+
.describe('Budget status'),
|
|
1107
|
+
progress_status: z
|
|
1108
|
+
.enum(['off_track', 'on_track', 'monitor'])
|
|
1109
|
+
.optional()
|
|
1110
|
+
.describe('Progress status'),
|
|
718
1111
|
image_url: z.string().max(2000).optional().describe('Image URL'),
|
|
719
1112
|
}, async (params) => {
|
|
720
1113
|
try {
|
|
@@ -731,16 +1124,42 @@ export function registerWriteTools(server, client) {
|
|
|
731
1124
|
status: z.string().max(100).optional().describe('Project status'),
|
|
732
1125
|
start_date: z.string().optional().describe('Start date (ISO 8601)'),
|
|
733
1126
|
project_code: z.string().max(100).optional().describe('Project code'),
|
|
734
|
-
project_type: z
|
|
1127
|
+
project_type: z
|
|
1128
|
+
.enum([
|
|
1129
|
+
'capital',
|
|
1130
|
+
'maintenance',
|
|
1131
|
+
'repair',
|
|
1132
|
+
'upgrade',
|
|
1133
|
+
'new_construction',
|
|
1134
|
+
'renovation',
|
|
1135
|
+
'deferred_maintenance',
|
|
1136
|
+
'other',
|
|
1137
|
+
])
|
|
1138
|
+
.optional()
|
|
1139
|
+
.describe('Project type'),
|
|
735
1140
|
current_phase: z.string().max(100).optional().describe('Current phase'),
|
|
736
1141
|
description: z.string().optional().describe('Description'),
|
|
737
1142
|
end_date: z.string().optional().describe('End date (ISO 8601)'),
|
|
738
1143
|
budget: z.number().min(0).optional().describe('Total budget'),
|
|
739
1144
|
project_manager: z.string().max(200).optional().describe('Project manager name'),
|
|
740
|
-
progress_percentage: z
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
1145
|
+
progress_percentage: z
|
|
1146
|
+
.number()
|
|
1147
|
+
.min(0)
|
|
1148
|
+
.max(100)
|
|
1149
|
+
.optional()
|
|
1150
|
+
.describe('Progress percentage (0-100)'),
|
|
1151
|
+
health_status: z
|
|
1152
|
+
.enum(['on_track', 'at_risk', 'delayed', 'critical'])
|
|
1153
|
+
.optional()
|
|
1154
|
+
.describe('Health status'),
|
|
1155
|
+
budget_status: z
|
|
1156
|
+
.enum(['off_track', 'on_track', 'not_set', 'monitor'])
|
|
1157
|
+
.optional()
|
|
1158
|
+
.describe('Budget status'),
|
|
1159
|
+
progress_status: z
|
|
1160
|
+
.enum(['off_track', 'on_track', 'monitor'])
|
|
1161
|
+
.optional()
|
|
1162
|
+
.describe('Progress status'),
|
|
744
1163
|
image_url: z.string().max(2000).optional().describe('Image URL'),
|
|
745
1164
|
}, async ({ id, ...rest }) => {
|
|
746
1165
|
try {
|
|
@@ -824,7 +1243,10 @@ export function registerWriteTools(server, client) {
|
|
|
824
1243
|
tax_amount: z.number().min(0).optional().describe('Tax amount'),
|
|
825
1244
|
due_date: z.string().optional().describe('Due date (ISO 8601)'),
|
|
826
1245
|
paid_date: z.string().optional().describe('Paid date (ISO 8601)'),
|
|
827
|
-
status: z
|
|
1246
|
+
status: z
|
|
1247
|
+
.enum(['pending', 'approved', 'paid', 'voided'])
|
|
1248
|
+
.optional()
|
|
1249
|
+
.describe('Invoice status'),
|
|
828
1250
|
notes: z.string().optional().describe('Notes'),
|
|
829
1251
|
project_id: z.string().uuid().optional().describe('Project ID'),
|
|
830
1252
|
work_order_id: z.string().uuid().optional().describe('Work order ID'),
|
|
@@ -849,7 +1271,10 @@ export function registerWriteTools(server, client) {
|
|
|
849
1271
|
tax_amount: z.number().min(0).optional().describe('Tax amount'),
|
|
850
1272
|
due_date: z.string().optional().describe('Due date (ISO 8601)'),
|
|
851
1273
|
paid_date: z.string().optional().describe('Paid date (ISO 8601)'),
|
|
852
|
-
status: z
|
|
1274
|
+
status: z
|
|
1275
|
+
.enum(['pending', 'approved', 'paid', 'voided'])
|
|
1276
|
+
.optional()
|
|
1277
|
+
.describe('Invoice status'),
|
|
853
1278
|
notes: z.string().optional().describe('Notes'),
|
|
854
1279
|
project_id: z.string().uuid().optional().describe('Project ID'),
|
|
855
1280
|
work_order_id: z.string().uuid().optional().describe('Work order ID'),
|
|
@@ -881,7 +1306,10 @@ export function registerWriteTools(server, client) {
|
|
|
881
1306
|
po_number: z.string().min(1).max(200).describe('PO number (required)'),
|
|
882
1307
|
amount: z.number().min(0).describe('PO amount (required)'),
|
|
883
1308
|
description: z.string().optional().describe('Description'),
|
|
884
|
-
status: z
|
|
1309
|
+
status: z
|
|
1310
|
+
.enum(['draft', 'issued', 'partially_received', 'received', 'closed', 'cancelled'])
|
|
1311
|
+
.optional()
|
|
1312
|
+
.describe('PO status'),
|
|
885
1313
|
issued_date: z.string().optional().describe('Issued date (ISO 8601)'),
|
|
886
1314
|
expected_date: z.string().optional().describe('Expected delivery date (ISO 8601)'),
|
|
887
1315
|
notes: z.string().optional().describe('Notes'),
|
|
@@ -903,7 +1331,10 @@ export function registerWriteTools(server, client) {
|
|
|
903
1331
|
po_number: z.string().min(1).max(200).optional().describe('PO number'),
|
|
904
1332
|
amount: z.number().min(0).optional().describe('PO amount'),
|
|
905
1333
|
description: z.string().optional().describe('Description'),
|
|
906
|
-
status: z
|
|
1334
|
+
status: z
|
|
1335
|
+
.enum(['draft', 'issued', 'partially_received', 'received', 'closed', 'cancelled'])
|
|
1336
|
+
.optional()
|
|
1337
|
+
.describe('PO status'),
|
|
907
1338
|
issued_date: z.string().optional().describe('Issued date (ISO 8601)'),
|
|
908
1339
|
expected_date: z.string().optional().describe('Expected delivery date (ISO 8601)'),
|
|
909
1340
|
notes: z.string().optional().describe('Notes'),
|
|
@@ -1037,7 +1468,10 @@ export function registerWriteTools(server, client) {
|
|
|
1037
1468
|
server.tool('create_project_document_folder_template', 'Create a project document folder template. Requires project_document_folder_templates:write scope.', {
|
|
1038
1469
|
name: z.string().min(1).max(500).describe('Template name (required)'),
|
|
1039
1470
|
description: z.string().max(2000).optional().describe('Template description'),
|
|
1040
|
-
structure: z
|
|
1471
|
+
structure: z
|
|
1472
|
+
.array(z.record(z.string(), z.unknown()))
|
|
1473
|
+
.optional()
|
|
1474
|
+
.describe('Folder hierarchy as JSON array'),
|
|
1041
1475
|
is_default: z.boolean().optional().describe('Whether this is the default template'),
|
|
1042
1476
|
}, async (params) => {
|
|
1043
1477
|
try {
|
|
@@ -1052,7 +1486,10 @@ export function registerWriteTools(server, client) {
|
|
|
1052
1486
|
id: z.string().uuid().describe('Template ID'),
|
|
1053
1487
|
name: z.string().min(1).max(500).optional().describe('Template name'),
|
|
1054
1488
|
description: z.string().max(2000).optional().describe('Template description'),
|
|
1055
|
-
structure: z
|
|
1489
|
+
structure: z
|
|
1490
|
+
.array(z.record(z.string(), z.unknown()))
|
|
1491
|
+
.optional()
|
|
1492
|
+
.describe('Folder hierarchy as JSON array'),
|
|
1056
1493
|
is_default: z.boolean().optional().describe('Whether this is the default template'),
|
|
1057
1494
|
}, async ({ id, ...rest }) => {
|
|
1058
1495
|
try {
|
|
@@ -1125,7 +1562,6 @@ export function registerWriteTools(server, client) {
|
|
|
1125
1562
|
server.tool('create_asset_comment', 'Create a new comment on an asset. Requires asset_comments:write scope.', {
|
|
1126
1563
|
asset_id: z.string().uuid().describe('Asset ID (required)'),
|
|
1127
1564
|
comment: z.string().min(1).describe('Comment text (required)'),
|
|
1128
|
-
user_id: z.string().max(200).optional().describe('User ID of commenter'),
|
|
1129
1565
|
}, async (params) => {
|
|
1130
1566
|
try {
|
|
1131
1567
|
const result = await client.create('asset-comments', buildBody(params));
|
|
@@ -1138,7 +1574,6 @@ export function registerWriteTools(server, client) {
|
|
|
1138
1574
|
server.tool('update_asset_comment', 'Update an existing asset comment by ID. Requires asset_comments:write scope.', {
|
|
1139
1575
|
id: z.string().uuid().describe('Asset comment ID'),
|
|
1140
1576
|
comment: z.string().min(1).optional().describe('Comment text'),
|
|
1141
|
-
user_id: z.string().max(200).optional().describe('User ID of commenter'),
|
|
1142
1577
|
}, async ({ id, ...rest }) => {
|
|
1143
1578
|
try {
|
|
1144
1579
|
const result = await client.update('asset-comments', id, buildBody(rest));
|
|
@@ -1163,7 +1598,6 @@ export function registerWriteTools(server, client) {
|
|
|
1163
1598
|
server.tool('create_work_order_comment', 'Create a new comment on a work order. Requires work_order_comments:write scope.', {
|
|
1164
1599
|
work_order_id: z.string().uuid().describe('Work order ID (required)'),
|
|
1165
1600
|
comment: z.string().min(1).describe('Comment text (required)'),
|
|
1166
|
-
user_id: z.string().max(200).optional().describe('User ID of commenter'),
|
|
1167
1601
|
}, async (params) => {
|
|
1168
1602
|
try {
|
|
1169
1603
|
const result = await client.create('work-order-comments', buildBody(params));
|
|
@@ -1176,7 +1610,6 @@ export function registerWriteTools(server, client) {
|
|
|
1176
1610
|
server.tool('update_work_order_comment', 'Update an existing work order comment by ID. Requires work_order_comments:write scope.', {
|
|
1177
1611
|
id: z.string().uuid().describe('Work order comment ID'),
|
|
1178
1612
|
comment: z.string().min(1).optional().describe('Comment text'),
|
|
1179
|
-
user_id: z.string().max(200).optional().describe('User ID of commenter'),
|
|
1180
1613
|
}, async ({ id, ...rest }) => {
|
|
1181
1614
|
try {
|
|
1182
1615
|
const result = await client.update('work-order-comments', id, buildBody(rest));
|
|
@@ -1202,7 +1635,6 @@ export function registerWriteTools(server, client) {
|
|
|
1202
1635
|
project_id: z.string().uuid().describe('Project ID (required)'),
|
|
1203
1636
|
content: z.string().min(1).describe('Comment content (required)'),
|
|
1204
1637
|
parent_id: z.string().uuid().optional().describe('Parent comment ID (for threading)'),
|
|
1205
|
-
user_id: z.string().max(200).optional().describe('User ID of commenter'),
|
|
1206
1638
|
}, async (params) => {
|
|
1207
1639
|
try {
|
|
1208
1640
|
const result = await client.create('project-comments', buildBody(params));
|
|
@@ -1216,7 +1648,6 @@ export function registerWriteTools(server, client) {
|
|
|
1216
1648
|
id: z.string().uuid().describe('Project comment ID'),
|
|
1217
1649
|
content: z.string().min(1).optional().describe('Comment content'),
|
|
1218
1650
|
parent_id: z.string().uuid().optional().describe('Parent comment ID'),
|
|
1219
|
-
user_id: z.string().max(200).optional().describe('User ID of commenter'),
|
|
1220
1651
|
}, async ({ id, ...rest }) => {
|
|
1221
1652
|
try {
|
|
1222
1653
|
const result = await client.update('project-comments', id, buildBody(rest));
|
|
@@ -1239,7 +1670,9 @@ export function registerWriteTools(server, client) {
|
|
|
1239
1670
|
// 18. Asset Costs (scope: asset_costs)
|
|
1240
1671
|
// ============================================================
|
|
1241
1672
|
server.tool('create_asset_cost', 'Create a new asset cost entry. Requires asset_costs:write scope.', {
|
|
1242
|
-
category: z
|
|
1673
|
+
category: z
|
|
1674
|
+
.enum(['Repair', 'PM', 'Operation', 'Replacement', 'Decommission', 'Other'])
|
|
1675
|
+
.describe('Cost category (required)'),
|
|
1243
1676
|
amount: z.number().min(0).describe('Cost amount (required)'),
|
|
1244
1677
|
cost_date: z.string().describe('Cost date (ISO 8601, required)'),
|
|
1245
1678
|
asset_id: z.string().uuid().optional().describe('Asset ID'),
|
|
@@ -1258,7 +1691,10 @@ export function registerWriteTools(server, client) {
|
|
|
1258
1691
|
});
|
|
1259
1692
|
server.tool('update_asset_cost', 'Update an existing asset cost entry by ID. Requires asset_costs:write scope.', {
|
|
1260
1693
|
id: z.string().uuid().describe('Asset cost ID'),
|
|
1261
|
-
category: z
|
|
1694
|
+
category: z
|
|
1695
|
+
.enum(['Repair', 'PM', 'Operation', 'Replacement', 'Decommission', 'Other'])
|
|
1696
|
+
.optional()
|
|
1697
|
+
.describe('Cost category'),
|
|
1262
1698
|
amount: z.number().min(0).optional().describe('Cost amount'),
|
|
1263
1699
|
cost_date: z.string().optional().describe('Cost date (ISO 8601)'),
|
|
1264
1700
|
asset_id: z.string().uuid().optional().describe('Asset ID'),
|
|
@@ -1289,10 +1725,18 @@ export function registerWriteTools(server, client) {
|
|
|
1289
1725
|
// ============================================================
|
|
1290
1726
|
server.tool('create_asset_replacement_plan', 'Create a new asset replacement plan. Requires asset_replacement_plans:write scope.', {
|
|
1291
1727
|
asset_id: z.string().uuid().describe('Asset ID (required)'),
|
|
1292
|
-
planned_replacement_year: z
|
|
1728
|
+
planned_replacement_year: z
|
|
1729
|
+
.number()
|
|
1730
|
+
.int()
|
|
1731
|
+
.min(2000)
|
|
1732
|
+
.max(2100)
|
|
1733
|
+
.describe('Planned replacement year (required)'),
|
|
1293
1734
|
estimated_cost: z.number().min(0).optional().describe('Estimated replacement cost'),
|
|
1294
1735
|
priority: z.enum(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']).optional().describe('Priority'),
|
|
1295
|
-
status: z
|
|
1736
|
+
status: z
|
|
1737
|
+
.enum(['PLANNED', 'BUDGETED', 'APPROVED', 'COMPLETED', 'CANCELLED'])
|
|
1738
|
+
.optional()
|
|
1739
|
+
.describe('Status'),
|
|
1296
1740
|
notes: z.string().optional().describe('Notes'),
|
|
1297
1741
|
funding_source: z.string().max(200).optional().describe('Funding source'),
|
|
1298
1742
|
}, async (params) => {
|
|
@@ -1307,10 +1751,19 @@ export function registerWriteTools(server, client) {
|
|
|
1307
1751
|
server.tool('update_asset_replacement_plan', 'Update an existing asset replacement plan by ID. Requires asset_replacement_plans:write scope.', {
|
|
1308
1752
|
id: z.string().uuid().describe('Asset replacement plan ID'),
|
|
1309
1753
|
asset_id: z.string().uuid().optional().describe('Asset ID'),
|
|
1310
|
-
planned_replacement_year: z
|
|
1754
|
+
planned_replacement_year: z
|
|
1755
|
+
.number()
|
|
1756
|
+
.int()
|
|
1757
|
+
.min(2000)
|
|
1758
|
+
.max(2100)
|
|
1759
|
+
.optional()
|
|
1760
|
+
.describe('Planned replacement year'),
|
|
1311
1761
|
estimated_cost: z.number().min(0).optional().describe('Estimated replacement cost'),
|
|
1312
1762
|
priority: z.enum(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']).optional().describe('Priority'),
|
|
1313
|
-
status: z
|
|
1763
|
+
status: z
|
|
1764
|
+
.enum(['PLANNED', 'BUDGETED', 'APPROVED', 'COMPLETED', 'CANCELLED'])
|
|
1765
|
+
.optional()
|
|
1766
|
+
.describe('Status'),
|
|
1314
1767
|
notes: z.string().optional().describe('Notes'),
|
|
1315
1768
|
funding_source: z.string().max(200).optional().describe('Funding source'),
|
|
1316
1769
|
}, async ({ id, ...rest }) => {
|
|
@@ -1339,7 +1792,10 @@ export function registerWriteTools(server, client) {
|
|
|
1339
1792
|
title: z.string().min(1).max(500).describe('Task title (required)'),
|
|
1340
1793
|
description: z.string().optional().describe('Description'),
|
|
1341
1794
|
phase_id: z.string().uuid().optional().describe('Phase ID'),
|
|
1342
|
-
status: z
|
|
1795
|
+
status: z
|
|
1796
|
+
.enum(['todo', 'in_progress', 'completed', 'blocked', 'cancelled'])
|
|
1797
|
+
.optional()
|
|
1798
|
+
.describe('Task status'),
|
|
1343
1799
|
priority: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Priority'),
|
|
1344
1800
|
assigned_to: z.string().max(200).optional().describe('Assigned user'),
|
|
1345
1801
|
start_date: z.string().optional().describe('Start date (ISO 8601)'),
|
|
@@ -1361,7 +1817,10 @@ export function registerWriteTools(server, client) {
|
|
|
1361
1817
|
title: z.string().min(1).max(500).optional().describe('Task title'),
|
|
1362
1818
|
description: z.string().optional().describe('Description'),
|
|
1363
1819
|
phase_id: z.string().uuid().optional().describe('Phase ID'),
|
|
1364
|
-
status: z
|
|
1820
|
+
status: z
|
|
1821
|
+
.enum(['todo', 'in_progress', 'completed', 'blocked', 'cancelled'])
|
|
1822
|
+
.optional()
|
|
1823
|
+
.describe('Task status'),
|
|
1365
1824
|
priority: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Priority'),
|
|
1366
1825
|
assigned_to: z.string().max(200).optional().describe('Assigned user'),
|
|
1367
1826
|
start_date: z.string().optional().describe('Start date (ISO 8601)'),
|
|
@@ -1394,7 +1853,10 @@ export function registerWriteTools(server, client) {
|
|
|
1394
1853
|
name: z.string().min(1).max(500).describe('Milestone name (required)'),
|
|
1395
1854
|
due_date: z.string().describe('Due date (ISO 8601, required)'),
|
|
1396
1855
|
description: z.string().optional().describe('Description'),
|
|
1397
|
-
status: z
|
|
1856
|
+
status: z
|
|
1857
|
+
.enum(['pending', 'completed', 'missed', 'at_risk'])
|
|
1858
|
+
.optional()
|
|
1859
|
+
.describe('Milestone status'),
|
|
1398
1860
|
completed_date: z.string().optional().describe('Completed date (ISO 8601)'),
|
|
1399
1861
|
}, async (params) => {
|
|
1400
1862
|
try {
|
|
@@ -1411,7 +1873,10 @@ export function registerWriteTools(server, client) {
|
|
|
1411
1873
|
name: z.string().min(1).max(500).optional().describe('Milestone name'),
|
|
1412
1874
|
due_date: z.string().optional().describe('Due date (ISO 8601)'),
|
|
1413
1875
|
description: z.string().optional().describe('Description'),
|
|
1414
|
-
status: z
|
|
1876
|
+
status: z
|
|
1877
|
+
.enum(['pending', 'completed', 'missed', 'at_risk'])
|
|
1878
|
+
.optional()
|
|
1879
|
+
.describe('Milestone status'),
|
|
1415
1880
|
completed_date: z.string().optional().describe('Completed date (ISO 8601)'),
|
|
1416
1881
|
}, async ({ id, ...rest }) => {
|
|
1417
1882
|
try {
|
|
@@ -1438,7 +1903,10 @@ export function registerWriteTools(server, client) {
|
|
|
1438
1903
|
project_id: z.string().uuid().describe('Project ID (required)'),
|
|
1439
1904
|
name: z.string().min(1).max(500).describe('Phase name (required)'),
|
|
1440
1905
|
description: z.string().optional().describe('Description'),
|
|
1441
|
-
status: z
|
|
1906
|
+
status: z
|
|
1907
|
+
.enum(['pending', 'in_progress', 'completed', 'skipped'])
|
|
1908
|
+
.optional()
|
|
1909
|
+
.describe('Phase status'),
|
|
1442
1910
|
start_date: z.string().optional().describe('Start date (ISO 8601)'),
|
|
1443
1911
|
end_date: z.string().optional().describe('End date (ISO 8601)'),
|
|
1444
1912
|
sort_order: z.number().int().min(0).optional().describe('Sort order'),
|
|
@@ -1456,7 +1924,10 @@ export function registerWriteTools(server, client) {
|
|
|
1456
1924
|
project_id: z.string().uuid().optional().describe('Project ID'),
|
|
1457
1925
|
name: z.string().min(1).max(500).optional().describe('Phase name'),
|
|
1458
1926
|
description: z.string().optional().describe('Description'),
|
|
1459
|
-
status: z
|
|
1927
|
+
status: z
|
|
1928
|
+
.enum(['pending', 'in_progress', 'completed', 'skipped'])
|
|
1929
|
+
.optional()
|
|
1930
|
+
.describe('Phase status'),
|
|
1460
1931
|
start_date: z.string().optional().describe('Start date (ISO 8601)'),
|
|
1461
1932
|
end_date: z.string().optional().describe('End date (ISO 8601)'),
|
|
1462
1933
|
sort_order: z.number().int().min(0).optional().describe('Sort order'),
|
|
@@ -1483,7 +1954,17 @@ export function registerWriteTools(server, client) {
|
|
|
1483
1954
|
// ============================================================
|
|
1484
1955
|
server.tool('create_project_budget_item', 'Create a new project budget item. Requires project_budget_items:write scope.', {
|
|
1485
1956
|
project_id: z.string().uuid().describe('Project ID (required)'),
|
|
1486
|
-
category: z
|
|
1957
|
+
category: z
|
|
1958
|
+
.enum([
|
|
1959
|
+
'labor',
|
|
1960
|
+
'materials',
|
|
1961
|
+
'equipment',
|
|
1962
|
+
'subcontractors',
|
|
1963
|
+
'permits',
|
|
1964
|
+
'contingency',
|
|
1965
|
+
'other',
|
|
1966
|
+
])
|
|
1967
|
+
.describe('Budget category (required)'),
|
|
1487
1968
|
description: z.string().optional().describe('Description'),
|
|
1488
1969
|
estimated_amount: z.number().min(0).optional().describe('Estimated amount'),
|
|
1489
1970
|
actual_amount: z.number().min(0).optional().describe('Actual amount'),
|
|
@@ -1499,7 +1980,18 @@ export function registerWriteTools(server, client) {
|
|
|
1499
1980
|
server.tool('update_project_budget_item', 'Update an existing project budget item by ID. Requires project_budget_items:write scope.', {
|
|
1500
1981
|
id: z.string().uuid().describe('Project budget item ID'),
|
|
1501
1982
|
project_id: z.string().uuid().optional().describe('Project ID'),
|
|
1502
|
-
category: z
|
|
1983
|
+
category: z
|
|
1984
|
+
.enum([
|
|
1985
|
+
'labor',
|
|
1986
|
+
'materials',
|
|
1987
|
+
'equipment',
|
|
1988
|
+
'subcontractors',
|
|
1989
|
+
'permits',
|
|
1990
|
+
'contingency',
|
|
1991
|
+
'other',
|
|
1992
|
+
])
|
|
1993
|
+
.optional()
|
|
1994
|
+
.describe('Budget category'),
|
|
1503
1995
|
description: z.string().optional().describe('Description'),
|
|
1504
1996
|
estimated_amount: z.number().min(0).optional().describe('Estimated amount'),
|
|
1505
1997
|
actual_amount: z.number().min(0).optional().describe('Actual amount'),
|
|
@@ -1810,7 +2302,13 @@ export function registerWriteTools(server, client) {
|
|
|
1810
2302
|
server.tool('create_project_phase_category', 'Create a new project phase category. Requires project_phase_categories:write scope.', {
|
|
1811
2303
|
name: z.string().min(1).max(500).describe('Phase name (required), e.g. "Planning", "Design"'),
|
|
1812
2304
|
description: z.string().max(2000).optional().describe('Description of the phase'),
|
|
1813
|
-
sort_order: z
|
|
2305
|
+
sort_order: z
|
|
2306
|
+
.number()
|
|
2307
|
+
.int()
|
|
2308
|
+
.min(0)
|
|
2309
|
+
.max(10000)
|
|
2310
|
+
.optional()
|
|
2311
|
+
.describe('Display order (lower = first)'),
|
|
1814
2312
|
}, async (params) => {
|
|
1815
2313
|
try {
|
|
1816
2314
|
const result = await client.create('project-phase-categories', buildBody(params));
|
|
@@ -1824,7 +2322,13 @@ export function registerWriteTools(server, client) {
|
|
|
1824
2322
|
id: z.string().uuid().describe('Project phase category ID'),
|
|
1825
2323
|
name: z.string().min(1).max(500).optional().describe('Phase name'),
|
|
1826
2324
|
description: z.string().max(2000).optional().describe('Description'),
|
|
1827
|
-
sort_order: z
|
|
2325
|
+
sort_order: z
|
|
2326
|
+
.number()
|
|
2327
|
+
.int()
|
|
2328
|
+
.min(0)
|
|
2329
|
+
.max(10000)
|
|
2330
|
+
.optional()
|
|
2331
|
+
.describe('Display order (lower = first)'),
|
|
1828
2332
|
}, async ({ id, ...rest }) => {
|
|
1829
2333
|
try {
|
|
1830
2334
|
const result = await client.update('project-phase-categories', id, buildBody(rest));
|
|
@@ -2129,9 +2633,15 @@ export function registerWriteTools(server, client) {
|
|
|
2129
2633
|
// 35. Custom Field Definitions (scope: custom_fields)
|
|
2130
2634
|
// ============================================================
|
|
2131
2635
|
server.tool('create_custom_field_definition', 'Create a new custom field definition. Requires custom_fields:write scope.', {
|
|
2132
|
-
entity_type: z
|
|
2636
|
+
entity_type: z
|
|
2637
|
+
.string()
|
|
2638
|
+
.min(1)
|
|
2639
|
+
.max(100)
|
|
2640
|
+
.describe('Entity type this field applies to (required)'),
|
|
2133
2641
|
field_name: z.string().min(1).max(200).describe('Field name / key (required)'),
|
|
2134
|
-
field_type: z
|
|
2642
|
+
field_type: z
|
|
2643
|
+
.enum(['text', 'number', 'date', 'boolean', 'select'])
|
|
2644
|
+
.describe('Field data type (required)'),
|
|
2135
2645
|
field_label: z.string().max(200).optional().describe('Display label'),
|
|
2136
2646
|
options: z.array(z.string()).optional().describe('Options for select-type fields'),
|
|
2137
2647
|
is_required: z.boolean().optional().describe('Whether the field is required'),
|
|
@@ -2149,7 +2659,10 @@ export function registerWriteTools(server, client) {
|
|
|
2149
2659
|
id: z.string().uuid().describe('Custom field definition ID'),
|
|
2150
2660
|
entity_type: z.string().min(1).max(100).optional().describe('Entity type'),
|
|
2151
2661
|
field_name: z.string().min(1).max(200).optional().describe('Field name / key'),
|
|
2152
|
-
field_type: z
|
|
2662
|
+
field_type: z
|
|
2663
|
+
.enum(['text', 'number', 'date', 'boolean', 'select'])
|
|
2664
|
+
.optional()
|
|
2665
|
+
.describe('Field data type'),
|
|
2153
2666
|
field_label: z.string().max(200).optional().describe('Display label'),
|
|
2154
2667
|
options: z.array(z.string()).optional().describe('Options for select-type fields'),
|
|
2155
2668
|
is_required: z.boolean().optional().describe('Whether the field is required'),
|
|
@@ -2175,14 +2688,38 @@ export function registerWriteTools(server, client) {
|
|
|
2175
2688
|
// ============================================================
|
|
2176
2689
|
// 36. Custom Field Values (scope: custom_fields)
|
|
2177
2690
|
// ============================================================
|
|
2178
|
-
server.tool('create_custom_field_value',
|
|
2691
|
+
server.tool('create_custom_field_value', "Create (upsert) a custom field value for an entity. The table stores values in typed columns — prefer setting the one that matches the field definition's field_type: value_text (text/select), value_number (number), value_date (date, ISO YYYY-MM-DD), value_boolean (boolean). Alternatively pass a single `value` string and the server will dispatch it to the right column based on field_type. Writes upsert on (entity_id, field_definition_id) so replaying a batch is idempotent. Requires custom_fields:write scope.", {
|
|
2179
2692
|
entity_id: z.string().uuid().describe('Entity ID — e.g. asset.id, work_order.id (required)'),
|
|
2180
|
-
field_definition_id: z
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2693
|
+
field_definition_id: z
|
|
2694
|
+
.string()
|
|
2695
|
+
.uuid()
|
|
2696
|
+
.describe('Custom field definition ID — resolve via list_custom_field_definitions (required)'),
|
|
2697
|
+
value_text: z
|
|
2698
|
+
.string()
|
|
2699
|
+
.max(5000)
|
|
2700
|
+
.nullable()
|
|
2701
|
+
.optional()
|
|
2702
|
+
.describe('Text value (use for field_type=text or select)'),
|
|
2703
|
+
value_number: z
|
|
2704
|
+
.number()
|
|
2705
|
+
.nullable()
|
|
2706
|
+
.optional()
|
|
2707
|
+
.describe('Numeric value (use for field_type=number)'),
|
|
2708
|
+
value_date: z
|
|
2709
|
+
.string()
|
|
2710
|
+
.nullable()
|
|
2711
|
+
.optional()
|
|
2712
|
+
.describe('Date value, ISO YYYY-MM-DD (use for field_type=date)'),
|
|
2713
|
+
value_boolean: z
|
|
2714
|
+
.boolean()
|
|
2715
|
+
.nullable()
|
|
2716
|
+
.optional()
|
|
2717
|
+
.describe('Boolean value (use for field_type=boolean)'),
|
|
2718
|
+
value: z
|
|
2719
|
+
.string()
|
|
2720
|
+
.max(5000)
|
|
2721
|
+
.optional()
|
|
2722
|
+
.describe("Legacy single-value shim — server dispatches to the correct typed column based on the field definition's field_type. Ignored if any value_* typed column is set."),
|
|
2186
2723
|
}, async (params) => {
|
|
2187
2724
|
try {
|
|
2188
2725
|
const result = await client.create('custom-field-values', buildBody(params));
|
|
@@ -2192,15 +2729,23 @@ export function registerWriteTools(server, client) {
|
|
|
2192
2729
|
return formatError(err);
|
|
2193
2730
|
}
|
|
2194
2731
|
});
|
|
2195
|
-
server.tool('update_custom_field_value',
|
|
2732
|
+
server.tool('update_custom_field_value', "Update an existing custom field value by ID. Set whichever typed column matches the definition's field_type (value_text / value_number / value_date / value_boolean), or pass a single `value` and the server will dispatch it. Requires custom_fields:write scope.", {
|
|
2196
2733
|
id: z.string().uuid().describe('Custom field value ID'),
|
|
2197
2734
|
entity_id: z.string().uuid().optional().describe('Entity ID'),
|
|
2198
|
-
field_definition_id: z
|
|
2735
|
+
field_definition_id: z
|
|
2736
|
+
.string()
|
|
2737
|
+
.uuid()
|
|
2738
|
+
.optional()
|
|
2739
|
+
.describe('Custom field definition ID — required when using the `value` fallback if you want to avoid the server fetching it'),
|
|
2199
2740
|
value_text: z.string().max(5000).nullable().optional().describe('Text value'),
|
|
2200
2741
|
value_number: z.number().nullable().optional().describe('Numeric value'),
|
|
2201
2742
|
value_date: z.string().nullable().optional().describe('Date value, ISO YYYY-MM-DD'),
|
|
2202
2743
|
value_boolean: z.boolean().nullable().optional().describe('Boolean value'),
|
|
2203
|
-
value: z
|
|
2744
|
+
value: z
|
|
2745
|
+
.string()
|
|
2746
|
+
.max(5000)
|
|
2747
|
+
.optional()
|
|
2748
|
+
.describe('Legacy single-value shim — server dispatches based on field_type'),
|
|
2204
2749
|
}, async ({ id, ...rest }) => {
|
|
2205
2750
|
try {
|
|
2206
2751
|
const result = await client.update('custom-field-values', id, buildBody(rest));
|
|
@@ -2226,10 +2771,27 @@ export function registerWriteTools(server, client) {
|
|
|
2226
2771
|
name: z.string().min(1).max(500).describe('Part name (required)'),
|
|
2227
2772
|
part_number: z.string().max(200).optional().describe('Part number / SKU'),
|
|
2228
2773
|
category: z.string().max(200).optional().describe('Category label'),
|
|
2229
|
-
supplier: z
|
|
2774
|
+
supplier: z
|
|
2775
|
+
.string()
|
|
2776
|
+
.max(500)
|
|
2777
|
+
.optional()
|
|
2778
|
+
.describe('DEPRECATED — legacy free-text supplier name. Use supplier_id instead.'),
|
|
2779
|
+
supplier_id: z.string().uuid().optional().describe('Vendor ID (resolve via list_vendors)'),
|
|
2230
2780
|
cost: z.number().min(0).optional().describe('Unit cost'),
|
|
2231
|
-
quantity: z
|
|
2232
|
-
|
|
2781
|
+
quantity: z
|
|
2782
|
+
.number()
|
|
2783
|
+
.int()
|
|
2784
|
+
.min(0)
|
|
2785
|
+
.max(10_000_000)
|
|
2786
|
+
.optional()
|
|
2787
|
+
.describe('Current stock quantity'),
|
|
2788
|
+
desired_quantity: z
|
|
2789
|
+
.number()
|
|
2790
|
+
.int()
|
|
2791
|
+
.min(0)
|
|
2792
|
+
.max(10_000_000)
|
|
2793
|
+
.optional()
|
|
2794
|
+
.describe('Target / reorder quantity'),
|
|
2233
2795
|
specific_location: z.string().max(500).optional().describe('Storage location description'),
|
|
2234
2796
|
site_id: z.string().uuid().optional().describe('Site ID'),
|
|
2235
2797
|
building_id: z.string().uuid().optional().describe('Building ID'),
|
|
@@ -2248,10 +2810,27 @@ export function registerWriteTools(server, client) {
|
|
|
2248
2810
|
name: z.string().min(1).max(500).optional().describe('Part name'),
|
|
2249
2811
|
part_number: z.string().max(200).optional().describe('Part number / SKU'),
|
|
2250
2812
|
category: z.string().max(200).optional().describe('Category label'),
|
|
2251
|
-
supplier: z
|
|
2813
|
+
supplier: z
|
|
2814
|
+
.string()
|
|
2815
|
+
.max(500)
|
|
2816
|
+
.optional()
|
|
2817
|
+
.describe('DEPRECATED — legacy free-text supplier name. Use supplier_id instead.'),
|
|
2818
|
+
supplier_id: z.string().uuid().optional().describe('Vendor ID (resolve via list_vendors)'),
|
|
2252
2819
|
cost: z.number().min(0).optional().describe('Unit cost'),
|
|
2253
|
-
quantity: z
|
|
2254
|
-
|
|
2820
|
+
quantity: z
|
|
2821
|
+
.number()
|
|
2822
|
+
.int()
|
|
2823
|
+
.min(0)
|
|
2824
|
+
.max(10_000_000)
|
|
2825
|
+
.optional()
|
|
2826
|
+
.describe('Current stock quantity'),
|
|
2827
|
+
desired_quantity: z
|
|
2828
|
+
.number()
|
|
2829
|
+
.int()
|
|
2830
|
+
.min(0)
|
|
2831
|
+
.max(10_000_000)
|
|
2832
|
+
.optional()
|
|
2833
|
+
.describe('Target / reorder quantity'),
|
|
2255
2834
|
specific_location: z.string().max(500).optional().describe('Storage location description'),
|
|
2256
2835
|
site_id: z.string().uuid().optional().describe('Site ID'),
|
|
2257
2836
|
building_id: z.string().uuid().optional().describe('Building ID'),
|
|
@@ -2278,7 +2857,15 @@ export function registerWriteTools(server, client) {
|
|
|
2278
2857
|
// Upload URLs
|
|
2279
2858
|
// ============================================================
|
|
2280
2859
|
server.tool('create_upload_url', 'Generate a signed upload URL for uploading a file to AssetLab storage. Returns a signed_url to PUT the file to, a public_url for referencing, and the path. IMPORTANT — When a user wants to upload a file, always clarify the target. File upload paths: (1) Asset IMAGE: bucket "asset-images" → update_asset with image_url. (2) Asset DOCUMENT (O&M, warranty, spec): bucket "documents" → create_asset_document. (3) Work order IMAGE: bucket "attachments" → update_work_order with image_url. (4) Work order/request/PM ATTACHMENT: bucket "attachments" → create_attachment with the parent ID. (5) Project DOCUMENT: bucket "project-documents" → create_project_document. (6) Contract DOCUMENT: bucket "contract-documents" → create_contract_document. Always ask the user which type they mean if ambiguous. Requires upload_urls:write scope.', {
|
|
2281
|
-
bucket: z
|
|
2860
|
+
bucket: z
|
|
2861
|
+
.enum([
|
|
2862
|
+
'documents',
|
|
2863
|
+
'attachments',
|
|
2864
|
+
'project-documents',
|
|
2865
|
+
'contract-documents',
|
|
2866
|
+
'asset-images',
|
|
2867
|
+
])
|
|
2868
|
+
.describe('Storage bucket (required). Use "asset-images" for asset photos.'),
|
|
2282
2869
|
file_name: z.string().min(1).max(500).describe('File name including extension (required)'),
|
|
2283
2870
|
}, async (params) => {
|
|
2284
2871
|
try {
|
|
@@ -2290,10 +2877,25 @@ export function registerWriteTools(server, client) {
|
|
|
2290
2877
|
}
|
|
2291
2878
|
});
|
|
2292
2879
|
server.tool('upload_file', 'Upload a file to AssetLab storage by sending its bytes inline (base64). The AssetLab backend performs the storage upload server-side — use this tool when the client cannot PUT directly to Supabase Storage (e.g. Claude integrations whose outbound network blocks arbitrary supabase.co hosts). Returns { path, public_url, bucket, file_size, content_type }. After uploading, pass path or public_url to the appropriate record tool (update_asset image_url, create_asset_document file_path, update_work_order image_url, create_attachment file_path, create_project_document file_path, create_contract_document file_path). Server limit is ~10 MB decoded; MCP arg ceiling effectively caps file size around 700 KB–1 MB. For larger files, use create_upload_url instead. Requires upload_urls:write scope.', {
|
|
2293
|
-
bucket: z
|
|
2880
|
+
bucket: z
|
|
2881
|
+
.enum([
|
|
2882
|
+
'documents',
|
|
2883
|
+
'attachments',
|
|
2884
|
+
'project-documents',
|
|
2885
|
+
'contract-documents',
|
|
2886
|
+
'asset-images',
|
|
2887
|
+
])
|
|
2888
|
+
.describe('Storage bucket (required). Use "asset-images" for asset photos, "attachments" for work-order/PM attachments.'),
|
|
2294
2889
|
file_name: z.string().min(1).max(500).describe('File name including extension (required)'),
|
|
2295
|
-
content_base64: z
|
|
2296
|
-
|
|
2890
|
+
content_base64: z
|
|
2891
|
+
.string()
|
|
2892
|
+
.min(1)
|
|
2893
|
+
.describe('File contents base64-encoded (required). Data URI prefixes like "data:image/png;base64," are stripped automatically.'),
|
|
2894
|
+
content_type: z
|
|
2895
|
+
.string()
|
|
2896
|
+
.max(200)
|
|
2897
|
+
.optional()
|
|
2898
|
+
.describe('MIME type (e.g. image/jpeg, application/pdf). Defaults to application/octet-stream.'),
|
|
2297
2899
|
}, async (params) => {
|
|
2298
2900
|
try {
|
|
2299
2901
|
const result = await client.create('upload-files', buildBody(params));
|
|
@@ -2308,9 +2910,16 @@ export function registerWriteTools(server, client) {
|
|
|
2308
2910
|
// ============================================================
|
|
2309
2911
|
server.tool('create_asset_document', 'Create an asset document record (after uploading the file via create_upload_url). Requires asset_documents:write scope.', {
|
|
2310
2912
|
name: z.string().min(1).max(500).describe('Document name (required)'),
|
|
2311
|
-
file_path: z
|
|
2913
|
+
file_path: z
|
|
2914
|
+
.string()
|
|
2915
|
+
.min(1)
|
|
2916
|
+
.max(2000)
|
|
2917
|
+
.describe('Storage path from upload URL response (required)'),
|
|
2312
2918
|
asset_id: z.string().uuid().describe('Asset ID this document belongs to (required)'),
|
|
2313
|
-
category: z
|
|
2919
|
+
category: z
|
|
2920
|
+
.enum(['om', 'commissioning', 'warranty', 'installation', 'specification', 'other'])
|
|
2921
|
+
.optional()
|
|
2922
|
+
.describe('Document category'),
|
|
2314
2923
|
description: z.string().optional().describe('Description'),
|
|
2315
2924
|
file_type: z.string().max(200).optional().describe('MIME type'),
|
|
2316
2925
|
file_size: z.number().min(0).optional().describe('File size in bytes'),
|
|
@@ -2329,7 +2938,10 @@ export function registerWriteTools(server, client) {
|
|
|
2329
2938
|
name: z.string().min(1).max(500).optional().describe('Document name'),
|
|
2330
2939
|
file_path: z.string().max(2000).optional().describe('Storage path'),
|
|
2331
2940
|
asset_id: z.string().uuid().optional().describe('Asset ID'),
|
|
2332
|
-
category: z
|
|
2941
|
+
category: z
|
|
2942
|
+
.enum(['om', 'commissioning', 'warranty', 'installation', 'specification', 'other'])
|
|
2943
|
+
.optional()
|
|
2944
|
+
.describe('Document category'),
|
|
2333
2945
|
description: z.string().optional().describe('Description'),
|
|
2334
2946
|
file_type: z.string().max(200).optional().describe('MIME type'),
|
|
2335
2947
|
file_size: z.number().min(0).optional().describe('File size in bytes'),
|
|
@@ -2362,10 +2974,26 @@ export function registerWriteTools(server, client) {
|
|
|
2362
2974
|
file_type: z.string().max(200).optional().describe('MIME type'),
|
|
2363
2975
|
uploaded_by: z.string().max(200).optional().describe('Uploader user ID'),
|
|
2364
2976
|
description: z.string().optional().describe('Description'),
|
|
2365
|
-
work_order_id: z
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2977
|
+
work_order_id: z
|
|
2978
|
+
.string()
|
|
2979
|
+
.uuid()
|
|
2980
|
+
.optional()
|
|
2981
|
+
.describe('Work order ID (exactly one parent required)'),
|
|
2982
|
+
work_request_id: z
|
|
2983
|
+
.string()
|
|
2984
|
+
.uuid()
|
|
2985
|
+
.optional()
|
|
2986
|
+
.describe('Work request ID (exactly one parent required)'),
|
|
2987
|
+
pm_schedule_id: z
|
|
2988
|
+
.string()
|
|
2989
|
+
.uuid()
|
|
2990
|
+
.optional()
|
|
2991
|
+
.describe('PM schedule ID (exactly one parent required)'),
|
|
2992
|
+
pm_template_id: z
|
|
2993
|
+
.string()
|
|
2994
|
+
.uuid()
|
|
2995
|
+
.optional()
|
|
2996
|
+
.describe('PM template ID (exactly one parent required)'),
|
|
2369
2997
|
}, async (params) => {
|
|
2370
2998
|
try {
|
|
2371
2999
|
const result = await client.create('attachments', buildBody(params));
|
|
@@ -2411,7 +3039,11 @@ export function registerWriteTools(server, client) {
|
|
|
2411
3039
|
server.tool('create_project_document', 'Create a project document record. Requires project_documents:write scope.', {
|
|
2412
3040
|
project_id: z.string().uuid().describe('Project ID (required)'),
|
|
2413
3041
|
name: z.string().min(1).max(500).describe('Document name (required)'),
|
|
2414
|
-
file_path: z
|
|
3042
|
+
file_path: z
|
|
3043
|
+
.string()
|
|
3044
|
+
.min(1)
|
|
3045
|
+
.max(2000)
|
|
3046
|
+
.describe('Storage path from upload URL response (required)'),
|
|
2415
3047
|
uploaded_by: z.string().min(1).max(200).describe('Uploader user ID (required)'),
|
|
2416
3048
|
folder_id: z.string().uuid().optional().describe('Folder ID'),
|
|
2417
3049
|
description: z.string().optional().describe('Description'),
|
|
@@ -2460,7 +3092,11 @@ export function registerWriteTools(server, client) {
|
|
|
2460
3092
|
server.tool('create_contract_document', 'Create a contract document record. Requires contract_documents:write scope.', {
|
|
2461
3093
|
contract_id: z.string().uuid().describe('Contract ID (required)'),
|
|
2462
3094
|
file_name: z.string().min(1).max(500).describe('File name (required)'),
|
|
2463
|
-
file_path: z
|
|
3095
|
+
file_path: z
|
|
3096
|
+
.string()
|
|
3097
|
+
.min(1)
|
|
3098
|
+
.max(2000)
|
|
3099
|
+
.describe('Storage path from upload URL response (required)'),
|
|
2464
3100
|
file_size: z.number().min(0).optional().describe('File size in bytes'),
|
|
2465
3101
|
file_type: z.string().max(200).optional().describe('MIME type'),
|
|
2466
3102
|
uploaded_by: z.string().max(200).optional().describe('Uploader user ID'),
|
|
@@ -2552,7 +3188,10 @@ export function registerWriteTools(server, client) {
|
|
|
2552
3188
|
server.tool('create_project_task_dependency', 'Create a dependency between two project tasks. Requires project_task_dependencies:write scope.', {
|
|
2553
3189
|
task_id: z.string().uuid().describe('Task ID (the dependent task, required)'),
|
|
2554
3190
|
depends_on_task_id: z.string().uuid().describe('Task ID that must complete first (required)'),
|
|
2555
|
-
dependency_type: z
|
|
3191
|
+
dependency_type: z
|
|
3192
|
+
.enum(['finish_to_start', 'start_to_start', 'finish_to_finish', 'start_to_finish'])
|
|
3193
|
+
.optional()
|
|
3194
|
+
.describe('Dependency type (default: finish_to_start)'),
|
|
2556
3195
|
}, async (params) => {
|
|
2557
3196
|
try {
|
|
2558
3197
|
const result = await client.create('project-task-dependencies', buildBody(params));
|
|
@@ -2577,9 +3216,20 @@ export function registerWriteTools(server, client) {
|
|
|
2577
3216
|
server.tool('create_project_update', 'Create a periodic project status update. Requires project_updates:write scope.', {
|
|
2578
3217
|
project_id: z.string().uuid().describe('Project ID (required)'),
|
|
2579
3218
|
author_id: z.string().min(1).max(200).describe('Author Clerk user ID (required)'),
|
|
2580
|
-
timeframe: z
|
|
2581
|
-
|
|
2582
|
-
|
|
3219
|
+
timeframe: z
|
|
3220
|
+
.enum(['monthly', 'quarterly', 'bi-annually', 'annually'])
|
|
3221
|
+
.describe('Update timeframe (required)'),
|
|
3222
|
+
period_year: z
|
|
3223
|
+
.number()
|
|
3224
|
+
.int()
|
|
3225
|
+
.min(2000)
|
|
3226
|
+
.max(2100)
|
|
3227
|
+
.describe('Year for this update period (required)'),
|
|
3228
|
+
period_value: z
|
|
3229
|
+
.string()
|
|
3230
|
+
.min(1)
|
|
3231
|
+
.max(20)
|
|
3232
|
+
.describe('Period value — 1-12 for monthly, 1-4 for quarterly, etc. (required)'),
|
|
2583
3233
|
content: z.string().min(1).describe('Update content (required)'),
|
|
2584
3234
|
title: z.string().max(500).optional().describe('Optional custom title'),
|
|
2585
3235
|
}, async (params) => {
|
|
@@ -2595,8 +3245,17 @@ export function registerWriteTools(server, client) {
|
|
|
2595
3245
|
id: z.string().uuid().describe('Project update ID'),
|
|
2596
3246
|
project_id: z.string().uuid().optional().describe('Project ID'),
|
|
2597
3247
|
author_id: z.string().min(1).max(200).optional().describe('Author Clerk user ID'),
|
|
2598
|
-
timeframe: z
|
|
2599
|
-
|
|
3248
|
+
timeframe: z
|
|
3249
|
+
.enum(['monthly', 'quarterly', 'bi-annually', 'annually'])
|
|
3250
|
+
.optional()
|
|
3251
|
+
.describe('Update timeframe'),
|
|
3252
|
+
period_year: z
|
|
3253
|
+
.number()
|
|
3254
|
+
.int()
|
|
3255
|
+
.min(2000)
|
|
3256
|
+
.max(2100)
|
|
3257
|
+
.optional()
|
|
3258
|
+
.describe('Year for this update period'),
|
|
2600
3259
|
period_value: z.string().min(1).max(20).optional().describe('Period value'),
|
|
2601
3260
|
content: z.string().min(1).optional().describe('Update content'),
|
|
2602
3261
|
title: z.string().max(500).optional().describe('Optional custom title'),
|
|
@@ -2627,7 +3286,12 @@ export function registerWriteTools(server, client) {
|
|
|
2627
3286
|
total_budget: z.number().min(0).describe('Total budget amount (required)'),
|
|
2628
3287
|
actual_cost: z.number().min(0).describe('Actual cost to date (required)'),
|
|
2629
3288
|
forecasted_cost: z.number().min(0).optional().describe('Forecasted total cost'),
|
|
2630
|
-
percent_complete: z
|
|
3289
|
+
percent_complete: z
|
|
3290
|
+
.number()
|
|
3291
|
+
.min(0)
|
|
3292
|
+
.max(100)
|
|
3293
|
+
.optional()
|
|
3294
|
+
.describe('Completion percentage (0-100)'),
|
|
2631
3295
|
}, async (params) => {
|
|
2632
3296
|
try {
|
|
2633
3297
|
const result = await client.create('project-cost-snapshots', buildBody(params));
|
|
@@ -2797,10 +3461,16 @@ export function registerWriteTools(server, client) {
|
|
|
2797
3461
|
project_id: z.string().uuid().describe('Project ID (required)'),
|
|
2798
3462
|
title: z.string().min(1).max(500).describe('Risk title (required)'),
|
|
2799
3463
|
description: z.string().optional().describe('Risk description'),
|
|
2800
|
-
category: z
|
|
3464
|
+
category: z
|
|
3465
|
+
.enum(['technical', 'financial', 'schedule', 'resource', 'external'])
|
|
3466
|
+
.optional()
|
|
3467
|
+
.describe('Risk category'),
|
|
2801
3468
|
probability: z.enum(['low', 'medium', 'high']).optional().describe('Probability level'),
|
|
2802
3469
|
impact: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Impact level'),
|
|
2803
|
-
status: z
|
|
3470
|
+
status: z
|
|
3471
|
+
.enum(['identified', 'analyzing', 'mitigating', 'resolved', 'accepted'])
|
|
3472
|
+
.optional()
|
|
3473
|
+
.describe('Risk status (default: identified)'),
|
|
2804
3474
|
mitigation_plan: z.string().optional().describe('Mitigation plan'),
|
|
2805
3475
|
contingency_plan: z.string().optional().describe('Contingency plan'),
|
|
2806
3476
|
owner_id: z.string().max(200).optional().describe('Risk owner (Clerk user ID)'),
|
|
@@ -2820,10 +3490,16 @@ export function registerWriteTools(server, client) {
|
|
|
2820
3490
|
project_id: z.string().uuid().optional().describe('Project ID'),
|
|
2821
3491
|
title: z.string().min(1).max(500).optional().describe('Risk title'),
|
|
2822
3492
|
description: z.string().optional().describe('Risk description'),
|
|
2823
|
-
category: z
|
|
3493
|
+
category: z
|
|
3494
|
+
.enum(['technical', 'financial', 'schedule', 'resource', 'external'])
|
|
3495
|
+
.optional()
|
|
3496
|
+
.describe('Risk category'),
|
|
2824
3497
|
probability: z.enum(['low', 'medium', 'high']).optional().describe('Probability level'),
|
|
2825
3498
|
impact: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Impact level'),
|
|
2826
|
-
status: z
|
|
3499
|
+
status: z
|
|
3500
|
+
.enum(['identified', 'analyzing', 'mitigating', 'resolved', 'accepted'])
|
|
3501
|
+
.optional()
|
|
3502
|
+
.describe('Risk status'),
|
|
2827
3503
|
mitigation_plan: z.string().optional().describe('Mitigation plan'),
|
|
2828
3504
|
contingency_plan: z.string().optional().describe('Contingency plan'),
|
|
2829
3505
|
owner_id: z.string().max(200).optional().describe('Risk owner (Clerk user ID)'),
|
|
@@ -2879,7 +3555,10 @@ export function registerWriteTools(server, client) {
|
|
|
2879
3555
|
icon: z.string().max(200).optional().describe('Icon name (e.g., "droplets" for water)'),
|
|
2880
3556
|
color: z.string().max(50).optional().describe('Hex color code (e.g., "#3B82F6")'),
|
|
2881
3557
|
sort_order: z.number().int().min(0).optional().describe('Sort order for display'),
|
|
2882
|
-
is_active: z
|
|
3558
|
+
is_active: z
|
|
3559
|
+
.boolean()
|
|
3560
|
+
.optional()
|
|
3561
|
+
.describe('Whether the service area is active (default: true)'),
|
|
2883
3562
|
}, async (params) => {
|
|
2884
3563
|
try {
|
|
2885
3564
|
const result = await client.create('service-areas', buildBody(params));
|
|
@@ -2969,24 +3648,65 @@ export function registerWriteTools(server, client) {
|
|
|
2969
3648
|
server.tool('create_los_measure', 'Create a new LoS measure within a service area. Requires los_measures:write scope. Resolve service_area_id first via list_service_areas.', {
|
|
2970
3649
|
service_area_id: z.string().uuid().describe('Service area ID (required)'),
|
|
2971
3650
|
name: z.string().min(1).max(500).describe('Measure name (required, unique per service area)'),
|
|
2972
|
-
category: z
|
|
3651
|
+
category: z
|
|
3652
|
+
.enum([
|
|
3653
|
+
'quality',
|
|
3654
|
+
'reliability',
|
|
3655
|
+
'responsiveness',
|
|
3656
|
+
'safety',
|
|
3657
|
+
'sustainability',
|
|
3658
|
+
'cost_efficiency',
|
|
3659
|
+
'capacity',
|
|
3660
|
+
])
|
|
3661
|
+
.describe('Measure category (required)'),
|
|
2973
3662
|
type: z.enum(['community', 'technical']).describe('Measure type (required)'),
|
|
2974
|
-
data_source: z
|
|
2975
|
-
|
|
2976
|
-
'
|
|
2977
|
-
'
|
|
2978
|
-
'
|
|
3663
|
+
data_source: z
|
|
3664
|
+
.enum([
|
|
3665
|
+
'manual',
|
|
3666
|
+
'custom_formula',
|
|
3667
|
+
'asset_condition_avg',
|
|
3668
|
+
'asset_condition_pct_above',
|
|
3669
|
+
'asset_condition_pct_below',
|
|
3670
|
+
'risk_score_avg',
|
|
3671
|
+
'risk_pct_critical',
|
|
3672
|
+
'wo_response_time_avg',
|
|
3673
|
+
'wo_completion_time_avg',
|
|
3674
|
+
'wo_backlog_count',
|
|
3675
|
+
'wo_overdue_count',
|
|
3676
|
+
'pm_compliance_rate',
|
|
3677
|
+
'compliance_score',
|
|
3678
|
+
'fci',
|
|
3679
|
+
'deferred_maintenance_ratio',
|
|
2979
3680
|
'asset_past_useful_life_pct',
|
|
2980
|
-
])
|
|
3681
|
+
])
|
|
3682
|
+
.describe('Data source type (required). Use "manual" if values will be entered by hand.'),
|
|
2981
3683
|
description: z.string().max(2000).optional().describe('Description'),
|
|
2982
|
-
community_statement: z
|
|
2983
|
-
|
|
2984
|
-
|
|
3684
|
+
community_statement: z
|
|
3685
|
+
.string()
|
|
3686
|
+
.max(2000)
|
|
3687
|
+
.optional()
|
|
3688
|
+
.describe('Community-facing statement (for community type measures)'),
|
|
3689
|
+
unit: z
|
|
3690
|
+
.string()
|
|
3691
|
+
.max(100)
|
|
3692
|
+
.optional()
|
|
3693
|
+
.describe('Unit of measurement (e.g., "%", "hours", "count")'),
|
|
3694
|
+
trend_direction: z
|
|
3695
|
+
.enum(['higher_is_better', 'lower_is_better', 'target_is_optimal'])
|
|
3696
|
+
.optional()
|
|
3697
|
+
.describe('Which direction is better'),
|
|
2985
3698
|
target_value: z.number().optional().describe('Target value'),
|
|
2986
3699
|
minimum_acceptable: z.number().optional().describe('Minimum acceptable value'),
|
|
2987
3700
|
stretch_goal: z.number().optional().describe('Stretch goal value'),
|
|
2988
|
-
weight: z
|
|
2989
|
-
|
|
3701
|
+
weight: z
|
|
3702
|
+
.number()
|
|
3703
|
+
.min(0)
|
|
3704
|
+
.optional()
|
|
3705
|
+
.describe('Weight for composite score calculation (default: 1.0)'),
|
|
3706
|
+
data_source_config: z
|
|
3707
|
+
.record(z.unknown())
|
|
3708
|
+
.optional()
|
|
3709
|
+
.describe('Data source configuration (JSONB). E.g., {"threshold": 3} for pct_above/below, {"days_back": 90} for WO metrics.'),
|
|
2990
3710
|
is_active: z.boolean().optional().describe('Whether the measure is active (default: true)'),
|
|
2991
3711
|
sort_order: z.number().int().min(0).optional().describe('Sort order for display'),
|
|
2992
3712
|
}, async (params) => {
|
|
@@ -3001,24 +3721,55 @@ export function registerWriteTools(server, client) {
|
|
|
3001
3721
|
server.tool('update_los_measure', 'Update an existing LoS measure by ID. Requires los_measures:write scope.', {
|
|
3002
3722
|
id: z.string().uuid().describe('LoS measure ID'),
|
|
3003
3723
|
name: z.string().min(1).max(500).optional().describe('Measure name'),
|
|
3004
|
-
category: z
|
|
3724
|
+
category: z
|
|
3725
|
+
.enum([
|
|
3726
|
+
'quality',
|
|
3727
|
+
'reliability',
|
|
3728
|
+
'responsiveness',
|
|
3729
|
+
'safety',
|
|
3730
|
+
'sustainability',
|
|
3731
|
+
'cost_efficiency',
|
|
3732
|
+
'capacity',
|
|
3733
|
+
])
|
|
3734
|
+
.optional()
|
|
3735
|
+
.describe('Measure category'),
|
|
3005
3736
|
type: z.enum(['community', 'technical']).optional().describe('Measure type'),
|
|
3006
|
-
data_source: z
|
|
3007
|
-
|
|
3008
|
-
'
|
|
3009
|
-
'
|
|
3010
|
-
'
|
|
3737
|
+
data_source: z
|
|
3738
|
+
.enum([
|
|
3739
|
+
'manual',
|
|
3740
|
+
'custom_formula',
|
|
3741
|
+
'asset_condition_avg',
|
|
3742
|
+
'asset_condition_pct_above',
|
|
3743
|
+
'asset_condition_pct_below',
|
|
3744
|
+
'risk_score_avg',
|
|
3745
|
+
'risk_pct_critical',
|
|
3746
|
+
'wo_response_time_avg',
|
|
3747
|
+
'wo_completion_time_avg',
|
|
3748
|
+
'wo_backlog_count',
|
|
3749
|
+
'wo_overdue_count',
|
|
3750
|
+
'pm_compliance_rate',
|
|
3751
|
+
'compliance_score',
|
|
3752
|
+
'fci',
|
|
3753
|
+
'deferred_maintenance_ratio',
|
|
3011
3754
|
'asset_past_useful_life_pct',
|
|
3012
|
-
])
|
|
3755
|
+
])
|
|
3756
|
+
.optional()
|
|
3757
|
+
.describe('Data source type'),
|
|
3013
3758
|
description: z.string().max(2000).optional().describe('Description'),
|
|
3014
3759
|
community_statement: z.string().max(2000).optional().describe('Community-facing statement'),
|
|
3015
3760
|
unit: z.string().max(100).optional().describe('Unit of measurement'),
|
|
3016
|
-
trend_direction: z
|
|
3761
|
+
trend_direction: z
|
|
3762
|
+
.enum(['higher_is_better', 'lower_is_better', 'target_is_optimal'])
|
|
3763
|
+
.optional()
|
|
3764
|
+
.describe('Which direction is better'),
|
|
3017
3765
|
target_value: z.number().optional().describe('Target value'),
|
|
3018
3766
|
minimum_acceptable: z.number().optional().describe('Minimum acceptable value'),
|
|
3019
3767
|
stretch_goal: z.number().optional().describe('Stretch goal value'),
|
|
3020
3768
|
weight: z.number().min(0).optional().describe('Weight for composite score calculation'),
|
|
3021
|
-
data_source_config: z
|
|
3769
|
+
data_source_config: z
|
|
3770
|
+
.record(z.unknown())
|
|
3771
|
+
.optional()
|
|
3772
|
+
.describe('Data source configuration (JSONB)'),
|
|
3022
3773
|
is_active: z.boolean().optional().describe('Active status'),
|
|
3023
3774
|
sort_order: z.number().int().min(0).optional().describe('Sort order'),
|
|
3024
3775
|
}, async ({ id, ...rest }) => {
|
|
@@ -3044,12 +3795,19 @@ export function registerWriteTools(server, client) {
|
|
|
3044
3795
|
// ============================================================
|
|
3045
3796
|
server.tool('create_los_measurement', 'Record a new LoS measurement value. Requires los_measurements:write scope. Resolve los_measure_id first via list_los_measures.', {
|
|
3046
3797
|
los_measure_id: z.string().uuid().describe('LoS measure ID (required)'),
|
|
3047
|
-
period_type: z
|
|
3048
|
-
|
|
3798
|
+
period_type: z
|
|
3799
|
+
.enum(['monthly', 'quarterly', 'semi_annual', 'annual'])
|
|
3800
|
+
.describe('Period type (required)'),
|
|
3801
|
+
period_start: z
|
|
3802
|
+
.string()
|
|
3803
|
+
.describe('Period start date (ISO 8601, required, e.g., "2026-01-01")'),
|
|
3049
3804
|
period_end: z.string().describe('Period end date (ISO 8601, required, e.g., "2026-03-31")'),
|
|
3050
3805
|
actual_value: z.number().describe('Measured value (required)'),
|
|
3051
3806
|
notes: z.string().max(2000).optional().describe('Notes or context for this measurement'),
|
|
3052
|
-
is_auto: z
|
|
3807
|
+
is_auto: z
|
|
3808
|
+
.boolean()
|
|
3809
|
+
.optional()
|
|
3810
|
+
.describe('Whether this is an auto-calculated value (default: false)'),
|
|
3053
3811
|
}, async (params) => {
|
|
3054
3812
|
try {
|
|
3055
3813
|
const result = await client.create('los-measurements', buildBody(params));
|
|
@@ -3063,7 +3821,10 @@ export function registerWriteTools(server, client) {
|
|
|
3063
3821
|
id: z.string().uuid().describe('LoS measurement ID'),
|
|
3064
3822
|
actual_value: z.number().optional().describe('Measured value'),
|
|
3065
3823
|
notes: z.string().max(2000).optional().describe('Notes or context'),
|
|
3066
|
-
period_type: z
|
|
3824
|
+
period_type: z
|
|
3825
|
+
.enum(['monthly', 'quarterly', 'semi_annual', 'annual'])
|
|
3826
|
+
.optional()
|
|
3827
|
+
.describe('Period type'),
|
|
3067
3828
|
period_start: z.string().optional().describe('Period start date (ISO 8601)'),
|
|
3068
3829
|
period_end: z.string().optional().describe('Period end date (ISO 8601)'),
|
|
3069
3830
|
is_auto: z.boolean().optional().describe('Whether this is auto-calculated'),
|
|
@@ -3095,14 +3856,39 @@ export function registerWriteTools(server, client) {
|
|
|
3095
3856
|
.min(3)
|
|
3096
3857
|
.describe('Polygon outline as an array of [x, y] points in normalized 0-1 coordinates (origin top-left). At least 3 points.');
|
|
3097
3858
|
server.tool('create_floorplan', 'Create a floorplan row. Provide EXACTLY ONE of building_id (per-building floor) or site_id (site-level / campus plan). Typically the web app calls this per page of an uploaded PDF; MCP clients rarely need to call this directly since they do not upload the PDF itself. Requires floorplans:write scope.', {
|
|
3098
|
-
building_id: z
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3859
|
+
building_id: z
|
|
3860
|
+
.string()
|
|
3861
|
+
.uuid()
|
|
3862
|
+
.optional()
|
|
3863
|
+
.describe('Building this floor belongs to (omit if site-scoped)'),
|
|
3864
|
+
site_id: z
|
|
3865
|
+
.string()
|
|
3866
|
+
.uuid()
|
|
3867
|
+
.optional()
|
|
3868
|
+
.describe('Site this plan belongs to (use for site-level / campus plans; omit if building-scoped)'),
|
|
3869
|
+
floor_label: z
|
|
3870
|
+
.string()
|
|
3871
|
+
.max(200)
|
|
3872
|
+
.describe('Human-readable label (e.g. "Ground Floor", "Mezzanine", "Site Plan")'),
|
|
3873
|
+
floor_order: z
|
|
3874
|
+
.number()
|
|
3875
|
+
.int()
|
|
3876
|
+
.min(0)
|
|
3877
|
+
.optional()
|
|
3878
|
+
.describe('Sort order within the scope (lowest first)'),
|
|
3102
3879
|
pdf_storage_path: z.string().max(1000).describe('Supabase Storage path to the PDF file'),
|
|
3103
3880
|
pdf_filename: z.string().max(500).describe('Original filename for display'),
|
|
3104
|
-
page_number: z
|
|
3105
|
-
|
|
3881
|
+
page_number: z
|
|
3882
|
+
.number()
|
|
3883
|
+
.int()
|
|
3884
|
+
.min(1)
|
|
3885
|
+
.optional()
|
|
3886
|
+
.describe('1-indexed page number within the PDF'),
|
|
3887
|
+
page_width_pt: z
|
|
3888
|
+
.number()
|
|
3889
|
+
.min(0)
|
|
3890
|
+
.optional()
|
|
3891
|
+
.describe('Page width in PDF points (discovered client-side)'),
|
|
3106
3892
|
page_height_pt: z.number().min(0).optional().describe('Page height in PDF points'),
|
|
3107
3893
|
status: z.enum(FLOORPLAN_STATUSES).optional().describe('Detection status (default: pending)'),
|
|
3108
3894
|
}, async (params) => {
|
|
@@ -3147,10 +3933,25 @@ export function registerWriteTools(server, client) {
|
|
|
3147
3933
|
floorplan_id: z.string().uuid().describe('Floorplan this region belongs to'),
|
|
3148
3934
|
label: z.string().max(500).describe('Region label (e.g. "Boiler Room 2B")'),
|
|
3149
3935
|
polygon: polygonSchema,
|
|
3150
|
-
location_id: z
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3936
|
+
location_id: z
|
|
3937
|
+
.string()
|
|
3938
|
+
.uuid()
|
|
3939
|
+
.optional()
|
|
3940
|
+
.describe('Linked Location ID (resolved via list_locations)'),
|
|
3941
|
+
source: z
|
|
3942
|
+
.enum(REGION_SOURCES)
|
|
3943
|
+
.optional()
|
|
3944
|
+
.describe('"manual" (default) or "ai" for AI-detected'),
|
|
3945
|
+
confidence: z
|
|
3946
|
+
.number()
|
|
3947
|
+
.min(0)
|
|
3948
|
+
.max(1)
|
|
3949
|
+
.optional()
|
|
3950
|
+
.describe('AI confidence score (0-1), only set when source=ai'),
|
|
3951
|
+
reviewed: z
|
|
3952
|
+
.boolean()
|
|
3953
|
+
.optional()
|
|
3954
|
+
.describe('True if an admin has reviewed this region (default: true for manual, false for ai)'),
|
|
3154
3955
|
}, async (params) => {
|
|
3155
3956
|
try {
|
|
3156
3957
|
const result = await client.create('floorplan-regions', buildBody(params));
|
|
@@ -3164,7 +3965,11 @@ export function registerWriteTools(server, client) {
|
|
|
3164
3965
|
id: z.string().uuid().describe('Floorplan region ID'),
|
|
3165
3966
|
label: z.string().max(500).optional().describe('New label'),
|
|
3166
3967
|
polygon: polygonSchema.optional(),
|
|
3167
|
-
location_id: z
|
|
3968
|
+
location_id: z
|
|
3969
|
+
.string()
|
|
3970
|
+
.uuid()
|
|
3971
|
+
.optional()
|
|
3972
|
+
.describe('Linked Location ID (set to null to unlink)'),
|
|
3168
3973
|
confidence: z.number().min(0).max(1).optional(),
|
|
3169
3974
|
reviewed: z.boolean().optional().describe('Mark region as reviewed by admin'),
|
|
3170
3975
|
}, async ({ id, ...rest }) => {
|
|
@@ -3190,8 +3995,15 @@ export function registerWriteTools(server, client) {
|
|
|
3190
3995
|
floorplan_id: z.string().uuid().describe('Target floorplan (resolve via list_floorplans)'),
|
|
3191
3996
|
x: z.number().min(0).max(1).describe('Normalized x coordinate (0=left, 1=right)'),
|
|
3192
3997
|
y: z.number().min(0).max(1).describe('Normalized y coordinate (0=top, 1=bottom)'),
|
|
3193
|
-
region_id: z
|
|
3194
|
-
|
|
3998
|
+
region_id: z
|
|
3999
|
+
.string()
|
|
4000
|
+
.uuid()
|
|
4001
|
+
.optional()
|
|
4002
|
+
.describe('Optional region the pin sits inside (usually auto-inferred)'),
|
|
4003
|
+
source: z
|
|
4004
|
+
.enum(REGION_SOURCES)
|
|
4005
|
+
.optional()
|
|
4006
|
+
.describe('"manual" (default) or "ai" for AI-placed'),
|
|
3195
4007
|
}, async (params) => {
|
|
3196
4008
|
try {
|
|
3197
4009
|
const result = await client.create('asset-placements', buildBody(params));
|
|
@@ -3229,30 +4041,80 @@ export function registerWriteTools(server, client) {
|
|
|
3229
4041
|
// Bulk operations
|
|
3230
4042
|
// ============================================================
|
|
3231
4043
|
const BULK_RESOURCES = [
|
|
3232
|
-
'assets',
|
|
3233
|
-
'
|
|
3234
|
-
'
|
|
3235
|
-
'
|
|
3236
|
-
'
|
|
3237
|
-
'
|
|
3238
|
-
'
|
|
3239
|
-
'
|
|
3240
|
-
'
|
|
3241
|
-
'
|
|
3242
|
-
'
|
|
3243
|
-
'
|
|
3244
|
-
'
|
|
3245
|
-
'
|
|
3246
|
-
'
|
|
3247
|
-
'
|
|
3248
|
-
'
|
|
3249
|
-
'
|
|
3250
|
-
'
|
|
3251
|
-
'
|
|
4044
|
+
'assets',
|
|
4045
|
+
'work-orders',
|
|
4046
|
+
'work-requests',
|
|
4047
|
+
'vendors',
|
|
4048
|
+
'sites',
|
|
4049
|
+
'buildings',
|
|
4050
|
+
'locations',
|
|
4051
|
+
'systems',
|
|
4052
|
+
'system-groups',
|
|
4053
|
+
'system-classes',
|
|
4054
|
+
'pm-schedules',
|
|
4055
|
+
'pm-templates',
|
|
4056
|
+
'projects',
|
|
4057
|
+
'contracts',
|
|
4058
|
+
'invoices',
|
|
4059
|
+
'purchase-orders',
|
|
4060
|
+
'expenses',
|
|
4061
|
+
'budgets',
|
|
4062
|
+
'asset-types',
|
|
4063
|
+
'asset-type-groups',
|
|
4064
|
+
'asset-statuses',
|
|
4065
|
+
'work-categories',
|
|
4066
|
+
'manufacturers',
|
|
4067
|
+
'building-types',
|
|
4068
|
+
'location-types',
|
|
4069
|
+
'cost-categories',
|
|
4070
|
+
'compliance',
|
|
4071
|
+
'compliance-records',
|
|
4072
|
+
'asset-comments',
|
|
4073
|
+
'asset-costs',
|
|
4074
|
+
'asset-replacement-plans',
|
|
4075
|
+
'work-order-comments',
|
|
4076
|
+
'project-tasks',
|
|
4077
|
+
'project-milestones',
|
|
4078
|
+
'project-phases',
|
|
4079
|
+
'project-budget-items',
|
|
4080
|
+
'project-time-entries',
|
|
4081
|
+
'project-comments',
|
|
4082
|
+
'project-team-members',
|
|
4083
|
+
'project-task-dependencies',
|
|
4084
|
+
'project-updates',
|
|
4085
|
+
'project-cost-snapshots',
|
|
4086
|
+
'project-locations',
|
|
4087
|
+
'project-sites',
|
|
4088
|
+
'project-buildings',
|
|
4089
|
+
'project-systems',
|
|
4090
|
+
'project-system-classes',
|
|
4091
|
+
'project-system-groups',
|
|
4092
|
+
'project-assets',
|
|
4093
|
+
'project-risks',
|
|
4094
|
+
'parts',
|
|
4095
|
+
'part-categories',
|
|
4096
|
+
'custom-field-definitions',
|
|
4097
|
+
'custom-field-values',
|
|
4098
|
+
'vendor-site-assignments',
|
|
4099
|
+
'contract-sites',
|
|
4100
|
+
'asset-documents',
|
|
4101
|
+
'attachments',
|
|
4102
|
+
'project-documents',
|
|
4103
|
+
'contract-documents',
|
|
4104
|
+
'service-areas',
|
|
4105
|
+
'los-measures',
|
|
4106
|
+
'los-measurements',
|
|
4107
|
+
'floorplans',
|
|
4108
|
+
'floorplan-regions',
|
|
4109
|
+
'asset-placements',
|
|
3252
4110
|
];
|
|
3253
4111
|
server.tool('bulk_create', 'Create multiple records of a resource type in one API call (max 100). Each item is processed independently — one failure does not affect others. Returns per-item results. Requires {resource}:write scope. Counts as 1 request for rate limiting.', {
|
|
3254
4112
|
resource: z.enum(BULK_RESOURCES).describe('Resource type (e.g. "assets", "work-orders")'),
|
|
3255
|
-
items: z
|
|
4113
|
+
items: z
|
|
4114
|
+
.array(z.record(z.unknown()))
|
|
4115
|
+
.min(1)
|
|
4116
|
+
.max(100)
|
|
4117
|
+
.describe('Array of objects to create (max 100). Each object uses the same fields as the single-create endpoint for that resource.'),
|
|
3256
4118
|
}, async ({ resource, items }) => {
|
|
3257
4119
|
try {
|
|
3258
4120
|
const result = await client.bulkCreate(resource, items);
|
|
@@ -3306,7 +4168,12 @@ export function registerWriteTools(server, client) {
|
|
|
3306
4168
|
name: z.string().max(500).describe('Compliance item name (required)'),
|
|
3307
4169
|
description: z.string().optional().describe('Description'),
|
|
3308
4170
|
regulation_reference: z.string().max(500).optional().describe('Regulation or code reference'),
|
|
3309
|
-
compliance_period_months: z
|
|
4171
|
+
compliance_period_months: z
|
|
4172
|
+
.number()
|
|
4173
|
+
.int()
|
|
4174
|
+
.min(1)
|
|
4175
|
+
.optional()
|
|
4176
|
+
.describe('Compliance period in months'),
|
|
3310
4177
|
status: z.enum(['active', 'archived']).optional().describe('Status'),
|
|
3311
4178
|
system_id: z.string().uuid().optional().describe('Associated system ID'),
|
|
3312
4179
|
}, async (params) => {
|
|
@@ -3323,7 +4190,12 @@ export function registerWriteTools(server, client) {
|
|
|
3323
4190
|
name: z.string().max(500).optional().describe('Compliance item name'),
|
|
3324
4191
|
description: z.string().optional().describe('Description'),
|
|
3325
4192
|
regulation_reference: z.string().max(500).optional().describe('Regulation or code reference'),
|
|
3326
|
-
compliance_period_months: z
|
|
4193
|
+
compliance_period_months: z
|
|
4194
|
+
.number()
|
|
4195
|
+
.int()
|
|
4196
|
+
.min(1)
|
|
4197
|
+
.optional()
|
|
4198
|
+
.describe('Compliance period in months'),
|
|
3327
4199
|
status: z.enum(['active', 'archived']).optional().describe('Status'),
|
|
3328
4200
|
system_id: z.string().uuid().optional().describe('Associated system ID'),
|
|
3329
4201
|
}, async ({ id, ...rest }) => {
|
|
@@ -3353,8 +4225,17 @@ export function registerWriteTools(server, client) {
|
|
|
3353
4225
|
work_order_id: z.string().uuid().describe('Work order ID (required)'),
|
|
3354
4226
|
completed_at: z.string().describe('Completion date-time (ISO 8601, required)'),
|
|
3355
4227
|
completed_by: z.string().max(200).optional().describe('User ID who completed'),
|
|
3356
|
-
required_frequency_days: z
|
|
3357
|
-
|
|
4228
|
+
required_frequency_days: z
|
|
4229
|
+
.number()
|
|
4230
|
+
.int()
|
|
4231
|
+
.min(1)
|
|
4232
|
+
.describe('Required frequency in days (required)'),
|
|
4233
|
+
days_since_last_completion: z
|
|
4234
|
+
.number()
|
|
4235
|
+
.int()
|
|
4236
|
+
.min(0)
|
|
4237
|
+
.optional()
|
|
4238
|
+
.describe('Days since last completion'),
|
|
3358
4239
|
}, async (params) => {
|
|
3359
4240
|
try {
|
|
3360
4241
|
const result = await client.create('compliance-records', buildBody(params));
|
|
@@ -3368,8 +4249,18 @@ export function registerWriteTools(server, client) {
|
|
|
3368
4249
|
id: z.string().uuid().describe('Compliance record ID'),
|
|
3369
4250
|
completed_at: z.string().optional().describe('Completion date-time (ISO 8601)'),
|
|
3370
4251
|
completed_by: z.string().max(200).optional().describe('User ID who completed'),
|
|
3371
|
-
required_frequency_days: z
|
|
3372
|
-
|
|
4252
|
+
required_frequency_days: z
|
|
4253
|
+
.number()
|
|
4254
|
+
.int()
|
|
4255
|
+
.min(1)
|
|
4256
|
+
.optional()
|
|
4257
|
+
.describe('Required frequency in days'),
|
|
4258
|
+
days_since_last_completion: z
|
|
4259
|
+
.number()
|
|
4260
|
+
.int()
|
|
4261
|
+
.min(0)
|
|
4262
|
+
.optional()
|
|
4263
|
+
.describe('Days since last completion'),
|
|
3373
4264
|
}, async ({ id, ...rest }) => {
|
|
3374
4265
|
try {
|
|
3375
4266
|
const result = await client.update('compliance-records', id, buildBody(rest));
|
|
@@ -3393,7 +4284,11 @@ export function registerWriteTools(server, client) {
|
|
|
3393
4284
|
// ============================================================
|
|
3394
4285
|
server.tool('bulk_update', 'Update multiple records of a resource type in one API call (max 100). Each item must include an "id" field (UUID). Each item is processed independently — one failure does not affect others. Returns per-item results. Requires {resource}:write scope. Counts as 1 request for rate limiting.', {
|
|
3395
4286
|
resource: z.enum(BULK_RESOURCES).describe('Resource type (e.g. "assets", "work-orders")'),
|
|
3396
|
-
items: z
|
|
4287
|
+
items: z
|
|
4288
|
+
.array(z.record(z.unknown()))
|
|
4289
|
+
.min(1)
|
|
4290
|
+
.max(100)
|
|
4291
|
+
.describe('Array of objects to update (max 100). Each must include an "id" field (UUID) plus fields to change.'),
|
|
3397
4292
|
}, async ({ resource, items }) => {
|
|
3398
4293
|
try {
|
|
3399
4294
|
const result = await client.bulkUpdate(resource, items);
|