@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.
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/dist/cache/occupancy-cache.d.ts +72 -0
- package/dist/cache/occupancy-cache.d.ts.map +1 -0
- package/dist/cache/occupancy-cache.js +166 -0
- package/dist/cache/occupancy-cache.js.map +1 -0
- package/dist/cache/topology-cache.d.ts +36 -0
- package/dist/cache/topology-cache.d.ts.map +1 -0
- package/dist/cache/topology-cache.js +74 -0
- package/dist/cache/topology-cache.js.map +1 -0
- package/dist/clients/auth-client.d.ts +22 -0
- package/dist/clients/auth-client.d.ts.map +1 -0
- package/dist/clients/auth-client.js +82 -0
- package/dist/clients/auth-client.js.map +1 -0
- package/dist/clients/graphql-client.d.ts +6 -0
- package/dist/clients/graphql-client.d.ts.map +1 -0
- package/dist/clients/graphql-client.js +106 -0
- package/dist/clients/graphql-client.js.map +1 -0
- package/dist/clients/queries/topology.d.ts +36 -0
- package/dist/clients/queries/topology.d.ts.map +1 -0
- package/dist/clients/queries/topology.js +252 -0
- package/dist/clients/queries/topology.js.map +1 -0
- package/dist/clients/reporting-client.d.ts +191 -0
- package/dist/clients/reporting-client.d.ts.map +1 -0
- package/dist/clients/reporting-client.js +353 -0
- package/dist/clients/reporting-client.js.map +1 -0
- package/dist/clients/stats-client.d.ts +119 -0
- package/dist/clients/stats-client.d.ts.map +1 -0
- package/dist/clients/stats-client.js +238 -0
- package/dist/clients/stats-client.js.map +1 -0
- package/dist/clients/types.d.ts +215 -0
- package/dist/clients/types.d.ts.map +1 -0
- package/dist/clients/types.js +6 -0
- package/dist/clients/types.js.map +1 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -0
- package/dist/errors/mcp-errors.d.ts +63 -0
- package/dist/errors/mcp-errors.d.ts.map +1 -0
- package/dist/errors/mcp-errors.js +144 -0
- package/dist/errors/mcp-errors.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/butlr-available-rooms.d.ts +49 -0
- package/dist/tools/butlr-available-rooms.d.ts.map +1 -0
- package/dist/tools/butlr-available-rooms.js +492 -0
- package/dist/tools/butlr-available-rooms.js.map +1 -0
- package/dist/tools/butlr-fetch-entity-details.d.ts +42 -0
- package/dist/tools/butlr-fetch-entity-details.d.ts.map +1 -0
- package/dist/tools/butlr-fetch-entity-details.js +276 -0
- package/dist/tools/butlr-fetch-entity-details.js.map +1 -0
- package/dist/tools/butlr-get-asset-details.d.ts +51 -0
- package/dist/tools/butlr-get-asset-details.d.ts.map +1 -0
- package/dist/tools/butlr-get-asset-details.js +391 -0
- package/dist/tools/butlr-get-asset-details.js.map +1 -0
- package/dist/tools/butlr-get-current-occupancy.d.ts +21 -0
- package/dist/tools/butlr-get-current-occupancy.d.ts.map +1 -0
- package/dist/tools/butlr-get-current-occupancy.js +126 -0
- package/dist/tools/butlr-get-current-occupancy.js.map +1 -0
- package/dist/tools/butlr-get-occupancy-timeseries.d.ts +31 -0
- package/dist/tools/butlr-get-occupancy-timeseries.d.ts.map +1 -0
- package/dist/tools/butlr-get-occupancy-timeseries.js +145 -0
- package/dist/tools/butlr-get-occupancy-timeseries.js.map +1 -0
- package/dist/tools/butlr-hardware-snapshot.d.ts +55 -0
- package/dist/tools/butlr-hardware-snapshot.d.ts.map +1 -0
- package/dist/tools/butlr-hardware-snapshot.js +556 -0
- package/dist/tools/butlr-hardware-snapshot.js.map +1 -0
- package/dist/tools/butlr-list-topology.d.ts +27 -0
- package/dist/tools/butlr-list-topology.d.ts.map +1 -0
- package/dist/tools/butlr-list-topology.js +241 -0
- package/dist/tools/butlr-list-topology.js.map +1 -0
- package/dist/tools/butlr-search-assets.d.ts +53 -0
- package/dist/tools/butlr-search-assets.d.ts.map +1 -0
- package/dist/tools/butlr-search-assets.js +206 -0
- package/dist/tools/butlr-search-assets.js.map +1 -0
- package/dist/tools/butlr-space-busyness.d.ts +23 -0
- package/dist/tools/butlr-space-busyness.d.ts.map +1 -0
- package/dist/tools/butlr-space-busyness.js +304 -0
- package/dist/tools/butlr-space-busyness.js.map +1 -0
- package/dist/tools/butlr-traffic-flow.d.ts +39 -0
- package/dist/tools/butlr-traffic-flow.d.ts.map +1 -0
- package/dist/tools/butlr-traffic-flow.js +369 -0
- package/dist/tools/butlr-traffic-flow.js.map +1 -0
- package/dist/types/responses.d.ts +253 -0
- package/dist/types/responses.d.ts.map +1 -0
- package/dist/types/responses.js +8 -0
- package/dist/types/responses.js.map +1 -0
- package/dist/utils/asset-flattener.d.ts +50 -0
- package/dist/utils/asset-flattener.d.ts.map +1 -0
- package/dist/utils/asset-flattener.js +131 -0
- package/dist/utils/asset-flattener.js.map +1 -0
- package/dist/utils/asset-helpers.d.ts +8 -0
- package/dist/utils/asset-helpers.d.ts.map +1 -0
- package/dist/utils/asset-helpers.js +24 -0
- package/dist/utils/asset-helpers.js.map +1 -0
- package/dist/utils/field-validator.d.ts +29 -0
- package/dist/utils/field-validator.d.ts.map +1 -0
- package/dist/utils/field-validator.js +197 -0
- package/dist/utils/field-validator.js.map +1 -0
- package/dist/utils/fuzzy-match.d.ts +29 -0
- package/dist/utils/fuzzy-match.d.ts.map +1 -0
- package/dist/utils/fuzzy-match.js +70 -0
- package/dist/utils/fuzzy-match.js.map +1 -0
- package/dist/utils/graphql-helpers.d.ts +29 -0
- package/dist/utils/graphql-helpers.d.ts.map +1 -0
- package/dist/utils/graphql-helpers.js +43 -0
- package/dist/utils/graphql-helpers.js.map +1 -0
- package/dist/utils/natural-language.d.ts +73 -0
- package/dist/utils/natural-language.d.ts.map +1 -0
- package/dist/utils/natural-language.js +172 -0
- package/dist/utils/natural-language.js.map +1 -0
- package/dist/utils/occupancy-helpers.d.ts +63 -0
- package/dist/utils/occupancy-helpers.d.ts.map +1 -0
- package/dist/utils/occupancy-helpers.js +203 -0
- package/dist/utils/occupancy-helpers.js.map +1 -0
- package/dist/utils/path-builder.d.ts +10 -0
- package/dist/utils/path-builder.d.ts.map +1 -0
- package/dist/utils/path-builder.js +34 -0
- package/dist/utils/path-builder.js.map +1 -0
- package/dist/utils/time-range-validator.d.ts +13 -0
- package/dist/utils/time-range-validator.d.ts.map +1 -0
- package/dist/utils/time-range-validator.js +65 -0
- package/dist/utils/time-range-validator.js.map +1 -0
- package/dist/utils/timezone-helpers.d.ts +49 -0
- package/dist/utils/timezone-helpers.d.ts.map +1 -0
- package/dist/utils/timezone-helpers.js +209 -0
- package/dist/utils/timezone-helpers.js.map +1 -0
- package/dist/utils/tree-formatter.d.ts +23 -0
- package/dist/utils/tree-formatter.d.ts.map +1 -0
- package/dist/utils/tree-formatter.js +258 -0
- package/dist/utils/tree-formatter.js.map +1 -0
- package/package.json +93 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { apolloClient } from "../clients/graphql-client.js";
|
|
2
|
+
import { gql } from "@apollo/client";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { translateGraphQLError, formatMCPError, createValidationError, } from "../errors/mcp-errors.js";
|
|
5
|
+
import { detectAssetType } from "../utils/asset-helpers.js";
|
|
6
|
+
const assetIdSchema = z
|
|
7
|
+
.string()
|
|
8
|
+
.min(1, "Asset ID cannot be empty")
|
|
9
|
+
.refine((val) => {
|
|
10
|
+
const validPrefixes = ["site_", "building_", "space_", "floor_", "room_", "zone_"];
|
|
11
|
+
return validPrefixes.some((prefix) => val.startsWith(prefix));
|
|
12
|
+
}, {
|
|
13
|
+
message: "Asset ID must start with valid prefix: site_, building_, floor_, space_, room_, or zone_. For sensor/hive details, use butlr_search_assets or butlr_hardware_snapshot.",
|
|
14
|
+
});
|
|
15
|
+
/** Shared shape — used by both registerTool and full validation */
|
|
16
|
+
const getAssetDetailsInputShape = {
|
|
17
|
+
ids: z
|
|
18
|
+
.array(assetIdSchema)
|
|
19
|
+
.min(1, "ids array must contain at least 1 asset ID")
|
|
20
|
+
.max(50, "ids array cannot exceed 50 assets")
|
|
21
|
+
.describe("Asset IDs to fetch (e.g., ['room_123', 'floor_456'])"),
|
|
22
|
+
include_children: z.boolean().default(true).describe("Include child assets"),
|
|
23
|
+
include_devices: z.boolean().default(false).describe("Include sensors and hives"),
|
|
24
|
+
include_parent_context: z.boolean().default(true).describe("Include parent names for context"),
|
|
25
|
+
};
|
|
26
|
+
export const GetAssetDetailsArgsSchema = z
|
|
27
|
+
.object(getAssetDetailsInputShape)
|
|
28
|
+
.strict()
|
|
29
|
+
.refine((data) => {
|
|
30
|
+
const unique = new Set(data.ids);
|
|
31
|
+
return unique.size === data.ids.length;
|
|
32
|
+
}, {
|
|
33
|
+
message: "ids array contains duplicate asset IDs",
|
|
34
|
+
path: ["ids"],
|
|
35
|
+
});
|
|
36
|
+
const GET_ASSET_DETAILS_DESCRIPTION = "Get comprehensive details for specific assets by ID (sites, buildings, floors, rooms, zones). Automatically detects asset type from ID prefix and returns appropriate fields. Supports batch queries (multiple IDs), optional child/parent context, and device inclusion. Essential for configuration validation, integration development, and detailed asset inspection.\n\n" +
|
|
37
|
+
"Primary Users:\n" +
|
|
38
|
+
"- IT Manager: Verify sensor configurations (mode, model, online status), validate floor/building setups\n" +
|
|
39
|
+
"- Field Technician: Get sensor MAC addresses, hive serial numbers, and physical coordinates before site visits\n" +
|
|
40
|
+
"- Facilities Manager: Verify room capacities, areas, and metadata for space planning\n" +
|
|
41
|
+
"- Developer/Integrator: Fetch asset metadata for building workplace apps, dashboards, or integrations\n\n" +
|
|
42
|
+
"Example Queries:\n" +
|
|
43
|
+
'1. "Show me full details for Conference Room 401" (after finding ID via search)\n' +
|
|
44
|
+
'2. "Get sensor configuration for all sensors on Floor 3" (mode, model, MAC, online status)\n' +
|
|
45
|
+
'3. "Show me all rooms on Floor 6 with their capacities and areas"\n' +
|
|
46
|
+
'4. "Get building details including all floors and their room counts"\n' +
|
|
47
|
+
"5. \"Show me sensor MAC addresses for Room 'Café Barista' for field tech visit\"\n" +
|
|
48
|
+
'6. "Get hive serial numbers and online status for Building 2"\n' +
|
|
49
|
+
'7. "Show me site timezone and all buildings in the Chicago office"\n' +
|
|
50
|
+
'8. "Get room coordinates and rotation for floor plan mapping"\n\n' +
|
|
51
|
+
"When to Use:\n" +
|
|
52
|
+
"- Have asset IDs and need detailed configuration, metadata, or relationships\n" +
|
|
53
|
+
"- Validating sensor/hive assignments for troubleshooting (which hive is this sensor on?)\n" +
|
|
54
|
+
"- Need room capacities, areas, or coordinates for space planning or floor plan integrations\n" +
|
|
55
|
+
"- Preparing for field technician site visit (need device identifiers: MACs, serials)\n" +
|
|
56
|
+
"- Building integrations and need to fetch parent context (room → floor → building → site)\n\n" +
|
|
57
|
+
"When NOT to Use:\n" +
|
|
58
|
+
"- Don't have asset IDs yet → use butlr_search_assets first to find IDs by name\n" +
|
|
59
|
+
"- Need real-time occupancy or sensor data → use occupancy/traffic tools instead\n" +
|
|
60
|
+
"- Want to browse organizational hierarchy → use butlr_list_topology for tree view\n" +
|
|
61
|
+
"- Need to update/configure assets → this is read-only; use Butlr Dashboard for changes\n\n" +
|
|
62
|
+
"Options: include_children (default true), include_devices (default false), include_parent_context (default true)\n\n" +
|
|
63
|
+
"Batch Query: Supports multiple IDs in single call - mixed asset types supported\n\n" +
|
|
64
|
+
"See Also: butlr_search_assets, butlr_list_topology, butlr_fetch_entity_details, butlr_hardware_snapshot";
|
|
65
|
+
/**
|
|
66
|
+
* Build GraphQL query based on asset type and options
|
|
67
|
+
*/
|
|
68
|
+
function buildQuery(type, includeChildren, includeDevices, includeParentContext) {
|
|
69
|
+
switch (type) {
|
|
70
|
+
case "site":
|
|
71
|
+
return gql `
|
|
72
|
+
query GetSiteDetails($id: ID!) {
|
|
73
|
+
site(id: $id) {
|
|
74
|
+
id
|
|
75
|
+
name
|
|
76
|
+
timezone
|
|
77
|
+
siteNumber
|
|
78
|
+
customID
|
|
79
|
+
${includeChildren
|
|
80
|
+
? `
|
|
81
|
+
buildings {
|
|
82
|
+
id
|
|
83
|
+
name
|
|
84
|
+
building_number
|
|
85
|
+
capacity { max mid }
|
|
86
|
+
${includeChildren
|
|
87
|
+
? `
|
|
88
|
+
floors {
|
|
89
|
+
id
|
|
90
|
+
name
|
|
91
|
+
floorNumber
|
|
92
|
+
}
|
|
93
|
+
`
|
|
94
|
+
: ""}
|
|
95
|
+
}
|
|
96
|
+
`
|
|
97
|
+
: ""}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
`;
|
|
101
|
+
case "building":
|
|
102
|
+
return gql `
|
|
103
|
+
query GetBuildingDetails($id: ID!) {
|
|
104
|
+
building(id: $id) {
|
|
105
|
+
id
|
|
106
|
+
name
|
|
107
|
+
building_number
|
|
108
|
+
customID
|
|
109
|
+
capacity { max mid }
|
|
110
|
+
address { lines country }
|
|
111
|
+
${includeParentContext
|
|
112
|
+
? `
|
|
113
|
+
site {
|
|
114
|
+
id
|
|
115
|
+
name
|
|
116
|
+
timezone
|
|
117
|
+
}
|
|
118
|
+
`
|
|
119
|
+
: ""}
|
|
120
|
+
${includeChildren
|
|
121
|
+
? `
|
|
122
|
+
floors {
|
|
123
|
+
id
|
|
124
|
+
name
|
|
125
|
+
floorNumber
|
|
126
|
+
timezone
|
|
127
|
+
capacity { max mid }
|
|
128
|
+
}
|
|
129
|
+
`
|
|
130
|
+
: ""}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
`;
|
|
134
|
+
case "floor":
|
|
135
|
+
return gql `
|
|
136
|
+
query GetFloorDetails($id: ID!) {
|
|
137
|
+
floor(id: $id) {
|
|
138
|
+
id
|
|
139
|
+
name
|
|
140
|
+
floorNumber
|
|
141
|
+
timezone
|
|
142
|
+
installation_date
|
|
143
|
+
installation_status
|
|
144
|
+
customID
|
|
145
|
+
capacity { max mid }
|
|
146
|
+
area { value unit }
|
|
147
|
+
${includeParentContext
|
|
148
|
+
? `
|
|
149
|
+
building {
|
|
150
|
+
id
|
|
151
|
+
name
|
|
152
|
+
building_number
|
|
153
|
+
site {
|
|
154
|
+
id
|
|
155
|
+
name
|
|
156
|
+
timezone
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
`
|
|
160
|
+
: ""}
|
|
161
|
+
${includeChildren
|
|
162
|
+
? `
|
|
163
|
+
rooms {
|
|
164
|
+
id
|
|
165
|
+
name
|
|
166
|
+
roomType
|
|
167
|
+
customID
|
|
168
|
+
capacity { max mid }
|
|
169
|
+
coordinates
|
|
170
|
+
}
|
|
171
|
+
zones {
|
|
172
|
+
id
|
|
173
|
+
name
|
|
174
|
+
roomID
|
|
175
|
+
customID
|
|
176
|
+
coordinates
|
|
177
|
+
}
|
|
178
|
+
`
|
|
179
|
+
: ""}
|
|
180
|
+
${includeDevices
|
|
181
|
+
? `
|
|
182
|
+
sensors {
|
|
183
|
+
id
|
|
184
|
+
name
|
|
185
|
+
mac_address
|
|
186
|
+
mode
|
|
187
|
+
model
|
|
188
|
+
roomID
|
|
189
|
+
hive_serial
|
|
190
|
+
is_online
|
|
191
|
+
}
|
|
192
|
+
hives {
|
|
193
|
+
id
|
|
194
|
+
name
|
|
195
|
+
serialNumber
|
|
196
|
+
roomID
|
|
197
|
+
isOnline
|
|
198
|
+
coordinates
|
|
199
|
+
}
|
|
200
|
+
`
|
|
201
|
+
: ""}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
`;
|
|
205
|
+
case "room":
|
|
206
|
+
return gql `
|
|
207
|
+
query GetRoomDetails($id: ID!) {
|
|
208
|
+
room(id: $id) {
|
|
209
|
+
id
|
|
210
|
+
name
|
|
211
|
+
roomType
|
|
212
|
+
customID
|
|
213
|
+
capacity { max mid }
|
|
214
|
+
area { value unit }
|
|
215
|
+
coordinates
|
|
216
|
+
rotation
|
|
217
|
+
note
|
|
218
|
+
${includeParentContext
|
|
219
|
+
? `
|
|
220
|
+
floor {
|
|
221
|
+
id
|
|
222
|
+
name
|
|
223
|
+
floorNumber
|
|
224
|
+
building {
|
|
225
|
+
id
|
|
226
|
+
name
|
|
227
|
+
site {
|
|
228
|
+
id
|
|
229
|
+
name
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
`
|
|
234
|
+
: ""}
|
|
235
|
+
${includeDevices
|
|
236
|
+
? `
|
|
237
|
+
sensors {
|
|
238
|
+
id
|
|
239
|
+
name
|
|
240
|
+
mac_address
|
|
241
|
+
mode
|
|
242
|
+
model
|
|
243
|
+
is_online
|
|
244
|
+
hive_serial
|
|
245
|
+
}
|
|
246
|
+
`
|
|
247
|
+
: ""}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
`;
|
|
251
|
+
case "zone":
|
|
252
|
+
return gql `
|
|
253
|
+
query GetZoneDetails($id: ID!) {
|
|
254
|
+
zone(id: $id) {
|
|
255
|
+
id
|
|
256
|
+
name
|
|
257
|
+
roomID
|
|
258
|
+
customID
|
|
259
|
+
capacity { max mid }
|
|
260
|
+
area { value unit }
|
|
261
|
+
coordinates
|
|
262
|
+
rotation
|
|
263
|
+
note
|
|
264
|
+
${includeDevices
|
|
265
|
+
? `
|
|
266
|
+
sensors {
|
|
267
|
+
id
|
|
268
|
+
name
|
|
269
|
+
mac_address
|
|
270
|
+
is_online
|
|
271
|
+
}
|
|
272
|
+
`
|
|
273
|
+
: ""}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
`;
|
|
277
|
+
default:
|
|
278
|
+
throw createValidationError(`Unsupported asset type: ${type}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Execute get_asset_details tool
|
|
283
|
+
*/
|
|
284
|
+
export async function executeGetAssetDetails(args) {
|
|
285
|
+
const includeChildren = args.include_children !== false; // Default true
|
|
286
|
+
const includeDevices = args.include_devices === true; // Default false
|
|
287
|
+
const includeParentContext = args.include_parent_context !== false; // Default true
|
|
288
|
+
if (process.env.DEBUG) {
|
|
289
|
+
console.error(`[get-asset-details] Fetching details for ${args.ids.length} asset(s): ${args.ids.join(", ")}`);
|
|
290
|
+
}
|
|
291
|
+
const results = [];
|
|
292
|
+
// Group IDs by type
|
|
293
|
+
const assetsByType = {};
|
|
294
|
+
for (const id of args.ids) {
|
|
295
|
+
const type = detectAssetType(id);
|
|
296
|
+
if (type === "unknown") {
|
|
297
|
+
if (process.env.DEBUG) {
|
|
298
|
+
console.error(`[get-asset-details] Warning: Unknown asset type for ID: ${id}`);
|
|
299
|
+
}
|
|
300
|
+
results.push({
|
|
301
|
+
id,
|
|
302
|
+
_type: "unknown",
|
|
303
|
+
error: `Unknown asset type for ID: ${id}. Expected prefix: site_, building_, floor_, room_, zone_`,
|
|
304
|
+
});
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (!assetsByType[type]) {
|
|
308
|
+
assetsByType[type] = [];
|
|
309
|
+
}
|
|
310
|
+
assetsByType[type].push(id);
|
|
311
|
+
}
|
|
312
|
+
// Fetch all assets in parallel (grouped by type for query selection)
|
|
313
|
+
const fetchPromises = [];
|
|
314
|
+
for (const [type, ids] of Object.entries(assetsByType)) {
|
|
315
|
+
const query = buildQuery(type, includeChildren, includeDevices, includeParentContext);
|
|
316
|
+
for (const id of ids) {
|
|
317
|
+
fetchPromises.push({
|
|
318
|
+
id,
|
|
319
|
+
type,
|
|
320
|
+
promise: apolloClient.query({ query, variables: { id }, fetchPolicy: "network-only" }),
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const settled = await Promise.allSettled(fetchPromises.map((f) => f.promise));
|
|
325
|
+
for (let i = 0; i < settled.length; i++) {
|
|
326
|
+
const { id, type } = fetchPromises[i];
|
|
327
|
+
const outcome = settled[i];
|
|
328
|
+
if (outcome.status === "fulfilled") {
|
|
329
|
+
const { data, error } = outcome.value;
|
|
330
|
+
if (error) {
|
|
331
|
+
const mcpError = translateGraphQLError(error);
|
|
332
|
+
results.push({ id, error: formatMCPError(mcpError), _type: type });
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
const asset = data[type];
|
|
336
|
+
if (asset && typeof asset === "object") {
|
|
337
|
+
results.push({ ...asset, _type: type });
|
|
338
|
+
}
|
|
339
|
+
else if (process.env.DEBUG) {
|
|
340
|
+
console.error(`[get-asset-details] Asset not found: ${id}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
const err = outcome.reason;
|
|
345
|
+
if (err && (err.graphQLErrors || err.networkError)) {
|
|
346
|
+
const mcpError = translateGraphQLError(err);
|
|
347
|
+
if (process.env.DEBUG) {
|
|
348
|
+
console.error(`[get-asset-details] Error fetching ${id}:`, formatMCPError(mcpError));
|
|
349
|
+
}
|
|
350
|
+
results.push({ id, error: formatMCPError(mcpError), _type: type });
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
throw err;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
assets: results,
|
|
359
|
+
total_count: results.length,
|
|
360
|
+
requested_count: args.ids.length,
|
|
361
|
+
options: {
|
|
362
|
+
include_children: includeChildren,
|
|
363
|
+
include_devices: includeDevices,
|
|
364
|
+
include_parent_context: includeParentContext,
|
|
365
|
+
},
|
|
366
|
+
timestamp: new Date().toISOString(),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Register butlr_get_asset_details with an McpServer instance
|
|
371
|
+
*/
|
|
372
|
+
export function registerGetAssetDetails(server) {
|
|
373
|
+
server.registerTool("butlr_get_asset_details", {
|
|
374
|
+
title: "Get Butlr Asset Details",
|
|
375
|
+
description: GET_ASSET_DETAILS_DESCRIPTION,
|
|
376
|
+
inputSchema: getAssetDetailsInputShape,
|
|
377
|
+
annotations: {
|
|
378
|
+
readOnlyHint: true,
|
|
379
|
+
destructiveHint: false,
|
|
380
|
+
idempotentHint: true,
|
|
381
|
+
openWorldHint: true,
|
|
382
|
+
},
|
|
383
|
+
}, async (args) => {
|
|
384
|
+
const validated = GetAssetDetailsArgsSchema.parse(args);
|
|
385
|
+
const result = await executeGetAssetDetails(validated);
|
|
386
|
+
return {
|
|
387
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
388
|
+
};
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
//# sourceMappingURL=butlr-get-asset-details.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"butlr-get-asset-details.js","sourceRoot":"","sources":["../../src/tools/butlr-get-asset-details.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { CurrentOccupancyResponse } from "../types/responses.js";
|
|
4
|
+
export declare const GetCurrentOccupancyArgsSchema: z.ZodObject<{
|
|
5
|
+
asset_ids: z.ZodArray<z.ZodString, "many">;
|
|
6
|
+
}, "strict", z.ZodTypeAny, {
|
|
7
|
+
asset_ids: string[];
|
|
8
|
+
}, {
|
|
9
|
+
asset_ids: string[];
|
|
10
|
+
}>;
|
|
11
|
+
/** Input arguments — inferred from Zod schema */
|
|
12
|
+
export type GetCurrentOccupancyArgs = z.output<typeof GetCurrentOccupancyArgsSchema>;
|
|
13
|
+
/**
|
|
14
|
+
* Execute unified current occupancy tool
|
|
15
|
+
*/
|
|
16
|
+
export declare function executeGetCurrentOccupancy(args: GetCurrentOccupancyArgs): Promise<CurrentOccupancyResponse>;
|
|
17
|
+
/**
|
|
18
|
+
* Register butlr_get_current_occupancy with an McpServer instance
|
|
19
|
+
*/
|
|
20
|
+
export declare function registerGetCurrentOccupancy(server: McpServer): void;
|
|
21
|
+
//# sourceMappingURL=butlr-get-current-occupancy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"butlr-get-current-occupancy.d.ts","sourceRoot":"","sources":["../../src/tools/butlr-get-current-occupancy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAUxB,OAAO,KAAK,EACV,wBAAwB,EAGzB,MAAM,uBAAuB,CAAC;AAY/B,eAAO,MAAM,6BAA6B;;;;;;EAAmD,CAAC;AAE9F,iDAAiD;AACjD,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAErF;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,uBAAuB,GAC5B,OAAO,CAAC,wBAAwB,CAAC,CA2GnC;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsBnE"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { ReportingRequestBuilder } from "../clients/reporting-client.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { fetchTopologyAndSensors, resolveAssetContext, getPresenceMeasurement, getTrafficMeasurement, getPresenceCoverageNote, getTrafficCoverageNote, buildRecommendation, } from "../utils/occupancy-helpers.js";
|
|
4
|
+
const GET_CURRENT_OCCUPANCY_DESCRIPTION = "Get current occupancy for floors, rooms, or zones (last 5 minutes median). Automatically queries both traffic and presence measurements, " +
|
|
5
|
+
"analyzes which are available based on sensor configuration, and returns structured data with timezone context. " +
|
|
6
|
+
"Single tool call provides complete current occupancy picture.";
|
|
7
|
+
/** Shared shape — used by both registerTool (SDK schema) and full validation */
|
|
8
|
+
const getCurrentOccupancyInputShape = {
|
|
9
|
+
asset_ids: z.array(z.string()).min(1).max(50).describe("Floor, room, or zone IDs"),
|
|
10
|
+
};
|
|
11
|
+
export const GetCurrentOccupancyArgsSchema = z.object(getCurrentOccupancyInputShape).strict();
|
|
12
|
+
/**
|
|
13
|
+
* Execute unified current occupancy tool
|
|
14
|
+
*/
|
|
15
|
+
export async function executeGetCurrentOccupancy(args) {
|
|
16
|
+
if (process.env.DEBUG) {
|
|
17
|
+
console.error(`[butlr-get-current-occupancy] Querying ${args.asset_ids.length} assets`);
|
|
18
|
+
}
|
|
19
|
+
// Fetch topology and sensors using shared helper
|
|
20
|
+
const ctx = await fetchTopologyAndSensors();
|
|
21
|
+
// Process each asset
|
|
22
|
+
const assets = [];
|
|
23
|
+
const now = new Date();
|
|
24
|
+
const fiveMinAgo = new Date(now.getTime() - 5 * 60 * 1000);
|
|
25
|
+
for (const assetId of args.asset_ids) {
|
|
26
|
+
const asset = resolveAssetContext(assetId, ctx);
|
|
27
|
+
// ---- Presence ----
|
|
28
|
+
const presenceData = {
|
|
29
|
+
available: asset.presenceSensors.length > 0,
|
|
30
|
+
sensor_count: asset.presenceSensors.length,
|
|
31
|
+
coverage_note: getPresenceCoverageNote(asset.assetType, asset.presenceSensors.length),
|
|
32
|
+
};
|
|
33
|
+
let presenceHasData = false;
|
|
34
|
+
if (asset.presenceSensors.length > 0) {
|
|
35
|
+
const measurement = getPresenceMeasurement(asset.assetType);
|
|
36
|
+
try {
|
|
37
|
+
const response = await new ReportingRequestBuilder()
|
|
38
|
+
.assets(asset.assetType, [assetId])
|
|
39
|
+
.measurements([measurement])
|
|
40
|
+
.timeRange(fiveMinAgo.toISOString(), now.toISOString())
|
|
41
|
+
.window("1m", "median")
|
|
42
|
+
.execute();
|
|
43
|
+
if (response.data && Array.isArray(response.data) && response.data.length > 0) {
|
|
44
|
+
const latest = response.data[response.data.length - 1];
|
|
45
|
+
presenceData.current_occupancy = latest.value;
|
|
46
|
+
presenceData.timestamp = new Date(latest.time).toISOString();
|
|
47
|
+
presenceHasData = true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error(`[current-occupancy] Presence query failed:`, error);
|
|
52
|
+
presenceData.warning =
|
|
53
|
+
"Failed to retrieve current presence data. Occupancy value may be missing.";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// ---- Traffic ----
|
|
57
|
+
const trafficData = {
|
|
58
|
+
available: asset.trafficSensors.length > 0,
|
|
59
|
+
entrance_sensor_count: asset.assetType === "floor" ? asset.trafficSensors.length : undefined,
|
|
60
|
+
sensor_count: asset.assetType === "room" ? asset.trafficSensors.length : undefined,
|
|
61
|
+
coverage_note: getTrafficCoverageNote(asset.assetType, asset.trafficSensors.length),
|
|
62
|
+
};
|
|
63
|
+
let trafficHasData = false;
|
|
64
|
+
if (asset.trafficSensors.length > 0) {
|
|
65
|
+
const measurement = getTrafficMeasurement(asset.assetType);
|
|
66
|
+
try {
|
|
67
|
+
const response = await new ReportingRequestBuilder()
|
|
68
|
+
.assets(asset.assetType, [assetId])
|
|
69
|
+
.measurements([measurement])
|
|
70
|
+
.timeRange(fiveMinAgo.toISOString(), now.toISOString())
|
|
71
|
+
.window("1m", "median")
|
|
72
|
+
.execute();
|
|
73
|
+
if (response.data && Array.isArray(response.data) && response.data.length > 0) {
|
|
74
|
+
const latest = response.data[response.data.length - 1];
|
|
75
|
+
trafficData.current_occupancy = latest.value;
|
|
76
|
+
trafficData.timestamp = new Date(latest.time).toISOString();
|
|
77
|
+
trafficHasData = true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error(`[current-occupancy] Traffic query failed:`, error);
|
|
82
|
+
trafficData.warning =
|
|
83
|
+
"Failed to retrieve current traffic data. Occupancy value may be missing.";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// ---- Recommendation (checks data success, not just sensor availability) ----
|
|
87
|
+
const recommendation = buildRecommendation(presenceData, trafficData, presenceHasData, trafficHasData);
|
|
88
|
+
assets.push({
|
|
89
|
+
asset_id: assetId,
|
|
90
|
+
asset_type: asset.assetType,
|
|
91
|
+
asset_name: asset.assetName,
|
|
92
|
+
...asset.tzMetadata,
|
|
93
|
+
presence: presenceData,
|
|
94
|
+
traffic: trafficData,
|
|
95
|
+
...recommendation,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
assets,
|
|
100
|
+
timestamp: now.toISOString(),
|
|
101
|
+
timezone_note: "All timestamps are UTC (ISO-8601). Use site_timezone for local conversion.",
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Register butlr_get_current_occupancy with an McpServer instance
|
|
106
|
+
*/
|
|
107
|
+
export function registerGetCurrentOccupancy(server) {
|
|
108
|
+
server.registerTool("butlr_get_current_occupancy", {
|
|
109
|
+
title: "Get Current Occupancy",
|
|
110
|
+
description: GET_CURRENT_OCCUPANCY_DESCRIPTION,
|
|
111
|
+
inputSchema: getCurrentOccupancyInputShape,
|
|
112
|
+
annotations: {
|
|
113
|
+
readOnlyHint: true,
|
|
114
|
+
destructiveHint: false,
|
|
115
|
+
idempotentHint: false,
|
|
116
|
+
openWorldHint: true,
|
|
117
|
+
},
|
|
118
|
+
}, async (args) => {
|
|
119
|
+
const validated = GetCurrentOccupancyArgsSchema.parse(args);
|
|
120
|
+
const result = await executeGetCurrentOccupancy(validated);
|
|
121
|
+
return {
|
|
122
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=butlr-get-current-occupancy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"butlr-get-current-occupancy.js","sourceRoot":"","sources":["../../src/tools/butlr-get-current-occupancy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { OccupancyTimeseriesResponse } from "../types/responses.js";
|
|
4
|
+
export declare const GetOccupancyTimeseriesArgsSchema: z.ZodObject<{
|
|
5
|
+
asset_ids: z.ZodArray<z.ZodString, "many">;
|
|
6
|
+
interval: z.ZodEnum<["1m", "1h", "1d"]>;
|
|
7
|
+
start: z.ZodString;
|
|
8
|
+
stop: z.ZodString;
|
|
9
|
+
}, "strict", z.ZodTypeAny, {
|
|
10
|
+
start: string;
|
|
11
|
+
stop: string;
|
|
12
|
+
asset_ids: string[];
|
|
13
|
+
interval: "1h" | "1m" | "1d";
|
|
14
|
+
}, {
|
|
15
|
+
start: string;
|
|
16
|
+
stop: string;
|
|
17
|
+
asset_ids: string[];
|
|
18
|
+
interval: "1h" | "1m" | "1d";
|
|
19
|
+
}>;
|
|
20
|
+
/** Inferred args type — no manual interface needed */
|
|
21
|
+
type GetOccupancyTimeseriesArgs = z.output<typeof GetOccupancyTimeseriesArgsSchema>;
|
|
22
|
+
/**
|
|
23
|
+
* Execute unified occupancy timeseries tool
|
|
24
|
+
*/
|
|
25
|
+
export declare function executeGetOccupancyTimeseries(args: GetOccupancyTimeseriesArgs): Promise<OccupancyTimeseriesResponse>;
|
|
26
|
+
/**
|
|
27
|
+
* Register butlr_get_occupancy_timeseries with an McpServer instance
|
|
28
|
+
*/
|
|
29
|
+
export declare function registerGetOccupancyTimeseries(server: McpServer): void;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=butlr-get-occupancy-timeseries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"butlr-get-occupancy-timeseries.d.ts","sourceRoot":"","sources":["../../src/tools/butlr-get-occupancy-timeseries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,OAAO,KAAK,EACV,2BAA2B,EAI5B,MAAM,uBAAuB,CAAC;AAoB/B,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;EAAsD,CAAC;AAEpG,sDAAsD;AACtD,KAAK,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAgCpF;;GAEG;AACH,wBAAsB,6BAA6B,CACjD,IAAI,EAAE,0BAA0B,GAC/B,OAAO,CAAC,2BAA2B,CAAC,CAwGtC;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA2BtE"}
|