WIP.
diff --git a/nlpcraft-tests/cli/pom.xml b/nlpcraft-tests/cli/pom.xml
new file mode 100644
index 0000000..7c2f588
--- /dev/null
+++ b/nlpcraft-tests/cli/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <name>NLPCraft CLI Tests</name>
+    <artifactId>nlpcraft-tests-cli</artifactId>
+
+    <parent>
+        <groupId>org.apache.nlpcraft</groupId>
+        <artifactId>nlpcraft-parent</artifactId>
+        <version>0.9.0</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>nlpcraft</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <!-- Test dependencies. -->
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/scala</directory>
+            </resource>
+            <resource>
+                <directory>src/main/resources</directory>
+                <includes>
+                    <include>**/*.*</include>
+                </includes>
+            </resource>
+        </resources>
+    </build>
+</project>
diff --git a/nlpcraft-tests/cli/src/test/java/org/apache/nlpcraft/model/tools/cmdline/NCCliSpec.scala b/nlpcraft-tests/cli/src/test/java/org/apache/nlpcraft/model/tools/cmdline/NCCliSpec.scala
new file mode 100644
index 0000000..1486f4c
--- /dev/null
+++ b/nlpcraft-tests/cli/src/test/java/org/apache/nlpcraft/model/tools/cmdline/NCCliSpec.scala
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nlpcraft.model.tools.cmdline
+
+import org.apache.commons.lang3.SystemUtils
+import org.apache.nlpcraft.common.U
+import org.junit.jupiter.api.Test
+
+import java.io.{BufferedReader, File, FileFilter, InputStreamReader}
+import java.util.concurrent.{CountDownLatch, TimeUnit}
+import scala.collection.mutable
+import scala.util.Using
+
+/**
+  * This test designed only for maven tests (mvn clean verify)
+  * It cannot be started together with other server instances.
+  */
+class NCCliSpec {
+    private def check(dirs: File*): Unit = dirs.foreach(d => require(d.exists() && d.isDirectory, s"Invalid folder: $d"))
+
+    private def getAllDepsJar(dirTarget: File): File = {
+        val jars = dirTarget.listFiles(new FileFilter {
+            override def accept(f: File): Boolean = f.isFile && f.getName.toLowerCase().endsWith("all-deps.jar")
+        })
+
+        require(jars != null && jars.length == 1, s"Required jar file not found in ${dirTarget.getAbsolutePath}")
+
+        jars.head
+    }
+
+    private def makeProcess(
+        scriptArg: String,
+        script: String,
+        allDepsJar: File,
+        dirBin: File,
+        timeoutSecs: Int,
+        expectedLines: String*
+    ): Process = {
+        val args = if (SystemUtils.IS_OS_UNIX) Seq("bash", "-f", script, scriptArg) else Seq(script, scriptArg)
+
+        val builder = new ProcessBuilder(args: _*)
+
+        builder.environment().put("CP", allDepsJar.getAbsolutePath)
+        builder.directory(dirBin)
+        builder.redirectErrorStream(true)
+
+        val process = builder.start()
+
+        val cdl = new CountDownLatch(1)
+        var done = false
+
+        val thread = new Thread() {
+            override def run(): Unit = {
+                Using.resource { new BufferedReader(new InputStreamReader(process.getInputStream)) }(reader => {
+                    var line: String = reader.readLine()
+
+                    while (line != null && !done) {
+                        if (expectedLines.exists(line.contains)) {
+                            done = true
+
+                            println(s"$scriptArg finished fine by expected line: '$line'")
+                            println()
+
+                            cdl.countDown()
+                        }
+                        else {
+                            println(s"($scriptArg) $line")
+
+                            line = reader.readLine()
+                        }
+                    }
+                })
+            }
+        }
+
+        thread.start()
+
+        cdl.await(timeoutSecs, TimeUnit.SECONDS)
+
+        U.stopThread(thread)
+
+        require(done, s"Command cannot be started: $scriptArg")
+
+        process
+    }
+
+    @Test
+    def test(): Unit = {
+        // All folders should be exists because tests started from maven phase, after build project.
+        val ext = if (SystemUtils.IS_OS_UNIX) "sh" else "cmd"
+        val dirUsr = new File(SystemUtils.USER_DIR).getParentFile.getParentFile
+        val dirBin = new File(dirUsr, "bin")
+        val dirTarget = new File(dirUsr, "nlpcraft/target")
+        val script = new File(dirBin, s"nlpcraft.$ext").getAbsolutePath
+
+        check(dirUsr, dirBin, dirTarget)
+
+        val allDepsJar = getAllDepsJar(dirTarget)
+
+        def make(scriptArg: String, timeoutSecs: Int, expectedLines: String*): Process =
+            makeProcess(scriptArg, script, allDepsJar, dirBin, timeoutSecs, expectedLines: _*)
+
+        val procs = mutable.Buffer.empty[Process]
+
+        def stopInstances(): Unit = {
+            // Both variant (stopped or already stopped) are fine.
+            def stop(cmd: String): Process = make(cmd, 10, "has been stopped", "not found")
+
+            procs += stop("stop-server")
+            procs += stop("stop-probe")
+        }
+
+        try {
+            stopInstances()
+
+            procs += make("start-server", 120, "Started on")
+            procs += make("start-probe", 30, "Started on")
+            procs += make("info", 10, "Local server")
+        }
+        finally
+            try
+                stopInstances()
+            finally
+                procs.foreach(_.destroy())
+    }
+}
diff --git a/pom.xml b/pom.xml
index 4a6649e..b27bfe7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -648,6 +648,13 @@
         </profile>
 
         <profile>
+            <id>tests</id>
+            <modules>
+                <module>nlpcraft-tests/cli</module>
+            </modules>
+        </profile>
+
+        <profile>
             <id>release</id>
             <activation>
                 <property>