Network MCP

A "source of truth" for network devices and ports, backed by Elasticsearch, OPNsense, and Nmap.

Architecture

  • Elasticsearch: Stores current state (network-hosts) and historical events (network-events-*).
  • OPNsense Collector: Fetches DHCP/ARP/DNS data to discover hosts.
  • Nmap Collector: Scans discovered hosts for open ports and OS info.

Setup

  1. Environment Config Copy .env.example to .env and fill in your details:

    cp .env.example .env
    # Edit .env
    
  2. Bootstrap Elastic Run the bootstrap script (requires requests installed locally, or you can run it inside a container):

    python3 scripts/bootstrap_indices.py
    

    Note: Ensure you have connectivity to your Elasticsearch instance.

  3. Start Services

    docker-compose up -d --build
    

    This brings up the collectors and the lightweight frontend (reachable on port 5001).

Configuration

  • Static Metadata: Edit static/host_metadata.json to add manual notes, roles, or tags to hosts (keyed by mac:xx:xx...).
  • Intervals: Adjust polling intervals in .env.
  • VLAN Discovery (default on): Discovery sweeps (nmap -sn) run periodically across the OPNsense interfaces listed in NMAP_DISCOVERY_VLANS. Adjust the list (or set the flag to false) if you only want targeted subnets.
  • Quick vs Full Port Scans: Each collector loop runs a fast, common-port sweep (NMAP_QUICK_EXTRA_ARGS, NMAP_QUICK_BATCH_SIZE) while a deeper service scan (NMAP_PORT_RANGE, NMAP_BATCH_SIZE) is triggered once per NMAP_FULL_INTERVAL_SECONDS (default daily). Tune these env vars to balance coverage vs. runtime.
  • Inventory Overlay: Entries in ./inventory_targets.yml are mounted into the OPNsense collector and merged by IP—offline/static hosts from that file (names, notes, expected ports) now appear in network-hosts with source: inventory.

Data Model

  • network-hosts: Current state of every known host.
  • network-events-YYYY.MM.DD: Immutable log of scans and discovery events.

Usage

Query network-hosts for the latest view of your network:

GET network-hosts/_search
{
  "query": {
    "match_all": {}
  }
}

Quick Frontend

A minimal Flask frontend is bundled in docker-compose (service frontend) and is exposed on port 5001 so it can be reached from other machines:

docker-compose up -d frontend

Then visit http://<host-ip>:5001/ to see the merged view (inventory entries are marked with source: inventory). If you prefer to run it without Docker for debugging, follow the steps below:

cd network-mcp
python3 -m venv .venv && source .venv/bin/activate
pip install -r frontend/requirements.txt
python frontend/app.py

MCP / API Endpoints

The frontend doubles as a Model Context Protocol server. It exposes the manifest at /.well-known/mcp.json (or /api/mcp) and supports the standard JSON-RPC handshake (initialize, tools/list, tools/call) on the same URL. Agents can either use the RPC tools below or hit the underlying REST endpoints directly.

  • MCP Resources are also available (resources/list, resources/read, resources/templates/list) for clients that prefer resource-style access to snapshots and queries.

  • GET /api/hosts merged host list (supports limit, source, and repeated q params to fuzzy search names, hostnames, IPs, or MACs in a single call).

  • GET /api/hosts/<host_id> single host document with optional include_events=true.

  • GET /api/events recent scan/discovery events (limit, host_id, type, since filters).

  • GET /api/hosts/<host_id>/events scoped events for a host.

  • GET /api/map high-level “network map” grouping hosts by detected /24 (IPv4) or /64 (IPv6).

RPC tool names (mirrored in the manifest) are:

  • list_hosts accepts {limit, source, terms} and returns the merged host list.
  • network_map optional {limit} for building /24-/64 summaries.
  • get_host requires {host_id} plus optional include_events, events_limit.
  • list_events {limit, host_id, type, since}.
  • host_events requires {host_id} plus optional limit, type, since.

Resource URI examples:

  • network://hosts?q=seele&limit=50
  • network://host/mac:dc:a6:32:67:55:dc?include_events=true&events_limit=50
  • network://events?type=discovery&limit=100

All RPC and REST calls share the Elasticsearch credentials from .env, so an agent only needs HTTP access to port 5001 to query hosts, notes, and event timelines. Registering the MCP with Codex looks like:

codex mcp install network-mcp http://<host>:5001/.well-known/mcp.json
Description
No description provided
Readme 58 KiB
Languages
Python 93.1%
HTML 5.7%
Dockerfile 1.2%