diff --git a/app/graph/conn.c b/app/graph/conn.c
new file mode 100644
index 0000000000..dabc8deca2
--- /dev/null
+++ b/app/graph/conn.c
@@ -0,0 +1,284 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Marvell.
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "module_api.h"
+
+#define MSG_CMD_TOO_LONG "Command too long."
+
+static int
+data_event_handle(struct conn *conn, int fd_client)
+{
+	ssize_t len, i, rc = 0;
+
+	/* Read input message */
+	len = read(fd_client, conn->buf, conn->buf_size);
+	if (len == -1) {
+		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
+			return 0;
+
+		return -1;
+	}
+
+	if (len == 0)
+		return rc;
+
+	/* Handle input messages */
+	for (i = 0; i < len; i++) {
+		if (conn->buf[i] == '\n') {
+			size_t n;
+
+			conn->msg_in[conn->msg_in_len] = 0;
+			conn->msg_out[0] = 0;
+
+			conn->msg_handle(conn->msg_in, conn->msg_out, conn->msg_out_len_max,
+					 conn->msg_handle_arg);
+
+			n = strlen(conn->msg_out);
+			if (n) {
+				rc = write(fd_client, conn->msg_out, n);
+				if (rc == -1)
+					goto exit;
+			}
+
+			conn->msg_in_len = 0;
+		} else if (conn->msg_in_len < conn->msg_in_len_max) {
+			conn->msg_in[conn->msg_in_len] = conn->buf[i];
+			conn->msg_in_len++;
+		} else {
+			rc = write(fd_client, MSG_CMD_TOO_LONG, strlen(MSG_CMD_TOO_LONG));
+			if (rc == -1)
+				goto exit;
+
+			conn->msg_in_len = 0;
+		}
+	}
+
+	/* Write prompt */
+	rc = write(fd_client, conn->prompt, strlen(conn->prompt));
+	rc = (rc == -1) ? -1 : 0;
+
+exit:
+	return rc;
+}
+
+static int
+control_event_handle(struct conn *conn, int fd_client)
+{
+	int rc;
+
+	rc = epoll_ctl(conn->fd_client_group, EPOLL_CTL_DEL, fd_client, NULL);
+	if (rc == -1)
+		goto exit;
+
+	rc = close(fd_client);
+	if (rc == -1)
+		goto exit;
+
+	rc = 0;
+
+exit:
+	return rc;
+}
+
+struct conn *
+conn_init(struct conn_params *p)
+{
+	int fd_server, fd_client_group, rc;
+	struct sockaddr_in server_address;
+	struct conn *conn = NULL;
+
+	memset(&server_address, 0, sizeof(server_address));
+
+	/* Check input arguments */
+	if ((p == NULL) || (p->welcome == NULL) || (p->prompt == NULL) || (p->addr == NULL) ||
+	    (p->buf_size == 0) || (p->msg_in_len_max == 0) || (p->msg_out_len_max == 0) ||
+	    (p->msg_handle == NULL))
+		goto exit;
+
+	rc = inet_aton(p->addr, &server_address.sin_addr);
+	if (rc == 0)
+		goto exit;
+
+	/* Memory allocation */
+	conn = calloc(1, sizeof(struct conn));
+	if (conn == NULL)
+		goto exit;
+
+	conn->welcome = calloc(1, CONN_WELCOME_LEN_MAX + 1);
+	conn->prompt = calloc(1, CONN_PROMPT_LEN_MAX + 1);
+	conn->buf = calloc(1, p->buf_size);
+	conn->msg_in = calloc(1, p->msg_in_len_max + 1);
+	conn->msg_out = calloc(1, p->msg_out_len_max + 1);
+
+	if ((conn->welcome == NULL) || (conn->prompt == NULL) || (conn->buf == NULL) ||
+	    (conn->msg_in == NULL) || (conn->msg_out == NULL)) {
+		conn_free(conn);
+		conn = NULL;
+		goto exit;
+	}
+
+	/* Server socket */
+	server_address.sin_family = AF_INET;
+	server_address.sin_port = htons(p->port);
+
+	fd_server = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
+	if (fd_server == -1) {
+		conn_free(conn);
+		conn = NULL;
+		goto exit;
+	}
+
+	rc = bind(fd_server, (struct sockaddr *)&server_address, sizeof(server_address));
+	if (rc == -1) {
+		conn_free(conn);
+		close(fd_server);
+		conn = NULL;
+		goto exit;
+	}
+
+	rc = listen(fd_server, 16);
+	if (rc == -1) {
+		conn_free(conn);
+		close(fd_server);
+		conn = NULL;
+		goto exit;
+	}
+
+	/* Client group */
+	fd_client_group = epoll_create(1);
+	if (fd_client_group == -1) {
+		conn_free(conn);
+		close(fd_server);
+		conn = NULL;
+		goto exit;
+	}
+
+	/* Fill in */
+	strncpy(conn->welcome, p->welcome, CONN_WELCOME_LEN_MAX);
+	strncpy(conn->prompt, p->prompt, CONN_PROMPT_LEN_MAX);
+	conn->buf_size = p->buf_size;
+	conn->msg_in_len_max = p->msg_in_len_max;
+	conn->msg_out_len_max = p->msg_out_len_max;
+	conn->msg_in_len = 0;
+	conn->fd_server = fd_server;
+	conn->fd_client_group = fd_client_group;
+	conn->msg_handle = p->msg_handle;
+	conn->msg_handle_arg = p->msg_handle_arg;
+
+exit:
+	return conn;
+}
+
+void
+conn_free(struct conn *conn)
+{
+	if (conn == NULL)
+		return;
+
+	if (conn->fd_client_group)
+		close(conn->fd_client_group);
+
+	if (conn->fd_server)
+		close(conn->fd_server);
+
+	free(conn->msg_out);
+	free(conn->msg_in);
+	free(conn->prompt);
+	free(conn->welcome);
+	free(conn);
+}
+
+int
+conn_req_poll(struct conn *conn)
+{
+	struct sockaddr_in client_address;
+	socklen_t client_address_length;
+	struct epoll_event event;
+	int fd_client, rc;
+
+	/* Check input arguments */
+	if (conn == NULL)
+		return -1;
+
+	/* Server socket */
+	client_address_length = sizeof(client_address);
+	fd_client = accept4(conn->fd_server, (struct sockaddr *)&client_address,
+			    &client_address_length, SOCK_NONBLOCK);
+	if (fd_client == -1) {
+		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
+			return 0;
+
+		return -1;
+	}
+
+	/* Client group */
+	event.events = EPOLLIN | EPOLLRDHUP | EPOLLHUP;
+	event.data.fd = fd_client;
+
+	rc = epoll_ctl(conn->fd_client_group, EPOLL_CTL_ADD, fd_client, &event);
+	if (rc == -1) {
+		close(fd_client);
+		goto exit;
+	}
+
+	/* Client */
+	rc = write(fd_client, conn->welcome, strlen(conn->welcome));
+	if (rc == -1) {
+		close(fd_client);
+		goto exit;
+	}
+
+	rc = write(fd_client, conn->prompt, strlen(conn->prompt));
+	if (rc == -1) {
+		close(fd_client);
+		goto exit;
+	}
+
+	rc = 0;
+
+exit:
+	return rc;
+}
+
+int
+conn_msg_poll(struct conn *conn)
+{
+	int fd_client, rc, rc_data = 0, rc_control = 0;
+	struct epoll_event event;
+
+	/* Check input arguments */
+	if (conn == NULL)
+		return -1;
+
+	/* Client group */
+	rc = epoll_wait(conn->fd_client_group, &event, 1, 0);
+	if ((rc == -1) || rc == 0)
+		return rc;
+
+	fd_client = event.data.fd;
+
+	/* Data available */
+	if (event.events & EPOLLIN)
+		rc_data = data_event_handle(conn, fd_client);
+
+	/* Control events */
+	if (event.events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP))
+		rc_control = control_event_handle(conn, fd_client);
+
+	if (rc_data || rc_control)
+		return -1;
+
+	return 0;
+}
diff --git a/app/graph/conn.h b/app/graph/conn.h
new file mode 100644
index 0000000000..770964cf4c
--- /dev/null
+++ b/app/graph/conn.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Marvell.
+ */
+
+#ifndef APP_GRAPH_CONN_H
+#define APP_GRAPH_CONN_H
+
+#define CONN_WELCOME_LEN_MAX 1024
+#define CONN_PROMPT_LEN_MAX 16
+
+typedef void (*conn_msg_handle_t)(char *msg_in, char *msg_out, size_t msg_out_len_max, void *arg);
+
+struct conn {
+	char *welcome;
+	char *prompt;
+	char *buf;
+	char *msg_in;
+	char *msg_out;
+	size_t buf_size;
+	size_t msg_in_len_max;
+	size_t msg_out_len_max;
+	size_t msg_in_len;
+	int fd_server;
+	int fd_client_group;
+	conn_msg_handle_t msg_handle;
+	void *msg_handle_arg;
+};
+
+struct conn_params {
+	const char *welcome;
+	const char *prompt;
+	const char *addr;
+	uint16_t port;
+	size_t buf_size;
+	size_t msg_in_len_max;
+	size_t msg_out_len_max;
+	conn_msg_handle_t msg_handle;
+	void *msg_handle_arg;
+};
+
+struct conn *conn_init(struct conn_params *p);
+void conn_free(struct conn *conn);
+int conn_req_poll(struct conn *conn);
+int conn_msg_poll(struct conn *conn);
+
+#endif
diff --git a/app/graph/main.c b/app/graph/main.c
index a4af1ec7e1..79575fa2a6 100644
--- a/app/graph/main.c
+++ b/app/graph/main.c
@@ -16,13 +16,26 @@
 #include "module_api.h"
 
 volatile bool force_quit;
+struct conn *conn;
 
-static const char usage[] = "%s EAL_ARGS -- -s SCRIPT "
+static const char usage[] = "%s EAL_ARGS -- -s SCRIPT [-h HOST] [-p PORT] "
 			    "[--help]\n";
 
 static struct app_params {
+	struct conn_params conn;
 	char *script_name;
 } app = {
+	.conn = {
+		.welcome = "\nWelcome!\n\n",
+		.prompt = "graph> ",
+		.addr = "0.0.0.0",
+		.port = 8086,
+		.buf_size = 1024 * 1024,
+		.msg_in_len_max = 1024,
+		.msg_out_len_max = 1024 * 1024,
+		.msg_handle = cli_process,
+		.msg_handle_arg = NULL, /* set later. */
+	},
 	.script_name = NULL,
 };
 
@@ -41,7 +54,7 @@ app_args_parse(int argc, char **argv)
 	struct option lgopts[] = {
 		{"help", 0, 0, 'H'},
 	};
-	int s_present, n_args, i;
+	int h_present, p_present, s_present, n_args, i;
 	char *app_name = argv[0];
 	int opt, option_index;
 
@@ -58,10 +71,46 @@ app_args_parse(int argc, char **argv)
 		return 0;
 
 	/* Parse args */
+	h_present = 0;
+	p_present = 0;
 	s_present = 0;
 
-	while ((opt = getopt_long(argc, argv, "s:", lgopts, &option_index)) != EOF) {
+	while ((opt = getopt_long(argc, argv, "h:p:s:", lgopts, &option_index)) != EOF) {
 		switch (opt) {
+		case 'h':
+			if (h_present) {
+				printf("Error: Multiple -h arguments\n");
+				return -1;
+			}
+			h_present = 1;
+
+			if (!strlen(optarg)) {
+				printf("Error: Argument for -h not provided\n");
+				return -1;
+			}
+
+			app.conn.addr = strdup(optarg);
+			if (app.conn.addr == NULL) {
+				printf("Error: Not enough memory\n");
+				return -1;
+			}
+			break;
+
+		case 'p':
+			if (p_present) {
+				printf("Error: Multiple -p arguments\n");
+				return -1;
+			}
+			p_present = 1;
+
+			if (!strlen(optarg)) {
+				printf("Error: Argument for -p not provided\n");
+				return -1;
+			}
+
+			app.conn.port = (uint16_t)strtoul(optarg, NULL, 10);
+			break;
+
 		case 's':
 			if (s_present) {
 				printf("Error: Multiple -s arguments\n");
@@ -117,10 +166,26 @@ main(int argc, char **argv)
 
 	/* Script */
 	if (app.script_name) {
-		cli_script_process(app.script_name, 0,
-			0, NULL);
+		cli_script_process(app.script_name, app.conn.msg_in_len_max,
+			app.conn.msg_out_len_max, NULL);
+	}
+
+	/* Connectivity */
+	app.conn.msg_handle_arg = NULL;
+	conn = conn_init(&app.conn);
+	if (!conn) {
+		printf("Error: Connectivity initialization failed\n");
+		return -1;
+	};
+
+	/* Dispatch loop */
+	while (!force_quit) {
+		conn_req_poll(conn);
+
+		conn_msg_poll(conn);
 	}
 
+	conn_free(conn);
 	cli_exit();
 	rte_eal_cleanup();
 	return 0;
diff --git a/app/graph/meson.build b/app/graph/meson.build
index 87c4ce30a8..7e1ab0a214 100644
--- a/app/graph/meson.build
+++ b/app/graph/meson.build
@@ -10,5 +10,6 @@ endif
 deps += ['bus_pci', 'graph', 'eal', 'lpm', 'ethdev', 'node', 'cmdline']
 sources = files(
         'cli.c',
+        'conn.c',
         'main.c',
 )
diff --git a/app/graph/module_api.h b/app/graph/module_api.h
index 372aeae7e3..b927de9470 100644
--- a/app/graph/module_api.h
+++ b/app/graph/module_api.h
@@ -7,7 +7,9 @@
 
 #include <stdint.h>
 #include <stdbool.h>
+
 #include "cli.h"
+#include "conn.h"
 /*
  * Externs
  */
