From 1dee812ce6022597133a8818c41510aa2596659d Mon Sep 17 00:00:00 2001 From: "Jokob @NetAlertX" <96159884+jokob-sk@users.noreply.github.com> Date: Sun, 7 Dec 2025 11:33:20 +0000 Subject: [PATCH] cryptography build prevention + docs Signed-off-by: GitHub --- Dockerfile | 19 +- docs/API.md | 22 +- docs/API_DBQUERY.md | 10 +- docs/API_DEVICE.md | 2 + docs/API_DEVICES.md | 112 ++++++++- docs/API_EVENTS.md | 68 +++++- docs/API_MCP.md | 326 ++++++++++++++++++++++++++ docs/API_NETTOOLS.md | 9 + mkdocs.yml | 1 + server/api_server/api_server_start.py | 14 +- 10 files changed, 563 insertions(+), 20 deletions(-) create mode 100644 docs/API_MCP.md diff --git a/Dockerfile b/Dockerfile index ef86ecae..cd0a034b 100755 --- a/Dockerfile +++ b/Dockerfile @@ -26,16 +26,25 @@ ENV PATH="/opt/venv/bin:$PATH" # Install build dependencies COPY requirements.txt /tmp/requirements.txt -RUN apk add --no-cache bash shadow python3 python3-dev gcc musl-dev libffi-dev openssl-dev git rust cargo \ +RUN apk add --no-cache \ + bash \ + shadow \ + python3 \ + python3-dev \ + gcc \ + musl-dev \ + libffi-dev \ + openssl-dev \ + git \ + rust \ + cargo \ && python -m venv /opt/venv -# Create virtual environment owned by root, but readable by everyone else. This makes it easy to copy -# into hardened stage without worrying about permissions and keeps image size small. Keeping the commands -# together makes for a slightly smaller image size. +# Upgrade pip/wheel/setuptools and install Python packages RUN python -m pip install --upgrade pip setuptools wheel && \ pip install --no-cache-dir -r /tmp/requirements.txt && \ chmod -R u-rwx,g-rwx /opt - + # second stage is the main runtime stage with just the minimum required to run the application # The runner is used for both devcontainer, and as a base for the hardened stage. FROM alpine:3.22 AS runner diff --git a/docs/API.md b/docs/API.md index 3ad69a96..8a11403d 100755 --- a/docs/API.md +++ b/docs/API.md @@ -36,9 +36,15 @@ Authorization: Bearer If the token is missing or invalid, the server will return: ```json -{ "error": "Forbidden" } +{ + "success": false, + "message": "ERROR: Not authorized", + "error": "Forbidden" +} ``` +HTTP Status: **403 Forbidden** + --- ## Base URL @@ -54,6 +60,8 @@ http://:/ > [!TIP] > When retrieving devices or settings try using the GraphQL API endpoint first as it is read-optimized. +### Standard REST Endpoints + * [Device API Endpoints](API_DEVICE.md) – Manage individual devices * [Devices Collection](API_DEVICES.md) – Bulk operations on multiple devices * [Events](API_EVENTS.md) – Device event logging and management @@ -69,6 +77,18 @@ http://:/ * [Logs](API_LOGS.md) – Purging of logs and adding to the event execution queue for user triggered events * [DB query](API_DBQUERY.md) (⚠ Internal) - Low level database access - use other endpoints if possible +### MCP Server Bridge + +NetAlertX includes an **MCP (Model Context Protocol) Server Bridge** that provides AI assistants access to NetAlertX functionality through standardized tools. MCP endpoints are available at `/mcp/sse/*` paths and mirror the functionality of standard REST endpoints: + +* `/mcp/sse` - Server-Sent Events endpoint for MCP client connections +* `/mcp/sse/openapi.json` - OpenAPI specification for available MCP tools +* `/mcp/sse/device/*`, `/mcp/sse/devices/*`, `/mcp/sse/nettools/*`, `/mcp/sse/events/*` - MCP-enabled versions of REST endpoints + +MCP endpoints require the same Bearer token authentication as REST endpoints. + +**📖 See [MCP Server Bridge API](API_MCP.md) for complete documentation, tool specifications, and integration examples.** + See [Testing](API_TESTS.md) for example requests and usage. --- diff --git a/docs/API_DBQUERY.md b/docs/API_DBQUERY.md index a896d1c7..f93211ac 100755 --- a/docs/API_DBQUERY.md +++ b/docs/API_DBQUERY.md @@ -2,7 +2,7 @@ The **Database Query API** provides direct, low-level access to the NetAlertX database. It allows **read, write, update, and delete** operations against tables, using **base64-encoded** SQL or structured parameters. -> [!Warning] +> [!Warning] > This API is primarily used internally to generate and render the application UI. These endpoints are low-level and powerful, and should be used with caution. Wherever possible, prefer the [standard API endpoints](API.md). Invalid or unsafe queries can corrupt data. > If you need data in a specific format that is not already provided, please open an issue or pull request with a clear, broadly useful use case. This helps ensure new endpoints benefit the wider community rather than relying on raw database queries. @@ -16,10 +16,14 @@ All `/dbquery/*` endpoints require an API token in the HTTP headers: Authorization: Bearer ``` -If the token is missing or invalid: +If the token is missing or invalid (HTTP 403): ```json -{ "error": "Forbidden" } +{ + \"success\": false, + \"message\": \"ERROR: Not authorized\", + \"error\": \"Forbidden\" +} ``` --- diff --git a/docs/API_DEVICE.md b/docs/API_DEVICE.md index de9d283c..99692c3c 100755 --- a/docs/API_DEVICE.md +++ b/docs/API_DEVICE.md @@ -41,6 +41,8 @@ Manage a **single device** by its MAC address. Operations include retrieval, upd * Device not found → HTTP 404 * Unauthorized → HTTP 403 +**MCP Integration**: Available as `get_device_info` and `set_device_alias` tools. See [MCP Server Bridge API](API_MCP.md). + --- ## 2. Update Device Fields diff --git a/docs/API_DEVICES.md b/docs/API_DEVICES.md index 1ea390e1..42fa471c 100755 --- a/docs/API_DEVICES.md +++ b/docs/API_DEVICES.md @@ -170,7 +170,7 @@ The Devices Collection API provides operations to **retrieve, manage, import/exp **Response**: ```json -[ +[ 120, // Total devices 85, // Connected 5, // Favorites @@ -207,6 +207,93 @@ The Devices Collection API provides operations to **retrieve, manage, import/exp --- +### 9. Search Devices + +* **POST** `/devices/search` + Search for devices by MAC, name, or IP address. + +**Request Body** (JSON): + +```json +{ + "query": ".50" +} +``` + +**Response**: + +```json +{ + "success": true, + "devices": [ + { + "devName": "Test Device", + "devMac": "AA:BB:CC:DD:EE:FF", + "devLastIP": "192.168.1.50" + } + ] +} +``` + +--- + +### 10. Get Latest Device + +* **GET** `/devices/latest` + Get the most recently connected device. + +**Response**: + +```json +[ + { + "devName": "Latest Device", + "devMac": "AA:BB:CC:DD:EE:FF", + "devLastIP": "192.168.1.100", + "devFirstConnection": "2025-12-07 10:30:00" + } +] +``` + +--- + +### 11. Get Network Topology + +* **GET** `/devices/network/topology` + Get network topology showing device relationships. + +**Response**: + +```json +{ + "nodes": [ + { + "id": "AA:AA:AA:AA:AA:AA", + "name": "Router", + "vendor": "VendorA" + } + ], + "links": [ + { + "source": "AA:AA:AA:AA:AA:AA", + "target": "BB:BB:BB:BB:BB:BB", + "port": "eth1" + } + ] +} +``` + +--- + +## MCP Tools + +These endpoints are also available as **MCP Tools** for AI assistant integration: +- `list_devices`, `search_devices`, `get_latest_device`, `get_network_topology`, `set_device_alias` + +📖 See [MCP Server Bridge API](API_MCP.md) for AI integration details. + +--- + ## Example `curl` Requests **Get All Devices**: @@ -247,3 +334,26 @@ curl -X GET "http://:/devices/by-status?status=online" -H "Authorization: Bearer " ``` +**Search Devices**: + +```sh +curl -X POST "http://:/devices/search" \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + --data '{"query": "192.168.1"}' +``` + +**Get Latest Device**: + +```sh +curl -X GET "http://:/devices/latest" \ + -H "Authorization: Bearer " +``` + +**Get Network Topology**: + +```sh +curl -X GET "http://:/devices/network/topology" \ + -H "Authorization: Bearer " +``` + diff --git a/docs/API_EVENTS.md b/docs/API_EVENTS.md index c845e10d..ff423c4f 100755 --- a/docs/API_EVENTS.md +++ b/docs/API_EVENTS.md @@ -88,7 +88,56 @@ The Events API provides access to **device event logs**, allowing creation, retr --- -### 4. Event Totals Over a Period +### 4. Get Recent Events + +* **GET** `/events/recent` → Get events from the last 24 hours +* **GET** `/events/` → Get events from the last N hours + +**Response** (JSON): + +```json +{ + "success": true, + "hours": 24, + "count": 5, + "events": [ + { + "eve_DateTime": "2025-12-07 12:00:00", + "eve_EventType": "New Device", + "eve_MAC": "AA:BB:CC:DD:EE:FF", + "eve_IP": "192.168.1.100", + "eve_AdditionalInfo": "Device detected" + } + ] +} +``` + +--- + +### 5. Get Latest Events + +* **GET** `/events/last` + Get the 10 most recent events. + +**Response** (JSON): + +```json +{ + "success": true, + "count": 10, + "events": [ + { + "eve_DateTime": "2025-12-07 12:00:00", + "eve_EventType": "Device Down", + "eve_MAC": "AA:BB:CC:DD:EE:FF" + } + ] +} +``` + +--- + +### 6. Event Totals Over a Period * **GET** `/sessions/totals?period=` Return event and session totals over a given period. @@ -116,12 +165,25 @@ The Events API provides access to **device event logs**, allowing creation, retr --- +## MCP Tools + +Event endpoints are available as **MCP Tools** for AI assistant integration: +- `get_recent_alerts`, `get_last_events` + +📖 See [MCP Server Bridge API](API_MCP.md) for AI integration details. + +--- + ## Notes -* All endpoints require **authorization** (Bearer token). Unauthorized requests return: +* All endpoints require **authorization** (Bearer token). Unauthorized requests return HTTP 403: ```json -{ "error": "Forbidden" } +{ + "success": false, + "message": "ERROR: Not authorized", + "error": "Forbidden" +} ``` * Events are stored in the **Events table** with the following fields: diff --git a/docs/API_MCP.md b/docs/API_MCP.md new file mode 100644 index 00000000..c52cdf39 --- /dev/null +++ b/docs/API_MCP.md @@ -0,0 +1,326 @@ +# MCP Server Bridge API + +The **MCP (Model Context Protocol) Server Bridge** provides AI assistants with standardized access to NetAlertX functionality through tools and server-sent events. This enables AI systems to interact with your network monitoring data in real-time. + +--- + +## Overview + +The MCP Server Bridge exposes NetAlertX functionality as **MCP Tools** that AI assistants can call to: + +- Search and retrieve device information +- Trigger network scans +- Get network topology and events +- Wake devices via Wake-on-LAN +- Access open port information +- Set device aliases + +All MCP endpoints mirror the functionality of standard REST endpoints but are optimized for AI assistant integration. + +--- + +## Authentication + +MCP endpoints use the same **Bearer token authentication** as REST endpoints: + +```http +Authorization: Bearer +``` + +Unauthorized requests return HTTP 403: + +```json +{ + "success": false, + "message": "ERROR: Not authorized", + "error": "Forbidden" +} +``` + +--- + +## MCP Connection Endpoint + +### Server-Sent Events (SSE) + +* **GET/POST** `/mcp/sse` + + Main MCP connection endpoint for AI clients. Establishes a persistent connection using Server-Sent Events for real-time communication between AI assistants and NetAlertX. + +**Connection Example**: + +```javascript +const eventSource = new EventSource('/mcp/sse', { + headers: { + 'Authorization': 'Bearer ' + } +}); + +eventSource.onmessage = function(event) { + const response = JSON.parse(event.data); + console.log('MCP Response:', response); +}; +``` + +--- + +## OpenAPI Specification + +### Get MCP Tools Specification + +* **GET** `/mcp/sse/openapi.json` + + Returns the OpenAPI specification for all available MCP tools, describing the parameters and schemas for each tool. + +**Response**: + +```json +{ + "openapi": "3.0.0", + "info": { + "title": "NetAlertX Tools", + "version": "1.1.0" + }, + "servers": [{"url": "/"}], + "paths": { + "/devices/by-status": { + "post": {"operationId": "list_devices"} + }, + "/device/{mac}": { + "post": {"operationId": "get_device_info"} + }, + "/devices/search": { + "post": {"operationId": "search_devices"} + } + } +} +``` + +--- + +## Available MCP Tools + +### Device Management Tools + +| Tool | Endpoint | Description | +|------|----------|-------------| +| `list_devices` | `/mcp/sse/devices/by-status` | List devices by online status | +| `get_device_info` | `/mcp/sse/device/` | Get detailed device information | +| `search_devices` | `/mcp/sse/devices/search` | Search devices by MAC, name, or IP | +| `get_latest_device` | `/mcp/sse/devices/latest` | Get most recently connected device | +| `set_device_alias` | `/mcp/sse/device//set-alias` | Set device friendly name | + +### Network Tools + +| Tool | Endpoint | Description | +|------|----------|-------------| +| `trigger_scan` | `/mcp/sse/nettools/trigger-scan` | Trigger network discovery scan | +| `get_open_ports` | `/mcp/sse/device/open_ports` | Get stored NMAP open ports for device | +| `wol_wake_device` | `/mcp/sse/nettools/wakeonlan` | Wake device using Wake-on-LAN | +| `get_network_topology` | `/mcp/sse/devices/network/topology` | Get network topology map | + +### Event & Monitoring Tools + +| Tool | Endpoint | Description | +|------|----------|-------------| +| `get_recent_alerts` | `/mcp/sse/events/recent` | Get events from last 24 hours | +| `get_last_events` | `/mcp/sse/events/last` | Get 10 most recent events | + +--- + +## Tool Usage Examples + +### Search Devices Tool + +**Tool Call**: +```json +{ + "jsonrpc": "2.0", + "id": "1", + "method": "tools/call", + "params": { + "name": "search_devices", + "arguments": { + "query": "192.168.1" + } + } +} +``` + +**Response**: +```json +{ + "jsonrpc": "2.0", + "id": "1", + "result": { + "content": [ + { + "type": "text", + "text": "{\n \"success\": true,\n \"devices\": [\n {\n \"devName\": \"Router\",\n \"devMac\": \"AA:BB:CC:DD:EE:FF\",\n \"devLastIP\": \"192.168.1.1\"\n }\n ]\n}" + } + ], + "isError": false + } +} +``` + +### Trigger Network Scan Tool + +**Tool Call**: +```json +{ + "jsonrpc": "2.0", + "id": "2", + "method": "tools/call", + "params": { + "name": "trigger_scan", + "arguments": { + "type": "ARPSCAN" + } + } +} +``` + +**Response**: +```json +{ + "jsonrpc": "2.0", + "id": "2", + "result": { + "content": [ + { + "type": "text", + "text": "{\n \"success\": true,\n \"message\": \"Scan triggered for type: ARPSCAN\"\n}" + } + ], + "isError": false + } +} +``` + +### Wake-on-LAN Tool + +**Tool Call**: +```json +{ + "jsonrpc": "2.0", + "id": "3", + "method": "tools/call", + "params": { + "name": "wol_wake_device", + "arguments": { + "devMac": "AA:BB:CC:DD:EE:FF" + } + } +} +``` + +--- + +## Integration with AI Assistants + +### Claude Desktop Integration + +Add to your Claude Desktop `mcp.json` configuration: + +```json +{ + "mcp": { + "servers": { + "netalertx": { + "command": "node", + "args": ["/path/to/mcp-client.js"], + "env": { + "NETALERTX_URL": "http://your-server:", + "NETALERTX_TOKEN": "your-api-token" + } + } + } + } +} +``` + +### Generic MCP Client + +```python +import asyncio +import json +from mcp import ClientSession, StdioServerParameters +from mcp.client.stdio import stdio_client + +async def main(): + # Connect to NetAlertX MCP server + server_params = StdioServerParameters( + command="curl", + args=[ + "-N", "-H", "Authorization: Bearer ", + "http://your-server:/mcp/sse" + ] + ) + + async with stdio_client(server_params) as (read, write): + async with ClientSession(read, write) as session: + # Initialize connection + await session.initialize() + + # List available tools + tools = await session.list_tools() + print(f"Available tools: {[t.name for t in tools.tools]}") + + # Call a tool + result = await session.call_tool("search_devices", {"query": "router"}) + print(f"Search result: {result}") + +if __name__ == "__main__": + asyncio.run(main()) +``` + +--- + +## Error Handling + +MCP tool calls return structured error information: + +**Error Response**: +```json +{ + "jsonrpc": "2.0", + "id": "1", + "result": { + "content": [ + { + "type": "text", + "text": "Error calling tool: Device not found" + } + ], + "isError": true + } +} +``` + +**Common Error Types**: +- `401/403` - Authentication failure +- `400` - Invalid parameters or missing required fields +- `404` - Resource not found (device, scan results, etc.) +- `500` - Internal server error + +--- + +## Notes + +* MCP endpoints require the same API token authentication as REST endpoints +* All MCP tools return JSON responses wrapped in MCP protocol format +* Server-Sent Events maintain persistent connections for real-time updates +* Tool parameters match their REST endpoint equivalents +* Error responses include both HTTP status codes and descriptive messages +* MCP bridge automatically handles request/response serialization + +--- + +## Related Documentation + +* [Main API Overview](API.md) - Core REST API documentation +* [Device API](API_DEVICE.md) - Individual device management +* [Devices Collection API](API_DEVICES.md) - Bulk device operations +* [Network Tools API](API_NETTOOLS.md) - Wake-on-LAN, scans, network utilities +* [Events API](API_EVENTS.md) - Event logging and monitoring \ No newline at end of file diff --git a/docs/API_NETTOOLS.md b/docs/API_NETTOOLS.md index 629ac984..ba71bbb0 100755 --- a/docs/API_NETTOOLS.md +++ b/docs/API_NETTOOLS.md @@ -241,3 +241,12 @@ curl -X POST "http://:/nettools/nmap" \ curl "http://:/nettools/internetinfo" \ -H "Authorization: Bearer " ``` + +--- + +## MCP Tools + +Network tools are available as **MCP Tools** for AI assistant integration: +- `wol_wake_device`, `trigger_scan`, `get_open_ports` + +📖 See [MCP Server Bridge API](API_MCP.md) for AI integration details. diff --git a/mkdocs.yml b/mkdocs.yml index ba00a943..d3516a8e 100755 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -98,6 +98,7 @@ nav: - Sync: API_SYNC.md - GraphQL: API_GRAPHQL.md - DB query: API_DBQUERY.md + - MCP: API_MCP.md - Tests: API_TESTS.md - SUPERSEDED OLD API Overview: API_OLD.md - Integrations: diff --git a/server/api_server/api_server_start.py b/server/api_server/api_server_start.py index 00b420ad..d9088941 100755 --- a/server/api_server/api_server_start.py +++ b/server/api_server/api_server_start.py @@ -180,7 +180,7 @@ def graphql_endpoint(): if not is_authorized(): msg = '[graphql_server] Unauthorized access attempt - make sure your GRAPHQL_PORT and API_TOKEN settings are correct.' mylog('verbose', [msg]) - return jsonify({"success": False, "message": msg, "error": "Forbidden"}), 401 + return jsonify({"success": False, "message": msg, "error": "Forbidden"}), 403 # Retrieve and log request data data = request.get_json() @@ -301,7 +301,7 @@ def api_device_set_alias(mac): def api_device_open_ports(): """Get stored NMAP open ports for a target IP or MAC.""" if not is_authorized(): - return jsonify({"success": False, "error": "Unauthorized"}), 401 + return jsonify({"success": False, "message": "ERROR: Not authorized", "error": "Forbidden"}), 403 data = request.get_json(silent=True) or {} target = data.get('target') @@ -393,7 +393,7 @@ def api_devices_by_status(): def api_devices_search(): """Device search: accepts 'query' in JSON and maps to device info/search.""" if not is_authorized(): - return jsonify({"error": "Unauthorized"}), 401 + return jsonify({"success": False, "message": "ERROR: Not authorized", "error": "Forbidden"}), 403 data = request.get_json(silent=True) or {} query = data.get('query') @@ -424,7 +424,7 @@ def api_devices_search(): def api_devices_latest(): """Get latest device (most recent) - maps to DeviceInstance.getLatest().""" if not is_authorized(): - return jsonify({"error": "Unauthorized"}), 401 + return jsonify({"success": False, "message": "ERROR: Not authorized", "error": "Forbidden"}), 403 device_handler = DeviceInstance() @@ -440,7 +440,7 @@ def api_devices_latest(): def api_devices_network_topology(): """Network topology mapping.""" if not is_authorized(): - return jsonify({"error": "Unauthorized"}), 401 + return jsonify({"success": False, "message": "ERROR: Not authorized", "error": "Forbidden"}), 403 device_handler = DeviceInstance() @@ -564,7 +564,7 @@ def api_trigger_scan(): @app.route('/mcp/sse/openapi.json', methods=['GET']) def api_openapi_spec(): if not is_authorized(): - return jsonify({"success": False, "error": "Unauthorized"}), 401 + return jsonify({"success": False, "message": "ERROR: Not authorized", "error": "Forbidden"}), 403 return openapi_spec() @@ -786,7 +786,7 @@ def api_events_recent(hours): """Return events from the last hours using EventInstance.""" if not is_authorized(): - return jsonify({"success": False, "error": "Unauthorized"}), 401 + return jsonify({"success": False, "message": "ERROR: Not authorized", "error": "Forbidden"}), 403 # Validate hours input if hours <= 0: