rhombus-node-mcp 0.1.21 → 0.1.23

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 -91
  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 +183 -46
  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
package/dist/types.js CHANGED
@@ -9,24 +9,3 @@ export function createUuidSchema() {
9
9
  It contains only alphanumeric characters, digits, hyphens, and underscores.`);
10
10
  }
11
11
  export const UUID = createUuidSchema();
12
- export const VideoWallSettings = z.object({
13
- rowCount: z.number(),
14
- columnCount: z.number(),
15
- intervalSeconds: z.number().nullable(),
16
- rotateStrategy: z.enum(["none", "motion", "interval"]),
17
- });
18
- export const CreateVideoWallOptions = z
19
- .object({
20
- displayName: z.string().nullable().describe("What to call the video wall"),
21
- orgUuid: z.string().describe("The uuid of the organization"),
22
- deviceList: z
23
- .array(z.string())
24
- .min(1)
25
- .describe("The list of camera uuids (unique identifiers) to exist in the video wall. You must provide this manually by prompting the user at least once."),
26
- othersCanEdit: z
27
- .boolean()
28
- .nullable()
29
- .describe("Whether or not other users can edit the wall, defaults to false"),
30
- settings: VideoWallSettings,
31
- })
32
- .nullable();
package/dist/util.js CHANGED
@@ -2,6 +2,8 @@ import { z } from "zod";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
4
  import { DateTime } from "luxon";
5
+ import { filterIncludedFields, applyFilterBy } from "./filtering-utils.js";
6
+ export { INCLUDE_FIELDS_ARG, FILTER_BY_ARG, filterIncludedFields, applyFilterBy, zodToDotNotationPaths, createFilteringProxy } from "./filtering-utils.js";
5
7
  export function generateRandomString(length) {
6
8
  const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
7
9
  let result = "";
@@ -24,6 +26,9 @@ export function extractFromToolExtra(_extra) {
24
26
  sessionId: extra.sessionId,
25
27
  };
26
28
  }
29
+ // ---------------------------------------------------------------------------
30
+ // File utilities
31
+ // ---------------------------------------------------------------------------
27
32
  /**
28
33
  * Get all file paths in a directory in a directory
29
34
  *
@@ -52,6 +57,9 @@ export function getFilePathsInDirectory(dirPath) {
52
57
  }
53
58
  return filePaths;
54
59
  }
60
+ // ---------------------------------------------------------------------------
61
+ // Tool content helpers
62
+ // ---------------------------------------------------------------------------
55
63
  /**
56
64
  * Returns an object in the form expected by `server.tool`
57
65
  */
@@ -66,20 +74,38 @@ export function createToolTextContent(content) {
66
74
  };
67
75
  }
68
76
  /**
69
- * Returns strucutred content expected by `server.tool`
70
- * The generic type T allows for type safety. Set T to the output schema of the tool, and your content will be type checked
77
+ * Returns structured content expected by `server.tool`.
78
+ * Optionally applies programmatic field projection (includeFields) and
79
+ * row filtering (filterBy) before serialising.
80
+ *
81
+ * The generic type T allows for type safety. Set T to the output schema of the
82
+ * tool and your content will be type-checked.
83
+ *
84
+ * filterBy is applied first (prune rows), then includeFields (prune columns),
85
+ * which minimises output size optimally.
71
86
  */
72
- export function createToolStructuredContent(content) {
87
+ export function createToolStructuredContent(content, opts) {
88
+ // biome-ignore lint/suspicious/noExplicitAny: intentional runtime manipulation
89
+ let result = content;
90
+ if (opts?.filterBy?.length) {
91
+ result = applyFilterBy(result, opts.filterBy) ?? result;
92
+ }
93
+ if (opts?.includeFields?.length) {
94
+ result = filterIncludedFields(result, opts.includeFields) ?? result;
95
+ }
73
96
  return {
74
97
  content: [
75
98
  {
76
99
  type: "text",
77
- text: JSON.stringify(content),
100
+ text: JSON.stringify(result),
78
101
  },
79
102
  ],
80
- structuredContent: content,
103
+ structuredContent: result,
81
104
  };
82
105
  }
106
+ // ---------------------------------------------------------------------------
107
+ // removeNullFields
108
+ // ---------------------------------------------------------------------------
83
109
  /**
84
110
  * Recursively removes fields with null values from a JavaScript object.
85
111
  * If a nested object becomes empty after removing nulls, it will also be removed.
@@ -124,58 +150,9 @@ export function removeNullFields(obj) {
124
150
  }
125
151
  return Object.keys(cleanedObject).length > 0 ? cleanedObject : undefined;
126
152
  }
127
- export function filterIncludedFields(obj, fieldsToInclude) {
128
- if (!fieldsToInclude || fieldsToInclude.length === 0) {
129
- return obj;
130
- }
131
- if (Array.isArray(obj)) {
132
- return obj
133
- .map(item => filterIncludedFields(item, fieldsToInclude))
134
- .filter(item => {
135
- if (item === undefined || item === null) {
136
- return false;
137
- }
138
- if (Array.isArray(item)) {
139
- return item.length > 0;
140
- }
141
- if (typeof item === "object") {
142
- return Object.keys(item).length > 0;
143
- }
144
- // Keep primitives
145
- return true;
146
- });
147
- }
148
- if (typeof obj === "object" && obj !== null) {
149
- const newObj = {};
150
- for (const key in obj) {
151
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
152
- if (fieldsToInclude.includes(key)) {
153
- newObj[key] = obj[key];
154
- }
155
- else {
156
- const result = filterIncludedFields(obj[key], fieldsToInclude);
157
- if (result !== undefined && result !== null) {
158
- if (Array.isArray(result)) {
159
- if (result.length > 0) {
160
- newObj[key] = result;
161
- }
162
- }
163
- else if (typeof result === "object") {
164
- if (Object.keys(result).length > 0) {
165
- newObj[key] = result;
166
- }
167
- }
168
- else {
169
- newObj[key] = result;
170
- }
171
- }
172
- }
173
- }
174
- }
175
- return Object.keys(newObj).length > 0 ? newObj : undefined;
176
- }
177
- return undefined;
178
- }
153
+ // ---------------------------------------------------------------------------
154
+ // formatTimestamp
155
+ // ---------------------------------------------------------------------------
179
156
  /**
180
157
  * Formats a timestamp in milliseconds to a human-readable date string
181
158
  * Format: "February 24, 2025 at 3:23 PM"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rhombus-node-mcp",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "description": "MCP server for Rhombus API",
5
5
  "keywords": [
6
6
  "ai",
@@ -10,11 +10,11 @@
10
10
  ],
11
11
  "homepage": "https://modelcontextprotocol.io",
12
12
  "bugs": {
13
- "url": "https://github.com/modelcontextprotocol/servers/issues"
13
+ "url": "https://github.com/RhombusSystems/rhombus-node-mcp/issues"
14
14
  },
15
15
  "repository": {
16
16
  "type": "git",
17
- "url": "git+https://github.com/modelcontextprotocol/servers.git"
17
+ "url": "git+https://github.com/RhombusSystems/rhombus-node-mcp.git"
18
18
  },
19
19
  "license": "MIT",
20
20
  "author": "Model Context Protocol (https://modelcontextprotocol.io)",
@@ -28,6 +28,8 @@
28
28
  "build": "rm -rf dist && tsc && chmod +x dist/*.js",
29
29
  "docker:build": "docker build -t rhombussystems/mcp-server-rhombus .",
30
30
  "start": "node dist/index.js",
31
+ "start:http": "TRANSPORT_TYPE=streamable-http node dist/index.js",
32
+ "start:stdio": "TRANSPORT_TYPE=stdio node dist/index.js",
31
33
  "prepare": "npm run build",
32
34
  "watch": "npm run clean && tsc --watch",
33
35
  "generate-schemas": "npx tsx scripts/generate-zod-schemas.ts",
@@ -45,7 +47,7 @@
45
47
  "dist"
46
48
  ],
47
49
  "dependencies": {
48
- "@modelcontextprotocol/sdk": "^1.21.1",
50
+ "@modelcontextprotocol/sdk": "^1.21.2",
49
51
  "axios": "^1.11.0",
50
52
  "cheerio": "^1.1.2",
51
53
  "chrono-node": "^2.8.0",
@@ -76,4 +78,4 @@
76
78
  "engines": {
77
79
  "node": ">=18"
78
80
  }
79
- }
81
+ }
@@ -1,90 +0,0 @@
1
- import { FaissSearchService } from '../services/faiss-search-service.js';
2
- import { createToolTextContent } from '../util.js';
3
- import { logger } from '../logger.js';
4
- import { TOOL_ARGS } from '../types/semantic-search-tool-types.js';
5
- // Constants following coding style guidelines
6
- const DEFAULT_SEARCH_LIMIT = 3;
7
- const DEFAULT_MIN_SIMILARITY = 0.3;
8
- const TOOL_NAME = "semantic-search";
9
- const TOOL_DESCRIPTION = `
10
- This tool performs semantic search across the Rhombus knowledge base using AI embeddings and vector similarity.
11
-
12
- It searches through documentation, blog posts, support articles, and other content to find the most relevant
13
- information based on the meaning of your query, not just keyword matching.
14
-
15
- The tool returns an array of search results, each containing:
16
- - sourceTitle: The title of the source document
17
- - sourceUrl: The URL where the content can be found
18
- - chunkText: The relevant text snippet that matches your query
19
-
20
- Use this tool when you need to find information about:
21
- - Camera features, specifications, or comparisons
22
- - Installation and setup procedures
23
- - Technical troubleshooting
24
- - Product capabilities and benefits
25
- - Access control and security features
26
- - Cloud storage and management
27
- - Any other Rhombus-related topics
28
- `;
29
- // Singleton instance for reuse across searches
30
- let globalSearchService = null;
31
- let isGlobalServiceInitialized = false;
32
- async function getSearchService() {
33
- if (!globalSearchService) {
34
- globalSearchService = new FaissSearchService();
35
- }
36
- if (!isGlobalServiceInitialized) {
37
- await globalSearchService.loadEmbeddings();
38
- await globalSearchService.loadIndex();
39
- isGlobalServiceInitialized = true;
40
- }
41
- return globalSearchService;
42
- }
43
- const TOOL_HANDLER = async (args, extra) => {
44
- const { query } = args;
45
- try {
46
- if (!query || query.trim().length === 0) {
47
- return createToolTextContent(JSON.stringify({ error: "Query cannot be empty" }));
48
- }
49
- const searchService = await getSearchService();
50
- const searchOptions = {
51
- query: query.trim(),
52
- limit: DEFAULT_SEARCH_LIMIT,
53
- minSimilarity: DEFAULT_MIN_SIMILARITY,
54
- urlPattern: undefined,
55
- };
56
- const { results, stats } = await searchService.search(searchOptions);
57
- const searchResults = results.map(result => ({
58
- sourceTitle: result.sourceTitle,
59
- sourceUrl: result.sourceUrl,
60
- chunkText: result.chunkText,
61
- }));
62
- logger.info('Semantic search results:', results);
63
- // Log search statistics for debugging
64
- logger.info('Semantic search stats:', {
65
- searchTime: stats.searchTime,
66
- totalVectors: stats.totalVectors,
67
- resultsReturned: stats.resultsReturned,
68
- resultsFiltered: stats.resultsFiltered,
69
- query: query.trim()
70
- });
71
- return createToolTextContent(JSON.stringify(searchResults));
72
- }
73
- catch (error) {
74
- return createToolTextContent(JSON.stringify({
75
- error: `Search failed: ${error instanceof Error ? error.message : 'Unknown error'}`
76
- }));
77
- }
78
- };
79
- export async function createTool(server) {
80
- try {
81
- // Verify search service can be initialized and also save it into the cached variable
82
- await getSearchService();
83
- server.tool(TOOL_NAME, TOOL_DESCRIPTION, TOOL_ARGS, TOOL_HANDLER);
84
- }
85
- catch (error) {
86
- logger.error('Failed to initialize semantic search tool:', error);
87
- logger.error('Make sure you have the index and embeddings files in rhombus-node-mcp/data/embeddings/');
88
- throw error;
89
- }
90
- }
@@ -1,30 +0,0 @@
1
- import { requireConfirmation } from "../utils/confirmation.js";
2
- import { handleCreateVideoWallRequest } from "../api/create-tool-api.js";
3
- import { TOOL_ARGS } from "../types/create-tool-types.js";
4
- const TOOL_NAME = "create-tool";
5
- const TOOL_DESCRIPTION = "Tool for creating many entity types such as video walls.";
6
- const TOOL_HANDLER = async (args, extra) => {
7
- const { entityType, videoWallCreateOptions, confirmationId } = args;
8
- const confirmation = requireConfirmation(confirmationId);
9
- if (confirmation === true) {
10
- switch (entityType) {
11
- case "video-wall":
12
- return await handleCreateVideoWallRequest(videoWallCreateOptions, extra._meta?.requestModifiers, extra.sessionId);
13
- default:
14
- }
15
- return {
16
- content: [
17
- {
18
- type: "text",
19
- text: "",
20
- },
21
- ],
22
- };
23
- }
24
- else {
25
- return confirmation;
26
- }
27
- };
28
- export function createTool(server) {
29
- server.tool(TOOL_NAME, TOOL_DESCRIPTION, TOOL_ARGS, TOOL_HANDLER);
30
- }
@@ -1,8 +0,0 @@
1
- import { z } from "zod";
2
- import { CreateVideoWallOptions } from "../types.js";
3
- import { addConfirmationParams } from "../utils/confirmation.js";
4
- export const TOOL_ARGS = addConfirmationParams({
5
- entityType: z.enum(["video-wall"]).describe("The entity type to create. Example: video wall."),
6
- videoWallCreateOptions: CreateVideoWallOptions,
7
- });
8
- const TOOL_ARGS_SCHEMA = z.object(TOOL_ARGS);