rhombus-node-mcp
Version:
MCP server for Rhombus API
53 lines (49 loc) • 2.81 kB
JavaScript
import { getBadgeTimeline } from "../api/badge-timeline-tool-api.js";
import { OUTPUT_SCHEMA, TOOL_ARGS } from "../types/badge-timeline-tool-types.js";
import { createToolStructuredContent, extractFromToolExtra } from "../util.js";
const TOOL_NAME = "badge-timeline-tool";
const TOOL_DESCRIPTION = `
Reconstructs one person's movements through a building from their Honeywell OnGuard (Lenel) badge taps.
Use this for incident reconstruction / "follow the badge" requests, e.g. "reconstruct Eve's movements
yesterday" or "where did this cardholder go".
Returns the cardholder's badge taps in CHRONOLOGICAL order (oldest first), each with:
- datetime / timestampMs and the area entered
- deviceUuid: the camera at that door
- clipHint (camera + start/end window) and stillHint (camera + timestamp)
- gapToNextSeconds: time until the next tap (a large gap = unobserved movement between doors)
plus a "path" array summarizing the areas traversed in order.
Resolve relative times like "yesterday" to ISO 8601 first (use the timestamp tool), then pass
startTime/endTime. cardholderQuery is a full-text name match; if "ambiguousCardholders" is returned the
query matched more than one person — ask the user which one before trusting the timeline.
IMPORTANT — to show the movement visually: for each stop (or the key transitions), call the camera-tool
(requestType "image", cameraUuid = stop.deviceUuid, timestamp = stop.timestampMs) for a still you can see,
and/or the clips-tool (requestType "createClip", using stop.clipHint) for video. Issue those per-stop
media calls in PARALLEL, then present the timeline as a chronological narrative.
`;
const TOOL_HANDLER = async (args, _extra) => {
const { requestModifiers, sessionId } = extractFromToolExtra(_extra);
try {
const result = await getBadgeTimeline({
cardholderQuery: args.cardholderQuery,
locationUuids: args.locationUuids ?? undefined,
afterMs: args.startTime ? new Date(args.startTime).getTime() : undefined,
beforeMs: args.endTime ? new Date(args.endTime).getTime() : undefined,
clipPaddingSeconds: args.clipPaddingSeconds ?? undefined,
limit: args.limit ?? undefined,
}, args.timeZone ?? "UTC", requestModifiers, sessionId);
return createToolStructuredContent(result);
}
catch (error) {
const message = error instanceof Error ? error.message : "Unknown error";
return createToolStructuredContent({ error: message });
}
};
export function createTool(server) {
server.registerTool(TOOL_NAME, {
title: "Badge Timeline",
description: TOOL_DESCRIPTION,
inputSchema: TOOL_ARGS,
outputSchema: OUTPUT_SCHEMA.shape,
annotations: { readOnlyHint: true },
}, TOOL_HANDLER);
}