mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-04-04 09:11:34 -07:00
Merge pull request #1484 from adamoutler/agentic-workflows
MCP Enhancements: Named Totals, OpenAPI Resource, and Dev Skills
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: devcontainer-management
|
name: devcontainer-management
|
||||||
description: Guide for identifying, managing, and running commands within the NetAlertX development container. Use this when asked to run backend logic, setup scripts, or troubleshoot container issues.
|
description: Guide for identifying, managing, and running commands within the NetAlertX development container. Use this when asked to run commands, testing, setup scripts, or troubleshoot container issues.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Devcontainer Management
|
# Devcontainer Management
|
||||||
|
|||||||
52
.gemini/skills/mcp-activation/SKILL.md
Normal file
52
.gemini/skills/mcp-activation/SKILL.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
name: mcp-activation
|
||||||
|
description: Enables live interaction with the NetAlertX runtime. This skill configures the Model Context Protocol (MCP) connection, granting full API access for debugging, troubleshooting, and real-time operations including database queries, network scans, and device management.
|
||||||
|
---
|
||||||
|
|
||||||
|
# MCP Activation Skill
|
||||||
|
|
||||||
|
This skill configures the NetAlertX development environment to expose the Model Context Protocol (MCP) server to AI agents.
|
||||||
|
|
||||||
|
## Why use this?
|
||||||
|
|
||||||
|
By default, agents only have access to the static codebase (files). To perform dynamic actions—such as:
|
||||||
|
- **Querying the database** (e.g., getting device lists, events)
|
||||||
|
- **Triggering actions** (e.g., network scans, Wake-on-LAN)
|
||||||
|
- **Validating runtime state** (e.g., checking if a fix actually works)
|
||||||
|
|
||||||
|
...you need access to the **MCP Server** running inside the container. This skill sets up the necessary authentication tokens and connection configs to bridge your agent to that live server.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. **Devcontainer:** You must be connected to the NetAlertX devcontainer.
|
||||||
|
2. **Server Running:** The backend server must be running (to generate `app.conf` with the API token).
|
||||||
|
|
||||||
|
## Activation Steps
|
||||||
|
|
||||||
|
1. **Activate Devcontainer Skill:**
|
||||||
|
If you are not already inside the container, activate the management skill:
|
||||||
|
```text
|
||||||
|
activate_skill("devcontainer-management")
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Generate Configurations:**
|
||||||
|
Run the configuration generation script *inside* the container. This script extracts the API Token and creates the necessary settings files (`.gemini/settings.json` and `.vscode/mcp.json`).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run inside the container
|
||||||
|
/workspaces/NetAlertX/.devcontainer/scripts/generate-configs.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Apply Changes:**
|
||||||
|
|
||||||
|
* **For Gemini CLI:**
|
||||||
|
The agent session must be **restarted** to load the new `.gemini/settings.json`.
|
||||||
|
> "I have generated the MCP configuration. Please **restart this session** to activate the `netalertx-devcontainer` tools."
|
||||||
|
|
||||||
|
* **For VS Code (GitHub Copilot / Cline):**
|
||||||
|
The VS Code window must be **reloaded** to pick up the new `.vscode/mcp.json`.
|
||||||
|
> "I have generated the MCP configuration. Please run **'Developer: Reload Window'** in VS Code to activate the MCP server."
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
After restarting, you should see new tools available (e.g., `netalertx-devcontainer__get_devices`).
|
||||||
34
.github/skills/mcp-activation/SKILL.md
vendored
Normal file
34
.github/skills/mcp-activation/SKILL.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
name: mcp-activation
|
||||||
|
description: Enables live interaction with the NetAlertX runtime. This skill configures the Model Context Protocol (MCP) connection, granting full API access for debugging, troubleshooting, and real-time operations including database queries, network scans, and device management.
|
||||||
|
---
|
||||||
|
|
||||||
|
# MCP Activation Skill
|
||||||
|
|
||||||
|
This skill configures the environment to expose the Model Context Protocol (MCP) server to AI agents running inside the devcontainer.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
This skill assumes you are already running within the NetAlertX devcontainer.
|
||||||
|
|
||||||
|
1. **Generate Configurations:**
|
||||||
|
Run the configuration generation script to extract the API Token and update the VS Code MCP settings.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/workspaces/NetAlertX/.devcontainer/scripts/generate-configs.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Reload Window:**
|
||||||
|
Request the user to reload the VS Code window to activate the new tools.
|
||||||
|
> I have generated the MCP configuration. Please run the **'Developer: Reload Window'** command to activate the MCP server tools.
|
||||||
|
> In VS Code: open the Command Palette (Windows/Linux: Ctrl+Shift+P, macOS: Cmd+Shift+P), type Developer: Reload Window, press Enter — or click the Reload button if a notification appears. 🔁
|
||||||
|
> After you reload, tell me “Window reloaded” (or just “reloaded”) and I’ll continue.
|
||||||
|
|
||||||
|
|
||||||
|
## Why use this?
|
||||||
|
|
||||||
|
Access the live runtime API to perform operations that are not possible through static file analysis:
|
||||||
|
- **Query the database**
|
||||||
|
- **Trigger network scans**
|
||||||
|
- **Manage devices and events**
|
||||||
|
- **Troubleshoot real-time system state**
|
||||||
@@ -73,6 +73,7 @@ from .openapi.schemas import ( # noqa: E402 [flake8 lint suppression]
|
|||||||
DeviceInfo,
|
DeviceInfo,
|
||||||
BaseResponse, DeviceTotalsResponse,
|
BaseResponse, DeviceTotalsResponse,
|
||||||
DeviceTotalsNamedResponse,
|
DeviceTotalsNamedResponse,
|
||||||
|
EventsTotalsNamedResponse,
|
||||||
DeleteDevicesRequest, DeviceImportRequest,
|
DeleteDevicesRequest, DeviceImportRequest,
|
||||||
DeviceImportResponse, UpdateDeviceColumnRequest,
|
DeviceImportResponse, UpdateDeviceColumnRequest,
|
||||||
LockDeviceFieldRequest, UnlockDeviceFieldsRequest,
|
LockDeviceFieldRequest, UnlockDeviceFieldsRequest,
|
||||||
@@ -1509,8 +1510,8 @@ def api_delete_old_events(days: int, payload=None):
|
|||||||
@app.route("/sessions/totals", methods=["GET"])
|
@app.route("/sessions/totals", methods=["GET"])
|
||||||
@validate_request(
|
@validate_request(
|
||||||
operation_id="get_events_totals",
|
operation_id="get_events_totals",
|
||||||
summary="Get Events Totals",
|
summary="Get Events Totals (Deprecated)",
|
||||||
description="Retrieve event totals for a specified period.",
|
description="Retrieve event totals for a specified period. Deprecated: use /sessions/totals/named instead.",
|
||||||
query_params=[{
|
query_params=[{
|
||||||
"name": "period",
|
"name": "period",
|
||||||
"description": "Time period (e.g., '7 days')",
|
"description": "Time period (e.g., '7 days')",
|
||||||
@@ -1527,6 +1528,37 @@ def api_get_events_totals(payload=None):
|
|||||||
return jsonify(totals)
|
return jsonify(totals)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/sessions/totals/named", methods=["GET"])
|
||||||
|
@validate_request(
|
||||||
|
operation_id="get_events_totals_named",
|
||||||
|
summary="Get Named Event Totals",
|
||||||
|
description="Retrieve event/session totals with named fields for a specified period.",
|
||||||
|
query_params=[{
|
||||||
|
"name": "period",
|
||||||
|
"description": "Time period (e.g., '7 days')",
|
||||||
|
"required": False,
|
||||||
|
"schema": {"type": "string", "default": "7 days"}
|
||||||
|
}],
|
||||||
|
response_model=EventsTotalsNamedResponse,
|
||||||
|
tags=["events"],
|
||||||
|
auth_callable=is_authorized
|
||||||
|
)
|
||||||
|
def api_get_events_totals_named(payload=None):
|
||||||
|
period = request.args.get("period", "7 days")
|
||||||
|
event_handler = EventInstance()
|
||||||
|
totals = event_handler.getEventsTotals(period)
|
||||||
|
# totals order: [all_events, sessions, missing, voided, new, down]
|
||||||
|
totals_dict = {
|
||||||
|
"total": totals[0] if len(totals) > 0 else 0,
|
||||||
|
"sessions": totals[1] if len(totals) > 1 else 0,
|
||||||
|
"missing": totals[2] if len(totals) > 2 else 0,
|
||||||
|
"voided": totals[3] if len(totals) > 3 else 0,
|
||||||
|
"new": totals[4] if len(totals) > 4 else 0,
|
||||||
|
"down": totals[5] if len(totals) > 5 else 0
|
||||||
|
}
|
||||||
|
return jsonify({"success": True, "totals": totals_dict})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/events/recent', methods=['GET', 'POST'])
|
@app.route('/events/recent', methods=['GET', 'POST'])
|
||||||
@validate_request(
|
@validate_request(
|
||||||
operation_id="get_recent_events",
|
operation_id="get_recent_events",
|
||||||
|
|||||||
@@ -795,8 +795,17 @@ def get_log_dir() -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _list_resources() -> List[Dict[str, Any]]:
|
def _list_resources() -> List[Dict[str, Any]]:
|
||||||
"""List available MCP resources (read-only data like logs)."""
|
"""List available MCP resources (read-only data like logs and API spec)."""
|
||||||
resources = []
|
resources = []
|
||||||
|
|
||||||
|
# API Specification
|
||||||
|
resources.append({
|
||||||
|
"uri": "netalertx://api/openapi.json",
|
||||||
|
"name": "OpenAPI Specification",
|
||||||
|
"description": "The full OpenAPI 3.1 specification for the NetAlertX API and MCP tools",
|
||||||
|
"mimeType": "application/json"
|
||||||
|
})
|
||||||
|
|
||||||
log_dir = get_log_dir()
|
log_dir = get_log_dir()
|
||||||
if not log_dir:
|
if not log_dir:
|
||||||
return resources
|
return resources
|
||||||
@@ -840,6 +849,16 @@ def _list_resources() -> List[Dict[str, Any]]:
|
|||||||
|
|
||||||
def _read_resource(uri: str) -> List[Dict[str, Any]]:
|
def _read_resource(uri: str) -> List[Dict[str, Any]]:
|
||||||
"""Read a resource by URI."""
|
"""Read a resource by URI."""
|
||||||
|
# Handle API Specification
|
||||||
|
if uri == "netalertx://api/openapi.json":
|
||||||
|
from flask import current_app
|
||||||
|
spec = get_openapi_spec(flask_app=current_app)
|
||||||
|
return [{
|
||||||
|
"uri": uri,
|
||||||
|
"mimeType": "application/json",
|
||||||
|
"text": json.dumps(spec, indent=2)
|
||||||
|
}]
|
||||||
|
|
||||||
log_dir = get_log_dir()
|
log_dir = get_log_dir()
|
||||||
if not log_dir:
|
if not log_dir:
|
||||||
return [{"uri": uri, "text": "Error: NETALERTX_LOG directory not configured"}]
|
return [{"uri": uri, "text": "Error: NETALERTX_LOG directory not configured"}]
|
||||||
|
|||||||
@@ -318,6 +318,24 @@ class DeviceTotalsNamedResponse(BaseResponse):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class EventsTotalsNamedResponse(BaseResponse):
|
||||||
|
"""Response with named event/session statistics."""
|
||||||
|
totals: Dict[str, int] = Field(
|
||||||
|
...,
|
||||||
|
description="Dictionary of counts: total, sessions, missing, voided, new, down",
|
||||||
|
json_schema_extra={
|
||||||
|
"examples": [{
|
||||||
|
"total": 100,
|
||||||
|
"sessions": 50,
|
||||||
|
"missing": 0,
|
||||||
|
"voided": 0,
|
||||||
|
"new": 5,
|
||||||
|
"down": 2
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeviceExportRequest(BaseModel):
|
class DeviceExportRequest(BaseModel):
|
||||||
"""Request for exporting devices."""
|
"""Request for exporting devices."""
|
||||||
format: Literal["csv", "json"] = Field(
|
format: Literal["csv", "json"] = Field(
|
||||||
|
|||||||
Reference in New Issue
Block a user