update_device_fingerprint_preview
Preview the result of updating a device fingerprint without saving. Applies top-level overwrite and capabilities one-level overwrite merge rules to return the would-be fingerprint.
Instructions
Preview the merge result of update_device_fingerprint without persisting. Returns the would-be merged fingerprint dict using the same merge rules: top-level overwrite + capabilities one-level overwrite (incoming capability keys replace stored entries entirely; not recursive). Read-only — no DB write, no last_seen or updated_at mutation. Phase 38 D-05c.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| hostname | Yes | Hostname of the device to preview-fingerprint | |
| fingerprint | Yes | Same shape as update_device_fingerprint.fingerprint. Recognized top-level keys: kernel_name, kernel_version, os_name, os_version, package_fingerprint, capabilities. |
Implementation Reference
- Main handler function for update_device_fingerprint_preview. A read-only thin wrapper around merge_fingerprint: fetches device, computes would-be merge result, returns it without persisting. Validates hostname, normalizes fingerprint input, retrieves current stored fingerprint, and returns the merged result with preview=True.
async def handle_update_device_fingerprint_preview(arguments: dict[str, Any]) -> dict[str, Any]: """Handle update_device_fingerprint_preview tool (Phase 38 D-05c, Plan 05). Read-only thin wrapper around merge_fingerprint: fetches the device row via get_all_devices, computes the would-be merge result, and returns it WITHOUT persisting. The adapter's update_device_fingerprint method is NOT called. Mirrors the validation/filtering of handle_update_device_fingerprint so the preview shape exactly matches what the real call would write. Side-effect contract (Phase 43 WR-03 clarification): * Preview path (this function): pure read — no DB write, no last_seen mutation, no updated_at mutation. Safe to call repeatedly. * Persist path (handle_update_device_fingerprint → adapter.update_device_fingerprint): bumps updated_at on the device row; preserves last_seen as set by the most recent discover_and_map run (Phase 38 REVIEW-FIX commit f53365c). Consumers of analyze_network_topology row ordering (which keys off last_seen) are NOT disturbed by fingerprint updates. """ from ..database import merge_fingerprint # local import — avoids circular issues RECOGNIZED_TOP_LEVEL = { "kernel_name", "kernel_version", "os_name", "os_version", "package_fingerprint", "capabilities", } validate_hostname(arguments["hostname"]) fp_in = arguments.get("fingerprint", {}) if not isinstance(fp_in, dict): result_str = json.dumps( { "status": "error", "error": f"`fingerprint` must be an object (got {type(fp_in).__name__})", "hint": "Provide fingerprint as a JSON object with recognized top-level keys.", } ) return {"content": [{"type": "text", "text": result_str}]} cleaned = {k: v for k, v in fp_in.items() if k in RECOGNIZED_TOP_LEVEL} sitemap = NetworkSiteMap() devices = sitemap.db_adapter.get_all_devices() target = next((d for d in devices if d.get("hostname") == arguments["hostname"]), None) if target is None: result_str = json.dumps( { "status": "error", "error": f"Hostname not in sitemap: {arguments['hostname']!r}.", "hint": "Run discover_and_map for this hostname first to add it to the sitemap.", } ) return {"content": [{"type": "text", "text": result_str}]} stored = target.get("fingerprint") or {} if isinstance(stored, str): # Some adapters may still surface a JSON string; normalize to dict before merge. try: stored = json.loads(stored) except json.JSONDecodeError: stored = {} if not isinstance(stored, dict): stored = {} merged = merge_fingerprint(stored, cleaned) result_str = json.dumps( { "status": "success", "hostname": arguments["hostname"], "fingerprint": merged, "preview": True, }, indent=2, ) return {"content": [{"type": "text", "text": result_str}]} - Input schema definition for the tool. Defines hostname (string, required) and fingerprint (object, required) with recognized top-level keys: kernel_name, kernel_version, os_name, os_version, package_fingerprint, capabilities.
"update_device_fingerprint_preview": { "description": ( "Preview the merge result of update_device_fingerprint without persisting. " "Returns the would-be merged fingerprint dict using the same merge rules: " "top-level overwrite + capabilities one-level overwrite (incoming capability " "keys replace stored entries entirely; not recursive). Read-only — no DB " "write, no last_seen or updated_at mutation. Phase 38 D-05c." ), "inputSchema": { "type": "object", "properties": { "hostname": { "type": "string", "description": "Hostname of the device to preview-fingerprint", }, "fingerprint": { "type": "object", "description": ( "Same shape as update_device_fingerprint.fingerprint. Recognized top-level " "keys: kernel_name, kernel_version, os_name, os_version, package_fingerprint, " "capabilities." ), "properties": { "kernel_name": {"type": "string"}, "kernel_version": {"type": "string"}, "os_name": {"type": "string"}, "os_version": {"type": "string"}, "package_fingerprint": {"type": "string"}, "capabilities": {"type": "object", "additionalProperties": True}, }, "additionalProperties": False, }, }, "required": ["hostname", "fingerprint"], }, }, } - src/homelab_mcp/tool_handlers/__init__.py:36-101 (registration)Import of handle_update_device_fingerprint_preview from network_handlers.
handle_update_device_fingerprint_preview, # Phase 38 D-05c (Plan 05) ) from .proxmox_handlers import ( handle_clone_proxmox_vm, handle_create_proxmox_lxc, handle_create_proxmox_vm, handle_delete_proxmox_vm, handle_delete_proxmox_vm_preview, handle_get_proxmox_node_status, handle_get_proxmox_script_info, handle_get_proxmox_vm_status, handle_list_proxmox_resources, handle_manage_proxmox_vm, handle_search_proxmox_scripts, ) from .service_handlers import ( handle_check_ansible_service, handle_check_service_requirements, handle_destroy_terraform_service, handle_destroy_terraform_service_preview, handle_get_service_info, handle_get_service_status, handle_install_service, handle_list_available_services, handle_plan_terraform_service, handle_refresh_terraform_service, handle_run_ansible_playbook, ) from .ssh_handlers import ( handle_ssh_discover, handle_ssh_execute_command, handle_start_interactive_shell, ) from .vm_handlers import ( handle_control_vm, handle_deploy_vm, handle_get_vm_logs, handle_get_vm_status, handle_list_vms, handle_remove_vm, handle_remove_vm_preview, ) # Type alias for handler functions ToolHandler = Callable[[dict[str, Any]], Awaitable[dict[str, Any]]] # Tool handler registry mapping tool names to their handler functions TOOL_HANDLERS: dict[str, ToolHandler] = { # SSH tools "ssh_discover": handle_ssh_discover, "ssh_execute_command": handle_ssh_execute_command, "start_interactive_shell": handle_start_interactive_shell, # Network tools "discover_and_map": handle_discover_and_map, "bulk_discover_and_map": handle_bulk_discover_and_map, "get_network_sitemap": handle_get_network_sitemap, "analyze_network_topology": handle_analyze_network_topology, "suggest_deployments": handle_suggest_deployments, "get_device_changes": handle_get_device_changes, "purge_failed_discoveries": handle_purge_failed_discoveries, "purge_devices": handle_purge_devices, "purge_devices_preview": handle_purge_devices_preview, "remove_device": handle_remove_device, "remove_device_preview": handle_remove_device_preview, "update_device_fingerprint": handle_update_device_fingerprint, # Phase 38 "update_device_fingerprint_preview": handle_update_device_fingerprint_preview, # Phase 38 D-05c (Plan 05) - src/homelab_mcp/tool_handlers/__init__.py:101-101 (registration)Registration of 'update_device_fingerprint_preview' tool name to its handler function in the TOOL_HANDLERS registry.
"update_device_fingerprint_preview": handle_update_device_fingerprint_preview, # Phase 38 D-05c (Plan 05) - Listed as a read-only tool (readOnlyHint=True) in tool_annotations, confirming it is a non-destructive preview tool.
"update_device_fingerprint_preview", # Phase 38 D-05c (Plan 05) "remove_device_preview", # Phase 44 D-11 "purge_devices_preview", # Phase 44 D-11