rhombus-node-mcp 0.1.22 → 0.1.24

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 (82) hide show
  1. package/README.md +8 -0
  2. package/dist/api/access-control-tool-api.js +256 -0
  3. package/dist/api/alarm-monitoring-tool-api.js +65 -0
  4. package/dist/api/camera-tool-api.js +35 -15
  5. package/dist/api/camera-uptime-tool-api.js +112 -0
  6. package/dist/api/clips-tool-api.js +110 -21
  7. package/dist/api/create-tool-api.js +143 -16
  8. package/dist/api/door-tool-api.js +66 -0
  9. package/dist/api/events-tool-api.js +151 -28
  10. package/dist/api/faces-tool-api.js +118 -90
  11. package/dist/api/get-entity-tool-api.js +2 -0
  12. package/dist/api/guest-management-tool-api.js +75 -0
  13. package/dist/api/location-tool-api.js +48 -1
  14. package/dist/api/lpr-tool-api.js +26 -0
  15. package/dist/api/policy-alerts-tool-api.js +54 -0
  16. package/dist/api/report-tool-api.js +345 -23
  17. package/dist/api/rules-tool-api.js +78 -0
  18. package/dist/api/search-tool-api.js +93 -0
  19. package/dist/api/time-tool-api.js +3 -1
  20. package/dist/api/update-tool-api.js +204 -0
  21. package/dist/api/user-access-trail-tool-api.js +45 -0
  22. package/dist/api/user-audit-tool-api.js +47 -0
  23. package/dist/api/user-tool-api.js +77 -0
  24. package/dist/createServer.js +3 -1
  25. package/dist/filtering-utils.js +298 -0
  26. package/dist/index.js +0 -9
  27. package/dist/logger.js +6 -5
  28. package/dist/network.js +18 -4
  29. package/dist/tools/access-control-tool.js +97 -0
  30. package/dist/tools/alarm-monitoring-tool.js +50 -0
  31. package/dist/tools/analytics-tool.js +383 -0
  32. package/dist/tools/camera-tool.js +27 -9
  33. package/dist/tools/camera-uptime-tool.js +50 -0
  34. package/dist/tools/clips-tool.js +46 -16
  35. package/dist/tools/door-tool.js +66 -0
  36. package/dist/tools/events-tool.js +103 -81
  37. package/dist/tools/faces-tool.js +175 -32
  38. package/dist/tools/get-entity-tool.js +6 -1
  39. package/dist/tools/guest-management-tool.js +50 -0
  40. package/dist/tools/location-tool.js +23 -4
  41. package/dist/tools/lpr-tool.js +26 -20
  42. package/dist/tools/policy-alerts-tool.js +34 -4
  43. package/dist/tools/report-tool.js +181 -45
  44. package/dist/tools/rules-tool.js +76 -0
  45. package/dist/tools/search-tool.js +69 -0
  46. package/dist/tools/update-tool.js +63 -2
  47. package/dist/tools/user-access-trail-tool.js +64 -0
  48. package/dist/tools/user-audit-tool.js +53 -0
  49. package/dist/tools/user-tool.js +55 -0
  50. package/dist/tools/video-walls-tool.js +41 -0
  51. package/dist/transports/streamable-http.js +25 -15
  52. package/dist/types/access-control-tool-types.js +120 -0
  53. package/dist/types/alarm-monitoring-tool-types.js +60 -0
  54. package/dist/types/analytics-tool-types.js +190 -0
  55. package/dist/types/camera-tool-types.js +3 -2
  56. package/dist/types/camera-uptime-tool-types.js +52 -0
  57. package/dist/types/clips-tool-types.js +60 -11
  58. package/dist/types/door-tool-types.js +64 -0
  59. package/dist/types/events-tools-types.js +71 -3
  60. package/dist/types/faces-tools-types.js +36 -0
  61. package/dist/types/guest-management-tool-types.js +57 -0
  62. package/dist/types/location-tool-types.js +10 -1
  63. package/dist/types/lpr-tool-types.js +14 -0
  64. package/dist/types/policy-alerts-tool-types.js +38 -3
  65. package/dist/types/report-tool-types.js +284 -42
  66. package/dist/types/rules-tool-types.js +58 -0
  67. package/dist/types/schema.js +3123 -1
  68. package/dist/types/search-tool-types.js +82 -0
  69. package/dist/types/update-tool-types.js +4 -1
  70. package/dist/types/user-access-trail-tool-types.js +54 -0
  71. package/dist/types/user-audit-tool-types.js +48 -0
  72. package/dist/types/user-tool-types.js +61 -0
  73. package/dist/types/video-walls-tool-types.js +59 -0
  74. package/dist/types/zod-schemas.js +3237 -986
  75. package/dist/types.js +0 -21
  76. package/dist/util.js +34 -57
  77. package/package.json +7 -5
  78. package/dist/disabled-tools/semantic-search-tool.js +0 -90
  79. package/dist/tools/create-tool.js +0 -30
  80. package/dist/types/create-tool-types.js +0 -8
  81. package/dist/types/schema-components.js +0 -7211
  82. package/dist/types/semantic-search-tool-types.js +0 -5
@@ -0,0 +1,78 @@
1
+ import { postApi } from "../network.js";
2
+ export async function listRules(requestModifiers, sessionId) {
3
+ const res = await postApi({
4
+ route: "/rules/getRulesForOrg",
5
+ body: {},
6
+ modifiers: requestModifiers,
7
+ sessionId,
8
+ });
9
+ if (res.error) {
10
+ throw new Error(JSON.stringify(res));
11
+ }
12
+ return (res.rules?.map((rule) => ({
13
+ uuid: rule.uuid ?? undefined,
14
+ name: rule.name ?? undefined,
15
+ enabled: rule.enabled ?? undefined,
16
+ orgUuid: rule.orgUuid ?? undefined,
17
+ ruleType: rule.ruleType ?? undefined,
18
+ createdAtMs: rule.createdAtMs ?? undefined,
19
+ updatedAtMs: rule.updatedAtMs ?? undefined,
20
+ })) ?? []);
21
+ }
22
+ export async function createRule(ruleConfig, requestModifiers, sessionId) {
23
+ const res = await postApi({
24
+ route: "/rules/createRule",
25
+ body: ruleConfig,
26
+ modifiers: requestModifiers,
27
+ sessionId,
28
+ });
29
+ if (res.error) {
30
+ throw new Error(JSON.stringify(res));
31
+ }
32
+ return {
33
+ uuid: res.ruleUuid ?? undefined,
34
+ success: true,
35
+ };
36
+ }
37
+ export async function updateRule(ruleConfig, requestModifiers, sessionId) {
38
+ const res = await postApi({
39
+ route: "/rules/updateRule",
40
+ body: ruleConfig,
41
+ modifiers: requestModifiers,
42
+ sessionId,
43
+ });
44
+ if (res.error) {
45
+ throw new Error(JSON.stringify(res));
46
+ }
47
+ return { success: true };
48
+ }
49
+ export async function deleteRule(ruleUuid, requestModifiers, sessionId) {
50
+ const res = await postApi({
51
+ route: "/rules/deleteRule",
52
+ body: { ruleUuid },
53
+ modifiers: requestModifiers,
54
+ sessionId,
55
+ });
56
+ if (res.error) {
57
+ throw new Error(JSON.stringify(res));
58
+ }
59
+ return { uuid: ruleUuid, success: true };
60
+ }
61
+ export async function getRuleRecords(ruleUuid, requestModifiers, sessionId) {
62
+ const res = await postApi({
63
+ route: "/rulesRecords/getRulesEventRecords",
64
+ body: { ruleUuid },
65
+ modifiers: requestModifiers,
66
+ sessionId,
67
+ });
68
+ if (res.error) {
69
+ throw new Error(JSON.stringify(res));
70
+ }
71
+ return (res.rulesEventRecords?.map((record) => ({
72
+ uuid: record.uuid ?? undefined,
73
+ ruleUuid: record.ruleUuid ?? undefined,
74
+ triggeredAtMs: record.triggeredAtMs ?? undefined,
75
+ deviceUuid: record.deviceUuid ?? undefined,
76
+ locationUuid: record.locationUuid ?? undefined,
77
+ })) ?? []);
78
+ }
@@ -0,0 +1,93 @@
1
+ import { postApi } from "../network.js";
2
+ export async function searchLicensePlates(query, startTimeMs, endTimeMs, deviceUuids, locationUuids, requestModifiers, sessionId) {
3
+ const body = {
4
+ licensePlate: query,
5
+ startTime: startTimeMs,
6
+ endTime: endTimeMs,
7
+ deviceUuids: deviceUuids ?? undefined,
8
+ locationUuids: locationUuids ?? undefined,
9
+ };
10
+ const res = await postApi({
11
+ route: "/search/searchLicensePlates",
12
+ body,
13
+ modifiers: requestModifiers,
14
+ sessionId,
15
+ });
16
+ if (res.error) {
17
+ throw new Error(JSON.stringify(res));
18
+ }
19
+ return (res.vehicleEvents?.map((event) => ({
20
+ licensePlate: event.vehicleLicensePlate ?? undefined,
21
+ deviceUuid: event.deviceUuid ?? undefined,
22
+ timestampMs: event.eventTimestamp ?? undefined,
23
+ confidence: event.confidence ?? undefined,
24
+ })) ?? []);
25
+ }
26
+ export async function searchObjectsByColor(color, cameraUuid, startTimeMs, endTimeMs, requestModifiers, sessionId) {
27
+ const body = {
28
+ colorFilter: [color],
29
+ deviceFilter: [cameraUuid],
30
+ startTimeMs: startTimeMs,
31
+ endTimeMs: endTimeMs,
32
+ };
33
+ const res = await postApi({
34
+ route: "/search/searchObjectsByColor",
35
+ body,
36
+ modifiers: requestModifiers,
37
+ sessionId,
38
+ });
39
+ if (res.error) {
40
+ throw new Error(JSON.stringify(res));
41
+ }
42
+ return (res.objects?.map((result) => ({
43
+ deviceUuid: result.deviceUuid ?? undefined,
44
+ timestampMs: result.timestampMs ?? undefined,
45
+ objectType: result.objectType ?? undefined,
46
+ color: result.color ?? undefined,
47
+ })) ?? []);
48
+ }
49
+ export async function searchObjectsByText(textQuery, startTimeMs, endTimeMs, deviceUuids, requestModifiers, sessionId) {
50
+ const body = {
51
+ searchText: textQuery,
52
+ model: "CLIP_512",
53
+ queryStartTimeMs: startTimeMs,
54
+ queryEndTimeMs: endTimeMs,
55
+ queryDeviceUuids: deviceUuids ?? [],
56
+ maxNumResults: 100,
57
+ similarityThreshold: 0.5,
58
+ };
59
+ const res = await postApi({
60
+ route: "/search/searchSimilarObjectEmbeddingsByText",
61
+ body,
62
+ modifiers: requestModifiers,
63
+ sessionId,
64
+ });
65
+ if (res.error) {
66
+ throw new Error(JSON.stringify(res));
67
+ }
68
+ return (res.similarEmbeddings?.map((match) => ({
69
+ deviceUuid: match.embedding?.deviceUuid ?? undefined,
70
+ timestampMs: match.embedding?.timestamp ?? undefined,
71
+ score: match.distance ?? undefined,
72
+ })) ?? []);
73
+ }
74
+ export async function searchMotionGrid(cameraUuid, startTimeMs, endTimeMs, requestModifiers, sessionId) {
75
+ const body = {
76
+ deviceUuid: cameraUuid,
77
+ startTimeUtcSecs: Math.floor(startTimeMs / 1000),
78
+ endTimeUtcSecs: Math.floor(endTimeMs / 1000),
79
+ searchCells: [],
80
+ };
81
+ const res = await postApi({
82
+ route: "/event/searchMotionGrid",
83
+ body,
84
+ modifiers: requestModifiers,
85
+ sessionId,
86
+ });
87
+ if (res.error) {
88
+ throw new Error(JSON.stringify(res));
89
+ }
90
+ return (res.timeUtcSecsList?.map((ts) => ({
91
+ timestampSecs: ts,
92
+ })) ?? []);
93
+ }
@@ -21,7 +21,9 @@ function normalizeTimeDescription(description) {
21
21
  }
22
22
  // Handle "this" time periods - always refer to today
23
23
  if (normalized === "this morning") {
24
- return "today at 06:00";
24
+ // For occupancy/access-control investigations, "this morning" should include
25
+ // all events since local midnight unless a narrower range is explicitly requested.
26
+ return "today at 00:00";
25
27
  }
26
28
  if (normalized === "this afternoon") {
27
29
  return "today at 12:00";
@@ -59,6 +59,210 @@ export async function getCameraDetails(cameraUuid, requestModifiers, sessionId)
59
59
  };
60
60
  }
61
61
  }
62
+ async function getCameraHardwareVariation(cameraUuid, requestModifiers, sessionId) {
63
+ try {
64
+ const result = await postApi({
65
+ route: "/camera/getDetails",
66
+ body: {
67
+ cameraUuids: [cameraUuid],
68
+ },
69
+ modifiers: requestModifiers,
70
+ sessionId,
71
+ });
72
+ if (result.error) {
73
+ return {
74
+ success: false,
75
+ error: result.errorMsg || "Failed to get camera hardware variation",
76
+ };
77
+ }
78
+ const camera = result.cameras?.[0];
79
+ return {
80
+ success: true,
81
+ hwVariation: camera?.hwVariation ?? undefined,
82
+ };
83
+ }
84
+ catch (error) {
85
+ return {
86
+ success: false,
87
+ error: error instanceof Error ? error.message : "Unknown error occurred",
88
+ };
89
+ }
90
+ }
91
+ async function getOrgCameraConfigOptions(requestModifiers, sessionId) {
92
+ try {
93
+ const result = await postApi({
94
+ route: "/org/getOrgV2",
95
+ body: {},
96
+ modifiers: requestModifiers,
97
+ sessionId,
98
+ });
99
+ if (result.error) {
100
+ return {
101
+ success: false,
102
+ error: result.errorMsg || "Failed to get organization camera config options",
103
+ };
104
+ }
105
+ return {
106
+ success: true,
107
+ cameraConfigOptions: result.cameraConfigOptions ?? undefined,
108
+ };
109
+ }
110
+ catch (error) {
111
+ return {
112
+ success: false,
113
+ error: error instanceof Error ? error.message : "Unknown error occurred",
114
+ };
115
+ }
116
+ }
117
+ async function getCameraFacetedConfig(cameraUuid, requestModifiers, sessionId) {
118
+ try {
119
+ const result = await postApi({
120
+ route: "/camera/getFacetedConfig",
121
+ body: {
122
+ deviceUuid: cameraUuid,
123
+ },
124
+ modifiers: requestModifiers,
125
+ sessionId,
126
+ });
127
+ if (result.error) {
128
+ return {
129
+ success: false,
130
+ error: result.errorMsg || "Failed to get camera faceted config",
131
+ };
132
+ }
133
+ return {
134
+ success: true,
135
+ config: result.config,
136
+ };
137
+ }
138
+ catch (error) {
139
+ return {
140
+ success: false,
141
+ error: error instanceof Error ? error.message : "Unknown error occurred",
142
+ };
143
+ }
144
+ }
145
+ export async function validateCameraFeatureSupport(payload, requestModifiers, sessionId) {
146
+ const requestedVideoSettings = Object.values(payload.configUpdate.videoFacetSettings ?? {});
147
+ const requestedAudioSettings = Object.values(payload.configUpdate.audioFacetSettings ?? {});
148
+ const requestedDeviceSettings = payload.configUpdate.deviceSettings;
149
+ if (requestedVideoSettings.length === 0 &&
150
+ requestedAudioSettings.length === 0 &&
151
+ !requestedDeviceSettings) {
152
+ return { canProceed: true };
153
+ }
154
+ const requestedFacet = Object.keys(payload.configUpdate.videoFacetSettings ?? {})[0] ??
155
+ Object.keys(payload.configUpdate.audioFacetSettings ?? {})[0] ??
156
+ "v0";
157
+ const requestedVideoSettingsForFacet = requestedVideoSettings[0];
158
+ const requestedAudioSettingsForFacet = requestedAudioSettings[0];
159
+ const requestedWdrStrength = requestedVideoSettings
160
+ .map(settings => settings.wdr_strength)
161
+ .find(value => value !== undefined && value !== null);
162
+ const hasWdrRequest = requestedVideoSettings.some(settings => settings.wdr_enabled !== undefined || settings.wdr_strength !== undefined);
163
+ const requestedResolution = requestedVideoSettingsForFacet?.resolution;
164
+ const hasResolutionRequest = requestedResolution?.width !== undefined && requestedResolution?.height !== undefined;
165
+ const hasAudioRequest = requestedAudioSettings.length > 0;
166
+ const [hardwareVariationResult, orgOptionsResult, facetedConfigResult] = await Promise.all([
167
+ getCameraHardwareVariation(payload.configUpdate.deviceUuid, requestModifiers, sessionId),
168
+ getOrgCameraConfigOptions(requestModifiers, sessionId),
169
+ getCameraFacetedConfig(payload.configUpdate.deviceUuid, requestModifiers, sessionId),
170
+ ]);
171
+ if (facetedConfigResult.success && facetedConfigResult.config) {
172
+ const videoFacetConfig = facetedConfigResult.config.videoFacetSettings?.[requestedFacet];
173
+ const audioFacetConfig = facetedConfigResult.config.audioFacetSettings?.[requestedFacet];
174
+ const deviceConfig = facetedConfigResult.config.deviceSettings;
175
+ if (hasAudioRequest && audioFacetConfig?.audio_supported === false) {
176
+ return {
177
+ canProceed: false,
178
+ error: "This camera does not support audio features.",
179
+ };
180
+ }
181
+ const unsupportedVideoFields = getUnsupportedFieldsFromSetMethodMap(requestedVideoSettingsForFacet, videoFacetConfig?.updatedSetMethodMap);
182
+ if (unsupportedVideoFields.length > 0) {
183
+ return {
184
+ canProceed: false,
185
+ error: `This camera does not support updating video setting: ${unsupportedVideoFields[0]}.`,
186
+ };
187
+ }
188
+ const unsupportedAudioFields = getUnsupportedFieldsFromSetMethodMap(requestedAudioSettingsForFacet, audioFacetConfig?.updatedSetMethodMap);
189
+ if (unsupportedAudioFields.length > 0) {
190
+ return {
191
+ canProceed: false,
192
+ error: `This camera does not support updating audio setting: ${unsupportedAudioFields[0]}.`,
193
+ };
194
+ }
195
+ const unsupportedDeviceFields = getUnsupportedFieldsFromSetMethodMap(requestedDeviceSettings, deviceConfig?.updatedSetMethodMap);
196
+ if (unsupportedDeviceFields.length > 0) {
197
+ return {
198
+ canProceed: false,
199
+ error: `This camera does not support updating device setting: ${unsupportedDeviceFields[0]}.`,
200
+ };
201
+ }
202
+ }
203
+ // Model-level capability metadata can be unavailable on some deployments; do not hard-fail.
204
+ if (!hardwareVariationResult.success || !orgOptionsResult.success) {
205
+ return { canProceed: true };
206
+ }
207
+ const hwVariation = hardwareVariationResult.hwVariation;
208
+ if (!hwVariation) {
209
+ return { canProceed: true };
210
+ }
211
+ const cameraOptions = orgOptionsResult.cameraConfigOptions?.[hwVariation];
212
+ if ((!hasWdrRequest && !hasResolutionRequest) ||
213
+ !Array.isArray(cameraOptions) ||
214
+ cameraOptions.length === 0) {
215
+ return { canProceed: true };
216
+ }
217
+ if (hasWdrRequest) {
218
+ const wdrOption = cameraOptions.find(option => option?.wdrRange !== undefined);
219
+ if (!wdrOption?.wdrRange) {
220
+ return {
221
+ canProceed: false,
222
+ error: "This camera does not support WDR feature.",
223
+ };
224
+ }
225
+ if (typeof requestedWdrStrength === "number") {
226
+ const min = wdrOption.wdrRange.min;
227
+ const max = wdrOption.wdrRange.max;
228
+ if (typeof min === "number" && requestedWdrStrength < min) {
229
+ return {
230
+ canProceed: false,
231
+ error: `This camera supports WDR, but wdr_strength=${requestedWdrStrength} is below the supported minimum (${min}).`,
232
+ };
233
+ }
234
+ if (typeof max === "number" && requestedWdrStrength > max) {
235
+ return {
236
+ canProceed: false,
237
+ error: `This camera supports WDR, but wdr_strength=${requestedWdrStrength} exceeds the supported maximum (${max}).`,
238
+ };
239
+ }
240
+ }
241
+ }
242
+ if (hasResolutionRequest) {
243
+ const isRequestedResolutionSupported = cameraOptions.some(option => {
244
+ const optionWidth = option?.resolution?.width;
245
+ const optionHeight = option?.resolution?.height;
246
+ return (typeof optionWidth === "number" &&
247
+ typeof optionHeight === "number" &&
248
+ optionWidth === requestedResolution.width &&
249
+ optionHeight === requestedResolution.height);
250
+ });
251
+ if (!isRequestedResolutionSupported) {
252
+ return {
253
+ canProceed: false,
254
+ error: `This camera does not support resolution ${requestedResolution.width}x${requestedResolution.height}.`,
255
+ };
256
+ }
257
+ }
258
+ return { canProceed: true };
259
+ }
260
+ function getUnsupportedFieldsFromSetMethodMap(requestedSettings, setMethodMap) {
261
+ if (!requestedSettings || !setMethodMap) {
262
+ return [];
263
+ }
264
+ return Object.keys(requestedSettings).filter(field => setMethodMap[field] === false);
265
+ }
62
266
  /**
63
267
  * Helper function to remove null/undefined fields from update payload
64
268
  */
@@ -0,0 +1,45 @@
1
+ import { postApi } from "../network.js";
2
+ export async function resolveUserUuid(email, requestModifiers, sessionId) {
3
+ const res = await postApi({
4
+ route: "/user/findUserByEmail",
5
+ body: { email },
6
+ modifiers: requestModifiers,
7
+ sessionId,
8
+ });
9
+ if (res.error) {
10
+ throw new Error(res.errorMsg ?? "Failed to find user by email");
11
+ }
12
+ const user = res.user;
13
+ if (!user?.uuid) {
14
+ throw new Error(`No user found with email: ${email}`);
15
+ }
16
+ const name = [user.firstName, user.lastName].filter(Boolean).join(" ") || undefined;
17
+ return { uuid: user.uuid, name };
18
+ }
19
+ export async function getAccessEventsByUser(userUuid, startTimeMs, endTimeMs, limit, requestModifiers, sessionId) {
20
+ const body = {
21
+ userUuid,
22
+ limit: limit ?? 100,
23
+ };
24
+ if (startTimeMs)
25
+ body.createdAfterMs = startTimeMs;
26
+ if (endTimeMs)
27
+ body.createdBeforeMs = endTimeMs;
28
+ const res = await postApi({
29
+ route: "/component/findComponentEventsByUser",
30
+ body,
31
+ modifiers: requestModifiers,
32
+ sessionId,
33
+ });
34
+ const events = res.componentEvents ?? [];
35
+ return events.map((evt) => ({
36
+ eventUuid: evt.uuid ?? undefined,
37
+ eventType: evt.type ?? undefined,
38
+ timestampMs: evt.timestampMs ?? evt.createdAtMs ?? undefined,
39
+ locationUuid: evt.locationUuid ?? undefined,
40
+ componentUuid: evt.componentUuid ?? undefined,
41
+ ownerDeviceUuid: evt.ownerDeviceUuid ?? undefined,
42
+ authorizationResult: evt.authorizationResult ?? undefined,
43
+ authenticationResult: evt.authenticationResult ?? undefined,
44
+ }));
45
+ }
@@ -0,0 +1,47 @@
1
+ import { postApi } from "../network.js";
2
+ function mapAuditEvents(events) {
3
+ return events.map(evt => ({
4
+ uuid: evt.uuid ?? undefined,
5
+ action: evt.action ?? undefined,
6
+ displayText: evt.displayText ?? undefined,
7
+ principalName: evt.principalName ?? undefined,
8
+ principalUuid: evt.principalUuid ?? undefined,
9
+ targetName: evt.targetName ?? undefined,
10
+ targetUuid: evt.targetUuid ?? undefined,
11
+ timestamp: evt.timestamp ?? undefined,
12
+ failure: evt.failure ?? undefined,
13
+ sourceIp: evt.sourceIp ?? undefined,
14
+ sourceCity: evt.sourceCity ?? undefined,
15
+ sourceCountry: evt.sourceCountry ?? undefined,
16
+ }));
17
+ }
18
+ export async function getAuditFeedForPrincipal(principalUuid, maxResults, requestModifiers, sessionId) {
19
+ const res = await postApi({
20
+ route: "/report/getAuditFeedForPrincipal",
21
+ body: {
22
+ principalUuid,
23
+ maxResults: maxResults ?? 50,
24
+ },
25
+ modifiers: requestModifiers,
26
+ sessionId,
27
+ });
28
+ if (res.error) {
29
+ throw new Error(res.errorMsg ?? "Failed to get audit feed for principal");
30
+ }
31
+ return mapAuditEvents(res.auditEvents ?? []);
32
+ }
33
+ export async function getAuditFeedForTarget(targetUuid, maxResults, requestModifiers, sessionId) {
34
+ const res = await postApi({
35
+ route: "/report/getAuditFeedForTarget",
36
+ body: {
37
+ targetUuid,
38
+ maxResults: maxResults ?? 50,
39
+ },
40
+ modifiers: requestModifiers,
41
+ sessionId,
42
+ });
43
+ if (res.error) {
44
+ throw new Error(res.errorMsg ?? "Failed to get audit feed for target");
45
+ }
46
+ return mapAuditEvents(res.auditEvents ?? []);
47
+ }
@@ -0,0 +1,77 @@
1
+ import { postApi } from "../network.js";
2
+ export async function listUsers(requestModifiers, sessionId) {
3
+ const res = await postApi({
4
+ route: "/user/getUsersInOrg",
5
+ body: {},
6
+ modifiers: requestModifiers,
7
+ sessionId,
8
+ });
9
+ if (res.error) {
10
+ throw new Error(JSON.stringify(res));
11
+ }
12
+ return (res.users?.map(user => ({
13
+ uuid: user.uuid ?? undefined,
14
+ email: user.email ?? undefined,
15
+ firstName: user.firstName ?? undefined,
16
+ lastName: user.lastName ?? undefined,
17
+ })) ?? []);
18
+ }
19
+ export async function findUserByEmail(email, requestModifiers, sessionId) {
20
+ const res = await postApi({
21
+ route: "/user/findUserByEmail",
22
+ body: { email },
23
+ modifiers: requestModifiers,
24
+ sessionId,
25
+ });
26
+ if (res.error) {
27
+ throw new Error(JSON.stringify(res));
28
+ }
29
+ const user = res.user;
30
+ if (!user)
31
+ return undefined;
32
+ return {
33
+ uuid: user.uuid ?? undefined,
34
+ email: user.email ?? undefined,
35
+ firstName: user.firstName ?? undefined,
36
+ lastName: user.lastName ?? undefined,
37
+ role: user.roleUuid ?? undefined,
38
+ orgUuid: user.orgUuid ?? undefined,
39
+ lastLoginMs: user.lastLoginMs ?? undefined,
40
+ enabled: user.enabled ?? undefined,
41
+ mfaEnabled: user.mfaEnabled ?? undefined,
42
+ };
43
+ }
44
+ export async function getPermissionsForCurrentUser(requestModifiers, sessionId) {
45
+ const res = await postApi({
46
+ route: "/permission/getPermissionsForCurrentUser",
47
+ body: {},
48
+ modifiers: requestModifiers,
49
+ sessionId,
50
+ });
51
+ if (res.error) {
52
+ throw new Error(JSON.stringify(res));
53
+ }
54
+ return {
55
+ role: res.role ?? undefined,
56
+ functionalityAccessMap: res.functionalityAccessMap
57
+ ? Object.fromEntries(Object.entries(res.functionalityAccessMap).map(([k, v]) => [k, String(v)]))
58
+ : undefined,
59
+ };
60
+ }
61
+ export async function getPermissionGroups(requestModifiers, sessionId) {
62
+ const res = await postApi({
63
+ route: "/permission/getPermissionGroupsForOrg",
64
+ body: {},
65
+ modifiers: requestModifiers,
66
+ sessionId,
67
+ });
68
+ if (res.error) {
69
+ throw new Error(JSON.stringify(res));
70
+ }
71
+ return (res.permissionGroups?.map(group => ({
72
+ uuid: group.uuid ?? undefined,
73
+ name: group.name ?? undefined,
74
+ orgUuid: group.orgUuid ?? undefined,
75
+ role: group.role ?? undefined,
76
+ })) ?? []);
77
+ }
@@ -1,5 +1,6 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { logger } from "./logger.js";
3
+ import { createFilteringProxy } from "./util.js";
3
4
  import getResources from "./resources/getResources.js";
4
5
  import getTools from "./tools/getTools.js";
5
6
  let initiated = false;
@@ -35,9 +36,10 @@ export default async function createServer() {
35
36
  resource.create(server);
36
37
  }
37
38
  logger.info(`🛠️ Registered ${resources.length} resources`);
39
+ const filteredServer = createFilteringProxy(server, new Set(["time-tool", "count-tool", "time-conversion-tool"]));
38
40
  for (const tool of tools) {
39
41
  try {
40
- await tool.create(server);
42
+ await tool.create(filteredServer);
41
43
  }
42
44
  catch (error) {
43
45
  logger.error(`Failed to register tool ${tool.name}:`, error);