Skip to main content
Glama
NetMap — Network Diagram MCP

NetMap — Network Diagram MCP

by den-indance

@den.dance/network-diagram-mcp

npm

Visual network topology editor with AI agent integration via the Model Context Protocol. Open-source successor to netViz (netViz Inc., 1990 → CA Technologies → discontinued 2012).

Try it live → https://map.den.dance/

NetMap workspace

What makes it different

  • One-click ops dashboard. Click any web port (80 / 443 / 8080 / …) on a node card to open the service in a new tab. Click an SSH / Postgres / Redis port → in-app picker offers your installed clients (iTerm / Windows Terminal / psql / DBeaver / redis-cli / RDM / …) and launches the one you choose. The picker re-detects every click — newly installed clients show up immediately, never a silent locked-in default.

  • AI agents edit the map. 44 MCP tools — your Claude / Cursor / Claude Desktop session can build a diagram for you, search across nodes / ports / notes, run smart auto-layouts, export PNG / PDF — all by talking to the open browser tab.

  • nmap -oX import. Run a network scan, hand the XML to the agent, get a map with nodes auto-typed from OS fingerprint + port profile (router / switch / firewall / server / printer / …).

  • CSV inventory import. Drop in your asset spreadsheet — auto-detects columns (name / ip / type / ports / notes; aliases like hostname / description accepted).

  • Operator-grade node cards. Per-node: open ports with service names, Docker services, DNS domains, free-form notes. All searchable cross-entity via the agent.

  • Smart auto-layout. Force-directed (deterministic via seed), cluster-by-type, cluster-by-connection.

  • Multi-sheet, local-first. Multiple maps in one workspace, everything in localStorage. No login, no cloud. Export JSON / PNG / PDF.

Related MCP server: Heym

How it works

This package is a stdio → WebSocket bridge. It runs locally as an MCP server; the live NetMap browser tab connects to it over WebSocket. The agent talks to the bridge over MCP; the bridge forwards calls to the browser, which updates React state in real time.

LLM agent (Claude Code / Claude Desktop / Cursor)
   │  MCP protocol (stdio)
   ▼
@den.dance/network-diagram-mcp  ←  this package, runs locally
   │  WebSocket ws://localhost:47821
   ▼
NetMap in browser (https://map.den.dance/)  ←  React state updates live

⚠️ Requires an open NetMap browser tab. Open https://map.den.dance/ — the MCP bridge is enabled by default (v1.2+), so the connection comes up as soon as the tab is open. Without an open tab the bridge has no peer and every tool call will time out. (To opt out: Settings → ⚙ → Integrations → AI Agent (MCP) → Enable WebSocket connection.)

Install

Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "netmap": {
      "command": "npx",
      "args": ["@den.dance/network-diagram-mcp"]
    }
  }
}

Claude Code

claude mcp add netmap -- npx @den.dance/network-diagram-mcp

With a custom port:

claude mcp add netmap -e NETMAP_MCP_PORT=12345 -- npx @den.dance/network-diagram-mcp

Then open https://map.den.dance/ — the bridge is enabled by default (v1.2+). Settings → Integrations → AI Agent (MCP) if you need to inspect or toggle the connection. The toolbar will show a 🟢 MCP online badge when the browser and server are connected.

Verify the agent side:

claude mcp list

You should see netmap in the list.

Environment variables

Var

Default

Description

NETMAP_MCP_PORT

47821

Local WebSocket port the bridge listens on. Must match the URL configured in NetMap's Settings → Integrations → AI Agent (MCP).

URL Protocol Handlers + in-app client picker (v1.1+, picker since v1.2)

This package also ships an OS-level agent that registers handlers for ssh://, postgres://, and redis:// URLs. Click any such link in a browser or terminal and the agent spawns your installed client (psql / redis-cli / iTerm / Windows Terminal / kitty / DBeaver / TablePlus / RedisInsight / …).

Inside NetMap (v1.2+): when the serve daemon is running, clicking an SSH / Postgres / Redis port button on a node card opens an in-app picker of installed clients on your machine and launches the one you pick — every click re-detects, so newly installed clients show up immediately. Without the daemon, port-clicks fall back to the OS default URL handler.

Install once per machine:

# Linux:   ~/.local/share/applications/netmap-<scheme>-handler.desktop + xdg-mime
# macOS:   ~/Library/Application Support/NetMap/handlers/NetMap<Scheme>Handler.app + lsregister
# Windows: HKCU\Software\Classes\<scheme>  (per-user, no admin)
npx @den.dance/network-diagram-mcp install --all

Other CLI subcommands:

npx @den.dance/network-diagram-mcp install ssh        # single scheme
npx @den.dance/network-diagram-mcp uninstall postgres # remove registration
npx @den.dance/network-diagram-mcp list               # what's registered (JSON)
npx @den.dance/network-diagram-mcp detect ssh         # which clients are available
npx @den.dance/network-diagram-mcp serve              # daemon: WS + HTTP on :47821

Click handling is daemon-less — OS routes the link to a short-lived bin/handler.js process which parses the URL, picks the first installed client (priority order: CLI → popular GUI → cross-platform power), and spawns it. The optional serve daemon adds HTTP endpoints (/status, /detect, /exec) for browser-side integration with a Bearer-token-gated /exec.

npx @den.dance/network-diagram-mcp with no args still runs the original stdio MCP server (back-compat — Smithery / Claude Desktop probes are unaffected).

Top-5 clients per scheme (priority order, first-installed wins):

Scheme

Clients

ssh

iTerm2 → Windows Terminal → GNOME Terminal → Terminal.app → kitty

postgres

psql → TablePlus → DBeaver → Beekeeper Studio → pgAdmin 4

redis

redis-cli → RedisInsight → Another Redis DM → Medis → RDM (legacy)

postgresql:// aliases to postgres; rediss:// is preserved and triggers redis-cli --tls.

Available tools

State & layout

Tool

Description

map_get_state

Return all nodes, connections, stickies, notes

map_clear

Clear the entire map (requires confirm: true)

map_arrange

Naive auto-arrange (grid or circle layout — ignores connections)

map_suggest_layout

Smart auto-layout: force (force-directed, deterministic via seed), cluster-by-type (lanes per type), or cluster-by-connection (BFS components into zones)

Nodes

Tool

Description

map_add_node

Add a node (type, name, ip, x, y)

map_update_node

Update node fields by id

map_delete_node

Delete a node and its connections

map_move_node

Move node to new coordinates

Connections

Tool

Description

map_add_connection

Add connection (from_id, to_id, label?, color?)

map_update_connection

Update label or color of a connection

map_delete_connection

Delete a connection by id

Sticky notes

Tool

Description

map_add_sticky

Add a sticky note (text, x?, y?, color?)

map_update_sticky

Update text, color, x, y, w, h

map_delete_sticky

Delete a sticky note by id

map_move_sticky

Move sticky to new coordinates

Sheets (multi-sheet)

Tool

Description

map_list_sheets

List all sheets with metadata and active sheet id

map_get_sheet_data

Get nodes / connections / stickies for a sheet (default: active)

map_create_sheet

Create a new empty sheet and switch to it

map_switch_sheet

Switch active sheet by id

map_rename_sheet

Rename a sheet

map_delete_sheet

Delete a sheet (requires confirm: true, can't delete last)

View / canvas

Tool

Description

map_set_zoom

Set zoom level (0.1–3.0)

map_set_canvas_offset

Pan canvas to absolute pixel position

map_zoom_to_fit

Auto-fit all nodes into viewport

Lock / protection

Tool

Description

map_lock_sheet

Lock (locked: true) or unlock (locked: false) a sheet. Locked sheets reject all MCP mutations and disable manual editing in the UI.

Notes & settings

Tool

Description

map_get_notes

Get sheet-level notes text

map_set_notes

Set sheet-level notes text

map_get_settings

Get app settings (sshMode, showGrid, etc.)

map_update_settings

Update app settings

Tool

Description

map_find_node

Structured node-only filter by name / ip / type / text. Returns stripped {id, name, type, ip, x, y}.

map_search

Full-text cross-entity search. Looks across nodes (name / ip / notes), stickies (text), connection labels, and ports (port number + service name). Optional types: ["nodes","stickies","connections","ports"] narrows the scope.

map_get_nodes_by_type

Return every node of a single type with FULL field data (ports, dockerServices, domains, ips, notes, parentServer, …). Use this when you need the complete objects, not the stripped projection from map_find_node.

Example — find everything matching postgres anywhere in the map:

// → call
{ "q": "postgres" }

// → result
{
  "nodes":       [{ "id": "n1", "name": "db-primary", "type": "server", "ip": "10.0.0.5" }],
  "stickies":    [{ "id": "s2", "text": "TODO: upgrade postgres 15 → 16" }],
  "connections": [{ "id": "c4", "label": "postgres replication", "from": "n1", "to": "n2" }],
  "ports":       [{ "nodeId": "n1", "nodeName": "db-primary", "port": 5432, "protocol": "tcp", "service": "postgresql" }],
  "total": 4
}

map_search and map_get_nodes_by_type are read-only — they work on locked sheets.

Import

Tool

Description

map_import_sheet

Replace active-sheet content with a provided {nodes, connections, stickies?, notes?} JSON.

map_import_nmap

Parse nmap -oX output (or a pre-parsed hosts[] array) and create nodes with their open ports. Auto-infers node type from OS fingerprint + port profile (router / switch / firewall / server / printer / …). Nodes auto-arranged in a square-root grid.

map_import_csv

Import nodes from CSV. Auto-detects columns from the header row (name / ip / type / ports / notes — aliases like hostname / description accepted). Ports field accepts 22/tcp,80/tcp or bare numbers. Nodes laid out in a 6-column grid.

Example — turn a 2-host nmap scan into a map in one call:

// → call
{
  "xml": "<?xml version=\"1.0\"?>\n<nmaprun>\n  <host>\n    <address addr=\"10.0.0.1\" addrtype=\"ipv4\"/>\n    <hostnames><hostname name=\"web.local\" type=\"user\"/></hostnames>\n    <ports>\n      <port protocol=\"tcp\" portid=\"22\"><state state=\"open\"/><service name=\"ssh\"/></port>\n      <port protocol=\"tcp\" portid=\"80\"><state state=\"open\"/><service name=\"http\"/></port>\n    </ports>\n  </host>\n  <host>\n    <address addr=\"10.0.0.2\" addrtype=\"ipv4\"/>\n    <os><osmatch name=\"Cisco IOS router\" accuracy=\"98\"/></os>\n  </host>\n</nmaprun>"
}

// → result
{ "count": 2, "ids": ["…", "…"] }

Provide {"hosts": [...]} instead of xml when you already have parsed host data — hosts takes priority when both are given.

Example — turn a CSV inventory snippet into a map:

// → call
{
  "csv": "name,ip,type,ports,notes\ndb-primary,10.0.0.5,server,\"22/tcp,5432/tcp\",Postgres 15\nrouter-main,10.0.0.1,router,22/tcp,Edge router"
}

// → result
{ "count": 2, "ids": ["…", "…"] }

For non-standard headers, pass an explicit columns mapping (-1 means "absent"):

{
  "csv":     "Host,Address\nfoo,10.0.0.99",
  "columns": { "name": 0, "ip": 1, "type": -1, "ports": -1, "notes": -1 }
}

map_import_nmap and map_import_csv are mutations — blocked on locked sheets.

Layout

map_suggest_layout repositions every node according to a chosen algorithm; result shape: { ok: true, algorithm, changed: <node count> }.

// Force-directed (organic; reproducible with seed)
{ "algorithm": "force", "iterations": 200, "seed": 42 }

// Group nodes into horizontal lanes by type
{ "algorithm": "cluster-by-type" }

// Place each connected component in its own x-zone
{ "algorithm": "cluster-by-connection" }

Force-directed is O(n²) per iteration — fine up to a few hundred nodes; bring iterations down for larger scenes. map_suggest_layout is a mutation — blocked on locked sheets.

Export (JSON / PNG / PDF)

Tool

Description

map_export_sheet

Export a single sheet as a JSON object (auto-connections included). Legacy entry — still works.

map_export

Multi-format export: json (object), png and pdf (base64-encoded blob + mimeType).

// JSON
{ "format": "json" }
// → { format: "json", data: { nodes, connections, stickies, notes, auto_connections } }

// PNG (max 30 s; agent must persist the base64 to a file)
{ "format": "png" }
// → { format: "png", filename: "netmap-<sheetId>.png",
//      base64: "iVBORw0KGgoAAAANSUhEUg…",
//      mimeType: "image/png" }

// PDF
{ "format": "pdf" }
// → { format: "pdf", filename: "netmap-<sheetId>.pdf",
//      base64: "JVBERi0xLjQK…",
//      mimeType: "application/pdf" }

PNG / PDF capture renders the live workspace via html-to-image + jsPDF, so the MCP server raises the per-command timeout to 30 s for this tool. map_export is read-only — works on locked sheets.

Connection status indicator

A badge appears next to the NetMap version in the toolbar (click it to open Settings):

Badge

Meaning

🟢 MCP online

Connected — agent can edit the map

🟡 MCP

Connecting to server

🟠 MCP retry N/10

Retrying, up to 10 attempts × 30 sec

🔴 MCP error

Gave up — start the server, then toggle off / on to retry

(no badge)

Disabled in settings

Detailed status (with URL and hints) is shown inside Settings → Integrations → AI Agent (MCP).

Running from source (for contributors)

git clone https://github.com/den-indance/network-diagram-mcp.git
cd network-diagram-mcp
npm install
node index.js

Override port: NETMAP_MCP_PORT=12345 node index.js.

Register the local build in Claude Code instead of npm:

claude mcp add netmap-dev -- node /absolute/path/to/network-diagram-mcp/index.js

License

MIT — see LICENSE.

Available Tools

44 tools
map_add_connectionA

Add a manual connection between two nodes. Returns the new connection id.

Inputs: to_id (required), from_id (required), color, label.

map_add_nodeB

Add a new node to the map. Returns the new node id.

Inputs: name (required), type (required), x, y, +1 more.

map_add_stickyA

Add a sticky note to the map. Returns the new sticky id.

Inputs: text (required), x, y, color.

map_arrangeB

Auto-arrange all nodes in a grid or circle layout.

Inputs: layout (required).

map_bulk_add_connectionsA

Add multiple connections in a single call. Returns array of created ids.

Inputs: connections (required).

map_bulk_add_nodesA

Add multiple nodes in a single call. Each node accepts the same fields as map_add_node plus all type-specific fields (ports, dockerServices, domains, parentServer, etc.). Returns array of created ids.

Inputs: nodes (required).

map_clearA

Remove all nodes, connections and stickies from the current sheet.

Inputs: confirm (required).

map_create_sheetA

Create a new empty sheet and switch to it. Returns new sheet id.

Inputs: name.

map_delete_connectionB

Delete a connection by id.

Inputs: id (required).

map_delete_nodeA

Delete a node and all its connections.

Inputs: id (required).

map_delete_sheetA

Delete a sheet. Cannot delete the last remaining sheet.

Inputs: id (required), confirm (required).

map_delete_stickyA

Delete a sticky note by id.

Inputs: id (required).

map_duplicate_nodeA

Clone a node with all its properties (ports, services, domains, etc.). Returns new node id.

Inputs: id (required), name, offset_x, offset_y.

map_exportA

Export current or specified sheet as JSON / PNG / PDF. PNG and PDF return base64-encoded blobs with mimeType — the client decodes and saves to a file. Server raises the response timeout to 30s for PNG/PDF since large maps can take several seconds to render.

Inputs: format, sheet_id.

map_export_sheetA

Export sheet data as JSON including auto-connections. Defaults to active sheet.

Inputs: id.

map_find_nodeA

Search nodes by name, IP, type, or free-text query. Returns id, name, type, ip, x, y for each match. Use this to locate a node before updating it.

Inputs: ip, name, text, type.

map_get_all_connectionsA

Return all connections including auto-generated ones (DNS→server, project→server, service→node). Auto connections have auto:true flag. Use this instead of map_get_state.connections when you need the full topology picture.

Inputs: no named parameters.

map_get_nodes_by_typeA

Return all nodes of a given type with FULL field data (ports, dockerServices, domains, ips, notes, parentServer, etc.). For stripped projection or multi-criteria filtering use map_find_node instead.

Inputs: type (required).

map_get_notesB

Return the sheet-level notes text.

Inputs: no named parameters.

map_get_selectionA

Return currently selected node/sticky ids and connection id. Result shape: { nodeIds: string[], nodeId: string|null (= nodeIds[0], legacy single-id), connId: string|null }.

Inputs: no named parameters.

map_get_settingsA

Return current app settings (sshMode, dblClickAction, perfMode, showGrid, showZoomSlider).

Inputs: no named parameters.

map_get_sheet_dataA

Return nodes/connections/stickies/notes for a specific sheet (defaults to active sheet).

Inputs: id.

map_get_stateA

Return the current map state: all nodes, manual connections, stickies and notes. NOTE: only manual connections are included — use map_get_all_connections to also get auto-generated connections (DNS→server, project→server, service→node).

Inputs: no named parameters.

map_get_viewA

Return current canvas zoom level and pan offset.

Inputs: no named parameters.

map_import_csvA

Import nodes from CSV. Auto-detects columns from the header row (name/ip/type/ports/notes — case-insensitive, aliases like hostname/description/host_ip accepted). Ports field accepts 'port/protocol,port/protocol' (e.g. '22/tcp,80/tcp') or bare port numbers (default tcp). Nodes laid out in a 6-column grid. Returns {count, ids}.

Inputs: csv (required), columns.

map_import_nmapA

Parse nmap scan output (XML from nmap -oX) and create nodes with their open ports. Auto-infers node type from OS fingerprint + port profile (router/switch/firewall/server/printer/etc.). Provide either raw xml OR pre-parsed hosts[] — hosts takes priority when both are given. Nodes are auto-arranged in a grid. Returns {count, ids}.

Inputs: xml, hosts.

map_import_sheetA

Replace active sheet content with provided JSON data. Useful for loading a prepared topology in one call.

Inputs: data (required).

map_list_sheetsA

List all sheets with their id, name, createdAt, updatedAt and which is active.

Inputs: no named parameters.

map_lock_sheetA

Lock or unlock a sheet. A locked sheet rejects all MCP mutations and disables manual editing in the UI. Pass locked: false to unlock.

Inputs: id, locked.

map_move_nodeA

Move a node to new canvas coordinates without changing any other fields.

Inputs: x (required), y (required), id (required).

map_move_stickyA

Move a sticky note to new canvas coordinates without changing its text or color.

Inputs: x (required), y (required), id (required).

map_rename_sheetB

Rename a sheet.

Inputs: id (required), name (required).

map_selectA

Programmatically select node(s)/sticky(s) and/or a connection. Pass node_ids (array) for multi-select; node_id (string) is legacy single-select. Empty array or null deselects. Connection selection remains single (multi-select connections out of scope v1).

Inputs: conn_id, node_id, node_ids.

map_set_canvas_offsetA

Pan canvas to an absolute pixel offset.

Inputs: x (required), y (required).

map_set_notesA

Set the sheet-level notes text.

Inputs: notes (required).

map_set_zoomA

Set canvas zoom level (0.1–3.0).

Inputs: zoom (required).

map_suggest_layoutA

Smart auto-layout: force-directed (organic, deterministic with seed), cluster-by-type (horizontal lanes per node type), or cluster-by-connection (BFS components → separate zones). Different from map_arrange (naive grid/circle). Mutates node positions in-place.

Inputs: seed, algorithm, iterations.

map_switch_sheetA

Switch the active sheet. Saves current sheet state first.

Inputs: id (required).

map_update_connectionA

Update label or color of an existing connection.

Inputs: id (required), changes (required).

map_update_nodeA

Update fields of an existing node by id.

FIELD SCHEMA BY NODE TYPE All types: name, ip, x, y, w, h, notes ips: [{address, label}] — extra IP addresses

server / pc / nas / ap / camera / printer / router / switch / firewall / other: ports: [{port, protocol("tcp"|"udp"|"icmp"), service, url, bindIp}] dockerServices: [{name, image, hostPort, containerPort, protocol("tcp"|"udp"), url}]

project: parentServer — id of the parent server node (creates auto-connection) repoUrl — Git/GitLab URL deployPath — deployment directory cicdUrl — CI/CD pipeline URL dockerServices (same as server)

domain (DNS Zone): ip — registrar name (e.g. "Cloudflare") registrarUrl — registrar control panel URL domains: [{name, record("A"|"AAAA"|"CNAME"|"MX"|"TXT"|"NS"|"SRV"), target, url}] target IP in 'target' triggers auto-connection to matching server node

service (External Service): provider — provider name (e.g. "Supabase", "Vercel") dashboardUrl — link to service dashboard serviceConns: [{toId, label}] — links to other nodes (creates auto-connections) servedDomains: [string]

Inputs: id (required), changes (required).

map_update_settingsB

Update one or more app settings.

Inputs: changes (required).

map_update_stickyC

Update text, color or size of a sticky note.

Inputs: id (required), changes (required).

map_zoom_to_fitA

Auto-fit all nodes into the viewport by adjusting zoom and pan.

Inputs: padding.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.5/5.0

    Scored across 44 tools

    Disambiguation3/5

    While descriptions are detailed, several tools overlap in purpose: map_get_state, map_get_sheet_data, map_export_sheet, and map_export all return map data; map_arrange and map_suggest_layout both auto-layout; map_find_node, map_search, and map_get_nodes_by_type all query nodes. Explicit notes clarify some boundaries, but an agent could easily choose the wrong tool without careful reading.

    Naming Consistency4/5

    All tools share the 'map_' prefix and use snake_case throughout, with most following a verb_noun pattern (map_add_node, map_delete_connection). Minor deviations include bare verbs like map_clear, map_arrange, map_search, and map_export, but these are limited and the overall pattern is predictable.

    Tool Count2/5

    At 44 tools, the server is significantly over the typical range for an MCP server. While the domain is broad, many tools could be consolidated (e.g., map_get_state vs map_get_sheet_data, map_arrange vs map_suggest_layout) to reduce cognitive load. The high count feels heavy even with the expansive feature set.

    Completeness5/5

    The tool surface is remarkably comprehensive, covering full CRUD for nodes, connections, stickies, and sheets, plus view management, settings, search, import (CSV, nmap, JSON), export (JSON/PNG/PDF), layout algorithms, selection, and bulk operations. The domain of network diagram management is well-served with no major dead ends.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      C
      quality
      B
      maintenance
      Self-hosted, source-available AI workflow automation platform. Build multi-agent, RAG, and tool-using pipelines on a visual canvas and publish any workflow as an MCP server (stdio/SSE/Streamable HTTP). Also an MCP client via the agent node.
      2
      1,638
      -
    • A
      license
      Not graded
      quality
      F
      maintenance
      Agent-first knowledge graph MCP server that provides 25 tools for managing a knowledge graph with nodes and edges, plus a human-readable dashboard for LLMs and AI agents.
      288 npm
      Apache 2.0