@butlr/butlr-mcp-server 0.1.1 → 0.2.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.
- package/LICENSE +1 -1
- package/README.md +28 -2
- package/dist/cache/occupancy-cache.d.ts.map +1 -1
- package/dist/cache/occupancy-cache.js +9 -20
- package/dist/cache/occupancy-cache.js.map +1 -1
- package/dist/cache/topology-cache.d.ts.map +1 -1
- package/dist/cache/topology-cache.js +7 -10
- package/dist/cache/topology-cache.js.map +1 -1
- package/dist/clients/auth-client.d.ts.map +1 -1
- package/dist/clients/auth-client.js +5 -9
- package/dist/clients/auth-client.js.map +1 -1
- package/dist/clients/graphql-client.d.ts.map +1 -1
- package/dist/clients/graphql-client.js +5 -4
- package/dist/clients/graphql-client.js.map +1 -1
- package/dist/clients/queries/tags.d.ts +39 -0
- package/dist/clients/queries/tags.d.ts.map +1 -0
- package/dist/clients/queries/tags.js +42 -0
- package/dist/clients/queries/tags.js.map +1 -0
- package/dist/clients/queries/topology.d.ts +0 -1
- package/dist/clients/queries/topology.d.ts.map +1 -1
- package/dist/clients/queries/topology.js +0 -1
- package/dist/clients/queries/topology.js.map +1 -1
- package/dist/clients/reporting-client.d.ts +1 -1
- package/dist/clients/reporting-client.d.ts.map +1 -1
- package/dist/clients/reporting-client.js +10 -14
- package/dist/clients/reporting-client.js.map +1 -1
- package/dist/clients/stats-client.d.ts +1 -2
- package/dist/clients/stats-client.d.ts.map +1 -1
- package/dist/clients/stats-client.js +7 -11
- package/dist/clients/stats-client.js.map +1 -1
- package/dist/clients/types.d.ts +2 -3
- package/dist/clients/types.d.ts.map +1 -1
- package/dist/clients/types.js +0 -1
- package/dist/clients/types.js.map +1 -1
- package/dist/errors/mcp-errors.d.ts +6 -0
- package/dist/errors/mcp-errors.d.ts.map +1 -1
- package/dist/errors/mcp-errors.js +21 -1
- package/dist/errors/mcp-errors.js.map +1 -1
- package/dist/index.js +8 -5
- package/dist/index.js.map +1 -1
- package/dist/tools/butlr-available-rooms.d.ts +25 -1
- package/dist/tools/butlr-available-rooms.d.ts.map +1 -1
- package/dist/tools/butlr-available-rooms.js +130 -57
- package/dist/tools/butlr-available-rooms.js.map +1 -1
- package/dist/tools/butlr-fetch-entity-details.d.ts.map +1 -1
- package/dist/tools/butlr-fetch-entity-details.js +14 -14
- package/dist/tools/butlr-fetch-entity-details.js.map +1 -1
- package/dist/tools/butlr-get-asset-details.d.ts.map +1 -1
- package/dist/tools/butlr-get-asset-details.js +10 -15
- package/dist/tools/butlr-get-asset-details.js.map +1 -1
- package/dist/tools/butlr-get-current-occupancy.d.ts.map +1 -1
- package/dist/tools/butlr-get-current-occupancy.js +27 -12
- package/dist/tools/butlr-get-current-occupancy.js.map +1 -1
- package/dist/tools/butlr-get-occupancy-timeseries.d.ts.map +1 -1
- package/dist/tools/butlr-get-occupancy-timeseries.js +36 -28
- package/dist/tools/butlr-get-occupancy-timeseries.js.map +1 -1
- package/dist/tools/butlr-hardware-snapshot.d.ts.map +1 -1
- package/dist/tools/butlr-hardware-snapshot.js +7 -9
- package/dist/tools/butlr-hardware-snapshot.js.map +1 -1
- package/dist/tools/butlr-list-tags.d.ts +32 -0
- package/dist/tools/butlr-list-tags.d.ts.map +1 -0
- package/dist/tools/butlr-list-tags.js +108 -0
- package/dist/tools/butlr-list-tags.js.map +1 -0
- package/dist/tools/butlr-list-topology.d.ts.map +1 -1
- package/dist/tools/butlr-list-topology.js +20 -26
- package/dist/tools/butlr-list-topology.js.map +1 -1
- package/dist/tools/butlr-search-assets.d.ts.map +1 -1
- package/dist/tools/butlr-search-assets.js +19 -31
- package/dist/tools/butlr-search-assets.js.map +1 -1
- package/dist/tools/butlr-space-busyness.d.ts.map +1 -1
- package/dist/tools/butlr-space-busyness.js +27 -22
- package/dist/tools/butlr-space-busyness.js.map +1 -1
- package/dist/tools/butlr-traffic-flow.d.ts.map +1 -1
- package/dist/tools/butlr-traffic-flow.js +17 -31
- package/dist/tools/butlr-traffic-flow.js.map +1 -1
- package/dist/types/responses.d.ts +4 -0
- package/dist/types/responses.d.ts.map +1 -1
- package/dist/utils/asset-flattener.js +2 -2
- package/dist/utils/asset-flattener.js.map +1 -1
- package/dist/utils/debug.d.ts +6 -0
- package/dist/utils/debug.d.ts.map +1 -0
- package/dist/utils/debug.js +10 -0
- package/dist/utils/debug.js.map +1 -0
- package/dist/utils/graphql-helpers.d.ts +13 -0
- package/dist/utils/graphql-helpers.d.ts.map +1 -1
- package/dist/utils/graphql-helpers.js +25 -0
- package/dist/utils/graphql-helpers.js.map +1 -1
- package/dist/utils/occupancy-helpers.d.ts +2 -0
- package/dist/utils/occupancy-helpers.d.ts.map +1 -1
- package/dist/utils/occupancy-helpers.js +8 -5
- package/dist/utils/occupancy-helpers.js.map +1 -1
- package/dist/utils/timezone-helpers.d.ts +8 -3
- package/dist/utils/timezone-helpers.d.ts.map +1 -1
- package/dist/utils/timezone-helpers.js +27 -24
- package/dist/utils/timezone-helpers.js.map +1 -1
- package/llms.txt +125 -0
- package/package.json +3 -3
|
@@ -4,9 +4,15 @@ import { z } from "zod";
|
|
|
4
4
|
import { getCachedTopology, setCachedTopology, generateTopologyCacheKey, } from "../cache/topology-cache.js";
|
|
5
5
|
import { formatTopologyTree } from "../utils/tree-formatter.js";
|
|
6
6
|
import { isProductionSensor, isProductionHive, rethrowIfGraphQLError, } from "../utils/graphql-helpers.js";
|
|
7
|
+
import { debug } from "../utils/debug.js";
|
|
8
|
+
import { withToolErrorHandling } from "../errors/mcp-errors.js";
|
|
7
9
|
const LIST_TOPOLOGY_DESCRIPTION = "Display org hierarchy tree with flexible depth control. Can show full tree, specific subtrees, or flat lists. " +
|
|
8
10
|
"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
|
|
11
|
+
"Use starting_depth to choose which level to show, and traversal_depth to control how many levels below to include.\n\n" +
|
|
12
|
+
"When NOT to Use:\n" +
|
|
13
|
+
"- Searching for assets by name or keyword → use butlr_search_assets for fuzzy name-based lookups\n" +
|
|
14
|
+
"- Need detailed info for a specific asset you already have an ID for → use butlr_get_asset_details instead\n" +
|
|
15
|
+
"- Need only specific fields for known entity IDs → use butlr_fetch_entity_details for selective field fetching";
|
|
10
16
|
/** Shared shape — used by both registerTool (SDK schema) and full validation */
|
|
11
17
|
const listTopologyInputShape = {
|
|
12
18
|
asset_ids: z
|
|
@@ -33,9 +39,7 @@ export async function executeListTopology(args = {}) {
|
|
|
33
39
|
const startingDepth = args.starting_depth ?? 0;
|
|
34
40
|
const traversalDepth = args.traversal_depth ?? 0;
|
|
35
41
|
const assetIds = args.asset_ids ?? [];
|
|
36
|
-
|
|
37
|
-
console.error(`[butlr-list-topology] Fetching topology: starting_depth=${startingDepth}, traversal_depth=${traversalDepth}, assets=${assetIds.length || "all"}`);
|
|
38
|
-
}
|
|
42
|
+
debug("butlr-list-topology", `Fetching topology: starting_depth=${startingDepth}, traversal_depth=${traversalDepth}, assets=${assetIds.length || "all"}`);
|
|
39
43
|
// Use a cache key that includes devices
|
|
40
44
|
const cacheKey = generateTopologyCacheKey(process.env.BUTLR_ORG_ID || "default", true, // include devices
|
|
41
45
|
true, // include zones
|
|
@@ -45,16 +49,12 @@ export async function executeListTopology(args = {}) {
|
|
|
45
49
|
let partialData = false;
|
|
46
50
|
const cached = getCachedTopology(cacheKey);
|
|
47
51
|
if (cached && cached.data && cached.data.sites) {
|
|
48
|
-
|
|
49
|
-
console.error("[butlr-list-topology] Using cached topology");
|
|
50
|
-
}
|
|
52
|
+
debug("butlr-list-topology", "Using cached topology");
|
|
51
53
|
sites = cached.data.sites;
|
|
52
54
|
}
|
|
53
55
|
else {
|
|
54
56
|
// Fetch fresh topology
|
|
55
|
-
|
|
56
|
-
console.error("[butlr-list-topology] Fetching fresh topology");
|
|
57
|
-
}
|
|
57
|
+
debug("butlr-list-topology", "Fetching fresh topology");
|
|
58
58
|
try {
|
|
59
59
|
const result = await apolloClient.query({
|
|
60
60
|
query: GET_FULL_TOPOLOGY,
|
|
@@ -70,14 +70,12 @@ export async function executeListTopology(args = {}) {
|
|
|
70
70
|
}
|
|
71
71
|
// Track whether the topology data is partial (errors alongside data)
|
|
72
72
|
partialData = !!result.error;
|
|
73
|
-
if (partialData
|
|
74
|
-
|
|
73
|
+
if (partialData) {
|
|
74
|
+
debug("butlr-list-topology", "Warning: GraphQL errors present, data may be partial");
|
|
75
75
|
}
|
|
76
76
|
sites = result.data.sites.data;
|
|
77
77
|
// Query all sensors and hives separately (nested fields are broken)
|
|
78
|
-
|
|
79
|
-
console.error("[butlr-list-topology] Fetching all sensors and hives...");
|
|
80
|
-
}
|
|
78
|
+
debug("butlr-list-topology", "Fetching all sensors and hives...");
|
|
81
79
|
const [sensorsResult, hivesResult] = await Promise.all([
|
|
82
80
|
apolloClient.query({
|
|
83
81
|
query: GET_ALL_SENSORS,
|
|
@@ -92,20 +90,16 @@ export async function executeListTopology(args = {}) {
|
|
|
92
90
|
// (Note: These filters are ONLY for topology display, not occupancy queries)
|
|
93
91
|
const allSensors = (sensorsResult.data?.sensors?.data || []).filter(isProductionSensor);
|
|
94
92
|
const allHives = (hivesResult.data?.hives?.data || []).filter(isProductionHive);
|
|
95
|
-
|
|
96
|
-
console.error(`[butlr-list-topology] Got ${allSensors.length} production sensors, ${allHives.length} production hives (test/placeholder devices filtered)`);
|
|
97
|
-
}
|
|
93
|
+
debug("butlr-list-topology", `Got ${allSensors.length} production sensors, ${allHives.length} production hives (test/placeholder devices filtered)`);
|
|
98
94
|
// Merge sensors and hives into topology by floor_id
|
|
99
95
|
sites = mergeSensorsAndHivesIntoTopology(sites, allSensors, allHives);
|
|
100
96
|
// Only cache complete topology data — partial results should be re-fetched
|
|
101
97
|
if (!partialData) {
|
|
102
98
|
setCachedTopology(cacheKey, { sites });
|
|
103
|
-
|
|
104
|
-
console.error(`[butlr-list-topology] Cached topology with ${sites.length} sites`);
|
|
105
|
-
}
|
|
99
|
+
debug("butlr-list-topology", `Cached topology with ${sites.length} sites`);
|
|
106
100
|
}
|
|
107
|
-
else
|
|
108
|
-
|
|
101
|
+
else {
|
|
102
|
+
debug("butlr-list-topology", "Skipping cache — topology data is partial");
|
|
109
103
|
}
|
|
110
104
|
}
|
|
111
105
|
catch (error) {
|
|
@@ -228,14 +222,14 @@ export function registerListTopology(server) {
|
|
|
228
222
|
readOnlyHint: true,
|
|
229
223
|
destructiveHint: false,
|
|
230
224
|
idempotentHint: true,
|
|
231
|
-
openWorldHint:
|
|
225
|
+
openWorldHint: false,
|
|
232
226
|
},
|
|
233
|
-
}, async (args) => {
|
|
227
|
+
}, withToolErrorHandling(async (args) => {
|
|
234
228
|
const validated = ListTopologyArgsSchema.parse(args);
|
|
235
229
|
const result = await executeListTopology(validated);
|
|
236
230
|
return {
|
|
237
231
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
238
232
|
};
|
|
239
|
-
});
|
|
233
|
+
}));
|
|
240
234
|
}
|
|
241
235
|
//# sourceMappingURL=butlr-list-topology.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"butlr-list-topology.js","sourceRoot":"","sources":["../../src/tools/butlr-list-topology.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAWnG,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"butlr-list-topology.js","sourceRoot":"","sources":["../../src/tools/butlr-list-topology.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +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;
|
|
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;AAoDxB,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;;;;;;GAmH/D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsB5D"}
|
|
@@ -6,6 +6,8 @@ import { flattenTopology } from "../utils/asset-flattener.js";
|
|
|
6
6
|
import { searchAssets } from "../utils/fuzzy-match.js";
|
|
7
7
|
import { buildAssetPath } from "../utils/path-builder.js";
|
|
8
8
|
import { rethrowIfGraphQLError } from "../utils/graphql-helpers.js";
|
|
9
|
+
import { debug } from "../utils/debug.js";
|
|
10
|
+
import { withToolErrorHandling } from "../errors/mcp-errors.js";
|
|
9
11
|
const VALID_ASSET_TYPES = ["site", "building", "floor", "room", "zone", "sensor", "hive"];
|
|
10
12
|
/** Shared shape — used by both registerTool (SDK schema) and full validation */
|
|
11
13
|
const searchAssetsInputShape = {
|
|
@@ -80,11 +82,9 @@ const SEARCH_ASSETS_DESCRIPTION = "Search for Butlr assets (sites, buildings, fl
|
|
|
80
82
|
*/
|
|
81
83
|
export async function executeSearchAssets(args) {
|
|
82
84
|
const maxResults = args.max_results;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
` (max: ${maxResults})`);
|
|
87
|
-
}
|
|
85
|
+
debug("search-assets", `Searching for "${args.query}"` +
|
|
86
|
+
(args.asset_types ? ` in types: ${args.asset_types.join(",")}` : "") +
|
|
87
|
+
` (max: ${maxResults})`);
|
|
88
88
|
// Use a generic cache key for full topology (we'll search across it)
|
|
89
89
|
const cacheKey = generateTopologyCacheKey(process.env.BUTLR_ORG_ID || "default", true, // include devices for comprehensive search
|
|
90
90
|
true, // include zones
|
|
@@ -93,16 +93,12 @@ export async function executeSearchAssets(args) {
|
|
|
93
93
|
let sites = [];
|
|
94
94
|
const cached = getCachedTopology(cacheKey);
|
|
95
95
|
if (cached && cached.data && cached.data.sites) {
|
|
96
|
-
|
|
97
|
-
console.error("[search-assets] Using cached topology for search");
|
|
98
|
-
}
|
|
96
|
+
debug("search-assets", "Using cached topology for search");
|
|
99
97
|
sites = cached.data.sites;
|
|
100
98
|
}
|
|
101
99
|
else {
|
|
102
100
|
// Fetch fresh topology
|
|
103
|
-
|
|
104
|
-
console.error("[search-assets] Fetching fresh topology for search");
|
|
105
|
-
}
|
|
101
|
+
debug("search-assets", "Fetching fresh topology for search");
|
|
106
102
|
try {
|
|
107
103
|
const result = await apolloClient.query({
|
|
108
104
|
query: GET_FULL_TOPOLOGY,
|
|
@@ -118,19 +114,17 @@ export async function executeSearchAssets(args) {
|
|
|
118
114
|
}
|
|
119
115
|
// Track whether the topology data is partial (errors alongside data)
|
|
120
116
|
const partialData = !!result.error;
|
|
121
|
-
if (partialData
|
|
122
|
-
|
|
117
|
+
if (partialData) {
|
|
118
|
+
debug("search-assets", "Warning: GraphQL errors present, data may be partial");
|
|
123
119
|
}
|
|
124
120
|
sites = result.data.sites.data;
|
|
125
121
|
// Only cache complete topology data — partial results should be re-fetched
|
|
126
122
|
if (!partialData) {
|
|
127
123
|
setCachedTopology(cacheKey, { sites });
|
|
128
|
-
|
|
129
|
-
console.error(`[search-assets] Cached topology with ${sites.length} sites`);
|
|
130
|
-
}
|
|
124
|
+
debug("search-assets", `Cached topology with ${sites.length} sites`);
|
|
131
125
|
}
|
|
132
|
-
else
|
|
133
|
-
|
|
126
|
+
else {
|
|
127
|
+
debug("search-assets", "Skipping cache — topology data is partial");
|
|
134
128
|
}
|
|
135
129
|
}
|
|
136
130
|
catch (error) {
|
|
@@ -140,16 +134,12 @@ export async function executeSearchAssets(args) {
|
|
|
140
134
|
}
|
|
141
135
|
// Flatten topology into searchable list
|
|
142
136
|
const flattened = flattenTopology(sites);
|
|
143
|
-
|
|
144
|
-
console.error(`[search-assets] Flattened ${flattened.length} total assets`);
|
|
145
|
-
}
|
|
137
|
+
debug("search-assets", `Flattened ${flattened.length} total assets`);
|
|
146
138
|
// Filter by asset types if specified
|
|
147
139
|
let searchableAssets = flattened;
|
|
148
140
|
if (args.asset_types && args.asset_types.length > 0) {
|
|
149
|
-
searchableAssets = flattened.filter((asset) => args.asset_types
|
|
150
|
-
|
|
151
|
-
console.error(`[search-assets] Filtered to ${searchableAssets.length} assets of types: ${args.asset_types.join(",")}`);
|
|
152
|
-
}
|
|
141
|
+
searchableAssets = flattened.filter((asset) => args.asset_types?.includes(asset.type));
|
|
142
|
+
debug("search-assets", `Filtered to ${searchableAssets.length} assets of types: ${args.asset_types.join(",")}`);
|
|
153
143
|
}
|
|
154
144
|
// Perform fuzzy search
|
|
155
145
|
// Search on name, mac_address (sensors), and serialNumber (hives)
|
|
@@ -158,9 +148,7 @@ export async function executeSearchAssets(args) {
|
|
|
158
148
|
minScore: 70,
|
|
159
149
|
maxResults,
|
|
160
150
|
});
|
|
161
|
-
|
|
162
|
-
console.error(`[search-assets] Found ${matches.length} matches`);
|
|
163
|
-
}
|
|
151
|
+
debug("search-assets", `Found ${matches.length} matches`);
|
|
164
152
|
// Format results with minimal fields
|
|
165
153
|
const results = matches.map((match) => ({
|
|
166
154
|
id: match.asset.id,
|
|
@@ -193,14 +181,14 @@ export function registerSearchAssets(server) {
|
|
|
193
181
|
readOnlyHint: true,
|
|
194
182
|
destructiveHint: false,
|
|
195
183
|
idempotentHint: true,
|
|
196
|
-
openWorldHint:
|
|
184
|
+
openWorldHint: false,
|
|
197
185
|
},
|
|
198
|
-
}, async (args) => {
|
|
186
|
+
}, withToolErrorHandling(async (args) => {
|
|
199
187
|
const validated = SearchAssetsArgsSchema.parse(args);
|
|
200
188
|
const result = await executeSearchAssets(validated);
|
|
201
189
|
return {
|
|
202
190
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
203
191
|
};
|
|
204
|
-
});
|
|
192
|
+
}));
|
|
205
193
|
}
|
|
206
194
|
//# sourceMappingURL=butlr-search-assets.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"butlr-search-assets.js","sourceRoot":"","sources":["../../src/tools/butlr-search-assets.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,eAAe,EAAuB,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,YAAY,EAAwB,MAAM,yBAAyB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"butlr-search-assets.js","sourceRoot":"","sources":["../../src/tools/butlr-search-assets.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +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;
|
|
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;AAgBxB,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,kCAsMjE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsB7D"}
|
|
@@ -7,6 +7,8 @@ import { executeSearchAssets } from "./butlr-search-assets.js";
|
|
|
7
7
|
import { buildBusynessSummary, getOccupancyLabel, getTrendLabel, getBusinessRecommendation, formatDayAndTime, } from "../utils/natural-language.js";
|
|
8
8
|
import { getCachedOccupancy, setCachedOccupancy } from "../cache/occupancy-cache.js";
|
|
9
9
|
import { rethrowIfGraphQLError } from "../utils/graphql-helpers.js";
|
|
10
|
+
import { debug } from "../utils/debug.js";
|
|
11
|
+
import { withToolErrorHandling } from "../errors/mcp-errors.js";
|
|
10
12
|
/** Shared shape -- used by both registerTool (SDK schema) and full validation */
|
|
11
13
|
const spaceBusynessInputShape = {
|
|
12
14
|
space_id_or_name: z
|
|
@@ -110,9 +112,7 @@ export async function executeSpaceBusyness(args) {
|
|
|
110
112
|
let spaceType = "room";
|
|
111
113
|
// If not an ID, search for the space
|
|
112
114
|
if (!spaceId.match(/^(room|zone)_/)) {
|
|
113
|
-
|
|
114
|
-
console.error(`[space-busyness] Searching for space: "${args.space_id_or_name}"`);
|
|
115
|
-
}
|
|
115
|
+
debug("space-busyness", `Searching for space: "${args.space_id_or_name}"`);
|
|
116
116
|
const searchResults = await executeSearchAssets({
|
|
117
117
|
query: args.space_id_or_name,
|
|
118
118
|
asset_types: ["room", "zone"],
|
|
@@ -125,9 +125,7 @@ export async function executeSpaceBusyness(args) {
|
|
|
125
125
|
const bestMatch = searchResults.matches[0];
|
|
126
126
|
spaceId = bestMatch.id;
|
|
127
127
|
spaceType = bestMatch.type;
|
|
128
|
-
|
|
129
|
-
console.error(`[space-busyness] Using best match: ${bestMatch.name} (${spaceId})`);
|
|
130
|
-
}
|
|
128
|
+
debug("space-busyness", `Using best match: ${bestMatch.name} (${spaceId})`);
|
|
131
129
|
}
|
|
132
130
|
else {
|
|
133
131
|
// Determine type from ID prefix
|
|
@@ -136,7 +134,8 @@ export async function executeSpaceBusyness(args) {
|
|
|
136
134
|
// Query space details
|
|
137
135
|
let space = null;
|
|
138
136
|
let spacePath = "";
|
|
139
|
-
let spaceTimezone;
|
|
137
|
+
let spaceTimezone = process.env.BUTLR_TIMEZONE || "UTC";
|
|
138
|
+
let timezoneFallback = true;
|
|
140
139
|
try {
|
|
141
140
|
if (spaceType === "room") {
|
|
142
141
|
const result = await apolloClient.query({
|
|
@@ -151,7 +150,10 @@ export async function executeSpaceBusyness(args) {
|
|
|
151
150
|
const floor = space.floor;
|
|
152
151
|
const building = floor?.building;
|
|
153
152
|
spacePath = building ? `${building.name} > ${floor.name} > ${space.name}` : space.name;
|
|
154
|
-
|
|
153
|
+
if (building?.site?.timezone) {
|
|
154
|
+
spaceTimezone = building.site.timezone;
|
|
155
|
+
timezoneFallback = false;
|
|
156
|
+
}
|
|
155
157
|
}
|
|
156
158
|
else {
|
|
157
159
|
const result = await apolloClient.query({
|
|
@@ -166,7 +168,10 @@ export async function executeSpaceBusyness(args) {
|
|
|
166
168
|
const floor = space.floor;
|
|
167
169
|
const building = floor?.building;
|
|
168
170
|
spacePath = building ? `${building.name} > ${floor.name} > ${space.name}` : space.name;
|
|
169
|
-
|
|
171
|
+
if (building?.site?.timezone) {
|
|
172
|
+
spaceTimezone = building.site.timezone;
|
|
173
|
+
timezoneFallback = false;
|
|
174
|
+
}
|
|
170
175
|
}
|
|
171
176
|
}
|
|
172
177
|
catch (error) {
|
|
@@ -179,9 +184,7 @@ export async function executeSpaceBusyness(args) {
|
|
|
179
184
|
const cached = getCachedOccupancy(spaceId, now);
|
|
180
185
|
if (cached) {
|
|
181
186
|
currentOccupancy = cached.occupancy;
|
|
182
|
-
|
|
183
|
-
console.error(`[space-busyness] Using cached occupancy: ${currentOccupancy}`);
|
|
184
|
-
}
|
|
187
|
+
debug("space-busyness", `Using cached occupancy: ${currentOccupancy}`);
|
|
185
188
|
}
|
|
186
189
|
else {
|
|
187
190
|
try {
|
|
@@ -192,18 +195,20 @@ export async function executeSpaceBusyness(args) {
|
|
|
192
195
|
}
|
|
193
196
|
}
|
|
194
197
|
catch (error) {
|
|
195
|
-
|
|
198
|
+
debug("space-busyness", "Failed to get occupancy:", error);
|
|
196
199
|
throw new Error(`Failed to get current occupancy for ${space.name}. The space may not have active sensors.`);
|
|
197
200
|
}
|
|
198
201
|
}
|
|
199
202
|
// Calculate utilization (null when capacity is not configured)
|
|
200
|
-
const
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
: null;
|
|
203
|
+
const maxCapacity = space.capacity?.max;
|
|
204
|
+
const capacityConfigured = !!(maxCapacity && maxCapacity > 0);
|
|
205
|
+
const utilizationPercent = capacityConfigured ? (currentOccupancy / maxCapacity) * 100 : null;
|
|
204
206
|
const label = utilizationPercent !== null ? getOccupancyLabel(utilizationPercent) : null;
|
|
205
207
|
// Build response
|
|
206
208
|
const warnings = [];
|
|
209
|
+
if (timezoneFallback) {
|
|
210
|
+
warnings.push(`Could not determine site timezone for this space. Using ${spaceTimezone} as fallback — time-based comparisons may not reflect the site's actual local time.`);
|
|
211
|
+
}
|
|
207
212
|
if (!capacityConfigured) {
|
|
208
213
|
warnings.push("Capacity is not configured for this space. Utilization percentage and busyness label are unavailable. Configure capacity in the Butlr dashboard for richer insights.");
|
|
209
214
|
}
|
|
@@ -256,7 +261,7 @@ export async function executeSpaceBusyness(args) {
|
|
|
256
261
|
}
|
|
257
262
|
}
|
|
258
263
|
catch (error) {
|
|
259
|
-
|
|
264
|
+
debug("space-busyness", "Failed to get trend data:", error);
|
|
260
265
|
warnings.push("Could not retrieve historical trend data. Trend comparison is unavailable.");
|
|
261
266
|
}
|
|
262
267
|
}
|
|
@@ -265,7 +270,7 @@ export async function executeSpaceBusyness(args) {
|
|
|
265
270
|
response.summary = buildBusynessSummary({
|
|
266
271
|
spaceName: space.name,
|
|
267
272
|
occupancy: Math.round(currentOccupancy),
|
|
268
|
-
capacity:
|
|
273
|
+
capacity: maxCapacity,
|
|
269
274
|
utilizationPercent,
|
|
270
275
|
trendLabel: response.trend?.trend_label,
|
|
271
276
|
dayTime: formatDayAndTime(now, spaceTimezone),
|
|
@@ -291,14 +296,14 @@ export function registerSpaceBusyness(server) {
|
|
|
291
296
|
readOnlyHint: true,
|
|
292
297
|
destructiveHint: false,
|
|
293
298
|
idempotentHint: false,
|
|
294
|
-
openWorldHint:
|
|
299
|
+
openWorldHint: false,
|
|
295
300
|
},
|
|
296
|
-
}, async (args) => {
|
|
301
|
+
}, withToolErrorHandling(async (args) => {
|
|
297
302
|
const validated = SpaceBusynessArgsSchema.parse(args);
|
|
298
303
|
const result = await executeSpaceBusyness(validated);
|
|
299
304
|
return {
|
|
300
305
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
301
306
|
};
|
|
302
|
-
});
|
|
307
|
+
}));
|
|
303
308
|
}
|
|
304
309
|
//# sourceMappingURL=butlr-space-busyness.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"butlr-space-busyness.js","sourceRoot":"","sources":["../../src/tools/butlr-space-busyness.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACrF,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"butlr-space-busyness.js","sourceRoot":"","sources":["../../src/tools/butlr-space-busyness.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACrF,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAiBhE,iFAAiF;AACjF,MAAM,uBAAuB,GAAG;IAC9B,gBAAgB,EAAE,CAAC;SAChB,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,kCAAkC,CAAC;SAC1C,GAAG,CAAC,GAAG,EAAE,4CAA4C,CAAC;SACtD,IAAI,EAAE;SACN,QAAQ,CAAC,sDAAsD,CAAC;IAEnE,aAAa,EAAE,CAAC;SACb,OAAO,EAAE;SACT,OAAO,CAAC,IAAI,CAAC;SACb,QAAQ,CAAC,gDAAgD,CAAC;CAC9D,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,MAAM,EAAE,CAAC;AAElF,MAAM,0BAA0B,GAC9B,2VAA2V;IAC3V,kBAAkB;IAClB,oIAAoI;IACpI,iHAAiH;IACjH,8FAA8F;IAC9F,oGAAoG;IACpG,oBAAoB;IACpB,qDAAqD;IACrD,qDAAqD;IACrD,oCAAoC;IACpC,sDAAsD;IACtD,gDAAgD;IAChD,wEAAwE;IACxE,uDAAuD;IACvD,oEAAoE;IACpE,gBAAgB;IAChB,sEAAsE;IACtE,+EAA+E;IAC/E,4EAA4E;IAC5E,4FAA4F;IAC5F,uEAAuE;IACvE,oBAAoB;IACpB,wGAAwG;IACxG,+FAA+F;IAC/F,2FAA2F;IAC3F,0FAA0F;IAC1F,gFAAgF;IAChF,gHAAgH,CAAC;AAInH,MAAM,QAAQ,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BnB,CAAC;AAEF,MAAM,QAAQ,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;CAyBnB,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,IAAuB;IAChE,IAAI,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC;IACpC,IAAI,SAAS,GAAoB,MAAM,CAAC;IAExC,qCAAqC;IACrC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,CAAC;QACpC,KAAK,CAAC,gBAAgB,EAAE,yBAAyB,IAAI,CAAC,gBAAgB,GAAG,CAAC,CAAC;QAE3E,MAAM,aAAa,GAAG,MAAM,mBAAmB,CAAC;YAC9C,KAAK,EAAE,IAAI,CAAC,gBAAgB;YAC5B,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;YAC7B,WAAW,EAAE,CAAC;SACf,CAAC,CAAC;QAEH,IAAI,aAAa,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CACb,6BAA6B,IAAI,CAAC,gBAAgB,iCAAiC,CACpF,CAAC;QACJ,CAAC;QAED,iBAAiB;QACjB,MAAM,SAAS,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC3C,OAAO,GAAG,SAAS,CAAC,EAAE,CAAC;QACvB,SAAS,GAAG,SAAS,CAAC,IAAuB,CAAC;QAE9C,KAAK,CAAC,gBAAgB,EAAE,qBAAqB,SAAS,CAAC,IAAI,KAAK,OAAO,GAAG,CAAC,CAAC;IAC9E,CAAC;SAAM,CAAC;QACN,gCAAgC;QAChC,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAC5D,CAAC;IAED,sBAAsB;IACtB,IAAI,KAAK,GAAyC,IAAI,CAAC;IACvD,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,aAAa,GAAW,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,KAAK,CAAC;IAChE,IAAI,gBAAgB,GAAG,IAAI,CAAC;IAE5B,IAAI,CAAC;QACH,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,KAAK,CAA0B;gBAC/D,KAAK,EAAE,QAAQ;gBACf,SAAS,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE;gBAC9B,WAAW,EAAE,cAAc;aAC5B,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,QAAQ,OAAO,YAAY,CAAC,CAAC;YAC/C,CAAC;YAED,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;YACzB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;YAC1B,MAAM,QAAQ,GAAG,KAAK,EAAE,QAAQ,CAAC;YACjC,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,MAAM,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;YACvF,IAAI,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;gBAC7B,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;gBACvC,gBAAgB,GAAG,KAAK,CAAC;YAC3B,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,KAAK,CAA0B;gBAC/D,KAAK,EAAE,QAAQ;gBACf,SAAS,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE;gBAC9B,WAAW,EAAE,cAAc;aAC5B,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,QAAQ,OAAO,YAAY,CAAC,CAAC;YAC/C,CAAC;YAED,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;YACzB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;YAC1B,MAAM,QAAQ,GAAG,KAAK,EAAE,QAAQ,CAAC;YACjC,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,MAAM,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;YACvF,IAAI,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;gBAC7B,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;gBACvC,gBAAgB,GAAG,KAAK,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAC7B,MAAM,KAAK,CAAC;IACd,CAAC;IAED,4CAA4C;IAC5C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,IAAI,gBAAgB,GAAG,CAAC,CAAC;IAEzB,MAAM,MAAM,GAAG,kBAAkB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAChD,IAAI,MAAM,EAAE,CAAC;QACX,gBAAgB,GAAG,MAAM,CAAC,SAAS,CAAC;QACpC,KAAK,CAAC,gBAAgB,EAAE,2BAA2B,gBAAgB,EAAE,CAAC,CAAC;IACzE,CAAC;SAAM,CAAC;QACN,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,MAAM,mBAAmB,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;YACtE,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,gBAAgB,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;gBAC1C,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;YAChE,CAAC;QACH,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,KAAK,CAAC,gBAAgB,EAAE,0BAA0B,EAAE,KAAK,CAAC,CAAC;YAC3D,MAAM,IAAI,KAAK,CACb,uCAAuC,KAAK,CAAC,IAAI,0CAA0C,CAC5F,CAAC;QACJ,CAAC;IACH,CAAC;IAED,+DAA+D;IAC/D,MAAM,WAAW,GAAG,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC;IACxC,MAAM,kBAAkB,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC;IAC9D,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC,gBAAgB,GAAG,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9F,MAAM,KAAK,GAAG,kBAAkB,KAAK,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEzF,iBAAiB;IACjB,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,gBAAgB,EAAE,CAAC;QACrB,QAAQ,CAAC,IAAI,CACX,2DAA2D,aAAa,qFAAqF,CAC9J,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxB,QAAQ,CAAC,IAAI,CACX,sKAAsK,CACvK,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAA0B;QACtC,KAAK,EAAE;YACL,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,SAAS;SAChB;QACD,OAAO,EAAE;YACP,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC;YACvC,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,mBAAmB,EACjB,kBAAkB,KAAK,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI;YAChF,KAAK;YACL,mBAAmB,EAAE,kBAAkB;YACvC,KAAK,EAAE,GAAG,CAAC,WAAW,EAAE;SACzB;QACD,cAAc,EAAE,KAAK;YACnB,CAAC,CAAC,yBAAyB,CAAC,KAAK,CAAC;YAClC,CAAC,CAAC,wDAAwD;QAC5D,OAAO,EAAE,EAAE,EAAE,kBAAkB;QAC/B,SAAS,EAAE,GAAG,CAAC,WAAW,EAAE;KAC7B,CAAC;IAEF,yBAAyB;IACzB,IAAI,IAAI,CAAC,aAAa,KAAK,KAAK,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,iGAAiG;YACjG,MAAM,WAAW,GAAG,wBAAwB,CAAC;YAC7C,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;YAE5E,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC;gBAC3B,IAAI,gBAAwB,CAAC;gBAC7B,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;oBAChB,gBAAgB,GAAG,CAAC,CAAC,gBAAgB,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,GAAG,CAAC;gBACpE,CAAC;qBAAM,IAAI,gBAAgB,GAAG,CAAC,EAAE,CAAC;oBAChC,gBAAgB,GAAG,GAAG,CAAC;gBACzB,CAAC;qBAAM,CAAC;oBACN,gBAAgB,GAAG,CAAC,CAAC;gBACvB,CAAC;gBACD,MAAM,UAAU,GAAG,aAAa,CAAC,gBAAgB,CAAC,CAAC;gBAEnD,QAAQ,CAAC,KAAK,GAAG;oBACf,gBAAgB,EAAE,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;oBAChD,kBAAkB,EAAE,UAAU,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;oBAC3D,WAAW,EAAE,UAAU;oBACvB,kBAAkB,EAAE,GAAG,gBAAgB,CAAC,GAAG,EAAE,aAAa,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,wBAAwB;iBAChH,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,KAAK,CAAC,gBAAgB,EAAE,2BAA2B,EAAE,KAAK,CAAC,CAAC;YAC5D,QAAQ,CAAC,IAAI,CAAC,4EAA4E,CAAC,CAAC;QAC9F,CAAC;IACH,CAAC;IAED,gBAAgB;IAChB,IAAI,kBAAkB,IAAI,kBAAkB,KAAK,IAAI,EAAE,CAAC;QACtD,QAAQ,CAAC,OAAO,GAAG,oBAAoB,CAAC;YACtC,SAAS,EAAE,KAAK,CAAC,IAAI;YACrB,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC;YACvC,QAAQ,EAAE,WAAW;YACrB,kBAAkB;YAClB,UAAU,EAAE,QAAQ,CAAC,KAAK,EAAE,WAAW;YACvC,OAAO,EAAE,gBAAgB,CAAC,GAAG,EAAE,aAAa,CAAC;SAC9C,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,QAAQ,CAAC,OAAO,GAAG,GAAG,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,mCAAmC,CAAC;IACvG,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,QAAQ,CAAC,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAiB;IACrD,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,0BAA0B;QACvC,WAAW,EAAE,uBAAuB;QACpC,WAAW,EAAE;YACX,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,KAAK;YACrB,aAAa,EAAE,KAAK;SACrB;KACF,EACD,qBAAqB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACnC,MAAM,SAAS,GAAG,uBAAuB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,SAAS,CAAC,CAAC;QACrD,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC5E,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"butlr-traffic-flow.d.ts","sourceRoot":"","sources":["../../src/tools/butlr-traffic-flow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"butlr-traffic-flow.d.ts","sourceRoot":"","sources":["../../src/tools/butlr-traffic-flow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AA0CjE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;EAc/B,CAAC;AA+BJ,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAwBrE;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,eAAe,gCAuR7D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsB3D"}
|
|
@@ -5,8 +5,9 @@ import { ReportingRequestBuilder } from "../clients/reporting-client.js";
|
|
|
5
5
|
import { GET_ALL_SENSORS, GET_FULL_TOPOLOGY } from "../clients/queries/topology.js";
|
|
6
6
|
import { executeSearchAssets } from "./butlr-search-assets.js";
|
|
7
7
|
import { getTimezoneForAsset, buildTimezoneMetadata, getLocalMidnight, } from "../utils/timezone-helpers.js";
|
|
8
|
-
import { createValidationError } from "../errors/mcp-errors.js";
|
|
8
|
+
import { createValidationError, withToolErrorHandling } from "../errors/mcp-errors.js";
|
|
9
9
|
import { rethrowIfGraphQLError } from "../utils/graphql-helpers.js";
|
|
10
|
+
import { debug } from "../utils/debug.js";
|
|
10
11
|
const timeStringSchema = z.string().refine((val) => {
|
|
11
12
|
const isoMatch = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/.test(val);
|
|
12
13
|
const relativeMatch = /^-\d+[dhm]$/.test(val);
|
|
@@ -105,9 +106,7 @@ export async function executeTrafficFlow(args) {
|
|
|
105
106
|
let spaceId = args.space_id_or_name;
|
|
106
107
|
// If not an ID, search for the space
|
|
107
108
|
if (!spaceId.match(/^room_/)) {
|
|
108
|
-
|
|
109
|
-
console.error(`[traffic-flow] Searching for space: "${args.space_id_or_name}"`);
|
|
110
|
-
}
|
|
109
|
+
debug("traffic-flow", `Searching for space: "${args.space_id_or_name}"`);
|
|
111
110
|
const searchResults = await executeSearchAssets({
|
|
112
111
|
query: args.space_id_or_name,
|
|
113
112
|
asset_types: ["room"], // Traffic is room-level
|
|
@@ -117,15 +116,14 @@ export async function executeTrafficFlow(args) {
|
|
|
117
116
|
throw new Error(`No rooms found matching "${args.space_id_or_name}". Try a different search term.`);
|
|
118
117
|
}
|
|
119
118
|
spaceId = searchResults.matches[0].id;
|
|
120
|
-
|
|
121
|
-
console.error(`[traffic-flow] Using best match: ${searchResults.matches[0].name} (${spaceId})`);
|
|
122
|
-
}
|
|
119
|
+
debug("traffic-flow", `Using best match: ${searchResults.matches[0].name} (${spaceId})`);
|
|
123
120
|
}
|
|
124
121
|
// Query room details, topology for timezone, and all sensors
|
|
125
122
|
let room = null;
|
|
126
123
|
let roomPath = "";
|
|
127
124
|
let timezone;
|
|
128
125
|
let tzMetadata;
|
|
126
|
+
let timezoneFallback = false;
|
|
129
127
|
let trafficSensors = [];
|
|
130
128
|
try {
|
|
131
129
|
const [roomResult, topoResult, sensorsResult] = await Promise.all([
|
|
@@ -154,11 +152,9 @@ export async function executeTrafficFlow(args) {
|
|
|
154
152
|
const sites = topoResult.data?.sites?.data || [];
|
|
155
153
|
const buildings = sites.flatMap((s) => s.buildings || []);
|
|
156
154
|
const floors = buildings.flatMap((b) => b.floors || []);
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
timezone = roomTimezone;
|
|
155
|
+
const resolved = getTimezoneForAsset(spaceId, "room", floors, buildings, sites);
|
|
156
|
+
timezone = resolved.timezone;
|
|
157
|
+
timezoneFallback = resolved.isFallback;
|
|
162
158
|
tzMetadata = buildTimezoneMetadata(timezone);
|
|
163
159
|
// Analyze traffic sensors for this room
|
|
164
160
|
const allSensors = sensorsResult.data?.sensors?.data || [];
|
|
@@ -168,9 +164,7 @@ export async function executeTrafficFlow(args) {
|
|
|
168
164
|
if (trafficSensors.length === 0) {
|
|
169
165
|
throw new Error(`Room "${room.name}" does not have traffic-mode sensors. Try butlr_get_current_occupancy for occupancy data instead.`);
|
|
170
166
|
}
|
|
171
|
-
|
|
172
|
-
console.error(`[traffic-flow] Found ${trafficSensors.length} traffic sensors for room`);
|
|
173
|
-
}
|
|
167
|
+
debug("traffic-flow", `Found ${trafficSensors.length} traffic sensors for room`);
|
|
174
168
|
}
|
|
175
169
|
catch (error) {
|
|
176
170
|
rethrowIfGraphQLError(error);
|
|
@@ -181,7 +175,7 @@ export async function executeTrafficFlow(args) {
|
|
|
181
175
|
let start;
|
|
182
176
|
let stop = "now";
|
|
183
177
|
let periodDescription;
|
|
184
|
-
let usedUtcFallback =
|
|
178
|
+
let usedUtcFallback = timezoneFallback;
|
|
185
179
|
if (timeWindow === "custom") {
|
|
186
180
|
if (!args.custom_start) {
|
|
187
181
|
throw createValidationError("custom_start is required when time_window='custom'");
|
|
@@ -218,13 +212,9 @@ export async function executeTrafficFlow(args) {
|
|
|
218
212
|
periodDescription = usedUtcFallback
|
|
219
213
|
? "today (UTC fallback)"
|
|
220
214
|
: `today (${tzMetadata.timezone_abbr})`;
|
|
221
|
-
|
|
222
|
-
console.error(`[traffic-flow] Today starts at ${start} (midnight ${usedUtcFallback ? "UTC fallback" : tzMetadata.timezone_abbr})`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
if (process.env.DEBUG) {
|
|
226
|
-
console.error(`[traffic-flow] Querying traffic from ${start} to ${stop}`);
|
|
215
|
+
debug("traffic-flow", `Today starts at ${start} (midnight ${usedUtcFallback ? "UTC fallback" : tzMetadata.timezone_abbr})`);
|
|
227
216
|
}
|
|
217
|
+
debug("traffic-flow", `Querying traffic from ${start} to ${stop}`);
|
|
228
218
|
let trafficData = [];
|
|
229
219
|
try {
|
|
230
220
|
const response = await new ReportingRequestBuilder()
|
|
@@ -239,15 +229,11 @@ export async function executeTrafficFlow(args) {
|
|
|
239
229
|
throw new Error("Expected array response from traffic query");
|
|
240
230
|
}
|
|
241
231
|
trafficData = response.data;
|
|
242
|
-
|
|
243
|
-
console.error(`[traffic-flow] Received ${trafficData.length} data points from ${trafficSensors.length} sensors`);
|
|
244
|
-
}
|
|
232
|
+
debug("traffic-flow", `Received ${trafficData.length} data points from ${trafficSensors.length} sensors`);
|
|
245
233
|
}
|
|
246
234
|
catch (error) {
|
|
247
235
|
rethrowIfGraphQLError(error);
|
|
248
|
-
|
|
249
|
-
console.error(`[traffic-flow] Failed to get traffic data:`, error);
|
|
250
|
-
}
|
|
236
|
+
debug("traffic-flow", "Failed to get traffic data:", error);
|
|
251
237
|
const msg = error instanceof Error ? error.message : String(error);
|
|
252
238
|
throw new Error(`Failed to get traffic data for ${room.name}. ${msg}`);
|
|
253
239
|
}
|
|
@@ -356,14 +342,14 @@ export function registerTrafficFlow(server) {
|
|
|
356
342
|
readOnlyHint: true,
|
|
357
343
|
destructiveHint: false,
|
|
358
344
|
idempotentHint: false,
|
|
359
|
-
openWorldHint:
|
|
345
|
+
openWorldHint: false,
|
|
360
346
|
},
|
|
361
|
-
}, async (args) => {
|
|
347
|
+
}, withToolErrorHandling(async (args) => {
|
|
362
348
|
const validated = TrafficFlowArgsSchema.parse(args);
|
|
363
349
|
const result = await executeTrafficFlow(validated);
|
|
364
350
|
return {
|
|
365
351
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
366
352
|
};
|
|
367
|
-
});
|
|
353
|
+
}));
|
|
368
354
|
}
|
|
369
355
|
//# sourceMappingURL=butlr-traffic-flow.js.map
|