rhombus-node-mcp 0.1.24 → 0.1.27

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.
Files changed (41) hide show
  1. package/dist/api/access-control-tool-api.js +1 -1
  2. package/dist/api/alarm-monitoring-tool-api.js +1 -1
  3. package/dist/api/camera-tool-api.js +1 -1
  4. package/dist/api/camera-uptime-tool-api.js +1 -1
  5. package/dist/api/clips-tool-api.js +1 -1
  6. package/dist/api/create-camera-policy-tool-api.js +1 -1
  7. package/dist/api/create-tool-api.js +1 -1
  8. package/dist/api/door-schedule-exception-tool-api.js +206 -0
  9. package/dist/api/door-tool-api.js +1 -1
  10. package/dist/api/entity-lookup-tool-api.js +1 -1
  11. package/dist/api/events-tool-api.js +1 -1
  12. package/dist/api/faces-tool-api.js +1 -1
  13. package/dist/api/get-entity-tool-api.js +12 -11
  14. package/dist/api/get-org-information-tool-api.js +1 -1
  15. package/dist/api/guest-management-tool-api.js +1 -1
  16. package/dist/api/location-tool-api.js +1 -1
  17. package/dist/api/lpr-tool-api.js +1 -1
  18. package/dist/api/policy-alerts-tool-api.js +1 -1
  19. package/dist/api/reboot-cameras-tool-api.js +1 -1
  20. package/dist/api/report-tool-api.js +1 -1
  21. package/dist/api/rules-tool-api.js +1 -1
  22. package/dist/api/search-tool-api.js +1 -1
  23. package/dist/api/update-tool-api.js +1 -1
  24. package/dist/api/user-access-trail-tool-api.js +1 -1
  25. package/dist/api/user-audit-tool-api.js +1 -1
  26. package/dist/api/user-tool-api.js +1 -1
  27. package/dist/createServer.js +1 -0
  28. package/dist/filtering-utils.js +15 -7
  29. package/dist/network/locations.js +60 -0
  30. package/dist/{network.js → network/network.js} +8 -3
  31. package/dist/network/postApiMap.js +7 -0
  32. package/dist/tools/analytics-tool.js +1 -1
  33. package/dist/tools/create-camera-policy-tool.js +1 -1
  34. package/dist/tools/door-schedule-exception-tool.js +130 -0
  35. package/dist/types/access-control-tool-types.js +1 -1
  36. package/dist/types/door-schedule-exception-tool-types.js +190 -0
  37. package/dist/types/report-tool-types.js +7 -7
  38. package/dist/types/user-tool-types.js +1 -1
  39. package/dist/types/zod-schemas.js +670 -665
  40. package/dist/utils/timestampInput.js +5 -8
  41. package/package.json +4 -4
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  const PERMISSION_RANK = {
3
3
  LIVEONLY: 0,
4
4
  READONLY: 1,
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  export async function getOrgAlarmStatus(requestModifiers, sessionId) {
3
3
  const res = await postApi({
4
4
  route: "/alertmonitoring/orgStatus",
@@ -1,5 +1,5 @@
1
1
  import { getLogger } from "../logger.js";
2
- import { constructRequestHeaders, postApi } from "../network.js";
2
+ import { constructRequestHeaders, postApi } from "../network/network.js";
3
3
  import { removeNullFields } from "../util.js";
4
4
  const logger = getLogger("camera-tool");
5
5
  const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  function computeUptimeStats(cameraUuid, cameraName, locationUuid, windows, startTimeSec, endTimeSec) {
3
3
  const totalPeriodSeconds = endTimeSec - startTimeSec;
4
4
  let totalUptimeSeconds = 0;
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  export async function getSavedClips(args, requestModifiers, sessionId) {
3
3
  const res = await postApi({
4
4
  route: "/event/getClipsWithProgress",
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  export async function createCameraPolicy(args, requestModifiers, sessionId) {
3
3
  return await postApi({
4
4
  route: "/policy/createCameraPolicy",
@@ -1,5 +1,5 @@
1
1
  import { logger } from "../logger.js";
2
- import { postApi } from "../network.js";
2
+ import { postApi } from "../network/network.js";
3
3
  export async function createVideoWall(options, requestModifiers, sessionId) {
4
4
  // our grid layout settings is a little complicated. we'll dumb it down
5
5
  // to certain presets based on length of device list
@@ -0,0 +1,206 @@
1
+ import z from "zod";
2
+ import { postApi, throwIfApiError } from "../network/network.js";
3
+ import createUuidMap from "../network/postApiMap.js";
4
+ import { getAccessControlledDoors } from "./get-entity-tool-api.js";
5
+ export const DateRangeFilter = z.object({
6
+ localStartDateRangeStart: z.iso.datetime({ offset: true }).optional(),
7
+ localStartDateRangeEnd: z.iso.datetime({ offset: true }).optional(),
8
+ localEndDateRangeStart: z.iso.datetime({ offset: true }).optional(),
9
+ localEndDateRangeEnd: z.iso.datetime({ offset: true }).optional(),
10
+ });
11
+ function mapException(exception) {
12
+ const doorUuids = exception?.doorUuids?.filter((value) => value !== null) ??
13
+ [];
14
+ const intervalCount = Array.isArray(exception?.intervals)
15
+ ? exception.intervals.length
16
+ : 0;
17
+ return {
18
+ uuid: exception?.uuid ?? undefined,
19
+ name: exception?.name ?? undefined,
20
+ description: exception?.description ?? undefined,
21
+ locationUuid: exception?.locationUuid ?? undefined,
22
+ localStartDate: exception?.localStartDate ?? undefined,
23
+ localEndDate: exception?.localEndDate ?? undefined,
24
+ defaultState: exception?.defaultState ?? undefined,
25
+ doorUuids,
26
+ intervalCount,
27
+ createdAtMillis: exception?.createdAtMillis ?? undefined,
28
+ updatedAtMillis: exception?.updatedAtMillis ?? undefined,
29
+ };
30
+ }
31
+ function buildDateRangeFilter(filter) {
32
+ if (!filter)
33
+ return undefined;
34
+ const { localStartDateRangeStart, localStartDateRangeEnd, localEndDateRangeStart, localEndDateRangeEnd, } = filter;
35
+ const hasAnyFilter = Boolean(localStartDateRangeStart ||
36
+ localStartDateRangeEnd ||
37
+ localEndDateRangeStart ||
38
+ localEndDateRangeEnd);
39
+ if (!hasAnyFilter)
40
+ return undefined;
41
+ return {
42
+ localStartDateRangeStart,
43
+ localStartDateRangeEnd,
44
+ localEndDateRangeStart,
45
+ localEndDateRangeEnd,
46
+ };
47
+ }
48
+ async function verifyExceptionConfig(exception, options, requestModifiers, sessionId) {
49
+ const verified = {
50
+ ...exception,
51
+ };
52
+ const doorUuidsProvided = Array.isArray(exception.doorUuids);
53
+ const cleanedDoorUuids = doorUuidsProvided
54
+ ? (exception.doorUuids?.filter((doorUuid) => !!doorUuid) ?? [])
55
+ : undefined;
56
+ if (cleanedDoorUuids !== undefined) {
57
+ verified.doorUuids = cleanedDoorUuids;
58
+ }
59
+ if ((!Array.isArray(exception.intervals) || exception.intervals.length === 0) &&
60
+ exception.defaultState &&
61
+ exception.localStartDate &&
62
+ exception.localEndDate) {
63
+ verified.intervals = [
64
+ {
65
+ localStartDateTime: `${exception.localStartDate}T00:00:00`,
66
+ localEndDateTime: `${exception.localEndDate}T23:59:59`,
67
+ state: exception.defaultState,
68
+ },
69
+ ];
70
+ }
71
+ // construct locationToDoorsMap only when doors are explicitly provided.
72
+ // For update, omitting doorUuids should preserve the existing map.
73
+ if (cleanedDoorUuids !== undefined) {
74
+ const locationToDoorsMap = {};
75
+ if (cleanedDoorUuids.length > 0) {
76
+ const doors = await getAccessControlledDoors(requestModifiers, sessionId);
77
+ const doorsMap = await createUuidMap(doors.accessControlledDoors ?? [], "uuid");
78
+ for (const doorUuid of cleanedDoorUuids) {
79
+ const door = doorsMap.get(doorUuid);
80
+ const locationUuid = door?.locationUuid;
81
+ if (locationUuid) {
82
+ if (!locationToDoorsMap[locationUuid]) {
83
+ locationToDoorsMap[locationUuid] = [];
84
+ }
85
+ locationToDoorsMap[locationUuid].push(doorUuid);
86
+ }
87
+ }
88
+ }
89
+ verified.locationToDoorsMap = locationToDoorsMap;
90
+ }
91
+ else if (!options.isUpdate) {
92
+ verified.locationToDoorsMap = {};
93
+ }
94
+ return verified;
95
+ }
96
+ export async function createDoorScheduleException(exception, requestModifiers, sessionId) {
97
+ const normalizedException = await verifyExceptionConfig(exception, { isUpdate: false }, requestModifiers, sessionId);
98
+ const res = await postApi({
99
+ route: "/accesscontrol/doorScheduleException/createExceptionV2",
100
+ body: {
101
+ exception: normalizedException,
102
+ },
103
+ modifiers: requestModifiers,
104
+ sessionId,
105
+ });
106
+ throwIfApiError(res);
107
+ return {
108
+ exception: res.exception ? mapException(res.exception) : undefined,
109
+ warningMsg: res.warningMsg ?? undefined,
110
+ };
111
+ }
112
+ export async function updateDoorScheduleException(exception, requestModifiers, sessionId) {
113
+ const normalizedException = await verifyExceptionConfig(exception, { isUpdate: true }, requestModifiers, sessionId);
114
+ const res = await postApi({
115
+ route: "/accesscontrol/doorScheduleException/updateExceptionV2",
116
+ body: {
117
+ exception: normalizedException,
118
+ },
119
+ modifiers: requestModifiers,
120
+ sessionId,
121
+ });
122
+ throwIfApiError(res);
123
+ return {
124
+ exception: res.exception ? mapException(res.exception) : undefined,
125
+ expiredACDLicensesDoorUuids: res.expiredACDLicensesDoorUuids?.filter((value) => value !== null) ?? [],
126
+ unassignedACDLicensesDoorUuids: res.unassignedACDLicensesDoorUuids?.filter((value) => value !== null) ?? [],
127
+ warningMsg: res.warningMsg ?? undefined,
128
+ };
129
+ }
130
+ export async function deleteDoorScheduleException(exceptionUuid, requestModifiers, sessionId) {
131
+ const res = await postApi({
132
+ route: "/accesscontrol/doorScheduleException/deleteExceptionV2",
133
+ body: {
134
+ exceptionUuid,
135
+ },
136
+ modifiers: requestModifiers,
137
+ sessionId,
138
+ });
139
+ throwIfApiError(res);
140
+ return {
141
+ deleted: { success: true, exceptionUuid },
142
+ warningMsg: res.warningMsg ?? undefined,
143
+ };
144
+ }
145
+ export async function getDoorScheduleException(exceptionUuid, requestModifiers, sessionId) {
146
+ const res = await postApi({
147
+ route: "/accesscontrol/doorScheduleException/getExceptionV2",
148
+ body: {
149
+ exceptionUuid,
150
+ },
151
+ modifiers: requestModifiers,
152
+ sessionId,
153
+ });
154
+ throwIfApiError(res);
155
+ return {
156
+ exception: res.exception ? mapException(res.exception) : undefined,
157
+ warningMsg: res.warningMsg ?? undefined,
158
+ };
159
+ }
160
+ export async function findDoorScheduleExceptions(filter, requestModifiers, sessionId) {
161
+ const res = await postApi({
162
+ route: "/accesscontrol/doorScheduleException/findExceptionsV2",
163
+ body: {
164
+ dateRangeFilter: buildDateRangeFilter(filter),
165
+ },
166
+ modifiers: requestModifiers,
167
+ sessionId,
168
+ });
169
+ throwIfApiError(res);
170
+ return {
171
+ exceptions: res.exceptions?.map(mapException) ?? [],
172
+ warningMsg: res.warningMsg ?? undefined,
173
+ };
174
+ }
175
+ export async function findDoorScheduleExceptionsForLocation(locationUuid, filter, requestModifiers, sessionId) {
176
+ const res = await postApi({
177
+ route: "/accesscontrol/doorScheduleException/findExceptionsForLocationV2",
178
+ body: {
179
+ locationUuid,
180
+ dateRangeFilter: buildDateRangeFilter(filter),
181
+ },
182
+ modifiers: requestModifiers,
183
+ sessionId,
184
+ });
185
+ throwIfApiError(res);
186
+ return {
187
+ exceptions: res.exceptions?.map(mapException) ?? [],
188
+ warningMsg: res.warningMsg ?? undefined,
189
+ };
190
+ }
191
+ export async function findDoorScheduleExceptionsForDoor(doorUuid, filter, requestModifiers, sessionId) {
192
+ const res = await postApi({
193
+ route: "/accesscontrol/doorScheduleException/findExceptionsForDoor",
194
+ body: {
195
+ doorUuid,
196
+ dateRangeFilter: buildDateRangeFilter(filter),
197
+ },
198
+ modifiers: requestModifiers,
199
+ sessionId,
200
+ });
201
+ throwIfApiError(res);
202
+ return {
203
+ exceptions: res.exceptions?.map(mapException) ?? [],
204
+ warningMsg: res.warningMsg ?? undefined,
205
+ };
206
+ }
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  export async function getDoorControllerRules(doorControllerUuid, requestModifiers, sessionId) {
3
3
  const res = await postApi({
4
4
  route: "/doorcontroller/getDoorControllerRules",
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  import { getAccessControlledDoors, getAudioGateways, getBadgeReaders, getButtons, getCameraList, getDoorbellCameras, getDoorSensors, getEnvironmentalGateways, getEnvironmentalSensors, getKeypads, getMotionSensors, } from "./get-entity-tool-api.js";
3
3
  async function getDeviceFeatures(deviceUuid, requestModifiers, sessionId) {
4
4
  const response = await postApi({
@@ -1,6 +1,6 @@
1
1
  import z from "zod";
2
2
  import { FIVE_SECONDS_MS, THREE_HOURS_MS } from "../constants.js";
3
- import { postApi } from "../network.js";
3
+ import { postApi } from "../network/network.js";
4
4
  import { formatTimestamp } from "../util.js";
5
5
  import { tempFunc, TempUnit } from "../utils/temp.js";
6
6
  // Type definitions
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  import { formatTimestamp } from "../util.js";
3
3
  import { removeNulls } from "../utils/remove-nulls.js";
4
4
  export async function getFaceEvents(args, timeZone, requestModifiers, sessionId) {
@@ -1,5 +1,5 @@
1
1
  import { logger } from "../logger.js";
2
- import { postApi } from "../network.js";
2
+ import { postApi } from "../network/network.js";
3
3
  import { formatTimestamp } from "../util.js";
4
4
  import { tempFunc } from "../utils/temp.js";
5
5
  export async function getCameraList(requestModifiers, sessionId) {
@@ -28,7 +28,7 @@ export async function getBadgeReaders(requestModifiers, sessionId) {
28
28
  body: {},
29
29
  modifiers: requestModifiers,
30
30
  sessionId,
31
- }).then(response => {
31
+ }).then((response) => {
32
32
  return {
33
33
  badgeReaders: response.minimalStates.filter((badgeReader) => !!badgeReader.locationUuid),
34
34
  };
@@ -40,7 +40,7 @@ export async function getAccessControlledDoors(requestModifiers, sessionId) {
40
40
  body: {},
41
41
  modifiers: requestModifiers,
42
42
  sessionId,
43
- }).then(response => {
43
+ }).then((response) => {
44
44
  return {
45
45
  accessControlledDoors: (response.accessControlledDoors || []).map((door) => {
46
46
  return {
@@ -48,7 +48,8 @@ export async function getAccessControlledDoors(requestModifiers, sessionId) {
48
48
  locationUuid: door.locationUuid,
49
49
  policyUuid: door.policyUuid,
50
50
  remoteUnlockEnabled: door.remoteUnlockEnabled,
51
- associatedCameras: door.associatedCameras?.filter((c) => c !== null) ?? [],
51
+ associatedCameras: door.associatedCameras?.filter((c) => c !== null) ??
52
+ [],
52
53
  name: door.name,
53
54
  uuid: door.uuid,
54
55
  };
@@ -62,7 +63,7 @@ export async function getAudioGateways(timeZone, requestModifiers, sessionId) {
62
63
  body: {},
63
64
  modifiers: requestModifiers,
64
65
  sessionId,
65
- }).then(response => {
66
+ }).then((response) => {
66
67
  return {
67
68
  audioGateways: response.audioGatewayStates
68
69
  .filter((camera) => !!camera.locationUuid)
@@ -81,7 +82,7 @@ export async function getDoorSensors(requestModifiers, sessionId) {
81
82
  body: {},
82
83
  modifiers: requestModifiers,
83
84
  sessionId,
84
- }).then(response => {
85
+ }).then((response) => {
85
86
  return {
86
87
  doorStates: response.doorStates.filter((door) => !!door.locationUuid),
87
88
  };
@@ -93,7 +94,7 @@ export async function getEnvironmentalSensors(timeZone, tempUnit, requestModifie
93
94
  body: {},
94
95
  modifiers: requestModifiers,
95
96
  sessionId,
96
- }).then(response => {
97
+ }).then((response) => {
97
98
  logger.info("Using tempUnit: ", tempUnit);
98
99
  return {
99
100
  climateStates: response.climateStates
@@ -117,7 +118,7 @@ export async function getMotionSensors(requestModifiers, sessionId) {
117
118
  body: {},
118
119
  modifiers: requestModifiers,
119
120
  sessionId,
120
- }).then(response => {
121
+ }).then((response) => {
121
122
  return {
122
123
  occupancySensorStates: response.occupancySensorStates.filter((occupancySensor) => !!occupancySensor.locationUuid),
123
124
  };
@@ -129,7 +130,7 @@ export async function getButtons(timeZone, requestModifiers, sessionId) {
129
130
  body: {},
130
131
  modifiers: requestModifiers,
131
132
  sessionId,
132
- }).then(response => {
133
+ }).then((response) => {
133
134
  return {
134
135
  buttonStates: response.states
135
136
  .filter((button) => !!button.locationUuid)
@@ -148,7 +149,7 @@ export async function getKeypads(requestModifiers, sessionId) {
148
149
  body: {},
149
150
  modifiers: requestModifiers,
150
151
  sessionId,
151
- }).then(response => {
152
+ }).then((response) => {
152
153
  return {
153
154
  keypadStates: response.keypads.filter((keypad) => !!keypad.locationUuid),
154
155
  };
@@ -160,7 +161,7 @@ export async function getEnvironmentalGateways(timeZone, requestModifiers, sessi
160
161
  body: {},
161
162
  modifiers: requestModifiers,
162
163
  sessionId,
163
- }).then(response => {
164
+ }).then((response) => {
164
165
  return {
165
166
  minimalEnvironmentalGatewayStates: response.minimalEnvironmentalGatewayStates
166
167
  .filter((gateway) => !!gateway.locationUuid)
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  export async function getOrg(requestModifiers, sessionId) {
3
3
  return await postApi({
4
4
  route: "/org/getOrgV2",
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  export async function getAllGuestsByOrg(requestModifiers, sessionId) {
3
3
  const res = await postApi({
4
4
  route: "/guestmanagement/getAllGuestsByOrg",
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  export async function getLocations(requestModifiers, sessionId) {
3
3
  const res = await postApi({
4
4
  route: "/location/getLocationsV2",
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  import { formatTimestamp } from "../util.js";
3
3
  export async function getVehicleEvents(args, timeZone, requestModifiers, sessionId) {
4
4
  const requestArgs = {
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  import { formatTimestamp } from "../util.js";
3
3
  export async function getPolicyAlerts(args, requestModifiers, sessionId) {
4
4
  return await postApi({
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  export async function rebootCameras(cameraUuids, requestModifiers, sessionId) {
3
3
  let successCount = 0;
4
4
  let errorCount = 0;
@@ -1,6 +1,6 @@
1
1
  import { DateTime } from "luxon";
2
2
  import { getLogger } from "../logger.js";
3
- import { postApi } from "../network.js";
3
+ import { postApi } from "../network/network.js";
4
4
  import { ReportTypeEnum } from "../types/schema.js";
5
5
  import { formatTimestamp } from "../util.js";
6
6
  const REPORT_TYPES_THAT_RETURN_UTC = new Set([
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  export async function listRules(requestModifiers, sessionId) {
3
3
  const res = await postApi({
4
4
  route: "/rules/getRulesForOrg",
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  export async function searchLicensePlates(query, startTimeMs, endTimeMs, deviceUuids, locationUuids, requestModifiers, sessionId) {
3
3
  const body = {
4
4
  licensePlate: query,
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  /**
3
3
  * Updates camera configuration using the faceted config API
4
4
  */
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  export async function resolveUserUuid(email, requestModifiers, sessionId) {
3
3
  const res = await postApi({
4
4
  route: "/user/findUserByEmail",
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  function mapAuditEvents(events) {
3
3
  return events.map(evt => ({
4
4
  uuid: evt.uuid ?? undefined,
@@ -1,4 +1,4 @@
1
- import { postApi } from "../network.js";
1
+ import { postApi } from "../network/network.js";
2
2
  export async function listUsers(requestModifiers, sessionId) {
3
3
  const res = await postApi({
4
4
  route: "/user/getUsersInOrg",
@@ -27,6 +27,7 @@ export default async function createServer() {
27
27
  const server = new McpServer({
28
28
  name: "rhombus-node-mcp",
29
29
  version: "1.0.0",
30
+ }, {
30
31
  capabilities: {
31
32
  resources: {},
32
33
  tools: {},
@@ -45,7 +45,9 @@ function filterByTrie(obj, trie) {
45
45
  return obj;
46
46
  }
47
47
  if (Array.isArray(obj)) {
48
- const mapped = obj.map((item) => filterByTrie(item, trie)).filter((item) => item !== undefined);
48
+ const mapped = obj
49
+ .map((item) => filterByTrie(item, trie))
50
+ .filter((item) => item !== undefined);
49
51
  return mapped.length > 0 ? mapped : undefined;
50
52
  }
51
53
  if (typeof obj === "object" && obj !== null) {
@@ -180,8 +182,7 @@ export function applyFilterBy(obj, conditions) {
180
182
  */
181
183
  export function zodToDotNotationPaths(schema, prefix = "") {
182
184
  // Unwrap optional / nullable / default
183
- if (schema instanceof z.ZodOptional ||
184
- schema instanceof z.ZodNullable) {
185
+ if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) {
185
186
  return zodToDotNotationPaths(schema.unwrap(), prefix);
186
187
  }
187
188
  if (schema instanceof z.ZodDefault) {
@@ -248,11 +249,18 @@ function applyFilteringToResult(result, includeFields, filterBy) {
248
249
  let filteredStructured = result.structuredContent;
249
250
  if (filteredStructured) {
250
251
  if (filterBy?.length)
251
- filteredStructured = applyFilterBy(filteredStructured, filterBy) ?? filteredStructured;
252
+ filteredStructured =
253
+ applyFilterBy(filteredStructured, filterBy) ?? filteredStructured;
252
254
  if (effectiveIncludeFields?.length)
253
- filteredStructured = filterIncludedFields(filteredStructured, effectiveIncludeFields) ?? filteredStructured;
255
+ filteredStructured =
256
+ filterIncludedFields(filteredStructured, effectiveIncludeFields) ??
257
+ filteredStructured;
254
258
  }
255
- return { ...result, content: filteredContent, structuredContent: filteredStructured };
259
+ return {
260
+ ...result,
261
+ content: filteredContent,
262
+ structuredContent: filteredStructured,
263
+ };
256
264
  }
257
265
  /**
258
266
  * Returns a Proxy over an McpServer that intercepts every `registerTool` call to:
@@ -272,7 +280,7 @@ export function createFilteringProxy(server, blacklist = new Set()) {
272
280
  return Reflect.get(target, prop, receiver);
273
281
  }
274
282
  // biome-ignore lint/suspicious/noExplicitAny: proxy intercept
275
- return function (name, config, handler) {
283
+ return (name, config, handler) => {
276
284
  if (blacklist.has(name)) {
277
285
  return target.registerTool(name, config, handler);
278
286
  }
@@ -0,0 +1,60 @@
1
+ import { postApi } from "./network.js";
2
+ import createUuidMap from "./postApiMap.js";
3
+ /**
4
+ * Get a list of locations from the API.
5
+ *
6
+ * @param requestModifiers
7
+ * @param sessionId
8
+ * @returns
9
+ */
10
+ export async function getLocations(requestModifiers, sessionId) {
11
+ const res = await postApi({
12
+ route: "/location/getLocationsV2",
13
+ body: {},
14
+ modifiers: requestModifiers,
15
+ sessionId,
16
+ });
17
+ if (res.error) {
18
+ throw new Error(JSON.stringify(res));
19
+ }
20
+ return res.locations ?? [];
21
+ }
22
+ /**
23
+ * Get a map of locations by UUID from the API.
24
+ *
25
+ * @param requestModifiers
26
+ * @param sessionId
27
+ * @returns
28
+ */
29
+ export async function getLocationsMap(requestModifiers, sessionId) {
30
+ const locations = await getLocations(requestModifiers, sessionId);
31
+ return createUuidMap(locations, "uuid");
32
+ }
33
+ export async function resolveLocationFromDoorUuids(doorUuids, requestModifiers, sessionId) {
34
+ if (doorUuids.length === 0)
35
+ return {};
36
+ const res = await postApi({
37
+ route: "/component/findAccessControlledDoors",
38
+ body: {},
39
+ modifiers: requestModifiers,
40
+ sessionId,
41
+ });
42
+ if (res.error) {
43
+ throw new Error(`Failed to resolve door locations: ${JSON.stringify(res)}`);
44
+ }
45
+ const requestedDoorUuidSet = new Set(doorUuids);
46
+ const locationToDoorsMap = {};
47
+ for (const door of res.accessControlledDoors ?? []) {
48
+ if (!door?.uuid || !requestedDoorUuidSet.has(door.uuid) || !door.locationUuid)
49
+ continue;
50
+ if (!locationToDoorsMap[door.locationUuid]) {
51
+ locationToDoorsMap[door.locationUuid] = [];
52
+ }
53
+ locationToDoorsMap[door.locationUuid].push(door.uuid);
54
+ }
55
+ const locationUuids = Object.keys(locationToDoorsMap);
56
+ return {
57
+ locationUuid: locationUuids.length === 1 ? locationUuids[0] : undefined,
58
+ locationToDoorsMap: locationUuids.length > 0 ? locationToDoorsMap : undefined,
59
+ };
60
+ }
@@ -1,5 +1,5 @@
1
- import { logger } from "./logger.js";
2
- import { authStore } from "./transports/streamable-http.js";
1
+ import { logger } from "../logger.js";
2
+ import { authStore } from "../transports/streamable-http.js";
3
3
  export const RHOMBUS_API_KEY = process.env.RHOMBUS_API_KEY;
4
4
  export const serverUrl = process.env.RHOMBUS_API_SERVER || "api2.rhombussystems.com";
5
5
  export const BASE_URL = `https://${serverUrl}/api`;
@@ -120,7 +120,12 @@ export async function postApi({ route, body, modifiers, sessionId, }) {
120
120
  logger.error(`[POSTAPI] ERROR - ${JSON.stringify(error || {}, null, 4)}`);
121
121
  return {
122
122
  error: true,
123
- status: `Request Error: ${error}`,
123
+ status: `Request Error: ${JSON.stringify(error)}`,
124
124
  };
125
125
  }
126
126
  }
127
+ export function throwIfApiError(res) {
128
+ if (res.error) {
129
+ throw new Error(res.status ?? "API request failed.");
130
+ }
131
+ }
@@ -0,0 +1,7 @@
1
+ export default function createUuidMap(data, uuidKey) {
2
+ const map = new Map();
3
+ for (const item of data) {
4
+ map.set(item[uuidKey], item);
5
+ }
6
+ return map;
7
+ }
@@ -1,7 +1,7 @@
1
1
  import { getCountReportsForDevicesAtLocation, getRunningAverage, getLineCrossingEnabledCameras, getBatchThresholdCrossingCountReport, getSummaryCountReport, triggerScenePrompt, } from "../api/report-tool-api.js";
2
2
  import { AnalyticsRequestType, OUTPUT_SCHEMA, TOOL_ARGS, } from "../types/analytics-tool-types.js";
3
3
  import { createToolStructuredContent, extractFromToolExtra } from "../util.js";
4
- import { postApi } from "../network.js";
4
+ import { postApi } from "../network/network.js";
5
5
  import { DateTime } from "luxon";
6
6
  const TOOL_NAME = "analytics-tool";
7
7
  const TOOL_DESCRIPTION = `
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { createCameraPolicy } from "../api/create-camera-policy-tool-api.js";
3
3
  import { ApiPayloadSchema, OUTPUT_SCHEMA } from "../types/create-camera-policy-tool-types.js";
4
- import { postApi } from "../network.js";
4
+ import { postApi } from "../network/network.js";
5
5
  const TOOL_NAME = "create-camera-policy-tool";
6
6
  const TOOL_DESCRIPTION = `
7
7
  A tool for creating a camera policy that walks users through a multi-step process.