Skip to main content
Glama
README.md
# Network Incident MCP Demo

A small demo/prototype showing an LLM agent (Google Gemini) using the **Model Context Protocol (MCP)** to triage a simulated network incident: fetch device telemetry, inspect syslogs, decide whether action is needed, and execute a remediation step - all through tools exposed by a local MCP server.

This is a **prototype for learning/demo purposes**. All network data is fake and hardcoded in `mcp_server/mock_network_db.py`. There is no real network backend.

## How it works

- **`mcp_server/`** - an MCP server exposing:
  - `get_device_telemetry(device_id)` - device status, optical power, BGP flaps
  - `get_latest_syslog()` - recent syslog lines
  - `apply_traffic_reroute(source_pop, target_pop, circuit_id)` - a mock remediation action (rejects any target that isn't a known, healthy PoP)
  - `incident_triage_prompt(device_id)` - a prompt template describing the triage steps and the -20.0 dBm reroute threshold
- **`agent/gemini_mcp_client.py`** - a client that spawns the MCP server, hands its tools to Gemini as function-calling tools, and runs a loop: Gemini decides which tool to call next, the client executes it via MCP and feeds the result back, until Gemini gives a final answer.

## Setup

```bash
git clone <your-repo-url>
cd network-incident-mcp
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```

## Running the tests

```bash
python3 -m pytest tests/
```

## Running the MCP server standalone

```bash
python3 mcp_server/server.py
```

## Running the live agent

Requires a Gemini API key from [Google AI Studio](https://aistudio.google.com/apikey):

```bash
export GEMINI_API_KEY=your-key-here
python3 agent/gemini_mcp_client.py
```

### Example output

The agent is pointed at the mock device `router-van-01`, which is simulated as `DEGRADED` with optical power at -28.5 dBm - below the -20.0 dBm reroute threshold. A real run looks like this:

```
(venv) ac@ubuntuserver2026:~/network-incident-mcp$ python3 agent/gemini_mcp_client.py
Processing request of type ListToolsRequest
[+] Connected to MCP. Registered 3 tools with Gemini 3.6 Flash.
Processing request of type GetPromptRequest
[--> Model Tool Call Request]: get_device_telemetry({'device_id': 'router-van-01'})
Processing request of type CallToolRequest
[<-- MCP Execution Output]: {
  "device_id": "router-van-01",
  "data": {
    "status": "DEGRADED",
    "location": "Vancouver PoP",
    "bgp_flaps": 14,
    "optical_power_dbm": -28.5,
    "circuits": [
      "fiber-van-richmond-10G"
    ]
  }
}
[--> Model Tool Call Request]: get_latest_syslog({})
Processing request of type CallToolRequest
[<-- MCP Execution Output]:
[2026-08-24T10:12:01Z] WARN router-van-01 BGP neighbor 192.168.10.1 DOWN (Retries: 3)
[2026-08-24T10:12:15Z] CRIT router-van-01 Optical signal loss detected on fiber-van-richmond-10G (-28.5 dBm)
[2026-08-24T10:14:02Z] WARN router-van-01 Packet loss spike: 18.4% on interface ge-0/0/1

[--> Model Tool Call Request]: apply_traffic_reroute({'target_pop': 'Richmond PoP', 'circuit_id': 'fiber-van-richmond-10G', 'source_pop': 'Vancouver PoP'})
Processing request of type CallToolRequest
[<-- MCP Execution Output]: {
  "status": "ERROR",
  "error": "Target PoP 'Richmond PoP' is not a known healthy PoP. Healthy PoPs: ['Toronto PoP']"
}
[--> Model Tool Call Request]: apply_traffic_reroute({'circuit_id': 'fiber-van-richmond-10G', 'target_pop': 'Toronto PoP', 'source_pop': 'Vancouver PoP'})
Processing request of type CallToolRequest
[<-- MCP Execution Output]: {
  "status": "SUCCESS",
  "action": "Traffic shifted from Vancouver PoP to Toronto PoP",
  "isolated_circuit": "fiber-van-richmond-10G",
  "new_route_metric": 10
}
[Model Final Response]: ### Incident Analysis Summary for `router-van-01`

1. **Device Telemetry Analysis**
   - **Device ID:** `router-van-01`
   - **Location:** Vancouver PoP
   - **Device Status:** DEGRADED
   - **Optical Power Level:** **-28.5 dBm**
   - **BGP Flaps:** 14
   - **Active Circuit:** `fiber-van-richmond-10G`

2. **Syslog Inspection**
   - Critical optical signal loss log detected:
     `[2026-08-24T10:12:15Z] CRIT router-van-01 Optical signal loss detected on fiber-van-richmond-10G (-28.5 dBm)`
   - Additional warnings observed regarding BGP neighbor timeouts and interface packet loss spikes (`ge-0/0/1`).

3. **Remediation Action**
   - **Condition Met:** Optical power level (-28.5 dBm) is below the threshold of -20.0 dBm.
   - **Action Taken:** Executed `apply_traffic_reroute`.
     - **Circuit:** `fiber-van-richmond-10G`
     - **Source PoP:** Vancouver PoP
     - **Target PoP:** Toronto PoP
   - **Status:** **SUCCESS** - Traffic successfully rerouted from Vancouver PoP to Toronto PoP, isolating circuit `fiber-van-richmond-10G`.
```

Note the model's first reroute attempt targets `Richmond PoP`, which isn't a real, healthy location in the mock data - the server rejects it with an `ERROR` instead of silently succeeding, and the model retries with the correct healthy target (`Toronto PoP`) before succeeding.

## Public HTTP demo (no API key needed)

`web/app.py` is a small FastAPI wrapper around the same mock telemetry/reroute
logic, exposed as plain REST endpoints - no MCP client and no Gemini API key
required. `/triage/{device_id}` reimplements the fetch → check → reroute flow
deterministically in Python (not via an LLM), so it's free and safe to expose
publicly.

Run it locally:

```bash
uvicorn web.app:app --host 127.0.0.1 --port 8001
```

Then, from another terminal:

```bash
curl http://127.0.0.1:8001/devices
curl http://127.0.0.1:8001/telemetry/router-van-01
curl http://127.0.0.1:8001/syslog
curl http://127.0.0.1:8001/triage/router-van-01   # degraded device -> auto-reroutes
curl http://127.0.0.1:8001/triage/router-yyz-02   # healthy device -> no reroute
```

## Notes

- Requires `mcp<2` (already pinned in `requirements.txt`) - the server uses the v1 `FastMCP` API.
- Gemini model availability changes over time; if you hit a `404` model-not-found error, list the models available to your API key and update the model name in `agent/gemini_mcp_client.py`.
- Tool-calling rounds are capped per run (`GeminiMCPOrchestrator.MAX_TURNS`, default 5) to avoid runaway API usage.