UNPKG

rhombus-node-mcp

Version:
152 lines (140 loc) 7.52 kB
import { getCameraSettings, getImageForCameraAtTime, getCameraMediaUris, getCameraAIThresholds } from "../api/camera-tool-api.js"; import { getLogger } from "../logger.js"; import { BASE_TOOL_ARGS } from "../types/camera-tool-types.js"; import { extractFromToolExtra } from "../util.js"; const TOOL_NAME = "camera-tool"; const TOOL_DESCRIPTION = ` This tool can perform some action pertaining to the video stream of a camera. There are four types of requests that can be passed into "requestType": - image - get-settings - get-media-uris - get-ai-thresholds What follows is a description of the behavior of this tool given the requestType "image" This tool should be used any time someone wants to specify a subset of cameras to use for a task, based on some features that the camera sees. For example, interior cameras, cameras facing the street, cameras with a view of X, Y, Z, etc. For instance if someone says "I want X using cameras with Y" then this tool should get a snapshot of the image to answer the question of if the camera satisfies the Y predicate. This tool captures and returns a real-time snapshot from a designated security camera. The image reflects the current scene in the camera's field of view and serves as a contextual input source for downstream tasks such as object recognition, anomaly detection, incident investigation, or situational assessment. When invoked, the tool provides the following: - Visual Scene Capture: A high-resolution image of what the camera is actively observing, including people, vehicles, license plates, and any detectable objects. - Optional zoom: pass cropX, cropY, cropWidth, cropHeight (each a percentage 0-100, origin at the top-left) to return only a sub-region of the frame so you can inspect a detail (e.g. a license plate or a doorway) more closely. Omit them for the full frame. When zooming into a small crop, pass a smaller downscaleFactor (e.g. 1-3) to preserve detail. What follows is a description of the behavior of this tool given the requestType "get-settings" This tool retrieves the current configuration for a specified camera or associated device (e.g., sensor, access controller). The returned JSON object can include detailed camera settings (e.g., resolution, bitrate) and various device-specific configurations (e.g. storage settings). NOTE: To update camera settings, use the update-tool instead. --- **AUTOMATIC SNAPSHOT FOR IMAGE QUALITY ISSUES** — When a user mentions camera image quality (darkness, brightness, blur, washed out, "doesn't look great", "fix the image", etc.), you MUST IMMEDIATELY: 1. Call camera-tool with requestType "image" to capture a snapshot WITHOUT asking first. 2. Analyze the image to identify quality issues. 3. Call camera-tool with requestType "get-settings" to check current camera settings. 4. Propose specific setting changes based on your analysis (store the exact values you plan to change, e.g. img_brightness, wdr_strength). 5. When the user confirms ("yes", "confirm", "fix it", "apply", "go ahead", "ok", etc.), call update-tool with those stored settings — see update-tool's description for the confirmation flow. NEVER skip the update-tool call. Examples that REQUIRE the automatic snapshot flow: - "This camera's image doesn't look great" - "The image quality is poor" - "Can you fix the image" - "Adjust settings to be optimal" - "The camera looks blurry/dark/washed out" - Any mention of image appearance problems. **VISUAL-FEATURE CAMERA FILTERING** — When the user asks for cameras filtered by what they can see (indoors/outdoors, "facing the street", "with a view of X", parking lot, entrance), you MUST: 1. First get the camera list via get-entity-tool or location-tool. 2. Then call camera-tool with requestType "image" for EACH candidate camera (in PARALLEL). 3. Analyze each image to determine if it meets the user's criteria. 4. Return only the cameras that match. `; const logger = getLogger("camera-tool"); const TOOL_ARGS = BASE_TOOL_ARGS; const TOOL_HANDLER = async (args, extra) => { const { cameraUuid, timestampISO, requestType, cropX, cropY, cropWidth, cropHeight, downscaleFactor } = args; if (!cameraUuid) { return { content: [ { type: "text", text: JSON.stringify({ needUserInput: true, commandForUser: "Which camera are you talking about?", }), }, ], }; } // biome-ignore lint/suspicious/noExplicitAny: this will be returned, and can be any type since it will be JSON.stringify'd let response; const timestampMs = timestampISO ? new Date(timestampISO).getTime() : Date.now() - 1000 * 60 * 5; const { requestModifiers, sessionId } = extractFromToolExtra(extra); switch (requestType) { case "image": response = await getImageForCameraAtTime(cameraUuid, timestampMs, requestModifiers, sessionId, { crop: { x: cropX ?? null, y: cropY ?? null, width: cropWidth ?? null, height: cropHeight ?? null, }, downscaleFactor: downscaleFactor ?? null, }); if (!response.success || !response.imageData) { return { content: [{ type: "text", text: JSON.stringify(response) }], }; } logger.debug(`Received image response (base64 length ${response.imageData.length})`); return { content: [ { type: "image", data: response.imageData, mimeType: "image/jpeg", }, { type: "text", text: JSON.stringify({ success: true, status: "image-attached", cameraUuid, timestampMs, cropApplied: response.crop ?? null, }), }, ], }; case "get-settings": response = await getCameraSettings(cameraUuid, requestModifiers, sessionId); return { content: [{ type: "text", text: JSON.stringify(response) }], }; case "get-media-uris": response = await getCameraMediaUris(cameraUuid, requestModifiers, sessionId); return { content: [{ type: "text", text: JSON.stringify(response) }], }; case "get-ai-thresholds": response = await getCameraAIThresholds(cameraUuid, requestModifiers, sessionId); return { content: [{ type: "text", text: JSON.stringify(response) }], }; default: response = { error: true, status: "missing unknown type from tool call", }; break; } return { content: [ { type: "text", text: JSON.stringify({ response }), }, ], }; }; export function createTool(server) { server.registerTool(TOOL_NAME, { title: "Cameras", description: TOOL_DESCRIPTION, inputSchema: TOOL_ARGS, annotations: { readOnlyHint: true }, }, TOOL_HANDLER); }