HowTo Use LLMs with ntopng: Part I

A Large Language Model  (LLM) is a type of artificial intelligence system that uses deep learning to  understand, generate, and manipulate human language.  Examples include Antropic Claude, OpenAI GPT, Meta Llama and Google Gemini. They can be used to answer questions and explain concepts, write and edit code, perform logical reasoning and summarize documents.

At ntop we are making experiments with Artificial Intelligence (AI) and LLMs, and we’ll introduce new features in the coming months. For the time being we want to start introducing our community to the use of these tools with ntop tools. LLMs are usually cloud based in order to provide efficient answers by leveraging on high-performance datacenters. However in network traffic analysis and cybersecurity many people are reluctant to send data out of their network, hence a local LLM would be desirable. In this blog post we show how you can query ntopng with an LLM using tools freely available and without subscription fees.

Introduction

Above you can see the architecture that we’ll be using today. We will use Claude Code to query our system, a local LLM (we choose Qwen that is small enough to run on all PCs even with a small GPU), and MCP server that acts a bridge between the LLM and the ClickHouse database where ntopng is storing data. Below we show how to run all this on multiple hosts, but nothing prevents you from running all the components on the same host/VM.

Installation

Install the MCP server as follows

# Install the uv Python package manager
curl -LsSf https://astral.sh/uv/install.sh | sh

# Install the MCP server
mkdir clickhouse-mcp && cd clickhouse-mcp
uv init
uv add mcp-clickhouse

Now start the MCP server. We assume that both ClickHouse and the MCP server run on the same host; if not change the script below.

#!/bin/bash                                                                                                                                                                        

export CLICKHOUSE_HOST="127.0.0.1"
export CLICKHOUSE_PORT="8123"
export CLICKHOUSE_USER="default"
export CLICKHOUSE_PASSWORD=""
export CLICKHOUSE_SECURE="false"
export CLICKHOUSE_MCP_SERVER_TRANSPORT=sse
export CLICKHOUSE_MCP_BIND_HOST=0.0.0.0
export CLICKHOUSE_MCP_BIND_PORT=8000
export CLICKHOUSE_MCP_AUTH_DISABLED=true

uv run --with mcp-clickhouse --python 3.10 mcp-clickhouse

You can install the local LLM as follows. First install llama.cpp used to run the model

sudo apt-get update && sudo apt-get install build-essential cmake git -y
git clone https://github.com/ggml-org/llama.cpp
cmake llama.cpp -B llama.cpp/build -DGGML_CUDA=ON
cmake --build llama.cpp/build --config Release -j
cp llama.cpp/build/bin/llama-* llama.cpp/

and then start the model as follows

./llama.cpp/llama-server -hf unsloth/Qwen3.5-35B-A3B-GGUF:Q4_K_S --alias "Qwen3.5-35B-A3B-GGUF"  -c 131072   -ngl 999   -sm none   -mg 0   -fa auto  --cache-type-k q8_0   --cache-type-v q8_0  --port 2999   --host 0.0.0.0 --jinja --api-key ntopng_mcp_test

Note that we have used Wehn but you can also use any other mode. As you can see we use ‘ntopng_mcp_test’ as API key (for authentication).

Now It’s now time to install Claude Code using

curl -fsSL https://claude.ai/install.sh | bash

and to connect it to the LLM server

#!/bin/bash                                                                                                                                                                             

export ANTHROPIC_BASE_URL=https://host-where-the-llama.cpp-works
export ANTHROPIC_API_KEY=ntopng_mcp_test
export ANTHROPIC_DEFAULT_OPUS_MODEL=Qwen3.5-35B-A3B-GGUF
export ANTHROPIC_DEFAULT_SONNET_MODEL=Qwen3.5-35B-A3B-GGUF
export ANTHROPIC_DEFAULT_HAIKU_MODEL=Qwen3.5-35B-A3B-GGUF

claude

and to the MCP server

claude mcp add --transport sse mcp-clickhouse http://<remote-ip-were-the-mcp-server-runs>:8000/sse

Using It

Finally everything it’s now ready for testing.

As you can see Claude is using out local model and not Sonnet, also speaking to the MCP server. This means that all data stays local and nothing is sent out of our network.

Now it’s time to ask questions. Examples include

  • What is the host that made most traffic?
  • What’s the symbolic host name that correspond to this host?
  • What are the top hosts that it contacted?
  • Create a distribution chart for total traffic for each ip and create an html page to report it
  • Which port are used by 192.168.2.37 add host role (client or server) in the table breakdown
  • What is the total snmp traffic made by all hosts today?
  • Create an asn traffic breakdown for today
❯ What is the host that made most traffic?                                                             
                                           
⏺ I'll help you find the host that made the most traffic. Let me explore the available ClickHouse      
  databases and tables to find traffic data.                                                           
                                                                                                       
⏺ mcp-clickhouse - list_databases (MCP)                                                                
  ⎿  [                                                                                                 
       "INFORMATION_SCHEMA",                                                                           
       "default",        
     … +4 lines (ctrl+o to expand)                                                                     
                                                                                                                                                                                                                                                                      
⏺ Let me search for tables related to traffic or hosts:                                                
                                                                                                       
⏺ Searched memories, read 1 file (ctrl+o to expand)                                                    
   
⏺ Let me read the file to see the table names:                                                                                                            
   
⏺ mcp-clickhouse - run_select_query (MCP)(query: "SELECT toIPv4(3232236069) as host_ip,                
                                         SUM(TOTAL_BYTES) as total_bytes FROM ntopng.flows WHERE
                                         IPV4_SRC_ADDR = 3232236069 GROUP BY IPV4_SRC_ADDR ORDER BY    
                                         total_bytes DESC LIMIT 1")
  ⎿  {                                                                                                 
       "columns": [      
         "host_ip",      
     … +9 lines (ctrl+o to expand)                                                                     
                                                                                                       
⏺ The host with the most traffic is 192.168.2.37 with 9,197,039 bytes (approximately 9.2 MB) of total  
  traffic.                                                                                             
                                                                                                       
❯ can you create a breakdown of all hosts it contacted?                                                
                                                                                                       
⏺ mcp-clickhouse - run_select_query (MCP)(query: "SELECT toIPv4(IPV4_DST_ADDR) as destination,         
                                         SUM(TOTAL_BYTES) as total_bytes, SUM(PACKETS) as packets FROM 
                                         ntopng.flows WHERE IPV4_SRC_ADDR = 3232236069 AND             
                                         IPV4_DST_ADDR != 0 AND IPV4_DST_ADDR != 4294967295 GROUP BY
                                         IPV4_DST_ADDR ORDER BY total_bytes DESC")                     
  ⎿  {            
       "columns": [      
         "destination",  
     … +91 lines (ctrl+o to expand)                                                                    
                                                                                                       
⏺ Here's the traffic breakdown for host 192.168.2.37 - all destinations it contacted:                  
                                                                                                       
  ┌─────────────────┬───────────┬─────────┐                                                            
  │   Destination   │   Bytes   │ Packets │
  ├─────────────────┼───────────┼─────────┤                                                            
  │ 185.199.110.133 │ 5,340,280 │ 5,334   │
  ├─────────────────┼───────────┼─────────┤
  │ 151.101.130.49  │ 1,898,556 │ 1,972   │
  ├─────────────────┼───────────┼─────────┤
  │ 151.101.128.223 │ 538,440   │ 750     │
  ├─────────────────┼───────────┼─────────┤
  │ 147.32.82.194   │ 435,456   │ 492     │
  ├─────────────────┼───────────┼─────────┤
  │ 151.101.192.223 │ 106,150   │ 178     │
  ├─────────────────┼───────────┼─────────┤
  │ 10.255.255.205  │ 97,096    │ 916     │
  ├─────────────────┼───────────┼─────────┤
  │ 10.255.255.203  │ 97,096    │ 916     │
  ├─────────────────┼───────────┼─────────┤
  │ 10.255.255.204  │ 97,096    │ 916     │
  ├─────────────────┼───────────┼─────────┤
  │ 10.255.255.201  │ 97,096    │ 916     │
  ├─────────────────┼───────────┼─────────┤
  │ 10.255.255.31   │ 97,096    │ 916     │
  ├─────────────────┼───────────┼─────────┤
  │ 178.62.197.130  │ 74,364    │ 96      │
  ├─────────────────┼───────────┼─────────┤
  │ 192.168.2.1     │ 57,737    │ 238     │
  ├─────────────────┼───────────┼─────────┤
  │ 151.101.2.49    │ 46,978    │ 95      │
  ├─────────────────┼───────────┼─────────┤
  │ 151.101.64.223  │ 43,983    │ 61      │
  ├─────────────────┼───────────┼─────────┤
  │ 44.213.181.183  │ 38,267    │ 63      │
  ├─────────────────┼───────────┼─────────┤
  │ 10.255.255.202  │ 38,014    │ 458     │
  ├─────────────────┼───────────┼─────────┤
  │ 52.205.50.243   │ 37,937    │ 58      │
  ├─────────────────┼───────────┼─────────┤
  │ 185.199.109.133 │ 24,444    │ 73      │
  ├─────────────────┼───────────┼─────────┤
  │ 192.168.2.53    │ 15,776    │ 136     │
  ├─────────────────┼───────────┼─────────┤
  │ 172.66.43.7     │ 6,213     │ 19      │
  ├─────────────────┼───────────┼─────────┤
  │ 185.125.190.57  │ 5,580     │ 62      │
  ├─────────────────┼───────────┼─────────┤
  │ 90.147.160.73   │ 2,183     │ 15      │
  ├─────────────────┼───────────┼─────────┤
  │ 91.189.92.23    │ 1,201     │ 10      │
  └─────────────────┴───────────┴─────────┘

  Key observations:
  - Top external destination: 185.199.110.133 (GitHub CDN) - ~5.3 MB
  - Fastly CDN IPs: 151.101.x.x (multiple addresses)
  - Internal hosts: 10.255.255.x range and 192.168.2.x

───────────────────────────────────────────────────────────────────────────────────────────────── ▪▪▪ ─
❯  
───────────────────────────────────────────────────────────────────────────────────────────────────────
  ? for shortcuts

In future blog posts we’ll introduce you to additional AI-based tools.

Enjoy and share your experiments.

Share