@assetlab/mcp-server 1.11.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/tools-write.js +367 -1
- package/dist/tools-write.js.map +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +261 -0
- package/dist/tools.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,6 +86,7 @@ Works with **Claude**, **ChatGPT**, **Microsoft Copilot**, and any MCP-compatibl
|
|
|
86
86
|
| Invoices | `list_invoices` | `get_invoice` |
|
|
87
87
|
| Purchase Orders | `list_purchase_orders` | `get_purchase_order` |
|
|
88
88
|
| Expenses | `list_expenses` | `get_expense` |
|
|
89
|
+
| Change Orders | `list_change_orders` | `get_change_order` |
|
|
89
90
|
| Budgets | `list_budgets` | `get_budget` |
|
|
90
91
|
| Parts | `list_parts` | `get_part` |
|
|
91
92
|
| Part Categories | `list_part_categories` | `get_part_category` |
|
|
@@ -100,6 +101,7 @@ Works with **Claude**, **ChatGPT**, **Microsoft Copilot**, and any MCP-compatibl
|
|
|
100
101
|
| Project Budget Items | `list_project_budget_items` | `get_project_budget_item` |
|
|
101
102
|
| Project Time Entries | `list_project_time_entries` | `get_project_time_entry` |
|
|
102
103
|
| Project Comments | `list_project_comments` | `get_project_comment` |
|
|
104
|
+
| Project Document Folder Templates | `list_project_document_folder_templates` | `get_project_document_folder_template` |
|
|
103
105
|
| Site FCI History | `list_site_fci_history` | `get_site_fci_history_entry` |
|
|
104
106
|
| Vendor Site Assignments | `list_vendor_site_assignments` | `get_vendor_site_assignment` |
|
|
105
107
|
| Contract Sites | `list_contract_sites` | — |
|
|
@@ -131,6 +133,7 @@ Works with **Claude**, **ChatGPT**, **Microsoft Copilot**, and any MCP-compatibl
|
|
|
131
133
|
| Invoices | `create_invoice` | `update_invoice` | `delete_invoice` |
|
|
132
134
|
| Purchase Orders | `create_purchase_order` | `update_purchase_order` | `delete_purchase_order` |
|
|
133
135
|
| Expenses | `create_expense` | `update_expense` | `delete_expense` |
|
|
136
|
+
| Change Orders | `create_change_order` | `update_change_order` | `delete_change_order` |
|
|
134
137
|
| Budgets | `create_budget` | `update_budget` | `delete_budget` |
|
|
135
138
|
| Parts | `create_part` | `update_part` | `delete_part` |
|
|
136
139
|
| Part Categories | `create_part_category` | `update_part_category` | `delete_part_category` |
|
|
@@ -144,6 +147,7 @@ Works with **Claude**, **ChatGPT**, **Microsoft Copilot**, and any MCP-compatibl
|
|
|
144
147
|
| Project Budget Items | `create_project_budget_item` | `update_project_budget_item` | `delete_project_budget_item` |
|
|
145
148
|
| Project Time Entries | `create_project_time_entry` | `update_project_time_entry` | `delete_project_time_entry` |
|
|
146
149
|
| Project Comments | `create_project_comment` | `update_project_comment` | `delete_project_comment` |
|
|
150
|
+
| Project Document Folder Templates | `create_project_document_folder_template` | `update_project_document_folder_template` | `delete_project_document_folder_template` |
|
|
147
151
|
| Vendor Site Assignments | `create_vendor_site_assignment` | — | `delete_vendor_site_assignment` |
|
|
148
152
|
| Contract Sites | `create_contract_site` | — | `delete_contract_site` |
|
|
149
153
|
| Custom Field Definitions | `create_custom_field_definition` | `update_custom_field_definition` | `delete_custom_field_definition` |
|
package/dist/tools-write.js
CHANGED
|
@@ -831,6 +831,100 @@ export function registerWriteTools(server, client) {
|
|
|
831
831
|
}
|
|
832
832
|
});
|
|
833
833
|
// ============================================================
|
|
834
|
+
// Change Orders (scope: change_orders)
|
|
835
|
+
// ============================================================
|
|
836
|
+
server.tool('create_change_order', 'Create a new change order. Requires change_orders:write scope.', {
|
|
837
|
+
co_number: z.string().min(1).max(200).describe('Change order number (required)'),
|
|
838
|
+
description: z.string().min(1).describe('Description (required)'),
|
|
839
|
+
amount: z.number().describe('Amount (required, negative for credits)'),
|
|
840
|
+
status: z.enum(['draft', 'submitted', 'approved', 'rejected']).optional().describe('Status'),
|
|
841
|
+
reason: z.string().optional().describe('Reason for the change order'),
|
|
842
|
+
notes: z.string().optional().describe('Additional notes'),
|
|
843
|
+
project_id: z.string().uuid().optional().describe('Project ID'),
|
|
844
|
+
vendor_id: z.string().uuid().optional().describe('Vendor ID'),
|
|
845
|
+
category_id: z.string().uuid().optional().describe('Cost category ID'),
|
|
846
|
+
}, async (params) => {
|
|
847
|
+
try {
|
|
848
|
+
const result = await client.create('change-orders', buildBody(params));
|
|
849
|
+
return formatResult(result);
|
|
850
|
+
}
|
|
851
|
+
catch (err) {
|
|
852
|
+
return formatError(err);
|
|
853
|
+
}
|
|
854
|
+
});
|
|
855
|
+
server.tool('update_change_order', 'Update an existing change order by ID. Requires change_orders:write scope.', {
|
|
856
|
+
id: z.string().uuid().describe('Change order ID'),
|
|
857
|
+
co_number: z.string().min(1).max(200).optional().describe('Change order number'),
|
|
858
|
+
description: z.string().optional().describe('Description'),
|
|
859
|
+
amount: z.number().optional().describe('Amount (negative for credits)'),
|
|
860
|
+
status: z.enum(['draft', 'submitted', 'approved', 'rejected']).optional().describe('Status'),
|
|
861
|
+
reason: z.string().optional().describe('Reason'),
|
|
862
|
+
notes: z.string().optional().describe('Notes'),
|
|
863
|
+
project_id: z.string().uuid().optional().describe('Project ID'),
|
|
864
|
+
vendor_id: z.string().uuid().optional().describe('Vendor ID'),
|
|
865
|
+
category_id: z.string().uuid().optional().describe('Cost category ID'),
|
|
866
|
+
approved_by: z.string().optional().describe('Approved by (user ID)'),
|
|
867
|
+
approved_at: z.string().optional().describe('Approval date (ISO 8601)'),
|
|
868
|
+
}, async ({ id, ...rest }) => {
|
|
869
|
+
try {
|
|
870
|
+
const result = await client.update('change-orders', id, buildBody(rest));
|
|
871
|
+
return formatResult(result);
|
|
872
|
+
}
|
|
873
|
+
catch (err) {
|
|
874
|
+
return formatError(err);
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
server.tool('delete_change_order', 'Delete a change order by ID. Requires change_orders:write scope.', { id: z.string().uuid().describe('Change order ID') }, async ({ id }) => {
|
|
878
|
+
try {
|
|
879
|
+
const result = await client.remove('change-orders', id);
|
|
880
|
+
return formatResult(result);
|
|
881
|
+
}
|
|
882
|
+
catch (err) {
|
|
883
|
+
return formatError(err);
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
// ============================================================
|
|
887
|
+
// Project Document Folder Templates (scope: project_document_folder_templates)
|
|
888
|
+
// ============================================================
|
|
889
|
+
server.tool('create_project_document_folder_template', 'Create a project document folder template. Requires project_document_folder_templates:write scope.', {
|
|
890
|
+
name: z.string().min(1).max(500).describe('Template name (required)'),
|
|
891
|
+
description: z.string().max(2000).optional().describe('Template description'),
|
|
892
|
+
structure: z.array(z.any()).optional().describe('Folder hierarchy as JSON array'),
|
|
893
|
+
is_default: z.boolean().optional().describe('Whether this is the default template'),
|
|
894
|
+
}, async (params) => {
|
|
895
|
+
try {
|
|
896
|
+
const result = await client.create('project-document-folder-templates', buildBody(params));
|
|
897
|
+
return formatResult(result);
|
|
898
|
+
}
|
|
899
|
+
catch (err) {
|
|
900
|
+
return formatError(err);
|
|
901
|
+
}
|
|
902
|
+
});
|
|
903
|
+
server.tool('update_project_document_folder_template', 'Update a project document folder template by ID. Requires project_document_folder_templates:write scope.', {
|
|
904
|
+
id: z.string().uuid().describe('Template ID'),
|
|
905
|
+
name: z.string().min(1).max(500).optional().describe('Template name'),
|
|
906
|
+
description: z.string().max(2000).optional().describe('Template description'),
|
|
907
|
+
structure: z.array(z.any()).optional().describe('Folder hierarchy as JSON array'),
|
|
908
|
+
is_default: z.boolean().optional().describe('Whether this is the default template'),
|
|
909
|
+
}, async ({ id, ...rest }) => {
|
|
910
|
+
try {
|
|
911
|
+
const result = await client.update('project-document-folder-templates', id, buildBody(rest));
|
|
912
|
+
return formatResult(result);
|
|
913
|
+
}
|
|
914
|
+
catch (err) {
|
|
915
|
+
return formatError(err);
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
server.tool('delete_project_document_folder_template', 'Delete a project document folder template by ID. Requires project_document_folder_templates:write scope.', { id: z.string().uuid().describe('Template ID') }, async ({ id }) => {
|
|
919
|
+
try {
|
|
920
|
+
const result = await client.remove('project-document-folder-templates', id);
|
|
921
|
+
return formatResult(result);
|
|
922
|
+
}
|
|
923
|
+
catch (err) {
|
|
924
|
+
return formatError(err);
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
// ============================================================
|
|
834
928
|
// 14. Budgets (scope: budgets)
|
|
835
929
|
// ============================================================
|
|
836
930
|
server.tool('create_budget', 'Create a new budget. Requires budgets:write scope.', {
|
|
@@ -2490,6 +2584,62 @@ export function registerWriteTools(server, client) {
|
|
|
2490
2584
|
}
|
|
2491
2585
|
});
|
|
2492
2586
|
// ============================================================
|
|
2587
|
+
// Project Risks (scope: project_risks)
|
|
2588
|
+
// ============================================================
|
|
2589
|
+
server.tool('create_project_risk', 'Create a new project risk. Requires project_risks:write scope.', {
|
|
2590
|
+
project_id: z.string().uuid().describe('Project ID (required)'),
|
|
2591
|
+
title: z.string().min(1).max(500).describe('Risk title (required)'),
|
|
2592
|
+
description: z.string().optional().describe('Risk description'),
|
|
2593
|
+
category: z.enum(['technical', 'financial', 'schedule', 'resource', 'external']).optional().describe('Risk category'),
|
|
2594
|
+
probability: z.enum(['low', 'medium', 'high']).optional().describe('Probability level'),
|
|
2595
|
+
impact: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Impact level'),
|
|
2596
|
+
status: z.enum(['identified', 'analyzing', 'mitigating', 'resolved', 'accepted']).optional().describe('Risk status (default: identified)'),
|
|
2597
|
+
mitigation_plan: z.string().optional().describe('Mitigation plan'),
|
|
2598
|
+
contingency_plan: z.string().optional().describe('Contingency plan'),
|
|
2599
|
+
owner_id: z.string().max(200).optional().describe('Risk owner (Clerk user ID)'),
|
|
2600
|
+
due_date: z.string().optional().describe('Due date (ISO 8601)'),
|
|
2601
|
+
created_by: z.string().max(200).optional().describe('Creator (Clerk user ID)'),
|
|
2602
|
+
}, async (params) => {
|
|
2603
|
+
try {
|
|
2604
|
+
const result = await client.create('project-risks', buildBody(params));
|
|
2605
|
+
return formatResult(result);
|
|
2606
|
+
}
|
|
2607
|
+
catch (err) {
|
|
2608
|
+
return formatError(err);
|
|
2609
|
+
}
|
|
2610
|
+
});
|
|
2611
|
+
server.tool('update_project_risk', 'Update an existing project risk by ID. Requires project_risks:write scope.', {
|
|
2612
|
+
id: z.string().uuid().describe('Project risk ID'),
|
|
2613
|
+
project_id: z.string().uuid().optional().describe('Project ID'),
|
|
2614
|
+
title: z.string().min(1).max(500).optional().describe('Risk title'),
|
|
2615
|
+
description: z.string().optional().describe('Risk description'),
|
|
2616
|
+
category: z.enum(['technical', 'financial', 'schedule', 'resource', 'external']).optional().describe('Risk category'),
|
|
2617
|
+
probability: z.enum(['low', 'medium', 'high']).optional().describe('Probability level'),
|
|
2618
|
+
impact: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Impact level'),
|
|
2619
|
+
status: z.enum(['identified', 'analyzing', 'mitigating', 'resolved', 'accepted']).optional().describe('Risk status'),
|
|
2620
|
+
mitigation_plan: z.string().optional().describe('Mitigation plan'),
|
|
2621
|
+
contingency_plan: z.string().optional().describe('Contingency plan'),
|
|
2622
|
+
owner_id: z.string().max(200).optional().describe('Risk owner (Clerk user ID)'),
|
|
2623
|
+
due_date: z.string().optional().describe('Due date (ISO 8601)'),
|
|
2624
|
+
}, async ({ id, ...rest }) => {
|
|
2625
|
+
try {
|
|
2626
|
+
const result = await client.update('project-risks', id, buildBody(rest));
|
|
2627
|
+
return formatResult(result);
|
|
2628
|
+
}
|
|
2629
|
+
catch (err) {
|
|
2630
|
+
return formatError(err);
|
|
2631
|
+
}
|
|
2632
|
+
});
|
|
2633
|
+
server.tool('delete_project_risk', 'Delete a project risk by ID. Requires project_risks:write scope.', { id: z.string().uuid().describe('Project risk ID') }, async ({ id }) => {
|
|
2634
|
+
try {
|
|
2635
|
+
const result = await client.remove('project-risks', id);
|
|
2636
|
+
return formatResult(result);
|
|
2637
|
+
}
|
|
2638
|
+
catch (err) {
|
|
2639
|
+
return formatError(err);
|
|
2640
|
+
}
|
|
2641
|
+
});
|
|
2642
|
+
// ============================================================
|
|
2493
2643
|
// Project Assets
|
|
2494
2644
|
// ============================================================
|
|
2495
2645
|
server.tool('create_project_asset', 'Link an asset to a project. Requires project_assets:write scope.', {
|
|
@@ -2514,6 +2664,221 @@ export function registerWriteTools(server, client) {
|
|
|
2514
2664
|
}
|
|
2515
2665
|
});
|
|
2516
2666
|
// ============================================================
|
|
2667
|
+
// Level of Service: Service Areas (scope: service_areas)
|
|
2668
|
+
// ============================================================
|
|
2669
|
+
server.tool('create_service_area', 'Create a new service area for Level of Service tracking. Requires service_areas:write scope. After creating, link system classes via create_service_area_system_class and sites via create_service_area_site.', {
|
|
2670
|
+
name: z.string().min(1).max(500).describe('Service area name (required, unique per tenant)'),
|
|
2671
|
+
description: z.string().max(2000).optional().describe('Description'),
|
|
2672
|
+
icon: z.string().max(200).optional().describe('Icon name (e.g., "droplets" for water)'),
|
|
2673
|
+
color: z.string().max(50).optional().describe('Hex color code (e.g., "#3B82F6")'),
|
|
2674
|
+
sort_order: z.number().int().min(0).optional().describe('Sort order for display'),
|
|
2675
|
+
is_active: z.boolean().optional().describe('Whether the service area is active (default: true)'),
|
|
2676
|
+
}, async (params) => {
|
|
2677
|
+
try {
|
|
2678
|
+
const result = await client.create('service-areas', buildBody(params));
|
|
2679
|
+
return formatResult(result);
|
|
2680
|
+
}
|
|
2681
|
+
catch (err) {
|
|
2682
|
+
return formatError(err);
|
|
2683
|
+
}
|
|
2684
|
+
});
|
|
2685
|
+
server.tool('update_service_area', 'Update an existing service area by ID. Requires service_areas:write scope.', {
|
|
2686
|
+
id: z.string().uuid().describe('Service area ID'),
|
|
2687
|
+
name: z.string().min(1).max(500).optional().describe('Service area name'),
|
|
2688
|
+
description: z.string().max(2000).optional().describe('Description'),
|
|
2689
|
+
icon: z.string().max(200).optional().describe('Icon name'),
|
|
2690
|
+
color: z.string().max(50).optional().describe('Hex color code'),
|
|
2691
|
+
sort_order: z.number().int().min(0).optional().describe('Sort order'),
|
|
2692
|
+
is_active: z.boolean().optional().describe('Active status'),
|
|
2693
|
+
}, async ({ id, ...rest }) => {
|
|
2694
|
+
try {
|
|
2695
|
+
const result = await client.update('service-areas', id, buildBody(rest));
|
|
2696
|
+
return formatResult(result);
|
|
2697
|
+
}
|
|
2698
|
+
catch (err) {
|
|
2699
|
+
return formatError(err);
|
|
2700
|
+
}
|
|
2701
|
+
});
|
|
2702
|
+
server.tool('delete_service_area', 'Delete a service area by ID. WARNING: This also deletes all linked measures, measurements, and junction records. Requires service_areas:write scope.', { id: z.string().uuid().describe('Service area ID') }, async ({ id }) => {
|
|
2703
|
+
try {
|
|
2704
|
+
const result = await client.remove('service-areas', id);
|
|
2705
|
+
return formatResult(result);
|
|
2706
|
+
}
|
|
2707
|
+
catch (err) {
|
|
2708
|
+
return formatError(err);
|
|
2709
|
+
}
|
|
2710
|
+
});
|
|
2711
|
+
// ============================================================
|
|
2712
|
+
// Level of Service: Service Area System Classes (scope: service_areas)
|
|
2713
|
+
// ============================================================
|
|
2714
|
+
server.tool('create_service_area_system_class', 'Link a system class to a service area. Requires service_areas:write scope. Resolve IDs first: list_service_areas → service_area_id, list_system_classes → system_class_id.', {
|
|
2715
|
+
service_area_id: z.string().uuid().describe('Service area ID (required)'),
|
|
2716
|
+
system_class_id: z.string().uuid().describe('System class ID (required)'),
|
|
2717
|
+
}, async (params) => {
|
|
2718
|
+
try {
|
|
2719
|
+
const result = await client.create('service-area-system-classes', buildBody(params));
|
|
2720
|
+
return formatResult(result);
|
|
2721
|
+
}
|
|
2722
|
+
catch (err) {
|
|
2723
|
+
return formatError(err);
|
|
2724
|
+
}
|
|
2725
|
+
});
|
|
2726
|
+
server.tool('delete_service_area_system_class', 'Remove a system class link from a service area. Requires service_areas:write scope.', { id: z.string().uuid().describe('Service area system class link ID') }, async ({ id }) => {
|
|
2727
|
+
try {
|
|
2728
|
+
const result = await client.remove('service-area-system-classes', id);
|
|
2729
|
+
return formatResult(result);
|
|
2730
|
+
}
|
|
2731
|
+
catch (err) {
|
|
2732
|
+
return formatError(err);
|
|
2733
|
+
}
|
|
2734
|
+
});
|
|
2735
|
+
// ============================================================
|
|
2736
|
+
// Level of Service: Service Area Sites (scope: service_areas)
|
|
2737
|
+
// ============================================================
|
|
2738
|
+
server.tool('create_service_area_site', 'Link a site to a service area (optional scoping). Requires service_areas:write scope. Resolve IDs first: list_service_areas → service_area_id, list_sites → site_id.', {
|
|
2739
|
+
service_area_id: z.string().uuid().describe('Service area ID (required)'),
|
|
2740
|
+
site_id: z.string().uuid().describe('Site ID (required)'),
|
|
2741
|
+
}, async (params) => {
|
|
2742
|
+
try {
|
|
2743
|
+
const result = await client.create('service-area-sites', buildBody(params));
|
|
2744
|
+
return formatResult(result);
|
|
2745
|
+
}
|
|
2746
|
+
catch (err) {
|
|
2747
|
+
return formatError(err);
|
|
2748
|
+
}
|
|
2749
|
+
});
|
|
2750
|
+
server.tool('delete_service_area_site', 'Remove a site link from a service area. Requires service_areas:write scope.', { id: z.string().uuid().describe('Service area site link ID') }, async ({ id }) => {
|
|
2751
|
+
try {
|
|
2752
|
+
const result = await client.remove('service-area-sites', id);
|
|
2753
|
+
return formatResult(result);
|
|
2754
|
+
}
|
|
2755
|
+
catch (err) {
|
|
2756
|
+
return formatError(err);
|
|
2757
|
+
}
|
|
2758
|
+
});
|
|
2759
|
+
// ============================================================
|
|
2760
|
+
// Level of Service: Measures (scope: los_measures)
|
|
2761
|
+
// ============================================================
|
|
2762
|
+
server.tool('create_los_measure', 'Create a new LoS measure within a service area. Requires los_measures:write scope. Resolve service_area_id first via list_service_areas.', {
|
|
2763
|
+
service_area_id: z.string().uuid().describe('Service area ID (required)'),
|
|
2764
|
+
name: z.string().min(1).max(500).describe('Measure name (required, unique per service area)'),
|
|
2765
|
+
category: z.enum(['quality', 'reliability', 'responsiveness', 'safety', 'sustainability', 'cost_efficiency', 'capacity']).describe('Measure category (required)'),
|
|
2766
|
+
type: z.enum(['community', 'technical']).describe('Measure type (required)'),
|
|
2767
|
+
data_source: z.enum([
|
|
2768
|
+
'manual', 'custom_formula', 'asset_condition_avg', 'asset_condition_pct_above',
|
|
2769
|
+
'asset_condition_pct_below', 'risk_score_avg', 'risk_pct_critical',
|
|
2770
|
+
'wo_response_time_avg', 'wo_completion_time_avg', 'wo_backlog_count', 'wo_overdue_count',
|
|
2771
|
+
'pm_compliance_rate', 'compliance_score', 'fci', 'deferred_maintenance_ratio',
|
|
2772
|
+
'asset_past_useful_life_pct',
|
|
2773
|
+
]).describe('Data source type (required). Use "manual" if values will be entered by hand.'),
|
|
2774
|
+
description: z.string().max(2000).optional().describe('Description'),
|
|
2775
|
+
community_statement: z.string().max(2000).optional().describe('Community-facing statement (for community type measures)'),
|
|
2776
|
+
unit: z.string().max(100).optional().describe('Unit of measurement (e.g., "%", "hours", "count")'),
|
|
2777
|
+
trend_direction: z.enum(['higher_is_better', 'lower_is_better', 'target_is_optimal']).optional().describe('Which direction is better'),
|
|
2778
|
+
target_value: z.number().optional().describe('Target value'),
|
|
2779
|
+
minimum_acceptable: z.number().optional().describe('Minimum acceptable value'),
|
|
2780
|
+
stretch_goal: z.number().optional().describe('Stretch goal value'),
|
|
2781
|
+
weight: z.number().min(0).optional().describe('Weight for composite score calculation (default: 1.0)'),
|
|
2782
|
+
data_source_config: z.record(z.unknown()).optional().describe('Data source configuration (JSONB). E.g., {"threshold": 3} for pct_above/below, {"days_back": 90} for WO metrics.'),
|
|
2783
|
+
is_active: z.boolean().optional().describe('Whether the measure is active (default: true)'),
|
|
2784
|
+
sort_order: z.number().int().min(0).optional().describe('Sort order for display'),
|
|
2785
|
+
}, async (params) => {
|
|
2786
|
+
try {
|
|
2787
|
+
const result = await client.create('los-measures', buildBody(params));
|
|
2788
|
+
return formatResult(result);
|
|
2789
|
+
}
|
|
2790
|
+
catch (err) {
|
|
2791
|
+
return formatError(err);
|
|
2792
|
+
}
|
|
2793
|
+
});
|
|
2794
|
+
server.tool('update_los_measure', 'Update an existing LoS measure by ID. Requires los_measures:write scope.', {
|
|
2795
|
+
id: z.string().uuid().describe('LoS measure ID'),
|
|
2796
|
+
name: z.string().min(1).max(500).optional().describe('Measure name'),
|
|
2797
|
+
category: z.enum(['quality', 'reliability', 'responsiveness', 'safety', 'sustainability', 'cost_efficiency', 'capacity']).optional().describe('Measure category'),
|
|
2798
|
+
type: z.enum(['community', 'technical']).optional().describe('Measure type'),
|
|
2799
|
+
data_source: z.enum([
|
|
2800
|
+
'manual', 'custom_formula', 'asset_condition_avg', 'asset_condition_pct_above',
|
|
2801
|
+
'asset_condition_pct_below', 'risk_score_avg', 'risk_pct_critical',
|
|
2802
|
+
'wo_response_time_avg', 'wo_completion_time_avg', 'wo_backlog_count', 'wo_overdue_count',
|
|
2803
|
+
'pm_compliance_rate', 'compliance_score', 'fci', 'deferred_maintenance_ratio',
|
|
2804
|
+
'asset_past_useful_life_pct',
|
|
2805
|
+
]).optional().describe('Data source type'),
|
|
2806
|
+
description: z.string().max(2000).optional().describe('Description'),
|
|
2807
|
+
community_statement: z.string().max(2000).optional().describe('Community-facing statement'),
|
|
2808
|
+
unit: z.string().max(100).optional().describe('Unit of measurement'),
|
|
2809
|
+
trend_direction: z.enum(['higher_is_better', 'lower_is_better', 'target_is_optimal']).optional().describe('Which direction is better'),
|
|
2810
|
+
target_value: z.number().optional().describe('Target value'),
|
|
2811
|
+
minimum_acceptable: z.number().optional().describe('Minimum acceptable value'),
|
|
2812
|
+
stretch_goal: z.number().optional().describe('Stretch goal value'),
|
|
2813
|
+
weight: z.number().min(0).optional().describe('Weight for composite score calculation'),
|
|
2814
|
+
data_source_config: z.record(z.unknown()).optional().describe('Data source configuration (JSONB)'),
|
|
2815
|
+
is_active: z.boolean().optional().describe('Active status'),
|
|
2816
|
+
sort_order: z.number().int().min(0).optional().describe('Sort order'),
|
|
2817
|
+
}, async ({ id, ...rest }) => {
|
|
2818
|
+
try {
|
|
2819
|
+
const result = await client.update('los-measures', id, buildBody(rest));
|
|
2820
|
+
return formatResult(result);
|
|
2821
|
+
}
|
|
2822
|
+
catch (err) {
|
|
2823
|
+
return formatError(err);
|
|
2824
|
+
}
|
|
2825
|
+
});
|
|
2826
|
+
server.tool('delete_los_measure', 'Delete a LoS measure by ID. WARNING: This also deletes all associated measurements and targets history. Requires los_measures:write scope.', { id: z.string().uuid().describe('LoS measure ID') }, async ({ id }) => {
|
|
2827
|
+
try {
|
|
2828
|
+
const result = await client.remove('los-measures', id);
|
|
2829
|
+
return formatResult(result);
|
|
2830
|
+
}
|
|
2831
|
+
catch (err) {
|
|
2832
|
+
return formatError(err);
|
|
2833
|
+
}
|
|
2834
|
+
});
|
|
2835
|
+
// ============================================================
|
|
2836
|
+
// Level of Service: Measurements (scope: los_measurements)
|
|
2837
|
+
// ============================================================
|
|
2838
|
+
server.tool('create_los_measurement', 'Record a new LoS measurement value. Requires los_measurements:write scope. Resolve los_measure_id first via list_los_measures.', {
|
|
2839
|
+
los_measure_id: z.string().uuid().describe('LoS measure ID (required)'),
|
|
2840
|
+
period_type: z.enum(['monthly', 'quarterly', 'semi_annual', 'annual']).describe('Period type (required)'),
|
|
2841
|
+
period_start: z.string().describe('Period start date (ISO 8601, required, e.g., "2026-01-01")'),
|
|
2842
|
+
period_end: z.string().describe('Period end date (ISO 8601, required, e.g., "2026-03-31")'),
|
|
2843
|
+
actual_value: z.number().describe('Measured value (required)'),
|
|
2844
|
+
notes: z.string().max(2000).optional().describe('Notes or context for this measurement'),
|
|
2845
|
+
is_auto: z.boolean().optional().describe('Whether this is an auto-calculated value (default: false)'),
|
|
2846
|
+
}, async (params) => {
|
|
2847
|
+
try {
|
|
2848
|
+
const result = await client.create('los-measurements', buildBody(params));
|
|
2849
|
+
return formatResult(result);
|
|
2850
|
+
}
|
|
2851
|
+
catch (err) {
|
|
2852
|
+
return formatError(err);
|
|
2853
|
+
}
|
|
2854
|
+
});
|
|
2855
|
+
server.tool('update_los_measurement', 'Update an existing LoS measurement by ID. Requires los_measurements:write scope.', {
|
|
2856
|
+
id: z.string().uuid().describe('LoS measurement ID'),
|
|
2857
|
+
actual_value: z.number().optional().describe('Measured value'),
|
|
2858
|
+
notes: z.string().max(2000).optional().describe('Notes or context'),
|
|
2859
|
+
period_type: z.enum(['monthly', 'quarterly', 'semi_annual', 'annual']).optional().describe('Period type'),
|
|
2860
|
+
period_start: z.string().optional().describe('Period start date (ISO 8601)'),
|
|
2861
|
+
period_end: z.string().optional().describe('Period end date (ISO 8601)'),
|
|
2862
|
+
is_auto: z.boolean().optional().describe('Whether this is auto-calculated'),
|
|
2863
|
+
}, async ({ id, ...rest }) => {
|
|
2864
|
+
try {
|
|
2865
|
+
const result = await client.update('los-measurements', id, buildBody(rest));
|
|
2866
|
+
return formatResult(result);
|
|
2867
|
+
}
|
|
2868
|
+
catch (err) {
|
|
2869
|
+
return formatError(err);
|
|
2870
|
+
}
|
|
2871
|
+
});
|
|
2872
|
+
server.tool('delete_los_measurement', 'Delete a LoS measurement by ID. Requires los_measurements:write scope.', { id: z.string().uuid().describe('LoS measurement ID') }, async ({ id }) => {
|
|
2873
|
+
try {
|
|
2874
|
+
const result = await client.remove('los-measurements', id);
|
|
2875
|
+
return formatResult(result);
|
|
2876
|
+
}
|
|
2877
|
+
catch (err) {
|
|
2878
|
+
return formatError(err);
|
|
2879
|
+
}
|
|
2880
|
+
});
|
|
2881
|
+
// ============================================================
|
|
2517
2882
|
// Bulk operations
|
|
2518
2883
|
// ============================================================
|
|
2519
2884
|
const BULK_RESOURCES = [
|
|
@@ -2530,11 +2895,12 @@ export function registerWriteTools(server, client) {
|
|
|
2530
2895
|
'project-comments', 'project-team-members', 'project-task-dependencies',
|
|
2531
2896
|
'project-updates', 'project-cost-snapshots', 'project-locations',
|
|
2532
2897
|
'project-sites', 'project-buildings', 'project-systems',
|
|
2533
|
-
'project-system-classes', 'project-system-groups', 'project-assets',
|
|
2898
|
+
'project-system-classes', 'project-system-groups', 'project-assets', 'project-risks',
|
|
2534
2899
|
'parts', 'part-categories',
|
|
2535
2900
|
'custom-field-definitions', 'custom-field-values',
|
|
2536
2901
|
'vendor-site-assignments', 'contract-sites',
|
|
2537
2902
|
'asset-documents', 'attachments', 'project-documents', 'contract-documents',
|
|
2903
|
+
'service-areas', 'los-measures', 'los-measurements',
|
|
2538
2904
|
];
|
|
2539
2905
|
server.tool('bulk_create', 'Create multiple records of a resource type in one API call (max 100). Each item is processed independently — one failure does not affect others. Returns per-item results. Requires {resource}:write scope. Counts as 1 request for rate limiting.', {
|
|
2540
2906
|
resource: z.enum(BULK_RESOURCES).describe('Resource type (e.g. "assets", "work-orders")'),
|