@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,304 @@
|
|
|
1
|
+
import { apolloClient } from "../clients/graphql-client.js";
|
|
2
|
+
import { gql } from "@apollo/client";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { getCurrentOccupancy } from "../clients/reporting-client.js";
|
|
5
|
+
import { getSingleAssetStats } from "../clients/stats-client.js";
|
|
6
|
+
import { executeSearchAssets } from "./butlr-search-assets.js";
|
|
7
|
+
import { buildBusynessSummary, getOccupancyLabel, getTrendLabel, getBusinessRecommendation, formatDayAndTime, } from "../utils/natural-language.js";
|
|
8
|
+
import { getCachedOccupancy, setCachedOccupancy } from "../cache/occupancy-cache.js";
|
|
9
|
+
import { rethrowIfGraphQLError } from "../utils/graphql-helpers.js";
|
|
10
|
+
/** Shared shape -- used by both registerTool (SDK schema) and full validation */
|
|
11
|
+
const spaceBusynessInputShape = {
|
|
12
|
+
space_id_or_name: z
|
|
13
|
+
.string()
|
|
14
|
+
.min(1, "space_id_or_name cannot be empty")
|
|
15
|
+
.max(200, "space_id_or_name too long (max: 200 chars)")
|
|
16
|
+
.trim()
|
|
17
|
+
.describe("Space ID (room_123) or search term ('café', 'lobby')"),
|
|
18
|
+
include_trend: z
|
|
19
|
+
.boolean()
|
|
20
|
+
.default(true)
|
|
21
|
+
.describe("Compare to typical occupancy for this day/time"),
|
|
22
|
+
};
|
|
23
|
+
export const SpaceBusynessArgsSchema = z.object(spaceBusynessInputShape).strict();
|
|
24
|
+
const SPACE_BUSYNESS_DESCRIPTION = "Get current occupancy busyness for any room or zone with qualitative labels (quiet/moderate/busy) and trend comparison vs. typical usage. Designed for employee experience apps, wayfinding kiosks, and workplace analytics. Helps employees make real-time decisions about where to work and helps workplace teams understand space demand patterns.\n\n" +
|
|
25
|
+
"Primary Users:\n" +
|
|
26
|
+
"- Workplace Manager: Monitor amenity usage (cafés, focus rooms, lounges), optimize space allocation, improve employee experience\n" +
|
|
27
|
+
"- Employee/End User: Decide whether to visit café, find quiet focus areas, avoid crowded collaboration spaces\n" +
|
|
28
|
+
"- Facilities Coordinator: Validate HVAC/cleaning schedules match actual occupancy patterns\n" +
|
|
29
|
+
"- Portfolio Manager: Understand space demand during different times for densification analysis\n\n" +
|
|
30
|
+
"Example Queries:\n" +
|
|
31
|
+
'1. "How busy is the café right now? Should I go?"\n' +
|
|
32
|
+
'2. "Is the 3rd floor collaboration zone crowded?"\n' +
|
|
33
|
+
'3. "How full is the main lobby?"\n' +
|
|
34
|
+
'4. "Is the fitness center busy compared to usual?"\n' +
|
|
35
|
+
'5. "Check occupancy for Conference Room 401"\n' +
|
|
36
|
+
'6. "Is the outdoor terrace busier than normal for Friday afternoon?"\n' +
|
|
37
|
+
'7. "How crowded is the co-working area on Floor 2?"\n' +
|
|
38
|
+
'8. "Is the cafeteria quiet enough for a phone call right now?"\n\n' +
|
|
39
|
+
"When to Use:\n" +
|
|
40
|
+
"- Real-time 'should I go there now?' recommendations for employees\n" +
|
|
41
|
+
"- Understand if a space is busier or quieter than typical for this day/time\n" +
|
|
42
|
+
"- Troubleshooting complaints about crowded amenities (café, gym, lounge)\n" +
|
|
43
|
+
"- Validating right-sizing decisions ('Is this space too large/small for actual demand?')\n" +
|
|
44
|
+
"- Optimizing cleaning or HVAC schedules based on actual occupancy\n\n" +
|
|
45
|
+
"When NOT to Use:\n" +
|
|
46
|
+
"- Precise occupancy counts for capacity planning → use butlr_get_current_occupancy for exact numbers\n" +
|
|
47
|
+
"- Historical utilization patterns → use butlr_get_occupancy_timeseries for time series data\n" +
|
|
48
|
+
"- Entry/exit traffic counts (lobby, building entrance) → use butlr_traffic_flow instead\n" +
|
|
49
|
+
"- Searching for a space first → use butlr_search_assets to find room/zone ID by name\n\n" +
|
|
50
|
+
"Qualitative Labels: Quiet (<30% utilized), Moderate (30-70%), Busy (>=70%)\n\n" +
|
|
51
|
+
"See Also: butlr_get_current_occupancy, butlr_traffic_flow, butlr_search_assets, butlr_get_occupancy_timeseries";
|
|
52
|
+
const GET_ROOM = gql `
|
|
53
|
+
query GetRoom($roomId: ID!) {
|
|
54
|
+
room(id: $roomId) {
|
|
55
|
+
id
|
|
56
|
+
name
|
|
57
|
+
floorID
|
|
58
|
+
roomType
|
|
59
|
+
capacity {
|
|
60
|
+
max
|
|
61
|
+
mid
|
|
62
|
+
}
|
|
63
|
+
floor {
|
|
64
|
+
id
|
|
65
|
+
name
|
|
66
|
+
building_id
|
|
67
|
+
building {
|
|
68
|
+
id
|
|
69
|
+
name
|
|
70
|
+
site_id
|
|
71
|
+
site {
|
|
72
|
+
timezone
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
const GET_ZONE = gql `
|
|
80
|
+
query GetZone($zoneId: ID!) {
|
|
81
|
+
zone(id: $zoneId) {
|
|
82
|
+
id
|
|
83
|
+
name
|
|
84
|
+
floorID
|
|
85
|
+
capacity {
|
|
86
|
+
max
|
|
87
|
+
mid
|
|
88
|
+
}
|
|
89
|
+
floor {
|
|
90
|
+
id
|
|
91
|
+
name
|
|
92
|
+
building_id
|
|
93
|
+
building {
|
|
94
|
+
id
|
|
95
|
+
name
|
|
96
|
+
site_id
|
|
97
|
+
site {
|
|
98
|
+
timezone
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
105
|
+
/**
|
|
106
|
+
* Execute space busyness tool
|
|
107
|
+
*/
|
|
108
|
+
export async function executeSpaceBusyness(args) {
|
|
109
|
+
let spaceId = args.space_id_or_name;
|
|
110
|
+
let spaceType = "room";
|
|
111
|
+
// If not an ID, search for the space
|
|
112
|
+
if (!spaceId.match(/^(room|zone)_/)) {
|
|
113
|
+
if (process.env.DEBUG) {
|
|
114
|
+
console.error(`[space-busyness] Searching for space: "${args.space_id_or_name}"`);
|
|
115
|
+
}
|
|
116
|
+
const searchResults = await executeSearchAssets({
|
|
117
|
+
query: args.space_id_or_name,
|
|
118
|
+
asset_types: ["room", "zone"],
|
|
119
|
+
max_results: 5,
|
|
120
|
+
});
|
|
121
|
+
if (searchResults.matches.length === 0) {
|
|
122
|
+
throw new Error(`No spaces found matching "${args.space_id_or_name}". Try a different search term.`);
|
|
123
|
+
}
|
|
124
|
+
// Use best match
|
|
125
|
+
const bestMatch = searchResults.matches[0];
|
|
126
|
+
spaceId = bestMatch.id;
|
|
127
|
+
spaceType = bestMatch.type;
|
|
128
|
+
if (process.env.DEBUG) {
|
|
129
|
+
console.error(`[space-busyness] Using best match: ${bestMatch.name} (${spaceId})`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// Determine type from ID prefix
|
|
134
|
+
spaceType = spaceId.startsWith("room_") ? "room" : "zone";
|
|
135
|
+
}
|
|
136
|
+
// Query space details
|
|
137
|
+
let space = null;
|
|
138
|
+
let spacePath = "";
|
|
139
|
+
let spaceTimezone;
|
|
140
|
+
try {
|
|
141
|
+
if (spaceType === "room") {
|
|
142
|
+
const result = await apolloClient.query({
|
|
143
|
+
query: GET_ROOM,
|
|
144
|
+
variables: { roomId: spaceId },
|
|
145
|
+
fetchPolicy: "network-only",
|
|
146
|
+
});
|
|
147
|
+
if (!result.data?.room) {
|
|
148
|
+
throw new Error(`Room ${spaceId} not found`);
|
|
149
|
+
}
|
|
150
|
+
space = result.data.room;
|
|
151
|
+
const floor = space.floor;
|
|
152
|
+
const building = floor?.building;
|
|
153
|
+
spacePath = building ? `${building.name} > ${floor.name} > ${space.name}` : space.name;
|
|
154
|
+
spaceTimezone = building?.site?.timezone;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
const result = await apolloClient.query({
|
|
158
|
+
query: GET_ZONE,
|
|
159
|
+
variables: { zoneId: spaceId },
|
|
160
|
+
fetchPolicy: "network-only",
|
|
161
|
+
});
|
|
162
|
+
if (!result.data?.zone) {
|
|
163
|
+
throw new Error(`Zone ${spaceId} not found`);
|
|
164
|
+
}
|
|
165
|
+
space = result.data.zone;
|
|
166
|
+
const floor = space.floor;
|
|
167
|
+
const building = floor?.building;
|
|
168
|
+
spacePath = building ? `${building.name} > ${floor.name} > ${space.name}` : space.name;
|
|
169
|
+
spaceTimezone = building?.site?.timezone;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
rethrowIfGraphQLError(error);
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
// Get current occupancy (check cache first)
|
|
177
|
+
const now = new Date();
|
|
178
|
+
let currentOccupancy = 0;
|
|
179
|
+
const cached = getCachedOccupancy(spaceId, now);
|
|
180
|
+
if (cached) {
|
|
181
|
+
currentOccupancy = cached.occupancy;
|
|
182
|
+
if (process.env.DEBUG) {
|
|
183
|
+
console.error(`[space-busyness] Using cached occupancy: ${currentOccupancy}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
try {
|
|
188
|
+
const occupancyData = await getCurrentOccupancy(spaceType, [spaceId]);
|
|
189
|
+
if (occupancyData.length > 0) {
|
|
190
|
+
currentOccupancy = occupancyData[0].value;
|
|
191
|
+
setCachedOccupancy(spaceId, currentOccupancy, spaceType, now);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
console.error(`[space-busyness] Failed to get occupancy:`, error);
|
|
196
|
+
throw new Error(`Failed to get current occupancy for ${space.name}. The space may not have active sensors.`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Calculate utilization (null when capacity is not configured)
|
|
200
|
+
const capacityConfigured = !!(space.capacity?.max && space.capacity.max > 0);
|
|
201
|
+
const utilizationPercent = capacityConfigured
|
|
202
|
+
? (currentOccupancy / space.capacity.max) * 100
|
|
203
|
+
: null;
|
|
204
|
+
const label = utilizationPercent !== null ? getOccupancyLabel(utilizationPercent) : null;
|
|
205
|
+
// Build response
|
|
206
|
+
const warnings = [];
|
|
207
|
+
if (!capacityConfigured) {
|
|
208
|
+
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
|
+
}
|
|
210
|
+
const response = {
|
|
211
|
+
space: {
|
|
212
|
+
id: space.id,
|
|
213
|
+
name: space.name,
|
|
214
|
+
type: spaceType,
|
|
215
|
+
path: spacePath,
|
|
216
|
+
},
|
|
217
|
+
current: {
|
|
218
|
+
occupancy: Math.round(currentOccupancy),
|
|
219
|
+
capacity: space.capacity,
|
|
220
|
+
utilization_percent: utilizationPercent !== null ? parseFloat(utilizationPercent.toFixed(1)) : null,
|
|
221
|
+
label,
|
|
222
|
+
capacity_configured: capacityConfigured,
|
|
223
|
+
as_of: now.toISOString(),
|
|
224
|
+
},
|
|
225
|
+
recommendation: label
|
|
226
|
+
? getBusinessRecommendation(label)
|
|
227
|
+
: "Unable to assess busyness without configured capacity.",
|
|
228
|
+
summary: "", // Populated below
|
|
229
|
+
timestamp: now.toISOString(),
|
|
230
|
+
};
|
|
231
|
+
// Get trend if requested
|
|
232
|
+
if (args.include_trend !== false) {
|
|
233
|
+
try {
|
|
234
|
+
// Query last 4 weeks of data (v4 Stats API uses occupancy_avg_presence for both rooms and zones)
|
|
235
|
+
const measurement = "occupancy_avg_presence";
|
|
236
|
+
const stats = await getSingleAssetStats(measurement, spaceId, "-4w", "now");
|
|
237
|
+
if (stats) {
|
|
238
|
+
const typical = stats.mean;
|
|
239
|
+
let vsTypicalPercent;
|
|
240
|
+
if (typical > 0) {
|
|
241
|
+
vsTypicalPercent = ((currentOccupancy - typical) / typical) * 100;
|
|
242
|
+
}
|
|
243
|
+
else if (currentOccupancy > 0) {
|
|
244
|
+
vsTypicalPercent = 100;
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
vsTypicalPercent = 0;
|
|
248
|
+
}
|
|
249
|
+
const trendLabel = getTrendLabel(vsTypicalPercent);
|
|
250
|
+
response.trend = {
|
|
251
|
+
typical_for_time: parseFloat(typical.toFixed(1)),
|
|
252
|
+
vs_typical_percent: parseFloat(vsTypicalPercent.toFixed(1)),
|
|
253
|
+
trend_label: trendLabel,
|
|
254
|
+
historical_context: `${formatDayAndTime(now, spaceTimezone)} avg: ${Math.round(typical)} people (last 4 weeks)`,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
console.error(`[space-busyness] Failed to get trend data:`, error);
|
|
260
|
+
warnings.push("Could not retrieve historical trend data. Trend comparison is unavailable.");
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// Build summary
|
|
264
|
+
if (capacityConfigured && utilizationPercent !== null) {
|
|
265
|
+
response.summary = buildBusynessSummary({
|
|
266
|
+
spaceName: space.name,
|
|
267
|
+
occupancy: Math.round(currentOccupancy),
|
|
268
|
+
capacity: space.capacity.max,
|
|
269
|
+
utilizationPercent,
|
|
270
|
+
trendLabel: response.trend?.trend_label,
|
|
271
|
+
dayTime: formatDayAndTime(now, spaceTimezone),
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
response.summary = `${space.name}: ${Math.round(currentOccupancy)} people (capacity not configured)`;
|
|
276
|
+
}
|
|
277
|
+
if (warnings.length > 0) {
|
|
278
|
+
response.warning = warnings.join(" ");
|
|
279
|
+
}
|
|
280
|
+
return response;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Register butlr_space_busyness with an McpServer instance
|
|
284
|
+
*/
|
|
285
|
+
export function registerSpaceBusyness(server) {
|
|
286
|
+
server.registerTool("butlr_space_busyness", {
|
|
287
|
+
title: "Space Busyness",
|
|
288
|
+
description: SPACE_BUSYNESS_DESCRIPTION,
|
|
289
|
+
inputSchema: spaceBusynessInputShape,
|
|
290
|
+
annotations: {
|
|
291
|
+
readOnlyHint: true,
|
|
292
|
+
destructiveHint: false,
|
|
293
|
+
idempotentHint: false,
|
|
294
|
+
openWorldHint: true,
|
|
295
|
+
},
|
|
296
|
+
}, async (args) => {
|
|
297
|
+
const validated = SpaceBusynessArgsSchema.parse(args);
|
|
298
|
+
const result = await executeSpaceBusyness(validated);
|
|
299
|
+
return {
|
|
300
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
301
|
+
};
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
//# sourceMappingURL=butlr-space-busyness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"butlr-space-busyness.js","sourceRoot":"","sources":["../../src/tools/butlr-space-busyness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { TrafficFlowResponse } from "../types/responses.js";
|
|
4
|
+
export declare const TrafficFlowArgsSchema: z.ZodEffects<z.ZodObject<{
|
|
5
|
+
space_id_or_name: z.ZodString;
|
|
6
|
+
time_window: z.ZodDefault<z.ZodEnum<["20m", "1h", "today", "custom"]>>;
|
|
7
|
+
custom_start: z.ZodOptional<z.ZodPipeline<z.ZodString, z.ZodEffects<z.ZodString, string, string>>>;
|
|
8
|
+
custom_stop: z.ZodOptional<z.ZodPipeline<z.ZodString, z.ZodEffects<z.ZodString, string, string>>>;
|
|
9
|
+
}, "strict", z.ZodTypeAny, {
|
|
10
|
+
space_id_or_name: string;
|
|
11
|
+
time_window: "custom" | "20m" | "1h" | "today";
|
|
12
|
+
custom_start?: string | undefined;
|
|
13
|
+
custom_stop?: string | undefined;
|
|
14
|
+
}, {
|
|
15
|
+
space_id_or_name: string;
|
|
16
|
+
time_window?: "custom" | "20m" | "1h" | "today" | undefined;
|
|
17
|
+
custom_start?: string | undefined;
|
|
18
|
+
custom_stop?: string | undefined;
|
|
19
|
+
}>, {
|
|
20
|
+
space_id_or_name: string;
|
|
21
|
+
time_window: "custom" | "20m" | "1h" | "today";
|
|
22
|
+
custom_start?: string | undefined;
|
|
23
|
+
custom_stop?: string | undefined;
|
|
24
|
+
}, {
|
|
25
|
+
space_id_or_name: string;
|
|
26
|
+
time_window?: "custom" | "20m" | "1h" | "today" | undefined;
|
|
27
|
+
custom_start?: string | undefined;
|
|
28
|
+
custom_stop?: string | undefined;
|
|
29
|
+
}>;
|
|
30
|
+
export type TrafficFlowArgs = z.output<typeof TrafficFlowArgsSchema>;
|
|
31
|
+
/**
|
|
32
|
+
* Execute traffic flow tool
|
|
33
|
+
*/
|
|
34
|
+
export declare function executeTrafficFlow(args: TrafficFlowArgs): Promise<TrafficFlowResponse>;
|
|
35
|
+
/**
|
|
36
|
+
* Register butlr_traffic_flow with an McpServer instance
|
|
37
|
+
*/
|
|
38
|
+
export declare function registerTrafficFlow(server: McpServer): void;
|
|
39
|
+
//# sourceMappingURL=butlr-traffic-flow.d.ts.map
|
|
@@ -0,0 +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;AAaxB,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,gCAuS7D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsB3D"}
|