@reveldigital/mcp-graphql-proxy 2.0.0 → 2.2.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/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +62 -9
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/index.js +650 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/types/schema.graphql +389 -27
package/dist/index.js
CHANGED
|
@@ -533,6 +533,82 @@ mutation SendBulkDeviceCommands($input: BulkCommandInput) {
|
|
|
533
533
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
534
534
|
}
|
|
535
535
|
});
|
|
536
|
+
const groupMutations = [
|
|
537
|
+
{
|
|
538
|
+
toolName: 'create_device_group',
|
|
539
|
+
mutationField: 'createDeviceGroup',
|
|
540
|
+
mutationName: 'CreateDeviceGroup',
|
|
541
|
+
description: 'Create a new device group (organizational folder for player devices). Example: organize newly provisioned players into a "Lobby Displays" group.',
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
toolName: 'create_media_group',
|
|
545
|
+
mutationField: 'createMediaGroup',
|
|
546
|
+
mutationName: 'CreateMediaGroup',
|
|
547
|
+
description: 'Create a new media group (organizational folder for uploaded content files).',
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
toolName: 'create_playlist_group',
|
|
551
|
+
mutationField: 'createPlaylistGroup',
|
|
552
|
+
mutationName: 'CreatePlaylistGroup',
|
|
553
|
+
description: 'Create a new playlist group (organizational folder for playlists).',
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
toolName: 'create_schedule_group',
|
|
557
|
+
mutationField: 'createScheduleGroup',
|
|
558
|
+
mutationName: 'CreateScheduleGroup',
|
|
559
|
+
description: 'Create a new schedule group (organizational folder for schedules).',
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
toolName: 'create_template_group',
|
|
563
|
+
mutationField: 'createTemplateGroup',
|
|
564
|
+
mutationName: 'CreateTemplateGroup',
|
|
565
|
+
description: 'Create a new template group (organizational folder for screen layouts/templates).',
|
|
566
|
+
},
|
|
567
|
+
];
|
|
568
|
+
for (const { toolName, mutationField, mutationName, description } of groupMutations) {
|
|
569
|
+
server.tool(toolName, description, {
|
|
570
|
+
name: z.string().describe('The name of the new group'),
|
|
571
|
+
parentId: z.string().optional().describe('Optional parent group ID to nest this group under. If omitted, the group is created at the root level.'),
|
|
572
|
+
}, async (args) => {
|
|
573
|
+
try {
|
|
574
|
+
const mutation = `
|
|
575
|
+
mutation ${mutationName}($input: CreateGroupInput) {
|
|
576
|
+
${mutationField}(input: $input) {
|
|
577
|
+
success
|
|
578
|
+
group {
|
|
579
|
+
id
|
|
580
|
+
name
|
|
581
|
+
parentId
|
|
582
|
+
}
|
|
583
|
+
error
|
|
584
|
+
}
|
|
585
|
+
}`;
|
|
586
|
+
const input = { name: args.name };
|
|
587
|
+
if (args.parentId !== undefined)
|
|
588
|
+
input.parentId = args.parentId;
|
|
589
|
+
const response = await graphqlClient.query(mutation, { input });
|
|
590
|
+
if (response.errors && response.errors.length > 0) {
|
|
591
|
+
const result = {
|
|
592
|
+
success: false,
|
|
593
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
594
|
+
};
|
|
595
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
596
|
+
}
|
|
597
|
+
const result = {
|
|
598
|
+
success: true,
|
|
599
|
+
data: response.data,
|
|
600
|
+
};
|
|
601
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
602
|
+
}
|
|
603
|
+
catch (error) {
|
|
604
|
+
const result = {
|
|
605
|
+
success: false,
|
|
606
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
607
|
+
};
|
|
608
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
}
|
|
536
612
|
// ============================================================================
|
|
537
613
|
// Tool: Create Media From URL
|
|
538
614
|
// ============================================================================
|
|
@@ -1077,6 +1153,562 @@ mutation ReorderPlaylistSources($input: ReorderPlaylistSourcesInput) {
|
|
|
1077
1153
|
}
|
|
1078
1154
|
});
|
|
1079
1155
|
// ============================================================================
|
|
1156
|
+
// Tool: Update Template
|
|
1157
|
+
// ============================================================================
|
|
1158
|
+
server.tool('update_template', "Update a template's properties (name, group, tags, dimensions, colors, orientation, script). Only provided fields are changed. Example: AI adjusts template dimensions to optimize for a new display resolution.", {
|
|
1159
|
+
templateId: z.string().describe('The template ID to update'),
|
|
1160
|
+
name: z.string().optional().describe('Template name'),
|
|
1161
|
+
groupId: z.string().optional().describe('Group ID to assign the template to'),
|
|
1162
|
+
tags: z.string().optional().describe('Tags (newline-delimited values stored in the Description field)'),
|
|
1163
|
+
script: z.string().optional().describe('Template script for custom logic'),
|
|
1164
|
+
backColor: z.string().optional().describe("Background color (e.g., '#000000')"),
|
|
1165
|
+
width: z.number().optional().describe('Template width in pixels'),
|
|
1166
|
+
height: z.number().optional().describe('Template height in pixels'),
|
|
1167
|
+
orientation: z.number().optional().describe('Display orientation (0=Landscape, 1=Portrait, 2=Landscape Reversed, 3=Portrait Reversed)'),
|
|
1168
|
+
}, async (args) => {
|
|
1169
|
+
try {
|
|
1170
|
+
const mutation = `
|
|
1171
|
+
mutation UpdateTemplate($input: UpdateTemplateInput) {
|
|
1172
|
+
updateTemplate(input: $input) {
|
|
1173
|
+
success
|
|
1174
|
+
template {
|
|
1175
|
+
id
|
|
1176
|
+
name
|
|
1177
|
+
width
|
|
1178
|
+
height
|
|
1179
|
+
orientation
|
|
1180
|
+
modules {
|
|
1181
|
+
id
|
|
1182
|
+
name
|
|
1183
|
+
type
|
|
1184
|
+
left
|
|
1185
|
+
top
|
|
1186
|
+
width
|
|
1187
|
+
height
|
|
1188
|
+
playlistId
|
|
1189
|
+
sequence
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
error
|
|
1193
|
+
}
|
|
1194
|
+
}`;
|
|
1195
|
+
const input = { templateId: args.templateId };
|
|
1196
|
+
if (args.name !== undefined)
|
|
1197
|
+
input.name = args.name;
|
|
1198
|
+
if (args.groupId !== undefined)
|
|
1199
|
+
input.groupId = args.groupId;
|
|
1200
|
+
if (args.tags !== undefined)
|
|
1201
|
+
input.tags = args.tags;
|
|
1202
|
+
if (args.script !== undefined)
|
|
1203
|
+
input.script = args.script;
|
|
1204
|
+
if (args.backColor !== undefined)
|
|
1205
|
+
input.backColor = args.backColor;
|
|
1206
|
+
if (args.width !== undefined)
|
|
1207
|
+
input.width = args.width;
|
|
1208
|
+
if (args.height !== undefined)
|
|
1209
|
+
input.height = args.height;
|
|
1210
|
+
if (args.orientation !== undefined)
|
|
1211
|
+
input.orientation = args.orientation;
|
|
1212
|
+
const response = await graphqlClient.query(mutation, { input });
|
|
1213
|
+
if (response.errors && response.errors.length > 0) {
|
|
1214
|
+
const result = {
|
|
1215
|
+
success: false,
|
|
1216
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1217
|
+
};
|
|
1218
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1219
|
+
}
|
|
1220
|
+
const result = {
|
|
1221
|
+
success: true,
|
|
1222
|
+
data: response.data,
|
|
1223
|
+
};
|
|
1224
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1225
|
+
}
|
|
1226
|
+
catch (error) {
|
|
1227
|
+
const result = {
|
|
1228
|
+
success: false,
|
|
1229
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1230
|
+
};
|
|
1231
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1232
|
+
}
|
|
1233
|
+
});
|
|
1234
|
+
// ============================================================================
|
|
1235
|
+
// Tool: Add Template Module
|
|
1236
|
+
// ============================================================================
|
|
1237
|
+
server.tool('add_template_module', "Add a new module (zone/region) to a template. Modules define rectangular areas where content from a playlist is displayed. Example: AI adds a new weather module to show real-time conditions in the corner.", {
|
|
1238
|
+
templateId: z.string().describe('The template ID to add the module to'),
|
|
1239
|
+
module: z.object({
|
|
1240
|
+
name: z.string().optional().describe('Module/zone name'),
|
|
1241
|
+
type: z.string().optional().describe("Module/zone type (e.g., 'Playlist', 'Clock', 'Weather', 'Ticker', 'Web')"),
|
|
1242
|
+
left: z.number().optional().describe('Left position in pixels'),
|
|
1243
|
+
top: z.number().optional().describe('Top position in pixels'),
|
|
1244
|
+
width: z.number().optional().describe('Width in pixels'),
|
|
1245
|
+
height: z.number().optional().describe('Height in pixels'),
|
|
1246
|
+
playlistId: z.string().optional().describe('Playlist ID assigned to this module'),
|
|
1247
|
+
sequence: z.number().optional().describe('Z-order/sequence for overlapping modules (higher appears in front)'),
|
|
1248
|
+
}).describe('The module to add'),
|
|
1249
|
+
}, async (args) => {
|
|
1250
|
+
try {
|
|
1251
|
+
const mutation = `
|
|
1252
|
+
mutation AddTemplateModule($input: AddTemplateModuleInput) {
|
|
1253
|
+
addTemplateModule(input: $input) {
|
|
1254
|
+
success
|
|
1255
|
+
module {
|
|
1256
|
+
id
|
|
1257
|
+
name
|
|
1258
|
+
type
|
|
1259
|
+
left
|
|
1260
|
+
top
|
|
1261
|
+
width
|
|
1262
|
+
height
|
|
1263
|
+
playlistId
|
|
1264
|
+
sequence
|
|
1265
|
+
options {
|
|
1266
|
+
id
|
|
1267
|
+
name
|
|
1268
|
+
value
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
error
|
|
1272
|
+
}
|
|
1273
|
+
}`;
|
|
1274
|
+
const response = await graphqlClient.query(mutation, {
|
|
1275
|
+
input: {
|
|
1276
|
+
templateId: args.templateId,
|
|
1277
|
+
module: args.module,
|
|
1278
|
+
},
|
|
1279
|
+
});
|
|
1280
|
+
if (response.errors && response.errors.length > 0) {
|
|
1281
|
+
const result = {
|
|
1282
|
+
success: false,
|
|
1283
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1284
|
+
};
|
|
1285
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1286
|
+
}
|
|
1287
|
+
const result = {
|
|
1288
|
+
success: true,
|
|
1289
|
+
data: response.data,
|
|
1290
|
+
};
|
|
1291
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1292
|
+
}
|
|
1293
|
+
catch (error) {
|
|
1294
|
+
const result = {
|
|
1295
|
+
success: false,
|
|
1296
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1297
|
+
};
|
|
1298
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1299
|
+
}
|
|
1300
|
+
});
|
|
1301
|
+
// ============================================================================
|
|
1302
|
+
// Tool: Update Template Module
|
|
1303
|
+
// ============================================================================
|
|
1304
|
+
server.tool('update_template_module', 'Update a module (zone/region) within a template. Only provided fields are changed. Example: AI repositions a module based on engagement analytics, or changes the assigned playlist.', {
|
|
1305
|
+
templateId: z.string().describe('The template ID containing the module'),
|
|
1306
|
+
moduleId: z.string().describe('The module ID to update'),
|
|
1307
|
+
name: z.string().optional().describe('Module/zone name'),
|
|
1308
|
+
type: z.string().optional().describe("Module/zone type (e.g., 'Playlist', 'Clock', 'Weather', 'Ticker', 'Web')"),
|
|
1309
|
+
left: z.number().optional().describe('Left position in pixels'),
|
|
1310
|
+
top: z.number().optional().describe('Top position in pixels'),
|
|
1311
|
+
width: z.number().optional().describe('Width in pixels'),
|
|
1312
|
+
height: z.number().optional().describe('Height in pixels'),
|
|
1313
|
+
playlistId: z.string().optional().describe('Playlist ID assigned to this module'),
|
|
1314
|
+
sequence: z.number().optional().describe('Z-order/sequence for overlapping modules'),
|
|
1315
|
+
}, async (args) => {
|
|
1316
|
+
try {
|
|
1317
|
+
const mutation = `
|
|
1318
|
+
mutation UpdateTemplateModule($input: UpdateTemplateModuleInput) {
|
|
1319
|
+
updateTemplateModule(input: $input) {
|
|
1320
|
+
success
|
|
1321
|
+
module {
|
|
1322
|
+
id
|
|
1323
|
+
name
|
|
1324
|
+
type
|
|
1325
|
+
left
|
|
1326
|
+
top
|
|
1327
|
+
width
|
|
1328
|
+
height
|
|
1329
|
+
playlistId
|
|
1330
|
+
sequence
|
|
1331
|
+
options {
|
|
1332
|
+
id
|
|
1333
|
+
name
|
|
1334
|
+
value
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
error
|
|
1338
|
+
}
|
|
1339
|
+
}`;
|
|
1340
|
+
const input = {
|
|
1341
|
+
templateId: args.templateId,
|
|
1342
|
+
moduleId: args.moduleId,
|
|
1343
|
+
};
|
|
1344
|
+
if (args.name !== undefined)
|
|
1345
|
+
input.name = args.name;
|
|
1346
|
+
if (args.type !== undefined)
|
|
1347
|
+
input.type = args.type;
|
|
1348
|
+
if (args.left !== undefined)
|
|
1349
|
+
input.left = args.left;
|
|
1350
|
+
if (args.top !== undefined)
|
|
1351
|
+
input.top = args.top;
|
|
1352
|
+
if (args.width !== undefined)
|
|
1353
|
+
input.width = args.width;
|
|
1354
|
+
if (args.height !== undefined)
|
|
1355
|
+
input.height = args.height;
|
|
1356
|
+
if (args.playlistId !== undefined)
|
|
1357
|
+
input.playlistId = args.playlistId;
|
|
1358
|
+
if (args.sequence !== undefined)
|
|
1359
|
+
input.sequence = args.sequence;
|
|
1360
|
+
const response = await graphqlClient.query(mutation, { input });
|
|
1361
|
+
if (response.errors && response.errors.length > 0) {
|
|
1362
|
+
const result = {
|
|
1363
|
+
success: false,
|
|
1364
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1365
|
+
};
|
|
1366
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1367
|
+
}
|
|
1368
|
+
const result = {
|
|
1369
|
+
success: true,
|
|
1370
|
+
data: response.data,
|
|
1371
|
+
};
|
|
1372
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1373
|
+
}
|
|
1374
|
+
catch (error) {
|
|
1375
|
+
const result = {
|
|
1376
|
+
success: false,
|
|
1377
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1378
|
+
};
|
|
1379
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1382
|
+
// ============================================================================
|
|
1383
|
+
// Tool: Remove Template Module
|
|
1384
|
+
// ============================================================================
|
|
1385
|
+
server.tool('remove_template_module', 'Remove a module (zone/region) from a template by module ID. Example: AI removes an underperforming ad module to optimize content flow.', {
|
|
1386
|
+
templateId: z.string().describe('The template ID containing the module'),
|
|
1387
|
+
moduleId: z.string().describe('The module ID to remove'),
|
|
1388
|
+
}, async (args) => {
|
|
1389
|
+
try {
|
|
1390
|
+
const mutation = `
|
|
1391
|
+
mutation RemoveTemplateModule($input: RemoveTemplateModuleInput) {
|
|
1392
|
+
removeTemplateModule(input: $input) {
|
|
1393
|
+
success
|
|
1394
|
+
module {
|
|
1395
|
+
id
|
|
1396
|
+
name
|
|
1397
|
+
type
|
|
1398
|
+
}
|
|
1399
|
+
error
|
|
1400
|
+
}
|
|
1401
|
+
}`;
|
|
1402
|
+
const response = await graphqlClient.query(mutation, {
|
|
1403
|
+
input: {
|
|
1404
|
+
templateId: args.templateId,
|
|
1405
|
+
moduleId: args.moduleId,
|
|
1406
|
+
},
|
|
1407
|
+
});
|
|
1408
|
+
if (response.errors && response.errors.length > 0) {
|
|
1409
|
+
const result = {
|
|
1410
|
+
success: false,
|
|
1411
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1412
|
+
};
|
|
1413
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1414
|
+
}
|
|
1415
|
+
const result = {
|
|
1416
|
+
success: true,
|
|
1417
|
+
data: response.data,
|
|
1418
|
+
};
|
|
1419
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1420
|
+
}
|
|
1421
|
+
catch (error) {
|
|
1422
|
+
const result = {
|
|
1423
|
+
success: false,
|
|
1424
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1425
|
+
};
|
|
1426
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1427
|
+
}
|
|
1428
|
+
});
|
|
1429
|
+
// ============================================================================
|
|
1430
|
+
// Tool: Reorder Template Modules
|
|
1431
|
+
// ============================================================================
|
|
1432
|
+
server.tool('reorder_template_modules', 'Reorder modules (zones) within a template by updating their sequence/z-order. Higher sequence values appear in front. Example: AI reorders modules to bring important content to the front based on viewer engagement.', {
|
|
1433
|
+
templateId: z.string().describe('The template ID'),
|
|
1434
|
+
moduleSequences: z.array(z.object({
|
|
1435
|
+
moduleId: z.string().describe('The module ID'),
|
|
1436
|
+
sequence: z.number().describe('The new sequence/z-order value'),
|
|
1437
|
+
})).describe('List of module IDs with their new sequence numbers'),
|
|
1438
|
+
}, async (args) => {
|
|
1439
|
+
try {
|
|
1440
|
+
const mutation = `
|
|
1441
|
+
mutation ReorderTemplateModules($input: ReorderTemplateModulesInput) {
|
|
1442
|
+
reorderTemplateModules(input: $input) {
|
|
1443
|
+
success
|
|
1444
|
+
template {
|
|
1445
|
+
id
|
|
1446
|
+
name
|
|
1447
|
+
modules {
|
|
1448
|
+
id
|
|
1449
|
+
name
|
|
1450
|
+
type
|
|
1451
|
+
sequence
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
error
|
|
1455
|
+
}
|
|
1456
|
+
}`;
|
|
1457
|
+
const response = await graphqlClient.query(mutation, {
|
|
1458
|
+
input: {
|
|
1459
|
+
templateId: args.templateId,
|
|
1460
|
+
moduleSequences: args.moduleSequences,
|
|
1461
|
+
},
|
|
1462
|
+
});
|
|
1463
|
+
if (response.errors && response.errors.length > 0) {
|
|
1464
|
+
const result = {
|
|
1465
|
+
success: false,
|
|
1466
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1467
|
+
};
|
|
1468
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1469
|
+
}
|
|
1470
|
+
const result = {
|
|
1471
|
+
success: true,
|
|
1472
|
+
data: response.data,
|
|
1473
|
+
};
|
|
1474
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1475
|
+
}
|
|
1476
|
+
catch (error) {
|
|
1477
|
+
const result = {
|
|
1478
|
+
success: false,
|
|
1479
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1480
|
+
};
|
|
1481
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1482
|
+
}
|
|
1483
|
+
});
|
|
1484
|
+
// ============================================================================
|
|
1485
|
+
// Tool: Batch Update Template Modules
|
|
1486
|
+
// ============================================================================
|
|
1487
|
+
server.tool('batch_update_template_modules', 'Batch update multiple modules in a template at once. Each entry must include the moduleId; only provided fields are changed. Example: AI repositions all modules to create a new layout optimized for viewer attention.', {
|
|
1488
|
+
templateId: z.string().describe('The template ID'),
|
|
1489
|
+
modules: z.array(z.object({
|
|
1490
|
+
moduleId: z.string().describe('The module ID to update'),
|
|
1491
|
+
name: z.string().optional().describe('Module/zone name'),
|
|
1492
|
+
type: z.string().optional().describe('Module/zone type'),
|
|
1493
|
+
left: z.number().optional().describe('Left position in pixels'),
|
|
1494
|
+
top: z.number().optional().describe('Top position in pixels'),
|
|
1495
|
+
width: z.number().optional().describe('Width in pixels'),
|
|
1496
|
+
height: z.number().optional().describe('Height in pixels'),
|
|
1497
|
+
playlistId: z.string().optional().describe('Playlist ID assigned to this module'),
|
|
1498
|
+
sequence: z.number().optional().describe('Z-order/sequence'),
|
|
1499
|
+
})).describe('List of module updates to apply (each requires moduleId)'),
|
|
1500
|
+
}, async (args) => {
|
|
1501
|
+
try {
|
|
1502
|
+
const mutation = `
|
|
1503
|
+
mutation BatchUpdateTemplateModules($input: BatchUpdateTemplateModulesInput) {
|
|
1504
|
+
batchUpdateTemplateModules(input: $input) {
|
|
1505
|
+
totalModules
|
|
1506
|
+
successCount
|
|
1507
|
+
failureCount
|
|
1508
|
+
results {
|
|
1509
|
+
success
|
|
1510
|
+
module {
|
|
1511
|
+
id
|
|
1512
|
+
name
|
|
1513
|
+
type
|
|
1514
|
+
left
|
|
1515
|
+
top
|
|
1516
|
+
width
|
|
1517
|
+
height
|
|
1518
|
+
playlistId
|
|
1519
|
+
sequence
|
|
1520
|
+
}
|
|
1521
|
+
error
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
}`;
|
|
1525
|
+
const modules = args.modules.map((m) => ({ ...m, templateId: args.templateId }));
|
|
1526
|
+
const response = await graphqlClient.query(mutation, {
|
|
1527
|
+
input: {
|
|
1528
|
+
templateId: args.templateId,
|
|
1529
|
+
modules,
|
|
1530
|
+
},
|
|
1531
|
+
});
|
|
1532
|
+
if (response.errors && response.errors.length > 0) {
|
|
1533
|
+
const result = {
|
|
1534
|
+
success: false,
|
|
1535
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1536
|
+
};
|
|
1537
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1538
|
+
}
|
|
1539
|
+
const result = {
|
|
1540
|
+
success: true,
|
|
1541
|
+
data: response.data,
|
|
1542
|
+
};
|
|
1543
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1544
|
+
}
|
|
1545
|
+
catch (error) {
|
|
1546
|
+
const result = {
|
|
1547
|
+
success: false,
|
|
1548
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1549
|
+
};
|
|
1550
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1551
|
+
}
|
|
1552
|
+
});
|
|
1553
|
+
// ============================================================================
|
|
1554
|
+
// Tool: Set Module Options
|
|
1555
|
+
// ============================================================================
|
|
1556
|
+
server.tool('set_module_options', 'Set all configuration options for a module/zone (replaces existing options). Options are key-value pairs storing type-specific settings. Example: AI configures a weather module with location, units, and API key settings.', {
|
|
1557
|
+
templateId: z.string().describe('The template ID'),
|
|
1558
|
+
moduleId: z.string().describe('The module ID'),
|
|
1559
|
+
options: z.array(z.object({
|
|
1560
|
+
name: z.string().describe("Option name/key (e.g., 'timezone', 'location', 'url')"),
|
|
1561
|
+
value: z.string().describe('Option value (string representation, parsed by option type)'),
|
|
1562
|
+
})).describe('List of options to set (replaces all existing options)'),
|
|
1563
|
+
}, async (args) => {
|
|
1564
|
+
try {
|
|
1565
|
+
const mutation = `
|
|
1566
|
+
mutation SetModuleOptions($input: SetModuleOptionsInput) {
|
|
1567
|
+
setModuleOptions(input: $input) {
|
|
1568
|
+
success
|
|
1569
|
+
module {
|
|
1570
|
+
id
|
|
1571
|
+
name
|
|
1572
|
+
type
|
|
1573
|
+
options {
|
|
1574
|
+
id
|
|
1575
|
+
name
|
|
1576
|
+
value
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
error
|
|
1580
|
+
}
|
|
1581
|
+
}`;
|
|
1582
|
+
const response = await graphqlClient.query(mutation, {
|
|
1583
|
+
input: {
|
|
1584
|
+
templateId: args.templateId,
|
|
1585
|
+
moduleId: args.moduleId,
|
|
1586
|
+
options: args.options,
|
|
1587
|
+
},
|
|
1588
|
+
});
|
|
1589
|
+
if (response.errors && response.errors.length > 0) {
|
|
1590
|
+
const result = {
|
|
1591
|
+
success: false,
|
|
1592
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1593
|
+
};
|
|
1594
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1595
|
+
}
|
|
1596
|
+
const result = {
|
|
1597
|
+
success: true,
|
|
1598
|
+
data: response.data,
|
|
1599
|
+
};
|
|
1600
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1601
|
+
}
|
|
1602
|
+
catch (error) {
|
|
1603
|
+
const result = {
|
|
1604
|
+
success: false,
|
|
1605
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1606
|
+
};
|
|
1607
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1608
|
+
}
|
|
1609
|
+
});
|
|
1610
|
+
// ============================================================================
|
|
1611
|
+
// Tool: Upsert Module Option
|
|
1612
|
+
// ============================================================================
|
|
1613
|
+
server.tool('upsert_module_option', 'Add or update a single module/zone option (matched by name). Example: AI updates the refresh interval for a web module.', {
|
|
1614
|
+
templateId: z.string().describe('The template ID'),
|
|
1615
|
+
moduleId: z.string().describe('The module ID'),
|
|
1616
|
+
option: z.object({
|
|
1617
|
+
name: z.string().describe("Option name/key (e.g., 'timezone', 'location', 'url')"),
|
|
1618
|
+
value: z.string().describe('Option value (string representation, parsed by option type)'),
|
|
1619
|
+
}).describe('The option to create or update (matched by name)'),
|
|
1620
|
+
}, async (args) => {
|
|
1621
|
+
try {
|
|
1622
|
+
const mutation = `
|
|
1623
|
+
mutation UpsertModuleOption($input: UpsertModuleOptionInput) {
|
|
1624
|
+
upsertModuleOption(input: $input) {
|
|
1625
|
+
success
|
|
1626
|
+
option {
|
|
1627
|
+
id
|
|
1628
|
+
name
|
|
1629
|
+
value
|
|
1630
|
+
}
|
|
1631
|
+
error
|
|
1632
|
+
}
|
|
1633
|
+
}`;
|
|
1634
|
+
const response = await graphqlClient.query(mutation, {
|
|
1635
|
+
input: {
|
|
1636
|
+
templateId: args.templateId,
|
|
1637
|
+
moduleId: args.moduleId,
|
|
1638
|
+
option: args.option,
|
|
1639
|
+
},
|
|
1640
|
+
});
|
|
1641
|
+
if (response.errors && response.errors.length > 0) {
|
|
1642
|
+
const result = {
|
|
1643
|
+
success: false,
|
|
1644
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1645
|
+
};
|
|
1646
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1647
|
+
}
|
|
1648
|
+
const result = {
|
|
1649
|
+
success: true,
|
|
1650
|
+
data: response.data,
|
|
1651
|
+
};
|
|
1652
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1653
|
+
}
|
|
1654
|
+
catch (error) {
|
|
1655
|
+
const result = {
|
|
1656
|
+
success: false,
|
|
1657
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1658
|
+
};
|
|
1659
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1660
|
+
}
|
|
1661
|
+
});
|
|
1662
|
+
// ============================================================================
|
|
1663
|
+
// Tool: Remove Module Option
|
|
1664
|
+
// ============================================================================
|
|
1665
|
+
server.tool('remove_module_option', 'Remove a module/zone option by name. Example: AI removes an outdated configuration setting from a module.', {
|
|
1666
|
+
templateId: z.string().describe('The template ID'),
|
|
1667
|
+
moduleId: z.string().describe('The module ID'),
|
|
1668
|
+
optionName: z.string().describe('The option name to remove'),
|
|
1669
|
+
}, async (args) => {
|
|
1670
|
+
try {
|
|
1671
|
+
const mutation = `
|
|
1672
|
+
mutation RemoveModuleOption($input: RemoveModuleOptionInput) {
|
|
1673
|
+
removeModuleOption(input: $input) {
|
|
1674
|
+
success
|
|
1675
|
+
option {
|
|
1676
|
+
id
|
|
1677
|
+
name
|
|
1678
|
+
value
|
|
1679
|
+
}
|
|
1680
|
+
error
|
|
1681
|
+
}
|
|
1682
|
+
}`;
|
|
1683
|
+
const response = await graphqlClient.query(mutation, {
|
|
1684
|
+
input: {
|
|
1685
|
+
templateId: args.templateId,
|
|
1686
|
+
moduleId: args.moduleId,
|
|
1687
|
+
optionName: args.optionName,
|
|
1688
|
+
},
|
|
1689
|
+
});
|
|
1690
|
+
if (response.errors && response.errors.length > 0) {
|
|
1691
|
+
const result = {
|
|
1692
|
+
success: false,
|
|
1693
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1694
|
+
};
|
|
1695
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1696
|
+
}
|
|
1697
|
+
const result = {
|
|
1698
|
+
success: true,
|
|
1699
|
+
data: response.data,
|
|
1700
|
+
};
|
|
1701
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1702
|
+
}
|
|
1703
|
+
catch (error) {
|
|
1704
|
+
const result = {
|
|
1705
|
+
success: false,
|
|
1706
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1707
|
+
};
|
|
1708
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1709
|
+
}
|
|
1710
|
+
});
|
|
1711
|
+
// ============================================================================
|
|
1080
1712
|
// Tool: List Data Tables
|
|
1081
1713
|
// ============================================================================
|
|
1082
1714
|
server.tool('list_data_tables', 'List data tables in the account. Returns table summaries with pagination. Use to discover available tables before querying rows.', {
|
|
@@ -2095,6 +2727,13 @@ Raw logs return too much data for AI context windows.
|
|
|
2095
2727
|
- send_device_command - Send commands to a specific device
|
|
2096
2728
|
- send_bulk_device_commands - Send commands to multiple devices at once
|
|
2097
2729
|
|
|
2730
|
+
### Group Management
|
|
2731
|
+
- create_device_group - Create a new device group (optional parentId for nesting)
|
|
2732
|
+
- create_media_group - Create a new media group (optional parentId for nesting)
|
|
2733
|
+
- create_playlist_group - Create a new playlist group (optional parentId for nesting)
|
|
2734
|
+
- create_schedule_group - Create a new schedule group (optional parentId for nesting)
|
|
2735
|
+
- create_template_group - Create a new template group (optional parentId for nesting)
|
|
2736
|
+
|
|
2098
2737
|
### Media Management
|
|
2099
2738
|
- create_media_from_url - Create media by downloading from a URL
|
|
2100
2739
|
- update_media - Update media metadata (name, dates, group)
|
|
@@ -2108,6 +2747,17 @@ Raw logs return too much data for AI context windows.
|
|
|
2108
2747
|
- remove_playlist_source - Remove a source from a playlist
|
|
2109
2748
|
- reorder_playlist_sources - Reorder sources within a playlist
|
|
2110
2749
|
|
|
2750
|
+
### Template Management
|
|
2751
|
+
- update_template - Update a template's properties (name, group, tags, dimensions, colors, orientation, script)
|
|
2752
|
+
- add_template_module - Add a new module (zone) to a template
|
|
2753
|
+
- update_template_module - Update a module within a template
|
|
2754
|
+
- remove_template_module - Remove a module from a template
|
|
2755
|
+
- reorder_template_modules - Reorder modules within a template (sequence/z-order)
|
|
2756
|
+
- batch_update_template_modules - Update multiple modules in a template at once
|
|
2757
|
+
- set_module_options - Set all options for a module (replaces existing)
|
|
2758
|
+
- upsert_module_option - Add or update a single module option (matched by name)
|
|
2759
|
+
- remove_module_option - Remove a module option by name
|
|
2760
|
+
|
|
2111
2761
|
### Data Table Management
|
|
2112
2762
|
- create_data_table - Create a new data table with column schema
|
|
2113
2763
|
- update_data_table - Update table definition (partial update)
|