feat(api): MCP, OpenAPI & Dynamic Introspection

New Features:
- API endpoints now support comprehensive input validation with detailed error responses via Pydantic models.
- OpenAPI specification endpoint (/openapi.json) and interactive Swagger UI documentation (/docs) now available for API discovery.
- Enhanced MCP session lifecycle management with create, retrieve, and delete operations.
- Network diagnostic tools: traceroute, nslookup, NMAP scanning, and network topology viewing exposed via API.
- Device search, filtering by status (including 'offline'), and bulk operations (copy, delete, update).
- Wake-on-LAN functionality for remote device management.
- Added dynamic tool disablement and status reporting.

Bug Fixes:
- Fixed get_tools_status in registry to correctly return boolean values instead of None for enabled tools.
- Improved error handling for invalid API inputs with standardized validation responses.
- Fixed OPTIONS request handling for cross-origin requests.

Refactoring:
- Significant refactoring of api_server_start.py to use decorator-based validation (@validate_request).
This commit is contained in:
Adam Outler
2026-01-18 18:16:18 +00:00
parent cea3369b5e
commit ecea1d1fbd
46 changed files with 5195 additions and 1053 deletions

View File

@@ -49,7 +49,11 @@ def test_dbquery_create_device(client, api_token, test_mac):
INSERT INTO Devices (devMac, devName, devVendor, devOwner, devFirstConnection, devLastConnection, devLastIP)
VALUES ('{test_mac}', 'UnitTestDevice', 'TestVendor', 'UnitTest', '{now}', '{now}', '192.168.100.22' )
"""
resp = client.post("/dbquery/write", json={"rawSql": b64(sql)}, headers=auth_headers(api_token))
resp = client.post(
"/dbquery/write",
json={"rawSql": b64(sql), "confirm_dangerous_query": True},
headers=auth_headers(api_token)
)
print(resp.json)
print(resp)
assert resp.status_code == 200
@@ -59,7 +63,11 @@ def test_dbquery_create_device(client, api_token, test_mac):
def test_dbquery_read_device(client, api_token, test_mac):
sql = f"SELECT * FROM Devices WHERE devMac = '{test_mac}'"
resp = client.post("/dbquery/read", json={"rawSql": b64(sql)}, headers=auth_headers(api_token))
resp = client.post(
"/dbquery/read",
json={"rawSql": b64(sql), "confirm_dangerous_query": True},
headers=auth_headers(api_token)
)
assert resp.status_code == 200
assert resp.json.get("success") is True
results = resp.json.get("results")
@@ -72,27 +80,43 @@ def test_dbquery_update_device(client, api_token, test_mac):
SET devName = 'UnitTestDeviceRenamed'
WHERE devMac = '{test_mac}'
"""
resp = client.post("/dbquery/write", json={"rawSql": b64(sql)}, headers=auth_headers(api_token))
resp = client.post(
"/dbquery/write",
json={"rawSql": b64(sql), "confirm_dangerous_query": True},
headers=auth_headers(api_token)
)
assert resp.status_code == 200
assert resp.json.get("success") is True
assert resp.json.get("affected_rows") == 1
# Verify update
sql_check = f"SELECT devName FROM Devices WHERE devMac = '{test_mac}'"
resp2 = client.post("/dbquery/read", json={"rawSql": b64(sql_check)}, headers=auth_headers(api_token))
resp2 = client.post(
"/dbquery/read",
json={"rawSql": b64(sql_check), "confirm_dangerous_query": True},
headers=auth_headers(api_token)
)
assert resp2.status_code == 200
assert resp2.json.get("results")[0]["devName"] == "UnitTestDeviceRenamed"
def test_dbquery_delete_device(client, api_token, test_mac):
sql = f"DELETE FROM Devices WHERE devMac = '{test_mac}'"
resp = client.post("/dbquery/write", json={"rawSql": b64(sql)}, headers=auth_headers(api_token))
resp = client.post(
"/dbquery/write",
json={"rawSql": b64(sql), "confirm_dangerous_query": True},
headers=auth_headers(api_token)
)
assert resp.status_code == 200
assert resp.json.get("success") is True
assert resp.json.get("affected_rows") == 1
# Verify deletion
sql_check = f"SELECT * FROM Devices WHERE devMac = '{test_mac}'"
resp2 = client.post("/dbquery/read", json={"rawSql": b64(sql_check)}, headers=auth_headers(api_token))
resp2 = client.post(
"/dbquery/read",
json={"rawSql": b64(sql_check), "confirm_dangerous_query": True},
headers=auth_headers(api_token)
)
assert resp2.status_code == 200
assert resp2.json.get("results") == []