rhombus-node-mcp
Version:
MCP server for Rhombus API
72 lines (69 loc) • 4.36 kB
JavaScript
import { searchLicensePlates, searchObjectsByColor, searchObjectsByText, searchMotionGrid, } from "../api/search-tool-api.js";
import { SearchToolRequestType, OUTPUT_SCHEMA, TOOL_ARGS, } from "../types/search-tool-types.js";
import { createToolStructuredContent, createToolTextContent, extractFromToolExtra, } from "../util.js";
const TOOL_NAME = "search-tool";
const TOOL_DESCRIPTION = `
This tool performs visual and data searches across Rhombus camera footage and events.
It has the following modes of operation, determined by the "requestType" parameter:
- ${SearchToolRequestType.LICENSE_PLATE}: Search for license plate sightings by plate number. Requires query (the plate number or partial match).
- ${SearchToolRequestType.OBJECT_BY_COLOR}: Search for objects by color in camera footage. Requires query (color name) and cameraUuid.
- ${SearchToolRequestType.OBJECT_BY_TEXT}: Semantic search for objects in footage using a text description. Requires query (text description).
- ${SearchToolRequestType.MOTION_SEARCH}: Search for motion events in a camera's field of view. Requires cameraUuid, startTime, and endTime.
All search types support optional startTime and endTime to narrow the search window.
Use the get-entity-tool with entityType CAMERA to get camera UUIDs.
`;
const TOOL_HANDLER = async (args, _extra) => {
const { requestModifiers, sessionId } = extractFromToolExtra(_extra);
try {
const startTimeMs = args.startTime ? new Date(args.startTime).getTime() : undefined;
const endTimeMs = args.endTime ? new Date(args.endTime).getTime() : undefined;
switch (args.requestType) {
case SearchToolRequestType.LICENSE_PLATE: {
if (!args.query) {
return createToolTextContent(JSON.stringify({ error: "query is required for license-plate search." }));
}
const licensePlateResults = await searchLicensePlates(args.query, startTimeMs, endTimeMs, args.deviceUuids, args.locationUuids, requestModifiers, sessionId);
return createToolStructuredContent({ licensePlateResults });
}
case SearchToolRequestType.OBJECT_BY_COLOR: {
if (!args.query || !args.cameraUuid) {
return createToolTextContent(JSON.stringify({ error: "query and cameraUuid are required for object-by-color search." }));
}
const objectColorResults = await searchObjectsByColor(args.query, args.cameraUuid, startTimeMs, endTimeMs, requestModifiers, sessionId);
return createToolStructuredContent({ objectColorResults });
}
case SearchToolRequestType.OBJECT_BY_TEXT: {
if (!args.query) {
return createToolTextContent(JSON.stringify({ error: "query is required for object-by-text search." }));
}
const textSearchDeviceUuids = args.deviceUuids
?? (args.cameraUuid ? [args.cameraUuid] : null);
const objectTextResults = await searchObjectsByText(args.query, startTimeMs, endTimeMs, textSearchDeviceUuids, requestModifiers, sessionId);
return createToolStructuredContent({ objectTextResults });
}
case SearchToolRequestType.MOTION_SEARCH: {
if (!args.cameraUuid || !startTimeMs || !endTimeMs) {
return createToolTextContent(JSON.stringify({ error: "cameraUuid, startTime, and endTime are required for motion-search." }));
}
const motionResults = await searchMotionGrid(args.cameraUuid, startTimeMs, endTimeMs, requestModifiers, sessionId);
return createToolStructuredContent({ motionResults });
}
}
}
catch (error) {
if (error instanceof Error) {
return createToolStructuredContent({ error: error.message });
}
return createToolStructuredContent({ error: "Unknown error" });
}
return createToolStructuredContent({ error: "Invalid request type" });
};
export function createTool(server) {
server.registerTool(TOOL_NAME, {
title: "Search",
description: TOOL_DESCRIPTION,
inputSchema: TOOL_ARGS,
outputSchema: OUTPUT_SCHEMA.shape,
annotations: { readOnlyHint: true },
}, TOOL_HANDLER);
}