Skip to main content
Glama
RhombusSystems

Rhombus MCP Server

Official

update-tool

Update Rhombus device configurations like camera video, audio, and device settings through a guided multi-step process.

Instructions

This tool allows updating configuration settings for various Rhombus entities. Currently supports:

  • Cameras: Update video settings (resolution, HDR, WDR, brightness, contrast, etc.), audio settings (recording, microphone, speaker), and device settings (name, timezone, LED control).

For LED control, use EXACTLY these field names in cameraDeviceSettings:

  • To turn LED off: {"led_stealth_mode": true} (recommended) or {"led_mode": "always_off"}

  • To turn LED on: {"led_stealth_mode": false} or {"led_mode": "always_on"} or {"led_mode": "auto"}

  • IMPORTANT: Use underscore in field names (led_mode, led_stealth_mode), not camelCase

The tool supports faceted UUIDs (e.g., "cameraUuid.v0" or "cameraUuid.v1") to update specific camera facets. If no facet is specified, defaults to "v0".

The tool guides users through a multi-step process:

  1. Entity selection (if not provided)

  2. Settings configuration with current values shown

  3. Confirmation and application of changes

Future support planned for:

  • Climate sensors

  • Door controllers

  • Environmental gateways

  • Audio gateways

  • Doorbell cameras

  • Badge readers

The tool uses elicitation forms for rich user interaction and shows current settings before updates.

Output filtering (all tools):

  • includeFields (string[]): Dot-notation paths to keep in the response (e.g. "vehicleEvents.vehicleLicensePlate"). Omit to return all fields.

  • filterBy (array): Predicates to filter array items. Each entry: {field, op, value} where op is one of = != > >= < <= contains. All conditions are ANDed. Example: [{field:"vehicleLicensePlate", op:"=", value:"ABC123"}] WARNING: some tool responses exceed 400k characters — use these params to request only the data you need.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
entityTypeYesType of entity to update
entityUuidYesUUID of the entity to update
cameraVideoSettingsYesJSON string of video settings to update for camera
cameraAudioSettingsYesJSON string of audio settings to update for camera
cameraDeviceSettingsYesJSON string of device settings to update for camera
stepYesCurrent step in the update process
includeFieldsYesDot-notation field paths to include in the response (e.g. "vehicleEvents.vehicleLicensePlate"). Pass null to return all fields. WARNING: some responses can exceed 400k characters — use includeFields to request only the data you need. For high-volume tools this may be required to get a complete answer.
filterByYesFilter array items in the response by field values. All conditions are ANDed. Example: [{field: "vehicleLicensePlate", op: "=", value: "ABC123"}, {field: "confidence", op: ">", value: 0.8}] Use alongside includeFields to get only the specific records and fields you need.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
needUserInputNo
messageNo
requestTypeNo
submitActionNo
entityTypeNo
entityUuidNo
currentSettingsNo
successNo
errorNo
updatedSettingsNo

Implementation Reference

  • Main handler function that executes the update-tool logic. Implements a multi-step process: entity type selection, entity UUID selection, settings configuration, validation, and applying updates via the API. Currently supports camera settings updates (video, audio, device).
    const TOOL_HANDLER = async (args: ToolArgs, extra: any) => {
      const {
        entityType,
        entityUuid,
        cameraVideoSettings,
        cameraAudioSettings,
        cameraDeviceSettings,
        step,
      } = args;
    
      // Handle camera updates
      if (entityType === "camera") {
        // Step 3: Apply settings (if settings provided)
        if (
          entityUuid &&
          ((cameraVideoSettings && cameraVideoSettings.trim()) ||
            (cameraAudioSettings && cameraAudioSettings.trim()) ||
            (cameraDeviceSettings && cameraDeviceSettings.trim()))
        ) {
          try {
            // Parse the faceted UUID to extract base UUID and facet
            const { baseUuid, facet } = parseFacetedUuid(entityUuid);
    
            const updatePayload: any = {
              configUpdate: {
                deviceUuid: baseUuid,
              },
            };
    
            // Parse and add video settings
            if (cameraVideoSettings && cameraVideoSettings.trim()) {
              const videoSettings = JSON.parse(cameraVideoSettings);
              // Validate with zod schema
              const validatedVideo = CameraVideoSettings.parse(videoSettings);
              updatePayload.configUpdate.videoFacetSettings = {
                [facet]: cleanUpdatePayload(validatedVideo),
              };
            }
    
            // Parse and add audio settings
            if (cameraAudioSettings && cameraAudioSettings.trim()) {
              const audioSettings = JSON.parse(cameraAudioSettings);
              // Validate with zod schema
              const validatedAudio = CameraAudioSettings.parse(audioSettings);
              updatePayload.configUpdate.audioFacetSettings = {
                [facet]: cleanUpdatePayload(validatedAudio),
              };
            }
    
            // Parse and add device settings
            if (cameraDeviceSettings && cameraDeviceSettings.trim()) {
              const deviceSettings = JSON.parse(cameraDeviceSettings);
              logger.debug("[update-tool] Raw device settings:", deviceSettings);
    
              // Transform ledMode to led_mode and OFF to always_off before validation
              if ("ledMode" in deviceSettings) {
                deviceSettings.led_mode =
                  deviceSettings.ledMode === "OFF" ? "always_off" : deviceSettings.ledMode;
                delete deviceSettings.ledMode;
              }
    
              // Convert string "true"/"false" to boolean for led_stealth_mode
              if (
                "led_stealth_mode" in deviceSettings &&
                typeof deviceSettings.led_stealth_mode === "string"
              ) {
                deviceSettings.led_stealth_mode = deviceSettings.led_stealth_mode === "true";
              }
    
              // Validate with zod schema
              const validatedDevice = CameraDeviceSettings.parse(deviceSettings);
              logger.debug("[update-tool] Validated device settings:", validatedDevice);
    
              const cleaned = cleanUpdatePayload(validatedDevice);
              logger.debug("[update-tool] Cleaned device settings:", cleaned);
    
              updatePayload.configUpdate.deviceSettings = cleaned;
            }
    
            // Validate the full payload
            const validatedPayload = UpdateCameraConfigPayload.parse(updatePayload);
    
            const featureValidation = await validateCameraFeatureSupport(
              validatedPayload,
              extra._meta?.requestModifiers as RequestModifiers,
              extra.sessionId
            );
            if (!featureValidation.canProceed) {
              return {
                content: [
                  {
                    type: "text" as const,
                    text:
                      featureValidation.error ||
                      "This camera does not support one or more requested features.",
                  },
                ],
              };
            }
    
            // Apply the updates
            const result = await updateCameraConfig(
              validatedPayload,
              extra._meta?.requestModifiers as RequestModifiers,
              extra.sessionId
            );
    
            if (!result.success) {
              const hasCapabilitySensitiveChange = hasCapabilitySensitiveSettings(validatedPayload);
              const normalizedError = hasCapabilitySensitiveChange
                ? "This camera may not support one or more requested settings."
                : result.error;
              return {
                content: [
                  {
                    type: "text" as const,
                    text: `Failed to update camera settings: ${normalizedError}`,
                  },
                ],
              };
            }
    
            // Format the updated settings for display
            const formattedSettings = formatCameraSettings(validatedPayload.configUpdate);
    
            const jsonResultResponse = {
              needUserInput: false,
              success: true,
              message: `✅ Camera settings updated successfully!\n\n${formattedSettings}`,
              entityType: "camera",
              entityUuid,
              updatedSettings: validatedPayload.configUpdate,
            };
    
            return {
              content: [
                {
                  type: "text" as const,
                  text: jsonResultResponse.message,
                },
              ],
              structuredContent: jsonResultResponse,
            };
          } catch (error) {
            return {
              content: [
                {
                  type: "text" as const,
                  text: `Error updating camera settings: ${
                    error instanceof Error ? error.message : "Unknown error"
                  }`,
                },
              ],
            };
          }
        }
    
        // Step 2: Show settings configuration form (if entityUuid provided but no settings)
        if (entityUuid && step !== "entity-selection") {
          try {
            // Parse the faceted UUID to extract base UUID
            const { baseUuid } = parseFacetedUuid(entityUuid);
    
            // Get current camera details
            const cameraDetails = await getCameraDetails(
              baseUuid,
              extra._meta?.requestModifiers as RequestModifiers,
              extra.sessionId
            );
    
            if (!cameraDetails.success || !cameraDetails.data) {
              return {
                content: [
                  {
                    type: "text" as const,
                    text: `Failed to get camera details: ${cameraDetails.error || "Camera not found"}`,
                  },
                ],
              };
            }
    
            const camera = cameraDetails.data;
            const currentVideoSettings = camera.videoSettings || {};
            const currentAudioSettings = camera.audioSettings || {};
            const currentDeviceSettings = {
              camera_name: camera.name,
              camera_timezone: camera.timezone,
              led_intensity: camera.ledIntensity,
              led_mode: camera.ledMode,
            };
    
            const jsonResultResponse = {
              needUserInput: true,
              message: `Configure settings for camera "${camera.name}" (${entityUuid})\n\nCurrent settings are shown below. Modify the values you want to change:`,
              requestType: "camera-settings-configuration",
              submitAction: "update-tool",
              entityType: "camera",
              entityUuid,
              currentSettings: {
                video: currentVideoSettings,
                audio: currentAudioSettings,
                device: currentDeviceSettings,
              },
            };
    
            return {
              content: [
                {
                  type: "text" as const,
                  text: JSON.stringify(jsonResultResponse),
                },
              ],
              structuredContent: jsonResultResponse,
            };
          } catch (error) {
            return {
              content: [
                {
                  type: "text" as const,
                  text: `Error retrieving camera details: ${
                    error instanceof Error ? error.message : "Unknown error"
                  }`,
                },
              ],
            };
          }
        }
    
        // Step 1: Entity selection (if no entityUuid)
        if (!entityUuid) {
          const jsonResultResponse = {
            needUserInput: true,
            message:
              "Please provide the camera UUID you want to update.\n\nYou can provide a faceted UUID (e.g., 'cameraUuid.v0' or 'cameraUuid.v1') to update a specific facet, or just the base UUID to update facet v0 by default.\n\nYou can find the camera UUID in the Rhombus console or by using the search tools.",
            requestType: "entity-selection",
            submitAction: "update-tool",
            entityType: "camera",
          };
    
          return {
            content: [
              {
                type: "text" as const,
                text: JSON.stringify(jsonResultResponse),
              },
            ],
            structuredContent: jsonResultResponse,
          };
        }
      }
    
      // Handle other entity types (future implementation)
      if (entityType === "climate-sensor") {
        return {
          content: [
            {
              type: "text" as const,
              text: "Climate sensor updates are not yet implemented. Coming soon!",
            },
          ],
        };
      }
    
      if (entityType === "door-controller") {
        return {
          content: [
            {
              type: "text" as const,
              text: "Door controller updates are not yet implemented. Coming soon!",
            },
          ],
        };
      }
    
      if (entityType === "environmental-gateway") {
        return {
          content: [
            {
              type: "text" as const,
              text: "Environmental gateway updates are not yet implemented. Coming soon!",
            },
          ],
        };
      }
    
      if (entityType === "audio-gateway") {
        return {
          content: [
            {
              type: "text" as const,
              text: "Audio gateway updates are not yet fully implemented. Coming soon!",
            },
          ],
        };
      }
    
      if (entityType === "doorbell-camera") {
        return {
          content: [
            {
              type: "text" as const,
              text: "Doorbell camera updates are not yet fully implemented. Coming soon!",
            },
          ],
        };
      }
    
      if (entityType === "badge-reader") {
        return {
          content: [
            {
              type: "text" as const,
              text: "Badge reader updates are not yet fully implemented. Coming soon!",
            },
          ],
        };
      }
    
      // Step 0: Show initial form (no entityType provided)
      return {
        content: [
          {
            type: "text" as const,
            text: JSON.stringify({
              needUserInput: true,
              message:
                "Welcome to the Rhombus entity update tool!\n\nWhat type of entity would you like to update?\n\n• **camera** - Update camera video, audio, or device settings\n• **climate-sensor** - Update climate sensor settings (coming soon)\n• **door-controller** - Update door controller settings (coming soon)\n• **environmental-gateway** - Update environmental gateway settings (coming soon)\n\nPlease specify the entity type to continue.",
              requestType: "entity-type-selection",
              submitAction: "update-tool",
            }),
          },
        ],
      };
    };
  • Registers the 'update-tool' on the MCP server with its input schema, output schema, description, and handler function.
    export function createTool(server: McpServer) {
      server.registerTool(
        TOOL_NAME,
        {
          description: TOOL_DESCRIPTION,
          inputSchema: TOOL_ARGS,
          outputSchema: OUTPUT_SCHEMA.shape,
        },
        TOOL_HANDLER
      );
    }
  • Defines all type schemas: ENTITY_TYPE enum, CameraVideoSettings, CameraAudioSettings, CameraDeviceSettings, TOOL_ARGS (input schema), OUTPUT_SCHEMA, UpdateCameraConfigPayload (API payload), and the parseFacetedUuid helper.
    import { z } from "zod";
    
    // Define the entity types that can be updated
    export const ENTITY_TYPE = z.enum([
      "camera",
      "climate-sensor",
      "door-controller",
      "environmental-gateway",
      "audio-gateway",
      "doorbell-camera",
      "badge-reader",
    ]);
    export type EntityType = z.infer<typeof ENTITY_TYPE>;
    
    // Camera-specific update schemas
    export const CameraVideoSettings = z.object({
      hdr_enabled: z.boolean().optional().describe("Enable HDR (High Dynamic Range)"),
      img_brightness: z
        .number()
        .min(-255)
        .max(255)
        .optional()
        .describe("Image brightness adjustment (-255 to 255)"),
      img_contrast: z.number().min(0).max(128).optional().describe("Image contrast (0 to 128)"),
      img_saturation: z.number().min(0).max(255).optional().describe("Image saturation (0 to 255)"),
      img_sharpness: z.number().min(0).max(255).optional().describe("Image sharpness (0 to 255)"),
      resolution: z
        .object({
          width: z.number().int().positive().optional(),
          height: z.number().int().positive().optional(),
        })
        .optional()
        .describe("Video resolution (width x height)"),
      wdr_enabled: z.boolean().optional().describe("Enable Wide Dynamic Range"),
      wdr_strength: z.number().min(0).max(128).optional().describe("WDR strength (0 minimum to 128 maximum)"),
      video_persist_disabled: z.boolean().optional().describe("Disable video persistence"),
      zero_motion_video_bitrate_percent: z
        .number()
        .min(0)
        .max(100)
        .optional()
        .describe("Zero motion video bitrate percentage"),
      // Night mode settings
      night_img_brightness: z
        .number()
        .min(-255)
        .max(255)
        .optional()
        .describe("Night mode brightness (-255 to 255)"),
      night_img_contrast: z
        .number()
        .min(0)
        .max(128)
        .optional()
        .describe("Night mode contrast (0 to 128)"),
      night_img_saturation: z
        .number()
        .min(0)
        .max(255)
        .optional()
        .describe("Night mode saturation (0 to 255)"),
      night_img_sharpness: z
        .number()
        .min(0)
        .max(255)
        .optional()
        .describe("Night mode sharpness (0 to 255)"),
    });
    
    export const CameraAudioSettings = z.object({
      audio_record: z.boolean().optional().describe("Enable audio recording"),
      device_mic_enabled: z.boolean().optional().describe("Enable device microphone"),
      device_speaker_enabled: z.boolean().optional().describe("Enable device speaker"),
      audio_internal_mic_volume: z
        .number()
        .min(0)
        .max(100)
        .optional()
        .describe("Internal microphone volume (0-100)"),
      audio_internal_speaker_volume: z
        .number()
        .min(0)
        .max(100)
        .optional()
        .describe("Internal speaker volume (0-100)"),
    });
    
    export const CameraDeviceSettings = z.object({
      camera_name: z.string().optional().describe("Camera display name"),
      camera_timezone: z.string().optional().describe("Camera timezone (e.g., 'America/Los_Angeles')"),
      led_intensity: z.number().min(0).max(100).optional().describe("LED intensity (0-100)"),
      led_mode: z
        .enum(["auto", "always_on", "always_off"])
        .optional()
        .describe("LED mode - use 'always_off' to turn LED off"),
      led_stealth_mode: z
        .boolean()
        .optional()
        .describe("Enable stealth mode to turn off LED completely - set to true to turn LED off"),
    });
    
    // Input schema for the tool
    export const TOOL_ARGS = {
      entityType: ENTITY_TYPE.describe("Type of entity to update"),
      entityUuid: z.string().nullable().describe("UUID of the entity to update"),
    
      // Camera-specific update fields
      cameraVideoSettings: z
        .string()
        .nullable()
        .describe("JSON string of video settings to update for camera"),
      cameraAudioSettings: z
        .string()
        .nullable()
        .describe("JSON string of audio settings to update for camera"),
      cameraDeviceSettings: z
        .string()
        .nullable()
        .describe("JSON string of device settings to update for camera"),
    
      // Step tracking for multi-step updates
      step: z
        .enum(["entity-selection", "settings-configuration", "confirmation"])
        .nullable()
        .describe("Current step in the update process"),
    } as const;
    
    const TOOL_ARGS_SCHEMA = z.object(TOOL_ARGS);
    export type ToolArgs = z.infer<typeof TOOL_ARGS_SCHEMA>;
    
    // Output schema
    export const OUTPUT_SCHEMA = z.object({
      needUserInput: z.boolean().optional(),
      message: z.string().optional(),
      requestType: z.string().optional(),
      submitAction: z.string().optional(),
      entityType: z.string().optional(),
      entityUuid: z.string().optional(),
      currentSettings: z.any().optional(),
      success: z.boolean().optional(),
      error: z.string().optional(),
      updatedSettings: z.any().optional(),
    });
    
    // API payload types
    export const UpdateCameraConfigPayload = z.object({
      configUpdate: z.object({
        deviceUuid: z.string(),
        videoFacetSettings: z
          .record(
            z.string(),
            z.object({
              hdr_enabled: z.boolean().nullable().optional(),
              img_brightness: z.number().nullable().optional(),
              img_contrast: z.number().nullable().optional(),
              img_saturation: z.number().nullable().optional(),
              img_sharpness: z.number().nullable().optional(),
              resolution: z
                .object({
                  width: z.number().nullable().optional(),
                  height: z.number().nullable().optional(),
                })
                .nullable()
                .optional(),
              wdr_enabled: z.boolean().nullable().optional(),
              wdr_strength: z.number().nullable().optional(),
              video_persist_disabled: z.boolean().nullable().optional(),
              zero_motion_video_bitrate_percent: z.number().nullable().optional(),
              night_img_brightness: z.number().nullable().optional(),
              night_img_contrast: z.number().nullable().optional(),
              night_img_saturation: z.number().nullable().optional(),
              night_img_sharpness: z.number().nullable().optional(),
            })
          )
          .optional(),
        audioFacetSettings: z
          .record(
            z.string(),
            z.object({
              audio_record: z.boolean().nullable().optional(),
              device_mic_enabled: z.boolean().nullable().optional(),
              device_speaker_enabled: z.boolean().nullable().optional(),
              audio_internal_mic_volume: z.number().nullable().optional(),
              audio_internal_speaker_volume: z.number().nullable().optional(),
            })
          )
          .optional(),
        deviceSettings: z
          .object({
            camera_name: z.string().nullable().optional(),
            camera_timezone: z.string().nullable().optional(),
            led_intensity: z.number().nullable().optional(),
            led_mode: z.string().nullable().optional(),
            led_stealth_mode: z.boolean().nullable().optional(),
          })
          .optional(),
      }),
    });
    
    export type UpdateCameraConfigPayload = z.infer<typeof UpdateCameraConfigPayload>;
    
    // Helper function to parse faceted UUIDs
    export function parseFacetedUuid(uuid: string): { baseUuid: string; facet: string } {
      const parts = uuid.split(".");
      if (parts.length === 2) {
        return { baseUuid: parts[0], facet: parts[1] };
      }
      // Default to v0 facet if not specified
      return { baseUuid: uuid, facet: "v0" };
    }
  • Helper/API layer providing functions for updating camera config, getting camera details, validating feature support (WDR, resolution, audio, set-method-map), cleaning payloads, and formatting settings for display.
    import { postApi } from "../network/network.js";
    import type { RequestModifiers } from "../util.js";
    import type { UpdateCameraConfigPayload } from "../types/update-tool-types.js";
    import { schema } from "../types/schema.js";
    
    type CameraConfigOption = {
      wdrRange?: {
        min?: number | null;
        max?: number | null;
      } | null;
      resolution?: {
        width?: number | null;
        height?: number | null;
      } | null;
    } | null;
    
    type CameraFeatureValidationResult = {
      canProceed: boolean;
      error?: string;
    };
    
    /**
     * Updates camera configuration using the faceted config API
     */
    export async function updateCameraConfig(
      payload: UpdateCameraConfigPayload,
      requestModifiers?: RequestModifiers,
      sessionId?: string
    ): Promise<{
      success: boolean;
      error?: string;
      updatedSettings?: any;
    }> {
      try {
        const result = await postApi<schema["Common_devices_UpdateConfigWSResponse"]>({
          route: "/camera/updateFacetedConfig",
          body: payload,
          modifiers: requestModifiers,
          sessionId,
        });
    
        if (result.error) {
          return {
            success: false,
            error: result.errorMsg || "Failed to update camera configuration",
          };
        }
    
        return {
          success: true,
          updatedSettings: payload.configUpdate,
        };
      } catch (error) {
        return {
          success: false,
          error: error instanceof Error ? error.message : "Unknown error occurred",
        };
      }
    }
    
    /**
     * Gets current camera configuration
     */
    export async function getCameraDetails(
      cameraUuid: string,
      requestModifiers?: RequestModifiers,
      sessionId?: string
    ): Promise<{
      success: boolean;
      error?: string;
      data?: any;
    }> {
      try {
        const result = await postApi<any>({
          route: "/camera/getDetailsV2",
          body: {
            uuid: cameraUuid,
          },
          modifiers: requestModifiers,
          sessionId,
        });
    
        if (result.error) {
          return {
            success: false,
            error: result.errorMsg || "Failed to get camera details",
          };
        }
    
        return {
          success: true,
          data: result.camera,
        };
      } catch (error) {
        return {
          success: false,
          error: error instanceof Error ? error.message : "Unknown error occurred",
        };
      }
    }
    
    async function getCameraHardwareVariation(
      cameraUuid: string,
      requestModifiers?: RequestModifiers,
      sessionId?: string
    ): Promise<{
      success: boolean;
      error?: string;
      hwVariation?: string;
    }> {
      try {
        const result = await postApi<schema["Camera_GetCameraDetailsWSResponse"]>({
          route: "/camera/getDetails",
          body: {
            cameraUuids: [cameraUuid],
          },
          modifiers: requestModifiers,
          sessionId,
        });
    
        if (result.error) {
          return {
            success: false,
            error: result.errorMsg || "Failed to get camera hardware variation",
          };
        }
    
        const camera = result.cameras?.[0];
        return {
          success: true,
          hwVariation: camera?.hwVariation ?? undefined,
        };
      } catch (error) {
        return {
          success: false,
          error: error instanceof Error ? error.message : "Unknown error occurred",
        };
      }
    }
    
    async function getOrgCameraConfigOptions(
      requestModifiers?: RequestModifiers,
      sessionId?: string
    ): Promise<{
      success: boolean;
      error?: string;
      cameraConfigOptions?: Record<string, CameraConfigOption[] | null>;
    }> {
      try {
        const result = await postApi<schema["Org_GetOrgV2WSResponse"]>({
          route: "/org/getOrgV2",
          body: {},
          modifiers: requestModifiers,
          sessionId,
        });
    
        if (result.error) {
          return {
            success: false,
            error: result.errorMsg || "Failed to get organization camera config options",
          };
        }
    
        return {
          success: true,
          cameraConfigOptions: (result.cameraConfigOptions as Record<
            string,
            CameraConfigOption[] | null
          > | null) ?? undefined,
        };
      } catch (error) {
        return {
          success: false,
          error: error instanceof Error ? error.message : "Unknown error occurred",
        };
      }
    }
    
    async function getCameraFacetedConfig(
      cameraUuid: string,
      requestModifiers?: RequestModifiers,
      sessionId?: string
    ): Promise<{
      success: boolean;
      error?: string;
      config?: schema["Device_config_userconfig_ExternalReadableFacetedUserConfig"];
    }> {
      try {
        const result = await postApi<schema["Device_config_GetFacetedUserConfigWSResponse"]>({
          route: "/camera/getFacetedConfig",
          body: {
            deviceUuid: cameraUuid,
          },
          modifiers: requestModifiers,
          sessionId,
        });
    
        if (result.error) {
          return {
            success: false,
            error: result.errorMsg || "Failed to get camera faceted config",
          };
        }
    
        return {
          success: true,
          config: result.config,
        };
      } catch (error) {
        return {
          success: false,
          error: error instanceof Error ? error.message : "Unknown error occurred",
        };
      }
    }
    
    export async function validateCameraFeatureSupport(
      payload: UpdateCameraConfigPayload,
      requestModifiers?: RequestModifiers,
      sessionId?: string
    ): Promise<CameraFeatureValidationResult> {
      const requestedVideoSettings = Object.values(payload.configUpdate.videoFacetSettings ?? {});
      const requestedAudioSettings = Object.values(payload.configUpdate.audioFacetSettings ?? {});
      const requestedDeviceSettings = payload.configUpdate.deviceSettings;
      if (
        requestedVideoSettings.length === 0 &&
        requestedAudioSettings.length === 0 &&
        !requestedDeviceSettings
      ) {
        return { canProceed: true };
      }
    
      const requestedFacet =
        Object.keys(payload.configUpdate.videoFacetSettings ?? {})[0] ??
        Object.keys(payload.configUpdate.audioFacetSettings ?? {})[0] ??
        "v0";
      const requestedVideoSettingsForFacet = requestedVideoSettings[0];
      const requestedAudioSettingsForFacet = requestedAudioSettings[0];
    
      const requestedWdrStrength = requestedVideoSettings
        .map(settings => settings.wdr_strength)
        .find(value => value !== undefined && value !== null);
      const hasWdrRequest = requestedVideoSettings.some(
        settings => settings.wdr_enabled !== undefined || settings.wdr_strength !== undefined
      );
      const requestedResolution = requestedVideoSettingsForFacet?.resolution;
      const hasResolutionRequest =
        requestedResolution?.width !== undefined && requestedResolution?.height !== undefined;
      const hasAudioRequest = requestedAudioSettings.length > 0;
    
      const [hardwareVariationResult, orgOptionsResult, facetedConfigResult] = await Promise.all([
        getCameraHardwareVariation(payload.configUpdate.deviceUuid, requestModifiers, sessionId),
        getOrgCameraConfigOptions(requestModifiers, sessionId),
        getCameraFacetedConfig(payload.configUpdate.deviceUuid, requestModifiers, sessionId),
      ]);
    
      if (facetedConfigResult.success && facetedConfigResult.config) {
        const videoFacetConfig = facetedConfigResult.config.videoFacetSettings?.[requestedFacet] as
          | (schema["Device_config_settings_ExternalReadableVideoSettings"] & {
              updatedSetMethodMap?: Record<string, boolean | null> | null;
            })
          | undefined;
        const audioFacetConfig = facetedConfigResult.config.audioFacetSettings?.[requestedFacet] as
          | (schema["Device_config_settings_ExternalReadableAudioSettings"] & {
              updatedSetMethodMap?: Record<string, boolean | null> | null;
            })
          | undefined;
        const deviceConfig = facetedConfigResult.config.deviceSettings as
          | (schema["Device_config_settings_ExternalReadableDeviceSettings"] & {
              updatedSetMethodMap?: Record<string, boolean | null> | null;
            })
          | undefined;
    
        if (hasAudioRequest && audioFacetConfig?.audio_supported === false) {
          return {
            canProceed: false,
            error: "This camera does not support audio features.",
          };
        }
    
        const unsupportedVideoFields = getUnsupportedFieldsFromSetMethodMap(
          requestedVideoSettingsForFacet,
          videoFacetConfig?.updatedSetMethodMap
        );
        if (unsupportedVideoFields.length > 0) {
          return {
            canProceed: false,
            error: `This camera does not support updating video setting: ${unsupportedVideoFields[0]}.`,
          };
        }
    
        const unsupportedAudioFields = getUnsupportedFieldsFromSetMethodMap(
          requestedAudioSettingsForFacet,
          audioFacetConfig?.updatedSetMethodMap
        );
        if (unsupportedAudioFields.length > 0) {
          return {
            canProceed: false,
            error: `This camera does not support updating audio setting: ${unsupportedAudioFields[0]}.`,
          };
        }
    
        const unsupportedDeviceFields = getUnsupportedFieldsFromSetMethodMap(
          requestedDeviceSettings,
          deviceConfig?.updatedSetMethodMap
        );
        if (unsupportedDeviceFields.length > 0) {
          return {
            canProceed: false,
            error: `This camera does not support updating device setting: ${unsupportedDeviceFields[0]}.`,
          };
        }
      }
    
      // Model-level capability metadata can be unavailable on some deployments; do not hard-fail.
      if (!hardwareVariationResult.success || !orgOptionsResult.success) {
        return { canProceed: true };
      }
    
      const hwVariation = hardwareVariationResult.hwVariation;
      if (!hwVariation) {
        return { canProceed: true };
      }
      const cameraOptions = orgOptionsResult.cameraConfigOptions?.[hwVariation];
      if (
        (!hasWdrRequest && !hasResolutionRequest) ||
        !Array.isArray(cameraOptions) ||
        cameraOptions.length === 0
      ) {
        return { canProceed: true };
      }
    
      if (hasWdrRequest) {
        const wdrOption = cameraOptions.find(option => option?.wdrRange !== undefined);
        if (!wdrOption?.wdrRange) {
          return {
            canProceed: false,
            error: "This camera does not support WDR feature.",
          };
        }
    
        if (typeof requestedWdrStrength === "number") {
          const min = wdrOption.wdrRange.min;
          const max = wdrOption.wdrRange.max;
          if (typeof min === "number" && requestedWdrStrength < min) {
            return {
              canProceed: false,
              error: `This camera supports WDR, but wdr_strength=${requestedWdrStrength} is below the supported minimum (${min}).`,
            };
          }
          if (typeof max === "number" && requestedWdrStrength > max) {
            return {
              canProceed: false,
              error: `This camera supports WDR, but wdr_strength=${requestedWdrStrength} exceeds the supported maximum (${max}).`,
            };
          }
        }
      }
    
      if (hasResolutionRequest) {
        const isRequestedResolutionSupported = cameraOptions.some(option => {
          const optionWidth = option?.resolution?.width;
          const optionHeight = option?.resolution?.height;
          return (
            typeof optionWidth === "number" &&
            typeof optionHeight === "number" &&
            optionWidth === requestedResolution.width &&
            optionHeight === requestedResolution.height
          );
        });
        if (!isRequestedResolutionSupported) {
          return {
            canProceed: false,
            error: `This camera does not support resolution ${requestedResolution.width}x${requestedResolution.height}.`,
          };
        }
      }
    
      return { canProceed: true };
    }
    
    function getUnsupportedFieldsFromSetMethodMap(
      requestedSettings: Record<string, unknown> | undefined,
      setMethodMap: Record<string, boolean | null> | null | undefined
    ): string[] {
      if (!requestedSettings || !setMethodMap) {
        return [];
      }
      return Object.keys(requestedSettings).filter(field => setMethodMap[field] === false);
    }
    
    /**
     * Helper function to remove null/undefined fields from update payload
     */
    export function cleanUpdatePayload(payload: any): any {
      const cleaned: any = {};
    
      for (const [key, value] of Object.entries(payload)) {
        if (value !== null && value !== undefined) {
          if (typeof value === "object" && !Array.isArray(value)) {
            const cleanedValue = cleanUpdatePayload(value);
            if (Object.keys(cleanedValue).length > 0) {
              cleaned[key] = cleanedValue;
            }
          } else {
            cleaned[key] = value;
          }
        }
      }
    
      return cleaned;
    }
    
    /**
     * Formats camera settings for display
     */
    export function formatCameraSettings(settings: any): string {
      const sections: string[] = [];
    
      if (settings.videoFacetSettings) {
        const videoSettings = Object.values(settings.videoFacetSettings)[0] as any;
        if (videoSettings) {
          const videoItems: string[] = [];
          if (videoSettings.resolution)
            videoItems.push(
              `Resolution: ${videoSettings.resolution.width}x${videoSettings.resolution.height}`
            );
          if (videoSettings.img_brightness !== undefined)
            videoItems.push(`Brightness: ${videoSettings.img_brightness}`);
          if (videoSettings.img_contrast !== undefined)
            videoItems.push(`Contrast: ${videoSettings.img_contrast}`);
          if (videoSettings.img_saturation !== undefined)
            videoItems.push(`Saturation: ${videoSettings.img_saturation}`);
          if (videoSettings.img_sharpness !== undefined)
            videoItems.push(`Sharpness: ${videoSettings.img_sharpness}`);
          if (videoSettings.hdr_enabled !== undefined)
            videoItems.push(`HDR: ${videoSettings.hdr_enabled ? "On" : "Off"}`);
          if (videoSettings.wdr_enabled !== undefined)
            videoItems.push(`WDR: ${videoSettings.wdr_enabled ? "On" : "Off"}`);
          if (videoSettings.wdr_strength !== undefined)
            videoItems.push(`WDR Strength: ${videoSettings.wdr_strength}`);
          if (videoSettings.video_persist_disabled !== undefined)
            videoItems.push(
              `Video Persist: ${videoSettings.video_persist_disabled ? "Disabled" : "Enabled"}`
            );
          if (videoSettings.zero_motion_video_bitrate_percent !== undefined)
            videoItems.push(`Zero Motion Bitrate: ${videoSettings.zero_motion_video_bitrate_percent}%`);
    
          if (videoItems.length > 0) {
            sections.push("**Video Settings:**\n" + videoItems.map(item => `• ${item}`).join("\n"));
          }
        }
      }
    
      if (settings.audioFacetSettings) {
        const audioSettings = Object.values(settings.audioFacetSettings)[0] as any;
        if (audioSettings) {
          const audioItems: string[] = [];
          if (audioSettings.audio_record !== undefined)
            audioItems.push(`Recording: ${audioSettings.audio_record ? "On" : "Off"}`);
          if (audioSettings.device_mic_enabled !== undefined)
            audioItems.push(`Microphone: ${audioSettings.device_mic_enabled ? "Enabled" : "Disabled"}`);
          if (audioSettings.device_speaker_enabled !== undefined)
            audioItems.push(
              `Speaker: ${audioSettings.device_speaker_enabled ? "Enabled" : "Disabled"}`
            );
    
          if (audioItems.length > 0) {
            sections.push("**Audio Settings:**\n" + audioItems.map(item => `• ${item}`).join("\n"));
          }
        }
      }
    
      if (settings.deviceSettings) {
        const deviceItems: string[] = [];
        if (settings.deviceSettings.camera_name)
          deviceItems.push(`Name: ${settings.deviceSettings.camera_name}`);
        if (settings.deviceSettings.camera_timezone)
          deviceItems.push(`Timezone: ${settings.deviceSettings.camera_timezone}`);
        if (settings.deviceSettings.led_mode)
          deviceItems.push(`LED Mode: ${settings.deviceSettings.led_mode}`);
        if (settings.deviceSettings.led_intensity !== undefined)
          deviceItems.push(`LED Intensity: ${settings.deviceSettings.led_intensity}`);
        if (settings.deviceSettings.led_stealth_mode !== undefined)
          deviceItems.push(
            `LED Stealth Mode: ${settings.deviceSettings.led_stealth_mode ? "On" : "Off"}`
          );
    
        if (deviceItems.length > 0) {
          sections.push("**Device Settings:**\n" + deviceItems.map(item => `• ${item}`).join("\n"));
        }
      }
    
      return sections.join("\n\n");
    }
  • Helper function that checks if the payload contains settings that depend on camera capabilities (resolution, HDR, WDR, audio recording, mic, speaker).
    function hasCapabilitySensitiveSettings(payload: UpdateCameraConfigPayload): boolean {
      const requestedVideoSettings = Object.values(payload.configUpdate.videoFacetSettings ?? {});
      const hasCapabilityDependentVideoChange = requestedVideoSettings.some(
        settings =>
          settings.resolution !== undefined ||
          settings.hdr_enabled !== undefined ||
          settings.wdr_enabled !== undefined ||
          settings.wdr_strength !== undefined
      );
      const requestedAudioSettings = Object.values(payload.configUpdate.audioFacetSettings ?? {});
      const hasCapabilityDependentAudioChange = requestedAudioSettings.some(
        settings =>
          settings.audio_record !== undefined ||
          settings.device_mic_enabled !== undefined ||
          settings.device_speaker_enabled !== undefined
      );
      return hasCapabilityDependentVideoChange || hasCapabilityDependentAudioChange;
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so the description must disclose all behavioral traits. It reveals that the tool mutates settings and uses a multi-step process with current value display, but lacks information on error handling, idempotency, authorization needs, or potential side effects like camera reboots.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is lengthy and includes a mix of core functionality, specific instructions, and a generic output filtering section. It is front-loaded with the main purpose and structured with bullet points, but some parts like the future support list could be trimmed for conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 8 parameters, a multi-step process, and faceted UUIDs, the description covers entity types, settings categories, process flow, and output filtering. It has an output schema so return values are covered. Missing details on error handling and validation slightly reduce completeness, but overall it is thorough.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, giving a baseline of 3. The description adds significant value beyond the schema, especially for cameraDeviceSettings (exact LED field names, underscore note) and includeFields/filterBy (warning about large responses and usage examples). This enrichment justifies a 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states that the tool updates configuration settings for Rhombus entities, specifically cameras currently, and lists the supported settings. It differentiates from sibling tools by focusing on updates rather than other operations, but does not explicitly contrast with other update tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides step-by-step guidance and specific instructions for LED control, and mentions the faceted UUID default. However, it does not specify when to use this tool versus alternatives, nor does it provide when-not-to-use guidance or prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/RhombusSystems/rhombus-node-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server