rhombus-node-mcp 0.1.9 โ†’ 0.1.11

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 (3) hide show
  1. package/README.md +54 -18
  2. package/dist/index.js +129 -4
  3. package/package.json +4 -1
package/README.md CHANGED
@@ -1,20 +1,39 @@
1
- # Rhombus MCP Server
1
+ # ๐Ÿš€ Rhombus MCP Server - Supercharge Your AI Tools!
2
2
 
3
- An MCP server implementation that integrates the Rhombus API to provide Chatbot tools.
3
+ Unleash the power of the Rhombus API with this cutting-edge MCP server implementation! Transform your chatbot experience with advanced security and surveillance capabilities at your fingertips.
4
4
 
5
- ## Configuration
5
+ ## ๐Ÿ” About Rhombus
6
6
 
7
- ### Step 1: Get a Rhombus API Key
7
+ Rhombus is the #1 rated commercial security system, built to protect and designed to adapt! We deliver enterprise-grade physical security with a cybersecurity focus, combining:
8
8
 
9
- 1. Sign up for a [Rhombus Account](https://console.rhombus.com).
10
- 2. Create an API Key at [API Key Settings](https://console.rhombus.com/settings/api-management)
11
- 3. Set the API key in your environment as `RHOMBUS_API_KEY`.
9
+ * ๐Ÿ’ก **Smart Cameras** - Innovative dome, fisheye, bullet, and multisensor security cameras
10
+ * ๐Ÿšช **Access Control** - Secure door controllers, readers, and intercoms with mobile, card, and fob access
11
+ * ๐Ÿ“ก **IoT Sensors** - Advanced audio, environmental, motion, entry, and multipurpose panic sensors
12
+ * ๐Ÿ”’ **Alarm Monitoring** - TMA 5-diamond certified live agent verification and dispatch
12
13
 
13
- ### Step 2: Configure Claude Desktop
14
+ Our platform is powered by actionable AI insights that help you respond faster with real-time detections, all while being rooted in strong cybersecurity principles.
14
15
 
15
- 1. Download Claude desktop [here](https://claude.ai/download).
16
+ ## ๐Ÿงช Join Our Beta Test!
16
17
 
17
- 2. Add this to your `claude_desktop_config.json`:
18
+ **We're thrilled to be part of the AI Generation!** Help us shape the future of AI-powered security by testing our MCP server implementation and providing valuable feedback.
19
+
20
+ ๐Ÿ‘‰ **[Fill out our beta test form here](https://rhmbs.link/beta_test)** ๐Ÿ‘ˆ
21
+
22
+ Your insights will directly influence our development roadmap and help us create the most powerful AI security tools on the market!
23
+
24
+ ## ๐Ÿ”ง Quick Setup
25
+
26
+ ### Step 1: Give Your Rhombus Console Superpowers
27
+
28
+ 1. Login to your Rhombus Console - [Login Here](https://console.rhombus.com)! ๐Ÿ”
29
+ 2. Generate your magic key at [API Key Settings](https://console.rhombus.com/settings/api-management) โœจ
30
+ 3. Activate your powers by setting `RHOMBUS_API_KEY` in your environment ๐Ÿ’ช
31
+
32
+ ### Step 2: Supercharge Claude Desktop
33
+
34
+ 1. Grab the incredible Claude desktop [right here](https://claude.ai/download)! ๐Ÿ“ฅ
35
+
36
+ 2. Unleash the magic by adding this to your `claude_desktop_config.json`:
18
37
 
19
38
  ### DOCKER
20
39
 
@@ -61,18 +80,35 @@ You can access the file using:
61
80
  vim ~/Library/Application\ Support/Claude/claude_desktop_config.json
62
81
  ```
63
82
 
64
- ### Step 3: Testing
83
+ ### Step 3: Take It For A Spin! ๐ŸŽก
84
+
85
+ Time to see the magic happen! Let's verify that Claude for Desktop is detecting our awesome `rhombus` server tools:
86
+
87
+ 1. Look for the powerful hammer icon in Claude's interface โš’๏ธ
88
+ 2. Click it to reveal your new arsenal of tools
89
+ 3. Spot the Filesystem MCP Server tools in the list
90
+
91
+ If they're visible, YOU'VE DONE IT! ๐ŸŽ‰ Your integration is live and ready to rock! Claude can now communicate directly with Rhombus systems, giving you the same incredible capabilities as the Rhombus web app, but with the added power of Claude's intelligence!
92
+
93
+ ### Hitting a Snag? We've Got You! ๐Ÿ›Ÿ
94
+
95
+ Check out Claude's fantastic [troubleshooting guide](https://modelcontextprotocol.io/docs/tools/debugging) for quick fixes! Still stuck? Our team of experts is ready to help!
96
+
97
+ * Email us: developer@rhombus.com ๐Ÿ“ง
98
+ * Share your feedback via our [beta test form](https://rhmbs.link/beta_test) ๐ŸŒŸ
65
99
 
66
- Let's make sure Claude for Desktop is picking up the tools we've exposed in our `rhombus` server. You can do this by looking for the hammer icon.
100
+ We're committed to making your experience absolutely seamless and are SUPER excited to hear what amazing applications you're building with our tools!
67
101
 
68
- After clicking on the hammer icon, you should see the tools that come with the Filesystem MCP Server:
102
+ ## ๐Ÿค We Want Your Feedback!
69
103
 
70
- If you see both of these this means that the integration is active. Congratulations! This means Claude can now ask Rhombus questions. You can then simply use it as you would use the Rhombus web app.
104
+ As pioneers in the AI Generation, we're passionate about pushing the boundaries of what's possible when combining advanced physical security with cutting-edge AI tools. Your feedback is invaluable as we continue to innovate and improve.
71
105
 
72
- ### Troubleshooting
106
+ * What features would make your workflow even better?
107
+ * How are you using our MCP server in your projects?
108
+ * What integrations would you like to see next?
73
109
 
74
- The Claude documentation provides an excellent [troubleshooting guide](https://modelcontextprotocol.io/docs/tools/debugging) you can refer to. However, you can still reach out to us at api@rhombus.com for any additional support or [file a bug](https://github.com/ppl-ai/api-discussion/issues).
110
+ Share your thoughts, ideas, and success stories with us! Together, we're creating the future of intelligent security.
75
111
 
76
- ## License
112
+ ## ๐Ÿ“œ License
77
113
 
78
- This MCP server is licensed under the MIT License. This means you are free to use, modify, and distribute the software, subject to the terms and conditions of the MIT License. For more details, please see the LICENSE file in the project repository.
114
+ This awesome MCP server comes with the freedom of the MIT License! โœ… Use it, โœ… modify it, โœ… share it - the possibilities are endless! Just remember to follow the MIT License terms and conditions. Check out the LICENSE file in our repository for all the details.
package/dist/index.js CHANGED
@@ -2,6 +2,9 @@
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { z } from "zod";
5
+ import { CreateVideoWallOptions } from "./types.js";
6
+ import { parse } from "chrono-node";
7
+ import { DateTime } from "luxon";
5
8
  const THREE_HOURS_MS = 3 * 60 * 60 * 1000;
6
9
  const FIVE_SECONDS_MS = 5 * 1000;
7
10
  const RHOMBUS_API_KEY = process.env.RHOMBUS_API_KEY;
@@ -69,6 +72,7 @@ async function postApi(url, body, modifiers) {
69
72
  const response = await fetch(url, { method: "POST", headers: requestHeaders, body });
70
73
  log(`[POSTAPI] RESPONSE - ${JSON.stringify(response)}`);
71
74
  if (!response.ok) {
75
+ log(`โŒ RESPONSE - ${response.ok} - ${response.status}`);
72
76
  if (response.status === 401 || response.status === 403) {
73
77
  return {
74
78
  error: true,
@@ -77,7 +81,9 @@ async function postApi(url, body, modifiers) {
77
81
  }
78
82
  throw new Error(`HTTP error! status: ${response.status}`);
79
83
  }
80
- return await response.json();
84
+ const ret = await response.json();
85
+ log(`โŒ RESPONSE - ${response.ok} - ${JSON.stringify(ret)}`);
86
+ return ret;
81
87
  }
82
88
  catch (error) {
83
89
  log(`[POSTAPI] ERROR - ${JSON.stringify(error || {})}`);
@@ -181,6 +187,25 @@ async function rebootCameras(cameraUuids, requestModifiers) {
181
187
  return { status, successCount, errorCount };
182
188
  }
183
189
  }
190
+ async function createVideoWall(options, headers) {
191
+ const url = BASE_URL + "/camera/createVideoWall";
192
+ const body = JSON.stringify({
193
+ videoWall: {
194
+ displayName: options?.displayName,
195
+ deviceList: options?.deviceList,
196
+ othersCanEdit: true,
197
+ orgUuid: options?.orgUuid,
198
+ shared: true,
199
+ settings: {
200
+ gridSize: { width: options?.settings.columnCount, height: options?.settings.columnCount },
201
+ gridLayout: "1 2\n3 4",
202
+ intervalSeconds: options?.settings.intervalSeconds || 5,
203
+ },
204
+ },
205
+ });
206
+ const response = await postApi(url, body, headers);
207
+ return response;
208
+ }
184
209
  async function getImageForCameraAtTime(cameraUuid, timestampMs, requestModifiers) {
185
210
  const url = BASE_URL + "/video/getExactFrameUri";
186
211
  const body = JSON.stringify({
@@ -231,6 +256,104 @@ server.tool("get-org-information", "Get general information about the organizati
231
256
  ],
232
257
  };
233
258
  });
259
+ async function handleCreateVideoWallRequest(videoWallCreateOptions, headers) {
260
+ let text = "Unable to create video wall!";
261
+ // console.error("๐Ÿ”จ Creating video wall");
262
+ if (!videoWallCreateOptions?.displayName) {
263
+ text = JSON.stringify({
264
+ needUserInput: true,
265
+ commandForUser: "What should the name of the video wall be?",
266
+ });
267
+ }
268
+ else if ((videoWallCreateOptions?.deviceList || []).length === 0) {
269
+ text = JSON.stringify({
270
+ needUserInput: true,
271
+ commandForUser: "Which cameras would you like on this video wall?",
272
+ });
273
+ }
274
+ else {
275
+ // console.error("Creating video wall with options: ", JSON.stringify(videoWallCreateOptions));
276
+ text = JSON.stringify(await createVideoWall(videoWallCreateOptions, headers));
277
+ }
278
+ return Promise.resolve({
279
+ content: [
280
+ {
281
+ type: "text",
282
+ text,
283
+ },
284
+ ],
285
+ });
286
+ }
287
+ server.tool("create-tool", "Tool for creating many entity types such as video walls.", {
288
+ entityType: z.enum(["video-wall"]).describe("The entity type to create. Example: video wall."),
289
+ videoWallCreateOptions: CreateVideoWallOptions,
290
+ ...STATIC_ARGS,
291
+ }, async ({ entityType, videoWallCreateOptions, requestModifiers }) => {
292
+ switch (entityType) {
293
+ case "video-wall":
294
+ return await handleCreateVideoWallRequest(videoWallCreateOptions, requestModifiers);
295
+ default:
296
+ }
297
+ return {
298
+ content: [
299
+ {
300
+ type: "text",
301
+ text: "",
302
+ },
303
+ ],
304
+ };
305
+ });
306
+ function nullToUndefined(value) {
307
+ return value === null ? undefined : value;
308
+ }
309
+ server.tool("time-tool", "Tool for converting a natural language time description into a timestamp in milliseconds.", {
310
+ time_description: z
311
+ .string()
312
+ .describe("A natural language description of the time (e.g., '2pm today', 'tomorrow at noon')."),
313
+ timezone: z
314
+ .optional(z.string())
315
+ .describe("Optional IANA timezone string (e.g., 'America/Los_Angeles', 'UTC'). Defaults to system timezone."),
316
+ }, async ({ time_description, timezone }) => {
317
+ // console.error(`๐Ÿ•› handling tool call for time ${time_description} using timezone ${timezone}`);
318
+ const now = DateTime.now()
319
+ .setZone(timezone || undefined)
320
+ .toJSDate();
321
+ const parsed = parse(time_description, now, { forwardDate: true });
322
+ if (!parsed || parsed.length === 0) {
323
+ throw new Error(`Could not parse time description: ${time_description}`);
324
+ }
325
+ const dateComponents = parsed[0].start;
326
+ if (!dateComponents) {
327
+ throw new Error("Parsed time has no start component");
328
+ }
329
+ const dt = DateTime.fromObject({
330
+ year: nullToUndefined(dateComponents.get("year")),
331
+ month: nullToUndefined(dateComponents.get("month")),
332
+ day: nullToUndefined(dateComponents.get("day")),
333
+ hour: nullToUndefined(dateComponents.get("hour")),
334
+ minute: nullToUndefined(dateComponents.get("minute")),
335
+ second: nullToUndefined(dateComponents.get("second")),
336
+ millisecond: 0,
337
+ }, {
338
+ zone: timezone || "local",
339
+ });
340
+ if (!dt.isValid) {
341
+ throw new Error(`Could not construct valid DateTime: ${dt.invalidReason}`);
342
+ }
343
+ const timestamp = dt.toMillis();
344
+ return {
345
+ content: [
346
+ {
347
+ type: "text",
348
+ text: JSON.stringify({
349
+ timestamp,
350
+ iso: dt.toISO(),
351
+ timezone: dt.zoneName,
352
+ }),
353
+ },
354
+ ],
355
+ };
356
+ });
234
357
  server.tool("get-entity-tool", "get a list of entities like cameras, access controlled doors, sensors, etc", {
235
358
  entityType: z
236
359
  .enum(["camera", "access-controlled-doors"])
@@ -258,9 +381,11 @@ server.tool("get-entity-tool", "get a list of entities like cameras, access cont
258
381
  ],
259
382
  };
260
383
  });
261
- server.tool("camera-tool", "get specific requested information about a camera such as an image snapshot, or detailed analytics info. this can be used to answer questions about tracking people across cameras", {
384
+ server.tool("camera-tool", 'This tool captures and returns a real-time snapshot from a designated security camera. The image reflects the current scene in the camera\'s field of view and serves as a contextual input source for downstream tasks such as object recognition, anomaly detection, incident investigation, or situational assessment. When invoked, the tool provides the following: \n โ€ข Visual Scene Capture: A high-resolution image of what the camera is actively observing, including people, vehicles, license plates, and any detectable objects. \nโ€ข Scene Context: Metadata such as camera ID, location name, timestamp, and motion detection status if available. \nโ€ข Enriched Data Potential: The image can be paired with AI models or downstream analytics to extract insights such as: \nโ€ข Number and type of objects in frame (e.g., humans, cars, packages) \nโ€ข Unusual behaviors (e.g., loitering, unauthorized access) \nโ€ข Environmental conditions (e.g., lighting, obstruction, cleanliness) \nUse Cases: \nโ€ข Verify what triggered a motion alert or analytic rule. \nโ€ข Provide visual context for access events or alarms. \nโ€ข Support live incident triage or retrospective investigations. \nโ€ข Feed contextual imagery to agents making security or operational decisions. \nInvocation Notes: \nTo use this tool correctly, the agent should provide the specific camera identifier or location name. If possible, include the intent (e.g., "verify unauthorized access", "identify vehicle", "check for obstructions") to enhance downstream processing or summarization.', {
262
385
  requestType: z.enum(["image"]),
263
- timestampMs: z.optional(z.number()).describe("the timestamp in milliseconds"),
386
+ timestampMs: z
387
+ .optional(z.number())
388
+ .describe("the timestamp in milliseconds which should always be obtained using time-tool"),
264
389
  cameraUuid: z.optional(z.string()).describe("the camera uuid requested"),
265
390
  ...STATIC_ARGS,
266
391
  }, async ({ cameraUuid, timestampMs, requestType, requestModifiers }) => {
@@ -378,7 +503,7 @@ server.tool("events-tool", "event data for certain types of information like fac
378
503
  };
379
504
  });
380
505
  server.tool("location-tool", "contains basic operations for locations and response in JSON format.", {
381
- action: z.enum(["get"]),
506
+ action: z.enum(["get", "update"]),
382
507
  locationUpdate: z.optional(z.object({ uuid: z.string(), name: z.optional(z.string()) })),
383
508
  ...STATIC_ARGS,
384
509
  }, async ({ action, locationUpdate, requestModifiers }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rhombus-node-mcp",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "MCP server for Rhombus API",
5
5
  "keywords": [
6
6
  "ai",
@@ -33,9 +33,12 @@
33
33
  ],
34
34
  "dependencies": {
35
35
  "@modelcontextprotocol/sdk": "^1.9.0",
36
+ "chrono-node": "^2.8.0",
37
+ "luxon": "^3.6.1",
36
38
  "zod": "^3.24.2"
37
39
  },
38
40
  "devDependencies": {
41
+ "@types/luxon": "^3.6.2",
39
42
  "@types/node": "^22.14.0",
40
43
  "prettier": "3.5.3",
41
44
  "typescript": "^5.8.3"