4.1 KiB
4.1 KiB
Network MCP - Project Summary
Overview
This project is a long-running Network MCP service that merges OPNsense discovery data, Nmap scans, and static inventory into Elasticsearch, then exposes both a minimal web UI and a full MCP JSON-RPC interface for LLM agents. It runs via Docker Compose and is now located at /var/core/network-mcp.
What We Built
- Collectors
- OPNsense collector ingests DHCP/ARP/DNS and overlays inventory targets.
- Nmap collector performs discovery and port scans.
- Data lands in Elasticsearch:
network-hosts(current state) andnetwork-events-*(historical events).
- Inventory merge
- Inventory data from
inventory_targets.ymlis merged onto live hosts by IP when a MAC is known (so live MAC-based records carry inventory notes/expected ports).
- Inventory data from
- Frontend
- Flask UI + JSON API, containerized with Gunicorn and exposed on port
5001for LAN access.
- Flask UI + JSON API, containerized with Gunicorn and exposed on port
- MCP server
- JSON-RPC endpoint at
/.well-known/mcp.json(and/api/mcp) supports:initialize,ping,tools/list,tools/callresources/list,resources/read,resources/templates/list
- Tool schemas include titles, descriptions, input/output schemas, and annotations (read-only hints).
- Resource templates provide snapshot + query access (e.g.
network://hosts?q=...).
- JSON-RPC endpoint at
- Search behavior
- Host search is case-insensitive across name/hostname/IP/MAC.
- Tests
- Unit tests for REST and MCP search by hostname/IP/MAC, MCP resource reads, and MCP notifications.
Key Endpoints
- UI:
http://<host>:5001/ - REST:
GET /api/hosts(supportsq,source,limit)GET /api/hosts/<host_id>GET /api/eventsGET /api/hosts/<host_id>/eventsGET /api/map
- MCP JSON-RPC:
POST /.well-known/mcp.json
MCP Tools (JSON-RPC)
list_hosts(search by hostname/IP/MAC; case-insensitive)get_host(optional events)list_eventshost_eventsnetwork_map
MCP Resources
resources/list->network://hosts,network://map,network://eventsresources/templates/list-> query templates such as:network://hosts{?q,source,limit}network://host/{host_id}{?include_events,events_limit}network://events{?host_id,type,since,limit}
Docker & Repo State
- Repo path:
/var/core/network-mcp inventory_targets.ymllives in the repo and is mounted via compose.- Services run via
docker-compose up -d. - Git repo initialized and initial commit created.
Gotchas / Pitfalls We Hit
- MCP handshake: Codex sent
notifications/initializedwithoutid(notification). Returning a response caused the transport to close. Fixed by treating notifications as no-response. - Case-sensitive search: Elasticsearch wildcard on
.keywordfields was case-sensitive, soseeledidn’t matchSEELE. Fixed viacase_insensitive: truein wildcard queries. - Inventory merge duplication: Initial inventory-only docs were
ip:*and live docs weremac:*, so both existed. Merge now attaches inventory to live MAC records by IP. Legacyip:*docs may remain stale unless cleaned. - MCP errors: Tool errors are now returned as
CallToolResultwithisError: true(instead of JSON-RPC errors), so LLMs can see and correct issues. - Service move: Repo moved from
/var/core/ansible/network-mcpto/var/core/network-mcp. Compose mount paths updated.
Verification Performed
- REST search works for hostname/IP/MAC.
- MCP
initialize,tools/list,tools/callwork. - MCP resource list/templates/read work.
- Services verified running via
docker-compose up -d.
Future Work Ideas
- Cleanup: Add a cleanup job to remove stale
ip:*docs after successful MAC merge. - Resource subscriptions: Implement
resources/subscribeif clients need push updates. - Auth: Optional token on the MCP endpoint for shared LAN exposure.
- More UI: Add filters/alerts for stale hosts or missing expected ports.
- Metrics: Export collector stats to detect scan/ingest failures.
- Schema mapping: Improve Elasticsearch mappings for search (e.g., lowercase normalizers for names/hostnames).