@assetlab/mcp-server 1.1.0 → 1.3.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.
@@ -0,0 +1,1757 @@
1
+ /**
2
+ * MCP write-tool registrations for AssetLab.
3
+ *
4
+ * Registers create, update, and delete tools for every API resource.
5
+ * Each tool maps to POST / PATCH / DELETE on the AssetLab API Gateway.
6
+ * Write tools require API keys with the appropriate :write scope.
7
+ */
8
+ import { z } from 'zod';
9
+ // ---------------------------------------------------------------------------
10
+ // Helpers
11
+ // ---------------------------------------------------------------------------
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
+ /** Strip undefined values so the API only receives explicitly-set fields. */
20
+ function buildBody(params) {
21
+ const body = {};
22
+ for (const [k, v] of Object.entries(params)) {
23
+ if (v !== undefined)
24
+ body[k] = v;
25
+ }
26
+ return body;
27
+ }
28
+ // ---------------------------------------------------------------------------
29
+ // Registration
30
+ // ---------------------------------------------------------------------------
31
+ export function registerWriteTools(server, client) {
32
+ // ============================================================
33
+ // 1. Work Orders (scope: work_orders)
34
+ // ============================================================
35
+ server.tool('create_work_order', 'Create a new work order. Requires work_orders:write scope.', {
36
+ title: z.string().min(1).max(500).describe('Work order title (required)'),
37
+ description: z.string().optional().describe('Detailed description'),
38
+ priority: z.enum(['LOW', 'MEDIUM', 'HIGH', 'URGENT']).optional().describe('Priority level'),
39
+ status: z.enum(['NEW', 'IN_PROGRESS', 'ON_HOLD', 'REJECTED', 'COMPLETED', 'CANCELLED']).optional().describe('Status'),
40
+ type: z.enum(['PM', 'DEMAND']).optional().describe('Work order type'),
41
+ site_id: z.string().uuid().optional().describe('Site ID'),
42
+ building_id: z.string().uuid().optional().describe('Building ID'),
43
+ location_id: z.string().uuid().optional().describe('Location ID'),
44
+ asset_id: z.string().uuid().optional().describe('Asset ID'),
45
+ start_date: z.string().optional().describe('Start date (ISO 8601)'),
46
+ due_date: z.string().optional().describe('Due date (ISO 8601)'),
47
+ estimated_time: z.number().min(0).optional().describe('Estimated time in hours'),
48
+ estimated_cost: z.number().min(0).optional().describe('Estimated cost'),
49
+ work_category_id: z.string().uuid().optional().describe('Work category ID'),
50
+ assigned_to: z.string().optional().describe('Assigned user ID'),
51
+ }, async (params) => {
52
+ try {
53
+ const result = await client.create('work-orders', buildBody(params));
54
+ return formatResult(result);
55
+ }
56
+ catch (err) {
57
+ return formatError(err);
58
+ }
59
+ });
60
+ server.tool('update_work_order', 'Update an existing work order by ID. Requires work_orders:write scope.', {
61
+ id: z.string().uuid().describe('Work order ID'),
62
+ title: z.string().min(1).max(500).optional().describe('Work order title'),
63
+ description: z.string().optional().describe('Detailed description'),
64
+ priority: z.enum(['LOW', 'MEDIUM', 'HIGH', 'URGENT']).optional().describe('Priority level'),
65
+ status: z.enum(['NEW', 'IN_PROGRESS', 'ON_HOLD', 'REJECTED', 'COMPLETED', 'CANCELLED']).optional().describe('Status'),
66
+ type: z.enum(['PM', 'DEMAND']).optional().describe('Work order type'),
67
+ site_id: z.string().uuid().optional().describe('Site ID'),
68
+ building_id: z.string().uuid().optional().describe('Building ID'),
69
+ location_id: z.string().uuid().optional().describe('Location ID'),
70
+ asset_id: z.string().uuid().optional().describe('Asset ID'),
71
+ start_date: z.string().optional().describe('Start date (ISO 8601)'),
72
+ due_date: z.string().optional().describe('Due date (ISO 8601)'),
73
+ estimated_time: z.number().min(0).optional().describe('Estimated time in hours'),
74
+ estimated_cost: z.number().min(0).optional().describe('Estimated cost'),
75
+ work_category_id: z.string().uuid().optional().describe('Work category ID'),
76
+ assigned_to: z.string().optional().describe('Assigned user ID'),
77
+ }, async ({ id, ...rest }) => {
78
+ try {
79
+ const result = await client.update('work-orders', id, buildBody(rest));
80
+ return formatResult(result);
81
+ }
82
+ catch (err) {
83
+ return formatError(err);
84
+ }
85
+ });
86
+ server.tool('delete_work_order', 'Delete a work order by ID. Requires work_orders:write scope.', { id: z.string().uuid().describe('Work order ID') }, async ({ id }) => {
87
+ try {
88
+ const result = await client.remove('work-orders', id);
89
+ return formatResult(result);
90
+ }
91
+ catch (err) {
92
+ return formatError(err);
93
+ }
94
+ });
95
+ // ============================================================
96
+ // 2. Assets (scope: assets)
97
+ // ============================================================
98
+ server.tool('create_asset', 'Create a new asset. Requires assets:write scope.', {
99
+ name: z.string().min(1).max(500).describe('Asset name (required)'),
100
+ description: z.string().optional().describe('Description'),
101
+ model: z.string().max(500).optional().describe('Model name/number'),
102
+ serial_number: z.string().max(200).optional().describe('Serial number'),
103
+ purchase_cost: z.number().min(0).optional().describe('Purchase cost'),
104
+ purchase_date: z.string().optional().describe('Purchase date (ISO 8601)'),
105
+ expected_lifetime_years: z.number().min(0).optional().describe('Expected lifetime in years'),
106
+ condition_score: z.number().min(0).max(100).optional().describe('Condition score (0-100)'),
107
+ risk_factor: z.string().optional().describe('Risk factor'),
108
+ status_id: z.string().max(100).optional().describe('Status identifier'),
109
+ site_id: z.string().uuid().optional().describe('Site ID'),
110
+ building_id: z.string().uuid().optional().describe('Building ID'),
111
+ location_id: z.string().uuid().optional().describe('Location ID'),
112
+ system_id: z.string().uuid().optional().describe('System ID'),
113
+ image_url: z.string().max(2000).optional().describe('Image URL'),
114
+ }, async (params) => {
115
+ try {
116
+ const result = await client.create('assets', buildBody(params));
117
+ return formatResult(result);
118
+ }
119
+ catch (err) {
120
+ return formatError(err);
121
+ }
122
+ });
123
+ server.tool('update_asset', 'Update an existing asset by ID. Requires assets:write scope.', {
124
+ id: z.string().uuid().describe('Asset ID'),
125
+ name: z.string().min(1).max(500).optional().describe('Asset name'),
126
+ description: z.string().optional().describe('Description'),
127
+ model: z.string().max(500).optional().describe('Model name/number'),
128
+ serial_number: z.string().max(200).optional().describe('Serial number'),
129
+ purchase_cost: z.number().min(0).optional().describe('Purchase cost'),
130
+ purchase_date: z.string().optional().describe('Purchase date (ISO 8601)'),
131
+ expected_lifetime_years: z.number().min(0).optional().describe('Expected lifetime in years'),
132
+ condition_score: z.number().min(0).max(100).optional().describe('Condition score (0-100)'),
133
+ risk_factor: z.string().optional().describe('Risk factor'),
134
+ status_id: z.string().max(100).optional().describe('Status identifier'),
135
+ site_id: z.string().uuid().optional().describe('Site ID'),
136
+ building_id: z.string().uuid().optional().describe('Building ID'),
137
+ location_id: z.string().uuid().optional().describe('Location ID'),
138
+ system_id: z.string().uuid().optional().describe('System ID'),
139
+ image_url: z.string().max(2000).optional().describe('Image URL'),
140
+ }, async ({ id, ...rest }) => {
141
+ try {
142
+ const result = await client.update('assets', id, buildBody(rest));
143
+ return formatResult(result);
144
+ }
145
+ catch (err) {
146
+ return formatError(err);
147
+ }
148
+ });
149
+ server.tool('delete_asset', 'Delete an asset by ID. Requires assets:write scope.', { id: z.string().uuid().describe('Asset ID') }, async ({ id }) => {
150
+ try {
151
+ const result = await client.remove('assets', id);
152
+ return formatResult(result);
153
+ }
154
+ catch (err) {
155
+ return formatError(err);
156
+ }
157
+ });
158
+ // ============================================================
159
+ // 3. Work Requests (scope: work_requests)
160
+ // ============================================================
161
+ server.tool('create_work_request', 'Create a new work request. Requires work_requests:write scope.', {
162
+ title: z.string().min(1).max(500).describe('Work request title (required)'),
163
+ description: z.string().optional().describe('Description'),
164
+ priority: z.enum(['LOW', 'MEDIUM', 'HIGH', 'CRITICAL']).optional().describe('Priority level'),
165
+ status: z.enum(['SUBMITTED', 'APPROVED', 'REJECTED', 'CONVERTED']).optional().describe('Status'),
166
+ site_id: z.string().uuid().optional().describe('Site ID'),
167
+ building_id: z.string().uuid().optional().describe('Building ID'),
168
+ location_id: z.string().uuid().optional().describe('Location ID'),
169
+ asset_id: z.string().uuid().optional().describe('Asset ID'),
170
+ system_id: z.string().uuid().optional().describe('System ID'),
171
+ work_category_id: z.string().uuid().optional().describe('Work category ID'),
172
+ }, async (params) => {
173
+ try {
174
+ const result = await client.create('work-requests', buildBody(params));
175
+ return formatResult(result);
176
+ }
177
+ catch (err) {
178
+ return formatError(err);
179
+ }
180
+ });
181
+ server.tool('update_work_request', 'Update an existing work request by ID. Requires work_requests:write scope.', {
182
+ id: z.string().uuid().describe('Work request ID'),
183
+ title: z.string().min(1).max(500).optional().describe('Work request title'),
184
+ description: z.string().optional().describe('Description'),
185
+ priority: z.enum(['LOW', 'MEDIUM', 'HIGH', 'CRITICAL']).optional().describe('Priority level'),
186
+ status: z.enum(['SUBMITTED', 'APPROVED', 'REJECTED', 'CONVERTED']).optional().describe('Status'),
187
+ site_id: z.string().uuid().optional().describe('Site ID'),
188
+ building_id: z.string().uuid().optional().describe('Building ID'),
189
+ location_id: z.string().uuid().optional().describe('Location ID'),
190
+ asset_id: z.string().uuid().optional().describe('Asset ID'),
191
+ system_id: z.string().uuid().optional().describe('System ID'),
192
+ work_category_id: z.string().uuid().optional().describe('Work category ID'),
193
+ }, async ({ id, ...rest }) => {
194
+ try {
195
+ const result = await client.update('work-requests', id, buildBody(rest));
196
+ return formatResult(result);
197
+ }
198
+ catch (err) {
199
+ return formatError(err);
200
+ }
201
+ });
202
+ server.tool('delete_work_request', 'Delete a work request by ID. Requires work_requests:write scope.', { id: z.string().uuid().describe('Work request ID') }, async ({ id }) => {
203
+ try {
204
+ const result = await client.remove('work-requests', id);
205
+ return formatResult(result);
206
+ }
207
+ catch (err) {
208
+ return formatError(err);
209
+ }
210
+ });
211
+ // ============================================================
212
+ // 4. Vendors (scope: vendors)
213
+ // ============================================================
214
+ server.tool('create_vendor', 'Create a new vendor. Requires vendors:write scope.', {
215
+ name: z.string().min(1).max(500).describe('Vendor name (required)'),
216
+ contact_name: z.string().max(200).optional().describe('Contact person name'),
217
+ contact_email: z.string().max(200).optional().describe('Contact email'),
218
+ contact_phone: z.string().max(50).optional().describe('Contact phone'),
219
+ address: z.string().max(500).optional().describe('Street address'),
220
+ city: z.string().max(100).optional().describe('City'),
221
+ state: z.string().max(100).optional().describe('State/province'),
222
+ country: z.string().max(100).optional().describe('Country'),
223
+ status: z.string().max(50).optional().describe('Vendor status'),
224
+ category: z.string().max(100).optional().describe('Vendor category'),
225
+ website: z.string().max(500).optional().describe('Website URL'),
226
+ description: z.string().optional().describe('Description'),
227
+ }, async (params) => {
228
+ try {
229
+ const result = await client.create('vendors', buildBody(params));
230
+ return formatResult(result);
231
+ }
232
+ catch (err) {
233
+ return formatError(err);
234
+ }
235
+ });
236
+ server.tool('update_vendor', 'Update an existing vendor by ID. Requires vendors:write scope.', {
237
+ id: z.string().uuid().describe('Vendor ID'),
238
+ name: z.string().min(1).max(500).optional().describe('Vendor name'),
239
+ contact_name: z.string().max(200).optional().describe('Contact person name'),
240
+ contact_email: z.string().max(200).optional().describe('Contact email'),
241
+ contact_phone: z.string().max(50).optional().describe('Contact phone'),
242
+ address: z.string().max(500).optional().describe('Street address'),
243
+ city: z.string().max(100).optional().describe('City'),
244
+ state: z.string().max(100).optional().describe('State/province'),
245
+ country: z.string().max(100).optional().describe('Country'),
246
+ status: z.string().max(50).optional().describe('Vendor status'),
247
+ category: z.string().max(100).optional().describe('Vendor category'),
248
+ website: z.string().max(500).optional().describe('Website URL'),
249
+ description: z.string().optional().describe('Description'),
250
+ }, async ({ id, ...rest }) => {
251
+ try {
252
+ const result = await client.update('vendors', id, buildBody(rest));
253
+ return formatResult(result);
254
+ }
255
+ catch (err) {
256
+ return formatError(err);
257
+ }
258
+ });
259
+ server.tool('delete_vendor', 'Delete a vendor by ID. Requires vendors:write scope.', { id: z.string().uuid().describe('Vendor ID') }, async ({ id }) => {
260
+ try {
261
+ const result = await client.remove('vendors', id);
262
+ return formatResult(result);
263
+ }
264
+ catch (err) {
265
+ return formatError(err);
266
+ }
267
+ });
268
+ // ============================================================
269
+ // 5. Sites (scope: sites)
270
+ // ============================================================
271
+ server.tool('create_site', 'Create a new site. Requires sites:write scope.', {
272
+ name: z.string().min(1).max(500).describe('Site name (required)'),
273
+ address: z.string().max(500).optional().describe('Street address'),
274
+ city: z.string().max(200).optional().describe('City'),
275
+ province: z.string().max(200).optional().describe('Province/state'),
276
+ postal_code: z.string().max(20).optional().describe('Postal/zip code'),
277
+ country: z.string().max(200).optional().describe('Country'),
278
+ description: z.string().optional().describe('Description'),
279
+ }, async (params) => {
280
+ try {
281
+ const result = await client.create('sites', buildBody(params));
282
+ return formatResult(result);
283
+ }
284
+ catch (err) {
285
+ return formatError(err);
286
+ }
287
+ });
288
+ server.tool('update_site', 'Update an existing site by ID. Requires sites:write scope.', {
289
+ id: z.string().uuid().describe('Site ID'),
290
+ name: z.string().min(1).max(500).optional().describe('Site name'),
291
+ address: z.string().max(500).optional().describe('Street address'),
292
+ city: z.string().max(200).optional().describe('City'),
293
+ province: z.string().max(200).optional().describe('Province/state'),
294
+ postal_code: z.string().max(20).optional().describe('Postal/zip code'),
295
+ country: z.string().max(200).optional().describe('Country'),
296
+ description: z.string().optional().describe('Description'),
297
+ }, async ({ id, ...rest }) => {
298
+ try {
299
+ const result = await client.update('sites', id, buildBody(rest));
300
+ return formatResult(result);
301
+ }
302
+ catch (err) {
303
+ return formatError(err);
304
+ }
305
+ });
306
+ server.tool('delete_site', 'Delete a site by ID. Requires sites:write scope.', { id: z.string().uuid().describe('Site ID') }, async ({ id }) => {
307
+ try {
308
+ const result = await client.remove('sites', id);
309
+ return formatResult(result);
310
+ }
311
+ catch (err) {
312
+ return formatError(err);
313
+ }
314
+ });
315
+ // ============================================================
316
+ // 6. Buildings (scope: buildings)
317
+ // ============================================================
318
+ server.tool('create_building', 'Create a new building. Requires buildings:write scope.', {
319
+ name: z.string().min(1).max(500).describe('Building name (required)'),
320
+ site_id: z.string().uuid().describe('Site ID (required)'),
321
+ floors: z.number().int().optional().describe('Number of floors'),
322
+ area: z.number().min(0).optional().describe('Area (sq ft or sq m)'),
323
+ type: z.string().max(100).optional().describe('Building type'),
324
+ }, async (params) => {
325
+ try {
326
+ const result = await client.create('buildings', buildBody(params));
327
+ return formatResult(result);
328
+ }
329
+ catch (err) {
330
+ return formatError(err);
331
+ }
332
+ });
333
+ server.tool('update_building', 'Update an existing building by ID. Requires buildings:write scope.', {
334
+ id: z.string().uuid().describe('Building ID'),
335
+ name: z.string().min(1).max(500).optional().describe('Building name'),
336
+ site_id: z.string().uuid().optional().describe('Site ID'),
337
+ floors: z.number().int().optional().describe('Number of floors'),
338
+ area: z.number().min(0).optional().describe('Area (sq ft or sq m)'),
339
+ type: z.string().max(100).optional().describe('Building type'),
340
+ }, async ({ id, ...rest }) => {
341
+ try {
342
+ const result = await client.update('buildings', id, buildBody(rest));
343
+ return formatResult(result);
344
+ }
345
+ catch (err) {
346
+ return formatError(err);
347
+ }
348
+ });
349
+ server.tool('delete_building', 'Delete a building by ID. Requires buildings:write scope.', { id: z.string().uuid().describe('Building ID') }, async ({ id }) => {
350
+ try {
351
+ const result = await client.remove('buildings', id);
352
+ return formatResult(result);
353
+ }
354
+ catch (err) {
355
+ return formatError(err);
356
+ }
357
+ });
358
+ // ============================================================
359
+ // 7. Locations (scope: locations)
360
+ // ============================================================
361
+ server.tool('create_location', 'Create a new location within a building. Requires locations:write scope.', {
362
+ name: z.string().min(1).max(500).describe('Location name (required)'),
363
+ building_id: z.string().uuid().describe('Building ID (required)'),
364
+ floor: z.string().max(50).optional().describe('Floor identifier'),
365
+ area: z.number().min(0).optional().describe('Area (sq ft or sq m)'),
366
+ type: z.string().max(100).optional().describe('Location type'),
367
+ }, async (params) => {
368
+ try {
369
+ const result = await client.create('locations', buildBody(params));
370
+ return formatResult(result);
371
+ }
372
+ catch (err) {
373
+ return formatError(err);
374
+ }
375
+ });
376
+ server.tool('update_location', 'Update an existing location by ID. Requires locations:write scope.', {
377
+ id: z.string().uuid().describe('Location ID'),
378
+ name: z.string().min(1).max(500).optional().describe('Location name'),
379
+ building_id: z.string().uuid().optional().describe('Building ID'),
380
+ floor: z.string().max(50).optional().describe('Floor identifier'),
381
+ area: z.number().min(0).optional().describe('Area (sq ft or sq m)'),
382
+ type: z.string().max(100).optional().describe('Location type'),
383
+ }, async ({ id, ...rest }) => {
384
+ try {
385
+ const result = await client.update('locations', id, buildBody(rest));
386
+ return formatResult(result);
387
+ }
388
+ catch (err) {
389
+ return formatError(err);
390
+ }
391
+ });
392
+ server.tool('delete_location', 'Delete a location by ID. Requires locations:write scope.', { id: z.string().uuid().describe('Location ID') }, async ({ id }) => {
393
+ try {
394
+ const result = await client.remove('locations', id);
395
+ return formatResult(result);
396
+ }
397
+ catch (err) {
398
+ return formatError(err);
399
+ }
400
+ });
401
+ // ============================================================
402
+ // 8. PM Schedules (scope: pm_schedules)
403
+ // ============================================================
404
+ server.tool('create_pm_schedule', 'Create a new preventive maintenance schedule. Requires pm_schedules:write scope.', {
405
+ title: z.string().min(1).max(500).describe('PM schedule title (required)'),
406
+ description: z.string().optional().describe('Description'),
407
+ frequency: z.enum(['DAILY', 'WEEKLY', 'MONTHLY', 'QUARTERLY', 'SEMI_ANNUAL', 'ANNUAL', 'FIVE_YEARLY', 'CUSTOM']).optional().describe('Frequency'),
408
+ custom_interval_weeks: z.number().int().min(1).optional().describe('Custom interval in weeks (when frequency is CUSTOM)'),
409
+ next_due: z.string().optional().describe('Next due date (ISO 8601)'),
410
+ estimated_hours: z.number().min(0).optional().describe('Estimated hours'),
411
+ estimated_cost: z.number().min(0).optional().describe('Estimated cost'),
412
+ site_id: z.string().uuid().optional().describe('Site ID'),
413
+ building_id: z.string().uuid().optional().describe('Building ID'),
414
+ location_id: z.string().uuid().optional().describe('Location ID'),
415
+ asset_id: z.string().uuid().optional().describe('Asset ID'),
416
+ work_category: z.string().max(200).optional().describe('Work category label'),
417
+ schedule_type: z.string().max(100).optional().describe('Schedule type'),
418
+ lead_time_days: z.number().int().min(0).optional().describe('Lead time in days'),
419
+ grace_period_days: z.number().int().min(0).optional().describe('Grace period in days'),
420
+ safety_requirements: z.string().optional().describe('Safety requirements'),
421
+ status: z.enum(['active', 'inactive']).optional().describe('Schedule status'),
422
+ auto_generate_wo: z.boolean().optional().describe('Auto-generate work orders'),
423
+ floating: z.boolean().optional().describe('Floating schedule (due date based on completion)'),
424
+ start_date: z.string().optional().describe('Start date (ISO 8601)'),
425
+ asset_ids: z.array(z.string().uuid()).optional().describe('Array of asset IDs'),
426
+ system_ids: z.array(z.string().uuid()).optional().describe('Array of system IDs'),
427
+ location_ids: z.array(z.string().uuid()).optional().describe('Array of location IDs'),
428
+ }, async (params) => {
429
+ try {
430
+ const result = await client.create('pm-schedules', buildBody(params));
431
+ return formatResult(result);
432
+ }
433
+ catch (err) {
434
+ return formatError(err);
435
+ }
436
+ });
437
+ server.tool('update_pm_schedule', 'Update an existing PM schedule by ID. Requires pm_schedules:write scope.', {
438
+ id: z.string().uuid().describe('PM schedule ID'),
439
+ title: z.string().min(1).max(500).optional().describe('PM schedule title'),
440
+ description: z.string().optional().describe('Description'),
441
+ frequency: z.enum(['DAILY', 'WEEKLY', 'MONTHLY', 'QUARTERLY', 'SEMI_ANNUAL', 'ANNUAL', 'FIVE_YEARLY', 'CUSTOM']).optional().describe('Frequency'),
442
+ custom_interval_weeks: z.number().int().min(1).optional().describe('Custom interval in weeks (when frequency is CUSTOM)'),
443
+ next_due: z.string().optional().describe('Next due date (ISO 8601)'),
444
+ estimated_hours: z.number().min(0).optional().describe('Estimated hours'),
445
+ estimated_cost: z.number().min(0).optional().describe('Estimated cost'),
446
+ site_id: z.string().uuid().optional().describe('Site ID'),
447
+ building_id: z.string().uuid().optional().describe('Building ID'),
448
+ location_id: z.string().uuid().optional().describe('Location ID'),
449
+ asset_id: z.string().uuid().optional().describe('Asset ID'),
450
+ work_category: z.string().max(200).optional().describe('Work category label'),
451
+ schedule_type: z.string().max(100).optional().describe('Schedule type'),
452
+ lead_time_days: z.number().int().min(0).optional().describe('Lead time in days'),
453
+ grace_period_days: z.number().int().min(0).optional().describe('Grace period in days'),
454
+ safety_requirements: z.string().optional().describe('Safety requirements'),
455
+ status: z.enum(['active', 'inactive']).optional().describe('Schedule status'),
456
+ auto_generate_wo: z.boolean().optional().describe('Auto-generate work orders'),
457
+ floating: z.boolean().optional().describe('Floating schedule (due date based on completion)'),
458
+ start_date: z.string().optional().describe('Start date (ISO 8601)'),
459
+ asset_ids: z.array(z.string().uuid()).optional().describe('Array of asset IDs'),
460
+ system_ids: z.array(z.string().uuid()).optional().describe('Array of system IDs'),
461
+ location_ids: z.array(z.string().uuid()).optional().describe('Array of location IDs'),
462
+ }, async ({ id, ...rest }) => {
463
+ try {
464
+ const result = await client.update('pm-schedules', id, buildBody(rest));
465
+ return formatResult(result);
466
+ }
467
+ catch (err) {
468
+ return formatError(err);
469
+ }
470
+ });
471
+ server.tool('delete_pm_schedule', 'Delete a PM schedule by ID. Requires pm_schedules:write scope.', { id: z.string().uuid().describe('PM schedule ID') }, async ({ id }) => {
472
+ try {
473
+ const result = await client.remove('pm-schedules', id);
474
+ return formatResult(result);
475
+ }
476
+ catch (err) {
477
+ return formatError(err);
478
+ }
479
+ });
480
+ // ============================================================
481
+ // 9. Projects (scope: projects)
482
+ // ============================================================
483
+ server.tool('create_project', 'Create a new project. Requires projects:write scope.', {
484
+ name: z.string().min(1).max(500).describe('Project name (required)'),
485
+ status: z.string().max(100).describe('Project status (required)'),
486
+ start_date: z.string().describe('Start date (ISO 8601, required)'),
487
+ project_code: z.string().max(100).optional().describe('Project code'),
488
+ project_type: z.string().max(100).optional().describe('Project type'),
489
+ current_phase: z.string().max(100).optional().describe('Current phase'),
490
+ description: z.string().optional().describe('Description'),
491
+ end_date: z.string().optional().describe('End date (ISO 8601)'),
492
+ budget: z.number().min(0).optional().describe('Total budget'),
493
+ project_manager: z.string().max(200).optional().describe('Project manager name'),
494
+ progress_percentage: z.number().min(0).max(100).optional().describe('Progress percentage (0-100)'),
495
+ health_status: z.enum(['on_track', 'at_risk', 'delayed', 'critical']).optional().describe('Health status'),
496
+ image_url: z.string().max(2000).optional().describe('Image URL'),
497
+ }, async (params) => {
498
+ try {
499
+ const result = await client.create('projects', buildBody(params));
500
+ return formatResult(result);
501
+ }
502
+ catch (err) {
503
+ return formatError(err);
504
+ }
505
+ });
506
+ server.tool('update_project', 'Update an existing project by ID. Requires projects:write scope.', {
507
+ id: z.string().uuid().describe('Project ID'),
508
+ name: z.string().min(1).max(500).optional().describe('Project name'),
509
+ status: z.string().max(100).optional().describe('Project status'),
510
+ start_date: z.string().optional().describe('Start date (ISO 8601)'),
511
+ project_code: z.string().max(100).optional().describe('Project code'),
512
+ project_type: z.string().max(100).optional().describe('Project type'),
513
+ current_phase: z.string().max(100).optional().describe('Current phase'),
514
+ description: z.string().optional().describe('Description'),
515
+ end_date: z.string().optional().describe('End date (ISO 8601)'),
516
+ budget: z.number().min(0).optional().describe('Total budget'),
517
+ project_manager: z.string().max(200).optional().describe('Project manager name'),
518
+ progress_percentage: z.number().min(0).max(100).optional().describe('Progress percentage (0-100)'),
519
+ health_status: z.enum(['on_track', 'at_risk', 'delayed', 'critical']).optional().describe('Health status'),
520
+ image_url: z.string().max(2000).optional().describe('Image URL'),
521
+ }, async ({ id, ...rest }) => {
522
+ try {
523
+ const result = await client.update('projects', id, buildBody(rest));
524
+ return formatResult(result);
525
+ }
526
+ catch (err) {
527
+ return formatError(err);
528
+ }
529
+ });
530
+ server.tool('delete_project', 'Delete a project by ID. Requires projects:write scope.', { id: z.string().uuid().describe('Project ID') }, async ({ id }) => {
531
+ try {
532
+ const result = await client.remove('projects', id);
533
+ return formatResult(result);
534
+ }
535
+ catch (err) {
536
+ return formatError(err);
537
+ }
538
+ });
539
+ // ============================================================
540
+ // 10. Contracts (scope: contracts)
541
+ // ============================================================
542
+ server.tool('create_contract', 'Create a new contract. Requires contracts:write scope.', {
543
+ title: z.string().min(1).max(500).describe('Contract title (required)'),
544
+ category: z.string().max(200).describe('Contract category (required)'),
545
+ start_date: z.string().describe('Start date (ISO 8601, required)'),
546
+ end_date: z.string().describe('End date (ISO 8601, required)'),
547
+ company_id: z.string().uuid().optional().describe('Vendor ID'),
548
+ purchase_order: z.string().max(200).optional().describe('Purchase order reference'),
549
+ extendable: z.boolean().optional().describe('Whether contract is extendable'),
550
+ annual_cost: z.number().min(0).optional().describe('Annual cost'),
551
+ description: z.string().optional().describe('Description'),
552
+ quality_score: z.number().min(1).max(10).optional().describe('Quality score (1-10)'),
553
+ }, async (params) => {
554
+ try {
555
+ const result = await client.create('contracts', buildBody(params));
556
+ return formatResult(result);
557
+ }
558
+ catch (err) {
559
+ return formatError(err);
560
+ }
561
+ });
562
+ server.tool('update_contract', 'Update an existing contract by ID. Requires contracts:write scope.', {
563
+ id: z.string().uuid().describe('Contract ID'),
564
+ title: z.string().min(1).max(500).optional().describe('Contract title'),
565
+ category: z.string().max(200).optional().describe('Contract category'),
566
+ start_date: z.string().optional().describe('Start date (ISO 8601)'),
567
+ end_date: z.string().optional().describe('End date (ISO 8601)'),
568
+ company_id: z.string().uuid().optional().describe('Vendor ID'),
569
+ purchase_order: z.string().max(200).optional().describe('Purchase order reference'),
570
+ extendable: z.boolean().optional().describe('Whether contract is extendable'),
571
+ annual_cost: z.number().min(0).optional().describe('Annual cost'),
572
+ description: z.string().optional().describe('Description'),
573
+ quality_score: z.number().min(1).max(10).optional().describe('Quality score (1-10)'),
574
+ }, async ({ id, ...rest }) => {
575
+ try {
576
+ const result = await client.update('contracts', id, buildBody(rest));
577
+ return formatResult(result);
578
+ }
579
+ catch (err) {
580
+ return formatError(err);
581
+ }
582
+ });
583
+ server.tool('delete_contract', 'Delete a contract by ID. Requires contracts:write scope.', { id: z.string().uuid().describe('Contract ID') }, async ({ id }) => {
584
+ try {
585
+ const result = await client.remove('contracts', id);
586
+ return formatResult(result);
587
+ }
588
+ catch (err) {
589
+ return formatError(err);
590
+ }
591
+ });
592
+ // ============================================================
593
+ // 11. Invoices (scope: invoices)
594
+ // ============================================================
595
+ server.tool('create_invoice', 'Create a new invoice. Requires invoices:write scope.', {
596
+ invoice_number: z.string().min(1).max(200).describe('Invoice number (required)'),
597
+ amount: z.number().min(0).describe('Invoice amount (required)'),
598
+ invoice_date: z.string().describe('Invoice date (ISO 8601, required)'),
599
+ description: z.string().optional().describe('Description'),
600
+ tax_amount: z.number().min(0).optional().describe('Tax amount'),
601
+ due_date: z.string().optional().describe('Due date (ISO 8601)'),
602
+ paid_date: z.string().optional().describe('Paid date (ISO 8601)'),
603
+ status: z.enum(['pending', 'approved', 'paid', 'voided']).optional().describe('Invoice status'),
604
+ notes: z.string().optional().describe('Notes'),
605
+ project_id: z.string().uuid().optional().describe('Project ID'),
606
+ work_order_id: z.string().uuid().optional().describe('Work order ID'),
607
+ purchase_order_id: z.string().uuid().optional().describe('Purchase order ID'),
608
+ vendor_id: z.string().uuid().optional().describe('Vendor ID'),
609
+ category_id: z.string().uuid().optional().describe('Cost category ID'),
610
+ }, async (params) => {
611
+ try {
612
+ const result = await client.create('invoices', buildBody(params));
613
+ return formatResult(result);
614
+ }
615
+ catch (err) {
616
+ return formatError(err);
617
+ }
618
+ });
619
+ server.tool('update_invoice', 'Update an existing invoice by ID. Requires invoices:write scope.', {
620
+ id: z.string().uuid().describe('Invoice ID'),
621
+ invoice_number: z.string().min(1).max(200).optional().describe('Invoice number'),
622
+ amount: z.number().min(0).optional().describe('Invoice amount'),
623
+ invoice_date: z.string().optional().describe('Invoice date (ISO 8601)'),
624
+ description: z.string().optional().describe('Description'),
625
+ tax_amount: z.number().min(0).optional().describe('Tax amount'),
626
+ due_date: z.string().optional().describe('Due date (ISO 8601)'),
627
+ paid_date: z.string().optional().describe('Paid date (ISO 8601)'),
628
+ status: z.enum(['pending', 'approved', 'paid', 'voided']).optional().describe('Invoice status'),
629
+ notes: z.string().optional().describe('Notes'),
630
+ project_id: z.string().uuid().optional().describe('Project ID'),
631
+ work_order_id: z.string().uuid().optional().describe('Work order ID'),
632
+ purchase_order_id: z.string().uuid().optional().describe('Purchase order ID'),
633
+ vendor_id: z.string().uuid().optional().describe('Vendor ID'),
634
+ category_id: z.string().uuid().optional().describe('Cost category ID'),
635
+ }, async ({ id, ...rest }) => {
636
+ try {
637
+ const result = await client.update('invoices', id, buildBody(rest));
638
+ return formatResult(result);
639
+ }
640
+ catch (err) {
641
+ return formatError(err);
642
+ }
643
+ });
644
+ server.tool('delete_invoice', 'Delete an invoice by ID. Requires invoices:write scope.', { id: z.string().uuid().describe('Invoice ID') }, async ({ id }) => {
645
+ try {
646
+ const result = await client.remove('invoices', id);
647
+ return formatResult(result);
648
+ }
649
+ catch (err) {
650
+ return formatError(err);
651
+ }
652
+ });
653
+ // ============================================================
654
+ // 12. Purchase Orders (scope: purchase_orders)
655
+ // ============================================================
656
+ server.tool('create_purchase_order', 'Create a new purchase order. Requires purchase_orders:write scope.', {
657
+ po_number: z.string().min(1).max(200).describe('PO number (required)'),
658
+ amount: z.number().min(0).describe('PO amount (required)'),
659
+ description: z.string().optional().describe('Description'),
660
+ status: z.enum(['draft', 'issued', 'partially_received', 'received', 'closed', 'cancelled']).optional().describe('PO status'),
661
+ issued_date: z.string().optional().describe('Issued date (ISO 8601)'),
662
+ expected_date: z.string().optional().describe('Expected delivery date (ISO 8601)'),
663
+ notes: z.string().optional().describe('Notes'),
664
+ project_id: z.string().uuid().optional().describe('Project ID'),
665
+ work_order_id: z.string().uuid().optional().describe('Work order ID'),
666
+ vendor_id: z.string().uuid().optional().describe('Vendor ID'),
667
+ category_id: z.string().uuid().optional().describe('Cost category ID'),
668
+ }, async (params) => {
669
+ try {
670
+ const result = await client.create('purchase-orders', buildBody(params));
671
+ return formatResult(result);
672
+ }
673
+ catch (err) {
674
+ return formatError(err);
675
+ }
676
+ });
677
+ server.tool('update_purchase_order', 'Update an existing purchase order by ID. Requires purchase_orders:write scope.', {
678
+ id: z.string().uuid().describe('Purchase order ID'),
679
+ po_number: z.string().min(1).max(200).optional().describe('PO number'),
680
+ amount: z.number().min(0).optional().describe('PO amount'),
681
+ description: z.string().optional().describe('Description'),
682
+ status: z.enum(['draft', 'issued', 'partially_received', 'received', 'closed', 'cancelled']).optional().describe('PO status'),
683
+ issued_date: z.string().optional().describe('Issued date (ISO 8601)'),
684
+ expected_date: z.string().optional().describe('Expected delivery date (ISO 8601)'),
685
+ notes: z.string().optional().describe('Notes'),
686
+ project_id: z.string().uuid().optional().describe('Project ID'),
687
+ work_order_id: z.string().uuid().optional().describe('Work order ID'),
688
+ vendor_id: z.string().uuid().optional().describe('Vendor ID'),
689
+ category_id: z.string().uuid().optional().describe('Cost category ID'),
690
+ }, async ({ id, ...rest }) => {
691
+ try {
692
+ const result = await client.update('purchase-orders', id, buildBody(rest));
693
+ return formatResult(result);
694
+ }
695
+ catch (err) {
696
+ return formatError(err);
697
+ }
698
+ });
699
+ server.tool('delete_purchase_order', 'Delete a purchase order by ID. Requires purchase_orders:write scope.', { id: z.string().uuid().describe('Purchase order ID') }, async ({ id }) => {
700
+ try {
701
+ const result = await client.remove('purchase-orders', id);
702
+ return formatResult(result);
703
+ }
704
+ catch (err) {
705
+ return formatError(err);
706
+ }
707
+ });
708
+ // ============================================================
709
+ // 13. Expenses (scope: expenses)
710
+ // ============================================================
711
+ server.tool('create_expense', 'Create a new expense. Requires expenses:write scope.', {
712
+ project_id: z.string().uuid().describe('Project ID (required)'),
713
+ description: z.string().min(1).describe('Expense description (required)'),
714
+ amount: z.number().min(0).describe('Expense amount (required)'),
715
+ expense_date: z.string().describe('Expense date (ISO 8601, required)'),
716
+ work_order_id: z.string().uuid().optional().describe('Work order ID'),
717
+ category_id: z.string().uuid().optional().describe('Cost category ID'),
718
+ receipt_url: z.string().max(2000).optional().describe('Receipt URL'),
719
+ notes: z.string().optional().describe('Notes'),
720
+ }, async (params) => {
721
+ try {
722
+ const result = await client.create('expenses', buildBody(params));
723
+ return formatResult(result);
724
+ }
725
+ catch (err) {
726
+ return formatError(err);
727
+ }
728
+ });
729
+ server.tool('update_expense', 'Update an existing expense by ID. Requires expenses:write scope.', {
730
+ id: z.string().uuid().describe('Expense ID'),
731
+ project_id: z.string().uuid().optional().describe('Project ID'),
732
+ description: z.string().optional().describe('Expense description'),
733
+ amount: z.number().min(0).optional().describe('Expense amount'),
734
+ expense_date: z.string().optional().describe('Expense date (ISO 8601)'),
735
+ work_order_id: z.string().uuid().optional().describe('Work order ID'),
736
+ category_id: z.string().uuid().optional().describe('Cost category ID'),
737
+ receipt_url: z.string().max(2000).optional().describe('Receipt URL'),
738
+ notes: z.string().optional().describe('Notes'),
739
+ }, async ({ id, ...rest }) => {
740
+ try {
741
+ const result = await client.update('expenses', id, buildBody(rest));
742
+ return formatResult(result);
743
+ }
744
+ catch (err) {
745
+ return formatError(err);
746
+ }
747
+ });
748
+ server.tool('delete_expense', 'Delete an expense by ID. Requires expenses:write scope.', { id: z.string().uuid().describe('Expense ID') }, async ({ id }) => {
749
+ try {
750
+ const result = await client.remove('expenses', id);
751
+ return formatResult(result);
752
+ }
753
+ catch (err) {
754
+ return formatError(err);
755
+ }
756
+ });
757
+ // ============================================================
758
+ // 14. Budgets (scope: budgets)
759
+ // ============================================================
760
+ server.tool('create_budget', 'Create a new budget. Requires budgets:write scope.', {
761
+ year: z.number().int().min(2000).max(2100).describe('Budget year (required)'),
762
+ site_id: z.string().uuid().optional().describe('Site ID'),
763
+ building_id: z.string().uuid().optional().describe('Building ID'),
764
+ funding_source: z.number().int().optional().describe('Funding source identifier'),
765
+ budgeted_amount: z.number().min(0).optional().describe('Budgeted amount'),
766
+ allocated_amount: z.number().min(0).optional().describe('Allocated amount'),
767
+ notes: z.string().optional().describe('Notes'),
768
+ }, async (params) => {
769
+ try {
770
+ const result = await client.create('budgets', buildBody(params));
771
+ return formatResult(result);
772
+ }
773
+ catch (err) {
774
+ return formatError(err);
775
+ }
776
+ });
777
+ server.tool('update_budget', 'Update an existing budget by ID. Requires budgets:write scope.', {
778
+ id: z.string().uuid().describe('Budget ID'),
779
+ year: z.number().int().min(2000).max(2100).optional().describe('Budget year'),
780
+ site_id: z.string().uuid().optional().describe('Site ID'),
781
+ building_id: z.string().uuid().optional().describe('Building ID'),
782
+ funding_source: z.number().int().optional().describe('Funding source identifier'),
783
+ budgeted_amount: z.number().min(0).optional().describe('Budgeted amount'),
784
+ allocated_amount: z.number().min(0).optional().describe('Allocated amount'),
785
+ notes: z.string().optional().describe('Notes'),
786
+ }, async ({ id, ...rest }) => {
787
+ try {
788
+ const result = await client.update('budgets', id, buildBody(rest));
789
+ return formatResult(result);
790
+ }
791
+ catch (err) {
792
+ return formatError(err);
793
+ }
794
+ });
795
+ server.tool('delete_budget', 'Delete a budget by ID. Requires budgets:write scope.', { id: z.string().uuid().describe('Budget ID') }, async ({ id }) => {
796
+ try {
797
+ const result = await client.remove('budgets', id);
798
+ return formatResult(result);
799
+ }
800
+ catch (err) {
801
+ return formatError(err);
802
+ }
803
+ });
804
+ // ============================================================
805
+ // 15. Asset Comments (scope: asset_comments)
806
+ // ============================================================
807
+ server.tool('create_asset_comment', 'Create a new comment on an asset. Requires asset_comments:write scope.', {
808
+ asset_id: z.string().uuid().describe('Asset ID (required)'),
809
+ comment: z.string().min(1).describe('Comment text (required)'),
810
+ user_id: z.string().max(200).optional().describe('User ID of commenter'),
811
+ }, async (params) => {
812
+ try {
813
+ const result = await client.create('asset-comments', buildBody(params));
814
+ return formatResult(result);
815
+ }
816
+ catch (err) {
817
+ return formatError(err);
818
+ }
819
+ });
820
+ server.tool('update_asset_comment', 'Update an existing asset comment by ID. Requires asset_comments:write scope.', {
821
+ id: z.string().uuid().describe('Asset comment ID'),
822
+ comment: z.string().min(1).optional().describe('Comment text'),
823
+ user_id: z.string().max(200).optional().describe('User ID of commenter'),
824
+ }, async ({ id, ...rest }) => {
825
+ try {
826
+ const result = await client.update('asset-comments', id, buildBody(rest));
827
+ return formatResult(result);
828
+ }
829
+ catch (err) {
830
+ return formatError(err);
831
+ }
832
+ });
833
+ server.tool('delete_asset_comment', 'Delete an asset comment by ID. Requires asset_comments:write scope.', { id: z.string().uuid().describe('Asset comment ID') }, async ({ id }) => {
834
+ try {
835
+ const result = await client.remove('asset-comments', id);
836
+ return formatResult(result);
837
+ }
838
+ catch (err) {
839
+ return formatError(err);
840
+ }
841
+ });
842
+ // ============================================================
843
+ // 16. Work Order Comments (scope: work_order_comments)
844
+ // ============================================================
845
+ server.tool('create_work_order_comment', 'Create a new comment on a work order. Requires work_order_comments:write scope.', {
846
+ work_order_id: z.string().uuid().describe('Work order ID (required)'),
847
+ comment: z.string().min(1).describe('Comment text (required)'),
848
+ user_id: z.string().max(200).optional().describe('User ID of commenter'),
849
+ }, async (params) => {
850
+ try {
851
+ const result = await client.create('work-order-comments', buildBody(params));
852
+ return formatResult(result);
853
+ }
854
+ catch (err) {
855
+ return formatError(err);
856
+ }
857
+ });
858
+ server.tool('update_work_order_comment', 'Update an existing work order comment by ID. Requires work_order_comments:write scope.', {
859
+ id: z.string().uuid().describe('Work order comment ID'),
860
+ comment: z.string().min(1).optional().describe('Comment text'),
861
+ user_id: z.string().max(200).optional().describe('User ID of commenter'),
862
+ }, async ({ id, ...rest }) => {
863
+ try {
864
+ const result = await client.update('work-order-comments', id, buildBody(rest));
865
+ return formatResult(result);
866
+ }
867
+ catch (err) {
868
+ return formatError(err);
869
+ }
870
+ });
871
+ server.tool('delete_work_order_comment', 'Delete a work order comment by ID. Requires work_order_comments:write scope.', { id: z.string().uuid().describe('Work order comment ID') }, async ({ id }) => {
872
+ try {
873
+ const result = await client.remove('work-order-comments', id);
874
+ return formatResult(result);
875
+ }
876
+ catch (err) {
877
+ return formatError(err);
878
+ }
879
+ });
880
+ // ============================================================
881
+ // 17. Project Comments (scope: project_comments)
882
+ // ============================================================
883
+ server.tool('create_project_comment', 'Create a new comment on a project. Requires project_comments:write scope.', {
884
+ project_id: z.string().uuid().describe('Project ID (required)'),
885
+ content: z.string().min(1).describe('Comment content (required)'),
886
+ parent_id: z.string().uuid().optional().describe('Parent comment ID (for threading)'),
887
+ user_id: z.string().max(200).optional().describe('User ID of commenter'),
888
+ }, async (params) => {
889
+ try {
890
+ const result = await client.create('project-comments', buildBody(params));
891
+ return formatResult(result);
892
+ }
893
+ catch (err) {
894
+ return formatError(err);
895
+ }
896
+ });
897
+ server.tool('update_project_comment', 'Update an existing project comment by ID. Requires project_comments:write scope.', {
898
+ id: z.string().uuid().describe('Project comment ID'),
899
+ content: z.string().min(1).optional().describe('Comment content'),
900
+ parent_id: z.string().uuid().optional().describe('Parent comment ID'),
901
+ user_id: z.string().max(200).optional().describe('User ID of commenter'),
902
+ }, async ({ id, ...rest }) => {
903
+ try {
904
+ const result = await client.update('project-comments', id, buildBody(rest));
905
+ return formatResult(result);
906
+ }
907
+ catch (err) {
908
+ return formatError(err);
909
+ }
910
+ });
911
+ server.tool('delete_project_comment', 'Delete a project comment by ID. Requires project_comments:write scope.', { id: z.string().uuid().describe('Project comment ID') }, async ({ id }) => {
912
+ try {
913
+ const result = await client.remove('project-comments', id);
914
+ return formatResult(result);
915
+ }
916
+ catch (err) {
917
+ return formatError(err);
918
+ }
919
+ });
920
+ // ============================================================
921
+ // 18. Asset Costs (scope: asset_costs)
922
+ // ============================================================
923
+ server.tool('create_asset_cost', 'Create a new asset cost entry. Requires asset_costs:write scope.', {
924
+ category: z.enum(['Repair', 'PM', 'Operation', 'Replacement', 'Decommission']).describe('Cost category (required)'),
925
+ amount: z.number().min(0).describe('Cost amount (required)'),
926
+ cost_date: z.string().describe('Cost date (ISO 8601, required)'),
927
+ asset_id: z.string().uuid().optional().describe('Asset ID'),
928
+ site_id: z.string().uuid().optional().describe('Site ID'),
929
+ building_id: z.string().uuid().optional().describe('Building ID'),
930
+ work_order_id: z.string().uuid().optional().describe('Work order ID'),
931
+ description: z.string().optional().describe('Description'),
932
+ vendor_id: z.string().uuid().optional().describe('Vendor ID'),
933
+ }, async (params) => {
934
+ try {
935
+ const result = await client.create('asset-costs', buildBody(params));
936
+ return formatResult(result);
937
+ }
938
+ catch (err) {
939
+ return formatError(err);
940
+ }
941
+ });
942
+ server.tool('update_asset_cost', 'Update an existing asset cost entry by ID. Requires asset_costs:write scope.', {
943
+ id: z.string().uuid().describe('Asset cost ID'),
944
+ category: z.enum(['Repair', 'PM', 'Operation', 'Replacement', 'Decommission']).optional().describe('Cost category'),
945
+ amount: z.number().min(0).optional().describe('Cost amount'),
946
+ cost_date: z.string().optional().describe('Cost date (ISO 8601)'),
947
+ asset_id: z.string().uuid().optional().describe('Asset ID'),
948
+ site_id: z.string().uuid().optional().describe('Site ID'),
949
+ building_id: z.string().uuid().optional().describe('Building ID'),
950
+ work_order_id: z.string().uuid().optional().describe('Work order ID'),
951
+ description: z.string().optional().describe('Description'),
952
+ vendor_id: z.string().uuid().optional().describe('Vendor ID'),
953
+ }, async ({ id, ...rest }) => {
954
+ try {
955
+ const result = await client.update('asset-costs', id, buildBody(rest));
956
+ return formatResult(result);
957
+ }
958
+ catch (err) {
959
+ return formatError(err);
960
+ }
961
+ });
962
+ server.tool('delete_asset_cost', 'Delete an asset cost entry by ID. Requires asset_costs:write scope.', { id: z.string().uuid().describe('Asset cost ID') }, async ({ id }) => {
963
+ try {
964
+ const result = await client.remove('asset-costs', id);
965
+ return formatResult(result);
966
+ }
967
+ catch (err) {
968
+ return formatError(err);
969
+ }
970
+ });
971
+ // ============================================================
972
+ // 19. Asset Replacement Plans (scope: asset_replacement_plans)
973
+ // ============================================================
974
+ server.tool('create_asset_replacement_plan', 'Create a new asset replacement plan. Requires asset_replacement_plans:write scope.', {
975
+ asset_id: z.string().uuid().describe('Asset ID (required)'),
976
+ planned_replacement_year: z.number().int().min(2000).max(2100).describe('Planned replacement year (required)'),
977
+ estimated_cost: z.number().min(0).optional().describe('Estimated replacement cost'),
978
+ priority: z.enum(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']).optional().describe('Priority'),
979
+ status: z.enum(['PLANNED', 'BUDGETED', 'APPROVED', 'COMPLETED', 'CANCELLED']).optional().describe('Status'),
980
+ notes: z.string().optional().describe('Notes'),
981
+ funding_source: z.string().max(200).optional().describe('Funding source'),
982
+ }, async (params) => {
983
+ try {
984
+ const result = await client.create('asset-replacement-plans', buildBody(params));
985
+ return formatResult(result);
986
+ }
987
+ catch (err) {
988
+ return formatError(err);
989
+ }
990
+ });
991
+ server.tool('update_asset_replacement_plan', 'Update an existing asset replacement plan by ID. Requires asset_replacement_plans:write scope.', {
992
+ id: z.string().uuid().describe('Asset replacement plan ID'),
993
+ asset_id: z.string().uuid().optional().describe('Asset ID'),
994
+ planned_replacement_year: z.number().int().min(2000).max(2100).optional().describe('Planned replacement year'),
995
+ estimated_cost: z.number().min(0).optional().describe('Estimated replacement cost'),
996
+ priority: z.enum(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']).optional().describe('Priority'),
997
+ status: z.enum(['PLANNED', 'BUDGETED', 'APPROVED', 'COMPLETED', 'CANCELLED']).optional().describe('Status'),
998
+ notes: z.string().optional().describe('Notes'),
999
+ funding_source: z.string().max(200).optional().describe('Funding source'),
1000
+ }, async ({ id, ...rest }) => {
1001
+ try {
1002
+ const result = await client.update('asset-replacement-plans', id, buildBody(rest));
1003
+ return formatResult(result);
1004
+ }
1005
+ catch (err) {
1006
+ return formatError(err);
1007
+ }
1008
+ });
1009
+ server.tool('delete_asset_replacement_plan', 'Delete an asset replacement plan by ID. Requires asset_replacement_plans:write scope.', { id: z.string().uuid().describe('Asset replacement plan ID') }, async ({ id }) => {
1010
+ try {
1011
+ const result = await client.remove('asset-replacement-plans', id);
1012
+ return formatResult(result);
1013
+ }
1014
+ catch (err) {
1015
+ return formatError(err);
1016
+ }
1017
+ });
1018
+ // ============================================================
1019
+ // 20. Project Tasks (scope: project_tasks)
1020
+ // ============================================================
1021
+ server.tool('create_project_task', 'Create a new project task. Requires project_tasks:write scope.', {
1022
+ project_id: z.string().uuid().describe('Project ID (required)'),
1023
+ title: z.string().min(1).max(500).describe('Task title (required)'),
1024
+ description: z.string().optional().describe('Description'),
1025
+ phase_id: z.string().uuid().optional().describe('Phase ID'),
1026
+ status: z.enum(['todo', 'in_progress', 'completed', 'blocked', 'cancelled']).optional().describe('Task status'),
1027
+ priority: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Priority'),
1028
+ assigned_to: z.string().max(200).optional().describe('Assigned user'),
1029
+ start_date: z.string().optional().describe('Start date (ISO 8601)'),
1030
+ due_date: z.string().optional().describe('Due date (ISO 8601)'),
1031
+ estimated_hours: z.number().min(0).optional().describe('Estimated hours'),
1032
+ estimated_cost: z.number().min(0).optional().describe('Estimated cost'),
1033
+ }, async (params) => {
1034
+ try {
1035
+ const result = await client.create('project-tasks', buildBody(params));
1036
+ return formatResult(result);
1037
+ }
1038
+ catch (err) {
1039
+ return formatError(err);
1040
+ }
1041
+ });
1042
+ server.tool('update_project_task', 'Update an existing project task by ID. Requires project_tasks:write scope.', {
1043
+ id: z.string().uuid().describe('Project task ID'),
1044
+ project_id: z.string().uuid().optional().describe('Project ID'),
1045
+ title: z.string().min(1).max(500).optional().describe('Task title'),
1046
+ description: z.string().optional().describe('Description'),
1047
+ phase_id: z.string().uuid().optional().describe('Phase ID'),
1048
+ status: z.enum(['todo', 'in_progress', 'completed', 'blocked', 'cancelled']).optional().describe('Task status'),
1049
+ priority: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Priority'),
1050
+ assigned_to: z.string().max(200).optional().describe('Assigned user'),
1051
+ start_date: z.string().optional().describe('Start date (ISO 8601)'),
1052
+ due_date: z.string().optional().describe('Due date (ISO 8601)'),
1053
+ estimated_hours: z.number().min(0).optional().describe('Estimated hours'),
1054
+ estimated_cost: z.number().min(0).optional().describe('Estimated cost'),
1055
+ }, async ({ id, ...rest }) => {
1056
+ try {
1057
+ const result = await client.update('project-tasks', id, buildBody(rest));
1058
+ return formatResult(result);
1059
+ }
1060
+ catch (err) {
1061
+ return formatError(err);
1062
+ }
1063
+ });
1064
+ server.tool('delete_project_task', 'Delete a project task by ID. Requires project_tasks:write scope.', { id: z.string().uuid().describe('Project task ID') }, async ({ id }) => {
1065
+ try {
1066
+ const result = await client.remove('project-tasks', id);
1067
+ return formatResult(result);
1068
+ }
1069
+ catch (err) {
1070
+ return formatError(err);
1071
+ }
1072
+ });
1073
+ // ============================================================
1074
+ // 21. Project Milestones (scope: project_milestones)
1075
+ // ============================================================
1076
+ server.tool('create_project_milestone', 'Create a new project milestone. Requires project_milestones:write scope.', {
1077
+ project_id: z.string().uuid().describe('Project ID (required)'),
1078
+ name: z.string().min(1).max(500).describe('Milestone name (required)'),
1079
+ due_date: z.string().describe('Due date (ISO 8601, required)'),
1080
+ description: z.string().optional().describe('Description'),
1081
+ status: z.enum(['pending', 'completed', 'missed', 'at_risk']).optional().describe('Milestone status'),
1082
+ completed_date: z.string().optional().describe('Completed date (ISO 8601)'),
1083
+ }, async (params) => {
1084
+ try {
1085
+ const result = await client.create('project-milestones', buildBody(params));
1086
+ return formatResult(result);
1087
+ }
1088
+ catch (err) {
1089
+ return formatError(err);
1090
+ }
1091
+ });
1092
+ server.tool('update_project_milestone', 'Update an existing project milestone by ID. Requires project_milestones:write scope.', {
1093
+ id: z.string().uuid().describe('Project milestone ID'),
1094
+ project_id: z.string().uuid().optional().describe('Project ID'),
1095
+ name: z.string().min(1).max(500).optional().describe('Milestone name'),
1096
+ due_date: z.string().optional().describe('Due date (ISO 8601)'),
1097
+ description: z.string().optional().describe('Description'),
1098
+ status: z.enum(['pending', 'completed', 'missed', 'at_risk']).optional().describe('Milestone status'),
1099
+ completed_date: z.string().optional().describe('Completed date (ISO 8601)'),
1100
+ }, async ({ id, ...rest }) => {
1101
+ try {
1102
+ const result = await client.update('project-milestones', id, buildBody(rest));
1103
+ return formatResult(result);
1104
+ }
1105
+ catch (err) {
1106
+ return formatError(err);
1107
+ }
1108
+ });
1109
+ server.tool('delete_project_milestone', 'Delete a project milestone by ID. Requires project_milestones:write scope.', { id: z.string().uuid().describe('Project milestone ID') }, async ({ id }) => {
1110
+ try {
1111
+ const result = await client.remove('project-milestones', id);
1112
+ return formatResult(result);
1113
+ }
1114
+ catch (err) {
1115
+ return formatError(err);
1116
+ }
1117
+ });
1118
+ // ============================================================
1119
+ // 22. Project Phases (scope: project_phases)
1120
+ // ============================================================
1121
+ server.tool('create_project_phase', 'Create a new project phase. Requires project_phases:write scope.', {
1122
+ project_id: z.string().uuid().describe('Project ID (required)'),
1123
+ name: z.string().min(1).max(500).describe('Phase name (required)'),
1124
+ description: z.string().optional().describe('Description'),
1125
+ status: z.enum(['pending', 'in_progress', 'completed', 'skipped']).optional().describe('Phase status'),
1126
+ start_date: z.string().optional().describe('Start date (ISO 8601)'),
1127
+ end_date: z.string().optional().describe('End date (ISO 8601)'),
1128
+ sort_order: z.number().int().min(0).optional().describe('Sort order'),
1129
+ }, async (params) => {
1130
+ try {
1131
+ const result = await client.create('project-phases', buildBody(params));
1132
+ return formatResult(result);
1133
+ }
1134
+ catch (err) {
1135
+ return formatError(err);
1136
+ }
1137
+ });
1138
+ server.tool('update_project_phase', 'Update an existing project phase by ID. Requires project_phases:write scope.', {
1139
+ id: z.string().uuid().describe('Project phase ID'),
1140
+ project_id: z.string().uuid().optional().describe('Project ID'),
1141
+ name: z.string().min(1).max(500).optional().describe('Phase name'),
1142
+ description: z.string().optional().describe('Description'),
1143
+ status: z.enum(['pending', 'in_progress', 'completed', 'skipped']).optional().describe('Phase status'),
1144
+ start_date: z.string().optional().describe('Start date (ISO 8601)'),
1145
+ end_date: z.string().optional().describe('End date (ISO 8601)'),
1146
+ sort_order: z.number().int().min(0).optional().describe('Sort order'),
1147
+ }, async ({ id, ...rest }) => {
1148
+ try {
1149
+ const result = await client.update('project-phases', id, buildBody(rest));
1150
+ return formatResult(result);
1151
+ }
1152
+ catch (err) {
1153
+ return formatError(err);
1154
+ }
1155
+ });
1156
+ server.tool('delete_project_phase', 'Delete a project phase by ID. Requires project_phases:write scope.', { id: z.string().uuid().describe('Project phase ID') }, async ({ id }) => {
1157
+ try {
1158
+ const result = await client.remove('project-phases', id);
1159
+ return formatResult(result);
1160
+ }
1161
+ catch (err) {
1162
+ return formatError(err);
1163
+ }
1164
+ });
1165
+ // ============================================================
1166
+ // 23. Project Budget Items (scope: project_budget_items)
1167
+ // ============================================================
1168
+ server.tool('create_project_budget_item', 'Create a new project budget item. Requires project_budget_items:write scope.', {
1169
+ project_id: z.string().uuid().describe('Project ID (required)'),
1170
+ category: z.enum(['labor', 'materials', 'equipment', 'subcontractors', 'permits', 'contingency', 'other']).describe('Budget category (required)'),
1171
+ description: z.string().optional().describe('Description'),
1172
+ estimated_amount: z.number().min(0).optional().describe('Estimated amount'),
1173
+ actual_amount: z.number().min(0).optional().describe('Actual amount'),
1174
+ }, async (params) => {
1175
+ try {
1176
+ const result = await client.create('project-budget-items', buildBody(params));
1177
+ return formatResult(result);
1178
+ }
1179
+ catch (err) {
1180
+ return formatError(err);
1181
+ }
1182
+ });
1183
+ server.tool('update_project_budget_item', 'Update an existing project budget item by ID. Requires project_budget_items:write scope.', {
1184
+ id: z.string().uuid().describe('Project budget item ID'),
1185
+ project_id: z.string().uuid().optional().describe('Project ID'),
1186
+ category: z.enum(['labor', 'materials', 'equipment', 'subcontractors', 'permits', 'contingency', 'other']).optional().describe('Budget category'),
1187
+ description: z.string().optional().describe('Description'),
1188
+ estimated_amount: z.number().min(0).optional().describe('Estimated amount'),
1189
+ actual_amount: z.number().min(0).optional().describe('Actual amount'),
1190
+ }, async ({ id, ...rest }) => {
1191
+ try {
1192
+ const result = await client.update('project-budget-items', id, buildBody(rest));
1193
+ return formatResult(result);
1194
+ }
1195
+ catch (err) {
1196
+ return formatError(err);
1197
+ }
1198
+ });
1199
+ server.tool('delete_project_budget_item', 'Delete a project budget item by ID. Requires project_budget_items:write scope.', { id: z.string().uuid().describe('Project budget item ID') }, async ({ id }) => {
1200
+ try {
1201
+ const result = await client.remove('project-budget-items', id);
1202
+ return formatResult(result);
1203
+ }
1204
+ catch (err) {
1205
+ return formatError(err);
1206
+ }
1207
+ });
1208
+ // ============================================================
1209
+ // 24. Project Time Entries (scope: project_time_entries)
1210
+ // ============================================================
1211
+ server.tool('create_project_time_entry', 'Create a new project time entry. Requires project_time_entries:write scope.', {
1212
+ project_id: z.string().uuid().describe('Project ID (required)'),
1213
+ task_id: z.string().uuid().describe('Task ID (required)'),
1214
+ user_id: z.string().min(1).describe('User ID (required)'),
1215
+ start_time: z.string().describe('Start time (ISO 8601 datetime, required)'),
1216
+ end_time: z.string().optional().describe('End time (ISO 8601 datetime)'),
1217
+ hours: z.number().min(0).optional().describe('Hours worked'),
1218
+ description: z.string().optional().describe('Description'),
1219
+ hourly_rate: z.number().min(0).optional().describe('Hourly rate'),
1220
+ }, async (params) => {
1221
+ try {
1222
+ const result = await client.create('project-time-entries', buildBody(params));
1223
+ return formatResult(result);
1224
+ }
1225
+ catch (err) {
1226
+ return formatError(err);
1227
+ }
1228
+ });
1229
+ server.tool('update_project_time_entry', 'Update an existing project time entry by ID. Requires project_time_entries:write scope.', {
1230
+ id: z.string().uuid().describe('Project time entry ID'),
1231
+ project_id: z.string().uuid().optional().describe('Project ID'),
1232
+ task_id: z.string().uuid().optional().describe('Task ID'),
1233
+ user_id: z.string().optional().describe('User ID'),
1234
+ start_time: z.string().optional().describe('Start time (ISO 8601 datetime)'),
1235
+ end_time: z.string().optional().describe('End time (ISO 8601 datetime)'),
1236
+ hours: z.number().min(0).optional().describe('Hours worked'),
1237
+ description: z.string().optional().describe('Description'),
1238
+ hourly_rate: z.number().min(0).optional().describe('Hourly rate'),
1239
+ }, async ({ id, ...rest }) => {
1240
+ try {
1241
+ const result = await client.update('project-time-entries', id, buildBody(rest));
1242
+ return formatResult(result);
1243
+ }
1244
+ catch (err) {
1245
+ return formatError(err);
1246
+ }
1247
+ });
1248
+ server.tool('delete_project_time_entry', 'Delete a project time entry by ID. Requires project_time_entries:write scope.', { id: z.string().uuid().describe('Project time entry ID') }, async ({ id }) => {
1249
+ try {
1250
+ const result = await client.remove('project-time-entries', id);
1251
+ return formatResult(result);
1252
+ }
1253
+ catch (err) {
1254
+ return formatError(err);
1255
+ }
1256
+ });
1257
+ // ============================================================
1258
+ // 25. Manufacturers (scope: manufacturers)
1259
+ // ============================================================
1260
+ server.tool('create_manufacturer', 'Create a new manufacturer. Requires manufacturers:write scope.', {
1261
+ name: z.string().min(1).max(500).describe('Manufacturer name (required)'),
1262
+ description: z.string().max(2000).optional().describe('Description'),
1263
+ }, async (params) => {
1264
+ try {
1265
+ const result = await client.create('manufacturers', buildBody(params));
1266
+ return formatResult(result);
1267
+ }
1268
+ catch (err) {
1269
+ return formatError(err);
1270
+ }
1271
+ });
1272
+ server.tool('update_manufacturer', 'Update an existing manufacturer by ID. Requires manufacturers:write scope.', {
1273
+ id: z.string().uuid().describe('Manufacturer ID'),
1274
+ name: z.string().min(1).max(500).optional().describe('Manufacturer name'),
1275
+ description: z.string().max(2000).optional().describe('Description'),
1276
+ }, async ({ id, ...rest }) => {
1277
+ try {
1278
+ const result = await client.update('manufacturers', id, buildBody(rest));
1279
+ return formatResult(result);
1280
+ }
1281
+ catch (err) {
1282
+ return formatError(err);
1283
+ }
1284
+ });
1285
+ server.tool('delete_manufacturer', 'Delete a manufacturer by ID. Requires manufacturers:write scope.', { id: z.string().uuid().describe('Manufacturer ID') }, async ({ id }) => {
1286
+ try {
1287
+ const result = await client.remove('manufacturers', id);
1288
+ return formatResult(result);
1289
+ }
1290
+ catch (err) {
1291
+ return formatError(err);
1292
+ }
1293
+ });
1294
+ // ============================================================
1295
+ // 26. Asset Types (scope: asset_types)
1296
+ // ============================================================
1297
+ server.tool('create_asset_type', 'Create a new asset type. Requires asset_types:write scope.', {
1298
+ name: z.string().min(1).max(500).describe('Asset type name (required)'),
1299
+ description: z.string().max(2000).optional().describe('Description'),
1300
+ category: z.string().max(200).optional().describe('Category'),
1301
+ group_id: z.string().uuid().optional().describe('Group ID'),
1302
+ }, async (params) => {
1303
+ try {
1304
+ const result = await client.create('asset-types', buildBody(params));
1305
+ return formatResult(result);
1306
+ }
1307
+ catch (err) {
1308
+ return formatError(err);
1309
+ }
1310
+ });
1311
+ server.tool('update_asset_type', 'Update an existing asset type by ID. Requires asset_types:write scope.', {
1312
+ id: z.string().uuid().describe('Asset type ID'),
1313
+ name: z.string().min(1).max(500).optional().describe('Asset type name'),
1314
+ description: z.string().max(2000).optional().describe('Description'),
1315
+ category: z.string().max(200).optional().describe('Category'),
1316
+ group_id: z.string().uuid().optional().describe('Group ID'),
1317
+ }, async ({ id, ...rest }) => {
1318
+ try {
1319
+ const result = await client.update('asset-types', id, buildBody(rest));
1320
+ return formatResult(result);
1321
+ }
1322
+ catch (err) {
1323
+ return formatError(err);
1324
+ }
1325
+ });
1326
+ server.tool('delete_asset_type', 'Delete an asset type by ID. Requires asset_types:write scope.', { id: z.string().uuid().describe('Asset type ID') }, async ({ id }) => {
1327
+ try {
1328
+ const result = await client.remove('asset-types', id);
1329
+ return formatResult(result);
1330
+ }
1331
+ catch (err) {
1332
+ return formatError(err);
1333
+ }
1334
+ });
1335
+ // ============================================================
1336
+ // 27. Work Categories (scope: work_categories)
1337
+ // ============================================================
1338
+ server.tool('create_work_category', 'Create a new work category. Requires work_categories:write scope.', {
1339
+ name: z.string().min(1).max(500).describe('Work category name (required)'),
1340
+ description: z.string().max(2000).optional().describe('Description'),
1341
+ }, async (params) => {
1342
+ try {
1343
+ const result = await client.create('work-categories', buildBody(params));
1344
+ return formatResult(result);
1345
+ }
1346
+ catch (err) {
1347
+ return formatError(err);
1348
+ }
1349
+ });
1350
+ server.tool('update_work_category', 'Update an existing work category by ID. Requires work_categories:write scope.', {
1351
+ id: z.string().uuid().describe('Work category ID'),
1352
+ name: z.string().min(1).max(500).optional().describe('Work category name'),
1353
+ description: z.string().max(2000).optional().describe('Description'),
1354
+ }, async ({ id, ...rest }) => {
1355
+ try {
1356
+ const result = await client.update('work-categories', id, buildBody(rest));
1357
+ return formatResult(result);
1358
+ }
1359
+ catch (err) {
1360
+ return formatError(err);
1361
+ }
1362
+ });
1363
+ server.tool('delete_work_category', 'Delete a work category by ID. Requires work_categories:write scope.', { id: z.string().uuid().describe('Work category ID') }, async ({ id }) => {
1364
+ try {
1365
+ const result = await client.remove('work-categories', id);
1366
+ return formatResult(result);
1367
+ }
1368
+ catch (err) {
1369
+ return formatError(err);
1370
+ }
1371
+ });
1372
+ // ============================================================
1373
+ // 28. Building Types (scope: building_types)
1374
+ // ============================================================
1375
+ server.tool('create_building_type', 'Create a new building type. Requires building_types:write scope.', {
1376
+ name: z.string().min(1).max(500).describe('Building type name (required)'),
1377
+ description: z.string().max(2000).optional().describe('Description'),
1378
+ }, async (params) => {
1379
+ try {
1380
+ const result = await client.create('building-types', buildBody(params));
1381
+ return formatResult(result);
1382
+ }
1383
+ catch (err) {
1384
+ return formatError(err);
1385
+ }
1386
+ });
1387
+ server.tool('update_building_type', 'Update an existing building type by ID. Requires building_types:write scope.', {
1388
+ id: z.string().uuid().describe('Building type ID'),
1389
+ name: z.string().min(1).max(500).optional().describe('Building type name'),
1390
+ description: z.string().max(2000).optional().describe('Description'),
1391
+ }, async ({ id, ...rest }) => {
1392
+ try {
1393
+ const result = await client.update('building-types', id, buildBody(rest));
1394
+ return formatResult(result);
1395
+ }
1396
+ catch (err) {
1397
+ return formatError(err);
1398
+ }
1399
+ });
1400
+ server.tool('delete_building_type', 'Delete a building type by ID. Requires building_types:write scope.', { id: z.string().uuid().describe('Building type ID') }, async ({ id }) => {
1401
+ try {
1402
+ const result = await client.remove('building-types', id);
1403
+ return formatResult(result);
1404
+ }
1405
+ catch (err) {
1406
+ return formatError(err);
1407
+ }
1408
+ });
1409
+ // ============================================================
1410
+ // 29. Location Types (scope: location_types)
1411
+ // ============================================================
1412
+ server.tool('create_location_type', 'Create a new location type. Requires location_types:write scope.', {
1413
+ name: z.string().min(1).max(500).describe('Location type name (required)'),
1414
+ description: z.string().max(2000).optional().describe('Description'),
1415
+ }, async (params) => {
1416
+ try {
1417
+ const result = await client.create('location-types', buildBody(params));
1418
+ return formatResult(result);
1419
+ }
1420
+ catch (err) {
1421
+ return formatError(err);
1422
+ }
1423
+ });
1424
+ server.tool('update_location_type', 'Update an existing location type by ID. Requires location_types:write scope.', {
1425
+ id: z.string().uuid().describe('Location type ID'),
1426
+ name: z.string().min(1).max(500).optional().describe('Location type name'),
1427
+ description: z.string().max(2000).optional().describe('Description'),
1428
+ }, async ({ id, ...rest }) => {
1429
+ try {
1430
+ const result = await client.update('location-types', id, buildBody(rest));
1431
+ return formatResult(result);
1432
+ }
1433
+ catch (err) {
1434
+ return formatError(err);
1435
+ }
1436
+ });
1437
+ server.tool('delete_location_type', 'Delete a location type by ID. Requires location_types:write scope.', { id: z.string().uuid().describe('Location type ID') }, async ({ id }) => {
1438
+ try {
1439
+ const result = await client.remove('location-types', id);
1440
+ return formatResult(result);
1441
+ }
1442
+ catch (err) {
1443
+ return formatError(err);
1444
+ }
1445
+ });
1446
+ // ============================================================
1447
+ // 30. Cost Categories (scope: cost_categories)
1448
+ // ============================================================
1449
+ server.tool('create_cost_category', 'Create a new cost category. Requires cost_categories:write scope.', {
1450
+ name: z.string().min(1).max(500).describe('Cost category name (required)'),
1451
+ description: z.string().max(2000).optional().describe('Description'),
1452
+ parent_id: z.string().uuid().optional().describe('Parent cost category ID'),
1453
+ is_active: z.boolean().optional().describe('Whether the category is active'),
1454
+ }, async (params) => {
1455
+ try {
1456
+ const result = await client.create('cost-categories', buildBody(params));
1457
+ return formatResult(result);
1458
+ }
1459
+ catch (err) {
1460
+ return formatError(err);
1461
+ }
1462
+ });
1463
+ server.tool('update_cost_category', 'Update an existing cost category by ID. Requires cost_categories:write scope.', {
1464
+ id: z.string().uuid().describe('Cost category ID'),
1465
+ name: z.string().min(1).max(500).optional().describe('Cost category name'),
1466
+ description: z.string().max(2000).optional().describe('Description'),
1467
+ parent_id: z.string().uuid().optional().describe('Parent cost category ID'),
1468
+ is_active: z.boolean().optional().describe('Whether the category is active'),
1469
+ }, async ({ id, ...rest }) => {
1470
+ try {
1471
+ const result = await client.update('cost-categories', id, buildBody(rest));
1472
+ return formatResult(result);
1473
+ }
1474
+ catch (err) {
1475
+ return formatError(err);
1476
+ }
1477
+ });
1478
+ server.tool('delete_cost_category', 'Delete a cost category by ID. Requires cost_categories:write scope.', { id: z.string().uuid().describe('Cost category ID') }, async ({ id }) => {
1479
+ try {
1480
+ const result = await client.remove('cost-categories', id);
1481
+ return formatResult(result);
1482
+ }
1483
+ catch (err) {
1484
+ return formatError(err);
1485
+ }
1486
+ });
1487
+ // ============================================================
1488
+ // 31. Part Categories (scope: part_categories)
1489
+ // ============================================================
1490
+ server.tool('create_part_category', 'Create a new part category. Requires part_categories:write scope.', {
1491
+ name: z.string().min(1).max(500).describe('Part category name (required)'),
1492
+ description: z.string().max(2000).optional().describe('Description'),
1493
+ }, async (params) => {
1494
+ try {
1495
+ const result = await client.create('part-categories', buildBody(params));
1496
+ return formatResult(result);
1497
+ }
1498
+ catch (err) {
1499
+ return formatError(err);
1500
+ }
1501
+ });
1502
+ server.tool('update_part_category', 'Update an existing part category by ID. Requires part_categories:write scope.', {
1503
+ id: z.string().uuid().describe('Part category ID'),
1504
+ name: z.string().min(1).max(500).optional().describe('Part category name'),
1505
+ description: z.string().max(2000).optional().describe('Description'),
1506
+ }, async ({ id, ...rest }) => {
1507
+ try {
1508
+ const result = await client.update('part-categories', id, buildBody(rest));
1509
+ return formatResult(result);
1510
+ }
1511
+ catch (err) {
1512
+ return formatError(err);
1513
+ }
1514
+ });
1515
+ server.tool('delete_part_category', 'Delete a part category by ID. Requires part_categories:write scope.', { id: z.string().uuid().describe('Part category ID') }, async ({ id }) => {
1516
+ try {
1517
+ const result = await client.remove('part-categories', id);
1518
+ return formatResult(result);
1519
+ }
1520
+ catch (err) {
1521
+ return formatError(err);
1522
+ }
1523
+ });
1524
+ // ============================================================
1525
+ // 32. Systems (scope: systems)
1526
+ // ============================================================
1527
+ server.tool('create_system', 'Create a new system. Requires systems:write scope.', {
1528
+ name: z.string().min(1).max(500).describe('System name (required)'),
1529
+ description: z.string().max(2000).optional().describe('Description'),
1530
+ system_group_id: z.string().uuid().optional().describe('System group ID'),
1531
+ crv_multiplier: z.number().min(0).optional().describe('CRV multiplier'),
1532
+ }, async (params) => {
1533
+ try {
1534
+ const result = await client.create('systems', buildBody(params));
1535
+ return formatResult(result);
1536
+ }
1537
+ catch (err) {
1538
+ return formatError(err);
1539
+ }
1540
+ });
1541
+ server.tool('update_system', 'Update an existing system by ID. Requires systems:write scope.', {
1542
+ id: z.string().uuid().describe('System ID'),
1543
+ name: z.string().min(1).max(500).optional().describe('System name'),
1544
+ description: z.string().max(2000).optional().describe('Description'),
1545
+ system_group_id: z.string().uuid().optional().describe('System group ID'),
1546
+ crv_multiplier: z.number().min(0).optional().describe('CRV multiplier'),
1547
+ }, async ({ id, ...rest }) => {
1548
+ try {
1549
+ const result = await client.update('systems', id, buildBody(rest));
1550
+ return formatResult(result);
1551
+ }
1552
+ catch (err) {
1553
+ return formatError(err);
1554
+ }
1555
+ });
1556
+ server.tool('delete_system', 'Delete a system by ID. Requires systems:write scope.', { id: z.string().uuid().describe('System ID') }, async ({ id }) => {
1557
+ try {
1558
+ const result = await client.remove('systems', id);
1559
+ return formatResult(result);
1560
+ }
1561
+ catch (err) {
1562
+ return formatError(err);
1563
+ }
1564
+ });
1565
+ // ============================================================
1566
+ // 33. Vendor-Site Assignments (scope: vendor_site_assignments)
1567
+ // POST + DELETE only — no PATCH/update
1568
+ // ============================================================
1569
+ server.tool('create_vendor_site_assignment', 'Assign a vendor to a site. Requires vendor_site_assignments:write scope.', {
1570
+ vendor_id: z.string().uuid().describe('Vendor ID (required)'),
1571
+ site_id: z.string().uuid().describe('Site ID (required)'),
1572
+ }, async (params) => {
1573
+ try {
1574
+ const result = await client.create('vendor-site-assignments', buildBody(params));
1575
+ return formatResult(result);
1576
+ }
1577
+ catch (err) {
1578
+ return formatError(err);
1579
+ }
1580
+ });
1581
+ server.tool('delete_vendor_site_assignment', 'Remove a vendor-site assignment by ID. Requires vendor_site_assignments:write scope.', { id: z.string().uuid().describe('Vendor-site assignment ID') }, async ({ id }) => {
1582
+ try {
1583
+ const result = await client.remove('vendor-site-assignments', id);
1584
+ return formatResult(result);
1585
+ }
1586
+ catch (err) {
1587
+ return formatError(err);
1588
+ }
1589
+ });
1590
+ // ============================================================
1591
+ // 34. Contract-Sites (scope: contract_sites)
1592
+ // POST + DELETE only — no PATCH/update
1593
+ // ============================================================
1594
+ server.tool('create_contract_site', 'Assign a contract to a site. Requires contract_sites:write scope.', {
1595
+ contract_id: z.string().uuid().describe('Contract ID (required)'),
1596
+ site_id: z.string().uuid().describe('Site ID (required)'),
1597
+ }, async (params) => {
1598
+ try {
1599
+ const result = await client.create('contract-sites', buildBody(params));
1600
+ return formatResult(result);
1601
+ }
1602
+ catch (err) {
1603
+ return formatError(err);
1604
+ }
1605
+ });
1606
+ server.tool('delete_contract_site', 'Remove a contract-site assignment by ID. Requires contract_sites:write scope.', { id: z.string().uuid().describe('Contract-site assignment ID') }, async ({ id }) => {
1607
+ try {
1608
+ const result = await client.remove('contract-sites', id);
1609
+ return formatResult(result);
1610
+ }
1611
+ catch (err) {
1612
+ return formatError(err);
1613
+ }
1614
+ });
1615
+ // ============================================================
1616
+ // 35. Custom Field Definitions (scope: custom_fields)
1617
+ // ============================================================
1618
+ server.tool('create_custom_field_definition', 'Create a new custom field definition. Requires custom_fields:write scope.', {
1619
+ entity_type: z.string().min(1).max(100).describe('Entity type this field applies to (required)'),
1620
+ field_name: z.string().min(1).max(200).describe('Field name / key (required)'),
1621
+ field_type: z.enum(['text', 'number', 'date', 'boolean', 'select']).describe('Field data type (required)'),
1622
+ field_label: z.string().max(200).optional().describe('Display label'),
1623
+ options: z.array(z.string()).optional().describe('Options for select-type fields'),
1624
+ is_required: z.boolean().optional().describe('Whether the field is required'),
1625
+ sort_order: z.number().int().min(0).optional().describe('Sort order'),
1626
+ }, async (params) => {
1627
+ try {
1628
+ const result = await client.create('custom-field-definitions', buildBody(params));
1629
+ return formatResult(result);
1630
+ }
1631
+ catch (err) {
1632
+ return formatError(err);
1633
+ }
1634
+ });
1635
+ server.tool('update_custom_field_definition', 'Update an existing custom field definition by ID. Requires custom_fields:write scope.', {
1636
+ id: z.string().uuid().describe('Custom field definition ID'),
1637
+ entity_type: z.string().min(1).max(100).optional().describe('Entity type'),
1638
+ field_name: z.string().min(1).max(200).optional().describe('Field name / key'),
1639
+ field_type: z.enum(['text', 'number', 'date', 'boolean', 'select']).optional().describe('Field data type'),
1640
+ field_label: z.string().max(200).optional().describe('Display label'),
1641
+ options: z.array(z.string()).optional().describe('Options for select-type fields'),
1642
+ is_required: z.boolean().optional().describe('Whether the field is required'),
1643
+ sort_order: z.number().int().min(0).optional().describe('Sort order'),
1644
+ }, async ({ id, ...rest }) => {
1645
+ try {
1646
+ const result = await client.update('custom-field-definitions', id, buildBody(rest));
1647
+ return formatResult(result);
1648
+ }
1649
+ catch (err) {
1650
+ return formatError(err);
1651
+ }
1652
+ });
1653
+ server.tool('delete_custom_field_definition', 'Delete a custom field definition by ID. Requires custom_fields:write scope.', { id: z.string().uuid().describe('Custom field definition ID') }, async ({ id }) => {
1654
+ try {
1655
+ const result = await client.remove('custom-field-definitions', id);
1656
+ return formatResult(result);
1657
+ }
1658
+ catch (err) {
1659
+ return formatError(err);
1660
+ }
1661
+ });
1662
+ // ============================================================
1663
+ // 36. Custom Field Values (scope: custom_fields)
1664
+ // ============================================================
1665
+ server.tool('create_custom_field_value', 'Create a new custom field value. Requires custom_fields:write scope.', {
1666
+ entity_id: z.string().uuid().describe('Entity ID (required)'),
1667
+ field_definition_id: z.string().uuid().describe('Custom field definition ID (required)'),
1668
+ value: z.string().max(5000).optional().describe('Field value'),
1669
+ }, async (params) => {
1670
+ try {
1671
+ const result = await client.create('custom-field-values', buildBody(params));
1672
+ return formatResult(result);
1673
+ }
1674
+ catch (err) {
1675
+ return formatError(err);
1676
+ }
1677
+ });
1678
+ server.tool('update_custom_field_value', 'Update an existing custom field value by ID. Requires custom_fields:write scope.', {
1679
+ id: z.string().uuid().describe('Custom field value ID'),
1680
+ entity_id: z.string().uuid().optional().describe('Entity ID'),
1681
+ field_definition_id: z.string().uuid().optional().describe('Custom field definition ID'),
1682
+ value: z.string().max(5000).optional().describe('Field value'),
1683
+ }, async ({ id, ...rest }) => {
1684
+ try {
1685
+ const result = await client.update('custom-field-values', id, buildBody(rest));
1686
+ return formatResult(result);
1687
+ }
1688
+ catch (err) {
1689
+ return formatError(err);
1690
+ }
1691
+ });
1692
+ server.tool('delete_custom_field_value', 'Delete a custom field value by ID. Requires custom_fields:write scope.', { id: z.string().uuid().describe('Custom field value ID') }, async ({ id }) => {
1693
+ try {
1694
+ const result = await client.remove('custom-field-values', id);
1695
+ return formatResult(result);
1696
+ }
1697
+ catch (err) {
1698
+ return formatError(err);
1699
+ }
1700
+ });
1701
+ // ============================================================
1702
+ // Parts (scope: parts) — moved from tools.ts
1703
+ // ============================================================
1704
+ server.tool('create_part', 'Create a new part/inventory item. Requires parts:write scope.', {
1705
+ name: z.string().min(1).max(500).describe('Part name (required)'),
1706
+ part_number: z.string().max(200).optional().describe('Part number / SKU'),
1707
+ category: z.string().max(200).optional().describe('Category label'),
1708
+ supplier: z.string().max(500).optional().describe('Supplier name'),
1709
+ cost: z.number().min(0).optional().describe('Unit cost'),
1710
+ quantity: z.number().int().min(0).max(10_000_000).optional().describe('Current stock quantity'),
1711
+ desired_quantity: z.number().int().min(0).max(10_000_000).optional().describe('Target / reorder quantity'),
1712
+ specific_location: z.string().max(500).optional().describe('Storage location description'),
1713
+ site_id: z.string().uuid().optional().describe('Site ID'),
1714
+ building_id: z.string().uuid().optional().describe('Building ID'),
1715
+ location_id: z.string().uuid().optional().describe('Location ID'),
1716
+ }, async (params) => {
1717
+ try {
1718
+ const result = await client.create('parts', buildBody(params));
1719
+ return formatResult(result);
1720
+ }
1721
+ catch (err) {
1722
+ return formatError(err);
1723
+ }
1724
+ });
1725
+ server.tool('update_part', 'Update an existing part/inventory item by ID. Requires parts:write scope.', {
1726
+ id: z.string().uuid().describe('Part ID'),
1727
+ name: z.string().min(1).max(500).optional().describe('Part name'),
1728
+ part_number: z.string().max(200).optional().describe('Part number / SKU'),
1729
+ category: z.string().max(200).optional().describe('Category label'),
1730
+ supplier: z.string().max(500).optional().describe('Supplier name'),
1731
+ cost: z.number().min(0).optional().describe('Unit cost'),
1732
+ quantity: z.number().int().min(0).max(10_000_000).optional().describe('Current stock quantity'),
1733
+ desired_quantity: z.number().int().min(0).max(10_000_000).optional().describe('Target / reorder quantity'),
1734
+ specific_location: z.string().max(500).optional().describe('Storage location description'),
1735
+ site_id: z.string().uuid().optional().describe('Site ID'),
1736
+ building_id: z.string().uuid().optional().describe('Building ID'),
1737
+ location_id: z.string().uuid().optional().describe('Location ID'),
1738
+ }, async ({ id, ...rest }) => {
1739
+ try {
1740
+ const result = await client.update('parts', id, buildBody(rest));
1741
+ return formatResult(result);
1742
+ }
1743
+ catch (err) {
1744
+ return formatError(err);
1745
+ }
1746
+ });
1747
+ server.tool('delete_part', 'Delete a part/inventory item by ID. Requires parts:write scope.', { id: z.string().uuid().describe('Part ID') }, async ({ id }) => {
1748
+ try {
1749
+ const result = await client.remove('parts', id);
1750
+ return formatResult(result);
1751
+ }
1752
+ catch (err) {
1753
+ return formatError(err);
1754
+ }
1755
+ });
1756
+ }
1757
+ //# sourceMappingURL=tools-write.js.map