@assetlab/mcp-server 1.0.3 → 1.1.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/dist/tools.js CHANGED
@@ -5,10 +5,11 @@
5
5
  * on the AssetLab API Gateway.
6
6
  */
7
7
  import { z } from 'zod';
8
- // Shared schema fragments
8
+ // Shared schema fragments — pagination is handled automatically via listAll()
9
+ // but still exposed for direct API users who want manual control
9
10
  const paginationSchema = {
10
- page: z.number().int().min(1).optional().describe('Page number (default: 1)'),
11
- per_page: z.number().int().min(1).max(100).optional().describe('Items per page (default: 25, max: 100)'),
11
+ page: z.number().int().min(1).optional().describe('Page number (default: all pages fetched automatically)'),
12
+ per_page: z.number().int().min(1).max(1000).optional().describe('Items per page (default: 1000, max: 1000). All pages are fetched automatically.'),
12
13
  };
13
14
  const searchSchema = {
14
15
  search: z.string().max(200).optional().describe('Search by name'),
@@ -21,6 +22,17 @@ function formatError(err) {
21
22
  const message = err instanceof Error ? err.message : String(err);
22
23
  return { content: [{ type: 'text', text: `Error: ${message}` }], isError: true };
23
24
  }
25
+ /**
26
+ * Smart list: if user explicitly passes page/per_page, use single-page list().
27
+ * Otherwise, auto-paginate with listAll() to return complete data.
28
+ */
29
+ async function smartList(client, resource, params) {
30
+ const { page, per_page, ...filters } = params;
31
+ if (page !== undefined || per_page !== undefined) {
32
+ return client.list(resource, params);
33
+ }
34
+ return client.listAll(resource, filters);
35
+ }
24
36
  export function registerTools(server, client) {
25
37
  // ============================================================
26
38
  // Assets
@@ -33,7 +45,7 @@ export function registerTools(server, client) {
33
45
  system_id: z.string().uuid().optional().describe('Filter by system ID'),
34
46
  }, async (params) => {
35
47
  try {
36
- const result = await client.list('assets', params);
48
+ const result = await smartList(client, 'assets', params);
37
49
  return formatResult(result);
38
50
  }
39
51
  catch (err) {
@@ -61,7 +73,7 @@ export function registerTools(server, client) {
61
73
  site_id: z.string().uuid().optional().describe('Filter by site ID'),
62
74
  }, async (params) => {
63
75
  try {
64
- const result = await client.list('work-orders', params);
76
+ const result = await smartList(client, 'work-orders', params);
65
77
  return formatResult(result);
66
78
  }
67
79
  catch (err) {
@@ -86,7 +98,7 @@ export function registerTools(server, client) {
86
98
  city: z.string().max(100).optional().describe('Filter by city name'),
87
99
  }, async (params) => {
88
100
  try {
89
- const result = await client.list('sites', params);
101
+ const result = await smartList(client, 'sites', params);
90
102
  return formatResult(result);
91
103
  }
92
104
  catch (err) {
@@ -111,7 +123,7 @@ export function registerTools(server, client) {
111
123
  site_id: z.string().uuid().optional().describe('Filter by site ID'),
112
124
  }, async (params) => {
113
125
  try {
114
- const result = await client.list('buildings', params);
126
+ const result = await smartList(client, 'buildings', params);
115
127
  return formatResult(result);
116
128
  }
117
129
  catch (err) {
@@ -127,7 +139,7 @@ export function registerTools(server, client) {
127
139
  building_id: z.string().uuid().optional().describe('Filter by building ID'),
128
140
  }, async (params) => {
129
141
  try {
130
- const result = await client.list('locations', params);
142
+ const result = await smartList(client, 'locations', params);
131
143
  return formatResult(result);
132
144
  }
133
145
  catch (err) {
@@ -143,7 +155,7 @@ export function registerTools(server, client) {
143
155
  system_group_id: z.string().uuid().optional().describe('Filter by system group ID'),
144
156
  }, async (params) => {
145
157
  try {
146
- const result = await client.list('systems', params);
158
+ const result = await smartList(client, 'systems', params);
147
159
  return formatResult(result);
148
160
  }
149
161
  catch (err) {
@@ -156,7 +168,7 @@ export function registerTools(server, client) {
156
168
  system_class_id: z.string().uuid().optional().describe('Filter by system class ID'),
157
169
  }, async (params) => {
158
170
  try {
159
- const result = await client.list('system-groups', params);
171
+ const result = await smartList(client, 'system-groups', params);
160
172
  return formatResult(result);
161
173
  }
162
174
  catch (err) {
@@ -168,7 +180,7 @@ export function registerTools(server, client) {
168
180
  ...paginationSchema,
169
181
  }, async (params) => {
170
182
  try {
171
- const result = await client.list('system-classes', params);
183
+ const result = await smartList(client, 'system-classes', params);
172
184
  return formatResult(result);
173
185
  }
174
186
  catch (err) {
@@ -186,7 +198,7 @@ export function registerTools(server, client) {
186
198
  frequency: z.string().optional().describe('Filter by frequency: DAILY, WEEKLY, MONTHLY, QUARTERLY, SEMI_ANNUAL, ANNUAL, FIVE_YEARLY, CUSTOM'),
187
199
  }, async (params) => {
188
200
  try {
189
- const result = await client.list('pm-schedules', params);
201
+ const result = await smartList(client, 'pm-schedules', params);
190
202
  return formatResult(result);
191
203
  }
192
204
  catch (err) {
@@ -210,7 +222,7 @@ export function registerTools(server, client) {
210
222
  ...paginationSchema,
211
223
  }, async (params) => {
212
224
  try {
213
- const result = await client.list('pm-templates', params);
225
+ const result = await smartList(client, 'pm-templates', params);
214
226
  return formatResult(result);
215
227
  }
216
228
  catch (err) {
@@ -227,7 +239,7 @@ export function registerTools(server, client) {
227
239
  health_status: z.string().optional().describe('Filter by health: on_track, at_risk, delayed, critical'),
228
240
  }, async (params) => {
229
241
  try {
230
- const result = await client.list('projects', params);
242
+ const result = await smartList(client, 'projects', params);
231
243
  return formatResult(result);
232
244
  }
233
245
  catch (err) {
@@ -252,7 +264,7 @@ export function registerTools(server, client) {
252
264
  category: z.string().max(100).optional().describe('Filter by contract category'),
253
265
  }, async (params) => {
254
266
  try {
255
- const result = await client.list('contracts', params);
267
+ const result = await smartList(client, 'contracts', params);
256
268
  return formatResult(result);
257
269
  }
258
270
  catch (err) {
@@ -269,7 +281,7 @@ export function registerTools(server, client) {
269
281
  system_id: z.string().uuid().optional().describe('Filter by system ID'),
270
282
  }, async (params) => {
271
283
  try {
272
- const result = await client.list('compliance', params);
284
+ const result = await smartList(client, 'compliance', params);
273
285
  return formatResult(result);
274
286
  }
275
287
  catch (err) {
@@ -286,6 +298,298 @@ export function registerTools(server, client) {
286
298
  }
287
299
  });
288
300
  // ============================================================
301
+ // Parts
302
+ // ============================================================
303
+ server.tool('list_parts', 'List parts/inventory items. Filter by site or category.', {
304
+ ...searchSchema,
305
+ ...paginationSchema,
306
+ site_id: z.string().uuid().optional().describe('Filter by site ID'),
307
+ category: z.string().max(100).optional().describe('Filter by category (partial match)'),
308
+ }, async (params) => {
309
+ try {
310
+ const result = await smartList(client, 'parts', params);
311
+ return formatResult(result);
312
+ }
313
+ catch (err) {
314
+ return formatError(err);
315
+ }
316
+ });
317
+ server.tool('get_part', 'Get detailed information about a specific part including location and stock levels.', { id: uuidParam.describe('Part ID') }, async ({ id }) => {
318
+ try {
319
+ const result = await client.getOne('parts', id);
320
+ return formatResult(result);
321
+ }
322
+ catch (err) {
323
+ return formatError(err);
324
+ }
325
+ });
326
+ // ============================================================
327
+ // Vendors
328
+ // ============================================================
329
+ server.tool('list_vendors', 'List vendors. Filter by status, category, or city.', {
330
+ ...searchSchema,
331
+ ...paginationSchema,
332
+ status: z.string().optional().describe('Filter by vendor status'),
333
+ category: z.string().max(100).optional().describe('Filter by category (partial match)'),
334
+ city: z.string().max(100).optional().describe('Filter by city (partial match)'),
335
+ }, async (params) => {
336
+ try {
337
+ const result = await smartList(client, 'vendors', params);
338
+ return formatResult(result);
339
+ }
340
+ catch (err) {
341
+ return formatError(err);
342
+ }
343
+ });
344
+ server.tool('get_vendor', 'Get detailed vendor information including contact details, address, and website.', { id: uuidParam.describe('Vendor ID') }, async ({ id }) => {
345
+ try {
346
+ const result = await client.getOne('vendors', id);
347
+ return formatResult(result);
348
+ }
349
+ catch (err) {
350
+ return formatError(err);
351
+ }
352
+ });
353
+ // ============================================================
354
+ // Work Requests
355
+ // ============================================================
356
+ server.tool('list_work_requests', 'List work requests (submitted by requesters). Filter by status (SUBMITTED, APPROVED, REJECTED, CONVERTED) or priority.', {
357
+ ...searchSchema,
358
+ ...paginationSchema,
359
+ status: z.string().optional().describe('Filter by status: SUBMITTED, APPROVED, REJECTED, CONVERTED'),
360
+ priority: z.string().optional().describe('Filter by priority: LOW, MEDIUM, HIGH, CRITICAL'),
361
+ site_id: z.string().uuid().optional().describe('Filter by site ID'),
362
+ }, async (params) => {
363
+ try {
364
+ const result = await smartList(client, 'work-requests', params);
365
+ return formatResult(result);
366
+ }
367
+ catch (err) {
368
+ return formatError(err);
369
+ }
370
+ });
371
+ server.tool('get_work_request', 'Get detailed work request information including description, attachments, and processing status.', { id: uuidParam.describe('Work request ID') }, async ({ id }) => {
372
+ try {
373
+ const result = await client.getOne('work-requests', id);
374
+ return formatResult(result);
375
+ }
376
+ catch (err) {
377
+ return formatError(err);
378
+ }
379
+ });
380
+ // ============================================================
381
+ // Invoices
382
+ // ============================================================
383
+ server.tool('list_invoices', 'List invoices. Filter by status (pending, approved, paid, voided), vendor, or project.', {
384
+ ...searchSchema,
385
+ ...paginationSchema,
386
+ status: z.string().optional().describe('Filter by status: pending, approved, paid, voided'),
387
+ vendor_id: z.string().uuid().optional().describe('Filter by vendor ID'),
388
+ project_id: z.string().uuid().optional().describe('Filter by project ID'),
389
+ }, async (params) => {
390
+ try {
391
+ const result = await smartList(client, 'invoices', params);
392
+ return formatResult(result);
393
+ }
394
+ catch (err) {
395
+ return formatError(err);
396
+ }
397
+ });
398
+ server.tool('get_invoice', 'Get detailed invoice information including amounts, dates, linked vendor, project, and purchase order.', { id: uuidParam.describe('Invoice ID') }, async ({ id }) => {
399
+ try {
400
+ const result = await client.getOne('invoices', id);
401
+ return formatResult(result);
402
+ }
403
+ catch (err) {
404
+ return formatError(err);
405
+ }
406
+ });
407
+ // ============================================================
408
+ // Purchase Orders
409
+ // ============================================================
410
+ server.tool('list_purchase_orders', 'List purchase orders. Filter by status (draft, issued, partially_received, received, closed, cancelled), vendor, or project.', {
411
+ ...searchSchema,
412
+ ...paginationSchema,
413
+ status: z.string().optional().describe('Filter by status: draft, issued, partially_received, received, closed, cancelled'),
414
+ vendor_id: z.string().uuid().optional().describe('Filter by vendor ID'),
415
+ project_id: z.string().uuid().optional().describe('Filter by project ID'),
416
+ }, async (params) => {
417
+ try {
418
+ const result = await smartList(client, 'purchase-orders', params);
419
+ return formatResult(result);
420
+ }
421
+ catch (err) {
422
+ return formatError(err);
423
+ }
424
+ });
425
+ server.tool('get_purchase_order', 'Get detailed purchase order information including amount, status, vendor, and linked project.', { id: uuidParam.describe('Purchase order ID') }, async ({ id }) => {
426
+ try {
427
+ const result = await client.getOne('purchase-orders', id);
428
+ return formatResult(result);
429
+ }
430
+ catch (err) {
431
+ return formatError(err);
432
+ }
433
+ });
434
+ // ============================================================
435
+ // Expenses
436
+ // ============================================================
437
+ server.tool('list_expenses', 'List expenses. Filter by project, work order, or cost category.', {
438
+ ...searchSchema,
439
+ ...paginationSchema,
440
+ project_id: z.string().uuid().optional().describe('Filter by project ID'),
441
+ work_order_id: z.string().uuid().optional().describe('Filter by work order ID'),
442
+ category_id: z.string().uuid().optional().describe('Filter by cost category ID'),
443
+ }, async (params) => {
444
+ try {
445
+ const result = await smartList(client, 'expenses', params);
446
+ return formatResult(result);
447
+ }
448
+ catch (err) {
449
+ return formatError(err);
450
+ }
451
+ });
452
+ server.tool('get_expense', 'Get detailed expense information including amount, date, receipt, and linked project or work order.', { id: uuidParam.describe('Expense ID') }, async ({ id }) => {
453
+ try {
454
+ const result = await client.getOne('expenses', id);
455
+ return formatResult(result);
456
+ }
457
+ catch (err) {
458
+ return formatError(err);
459
+ }
460
+ });
461
+ // ============================================================
462
+ // Asset Types
463
+ // ============================================================
464
+ server.tool('list_asset_types', 'List asset type classifications. Filter by category or group.', {
465
+ ...searchSchema,
466
+ ...paginationSchema,
467
+ category: z.string().max(100).optional().describe('Filter by category (partial match)'),
468
+ group_id: z.string().uuid().optional().describe('Filter by asset type group ID'),
469
+ }, async (params) => {
470
+ try {
471
+ const result = await smartList(client, 'asset-types', params);
472
+ return formatResult(result);
473
+ }
474
+ catch (err) {
475
+ return formatError(err);
476
+ }
477
+ });
478
+ // ============================================================
479
+ // Work Categories
480
+ // ============================================================
481
+ server.tool('list_work_categories', 'List work categories used to classify work orders and requests.', {
482
+ ...searchSchema,
483
+ ...paginationSchema,
484
+ }, async (params) => {
485
+ try {
486
+ const result = await smartList(client, 'work-categories', params);
487
+ return formatResult(result);
488
+ }
489
+ catch (err) {
490
+ return formatError(err);
491
+ }
492
+ });
493
+ // ============================================================
494
+ // Manufacturers
495
+ // ============================================================
496
+ server.tool('list_manufacturers', 'List manufacturers of equipment and assets.', {
497
+ ...searchSchema,
498
+ ...paginationSchema,
499
+ }, async (params) => {
500
+ try {
501
+ const result = await smartList(client, 'manufacturers', params);
502
+ return formatResult(result);
503
+ }
504
+ catch (err) {
505
+ return formatError(err);
506
+ }
507
+ });
508
+ server.tool('get_manufacturer', 'Get detailed manufacturer information including contact details and associated system classes.', { id: uuidParam.describe('Manufacturer ID') }, async ({ id }) => {
509
+ try {
510
+ const result = await client.getOne('manufacturers', id);
511
+ return formatResult(result);
512
+ }
513
+ catch (err) {
514
+ return formatError(err);
515
+ }
516
+ });
517
+ // ============================================================
518
+ // Building Types
519
+ // ============================================================
520
+ server.tool('list_building_types', 'List building type classifications.', {
521
+ ...searchSchema,
522
+ ...paginationSchema,
523
+ }, async (params) => {
524
+ try {
525
+ const result = await smartList(client, 'building-types', params);
526
+ return formatResult(result);
527
+ }
528
+ catch (err) {
529
+ return formatError(err);
530
+ }
531
+ });
532
+ // ============================================================
533
+ // Location Types
534
+ // ============================================================
535
+ server.tool('list_location_types', 'List location type classifications (e.g., room types, floor types).', {
536
+ ...searchSchema,
537
+ ...paginationSchema,
538
+ }, async (params) => {
539
+ try {
540
+ const result = await smartList(client, 'location-types', params);
541
+ return formatResult(result);
542
+ }
543
+ catch (err) {
544
+ return formatError(err);
545
+ }
546
+ });
547
+ // ============================================================
548
+ // Cost Categories
549
+ // ============================================================
550
+ server.tool('list_cost_categories', 'List cost categories used to classify expenses, invoices, and purchase orders. Supports hierarchical parent-child structure.', {
551
+ ...searchSchema,
552
+ ...paginationSchema,
553
+ is_active: z.enum(['true', 'false']).optional().describe('Filter by active status'),
554
+ parent_id: z.string().uuid().optional().describe('Filter by parent category ID'),
555
+ }, async (params) => {
556
+ try {
557
+ const result = await smartList(client, 'cost-categories', params);
558
+ return formatResult(result);
559
+ }
560
+ catch (err) {
561
+ return formatError(err);
562
+ }
563
+ });
564
+ // ============================================================
565
+ // Budgets (annual_funding_budgets)
566
+ // ============================================================
567
+ server.tool('list_budgets', 'List annual funding budgets. Filter by year, site, building, or funding source. Includes allocated, budgeted, and remaining amounts.', {
568
+ ...searchSchema,
569
+ ...paginationSchema,
570
+ year: z.number().int().optional().describe('Filter by budget year (e.g. 2026)'),
571
+ site_id: z.string().uuid().optional().describe('Filter by site ID'),
572
+ building_id: z.string().uuid().optional().describe('Filter by building ID'),
573
+ funding_source: z.number().int().optional().describe('Filter by funding source'),
574
+ }, async (params) => {
575
+ try {
576
+ const result = await smartList(client, 'budgets', params);
577
+ return formatResult(result);
578
+ }
579
+ catch (err) {
580
+ return formatError(err);
581
+ }
582
+ });
583
+ server.tool('get_budget', 'Get a single annual funding budget by ID. Use list_sites/list_buildings to resolve site_id/building_id.', { id: uuidParam.describe('Budget ID') }, async ({ id }) => {
584
+ try {
585
+ const result = await client.getOne('budgets', id);
586
+ return formatResult(result);
587
+ }
588
+ catch (err) {
589
+ return formatError(err);
590
+ }
591
+ });
592
+ // ============================================================
289
593
  // Dashboard
290
594
  // ============================================================
291
595
  server.tool('get_dashboard_summary', 'Get aggregated dashboard statistics: total assets, work orders by status, overdue count, active PM schedules, sites, and buildings.', {}, async () => {
@@ -297,5 +601,475 @@ export function registerTools(server, client) {
297
601
  return formatError(err);
298
602
  }
299
603
  });
604
+ // ============================================================
605
+ // Asset Comments
606
+ // ============================================================
607
+ server.tool('list_asset_comments', 'List comments on assets. Filter by asset_id to get comments for a specific asset.', {
608
+ ...paginationSchema,
609
+ asset_id: z.string().uuid().optional().describe('Filter by asset ID'),
610
+ }, async (params) => {
611
+ try {
612
+ const result = await smartList(client, 'asset-comments', params);
613
+ return formatResult(result);
614
+ }
615
+ catch (err) {
616
+ return formatError(err);
617
+ }
618
+ });
619
+ server.tool('get_asset_comment', 'Get a single asset comment by ID.', { id: uuidParam.describe('Asset comment ID') }, async ({ id }) => {
620
+ try {
621
+ const result = await client.getOne('asset-comments', id);
622
+ return formatResult(result);
623
+ }
624
+ catch (err) {
625
+ return formatError(err);
626
+ }
627
+ });
628
+ // ============================================================
629
+ // Asset Costs
630
+ // ============================================================
631
+ server.tool('list_asset_costs', 'List asset cost records (repairs, PM, operations, replacements, decommissions). Filter by asset, site, category, or work order.', {
632
+ ...paginationSchema,
633
+ asset_id: z.string().uuid().optional().describe('Filter by asset ID'),
634
+ site_id: z.string().uuid().optional().describe('Filter by site ID'),
635
+ category: z.enum(['Repair', 'PM', 'Operation', 'Replacement', 'Decommission']).optional().describe('Filter by cost category'),
636
+ work_order_id: z.string().uuid().optional().describe('Filter by work order ID'),
637
+ }, async (params) => {
638
+ try {
639
+ const result = await smartList(client, 'asset-costs', params);
640
+ return formatResult(result);
641
+ }
642
+ catch (err) {
643
+ return formatError(err);
644
+ }
645
+ });
646
+ server.tool('get_asset_cost', 'Get a single asset cost record by ID, including related asset, site, and building names.', { id: uuidParam.describe('Asset cost ID') }, async ({ id }) => {
647
+ try {
648
+ const result = await client.getOne('asset-costs', id);
649
+ return formatResult(result);
650
+ }
651
+ catch (err) {
652
+ return formatError(err);
653
+ }
654
+ });
655
+ // ============================================================
656
+ // Asset Replacement Plans
657
+ // ============================================================
658
+ server.tool('list_asset_replacement_plans', 'List asset replacement plans for lifecycle/capital planning. Filter by asset, status, priority, or planned year.', {
659
+ ...paginationSchema,
660
+ asset_id: z.string().uuid().optional().describe('Filter by asset ID'),
661
+ status: z.enum(['PLANNED', 'BUDGETED', 'APPROVED', 'COMPLETED', 'CANCELLED']).optional().describe('Filter by plan status'),
662
+ priority: z.enum(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']).optional().describe('Filter by priority'),
663
+ year: z.number().int().optional().describe('Filter by planned replacement year'),
664
+ }, async (params) => {
665
+ try {
666
+ const result = await smartList(client, 'asset-replacement-plans', params);
667
+ return formatResult(result);
668
+ }
669
+ catch (err) {
670
+ return formatError(err);
671
+ }
672
+ });
673
+ server.tool('get_asset_replacement_plan', 'Get a single asset replacement plan by ID.', { id: uuidParam.describe('Replacement plan ID') }, async ({ id }) => {
674
+ try {
675
+ const result = await client.getOne('asset-replacement-plans', id);
676
+ return formatResult(result);
677
+ }
678
+ catch (err) {
679
+ return formatError(err);
680
+ }
681
+ });
682
+ // ============================================================
683
+ // Asset Risk History
684
+ // ============================================================
685
+ server.tool('list_asset_risk_history', 'List asset risk assessment history. Shows risk scores, condition scores, and trigger events over time.', {
686
+ ...paginationSchema,
687
+ asset_id: z.string().uuid().optional().describe('Filter by asset ID'),
688
+ trigger_event: z.enum(['maintenance', 'inspection', 'manual_update', 'scheduled']).optional().describe('Filter by trigger event type'),
689
+ }, async (params) => {
690
+ try {
691
+ const result = await smartList(client, 'asset-risk-history', params);
692
+ return formatResult(result);
693
+ }
694
+ catch (err) {
695
+ return formatError(err);
696
+ }
697
+ });
698
+ server.tool('get_asset_risk_history_entry', 'Get a single asset risk history entry by ID.', { id: uuidParam.describe('Risk history entry ID') }, async ({ id }) => {
699
+ try {
700
+ const result = await client.getOne('asset-risk-history', id);
701
+ return formatResult(result);
702
+ }
703
+ catch (err) {
704
+ return formatError(err);
705
+ }
706
+ });
707
+ // ============================================================
708
+ // Work Order Comments
709
+ // ============================================================
710
+ server.tool('list_work_order_comments', 'List comments on work orders. Filter by work_order_id to get comments for a specific work order.', {
711
+ ...paginationSchema,
712
+ work_order_id: z.string().uuid().optional().describe('Filter by work order ID'),
713
+ }, async (params) => {
714
+ try {
715
+ const result = await smartList(client, 'work-order-comments', params);
716
+ return formatResult(result);
717
+ }
718
+ catch (err) {
719
+ return formatError(err);
720
+ }
721
+ });
722
+ server.tool('get_work_order_comment', 'Get a single work order comment by ID.', { id: uuidParam.describe('Work order comment ID') }, async ({ id }) => {
723
+ try {
724
+ const result = await client.getOne('work-order-comments', id);
725
+ return formatResult(result);
726
+ }
727
+ catch (err) {
728
+ return formatError(err);
729
+ }
730
+ });
731
+ // ============================================================
732
+ // Project Tasks
733
+ // ============================================================
734
+ server.tool('list_project_tasks', 'List project tasks (work breakdown structure). Filter by project, phase, status, or priority.', {
735
+ ...searchSchema,
736
+ ...paginationSchema,
737
+ project_id: z.string().uuid().optional().describe('Filter by project ID'),
738
+ phase_id: z.string().uuid().optional().describe('Filter by phase ID'),
739
+ status: z.enum(['todo', 'in_progress', 'completed', 'blocked', 'cancelled']).optional().describe('Filter by task status'),
740
+ priority: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Filter by priority'),
741
+ }, async (params) => {
742
+ try {
743
+ const result = await smartList(client, 'project-tasks', params);
744
+ return formatResult(result);
745
+ }
746
+ catch (err) {
747
+ return formatError(err);
748
+ }
749
+ });
750
+ server.tool('get_project_task', 'Get a single project task by ID, including cost and hour tracking.', { id: uuidParam.describe('Project task ID') }, async ({ id }) => {
751
+ try {
752
+ const result = await client.getOne('project-tasks', id);
753
+ return formatResult(result);
754
+ }
755
+ catch (err) {
756
+ return formatError(err);
757
+ }
758
+ });
759
+ // ============================================================
760
+ // Project Milestones
761
+ // ============================================================
762
+ server.tool('list_project_milestones', 'List project milestones. Filter by project or status (pending, completed, missed, at_risk).', {
763
+ ...searchSchema,
764
+ ...paginationSchema,
765
+ project_id: z.string().uuid().optional().describe('Filter by project ID'),
766
+ status: z.enum(['pending', 'completed', 'missed', 'at_risk']).optional().describe('Filter by milestone status'),
767
+ }, async (params) => {
768
+ try {
769
+ const result = await smartList(client, 'project-milestones', params);
770
+ return formatResult(result);
771
+ }
772
+ catch (err) {
773
+ return formatError(err);
774
+ }
775
+ });
776
+ server.tool('get_project_milestone', 'Get a single project milestone by ID.', { id: uuidParam.describe('Project milestone ID') }, async ({ id }) => {
777
+ try {
778
+ const result = await client.getOne('project-milestones', id);
779
+ return formatResult(result);
780
+ }
781
+ catch (err) {
782
+ return formatError(err);
783
+ }
784
+ });
785
+ // ============================================================
786
+ // Project Phases
787
+ // ============================================================
788
+ server.tool('list_project_phases', 'List project phases. Filter by project or status (pending, in_progress, completed, skipped).', {
789
+ ...paginationSchema,
790
+ project_id: z.string().uuid().optional().describe('Filter by project ID'),
791
+ status: z.enum(['pending', 'in_progress', 'completed', 'skipped']).optional().describe('Filter by phase status'),
792
+ }, async (params) => {
793
+ try {
794
+ const result = await smartList(client, 'project-phases', params);
795
+ return formatResult(result);
796
+ }
797
+ catch (err) {
798
+ return formatError(err);
799
+ }
800
+ });
801
+ server.tool('get_project_phase', 'Get a single project phase by ID.', { id: uuidParam.describe('Project phase ID') }, async ({ id }) => {
802
+ try {
803
+ const result = await client.getOne('project-phases', id);
804
+ return formatResult(result);
805
+ }
806
+ catch (err) {
807
+ return formatError(err);
808
+ }
809
+ });
810
+ // ============================================================
811
+ // Project Budget Items
812
+ // ============================================================
813
+ server.tool('list_project_budget_items', 'List project budget line items (labor, materials, equipment, subcontractors, permits, contingency, other).', {
814
+ ...paginationSchema,
815
+ project_id: z.string().uuid().optional().describe('Filter by project ID'),
816
+ category: z.enum(['labor', 'materials', 'equipment', 'subcontractors', 'permits', 'contingency', 'other']).optional().describe('Filter by budget category'),
817
+ }, async (params) => {
818
+ try {
819
+ const result = await smartList(client, 'project-budget-items', params);
820
+ return formatResult(result);
821
+ }
822
+ catch (err) {
823
+ return formatError(err);
824
+ }
825
+ });
826
+ server.tool('get_project_budget_item', 'Get a single project budget item by ID.', { id: uuidParam.describe('Budget item ID') }, async ({ id }) => {
827
+ try {
828
+ const result = await client.getOne('project-budget-items', id);
829
+ return formatResult(result);
830
+ }
831
+ catch (err) {
832
+ return formatError(err);
833
+ }
834
+ });
835
+ // ============================================================
836
+ // Project Time Entries
837
+ // ============================================================
838
+ server.tool('list_project_time_entries', 'List project time entries for labor tracking. Filter by project, task, or user.', {
839
+ ...paginationSchema,
840
+ project_id: z.string().uuid().optional().describe('Filter by project ID'),
841
+ task_id: z.string().uuid().optional().describe('Filter by task ID'),
842
+ user_id: z.string().optional().describe('Filter by user ID'),
843
+ }, async (params) => {
844
+ try {
845
+ const result = await smartList(client, 'project-time-entries', params);
846
+ return formatResult(result);
847
+ }
848
+ catch (err) {
849
+ return formatError(err);
850
+ }
851
+ });
852
+ server.tool('get_project_time_entry', 'Get a single project time entry by ID.', { id: uuidParam.describe('Time entry ID') }, async ({ id }) => {
853
+ try {
854
+ const result = await client.getOne('project-time-entries', id);
855
+ return formatResult(result);
856
+ }
857
+ catch (err) {
858
+ return formatError(err);
859
+ }
860
+ });
861
+ // ============================================================
862
+ // Project Comments
863
+ // ============================================================
864
+ server.tool('list_project_comments', 'List comments on projects. Supports threaded replies via parent_id.', {
865
+ ...paginationSchema,
866
+ project_id: z.string().uuid().optional().describe('Filter by project ID'),
867
+ }, async (params) => {
868
+ try {
869
+ const result = await smartList(client, 'project-comments', params);
870
+ return formatResult(result);
871
+ }
872
+ catch (err) {
873
+ return formatError(err);
874
+ }
875
+ });
876
+ server.tool('get_project_comment', 'Get a single project comment by ID.', { id: uuidParam.describe('Project comment ID') }, async ({ id }) => {
877
+ try {
878
+ const result = await client.getOne('project-comments', id);
879
+ return formatResult(result);
880
+ }
881
+ catch (err) {
882
+ return formatError(err);
883
+ }
884
+ });
885
+ // ============================================================
886
+ // Compliance Records
887
+ // ============================================================
888
+ server.tool('list_compliance_records', 'List compliance records — audit trail of completed compliance checks linked to work orders and PM schedules.', {
889
+ ...paginationSchema,
890
+ compliance_item_id: z.string().uuid().optional().describe('Filter by compliance item ID'),
891
+ work_order_id: z.string().uuid().optional().describe('Filter by work order ID'),
892
+ }, async (params) => {
893
+ try {
894
+ const result = await smartList(client, 'compliance-records', params);
895
+ return formatResult(result);
896
+ }
897
+ catch (err) {
898
+ return formatError(err);
899
+ }
900
+ });
901
+ server.tool('get_compliance_record', 'Get a single compliance record by ID.', { id: uuidParam.describe('Compliance record ID') }, async ({ id }) => {
902
+ try {
903
+ const result = await client.getOne('compliance-records', id);
904
+ return formatResult(result);
905
+ }
906
+ catch (err) {
907
+ return formatError(err);
908
+ }
909
+ });
910
+ // ============================================================
911
+ // Site FCI History
912
+ // ============================================================
913
+ server.tool('list_site_fci_history', 'List Facility Condition Index (FCI) history for sites. Track FCI trends over time.', {
914
+ ...paginationSchema,
915
+ site_id: z.string().uuid().optional().describe('Filter by site ID'),
916
+ }, async (params) => {
917
+ try {
918
+ const result = await smartList(client, 'site-fci-history', params);
919
+ return formatResult(result);
920
+ }
921
+ catch (err) {
922
+ return formatError(err);
923
+ }
924
+ });
925
+ server.tool('get_site_fci_history_entry', 'Get a single site FCI history entry by ID.', { id: uuidParam.describe('FCI history entry ID') }, async ({ id }) => {
926
+ try {
927
+ const result = await client.getOne('site-fci-history', id);
928
+ return formatResult(result);
929
+ }
930
+ catch (err) {
931
+ return formatError(err);
932
+ }
933
+ });
934
+ // ============================================================
935
+ // Dashboard Snapshots
936
+ // ============================================================
937
+ server.tool('list_dashboard_snapshots', 'List monthly dashboard snapshots with aggregate stats: asset counts, condition scores, work order metrics, and CRV totals.', {
938
+ ...paginationSchema,
939
+ year: z.number().int().optional().describe('Filter by snapshot year (e.g. 2026)'),
940
+ month: z.number().int().min(1).max(12).optional().describe('Filter by snapshot month (1-12)'),
941
+ }, async (params) => {
942
+ try {
943
+ const result = await smartList(client, 'dashboard-snapshots', params);
944
+ return formatResult(result);
945
+ }
946
+ catch (err) {
947
+ return formatError(err);
948
+ }
949
+ });
950
+ server.tool('get_dashboard_snapshot', 'Get a single dashboard snapshot by ID.', { id: uuidParam.describe('Dashboard snapshot ID') }, async ({ id }) => {
951
+ try {
952
+ const result = await client.getOne('dashboard-snapshots', id);
953
+ return formatResult(result);
954
+ }
955
+ catch (err) {
956
+ return formatError(err);
957
+ }
958
+ });
959
+ // ============================================================
960
+ // Vendor Site Assignments
961
+ // ============================================================
962
+ server.tool('list_vendor_site_assignments', 'List vendor-to-site assignments showing which vendors serve which sites.', {
963
+ ...paginationSchema,
964
+ vendor_id: z.string().uuid().optional().describe('Filter by vendor ID'),
965
+ site_id: z.string().uuid().optional().describe('Filter by site ID'),
966
+ }, async (params) => {
967
+ try {
968
+ const result = await smartList(client, 'vendor-site-assignments', params);
969
+ return formatResult(result);
970
+ }
971
+ catch (err) {
972
+ return formatError(err);
973
+ }
974
+ });
975
+ server.tool('get_vendor_site_assignment', 'Get a single vendor site assignment by ID, including vendor and site names.', { id: uuidParam.describe('Vendor site assignment ID') }, async ({ id }) => {
976
+ try {
977
+ const result = await client.getOne('vendor-site-assignments', id);
978
+ return formatResult(result);
979
+ }
980
+ catch (err) {
981
+ return formatError(err);
982
+ }
983
+ });
984
+ // ============================================================
985
+ // Contract Sites
986
+ // ============================================================
987
+ server.tool('list_contract_sites', 'List contract-to-site mappings showing which contracts cover which sites. No single-record lookup (composite key).', {
988
+ ...paginationSchema,
989
+ contract_id: z.string().uuid().optional().describe('Filter by contract ID'),
990
+ site_id: z.string().uuid().optional().describe('Filter by site ID'),
991
+ }, async (params) => {
992
+ try {
993
+ const result = await smartList(client, 'contract-sites', params);
994
+ return formatResult(result);
995
+ }
996
+ catch (err) {
997
+ return formatError(err);
998
+ }
999
+ });
1000
+ // ============================================================
1001
+ // Custom Field Definitions
1002
+ // ============================================================
1003
+ server.tool('list_custom_field_definitions', 'List custom field definitions configured for this tenant. Filter by entity type (e.g. asset, work_order) or field type.', {
1004
+ ...paginationSchema,
1005
+ entity_type: z.string().max(100).optional().describe('Filter by entity type (e.g. asset, work_order)'),
1006
+ field_type: z.enum(['text', 'number', 'date', 'boolean', 'select']).optional().describe('Filter by field type'),
1007
+ }, async (params) => {
1008
+ try {
1009
+ const result = await smartList(client, 'custom-field-definitions', params);
1010
+ return formatResult(result);
1011
+ }
1012
+ catch (err) {
1013
+ return formatError(err);
1014
+ }
1015
+ });
1016
+ server.tool('get_custom_field_definition', 'Get a single custom field definition by ID.', { id: uuidParam.describe('Custom field definition ID') }, async ({ id }) => {
1017
+ try {
1018
+ const result = await client.getOne('custom-field-definitions', id);
1019
+ return formatResult(result);
1020
+ }
1021
+ catch (err) {
1022
+ return formatError(err);
1023
+ }
1024
+ });
1025
+ // ============================================================
1026
+ // Custom Field Values
1027
+ // ============================================================
1028
+ server.tool('list_custom_field_values', 'List custom field values. Filter by entity_id to get all custom fields for a specific record, or by field_definition_id.', {
1029
+ ...paginationSchema,
1030
+ entity_id: z.string().uuid().optional().describe('Filter by entity ID (e.g. asset ID, work order ID)'),
1031
+ field_definition_id: z.string().uuid().optional().describe('Filter by field definition ID'),
1032
+ }, async (params) => {
1033
+ try {
1034
+ const result = await smartList(client, 'custom-field-values', params);
1035
+ return formatResult(result);
1036
+ }
1037
+ catch (err) {
1038
+ return formatError(err);
1039
+ }
1040
+ });
1041
+ server.tool('get_custom_field_value', 'Get a single custom field value by ID.', { id: uuidParam.describe('Custom field value ID') }, async ({ id }) => {
1042
+ try {
1043
+ const result = await client.getOne('custom-field-values', id);
1044
+ return formatResult(result);
1045
+ }
1046
+ catch (err) {
1047
+ return formatError(err);
1048
+ }
1049
+ });
1050
+ // ============================================================
1051
+ // Part Categories
1052
+ // ============================================================
1053
+ server.tool('list_part_categories', 'List part categories used to classify inventory parts.', {
1054
+ ...searchSchema,
1055
+ ...paginationSchema,
1056
+ }, async (params) => {
1057
+ try {
1058
+ const result = await smartList(client, 'part-categories', params);
1059
+ return formatResult(result);
1060
+ }
1061
+ catch (err) {
1062
+ return formatError(err);
1063
+ }
1064
+ });
1065
+ server.tool('get_part_category', 'Get a single part category by ID.', { id: uuidParam.describe('Part category ID') }, async ({ id }) => {
1066
+ try {
1067
+ const result = await client.getOne('part-categories', id);
1068
+ return formatResult(result);
1069
+ }
1070
+ catch (err) {
1071
+ return formatError(err);
1072
+ }
1073
+ });
300
1074
  }
301
1075
  //# sourceMappingURL=tools.js.map