@reveldigital/mcp-graphql-proxy 1.16.0 → 1.18.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/query-builder.d.ts.map +1 -1
- package/dist/core/query-builder.js +38 -0
- package/dist/core/query-builder.js.map +1 -1
- package/dist/core/system-prompt.d.ts +23 -0
- package/dist/core/system-prompt.d.ts.map +1 -0
- package/dist/core/system-prompt.js +583 -0
- package/dist/core/system-prompt.js.map +1 -0
- package/dist/index.js +1023 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/types/schema.graphql +339 -0
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import { GraphQLClient } from './core/graphql-client.js';
|
|
|
19
19
|
import { ResponseCache } from './core/cache.js';
|
|
20
20
|
import { QueryBuilder } from './core/query-builder.js';
|
|
21
21
|
import { ResponseOptimizer } from './core/response-optimizer.js';
|
|
22
|
+
import { generateSystemPrompt, generateCompactPrompt } from './core/system-prompt.js';
|
|
22
23
|
import { defaultGraphQLProxyConfig, } from './types/graphql-proxy.js';
|
|
23
24
|
// ============================================================================
|
|
24
25
|
// Server Configuration
|
|
@@ -121,6 +122,8 @@ server.tool('build_query', 'Build an optimized GraphQL query with field selectio
|
|
|
121
122
|
'alert',
|
|
122
123
|
// Audit & Compliance
|
|
123
124
|
'auditEvent',
|
|
125
|
+
// Data Tables
|
|
126
|
+
'dataTables', 'dataTable',
|
|
124
127
|
// Device Commands & Permissions
|
|
125
128
|
'displayCommands', 'permissions',
|
|
126
129
|
// Analytics (aggregated metrics - optimized for AI context windows)
|
|
@@ -696,15 +699,42 @@ server.tool('add_playlist_source', 'Add a content source to an existing playlist
|
|
|
696
699
|
source: z.object({
|
|
697
700
|
name: z.string().optional().describe('Display name for the source'),
|
|
698
701
|
type: z.enum([
|
|
699
|
-
'
|
|
700
|
-
'
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
702
|
+
'AUDIO', 'COMMAND', 'FLASH', 'GADGET', 'IMAGE', 'PDF',
|
|
703
|
+
'PLACE_EXCHANGE', 'PLAYLIST', 'POWER_POINT', 'RSS', 'SVG',
|
|
704
|
+
'TEMPLATE', 'TEXT', 'TWITTER', 'URL', 'VIDEO',
|
|
705
|
+
'VISTAR_MEDIA', 'WEB_PAGE', 'YOU_TUBE', 'VISTAR_MEDIA_EX',
|
|
706
|
+
]).describe('The content type (e.g., IMAGE, VIDEO, TEMPLATE, PLAYLIST, URL, WEB_PAGE, YOU_TUBE)'),
|
|
707
|
+
mediaId: z.string().optional().describe('Media file ID (for IMAGE, VIDEO, AUDIO, PDF, SVG types)'),
|
|
708
|
+
templateId: z.string().optional().describe('Template ID (for TEMPLATE type)'),
|
|
709
|
+
playlistId: z.string().optional().describe('Embedded playlist ID (for PLAYLIST type)'),
|
|
710
|
+
value: z.string().optional().describe('URL or text value (for URL, WEB_PAGE, RSS, TEXT, YOU_TUBE types)'),
|
|
706
711
|
interval: z.number().optional().describe('Duration in seconds'),
|
|
707
|
-
loopPolicyType: z.enum([
|
|
712
|
+
loopPolicyType: z.enum([
|
|
713
|
+
'DEFAULT', 'OVER_SATURATE2X', 'OVER_SATURATE3X', 'OVER_SATURATE4X', 'OVER_SATURATE5X',
|
|
714
|
+
'RANDOM', 'UNDER_SATURATE2X', 'UNDER_SATURATE3X', 'UNDER_SATURATE4X', 'UNDER_SATURATE5X',
|
|
715
|
+
'PREFILL', 'POSTFILL', 'SPREADFILL',
|
|
716
|
+
]).optional().describe('Loop policy (DEFAULT=1x per loop, OVER_SATURATE2X=2x per loop, UNDER_SATURATE2X=1 per 2 loops, RANDOM, PREFILL, POSTFILL, SPREADFILL)'),
|
|
717
|
+
conditions: z.array(z.object({
|
|
718
|
+
type: z.enum([
|
|
719
|
+
'AFTER_DATE', 'AFTER_TIME', 'ALWAYS', 'BEACON_ACTIVE', 'BEFORE_DATE', 'BEFORE_TIME',
|
|
720
|
+
'COMMAND', 'DATE_RANGE', 'DAY_OF_MONTH', 'DAYS_OF_WEEK',
|
|
721
|
+
'DEVICE_BY_GROUP', 'DEVICE_BY_NESTED_GROUP', 'DEVICE_BY_NAME', 'DEVICE_BY_TAG', 'DEVICE_BY_ORG',
|
|
722
|
+
'EVERYWHERE', 'GEO_LOCATION', 'GPS_WITHIN_RADIUS', 'KEY_EVENT',
|
|
723
|
+
'MONTH_OF_YEAR', 'MOTION', 'NEVER', 'NOWHERE',
|
|
724
|
+
'PCT_ADULT', 'PCT_CHILD', 'PCT_FEMALE', 'PCT_MALE', 'PCT_SENIOR', 'PCT_YOUNG', 'PCT_YOUNG_ADULT',
|
|
725
|
+
'PLAYLIST_BY_NAME', 'PLAYLIST_BY_TAG', 'SEND_COMMAND',
|
|
726
|
+
'SPECIFIC_DEVICE', 'SPECIFIC_PLAYLIST', 'SPECIFIC_TEMPLATE', 'SPECIFIC_ZONE',
|
|
727
|
+
'TEMPLATE_BY_NAME', 'TEMPLATE_BY_TAG',
|
|
728
|
+
'TIME_RANGE', 'TOPIC_SET', 'TOTAL_VIEWERS_GREATER_THAN', 'TOTAL_VIEWERS_LESS_THAN',
|
|
729
|
+
'TOUCH_EVENT', 'WEEK_OF_MONTH', 'WEEK_OF_YEAR',
|
|
730
|
+
]).describe('Condition type (e.g., DATE_RANGE, TIME_RANGE, DAYS_OF_WEEK, SPECIFIC_DEVICE)'),
|
|
731
|
+
operator: z.enum(['AND', 'OR', 'AND_NOT', 'OR_NOT']).optional()
|
|
732
|
+
.describe('Logical operator for combining with previous condition (ignored for first condition)'),
|
|
733
|
+
value1: z.string().optional().describe('Value 1 - meaning depends on condition type'),
|
|
734
|
+
value2: z.string().optional().describe('Value 2 - meaning depends on condition type'),
|
|
735
|
+
value3: z.string().optional().describe('Value 3 - meaning depends on condition type'),
|
|
736
|
+
value4: z.string().optional().describe('Value 4 - meaning depends on condition type'),
|
|
737
|
+
})).optional().describe('Conditions controlling when/where this source plays (e.g., date range, time range, device targeting). If omitted, plays unconditionally.'),
|
|
708
738
|
}).describe('The source to add'),
|
|
709
739
|
position: z.number().optional().describe('Optional position (0-based index). If omitted, appends to end.'),
|
|
710
740
|
}, async (args) => {
|
|
@@ -762,15 +792,42 @@ server.tool('update_playlist_source', 'Update a source within a playlist. Only p
|
|
|
762
792
|
source: z.object({
|
|
763
793
|
name: z.string().optional().describe('New display name'),
|
|
764
794
|
type: z.enum([
|
|
765
|
-
'
|
|
766
|
-
'
|
|
795
|
+
'AUDIO', 'COMMAND', 'FLASH', 'GADGET', 'IMAGE', 'PDF',
|
|
796
|
+
'PLACE_EXCHANGE', 'PLAYLIST', 'POWER_POINT', 'RSS', 'SVG',
|
|
797
|
+
'TEMPLATE', 'TEXT', 'TWITTER', 'URL', 'VIDEO',
|
|
798
|
+
'VISTAR_MEDIA', 'WEB_PAGE', 'YOU_TUBE', 'VISTAR_MEDIA_EX',
|
|
767
799
|
]).optional().describe('New content type'),
|
|
768
800
|
mediaId: z.string().optional().describe('New media file ID'),
|
|
769
801
|
templateId: z.string().optional().describe('New template ID'),
|
|
770
802
|
playlistId: z.string().optional().describe('New embedded playlist ID'),
|
|
771
803
|
value: z.string().optional().describe('New URL or text value'),
|
|
772
804
|
interval: z.number().optional().describe('New duration in seconds'),
|
|
773
|
-
loopPolicyType: z.enum([
|
|
805
|
+
loopPolicyType: z.enum([
|
|
806
|
+
'DEFAULT', 'OVER_SATURATE2X', 'OVER_SATURATE3X', 'OVER_SATURATE4X', 'OVER_SATURATE5X',
|
|
807
|
+
'RANDOM', 'UNDER_SATURATE2X', 'UNDER_SATURATE3X', 'UNDER_SATURATE4X', 'UNDER_SATURATE5X',
|
|
808
|
+
'PREFILL', 'POSTFILL', 'SPREADFILL',
|
|
809
|
+
]).optional().describe('New loop policy'),
|
|
810
|
+
conditions: z.array(z.object({
|
|
811
|
+
type: z.enum([
|
|
812
|
+
'AFTER_DATE', 'AFTER_TIME', 'ALWAYS', 'BEACON_ACTIVE', 'BEFORE_DATE', 'BEFORE_TIME',
|
|
813
|
+
'COMMAND', 'DATE_RANGE', 'DAY_OF_MONTH', 'DAYS_OF_WEEK',
|
|
814
|
+
'DEVICE_BY_GROUP', 'DEVICE_BY_NESTED_GROUP', 'DEVICE_BY_NAME', 'DEVICE_BY_TAG', 'DEVICE_BY_ORG',
|
|
815
|
+
'EVERYWHERE', 'GEO_LOCATION', 'GPS_WITHIN_RADIUS', 'KEY_EVENT',
|
|
816
|
+
'MONTH_OF_YEAR', 'MOTION', 'NEVER', 'NOWHERE',
|
|
817
|
+
'PCT_ADULT', 'PCT_CHILD', 'PCT_FEMALE', 'PCT_MALE', 'PCT_SENIOR', 'PCT_YOUNG', 'PCT_YOUNG_ADULT',
|
|
818
|
+
'PLAYLIST_BY_NAME', 'PLAYLIST_BY_TAG', 'SEND_COMMAND',
|
|
819
|
+
'SPECIFIC_DEVICE', 'SPECIFIC_PLAYLIST', 'SPECIFIC_TEMPLATE', 'SPECIFIC_ZONE',
|
|
820
|
+
'TEMPLATE_BY_NAME', 'TEMPLATE_BY_TAG',
|
|
821
|
+
'TIME_RANGE', 'TOPIC_SET', 'TOTAL_VIEWERS_GREATER_THAN', 'TOTAL_VIEWERS_LESS_THAN',
|
|
822
|
+
'TOUCH_EVENT', 'WEEK_OF_MONTH', 'WEEK_OF_YEAR',
|
|
823
|
+
]).describe('Condition type (e.g., DATE_RANGE, TIME_RANGE, DAYS_OF_WEEK, SPECIFIC_DEVICE)'),
|
|
824
|
+
operator: z.enum(['AND', 'OR', 'AND_NOT', 'OR_NOT']).optional()
|
|
825
|
+
.describe('Logical operator for combining with previous condition (ignored for first condition)'),
|
|
826
|
+
value1: z.string().optional().describe('Value 1 - meaning depends on condition type'),
|
|
827
|
+
value2: z.string().optional().describe('Value 2 - meaning depends on condition type'),
|
|
828
|
+
value3: z.string().optional().describe('Value 3 - meaning depends on condition type'),
|
|
829
|
+
value4: z.string().optional().describe('Value 4 - meaning depends on condition type'),
|
|
830
|
+
})).optional().describe('Conditions controlling when/where this source plays. Replaces existing conditions. If omitted, conditions are unchanged.'),
|
|
774
831
|
}).describe('Updated source properties (only provided fields are changed)'),
|
|
775
832
|
}, async (args) => {
|
|
776
833
|
try {
|
|
@@ -919,6 +976,916 @@ mutation ReorderPlaylistSources($input: ReorderPlaylistSourcesInput) {
|
|
|
919
976
|
}
|
|
920
977
|
});
|
|
921
978
|
// ============================================================================
|
|
979
|
+
// Tool: List Data Tables
|
|
980
|
+
// ============================================================================
|
|
981
|
+
server.tool('list_data_tables', 'List data tables in the account. Returns table summaries with pagination. Use to discover available tables before querying rows.', {
|
|
982
|
+
groupId: z.string().optional().describe('Optional group ID to filter by'),
|
|
983
|
+
pageSize: z.number().optional().default(50).describe('Number of tables per page (default 50)'),
|
|
984
|
+
continuationToken: z.string().optional().describe('Token for fetching the next page'),
|
|
985
|
+
}, async (args) => {
|
|
986
|
+
try {
|
|
987
|
+
const query = `
|
|
988
|
+
query DataTables($groupId: String, $pageSize: Int! = 50, $continuationToken: String) {
|
|
989
|
+
dataTables(groupId: $groupId, pageSize: $pageSize, continuationToken: $continuationToken) {
|
|
990
|
+
data {
|
|
991
|
+
id
|
|
992
|
+
name
|
|
993
|
+
description
|
|
994
|
+
columnCount
|
|
995
|
+
rowCount
|
|
996
|
+
groupId
|
|
997
|
+
updatedAt
|
|
998
|
+
}
|
|
999
|
+
continuationToken
|
|
1000
|
+
}
|
|
1001
|
+
}`;
|
|
1002
|
+
const variables = { pageSize: args.pageSize };
|
|
1003
|
+
if (args.groupId)
|
|
1004
|
+
variables.groupId = args.groupId;
|
|
1005
|
+
if (args.continuationToken)
|
|
1006
|
+
variables.continuationToken = args.continuationToken;
|
|
1007
|
+
const response = await graphqlClient.query(query, variables);
|
|
1008
|
+
if (response.errors && response.errors.length > 0) {
|
|
1009
|
+
const result = {
|
|
1010
|
+
success: false,
|
|
1011
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1012
|
+
};
|
|
1013
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1014
|
+
}
|
|
1015
|
+
const result = {
|
|
1016
|
+
success: true,
|
|
1017
|
+
data: response.data,
|
|
1018
|
+
};
|
|
1019
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1020
|
+
}
|
|
1021
|
+
catch (error) {
|
|
1022
|
+
const result = {
|
|
1023
|
+
success: false,
|
|
1024
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1025
|
+
};
|
|
1026
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1027
|
+
}
|
|
1028
|
+
});
|
|
1029
|
+
// ============================================================================
|
|
1030
|
+
// Tool: Get Data Table
|
|
1031
|
+
// ============================================================================
|
|
1032
|
+
server.tool('get_data_table', 'Get a single data table definition by ID, including its full column schema. Use this to understand table structure before querying or modifying rows.', {
|
|
1033
|
+
tableId: z.string().describe('The data table ID'),
|
|
1034
|
+
}, async (args) => {
|
|
1035
|
+
try {
|
|
1036
|
+
const query = `
|
|
1037
|
+
query DataTable($tableId: String) {
|
|
1038
|
+
dataTable(tableId: $tableId) {
|
|
1039
|
+
id
|
|
1040
|
+
name
|
|
1041
|
+
description
|
|
1042
|
+
groupId
|
|
1043
|
+
rowCount
|
|
1044
|
+
cacheTtlSeconds
|
|
1045
|
+
createdAt
|
|
1046
|
+
updatedAt
|
|
1047
|
+
columns {
|
|
1048
|
+
id
|
|
1049
|
+
name
|
|
1050
|
+
key
|
|
1051
|
+
type
|
|
1052
|
+
required
|
|
1053
|
+
sortable
|
|
1054
|
+
options
|
|
1055
|
+
default
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}`;
|
|
1059
|
+
const response = await graphqlClient.query(query, { tableId: args.tableId });
|
|
1060
|
+
if (response.errors && response.errors.length > 0) {
|
|
1061
|
+
const result = {
|
|
1062
|
+
success: false,
|
|
1063
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1064
|
+
};
|
|
1065
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1066
|
+
}
|
|
1067
|
+
const result = {
|
|
1068
|
+
success: true,
|
|
1069
|
+
data: response.data,
|
|
1070
|
+
};
|
|
1071
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1072
|
+
}
|
|
1073
|
+
catch (error) {
|
|
1074
|
+
const result = {
|
|
1075
|
+
success: false,
|
|
1076
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1077
|
+
};
|
|
1078
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
// ============================================================================
|
|
1082
|
+
// Tool: List Data Table Rows
|
|
1083
|
+
// ============================================================================
|
|
1084
|
+
server.tool('list_data_table_rows', 'List rows in a data table with optional filtering, sorting, field selection, and pagination.', {
|
|
1085
|
+
tableId: z.string().describe('The data table ID'),
|
|
1086
|
+
filter: z.string().optional().describe('Optional filter as a JSON string (e.g., {"status":"active"})'),
|
|
1087
|
+
sort: z.string().optional().describe('Column key to sort by'),
|
|
1088
|
+
sortDir: z.enum(['asc', 'desc']).optional().default('asc').describe('Sort direction'),
|
|
1089
|
+
pageSize: z.number().optional().default(50).describe('Number of rows per page (default 50)'),
|
|
1090
|
+
continuationToken: z.string().optional().describe('Token for fetching the next page'),
|
|
1091
|
+
fields: z.string().optional().describe('Comma-separated list of column keys to return'),
|
|
1092
|
+
}, async (args) => {
|
|
1093
|
+
try {
|
|
1094
|
+
const query = `
|
|
1095
|
+
query DataTableRows($tableId: String, $filter: String, $sort: String, $sortDir: String = "asc", $pageSize: Int! = 50, $continuationToken: String, $fields: String) {
|
|
1096
|
+
dataTableRows(tableId: $tableId, filter: $filter, sort: $sort, sortDir: $sortDir, pageSize: $pageSize, continuationToken: $continuationToken, fields: $fields) {
|
|
1097
|
+
data {
|
|
1098
|
+
id
|
|
1099
|
+
sortOrder
|
|
1100
|
+
data
|
|
1101
|
+
updatedAt
|
|
1102
|
+
}
|
|
1103
|
+
totalCount
|
|
1104
|
+
continuationToken
|
|
1105
|
+
}
|
|
1106
|
+
}`;
|
|
1107
|
+
const variables = {
|
|
1108
|
+
tableId: args.tableId,
|
|
1109
|
+
pageSize: args.pageSize,
|
|
1110
|
+
sortDir: args.sortDir,
|
|
1111
|
+
};
|
|
1112
|
+
if (args.filter)
|
|
1113
|
+
variables.filter = args.filter;
|
|
1114
|
+
if (args.sort)
|
|
1115
|
+
variables.sort = args.sort;
|
|
1116
|
+
if (args.continuationToken)
|
|
1117
|
+
variables.continuationToken = args.continuationToken;
|
|
1118
|
+
if (args.fields)
|
|
1119
|
+
variables.fields = args.fields;
|
|
1120
|
+
const response = await graphqlClient.query(query, variables);
|
|
1121
|
+
if (response.errors && response.errors.length > 0) {
|
|
1122
|
+
const result = {
|
|
1123
|
+
success: false,
|
|
1124
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1125
|
+
};
|
|
1126
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1127
|
+
}
|
|
1128
|
+
const result = {
|
|
1129
|
+
success: true,
|
|
1130
|
+
data: response.data,
|
|
1131
|
+
};
|
|
1132
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1133
|
+
}
|
|
1134
|
+
catch (error) {
|
|
1135
|
+
const result = {
|
|
1136
|
+
success: false,
|
|
1137
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1138
|
+
};
|
|
1139
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1140
|
+
}
|
|
1141
|
+
});
|
|
1142
|
+
// ============================================================================
|
|
1143
|
+
// Tool: Get Data Table Row
|
|
1144
|
+
// ============================================================================
|
|
1145
|
+
server.tool('get_data_table_row', 'Get a single row from a data table by row ID.', {
|
|
1146
|
+
tableId: z.string().describe('The data table ID'),
|
|
1147
|
+
rowId: z.string().describe('The row ID'),
|
|
1148
|
+
}, async (args) => {
|
|
1149
|
+
try {
|
|
1150
|
+
const query = `
|
|
1151
|
+
query DataTableRow($tableId: String, $rowId: String) {
|
|
1152
|
+
dataTableRow(tableId: $tableId, rowId: $rowId) {
|
|
1153
|
+
id
|
|
1154
|
+
sortOrder
|
|
1155
|
+
data
|
|
1156
|
+
updatedAt
|
|
1157
|
+
}
|
|
1158
|
+
}`;
|
|
1159
|
+
const response = await graphqlClient.query(query, {
|
|
1160
|
+
tableId: args.tableId,
|
|
1161
|
+
rowId: args.rowId,
|
|
1162
|
+
});
|
|
1163
|
+
if (response.errors && response.errors.length > 0) {
|
|
1164
|
+
const result = {
|
|
1165
|
+
success: false,
|
|
1166
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1167
|
+
};
|
|
1168
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1169
|
+
}
|
|
1170
|
+
const result = {
|
|
1171
|
+
success: true,
|
|
1172
|
+
data: response.data,
|
|
1173
|
+
};
|
|
1174
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1175
|
+
}
|
|
1176
|
+
catch (error) {
|
|
1177
|
+
const result = {
|
|
1178
|
+
success: false,
|
|
1179
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1180
|
+
};
|
|
1181
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
// ============================================================================
|
|
1185
|
+
// Tool: Export Data Table Rows
|
|
1186
|
+
// ============================================================================
|
|
1187
|
+
server.tool('export_data_table_rows', 'Export all rows from a data table as a CSV string. Useful for bulk data extraction or backup.', {
|
|
1188
|
+
tableId: z.string().describe('The data table ID'),
|
|
1189
|
+
}, async (args) => {
|
|
1190
|
+
try {
|
|
1191
|
+
const query = `
|
|
1192
|
+
query ExportDataTableRows($tableId: String) {
|
|
1193
|
+
exportDataTableRows(tableId: $tableId)
|
|
1194
|
+
}`;
|
|
1195
|
+
const response = await graphqlClient.query(query, { tableId: args.tableId });
|
|
1196
|
+
if (response.errors && response.errors.length > 0) {
|
|
1197
|
+
const result = {
|
|
1198
|
+
success: false,
|
|
1199
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1200
|
+
};
|
|
1201
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1202
|
+
}
|
|
1203
|
+
const result = {
|
|
1204
|
+
success: true,
|
|
1205
|
+
data: response.data,
|
|
1206
|
+
};
|
|
1207
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1208
|
+
}
|
|
1209
|
+
catch (error) {
|
|
1210
|
+
const result = {
|
|
1211
|
+
success: false,
|
|
1212
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1213
|
+
};
|
|
1214
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1215
|
+
}
|
|
1216
|
+
});
|
|
1217
|
+
// ============================================================================
|
|
1218
|
+
// Tool: Get Data Table Row Versions
|
|
1219
|
+
// ============================================================================
|
|
1220
|
+
server.tool('get_data_table_row_versions', 'List version history for a data table row (newest first). Use to audit changes or before rolling back.', {
|
|
1221
|
+
tableId: z.string().describe('The data table ID'),
|
|
1222
|
+
rowId: z.string().describe('The row ID'),
|
|
1223
|
+
pageSize: z.number().optional().default(25).describe('Number of versions per page (default 25)'),
|
|
1224
|
+
continuationToken: z.string().optional().describe('Token for fetching the next page'),
|
|
1225
|
+
}, async (args) => {
|
|
1226
|
+
try {
|
|
1227
|
+
const query = `
|
|
1228
|
+
query DataTableRowVersions($tableId: String, $rowId: String, $pageSize: Int! = 25, $continuationToken: String) {
|
|
1229
|
+
dataTableRowVersions(tableId: $tableId, rowId: $rowId, pageSize: $pageSize, continuationToken: $continuationToken) {
|
|
1230
|
+
data {
|
|
1231
|
+
id
|
|
1232
|
+
rowId
|
|
1233
|
+
version
|
|
1234
|
+
action
|
|
1235
|
+
data
|
|
1236
|
+
changedFields
|
|
1237
|
+
previousValues
|
|
1238
|
+
changedBy
|
|
1239
|
+
changedAt
|
|
1240
|
+
}
|
|
1241
|
+
totalCount
|
|
1242
|
+
continuationToken
|
|
1243
|
+
}
|
|
1244
|
+
}`;
|
|
1245
|
+
const variables = {
|
|
1246
|
+
tableId: args.tableId,
|
|
1247
|
+
rowId: args.rowId,
|
|
1248
|
+
pageSize: args.pageSize,
|
|
1249
|
+
};
|
|
1250
|
+
if (args.continuationToken)
|
|
1251
|
+
variables.continuationToken = args.continuationToken;
|
|
1252
|
+
const response = await graphqlClient.query(query, variables);
|
|
1253
|
+
if (response.errors && response.errors.length > 0) {
|
|
1254
|
+
const result = {
|
|
1255
|
+
success: false,
|
|
1256
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1257
|
+
};
|
|
1258
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1259
|
+
}
|
|
1260
|
+
const result = {
|
|
1261
|
+
success: true,
|
|
1262
|
+
data: response.data,
|
|
1263
|
+
};
|
|
1264
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1265
|
+
}
|
|
1266
|
+
catch (error) {
|
|
1267
|
+
const result = {
|
|
1268
|
+
success: false,
|
|
1269
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1270
|
+
};
|
|
1271
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1272
|
+
}
|
|
1273
|
+
});
|
|
1274
|
+
// ============================================================================
|
|
1275
|
+
// Tool: Get Data Table Row Version
|
|
1276
|
+
// ============================================================================
|
|
1277
|
+
server.tool('get_data_table_row_version', 'Get a specific version snapshot of a data table row. Use to inspect historical state before rollback.', {
|
|
1278
|
+
tableId: z.string().describe('The data table ID'),
|
|
1279
|
+
rowId: z.string().describe('The row ID'),
|
|
1280
|
+
version: z.number().describe('The version number to retrieve'),
|
|
1281
|
+
}, async (args) => {
|
|
1282
|
+
try {
|
|
1283
|
+
const query = `
|
|
1284
|
+
query DataTableRowVersion($tableId: String, $rowId: String, $version: Int!) {
|
|
1285
|
+
dataTableRowVersion(tableId: $tableId, rowId: $rowId, version: $version) {
|
|
1286
|
+
id
|
|
1287
|
+
rowId
|
|
1288
|
+
version
|
|
1289
|
+
action
|
|
1290
|
+
data
|
|
1291
|
+
changedFields
|
|
1292
|
+
previousValues
|
|
1293
|
+
changedBy
|
|
1294
|
+
changedAt
|
|
1295
|
+
}
|
|
1296
|
+
}`;
|
|
1297
|
+
const response = await graphqlClient.query(query, {
|
|
1298
|
+
tableId: args.tableId,
|
|
1299
|
+
rowId: args.rowId,
|
|
1300
|
+
version: args.version,
|
|
1301
|
+
});
|
|
1302
|
+
if (response.errors && response.errors.length > 0) {
|
|
1303
|
+
const result = {
|
|
1304
|
+
success: false,
|
|
1305
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1306
|
+
};
|
|
1307
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1308
|
+
}
|
|
1309
|
+
const result = {
|
|
1310
|
+
success: true,
|
|
1311
|
+
data: response.data,
|
|
1312
|
+
};
|
|
1313
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1314
|
+
}
|
|
1315
|
+
catch (error) {
|
|
1316
|
+
const result = {
|
|
1317
|
+
success: false,
|
|
1318
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1319
|
+
};
|
|
1320
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
// ============================================================================
|
|
1324
|
+
// Tool: Create Data Table
|
|
1325
|
+
// ============================================================================
|
|
1326
|
+
server.tool('create_data_table', 'Create a new data table with the specified column schema. Example: Create a menu board table with columns for item name, price, description, and image.', {
|
|
1327
|
+
name: z.string().describe('Table name (max 100 characters)'),
|
|
1328
|
+
description: z.string().optional().describe('Optional table description (max 500 characters)'),
|
|
1329
|
+
groupId: z.string().optional().describe('Optional group ID for organizing tables'),
|
|
1330
|
+
columns: z.array(z.object({
|
|
1331
|
+
name: z.string().describe('Display name for the column'),
|
|
1332
|
+
key: z.string().describe('Unique key used in row data objects'),
|
|
1333
|
+
type: z.enum(['STRING', 'NUMBER', 'BOOLEAN', 'DATE', 'SELECT', 'MEDIA', 'URL', 'RICH_TEXT', 'TIME', 'HIDDEN'])
|
|
1334
|
+
.describe('Data type of the column'),
|
|
1335
|
+
required: z.boolean().describe('Whether a value is required'),
|
|
1336
|
+
sortable: z.boolean().describe('Whether the column supports sorting'),
|
|
1337
|
+
options: z.array(z.string()).optional().describe('Allowed values for Select-type columns'),
|
|
1338
|
+
default: z.unknown().optional().describe('Default value for the column'),
|
|
1339
|
+
})).describe('Column definitions (at least one required)'),
|
|
1340
|
+
cacheTtlSeconds: z.number().optional().describe('Optional cache TTL in seconds for gadget/player reads'),
|
|
1341
|
+
}, async (args) => {
|
|
1342
|
+
try {
|
|
1343
|
+
const mutation = `
|
|
1344
|
+
mutation CreateDataTable($input: CreateDataTableInput) {
|
|
1345
|
+
createDataTable(input: $input) {
|
|
1346
|
+
success
|
|
1347
|
+
table {
|
|
1348
|
+
id
|
|
1349
|
+
name
|
|
1350
|
+
description
|
|
1351
|
+
columns {
|
|
1352
|
+
id
|
|
1353
|
+
name
|
|
1354
|
+
key
|
|
1355
|
+
type
|
|
1356
|
+
required
|
|
1357
|
+
}
|
|
1358
|
+
rowCount
|
|
1359
|
+
createdAt
|
|
1360
|
+
}
|
|
1361
|
+
error
|
|
1362
|
+
}
|
|
1363
|
+
}`;
|
|
1364
|
+
const input = {
|
|
1365
|
+
name: args.name,
|
|
1366
|
+
columns: args.columns,
|
|
1367
|
+
};
|
|
1368
|
+
if (args.description !== undefined)
|
|
1369
|
+
input.description = args.description;
|
|
1370
|
+
if (args.groupId !== undefined)
|
|
1371
|
+
input.groupId = args.groupId;
|
|
1372
|
+
if (args.cacheTtlSeconds !== undefined)
|
|
1373
|
+
input.cacheTtlSeconds = args.cacheTtlSeconds;
|
|
1374
|
+
const response = await graphqlClient.query(mutation, { input });
|
|
1375
|
+
if (response.errors && response.errors.length > 0) {
|
|
1376
|
+
const result = {
|
|
1377
|
+
success: false,
|
|
1378
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1379
|
+
};
|
|
1380
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1381
|
+
}
|
|
1382
|
+
const result = {
|
|
1383
|
+
success: true,
|
|
1384
|
+
data: response.data,
|
|
1385
|
+
};
|
|
1386
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1387
|
+
}
|
|
1388
|
+
catch (error) {
|
|
1389
|
+
const result = {
|
|
1390
|
+
success: false,
|
|
1391
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1392
|
+
};
|
|
1393
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1394
|
+
}
|
|
1395
|
+
});
|
|
1396
|
+
// ============================================================================
|
|
1397
|
+
// Tool: Update Data Table
|
|
1398
|
+
// ============================================================================
|
|
1399
|
+
server.tool('update_data_table', 'Update an existing data table definition. Only provided fields are changed. Example: Add a new "calories" column to a menu board table.', {
|
|
1400
|
+
tableId: z.string().describe('The table ID to update'),
|
|
1401
|
+
name: z.string().optional().describe('New table name (null leaves unchanged)'),
|
|
1402
|
+
description: z.string().optional().describe('New description (null leaves unchanged)'),
|
|
1403
|
+
groupId: z.string().optional().describe('New group ID (null leaves unchanged)'),
|
|
1404
|
+
columns: z.array(z.object({
|
|
1405
|
+
id: z.string().optional().describe('Column ID (only needed when updating existing columns)'),
|
|
1406
|
+
name: z.string().optional().describe('Display name for the column'),
|
|
1407
|
+
key: z.string().optional().describe('Unique key used in row data objects'),
|
|
1408
|
+
type: z.enum(['STRING', 'NUMBER', 'BOOLEAN', 'DATE', 'SELECT', 'MEDIA', 'URL', 'RICH_TEXT', 'TIME', 'HIDDEN'])
|
|
1409
|
+
.describe('Data type of the column'),
|
|
1410
|
+
required: z.boolean().describe('Whether a value is required'),
|
|
1411
|
+
sortable: z.boolean().describe('Whether the column supports sorting'),
|
|
1412
|
+
options: z.array(z.string()).optional().describe('Allowed values for Select-type columns'),
|
|
1413
|
+
default: z.unknown().optional().describe('Default value for the column'),
|
|
1414
|
+
})).optional().describe('Updated column definitions (null leaves unchanged)'),
|
|
1415
|
+
cacheTtlSeconds: z.number().optional().describe('New cache TTL in seconds (null leaves unchanged)'),
|
|
1416
|
+
}, async (args) => {
|
|
1417
|
+
try {
|
|
1418
|
+
const mutation = `
|
|
1419
|
+
mutation UpdateDataTable($input: UpdateDataTableInput) {
|
|
1420
|
+
updateDataTable(input: $input) {
|
|
1421
|
+
success
|
|
1422
|
+
table {
|
|
1423
|
+
id
|
|
1424
|
+
name
|
|
1425
|
+
description
|
|
1426
|
+
columns {
|
|
1427
|
+
id
|
|
1428
|
+
name
|
|
1429
|
+
key
|
|
1430
|
+
type
|
|
1431
|
+
required
|
|
1432
|
+
}
|
|
1433
|
+
rowCount
|
|
1434
|
+
updatedAt
|
|
1435
|
+
}
|
|
1436
|
+
error
|
|
1437
|
+
}
|
|
1438
|
+
}`;
|
|
1439
|
+
const input = { tableId: args.tableId };
|
|
1440
|
+
if (args.name !== undefined)
|
|
1441
|
+
input.name = args.name;
|
|
1442
|
+
if (args.description !== undefined)
|
|
1443
|
+
input.description = args.description;
|
|
1444
|
+
if (args.groupId !== undefined)
|
|
1445
|
+
input.groupId = args.groupId;
|
|
1446
|
+
if (args.columns !== undefined)
|
|
1447
|
+
input.columns = args.columns;
|
|
1448
|
+
if (args.cacheTtlSeconds !== undefined)
|
|
1449
|
+
input.cacheTtlSeconds = args.cacheTtlSeconds;
|
|
1450
|
+
const response = await graphqlClient.query(mutation, { input });
|
|
1451
|
+
if (response.errors && response.errors.length > 0) {
|
|
1452
|
+
const result = {
|
|
1453
|
+
success: false,
|
|
1454
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1455
|
+
};
|
|
1456
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1457
|
+
}
|
|
1458
|
+
const result = {
|
|
1459
|
+
success: true,
|
|
1460
|
+
data: response.data,
|
|
1461
|
+
};
|
|
1462
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1463
|
+
}
|
|
1464
|
+
catch (error) {
|
|
1465
|
+
const result = {
|
|
1466
|
+
success: false,
|
|
1467
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1468
|
+
};
|
|
1469
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1470
|
+
}
|
|
1471
|
+
});
|
|
1472
|
+
// ============================================================================
|
|
1473
|
+
// Tool: Delete Data Table
|
|
1474
|
+
// ============================================================================
|
|
1475
|
+
server.tool('delete_data_table', 'Delete a data table and all its rows. This action is irreversible.', {
|
|
1476
|
+
tableId: z.string().describe('The data table ID to delete'),
|
|
1477
|
+
}, async (args) => {
|
|
1478
|
+
try {
|
|
1479
|
+
const mutation = `
|
|
1480
|
+
mutation DeleteDataTable($tableId: String) {
|
|
1481
|
+
deleteDataTable(tableId: $tableId) {
|
|
1482
|
+
success
|
|
1483
|
+
error
|
|
1484
|
+
}
|
|
1485
|
+
}`;
|
|
1486
|
+
const response = await graphqlClient.query(mutation, { tableId: args.tableId });
|
|
1487
|
+
if (response.errors && response.errors.length > 0) {
|
|
1488
|
+
const result = {
|
|
1489
|
+
success: false,
|
|
1490
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1491
|
+
};
|
|
1492
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1493
|
+
}
|
|
1494
|
+
const result = {
|
|
1495
|
+
success: true,
|
|
1496
|
+
data: response.data,
|
|
1497
|
+
};
|
|
1498
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1499
|
+
}
|
|
1500
|
+
catch (error) {
|
|
1501
|
+
const result = {
|
|
1502
|
+
success: false,
|
|
1503
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1504
|
+
};
|
|
1505
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1506
|
+
}
|
|
1507
|
+
});
|
|
1508
|
+
// ============================================================================
|
|
1509
|
+
// Tool: Create Data Table Row
|
|
1510
|
+
// ============================================================================
|
|
1511
|
+
server.tool('create_data_table_row', 'Create a new row in a data table. Row data keys must match column keys defined in the table schema. Example: Add a new menu item to a menu board table.', {
|
|
1512
|
+
tableId: z.string().describe('The table ID to add the row to'),
|
|
1513
|
+
data: z.array(z.unknown()).describe('Row data as a JSON array. Keys must match column keys defined in the table schema.'),
|
|
1514
|
+
}, async (args) => {
|
|
1515
|
+
try {
|
|
1516
|
+
const mutation = `
|
|
1517
|
+
mutation CreateDataTableRow($input: CreateDataTableRowInput) {
|
|
1518
|
+
createDataTableRow(input: $input) {
|
|
1519
|
+
success
|
|
1520
|
+
row {
|
|
1521
|
+
id
|
|
1522
|
+
sortOrder
|
|
1523
|
+
data
|
|
1524
|
+
updatedAt
|
|
1525
|
+
}
|
|
1526
|
+
error
|
|
1527
|
+
validationErrors
|
|
1528
|
+
}
|
|
1529
|
+
}`;
|
|
1530
|
+
const response = await graphqlClient.query(mutation, {
|
|
1531
|
+
input: {
|
|
1532
|
+
tableId: args.tableId,
|
|
1533
|
+
data: args.data,
|
|
1534
|
+
},
|
|
1535
|
+
});
|
|
1536
|
+
if (response.errors && response.errors.length > 0) {
|
|
1537
|
+
const result = {
|
|
1538
|
+
success: false,
|
|
1539
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1540
|
+
};
|
|
1541
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1542
|
+
}
|
|
1543
|
+
const result = {
|
|
1544
|
+
success: true,
|
|
1545
|
+
data: response.data,
|
|
1546
|
+
};
|
|
1547
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1548
|
+
}
|
|
1549
|
+
catch (error) {
|
|
1550
|
+
const result = {
|
|
1551
|
+
success: false,
|
|
1552
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1553
|
+
};
|
|
1554
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1555
|
+
}
|
|
1556
|
+
});
|
|
1557
|
+
// ============================================================================
|
|
1558
|
+
// Tool: Update Data Table Row
|
|
1559
|
+
// ============================================================================
|
|
1560
|
+
server.tool('update_data_table_row', 'Update an existing row in a data table (partial update - only provided keys are changed). Supports optimistic concurrency via eTag. Example: Update the price of a menu item.', {
|
|
1561
|
+
tableId: z.string().describe('The table ID containing the row'),
|
|
1562
|
+
rowId: z.string().describe('The row ID to update'),
|
|
1563
|
+
data: z.array(z.unknown()).describe('Partial row data. Only provided keys are updated.'),
|
|
1564
|
+
eTag: z.string().optional().describe('Optional ETag for optimistic concurrency. Pass the row\'s current ETag to prevent conflicting writes.'),
|
|
1565
|
+
}, async (args) => {
|
|
1566
|
+
try {
|
|
1567
|
+
const mutation = `
|
|
1568
|
+
mutation UpdateDataTableRow($input: UpdateDataTableRowInput) {
|
|
1569
|
+
updateDataTableRow(input: $input) {
|
|
1570
|
+
success
|
|
1571
|
+
row {
|
|
1572
|
+
id
|
|
1573
|
+
sortOrder
|
|
1574
|
+
data
|
|
1575
|
+
updatedAt
|
|
1576
|
+
}
|
|
1577
|
+
error
|
|
1578
|
+
validationErrors
|
|
1579
|
+
}
|
|
1580
|
+
}`;
|
|
1581
|
+
const input = {
|
|
1582
|
+
tableId: args.tableId,
|
|
1583
|
+
rowId: args.rowId,
|
|
1584
|
+
data: args.data,
|
|
1585
|
+
};
|
|
1586
|
+
if (args.eTag !== undefined)
|
|
1587
|
+
input.eTag = args.eTag;
|
|
1588
|
+
const response = await graphqlClient.query(mutation, { input });
|
|
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: Delete Data Table Row
|
|
1612
|
+
// ============================================================================
|
|
1613
|
+
server.tool('delete_data_table_row', 'Delete a row from a data table.', {
|
|
1614
|
+
tableId: z.string().describe('The data table ID'),
|
|
1615
|
+
rowId: z.string().describe('The row ID to delete'),
|
|
1616
|
+
}, async (args) => {
|
|
1617
|
+
try {
|
|
1618
|
+
const mutation = `
|
|
1619
|
+
mutation DeleteDataTableRow($tableId: String, $rowId: String) {
|
|
1620
|
+
deleteDataTableRow(tableId: $tableId, rowId: $rowId) {
|
|
1621
|
+
success
|
|
1622
|
+
error
|
|
1623
|
+
}
|
|
1624
|
+
}`;
|
|
1625
|
+
const response = await graphqlClient.query(mutation, {
|
|
1626
|
+
tableId: args.tableId,
|
|
1627
|
+
rowId: args.rowId,
|
|
1628
|
+
});
|
|
1629
|
+
if (response.errors && response.errors.length > 0) {
|
|
1630
|
+
const result = {
|
|
1631
|
+
success: false,
|
|
1632
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1633
|
+
};
|
|
1634
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1635
|
+
}
|
|
1636
|
+
const result = {
|
|
1637
|
+
success: true,
|
|
1638
|
+
data: response.data,
|
|
1639
|
+
};
|
|
1640
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1641
|
+
}
|
|
1642
|
+
catch (error) {
|
|
1643
|
+
const result = {
|
|
1644
|
+
success: false,
|
|
1645
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1646
|
+
};
|
|
1647
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1648
|
+
}
|
|
1649
|
+
});
|
|
1650
|
+
// ============================================================================
|
|
1651
|
+
// Tool: Batch Create Data Table Rows
|
|
1652
|
+
// ============================================================================
|
|
1653
|
+
server.tool('batch_create_data_table_rows', 'Batch create multiple rows in a data table (max 100 rows per call). Example: Bulk-load menu items from an external data source.', {
|
|
1654
|
+
tableId: z.string().describe('The table ID to add rows to'),
|
|
1655
|
+
rows: z.array(z.array(z.unknown())).describe('Array of row data objects to create (max 100)'),
|
|
1656
|
+
}, async (args) => {
|
|
1657
|
+
try {
|
|
1658
|
+
const mutation = `
|
|
1659
|
+
mutation BatchCreateDataTableRows($input: BatchCreateDataTableRowsInput) {
|
|
1660
|
+
batchCreateDataTableRows(input: $input) {
|
|
1661
|
+
success
|
|
1662
|
+
results {
|
|
1663
|
+
rowId
|
|
1664
|
+
success
|
|
1665
|
+
error
|
|
1666
|
+
}
|
|
1667
|
+
error
|
|
1668
|
+
}
|
|
1669
|
+
}`;
|
|
1670
|
+
const response = await graphqlClient.query(mutation, {
|
|
1671
|
+
input: {
|
|
1672
|
+
tableId: args.tableId,
|
|
1673
|
+
rows: args.rows,
|
|
1674
|
+
},
|
|
1675
|
+
});
|
|
1676
|
+
if (response.errors && response.errors.length > 0) {
|
|
1677
|
+
const result = {
|
|
1678
|
+
success: false,
|
|
1679
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1680
|
+
};
|
|
1681
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1682
|
+
}
|
|
1683
|
+
const result = {
|
|
1684
|
+
success: true,
|
|
1685
|
+
data: response.data,
|
|
1686
|
+
};
|
|
1687
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1688
|
+
}
|
|
1689
|
+
catch (error) {
|
|
1690
|
+
const result = {
|
|
1691
|
+
success: false,
|
|
1692
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1693
|
+
};
|
|
1694
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1695
|
+
}
|
|
1696
|
+
});
|
|
1697
|
+
// ============================================================================
|
|
1698
|
+
// Tool: Batch Delete Data Table Rows
|
|
1699
|
+
// ============================================================================
|
|
1700
|
+
server.tool('batch_delete_data_table_rows', 'Batch delete multiple rows from a data table (max 100 row IDs per call).', {
|
|
1701
|
+
tableId: z.string().describe('The table ID containing the rows'),
|
|
1702
|
+
rowIds: z.array(z.string()).describe('Row IDs to delete (max 100)'),
|
|
1703
|
+
}, async (args) => {
|
|
1704
|
+
try {
|
|
1705
|
+
const mutation = `
|
|
1706
|
+
mutation BatchDeleteDataTableRows($input: BatchDeleteDataTableRowsInput) {
|
|
1707
|
+
batchDeleteDataTableRows(input: $input) {
|
|
1708
|
+
success
|
|
1709
|
+
deletedCount
|
|
1710
|
+
results {
|
|
1711
|
+
rowId
|
|
1712
|
+
success
|
|
1713
|
+
error
|
|
1714
|
+
}
|
|
1715
|
+
error
|
|
1716
|
+
}
|
|
1717
|
+
}`;
|
|
1718
|
+
const response = await graphqlClient.query(mutation, {
|
|
1719
|
+
input: {
|
|
1720
|
+
tableId: args.tableId,
|
|
1721
|
+
rowIds: args.rowIds,
|
|
1722
|
+
},
|
|
1723
|
+
});
|
|
1724
|
+
if (response.errors && response.errors.length > 0) {
|
|
1725
|
+
const result = {
|
|
1726
|
+
success: false,
|
|
1727
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1728
|
+
};
|
|
1729
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1730
|
+
}
|
|
1731
|
+
const result = {
|
|
1732
|
+
success: true,
|
|
1733
|
+
data: response.data,
|
|
1734
|
+
};
|
|
1735
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1736
|
+
}
|
|
1737
|
+
catch (error) {
|
|
1738
|
+
const result = {
|
|
1739
|
+
success: false,
|
|
1740
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1741
|
+
};
|
|
1742
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1743
|
+
}
|
|
1744
|
+
});
|
|
1745
|
+
// ============================================================================
|
|
1746
|
+
// Tool: Import Data Table Rows
|
|
1747
|
+
// ============================================================================
|
|
1748
|
+
server.tool('import_data_table_rows', 'Import rows from CSV content into a data table. Use replace mode to overwrite all existing rows, or append mode to add to existing rows.', {
|
|
1749
|
+
tableId: z.string().describe('The table ID to import rows into'),
|
|
1750
|
+
csvContent: z.string().describe('Raw CSV content (header row + data rows)'),
|
|
1751
|
+
replace: z.boolean().default(false).describe('When true, replaces all existing rows. When false (default), appends to existing rows.'),
|
|
1752
|
+
}, async (args) => {
|
|
1753
|
+
try {
|
|
1754
|
+
const mutation = `
|
|
1755
|
+
mutation ImportDataTableRows($input: ImportDataTableRowsInput) {
|
|
1756
|
+
importDataTableRows(input: $input) {
|
|
1757
|
+
success
|
|
1758
|
+
successCount
|
|
1759
|
+
errorCount
|
|
1760
|
+
errors {
|
|
1761
|
+
lineNumber
|
|
1762
|
+
error
|
|
1763
|
+
}
|
|
1764
|
+
error
|
|
1765
|
+
}
|
|
1766
|
+
}`;
|
|
1767
|
+
const response = await graphqlClient.query(mutation, {
|
|
1768
|
+
input: {
|
|
1769
|
+
tableId: args.tableId,
|
|
1770
|
+
csvContent: args.csvContent,
|
|
1771
|
+
replace: args.replace,
|
|
1772
|
+
},
|
|
1773
|
+
});
|
|
1774
|
+
if (response.errors && response.errors.length > 0) {
|
|
1775
|
+
const result = {
|
|
1776
|
+
success: false,
|
|
1777
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1778
|
+
};
|
|
1779
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1780
|
+
}
|
|
1781
|
+
const result = {
|
|
1782
|
+
success: true,
|
|
1783
|
+
data: response.data,
|
|
1784
|
+
};
|
|
1785
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1786
|
+
}
|
|
1787
|
+
catch (error) {
|
|
1788
|
+
const result = {
|
|
1789
|
+
success: false,
|
|
1790
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1791
|
+
};
|
|
1792
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1793
|
+
}
|
|
1794
|
+
});
|
|
1795
|
+
// ============================================================================
|
|
1796
|
+
// Tool: Reorder Data Table Rows
|
|
1797
|
+
// ============================================================================
|
|
1798
|
+
server.tool('reorder_data_table_rows', 'Reorder rows in a data table by specifying the desired row ID sequence.', {
|
|
1799
|
+
tableId: z.string().describe('The table ID containing the rows'),
|
|
1800
|
+
rowIds: z.array(z.string()).describe('Row IDs in the desired display order'),
|
|
1801
|
+
}, async (args) => {
|
|
1802
|
+
try {
|
|
1803
|
+
const mutation = `
|
|
1804
|
+
mutation ReorderDataTableRows($input: ReorderDataTableRowsInput) {
|
|
1805
|
+
reorderDataTableRows(input: $input) {
|
|
1806
|
+
success
|
|
1807
|
+
error
|
|
1808
|
+
}
|
|
1809
|
+
}`;
|
|
1810
|
+
const response = await graphqlClient.query(mutation, {
|
|
1811
|
+
input: {
|
|
1812
|
+
tableId: args.tableId,
|
|
1813
|
+
rowIds: args.rowIds,
|
|
1814
|
+
},
|
|
1815
|
+
});
|
|
1816
|
+
if (response.errors && response.errors.length > 0) {
|
|
1817
|
+
const result = {
|
|
1818
|
+
success: false,
|
|
1819
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1820
|
+
};
|
|
1821
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1822
|
+
}
|
|
1823
|
+
const result = {
|
|
1824
|
+
success: true,
|
|
1825
|
+
data: response.data,
|
|
1826
|
+
};
|
|
1827
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1828
|
+
}
|
|
1829
|
+
catch (error) {
|
|
1830
|
+
const result = {
|
|
1831
|
+
success: false,
|
|
1832
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1833
|
+
};
|
|
1834
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1835
|
+
}
|
|
1836
|
+
});
|
|
1837
|
+
// ============================================================================
|
|
1838
|
+
// Tool: Rollback Data Table Row
|
|
1839
|
+
// ============================================================================
|
|
1840
|
+
server.tool('rollback_data_table_row', 'Roll back a data table row to a previous version, creating a new version for the rollback action. Example: Undo an accidental price change by restoring a previous version.', {
|
|
1841
|
+
tableId: z.string().describe('The table ID containing the row'),
|
|
1842
|
+
rowId: z.string().describe('The row ID to roll back'),
|
|
1843
|
+
version: z.number().describe('The version number to restore'),
|
|
1844
|
+
}, async (args) => {
|
|
1845
|
+
try {
|
|
1846
|
+
const mutation = `
|
|
1847
|
+
mutation RollbackDataTableRow($input: RollbackDataTableRowInput) {
|
|
1848
|
+
rollbackDataTableRow(input: $input) {
|
|
1849
|
+
success
|
|
1850
|
+
newVersion
|
|
1851
|
+
restoredRow {
|
|
1852
|
+
id
|
|
1853
|
+
sortOrder
|
|
1854
|
+
data
|
|
1855
|
+
updatedAt
|
|
1856
|
+
}
|
|
1857
|
+
error
|
|
1858
|
+
}
|
|
1859
|
+
}`;
|
|
1860
|
+
const response = await graphqlClient.query(mutation, {
|
|
1861
|
+
input: {
|
|
1862
|
+
tableId: args.tableId,
|
|
1863
|
+
rowId: args.rowId,
|
|
1864
|
+
version: args.version,
|
|
1865
|
+
},
|
|
1866
|
+
});
|
|
1867
|
+
if (response.errors && response.errors.length > 0) {
|
|
1868
|
+
const result = {
|
|
1869
|
+
success: false,
|
|
1870
|
+
error: response.errors.map((e) => e.message).join('; '),
|
|
1871
|
+
};
|
|
1872
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1873
|
+
}
|
|
1874
|
+
const result = {
|
|
1875
|
+
success: true,
|
|
1876
|
+
data: response.data,
|
|
1877
|
+
};
|
|
1878
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1879
|
+
}
|
|
1880
|
+
catch (error) {
|
|
1881
|
+
const result = {
|
|
1882
|
+
success: false,
|
|
1883
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
1884
|
+
};
|
|
1885
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1886
|
+
}
|
|
1887
|
+
});
|
|
1888
|
+
// ============================================================================
|
|
922
1889
|
// Tool: Cache Management
|
|
923
1890
|
// ============================================================================
|
|
924
1891
|
server.tool('manage_cache', 'Manage the response cache - view statistics or clear cached data.', {
|
|
@@ -968,6 +1935,15 @@ server.resource('schema://reveldigital/graphql', 'Revel Digital GraphQL Schema',
|
|
|
968
1935
|
- templateGroups - Query template groups
|
|
969
1936
|
- user - Query users
|
|
970
1937
|
|
|
1938
|
+
### Data Tables
|
|
1939
|
+
- list_data_tables - List data tables with pagination
|
|
1940
|
+
- get_data_table - Get table definition with column schema
|
|
1941
|
+
- list_data_table_rows - List rows with filtering, sorting, pagination
|
|
1942
|
+
- get_data_table_row - Get a single row by ID
|
|
1943
|
+
- export_data_table_rows - Export all rows as CSV
|
|
1944
|
+
- get_data_table_row_versions - List row version history
|
|
1945
|
+
- get_data_table_row_version - Get a specific version snapshot
|
|
1946
|
+
|
|
971
1947
|
### Device Commands & Permissions
|
|
972
1948
|
- displayCommands - Query available commands for the account (e.g., reboot, clear_cache)
|
|
973
1949
|
- permissions - Get current user/API key permissions and allowed/denied mutations
|
|
@@ -1014,6 +1990,19 @@ Raw logs return too much data for AI context windows.
|
|
|
1014
1990
|
- remove_playlist_source - Remove a source from a playlist
|
|
1015
1991
|
- reorder_playlist_sources - Reorder sources within a playlist
|
|
1016
1992
|
|
|
1993
|
+
### Data Table Management
|
|
1994
|
+
- create_data_table - Create a new data table with column schema
|
|
1995
|
+
- update_data_table - Update table definition (partial update)
|
|
1996
|
+
- delete_data_table - Delete a table and all its rows
|
|
1997
|
+
- create_data_table_row - Create a new row
|
|
1998
|
+
- update_data_table_row - Update a row (partial update with optimistic concurrency)
|
|
1999
|
+
- delete_data_table_row - Delete a row
|
|
2000
|
+
- batch_create_data_table_rows - Batch create rows (max 100)
|
|
2001
|
+
- batch_delete_data_table_rows - Batch delete rows (max 100)
|
|
2002
|
+
- import_data_table_rows - Import rows from CSV content
|
|
2003
|
+
- reorder_data_table_rows - Reorder rows
|
|
2004
|
+
- rollback_data_table_row - Roll back a row to a previous version
|
|
2005
|
+
|
|
1017
2006
|
## Field Presets
|
|
1018
2007
|
Use the build_query tool with preset parameter:
|
|
1019
2008
|
- minimal - Just ID and name
|
|
@@ -1040,6 +2029,29 @@ or use set_auth tool at runtime.
|
|
|
1040
2029
|
};
|
|
1041
2030
|
});
|
|
1042
2031
|
// ============================================================================
|
|
2032
|
+
// Prompts: System Prompts for AI Agents
|
|
2033
|
+
// ============================================================================
|
|
2034
|
+
server.prompt('system-prompt', 'Full system prompt for AI agents. Use as the system message for complete tool docs, query patterns, and domain knowledge.', { timezone: z.string().optional().describe('IANA timezone (e.g., "America/New_York"). Defaults to UTC.') }, async (args) => {
|
|
2035
|
+
const prompt = generateSystemPrompt({
|
|
2036
|
+
currentDateTime: new Date().toISOString(),
|
|
2037
|
+
timezone: args.timezone,
|
|
2038
|
+
});
|
|
2039
|
+
return {
|
|
2040
|
+
description: 'Revel Digital AI agent system prompt',
|
|
2041
|
+
messages: [{ role: 'user', content: { type: 'text', text: prompt } }],
|
|
2042
|
+
};
|
|
2043
|
+
});
|
|
2044
|
+
server.prompt('system-prompt-compact', 'Token-optimized compact system prompt for AI agents. Use when context window space is limited.', { timezone: z.string().optional().describe('IANA timezone (e.g., "America/New_York"). Defaults to UTC.') }, async (args) => {
|
|
2045
|
+
const prompt = generateCompactPrompt({
|
|
2046
|
+
currentDateTime: new Date().toISOString(),
|
|
2047
|
+
timezone: args.timezone,
|
|
2048
|
+
});
|
|
2049
|
+
return {
|
|
2050
|
+
description: 'Revel Digital AI agent system prompt (compact)',
|
|
2051
|
+
messages: [{ role: 'user', content: { type: 'text', text: prompt } }],
|
|
2052
|
+
};
|
|
2053
|
+
});
|
|
2054
|
+
// ============================================================================
|
|
1043
2055
|
// Start Server
|
|
1044
2056
|
// ============================================================================
|
|
1045
2057
|
async function main() {
|