@butlr/butlr-mcp-server 0.1.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.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +195 -0
  3. package/dist/cache/occupancy-cache.d.ts +72 -0
  4. package/dist/cache/occupancy-cache.d.ts.map +1 -0
  5. package/dist/cache/occupancy-cache.js +166 -0
  6. package/dist/cache/occupancy-cache.js.map +1 -0
  7. package/dist/cache/topology-cache.d.ts +36 -0
  8. package/dist/cache/topology-cache.d.ts.map +1 -0
  9. package/dist/cache/topology-cache.js +74 -0
  10. package/dist/cache/topology-cache.js.map +1 -0
  11. package/dist/clients/auth-client.d.ts +22 -0
  12. package/dist/clients/auth-client.d.ts.map +1 -0
  13. package/dist/clients/auth-client.js +82 -0
  14. package/dist/clients/auth-client.js.map +1 -0
  15. package/dist/clients/graphql-client.d.ts +6 -0
  16. package/dist/clients/graphql-client.d.ts.map +1 -0
  17. package/dist/clients/graphql-client.js +106 -0
  18. package/dist/clients/graphql-client.js.map +1 -0
  19. package/dist/clients/queries/topology.d.ts +36 -0
  20. package/dist/clients/queries/topology.d.ts.map +1 -0
  21. package/dist/clients/queries/topology.js +252 -0
  22. package/dist/clients/queries/topology.js.map +1 -0
  23. package/dist/clients/reporting-client.d.ts +191 -0
  24. package/dist/clients/reporting-client.d.ts.map +1 -0
  25. package/dist/clients/reporting-client.js +353 -0
  26. package/dist/clients/reporting-client.js.map +1 -0
  27. package/dist/clients/stats-client.d.ts +119 -0
  28. package/dist/clients/stats-client.d.ts.map +1 -0
  29. package/dist/clients/stats-client.js +238 -0
  30. package/dist/clients/stats-client.js.map +1 -0
  31. package/dist/clients/types.d.ts +215 -0
  32. package/dist/clients/types.d.ts.map +1 -0
  33. package/dist/clients/types.js +6 -0
  34. package/dist/clients/types.js.map +1 -0
  35. package/dist/constants.d.ts +3 -0
  36. package/dist/constants.d.ts.map +1 -0
  37. package/dist/constants.js +3 -0
  38. package/dist/constants.js.map +1 -0
  39. package/dist/errors/mcp-errors.d.ts +63 -0
  40. package/dist/errors/mcp-errors.d.ts.map +1 -0
  41. package/dist/errors/mcp-errors.js +144 -0
  42. package/dist/errors/mcp-errors.js.map +1 -0
  43. package/dist/index.d.ts +3 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +43 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/tools/butlr-available-rooms.d.ts +49 -0
  48. package/dist/tools/butlr-available-rooms.d.ts.map +1 -0
  49. package/dist/tools/butlr-available-rooms.js +492 -0
  50. package/dist/tools/butlr-available-rooms.js.map +1 -0
  51. package/dist/tools/butlr-fetch-entity-details.d.ts +42 -0
  52. package/dist/tools/butlr-fetch-entity-details.d.ts.map +1 -0
  53. package/dist/tools/butlr-fetch-entity-details.js +276 -0
  54. package/dist/tools/butlr-fetch-entity-details.js.map +1 -0
  55. package/dist/tools/butlr-get-asset-details.d.ts +51 -0
  56. package/dist/tools/butlr-get-asset-details.d.ts.map +1 -0
  57. package/dist/tools/butlr-get-asset-details.js +391 -0
  58. package/dist/tools/butlr-get-asset-details.js.map +1 -0
  59. package/dist/tools/butlr-get-current-occupancy.d.ts +21 -0
  60. package/dist/tools/butlr-get-current-occupancy.d.ts.map +1 -0
  61. package/dist/tools/butlr-get-current-occupancy.js +126 -0
  62. package/dist/tools/butlr-get-current-occupancy.js.map +1 -0
  63. package/dist/tools/butlr-get-occupancy-timeseries.d.ts +31 -0
  64. package/dist/tools/butlr-get-occupancy-timeseries.d.ts.map +1 -0
  65. package/dist/tools/butlr-get-occupancy-timeseries.js +145 -0
  66. package/dist/tools/butlr-get-occupancy-timeseries.js.map +1 -0
  67. package/dist/tools/butlr-hardware-snapshot.d.ts +55 -0
  68. package/dist/tools/butlr-hardware-snapshot.d.ts.map +1 -0
  69. package/dist/tools/butlr-hardware-snapshot.js +556 -0
  70. package/dist/tools/butlr-hardware-snapshot.js.map +1 -0
  71. package/dist/tools/butlr-list-topology.d.ts +27 -0
  72. package/dist/tools/butlr-list-topology.d.ts.map +1 -0
  73. package/dist/tools/butlr-list-topology.js +241 -0
  74. package/dist/tools/butlr-list-topology.js.map +1 -0
  75. package/dist/tools/butlr-search-assets.d.ts +53 -0
  76. package/dist/tools/butlr-search-assets.d.ts.map +1 -0
  77. package/dist/tools/butlr-search-assets.js +206 -0
  78. package/dist/tools/butlr-search-assets.js.map +1 -0
  79. package/dist/tools/butlr-space-busyness.d.ts +23 -0
  80. package/dist/tools/butlr-space-busyness.d.ts.map +1 -0
  81. package/dist/tools/butlr-space-busyness.js +304 -0
  82. package/dist/tools/butlr-space-busyness.js.map +1 -0
  83. package/dist/tools/butlr-traffic-flow.d.ts +39 -0
  84. package/dist/tools/butlr-traffic-flow.d.ts.map +1 -0
  85. package/dist/tools/butlr-traffic-flow.js +369 -0
  86. package/dist/tools/butlr-traffic-flow.js.map +1 -0
  87. package/dist/types/responses.d.ts +253 -0
  88. package/dist/types/responses.d.ts.map +1 -0
  89. package/dist/types/responses.js +8 -0
  90. package/dist/types/responses.js.map +1 -0
  91. package/dist/utils/asset-flattener.d.ts +50 -0
  92. package/dist/utils/asset-flattener.d.ts.map +1 -0
  93. package/dist/utils/asset-flattener.js +131 -0
  94. package/dist/utils/asset-flattener.js.map +1 -0
  95. package/dist/utils/asset-helpers.d.ts +8 -0
  96. package/dist/utils/asset-helpers.d.ts.map +1 -0
  97. package/dist/utils/asset-helpers.js +24 -0
  98. package/dist/utils/asset-helpers.js.map +1 -0
  99. package/dist/utils/field-validator.d.ts +29 -0
  100. package/dist/utils/field-validator.d.ts.map +1 -0
  101. package/dist/utils/field-validator.js +197 -0
  102. package/dist/utils/field-validator.js.map +1 -0
  103. package/dist/utils/fuzzy-match.d.ts +29 -0
  104. package/dist/utils/fuzzy-match.d.ts.map +1 -0
  105. package/dist/utils/fuzzy-match.js +70 -0
  106. package/dist/utils/fuzzy-match.js.map +1 -0
  107. package/dist/utils/graphql-helpers.d.ts +29 -0
  108. package/dist/utils/graphql-helpers.d.ts.map +1 -0
  109. package/dist/utils/graphql-helpers.js +43 -0
  110. package/dist/utils/graphql-helpers.js.map +1 -0
  111. package/dist/utils/natural-language.d.ts +73 -0
  112. package/dist/utils/natural-language.d.ts.map +1 -0
  113. package/dist/utils/natural-language.js +172 -0
  114. package/dist/utils/natural-language.js.map +1 -0
  115. package/dist/utils/occupancy-helpers.d.ts +63 -0
  116. package/dist/utils/occupancy-helpers.d.ts.map +1 -0
  117. package/dist/utils/occupancy-helpers.js +203 -0
  118. package/dist/utils/occupancy-helpers.js.map +1 -0
  119. package/dist/utils/path-builder.d.ts +10 -0
  120. package/dist/utils/path-builder.d.ts.map +1 -0
  121. package/dist/utils/path-builder.js +34 -0
  122. package/dist/utils/path-builder.js.map +1 -0
  123. package/dist/utils/time-range-validator.d.ts +13 -0
  124. package/dist/utils/time-range-validator.d.ts.map +1 -0
  125. package/dist/utils/time-range-validator.js +65 -0
  126. package/dist/utils/time-range-validator.js.map +1 -0
  127. package/dist/utils/timezone-helpers.d.ts +49 -0
  128. package/dist/utils/timezone-helpers.d.ts.map +1 -0
  129. package/dist/utils/timezone-helpers.js +209 -0
  130. package/dist/utils/timezone-helpers.js.map +1 -0
  131. package/dist/utils/tree-formatter.d.ts +23 -0
  132. package/dist/utils/tree-formatter.d.ts.map +1 -0
  133. package/dist/utils/tree-formatter.js +258 -0
  134. package/dist/utils/tree-formatter.js.map +1 -0
  135. package/package.json +93 -0
@@ -0,0 +1,241 @@
1
+ import { apolloClient } from "../clients/graphql-client.js";
2
+ import { GET_FULL_TOPOLOGY, GET_ALL_SENSORS, GET_ALL_HIVES } from "../clients/queries/topology.js";
3
+ import { z } from "zod";
4
+ import { getCachedTopology, setCachedTopology, generateTopologyCacheKey, } from "../cache/topology-cache.js";
5
+ import { formatTopologyTree } from "../utils/tree-formatter.js";
6
+ import { isProductionSensor, isProductionHive, rethrowIfGraphQLError, } from "../utils/graphql-helpers.js";
7
+ const LIST_TOPOLOGY_DESCRIPTION = "Display org hierarchy tree with flexible depth control. Can show full tree, specific subtrees, or flat lists. " +
8
+ "Supports filtering by parent asset IDs. Depth levels: 0=sites, 1=buildings, 2=floors, 3=rooms/zones, 4=hives, 5=sensors. " +
9
+ "Use starting_depth to choose which level to show, and traversal_depth to control how many levels below to include.";
10
+ /** Shared shape — used by both registerTool (SDK schema) and full validation */
11
+ const listTopologyInputShape = {
12
+ asset_ids: z
13
+ .array(z.string())
14
+ .optional()
15
+ .describe("Optional: Parent asset IDs to show tree for. If empty, shows all sites. " +
16
+ "Examples: ['site_123'], ['building_456'], ['floor_789']"),
17
+ starting_depth: z
18
+ .number()
19
+ .default(0)
20
+ .describe("Depth level to start showing assets. 0=sites, 1=buildings, 2=floors, 3=rooms/zones, 4=hives, 5=sensors. " +
21
+ "Use with traversal_depth=0 to show only assets at this level (flat list)."),
22
+ traversal_depth: z
23
+ .number()
24
+ .default(0)
25
+ .describe("How many levels below starting_depth to traverse. 0=starting level only, 1=one level below, etc. " +
26
+ "Default is 0 to minimize token usage. Use 10 for full tree."),
27
+ };
28
+ export const ListTopologyArgsSchema = z.object(listTopologyInputShape).strict();
29
+ /**
30
+ * Execute butlr_list_topology tool
31
+ */
32
+ export async function executeListTopology(args = {}) {
33
+ const startingDepth = args.starting_depth ?? 0;
34
+ const traversalDepth = args.traversal_depth ?? 0;
35
+ const assetIds = args.asset_ids ?? [];
36
+ if (process.env.DEBUG) {
37
+ console.error(`[butlr-list-topology] Fetching topology: starting_depth=${startingDepth}, traversal_depth=${traversalDepth}, assets=${assetIds.length || "all"}`);
38
+ }
39
+ // Use a cache key that includes devices
40
+ const cacheKey = generateTopologyCacheKey(process.env.BUTLR_ORG_ID || "default", true, // include devices
41
+ true, // include zones
42
+ undefined);
43
+ // Try to get cached topology
44
+ let sites = [];
45
+ let partialData = false;
46
+ const cached = getCachedTopology(cacheKey);
47
+ if (cached && cached.data && cached.data.sites) {
48
+ if (process.env.DEBUG) {
49
+ console.error("[butlr-list-topology] Using cached topology");
50
+ }
51
+ sites = cached.data.sites;
52
+ }
53
+ else {
54
+ // Fetch fresh topology
55
+ if (process.env.DEBUG) {
56
+ console.error("[butlr-list-topology] Fetching fresh topology");
57
+ }
58
+ try {
59
+ const result = await apolloClient.query({
60
+ query: GET_FULL_TOPOLOGY,
61
+ fetchPolicy: "network-only",
62
+ });
63
+ // Apollo can return both data and errors - only fail if we have no data
64
+ if (!result.data || !result.data.sites || !result.data.sites.data) {
65
+ // If we have error but no data, throw
66
+ if (result.error) {
67
+ throw result.error;
68
+ }
69
+ throw new Error("Invalid response structure from API");
70
+ }
71
+ // Track whether the topology data is partial (errors alongside data)
72
+ partialData = !!result.error;
73
+ if (partialData && process.env.DEBUG) {
74
+ console.error(`[butlr-list-topology] Warning: GraphQL errors present, data may be partial`);
75
+ }
76
+ sites = result.data.sites.data;
77
+ // Query all sensors and hives separately (nested fields are broken)
78
+ if (process.env.DEBUG) {
79
+ console.error("[butlr-list-topology] Fetching all sensors and hives...");
80
+ }
81
+ const [sensorsResult, hivesResult] = await Promise.all([
82
+ apolloClient.query({
83
+ query: GET_ALL_SENSORS,
84
+ fetchPolicy: "network-only",
85
+ }),
86
+ apolloClient.query({
87
+ query: GET_ALL_HIVES,
88
+ fetchPolicy: "network-only",
89
+ }),
90
+ ]);
91
+ // Filter out test/placeholder devices from topology listing
92
+ // (Note: These filters are ONLY for topology display, not occupancy queries)
93
+ const allSensors = (sensorsResult.data?.sensors?.data || []).filter(isProductionSensor);
94
+ const allHives = (hivesResult.data?.hives?.data || []).filter(isProductionHive);
95
+ if (process.env.DEBUG) {
96
+ console.error(`[butlr-list-topology] Got ${allSensors.length} production sensors, ${allHives.length} production hives (test/placeholder devices filtered)`);
97
+ }
98
+ // Merge sensors and hives into topology by floor_id
99
+ sites = mergeSensorsAndHivesIntoTopology(sites, allSensors, allHives);
100
+ // Only cache complete topology data — partial results should be re-fetched
101
+ if (!partialData) {
102
+ setCachedTopology(cacheKey, { sites });
103
+ if (process.env.DEBUG) {
104
+ console.error(`[butlr-list-topology] Cached topology with ${sites.length} sites`);
105
+ }
106
+ }
107
+ else if (process.env.DEBUG) {
108
+ console.error(`[butlr-list-topology] Skipping cache — topology data is partial`);
109
+ }
110
+ }
111
+ catch (error) {
112
+ rethrowIfGraphQLError(error);
113
+ throw error;
114
+ }
115
+ }
116
+ // Filter topology by asset_ids if provided
117
+ let filteredSites = sites;
118
+ if (assetIds.length > 0) {
119
+ filteredSites = filterTopologyByAssets(sites, assetIds);
120
+ }
121
+ // Format as tree with depth controls
122
+ const tree = formatTopologyTree(filteredSites, startingDepth, traversalDepth);
123
+ const response = {
124
+ tree,
125
+ query_params: {
126
+ starting_depth: startingDepth,
127
+ traversal_depth: traversalDepth,
128
+ asset_filter: assetIds.length > 0 ? assetIds : "all",
129
+ },
130
+ timestamp: new Date().toISOString(),
131
+ };
132
+ if (partialData) {
133
+ response.warning =
134
+ "Topology data may be incomplete — the API returned partial results due to upstream errors.";
135
+ }
136
+ return response;
137
+ }
138
+ /**
139
+ * Merge sensors and hives into topology structure
140
+ * Groups by floor_id and nests under appropriate floors
141
+ */
142
+ function mergeSensorsAndHivesIntoTopology(sites, allSensors, allHives) {
143
+ // Group sensors by floor_id
144
+ const sensorsByFloor = {};
145
+ for (const sensor of allSensors) {
146
+ const floorId = sensor.floor_id || sensor.floorID;
147
+ if (floorId) {
148
+ if (!sensorsByFloor[floorId]) {
149
+ sensorsByFloor[floorId] = [];
150
+ }
151
+ sensorsByFloor[floorId].push(sensor);
152
+ }
153
+ }
154
+ // Group hives by floor_id
155
+ const hivesByFloor = {};
156
+ for (const hive of allHives) {
157
+ const floorId = hive.floor_id || hive.floorID;
158
+ if (floorId) {
159
+ if (!hivesByFloor[floorId]) {
160
+ hivesByFloor[floorId] = [];
161
+ }
162
+ hivesByFloor[floorId].push(hive);
163
+ }
164
+ }
165
+ // Merge into topology
166
+ for (const site of sites) {
167
+ for (const building of site.buildings || []) {
168
+ for (const floor of building.floors || []) {
169
+ // Add sensors and hives to this floor
170
+ floor.sensors = sensorsByFloor[floor.id] || [];
171
+ floor.hives = hivesByFloor[floor.id] || [];
172
+ }
173
+ }
174
+ }
175
+ return sites;
176
+ }
177
+ /**
178
+ * Filter topology to only include assets matching the provided IDs
179
+ * Returns a subset of the topology tree
180
+ */
181
+ function filterTopologyByAssets(sites, assetIds) {
182
+ const idSet = new Set(assetIds);
183
+ const filtered = [];
184
+ for (const site of sites) {
185
+ if (idSet.has(site.id)) {
186
+ filtered.push(site);
187
+ continue;
188
+ }
189
+ const matchedBuildings = [];
190
+ for (const building of site.buildings || []) {
191
+ if (idSet.has(building.id)) {
192
+ matchedBuildings.push(building);
193
+ continue;
194
+ }
195
+ const matchedFloors = [];
196
+ for (const floor of building.floors || []) {
197
+ if (idSet.has(floor.id)) {
198
+ matchedFloors.push(floor);
199
+ continue;
200
+ }
201
+ const hasMatchedRoom = floor.rooms?.some((r) => idSet.has(r.id));
202
+ const hasMatchedZone = floor.zones?.some((z) => idSet.has(z.id));
203
+ const hasMatchedHive = floor.hives?.some((h) => idSet.has(h.id));
204
+ const hasMatchedSensor = floor.sensors?.some((s) => idSet.has(s.id));
205
+ if (hasMatchedRoom || hasMatchedZone || hasMatchedHive || hasMatchedSensor) {
206
+ matchedFloors.push(floor);
207
+ }
208
+ }
209
+ if (matchedFloors.length > 0) {
210
+ matchedBuildings.push({ ...building, floors: matchedFloors });
211
+ }
212
+ }
213
+ if (matchedBuildings.length > 0) {
214
+ filtered.push({ ...site, buildings: matchedBuildings });
215
+ }
216
+ }
217
+ return filtered;
218
+ }
219
+ /**
220
+ * Register butlr_list_topology with an McpServer instance
221
+ */
222
+ export function registerListTopology(server) {
223
+ server.registerTool("butlr_list_topology", {
224
+ title: "List Topology",
225
+ description: LIST_TOPOLOGY_DESCRIPTION,
226
+ inputSchema: listTopologyInputShape,
227
+ annotations: {
228
+ readOnlyHint: true,
229
+ destructiveHint: false,
230
+ idempotentHint: true,
231
+ openWorldHint: true,
232
+ },
233
+ }, async (args) => {
234
+ const validated = ListTopologyArgsSchema.parse(args);
235
+ const result = await executeListTopology(validated);
236
+ return {
237
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
238
+ };
239
+ });
240
+ }
241
+ //# sourceMappingURL=butlr-list-topology.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"butlr-list-topology.js","sourceRoot":"","sources":["../../src/tools/butlr-list-topology.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ export declare const SearchAssetsArgsSchema: z.ZodEffects<z.ZodObject<{
4
+ query: z.ZodEffects<z.ZodString, string, string>;
5
+ asset_types: z.ZodOptional<z.ZodArray<z.ZodEnum<["site", "building", "floor", "room", "zone", "sensor", "hive"]>, "many">>;
6
+ max_results: z.ZodDefault<z.ZodNumber>;
7
+ }, "strict", z.ZodTypeAny, {
8
+ query: string;
9
+ max_results: number;
10
+ asset_types?: ("site" | "building" | "floor" | "room" | "zone" | "sensor" | "hive")[] | undefined;
11
+ }, {
12
+ query: string;
13
+ asset_types?: ("site" | "building" | "floor" | "room" | "zone" | "sensor" | "hive")[] | undefined;
14
+ max_results?: number | undefined;
15
+ }>, {
16
+ query: string;
17
+ max_results: number;
18
+ asset_types?: ("site" | "building" | "floor" | "room" | "zone" | "sensor" | "hive")[] | undefined;
19
+ }, {
20
+ query: string;
21
+ asset_types?: ("site" | "building" | "floor" | "room" | "zone" | "sensor" | "hive")[] | undefined;
22
+ max_results?: number | undefined;
23
+ }>;
24
+ export type SearchAssetsArgs = z.output<typeof SearchAssetsArgsSchema>;
25
+ /**
26
+ * Search result with minimal fields
27
+ */
28
+ export interface SearchResult {
29
+ id: string;
30
+ name: string;
31
+ type: string;
32
+ path: string;
33
+ match_score: number;
34
+ site_id?: string;
35
+ building_id?: string;
36
+ floor_id?: string;
37
+ room_id?: string;
38
+ }
39
+ /**
40
+ * Execute search_assets tool
41
+ */
42
+ export declare function executeSearchAssets(args: SearchAssetsArgs): Promise<{
43
+ query: string;
44
+ matches: SearchResult[];
45
+ total_matches: number;
46
+ searched_assets: number;
47
+ timestamp: string;
48
+ }>;
49
+ /**
50
+ * Register butlr_search_assets with an McpServer instance
51
+ */
52
+ export declare function registerSearchAssets(server: McpServer): void;
53
+ //# sourceMappingURL=butlr-search-assets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"butlr-search-assets.d.ts","sourceRoot":"","sources":["../../src/tools/butlr-search-assets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAkDxB,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;EAehC,CAAC;AAgCJ,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IAEpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB;;;;;;GAgI/D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsB5D"}
@@ -0,0 +1,206 @@
1
+ import { apolloClient } from "../clients/graphql-client.js";
2
+ import { z } from "zod";
3
+ import { GET_FULL_TOPOLOGY } from "../clients/queries/topology.js";
4
+ import { getCachedTopology, setCachedTopology, generateTopologyCacheKey, } from "../cache/topology-cache.js";
5
+ import { flattenTopology } from "../utils/asset-flattener.js";
6
+ import { searchAssets } from "../utils/fuzzy-match.js";
7
+ import { buildAssetPath } from "../utils/path-builder.js";
8
+ import { rethrowIfGraphQLError } from "../utils/graphql-helpers.js";
9
+ const VALID_ASSET_TYPES = ["site", "building", "floor", "room", "zone", "sensor", "hive"];
10
+ /** Shared shape — used by both registerTool (SDK schema) and full validation */
11
+ const searchAssetsInputShape = {
12
+ query: z
13
+ .string()
14
+ .min(1, "query cannot be empty")
15
+ .max(500, "query too long (max: 500 chars)")
16
+ .trim()
17
+ .refine((val) => val.length >= 2, "query must be at least 2 characters (after trimming whitespace)")
18
+ .describe("Search term to match against asset names"),
19
+ asset_types: z
20
+ .array(z.enum(VALID_ASSET_TYPES, {
21
+ errorMap: () => ({
22
+ message: `asset_type must be one of: ${VALID_ASSET_TYPES.join(", ")}`,
23
+ }),
24
+ }))
25
+ .min(1, "asset_types array cannot be empty (omit field to search all types)")
26
+ .max(VALID_ASSET_TYPES.length)
27
+ .optional()
28
+ .describe("Optional: Filter to specific asset types"),
29
+ max_results: z
30
+ .number()
31
+ .int("max_results must be an integer")
32
+ .min(1, "max_results must be at least 1")
33
+ .max(100, "max_results cannot exceed 100")
34
+ .default(20)
35
+ .describe("Maximum number of results to return"),
36
+ };
37
+ export const SearchAssetsArgsSchema = z
38
+ .object(searchAssetsInputShape)
39
+ .strict()
40
+ .refine((data) => {
41
+ if (data.asset_types) {
42
+ const unique = new Set(data.asset_types);
43
+ return unique.size === data.asset_types.length;
44
+ }
45
+ return true;
46
+ }, {
47
+ message: "asset_types contains duplicate values",
48
+ path: ["asset_types"],
49
+ });
50
+ const SEARCH_ASSETS_DESCRIPTION = "Search for Butlr assets (sites, buildings, floors, rooms, zones, sensors, hives) by name using fuzzy matching. Essential prerequisite tool for finding asset IDs before calling other tools. Returns minimal matched results with breadcrumb paths, match scores, and parent context. Searches across all asset types by default, with optional filters.\n\n" +
51
+ "Primary Users:\n" +
52
+ "- All Users: Find asset IDs by human-readable names before using other tools\n" +
53
+ "- IT Manager: Find sensors by MAC address for troubleshooting\n" +
54
+ "- Field Technician: Locate sensors/hives by name or serial number before site visits\n" +
55
+ "- Workplace Manager: Find rooms/spaces by common names (e.g., 'café', 'huddle room 3')\n\n" +
56
+ "Example Queries:\n" +
57
+ '1. "Find the main lobby" → returns room_lobby_123\n' +
58
+ '2. "Search for café or coffee shop" → fuzzy matches "Café Barista", "Coffee Bar"\n' +
59
+ '3. "Find Floor 6 in SF Tower" → returns floor_sf_tower_6\n' +
60
+ '4. "Search for sensors with MAC address starting with 00:1A" → matches sensors by MAC\n' +
61
+ '5. "Find all conference rooms" → searches room names containing "conference"\n' +
62
+ '6. "Locate hive with serial number HV-2023-001" → finds hive by serial\n' +
63
+ '7. "Search for Chicago office" → finds site/building matching "Chicago"\n' +
64
+ '8. "Find focus rooms on Floor 3" → searches for rooms with "focus" in name\n\n' +
65
+ "When to Use:\n" +
66
+ "- Know a space's name but not its ID (e.g., 'Café Barista' → room_cafe_123)\n" +
67
+ "- Looking for sensors/hives by MAC address or serial number\n" +
68
+ "- Find all assets of a specific type (e.g., all sites, all conference rooms)\n" +
69
+ "- Exploring org topology and don't know exact asset names\n" +
70
+ "- Want to find spaces with partial/fuzzy names (e.g., 'cafe' matches 'Café Barista')\n\n" +
71
+ "When NOT to Use:\n" +
72
+ "- Already have asset IDs and need detailed info → use butlr_get_asset_details directly\n" +
73
+ "- Want to browse full organizational hierarchy → use butlr_list_topology instead\n" +
74
+ "- Need to analyze occupancy or sensor data → use this tool first to find IDs, then use data tools\n\n" +
75
+ "Search Features: Fuzzy matching (handles typos), multi-field search (name, MAC, serial), score threshold (≥70), type filtering, result limiting (default 20, max 100)\n\n" +
76
+ "Example Workflow: butlr_search_assets(query: 'café') → get room_cafe_123 → butlr_space_busyness(space_id_or_name: 'room_cafe_123')\n\n" +
77
+ "See Also: butlr_get_asset_details, butlr_list_topology, butlr_fetch_entity_details";
78
+ /**
79
+ * Execute search_assets tool
80
+ */
81
+ export async function executeSearchAssets(args) {
82
+ const maxResults = args.max_results;
83
+ if (process.env.DEBUG) {
84
+ console.error(`[search-assets] Searching for "${args.query}"` +
85
+ (args.asset_types ? ` in types: ${args.asset_types.join(",")}` : "") +
86
+ ` (max: ${maxResults})`);
87
+ }
88
+ // Use a generic cache key for full topology (we'll search across it)
89
+ const cacheKey = generateTopologyCacheKey(process.env.BUTLR_ORG_ID || "default", true, // include devices for comprehensive search
90
+ true, // include zones
91
+ undefined);
92
+ // Try to get cached topology
93
+ let sites = [];
94
+ const cached = getCachedTopology(cacheKey);
95
+ if (cached && cached.data && cached.data.sites) {
96
+ if (process.env.DEBUG) {
97
+ console.error("[search-assets] Using cached topology for search");
98
+ }
99
+ sites = cached.data.sites;
100
+ }
101
+ else {
102
+ // Fetch fresh topology
103
+ if (process.env.DEBUG) {
104
+ console.error("[search-assets] Fetching fresh topology for search");
105
+ }
106
+ try {
107
+ const result = await apolloClient.query({
108
+ query: GET_FULL_TOPOLOGY,
109
+ fetchPolicy: "network-only",
110
+ });
111
+ // Apollo can return both data and errors - only fail if we have no data
112
+ if (!result.data || !result.data.sites || !result.data.sites.data) {
113
+ // If we have error but no data, throw
114
+ if (result.error) {
115
+ throw result.error;
116
+ }
117
+ throw new Error("Invalid response structure from API");
118
+ }
119
+ // Track whether the topology data is partial (errors alongside data)
120
+ const partialData = !!result.error;
121
+ if (partialData && process.env.DEBUG) {
122
+ console.error(`[search-assets] Warning: GraphQL errors present, data may be partial`);
123
+ }
124
+ sites = result.data.sites.data;
125
+ // Only cache complete topology data — partial results should be re-fetched
126
+ if (!partialData) {
127
+ setCachedTopology(cacheKey, { sites });
128
+ if (process.env.DEBUG) {
129
+ console.error(`[search-assets] Cached topology with ${sites.length} sites`);
130
+ }
131
+ }
132
+ else if (process.env.DEBUG) {
133
+ console.error(`[search-assets] Skipping cache — topology data is partial`);
134
+ }
135
+ }
136
+ catch (error) {
137
+ rethrowIfGraphQLError(error);
138
+ throw error;
139
+ }
140
+ }
141
+ // Flatten topology into searchable list
142
+ const flattened = flattenTopology(sites);
143
+ if (process.env.DEBUG) {
144
+ console.error(`[search-assets] Flattened ${flattened.length} total assets`);
145
+ }
146
+ // Filter by asset types if specified
147
+ let searchableAssets = flattened;
148
+ if (args.asset_types && args.asset_types.length > 0) {
149
+ searchableAssets = flattened.filter((asset) => args.asset_types.includes(asset.type));
150
+ if (process.env.DEBUG) {
151
+ console.error(`[search-assets] Filtered to ${searchableAssets.length} assets of types: ${args.asset_types.join(",")}`);
152
+ }
153
+ }
154
+ // Perform fuzzy search
155
+ // Search on name, mac_address (sensors), and serialNumber (hives)
156
+ const matches = searchAssets(searchableAssets, args.query, {
157
+ matchFields: ["name", "mac_address", "serialNumber"],
158
+ minScore: 70,
159
+ maxResults,
160
+ });
161
+ if (process.env.DEBUG) {
162
+ console.error(`[search-assets] Found ${matches.length} matches`);
163
+ }
164
+ // Format results with minimal fields
165
+ const results = matches.map((match) => ({
166
+ id: match.asset.id,
167
+ name: match.asset.name,
168
+ type: match.asset.type,
169
+ path: buildAssetPath(match.asset),
170
+ match_score: match.score,
171
+ site_id: match.asset.site_id,
172
+ building_id: match.asset.building_id,
173
+ floor_id: match.asset.floor_id,
174
+ room_id: match.asset.room_id,
175
+ }));
176
+ return {
177
+ query: args.query,
178
+ matches: results,
179
+ total_matches: results.length,
180
+ searched_assets: searchableAssets.length,
181
+ timestamp: new Date().toISOString(),
182
+ };
183
+ }
184
+ /**
185
+ * Register butlr_search_assets with an McpServer instance
186
+ */
187
+ export function registerSearchAssets(server) {
188
+ server.registerTool("butlr_search_assets", {
189
+ title: "Search Butlr Assets",
190
+ description: SEARCH_ASSETS_DESCRIPTION,
191
+ inputSchema: searchAssetsInputShape,
192
+ annotations: {
193
+ readOnlyHint: true,
194
+ destructiveHint: false,
195
+ idempotentHint: true,
196
+ openWorldHint: true,
197
+ },
198
+ }, async (args) => {
199
+ const validated = SearchAssetsArgsSchema.parse(args);
200
+ const result = await executeSearchAssets(validated);
201
+ return {
202
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
203
+ };
204
+ });
205
+ }
206
+ //# sourceMappingURL=butlr-search-assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"butlr-search-assets.js","sourceRoot":"","sources":["../../src/tools/butlr-search-assets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import type { SpaceBusynessResponse } from "../types/responses.js";
4
+ export declare const SpaceBusynessArgsSchema: z.ZodObject<{
5
+ space_id_or_name: z.ZodString;
6
+ include_trend: z.ZodDefault<z.ZodBoolean>;
7
+ }, "strict", z.ZodTypeAny, {
8
+ space_id_or_name: string;
9
+ include_trend: boolean;
10
+ }, {
11
+ space_id_or_name: string;
12
+ include_trend?: boolean | undefined;
13
+ }>;
14
+ export type SpaceBusynessArgs = z.output<typeof SpaceBusynessArgsSchema>;
15
+ /**
16
+ * Execute space busyness tool
17
+ */
18
+ export declare function executeSpaceBusyness(args: SpaceBusynessArgs): Promise<SpaceBusynessResponse>;
19
+ /**
20
+ * Register butlr_space_busyness with an McpServer instance
21
+ */
22
+ export declare function registerSpaceBusyness(server: McpServer): void;
23
+ //# sourceMappingURL=butlr-space-busyness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"butlr-space-busyness.d.ts","sourceRoot":"","sources":["../../src/tools/butlr-space-busyness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AA+BnE,eAAO,MAAM,uBAAuB;;;;;;;;;EAA6C,CAAC;AAgClF,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAyDzE;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,iBAAiB,kCAiMjE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsB7D"}