feat: add inventory, hidden-device and restore MCP tools
The status filter on list_pending_devices closed the leak but left the
MCP surface unable to see anything beyond pending: approved inventory
and hidden devices were no longer reachable, and a wrongly hidden device
could not be recovered through the MCP at all.
Add three tools mirroring existing backend endpoints:
- list_inventory GET /scan/pending (pending + approved, optional status filter)
- list_hidden_devices GET /scan/hidden
- restore_device POST /scan/pending/{id}/restore (undo hide_device)
Bulk approve/hide/restore and scan-config endpoints are intentionally
left out: bulk mutation is the same blast radius that caused the mass-hide
incident this PR addresses.
Tests: 5 new (47 passed total).
This commit is contained in:
@@ -134,6 +134,21 @@ def _build_tools() -> list[Tool]:
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
}),
|
||||
Tool(name="list_inventory", description="List the full device inventory (everything scanned except user-hidden devices): both pending devices awaiting triage and already-approved devices. Each row carries a 'status' field. Use the optional 'status' filter to narrow the result.", inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {"type": "string", "enum": ["all", "pending", "approved"], "default": "all", "description": "Filter inventory by status. 'all' returns pending + approved."},
|
||||
},
|
||||
}),
|
||||
Tool(name="list_hidden_devices", description="List devices the user has hidden from the inventory. Hidden devices are excluded from list_pending_devices and list_inventory; use restore_device to bring one back to pending.", inputSchema={
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
}),
|
||||
Tool(name="restore_device", description="Restore (un-hide) a previously hidden device, returning it to pending status so it reappears in the triage list. Use this to undo a hide_device action.", inputSchema={
|
||||
"type": "object",
|
||||
"required": ["id"],
|
||||
"properties": {"id": {"type": "string"}},
|
||||
}),
|
||||
Tool(name="list_designs", description="List all designs (canvases) with their IDs and node/group/text counts", inputSchema={
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
@@ -235,6 +250,21 @@ async def _dispatch(name: str, args: dict) -> dict:
|
||||
devices = await backend.get("/api/v1/scan/pending")
|
||||
return [d for d in devices if d.get("status", "pending") == "pending"]
|
||||
|
||||
if name == "list_inventory":
|
||||
# /scan/pending returns the whole inventory minus hidden rows (pending +
|
||||
# approved). Legacy rows without a status field count as pending.
|
||||
devices = await backend.get("/api/v1/scan/pending")
|
||||
wanted = args.get("status", "all")
|
||||
if wanted == "all":
|
||||
return devices
|
||||
return [d for d in devices if d.get("status", "pending") == wanted]
|
||||
|
||||
if name == "list_hidden_devices":
|
||||
return await backend.get("/api/v1/scan/hidden")
|
||||
|
||||
if name == "restore_device":
|
||||
return await backend.post(f"/api/v1/scan/pending/{args['id']}/restore", {})
|
||||
|
||||
if name == "list_designs":
|
||||
return await backend.get("/api/v1/designs")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user