@assetlab/mcp-server 1.27.0 → 2.0.1
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/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/tools-write.js +112 -40
- package/dist/tools-write.js.map +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +41 -19
- package/dist/tools.js.map +1 -1
- package/dist/version.d.ts +14 -0
- package/dist/version.js +15 -0
- package/dist/version.js.map +1 -0
- package/dist/worker.js +2 -1
- package/dist/worker.js.map +1 -1
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -13,12 +13,13 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
13
13
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
14
14
|
import { AssetLabClient, loadConfig } from './client.js';
|
|
15
15
|
import { registerTools, SERVER_INSTRUCTIONS } from './tools.js';
|
|
16
|
+
import { VERSION } from './version.js';
|
|
16
17
|
async function main() {
|
|
17
18
|
const config = loadConfig();
|
|
18
19
|
const client = new AssetLabClient(config);
|
|
19
20
|
const server = new McpServer({
|
|
20
21
|
name: 'assetlab',
|
|
21
|
-
version:
|
|
22
|
+
version: VERSION,
|
|
22
23
|
}, {
|
|
23
24
|
instructions: SERVER_INSTRUCTIONS,
|
|
24
25
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAC3B,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,CAAA;IAEzC,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B;QACE,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,OAAO;KACjB,EACD;QACE,YAAY,EAAE,mBAAmB;KAClC,CACF,CAAA;IAED,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAE7B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAA;IAC5C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;AACjC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;IACjB,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,CAAA;IAC3C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
|
package/dist/tools-write.js
CHANGED
|
@@ -20,7 +20,17 @@ function buildBody(params) {
|
|
|
20
20
|
return body;
|
|
21
21
|
}
|
|
22
22
|
const PM_RESOURCE_TYPES = ['TOOL', 'PART', 'MATERIAL', 'EQUIPMENT'];
|
|
23
|
-
|
|
23
|
+
// Shared by the four PM tools: linking a form here makes every work order the schedule
|
|
24
|
+
// generates carry a copy of that form for the technician to fill in.
|
|
25
|
+
const FORM_TEMPLATE_LINK_SCHEMA = z
|
|
26
|
+
.string()
|
|
27
|
+
.uuid()
|
|
28
|
+
.optional()
|
|
29
|
+
.describe('Form template ID to attach to every work order this generates — resolve via list_form_templates. Use a PUBLISHED template: generation resolves the current published version of the form, so a draft attaches nothing until it is published.');
|
|
30
|
+
// Work orders and PM schedules store each association twice — a singular id and a plural array.
|
|
31
|
+
// The API mirrors whichever side is sent, but callers still need the arrays to associate several
|
|
32
|
+
// records at once (and systems, which have no singular field at all).
|
|
33
|
+
const ASSOCIATION_ARRAY_SCHEMA = (description) => z.array(z.string().uuid()).optional().describe(description);
|
|
24
34
|
const FORM_TEMPLATE_STATUSES = ['draft', 'published', 'archived'];
|
|
25
35
|
const FORM_ITEM_TYPES = [
|
|
26
36
|
'section',
|
|
@@ -109,8 +119,15 @@ export function registerWriteTools(server, client) {
|
|
|
109
119
|
.string()
|
|
110
120
|
.uuid()
|
|
111
121
|
.optional()
|
|
112
|
-
.describe('Location
|
|
113
|
-
asset_id: z
|
|
122
|
+
.describe('Location this work order is for — resolve last via list_locations filtered by building_id. The server mirrors it into location_ids, so send this OR location_ids, not a conflicting pair.'),
|
|
123
|
+
asset_id: z
|
|
124
|
+
.string()
|
|
125
|
+
.uuid()
|
|
126
|
+
.optional()
|
|
127
|
+
.describe('Asset this work order is for — resolve via list_assets. The server mirrors it into asset_ids.'),
|
|
128
|
+
location_ids: ASSOCIATION_ARRAY_SCHEMA('Locations this work order covers — use instead of location_id when there is more than one. Sending location_id alone replaces this with that single id.'),
|
|
129
|
+
asset_ids: ASSOCIATION_ARRAY_SCHEMA('Assets this work order covers — use instead of asset_id when there is more than one.'),
|
|
130
|
+
system_ids: ASSOCIATION_ARRAY_SCHEMA('Systems this work order covers — resolve via list_systems. Systems have no singular field; this array is the only way to associate them.'),
|
|
114
131
|
start_date: z.string().optional().describe('Start date (ISO 8601)'),
|
|
115
132
|
due_date: z.string().optional().describe('Due date (ISO 8601)'),
|
|
116
133
|
estimated_time: z.number().min(0).optional().describe('Estimated time in hours'),
|
|
@@ -161,8 +178,15 @@ export function registerWriteTools(server, client) {
|
|
|
161
178
|
.string()
|
|
162
179
|
.uuid()
|
|
163
180
|
.optional()
|
|
164
|
-
.describe('Location
|
|
165
|
-
asset_id: z
|
|
181
|
+
.describe('Location this work order is for — resolve last via list_locations filtered by building_id. The server mirrors it into location_ids, so send this OR location_ids, not a conflicting pair.'),
|
|
182
|
+
asset_id: z
|
|
183
|
+
.string()
|
|
184
|
+
.uuid()
|
|
185
|
+
.optional()
|
|
186
|
+
.describe('Asset this work order is for — resolve via list_assets. The server mirrors it into asset_ids.'),
|
|
187
|
+
location_ids: ASSOCIATION_ARRAY_SCHEMA('Locations this work order covers — use instead of location_id when there is more than one. Sending location_id alone replaces this with that single id.'),
|
|
188
|
+
asset_ids: ASSOCIATION_ARRAY_SCHEMA('Assets this work order covers — use instead of asset_id when there is more than one.'),
|
|
189
|
+
system_ids: ASSOCIATION_ARRAY_SCHEMA('Systems this work order covers — resolve via list_systems. Systems have no singular field; this array is the only way to associate them.'),
|
|
166
190
|
start_date: z.string().optional().describe('Start date (ISO 8601)'),
|
|
167
191
|
due_date: z.string().optional().describe('Due date (ISO 8601)'),
|
|
168
192
|
estimated_time: z.number().min(0).optional().describe('Estimated time in hours'),
|
|
@@ -897,9 +921,9 @@ export function registerWriteTools(server, client) {
|
|
|
897
921
|
.describe('Meter interval — trigger every N units'),
|
|
898
922
|
meter_unit: z.string().max(50).optional().describe('Meter unit (km, miles, hours, cycles)'),
|
|
899
923
|
start_date: z.string().optional().describe('Start date (ISO 8601)'),
|
|
900
|
-
asset_ids:
|
|
901
|
-
system_ids:
|
|
902
|
-
location_ids:
|
|
924
|
+
asset_ids: ASSOCIATION_ARRAY_SCHEMA('Assets this schedule covers — use instead of asset_id when there is more than one.'),
|
|
925
|
+
system_ids: ASSOCIATION_ARRAY_SCHEMA('Systems this schedule covers. Systems have no singular field; this array is the only way to associate them.'),
|
|
926
|
+
location_ids: ASSOCIATION_ARRAY_SCHEMA('Locations this schedule covers — use instead of location_id when there is more than one. Sending location_id alone replaces this with that single id.'),
|
|
903
927
|
tasks: z
|
|
904
928
|
.array(z.object({
|
|
905
929
|
id: z.string().describe('Unique task ID (use a random string)'),
|
|
@@ -908,6 +932,7 @@ export function registerWriteTools(server, client) {
|
|
|
908
932
|
}))
|
|
909
933
|
.optional()
|
|
910
934
|
.describe('Checklist of tasks for this PM schedule'),
|
|
935
|
+
form_template_id: FORM_TEMPLATE_LINK_SCHEMA,
|
|
911
936
|
}, async (params) => {
|
|
912
937
|
try {
|
|
913
938
|
const result = await client.create('pm-schedules', buildBody(params));
|
|
@@ -971,9 +996,9 @@ export function registerWriteTools(server, client) {
|
|
|
971
996
|
.describe('Meter interval — trigger every N units'),
|
|
972
997
|
meter_unit: z.string().max(50).optional().describe('Meter unit (km, miles, hours, cycles)'),
|
|
973
998
|
start_date: z.string().optional().describe('Start date (ISO 8601)'),
|
|
974
|
-
asset_ids:
|
|
975
|
-
system_ids:
|
|
976
|
-
location_ids:
|
|
999
|
+
asset_ids: ASSOCIATION_ARRAY_SCHEMA('Assets this schedule covers — use instead of asset_id when there is more than one.'),
|
|
1000
|
+
system_ids: ASSOCIATION_ARRAY_SCHEMA('Systems this schedule covers. Systems have no singular field; this array is the only way to associate them.'),
|
|
1001
|
+
location_ids: ASSOCIATION_ARRAY_SCHEMA('Locations this schedule covers — use instead of location_id when there is more than one. Sending location_id alone replaces this with that single id.'),
|
|
977
1002
|
tasks: z
|
|
978
1003
|
.array(z.object({
|
|
979
1004
|
id: z.string().describe('Unique task ID'),
|
|
@@ -982,6 +1007,7 @@ export function registerWriteTools(server, client) {
|
|
|
982
1007
|
}))
|
|
983
1008
|
.optional()
|
|
984
1009
|
.describe('Checklist of tasks for this PM schedule'),
|
|
1010
|
+
form_template_id: FORM_TEMPLATE_LINK_SCHEMA,
|
|
985
1011
|
}, async ({ id, ...rest }) => {
|
|
986
1012
|
try {
|
|
987
1013
|
const result = await client.update('pm-schedules', id, buildBody(rest));
|
|
@@ -1066,6 +1092,7 @@ export function registerWriteTools(server, client) {
|
|
|
1066
1092
|
.array(z.string().uuid())
|
|
1067
1093
|
.optional()
|
|
1068
1094
|
.describe('Default location IDs to seed on derived schedules'),
|
|
1095
|
+
form_template_id: FORM_TEMPLATE_LINK_SCHEMA,
|
|
1069
1096
|
}, async (params) => {
|
|
1070
1097
|
try {
|
|
1071
1098
|
const result = await client.create('pm-templates', buildBody(normalizePmTemplateBody(params)));
|
|
@@ -1129,6 +1156,7 @@ export function registerWriteTools(server, client) {
|
|
|
1129
1156
|
documents: z.array(z.record(z.unknown())).optional().describe('Document references'),
|
|
1130
1157
|
asset_ids: z.array(z.string().uuid()).optional().describe('Default asset IDs'),
|
|
1131
1158
|
location_ids: z.array(z.string().uuid()).optional().describe('Default location IDs'),
|
|
1159
|
+
form_template_id: FORM_TEMPLATE_LINK_SCHEMA,
|
|
1132
1160
|
}, async ({ id, ...rest }) => {
|
|
1133
1161
|
try {
|
|
1134
1162
|
const result = await client.update('pm-templates', id, buildBody(normalizePmTemplateBody(rest)));
|
|
@@ -1153,10 +1181,11 @@ export function registerWriteTools(server, client) {
|
|
|
1153
1181
|
server.tool('create_form_template', 'Create a form template — a reusable inspection, checklist, compliance, or survey definition. Step 1 of building a form: create it as a draft, then add questions with create_form_template_item (or bulk_create on form-template-items), then publish with update_form_template status=published (publishing is rejected if any question is malformed). Requires form_templates:write scope.', {
|
|
1154
1182
|
name: z.string().min(1).max(500).describe('Form template name (required)'),
|
|
1155
1183
|
description: z.string().max(5000).optional().describe('Description'),
|
|
1156
|
-
|
|
1157
|
-
.
|
|
1184
|
+
work_category_id: z
|
|
1185
|
+
.string()
|
|
1186
|
+
.uuid()
|
|
1158
1187
|
.optional()
|
|
1159
|
-
.describe('
|
|
1188
|
+
.describe('Work category ID — the same tenant-configured categories used by work orders (e.g. Electrical, Plumbing, HVAC). Look them up with list_work_categories and pick the closest match; omit if none fits.'),
|
|
1160
1189
|
status: z
|
|
1161
1190
|
.enum(FORM_TEMPLATE_STATUSES)
|
|
1162
1191
|
.optional()
|
|
@@ -1174,10 +1203,11 @@ export function registerWriteTools(server, client) {
|
|
|
1174
1203
|
id: z.string().uuid().describe('Form template ID'),
|
|
1175
1204
|
name: z.string().min(1).max(500).optional().describe('Form template name'),
|
|
1176
1205
|
description: z.string().max(5000).optional().describe('Description'),
|
|
1177
|
-
|
|
1178
|
-
.
|
|
1206
|
+
work_category_id: z
|
|
1207
|
+
.string()
|
|
1208
|
+
.uuid()
|
|
1179
1209
|
.optional()
|
|
1180
|
-
.describe('
|
|
1210
|
+
.describe('Work category ID (look up with list_work_categories)'),
|
|
1181
1211
|
status: z
|
|
1182
1212
|
.enum(FORM_TEMPLATE_STATUSES)
|
|
1183
1213
|
.optional()
|
|
@@ -1286,6 +1316,44 @@ export function registerWriteTools(server, client) {
|
|
|
1286
1316
|
}
|
|
1287
1317
|
});
|
|
1288
1318
|
// ============================================================
|
|
1319
|
+
// 8d. Form responses — attach / detach (scope: form_responses)
|
|
1320
|
+
// ============================================================
|
|
1321
|
+
server.tool('create_form_response', "Attach a published form to one record so it can be filled in — the direct way to put an inspection or checklist on an existing work order. The form's questions are snapshotted at attach time, so later edits to the template never change a form already in progress. To put a form on every work order a PM schedule generates, set form_template_id on the schedule instead of calling this per work order. Answering and completing the form happen in the AssetLab app, not through this API. Requires form_responses:write scope.", {
|
|
1322
|
+
template_id: z
|
|
1323
|
+
.string()
|
|
1324
|
+
.uuid()
|
|
1325
|
+
.describe('Published form template ID — resolve via list_form_templates. A draft or archived template is rejected; publish it first with update_form_template status="published".'),
|
|
1326
|
+
subject_type: z
|
|
1327
|
+
.enum(['work_order', 'pm_schedule', 'infrastructure_asset', 'compliance_record', 'site'])
|
|
1328
|
+
.describe('What kind of record the form is being attached to'),
|
|
1329
|
+
subject_id: z
|
|
1330
|
+
.string()
|
|
1331
|
+
.uuid()
|
|
1332
|
+
.describe('ID of the record — resolve via the matching list tool (list_work_orders, list_pm_schedules, list_infrastructure_assets, list_compliance_records, list_sites)'),
|
|
1333
|
+
}, async (params) => {
|
|
1334
|
+
try {
|
|
1335
|
+
const result = await client.create('form-responses', buildBody(params));
|
|
1336
|
+
return formatResult(result);
|
|
1337
|
+
}
|
|
1338
|
+
catch (err) {
|
|
1339
|
+
return formatError(err);
|
|
1340
|
+
}
|
|
1341
|
+
});
|
|
1342
|
+
server.tool('delete_form_response', 'Remove a form from the record it is attached to, along with any answers already given. Use this before attaching a different form, since a record holds at most one form. Requires form_responses:write scope.', {
|
|
1343
|
+
id: z
|
|
1344
|
+
.string()
|
|
1345
|
+
.uuid()
|
|
1346
|
+
.describe('Form response ID — resolve via list_form_responses filtered by subject_id'),
|
|
1347
|
+
}, async ({ id }) => {
|
|
1348
|
+
try {
|
|
1349
|
+
const result = await client.remove('form-responses', id);
|
|
1350
|
+
return formatResult(result);
|
|
1351
|
+
}
|
|
1352
|
+
catch (err) {
|
|
1353
|
+
return formatError(err);
|
|
1354
|
+
}
|
|
1355
|
+
});
|
|
1356
|
+
// ============================================================
|
|
1289
1357
|
// 9. Projects (scope: projects)
|
|
1290
1358
|
// ============================================================
|
|
1291
1359
|
server.tool('create_project', 'Create a new project. Requires projects:write scope.', {
|
|
@@ -2247,9 +2315,10 @@ export function registerWriteTools(server, client) {
|
|
|
2247
2315
|
user_id: z.string().min(1).describe('User ID (required)'),
|
|
2248
2316
|
start_time: z.string().describe('Start time (ISO 8601 datetime, required)'),
|
|
2249
2317
|
end_time: z.string().optional().describe('End time (ISO 8601 datetime)'),
|
|
2250
|
-
|
|
2318
|
+
duration_minutes: z.number().min(0).optional().describe('Duration worked, in minutes'),
|
|
2251
2319
|
description: z.string().optional().describe('Description'),
|
|
2252
|
-
|
|
2320
|
+
user_name: z.string().max(200).optional().describe('Display name of the user'),
|
|
2321
|
+
is_billable: z.boolean().optional().describe('Whether the time is billable'),
|
|
2253
2322
|
}, async (params) => {
|
|
2254
2323
|
try {
|
|
2255
2324
|
const result = await client.create('project-time-entries', buildBody(params));
|
|
@@ -2266,9 +2335,10 @@ export function registerWriteTools(server, client) {
|
|
|
2266
2335
|
user_id: z.string().optional().describe('User ID'),
|
|
2267
2336
|
start_time: z.string().optional().describe('Start time (ISO 8601 datetime)'),
|
|
2268
2337
|
end_time: z.string().optional().describe('End time (ISO 8601 datetime)'),
|
|
2269
|
-
|
|
2338
|
+
duration_minutes: z.number().min(0).optional().describe('Duration worked, in minutes'),
|
|
2270
2339
|
description: z.string().optional().describe('Description'),
|
|
2271
|
-
|
|
2340
|
+
user_name: z.string().max(200).optional().describe('Display name of the user'),
|
|
2341
|
+
is_billable: z.boolean().optional().describe('Whether the time is billable'),
|
|
2272
2342
|
}, async ({ id, ...rest }) => {
|
|
2273
2343
|
try {
|
|
2274
2344
|
const result = await client.update('project-time-entries', id, buildBody(rest));
|
|
@@ -4280,6 +4350,7 @@ export function registerWriteTools(server, client) {
|
|
|
4280
4350
|
'pm-templates',
|
|
4281
4351
|
'form-templates',
|
|
4282
4352
|
'form-template-items',
|
|
4353
|
+
'form-responses',
|
|
4283
4354
|
'projects',
|
|
4284
4355
|
'contracts',
|
|
4285
4356
|
'invoices',
|
|
@@ -4508,44 +4579,45 @@ export function registerWriteTools(server, client) {
|
|
|
4508
4579
|
}
|
|
4509
4580
|
});
|
|
4510
4581
|
// ============================================================
|
|
4511
|
-
// Infrastructure
|
|
4582
|
+
// Infrastructure Feature Classes (scope: infrastructure_feature_classes)
|
|
4512
4583
|
// ============================================================
|
|
4513
4584
|
const INFRA_ASSET_CLASS_CATEGORIES = [
|
|
4514
|
-
'
|
|
4515
|
-
'sewer',
|
|
4585
|
+
'transportation',
|
|
4516
4586
|
'water',
|
|
4517
|
-
'
|
|
4587
|
+
'wastewater',
|
|
4588
|
+
'stormwater',
|
|
4589
|
+
'structures',
|
|
4518
4590
|
'electrical',
|
|
4519
4591
|
'telecom',
|
|
4520
|
-
'
|
|
4521
|
-
'
|
|
4592
|
+
'gas',
|
|
4593
|
+
'roadside',
|
|
4522
4594
|
'other',
|
|
4523
4595
|
];
|
|
4524
|
-
const
|
|
4596
|
+
const featureClassCode = z
|
|
4525
4597
|
.string()
|
|
4526
4598
|
.regex(/^[a-z][a-z0-9_]{0,49}$/)
|
|
4527
4599
|
.describe('Asset class code (lowercase snake_case, 1-50 chars)');
|
|
4528
|
-
server.tool('
|
|
4529
|
-
code:
|
|
4600
|
+
server.tool('create_infrastructure_feature_class', 'Create a tenant-defined infrastructure feature class. Builtin classes are managed by AssetLab and cannot be created via API (is_builtin is always forced to false). Requires infrastructure_feature_classes:write scope.', {
|
|
4601
|
+
code: featureClassCode,
|
|
4530
4602
|
name: z.string().max(200).describe('Display name (required)'),
|
|
4531
4603
|
category: z
|
|
4532
4604
|
.enum(INFRA_ASSET_CLASS_CATEGORIES)
|
|
4533
|
-
.describe('Category (
|
|
4605
|
+
.describe('Category — municipal service family (transportation, water, wastewater, stormwater, structures, electrical, telecom, gas, roadside, other)'),
|
|
4534
4606
|
description: z.string().optional().describe('Description'),
|
|
4535
4607
|
icon: z.string().max(50).optional().describe('Icon name'),
|
|
4536
4608
|
color: z.string().max(20).optional().describe('Display color (CSS color or hex)'),
|
|
4537
4609
|
sort_order: z.number().int().optional().describe('Sort order'),
|
|
4538
4610
|
}, async (params) => {
|
|
4539
4611
|
try {
|
|
4540
|
-
const result = await client.create('infrastructure-
|
|
4612
|
+
const result = await client.create('infrastructure-feature-classes', buildBody(params));
|
|
4541
4613
|
return formatResult(result);
|
|
4542
4614
|
}
|
|
4543
4615
|
catch (err) {
|
|
4544
4616
|
return formatError(err);
|
|
4545
4617
|
}
|
|
4546
4618
|
});
|
|
4547
|
-
server.tool('
|
|
4548
|
-
code:
|
|
4619
|
+
server.tool('update_infrastructure_feature_class', 'Update a tenant-defined infrastructure feature class (addressed by code). The `code`, `category`, and `is_builtin` fields are immutable; attempts to change them on a builtin class return 409. Requires infrastructure_feature_classes:write scope.', {
|
|
4620
|
+
code: featureClassCode,
|
|
4549
4621
|
name: z.string().max(200).optional().describe('Display name'),
|
|
4550
4622
|
description: z.string().optional().describe('Description'),
|
|
4551
4623
|
icon: z.string().max(50).optional().describe('Icon name'),
|
|
@@ -4553,16 +4625,16 @@ export function registerWriteTools(server, client) {
|
|
|
4553
4625
|
sort_order: z.number().int().optional().describe('Sort order'),
|
|
4554
4626
|
}, async ({ code, ...rest }) => {
|
|
4555
4627
|
try {
|
|
4556
|
-
const result = await client.update('infrastructure-
|
|
4628
|
+
const result = await client.update('infrastructure-feature-classes', code, buildBody(rest));
|
|
4557
4629
|
return formatResult(result);
|
|
4558
4630
|
}
|
|
4559
4631
|
catch (err) {
|
|
4560
4632
|
return formatError(err);
|
|
4561
4633
|
}
|
|
4562
4634
|
});
|
|
4563
|
-
server.tool('
|
|
4635
|
+
server.tool('delete_infrastructure_feature_class', 'Delete a tenant-defined infrastructure feature class by code. Builtin classes cannot be deleted. Classes referenced by any infrastructure network are protected by FK and cannot be deleted until those networks are reassigned. Requires infrastructure_feature_classes:write scope.', { code: featureClassCode }, async ({ code }) => {
|
|
4564
4636
|
try {
|
|
4565
|
-
const result = await client.remove('infrastructure-
|
|
4637
|
+
const result = await client.remove('infrastructure-feature-classes', code);
|
|
4566
4638
|
return formatResult(result);
|
|
4567
4639
|
}
|
|
4568
4640
|
catch (err) {
|
|
@@ -4572,9 +4644,9 @@ export function registerWriteTools(server, client) {
|
|
|
4572
4644
|
// ============================================================
|
|
4573
4645
|
// Infrastructure Networks (scope: infrastructure_networks)
|
|
4574
4646
|
// ============================================================
|
|
4575
|
-
server.tool('create_infrastructure_network', 'Create an infrastructure network — a named grouping of features bound to one
|
|
4647
|
+
server.tool('create_infrastructure_network', 'Create an infrastructure network — a named grouping of features bound to one feature class. Requires infrastructure_networks:write scope.', {
|
|
4576
4648
|
name: z.string().max(200).describe('Network name (required)'),
|
|
4577
|
-
|
|
4649
|
+
feature_class: featureClassCode.describe('Asset class code (must exist; resolve via list_infrastructure_feature_classes)'),
|
|
4578
4650
|
description: z.string().optional().describe('Description'),
|
|
4579
4651
|
color_scheme: z.string().max(50).optional().describe('Display color scheme'),
|
|
4580
4652
|
metadata: z.record(z.unknown()).optional().describe('Free-form JSON metadata'),
|
|
@@ -4590,7 +4662,7 @@ export function registerWriteTools(server, client) {
|
|
|
4590
4662
|
server.tool('update_infrastructure_network', 'Update an existing infrastructure network by ID. Requires infrastructure_networks:write scope.', {
|
|
4591
4663
|
id: z.string().uuid().describe('Infrastructure network ID'),
|
|
4592
4664
|
name: z.string().max(200).optional().describe('Network name'),
|
|
4593
|
-
|
|
4665
|
+
feature_class: featureClassCode.optional().describe('Asset class code'),
|
|
4594
4666
|
description: z.string().optional().describe('Description'),
|
|
4595
4667
|
color_scheme: z.string().max(50).optional().describe('Display color scheme'),
|
|
4596
4668
|
metadata: z.record(z.unknown()).optional().describe('Free-form JSON metadata'),
|