Annotations added: NCModelAddPackage, NCModelAddClasses.
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/package.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/package.scala
index d222e5e..da194cc 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/package.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/package.scala
@@ -137,7 +137,6 @@
         def kb: Long = KB
     }
 
-
     /**
      * Pimps integers with time units.
      *
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
index 1fbde7e..d8c1900 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
@@ -84,9 +84,7 @@
     private final val DISABLE_GA_PROP = "NLPCRAFT_DISABLE_GA"
 
     private lazy val ANSI_FG_8BIT_COLORS = for (i <- 16 to 255) yield ansi256Fg(i)
-
     private lazy val ANSI_BG_8BIT_COLORS = for (i <- 16 to 255) yield ansi256Bg(i)
-
     private lazy val ANSI_FG_4BIT_COLORS = Seq(
         ansiRedFg,
         ansiGreenFg,
@@ -300,13 +298,14 @@
       *
       * @param f Closure to convert.
       */
-    implicit def toRun(f: => Unit): Runnable = () => try {
-        f
-    }
-    catch {
-        case _: InterruptedException => Thread.currentThread().interrupt()
-        case e: Throwable => prettyError(logger, "Unhandled exception caught:", e)
-    }
+    implicit def toRun(f: => Unit): Runnable = () =>
+        try {
+            f
+        }
+        catch {
+            case _: InterruptedException => Thread.currentThread().interrupt()
+            case e: Throwable => prettyError(logger, "Unhandled exception caught:", e)
+        }
 
     /**
       * Destroys given process (using proper waiting algorithm).
@@ -874,9 +873,7 @@
       *
       * @param res Resource.
       */
-    @throws[NCE]
-    def hasResource(res: String): Boolean =
-        getClass.getClassLoader.getResourceAsStream(res) != null
+    def hasResource(res: String): Boolean = getClass.getClassLoader.getResourceAsStream(res) != null
 
     /**
       * Serializes data.
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntent.java b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntent.java
index 877f7d7..484a1d3 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntent.java
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntent.java
@@ -54,6 +54,8 @@
  * @see NCIntentTerm
  * @see NCIntentSample
  * @see NCIntentSampleRef
+ * @see NCModelAddClasses
+ * @see NCModelAddPackage
  * @see NCIntentSkip
  * @see NCIntentMatch
  * @see NCModel#onMatchedIntent(NCIntentMatch) 
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentMatch.java b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentMatch.java
index 2f1d382..8268d75 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentMatch.java
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentMatch.java
@@ -32,6 +32,8 @@
  * @see NCIntent
  * @see NCIntentTerm
  * @see NCIntentSkip
+ * @see NCModelAddClasses
+ * @see NCModelAddPackage
  * @see NCIntentSample
  * @see NCIntentSampleRef
  * @see NCIntentRef
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentRef.java b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentRef.java
index 263f09f..3348ffe 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentRef.java
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentRef.java
@@ -46,6 +46,8 @@
  * @see NCIntentTerm
  * @see NCIntentSample
  * @see NCIntentSampleRef
+ * @see NCModelAddClasses
+ * @see NCModelAddPackage
  * @see NCIntentSkip
  * @see NCIntentMatch
  * @see NCModel#onMatchedIntent(NCIntentMatch)
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentSample.java b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentSample.java
index e8d18c7..6d57d0d 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentSample.java
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentSample.java
@@ -76,6 +76,8 @@
  * @see NCIntent
  * @see NCIntentRef
  * @see NCIntentTerm
+ * @see NCModelAddClasses
+ * @see NCModelAddPackage
  * @see NCIntentSkip
  * @see NCIntentMatch
  * @see NCModel#onMatchedIntent(NCIntentMatch)
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentSampleRef.java b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentSampleRef.java
index 2596611..a455b8a 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentSampleRef.java
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentSampleRef.java
@@ -75,6 +75,8 @@
  * @see NCIntent
  * @see NCIntentRef
  * @see NCIntentTerm
+ * @see NCModelAddClasses
+ * @see NCModelAddPackage
  * @see NCIntentSkip
  * @see NCIntentMatch
  * @see NCModel#onMatchedIntent(NCIntentMatch)
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentTerm.java b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentTerm.java
index e864eb5..13832a0 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentTerm.java
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCIntentTerm.java
@@ -58,6 +58,8 @@
  * @see NCIntentRef
  * @see NCIntentSample
  * @see NCIntentSampleRef
+ * @see NCModelAddClasses
+ * @see NCModelAddPackage
  * @see NCIntentSkip
  * @see NCIntentMatch
  * @see NCModel#onMatchedIntent(NCIntentMatch)
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelAddClasses.java b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelAddClasses.java
new file mode 100644
index 0000000..d8241d4
--- /dev/null
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelAddClasses.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package org.apache.nlpcraft.model;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotation to add one or more classes that contain intent callbacks. This annotation should be applied to the main
+ * model class. When found the internal intent detection algorithm will scan these additional classes searching
+ * for intent callbacks.
+ * <p>
+ * Additionally with {@link NCModelAddPackage} annotation, these two annotations allowing to have model implementation,
+ * i.e. intent callbacks, in external classes not linked through sub-type relationship to the main model class. This
+ * approach provides greater modularity, isolated testability and overall coding efficiencies for the larger models
+ * <p>
+ * Read full documentation in <a target=_ href="https://nlpcraft.apache.org/intent-matching.html#binding">Intent Matching</a> section and review
+ * <a target=_ href="https://github.com/apache/incubator-nlpcraft/tree/master/nlpcraft-examples">examples</a>.
+ *
+ * @see NCModelAddPackage
+ * @see NCIntentRef
+ * @see NCIntentTerm
+ * @see NCIntentSample
+ * @see NCIntentSampleRef
+ * @see NCIntentSkip
+ * @see NCIntentMatch
+ */
+@Retention(value=RUNTIME)
+@Target(value=TYPE)
+public @interface NCModelAddClasses {
+    /**
+     * Array of class instances to additionally scan for intent callbacks.
+     *
+     * @return Array of class instances to additionally scan for intent callbacks.
+     */
+    Class<?>[] value();
+}
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelAddPackage.java b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelAddPackage.java
new file mode 100644
index 0000000..41ca9dc
--- /dev/null
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelAddPackage.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package org.apache.nlpcraft.model;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotation to add one or more JVM packages that contain classes with intent callbacks. This annotation should be
+ * applied to the main model class. When found the internal intent detection algorithm will recursively scan these
+ * additional packages and their classes searching for intent callbacks.
+ * <p>
+ * Additionally with {@link NCModelAddClasses} annotation, these two annotations allowing to have model implementation,
+ * i.e. intent callbacks, in external classes not linked through sub-type relationship to the main model class. This
+ * approach provides greater modularity, isolated testability and overall coding efficiencies for the larger models
+ * <p>
+ * Read full documentation in <a target=_ href="https://nlpcraft.apache.org/intent-matching.html#binding">Intent Matching</a> section and review
+ * <a target=_ href="https://github.com/apache/incubator-nlpcraft/tree/master/nlpcraft-examples">examples</a>.
+ *
+ * @see NCModelAddClasses
+ * @see NCIntentRef
+ * @see NCIntentTerm
+ * @see NCIntentSample
+ * @see NCIntentSampleRef
+ * @see NCIntentSkip
+ * @see NCIntentMatch
+ */
+@Retention(value=RUNTIME)
+@Target(value=TYPE)
+public @interface NCModelAddPackage {
+    /**
+     * Array of JVM package names to recursively scan for intent callbacks.
+     *
+     * @return Array of JVM package names to recursively scan for intent callbacks.
+     */
+    String[] value();
+}
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala
index f6ee6eb..e27eee6 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala
@@ -17,6 +17,8 @@
 
 package org.apache.nlpcraft.probe.mgrs.deploy
 
+import com.google.common.reflect.ClassPath
+
 import java.io._
 import java.lang.reflect.{InvocationTargetException, Method, Modifier, ParameterizedType, Type, WildcardType}
 import java.util
@@ -39,6 +41,7 @@
 import org.apache.nlpcraft.probe.mgrs.NCProbeSynonymChunkKind.{IDL, REGEX, TEXT}
 import org.apache.nlpcraft.probe.mgrs.{NCProbeModel, NCProbeModelCallback, NCProbeSynonym, NCProbeSynonymChunk, NCProbeSynonymsWrapper}
 
+import java.lang.annotation.Annotation
 import scala.util.Using
 import scala.compat.java8.OptionConverters._
 import scala.collection.mutable
@@ -58,6 +61,8 @@
     private final val CLS_SLV_CTX = classOf[NCIntentMatch]
     private final val CLS_SAMPLE = classOf[NCIntentSample]
     private final val CLS_SAMPLE_REF = classOf[NCIntentSampleRef]
+    private final val CLS_MDL_CLS_REF = classOf[NCModelAddClasses]
+    private final val CLS_MDL_PKGS_REF = classOf[NCModelAddPackage]
 
     // Java and scala lists.
     private final val CLS_SCALA_SEQ = classOf[Seq[_]]
@@ -76,12 +81,15 @@
         CLS_JAVA_OPT
     )
     
-    case class Callback(id: String, clsName: String, funName: String, cbFun: Function[NCIntentMatch, NCResult])
+    case class Callback(method: Method, cbFun: Function[NCIntentMatch, NCResult]) {
+        val id: String = method.toString
+        val clsName: String = method.getDeclaringClass.getName
+        val funName: String = method.getName
+    }
     type Intent = (NCIdlIntent, Callback)
     type Sample = (String/* Intent ID */, Seq[Seq[String]] /* List of list of input samples for that intent. */)
     
     private final val SEPARATORS = Seq('?', ',', '.', '-', '!')
-
     private final val SUSP_SYNS_CHARS = Seq("?", "*", "+")
 
     @volatile private var data: mutable.ArrayBuffer[NCProbeModel] = _
@@ -104,6 +112,25 @@
       */
     case class SynonymHolder(elmId: String, syn: NCProbeSynonym)
 
+    case class MethodOwner(method: Method, objClassName: String, obj: Any) {
+        require(method != null)
+        require(objClassName != null ^ obj != null)
+
+        private var lazyObj: Any = obj
+
+        def getObject: Any = {
+            if (lazyObj == null)
+                try
+                    lazyObj = U.mkObject(objClassName)
+                catch {
+                    // TODO:
+                    case e: Throwable => throw new NCE(s"Error initializing object of type: $objClassName", e)
+                }
+
+            lazyObj
+        }
+    }
+
     /**
       * Gives a list of JAR files at given path.
       *
@@ -668,7 +695,7 @@
                 val mf = makeModelFactory(mft)
 
                 mf.initialize(Config.modelFactoryProps.getOrElse(Map.empty[String, String]).asJava)
-                
+
                 mf
 
             case None => new NCBasicModelFactory
@@ -987,7 +1014,7 @@
     @throws[NCE]
     private def mkChunk(mdl: NCModel, chunk: String): NCProbeSynonymChunk = {
         def stripSuffix(fix: String, s: String): String = s.slice(fix.length, s.length - fix.length)
-        
+
         val mdlId = mdl.getId
 
         // Regex synonym.
@@ -1087,14 +1114,14 @@
         s"#${argIdx + (if (cxtFirstParam) 1 else 0)} of ${method2Str(mtd)}"
 
     /**
-      *
-      * @param mtd
+      * @param mo
       * @param mdl
       * @param intent
       */
     @throws[NCE]
-    private def prepareCallback(mtd: Method, mdl: NCModel, intent: NCIdlIntent): Callback = {
+    private def prepareCallback(mo: MethodOwner, mdl: NCModel, intent: NCIdlIntent): Callback = {
         val mdlId = mdl.getId
+        val mtd = mo.method
 
         // Checks method result type.
         if (mtd.getReturnType != CLS_QRY_RES)
@@ -1195,13 +1222,11 @@
         checkMinMax(mdl, mtd, tokParamTypes, termIds.map(allLimits), ctxFirstParam)
 
         Callback(
-            mtd.toString,
-            mtd.getDeclaringClass.getName,
-            mtd.getName,
+            mtd,
             (ctx: NCIntentMatch) => {
                 invoke(
-                    mtd,
-                    mdl,
+                    mdl.getId,
+                    mo,
                     (
                         (if (ctxFirstParam) Seq(ctx)
                         else Seq.empty) ++
@@ -1213,29 +1238,26 @@
     }
 
     /**
-      *
-      * @param mtd
-      * @param mdl
+      * @param mdlId
+      * @param mo
       * @param args
       */
     @throws[NCE]
-    private def invoke(mtd: Method, mdl: NCModel, args: Array[AnyRef]): NCResult = {
-        val mdlId = mdl.getId
+    private def invoke(mdlId: String, mo: MethodOwner, args: Array[AnyRef]): NCResult = {
+        val obj = if (Modifier.isStatic(mo.method.getModifiers)) null else mo.getObject
 
-        val obj = if (Modifier.isStatic(mtd.getModifiers)) null else mdl
-
-        var flag = mtd.canAccess(obj)
+        var flag = mo.method.canAccess(obj)
 
         try {
             if (!flag) {
-                mtd.setAccessible(true)
+                mo.method.setAccessible(true)
 
                 flag = true
             }
             else
                 flag = false
 
-            mtd.invoke(obj, args: _*).asInstanceOf[NCResult]
+            mo.method.invoke(obj, args: _*).asInstanceOf[NCResult]
         }
         catch {
             case e: InvocationTargetException => e.getTargetException match {
@@ -1245,25 +1267,25 @@
                 case e: Throwable =>
                     throw new NCE(s"Intent callback invocation error [" +
                         s"mdlId=$mdlId, " +
-                        s"callback=${method2Str(mtd)}" +
+                        s"callback=${method2Str(mo.method)}" +
                     s"]", e)
             }
 
             case e: Throwable =>
                 throw new NCE(s"Unexpected intent callback invocation error [" +
                     s"mdlId=$mdlId, " +
-                    s"callback=${method2Str(mtd)}" +
+                    s"callback=${method2Str(mo.method)}" +
                 s"]", e)
         }
         finally
             if (flag)
                 try
-                    mtd.setAccessible(false)
+                    mo.method.setAccessible(false)
                 catch {
                     case e: SecurityException =>
                         throw new NCE(s"Access or security error in intent callback [" +
                             s"mdlId=$mdlId, " +
-                            s"callback=${method2Str(mtd)}" +
+                            s"callback=${method2Str(mo.method)}" +
                         s"]", e)
                 }
     }
@@ -1498,18 +1520,24 @@
       * @param o Object.
       * @return Methods.
       */
-    private def getAllMethods(o: AnyRef): Set[Method] = {
-        val claxx = o.getClass
+    private def getAllMethods(o: AnyRef): Set[Method] = getAllMethods(o.getClass)
 
-        (claxx.getDeclaredMethods ++ claxx.getMethods).toSet
-    }
-    
+    /**
+      * Gets its own methods including private and accessible from parents.
+      *
+      * @param claxx Class.
+      * @return Methods.
+      */
+    private def getAllMethods(claxx: Class[_]): Set[Method] = (claxx.getDeclaredMethods ++ claxx.getMethods).toSet
+
     /**
       *
       * @param mdl
       */
     @throws[NCE]
     private def scanIntents(mdl: NCModel): Set[Intent] = {
+        val cl = Thread.currentThread().getContextClassLoader
+
         val mdlId = mdl.getId
         val intentDecls = mutable.Buffer.empty[NCIdlIntent]
         val intents = mutable.Buffer.empty[Intent]
@@ -1517,58 +1545,63 @@
         // First, get intent declarations from the JSON/YAML file, if any.
         mdl match {
             case adapter: NCModelFileAdapter =>
-                intentDecls ++= adapter
-                    .getIntents
-                    .asScala
-                    .flatMap(NCIdlCompiler.compileIntents(_, mdl, mdl.getOrigin))
-
+                intentDecls ++= adapter.getIntents.asScala.flatMap(NCIdlCompiler.compileIntents(_, mdl, mdl.getOrigin))
             case _ => ()
         }
 
+        def processClass(cls: Class[_]): Unit =
+            if (cls != null)
+                try
+                    for (
+                        ann <- cls.getAnnotationsByType(CLS_INTENT);
+                        intent <- NCIdlCompiler.compileIntents(ann.value(), mdl, cls.getName)
+                    )
+                        if (intentDecls.exists(_.id == intent.id))
+                            throw new NCE(s"Duplicate intent ID [" +
+                                s"mdlId=$mdlId, " +
+                                s"origin=${mdl.getOrigin}, " +
+                                s"class=$cls, " +
+                                s"id=${intent.id}" +
+                            s"]")
+                        else
+                            intentDecls += intent
+                catch {
+                    case _: ClassNotFoundException => throw new NCE(s"Failed to scan class for @NCIntent annotation: $cls")
+                }
+
         // Second, scan class for class-level @NCIntent annotations (intent declarations).
-        val mdlCls = mdl.meta[String](MDL_META_MODEL_CLASS_KEY)
+        processClass(Class.forName(mdl.meta[String](MDL_META_MODEL_CLASS_KEY)))
 
-        if (mdlCls != null) {
-            try {
-                val cls = Class.forName(mdlCls)
+        def processMethod(mo: MethodOwner): Unit = {
+            val m = mo.method
 
-                for (ann <- cls.getAnnotationsByType(CLS_INTENT); intent <- NCIdlCompiler.compileIntents(ann.value(), mdl, mdlCls))
-                    if (intentDecls.exists(_.id == intent.id))
-                        throw new NCE(s"Duplicate intent ID [" +
-                            s"mdlId=$mdlId, " +
-                            s"origin=${mdl.getOrigin}, " +
-                            s"class=$mdlCls, " +
-                            s"id=${intent.id}" +
-                        s"]")
-                    else
-                        intentDecls += intent
-            }
-            catch {
-                case _: ClassNotFoundException => throw new NCE(s"Failed to scan class for @NCIntent annotation: $mdlCls")
-            }
-        }
-
-        // Third, scan all methods for intent-callback bindings.
-        for (m <- getAllMethods(mdl)) {
             val mtdStr = method2Str(m)
 
-            def bindIntent(intent: NCIdlIntent, cb: Callback): Unit = {
+            def bindIntent(intent: NCIdlIntent, cb: Callback): Unit =
                 if (intents.exists(i => i._1.id == intent.id && i._2.id != cb.id))
                     throw new NCE(s"The intent cannot be bound to more than one callback [" +
                         s"mdlId=$mdlId, " +
                         s"origin=${mdl.getOrigin}, " +
-                        s"class=$mdlCls, " +
+                        s"class=${mo.objClassName}, " +
                         s"intentId=${intent.id}" +
                     s"]")
                 else {
                     intentDecls += intent
-                    intents += (intent -> prepareCallback(m, mdl, intent))
+                    intents += (intent -> prepareCallback(mo, mdl, intent))
                 }
-            }
+
+            def existsForOtherMethod(id: String): Boolean =
+                intents.find(_._1.id == id) match {
+                    case Some((_, cb)) => cb.method != m
+                    case None => false
+                }
 
             // Process inline intent declarations by @NCIntent annotation.
-            for (ann <- m.getAnnotationsByType(CLS_INTENT); intent <- NCIdlCompiler.compileIntents(ann.value(), mdl, mtdStr))
-                if (intentDecls.exists(_.id == intent.id) || intents.exists(_._1.id == intent.id))
+            for (
+                ann <- m.getAnnotationsByType(CLS_INTENT);
+                intent <- NCIdlCompiler.compileIntents(ann.value(), mdl, mtdStr)
+            )
+                if (intentDecls.exists(_.id == intent.id && existsForOtherMethod(intent.id)))
                     throw new NCE(s"Duplicate intent ID [" +
                         s"mdlId=$mdlId, " +
                         s"origin=${mdl.getOrigin}, " +
@@ -1576,25 +1609,81 @@
                         s"id=${intent.id}" +
                     s"]")
                 else
-                    bindIntent(intent, prepareCallback(m, mdl, intent))
+                    bindIntent(intent, prepareCallback(mo, mdl, intent))
 
             // Process intent references from @NCIntentRef annotation.
             for (ann <- m.getAnnotationsByType(CLS_INTENT_REF)) {
                 val refId = ann.value().trim
 
                 intentDecls.find(_.id == refId) match {
-                    case Some(intent) => bindIntent(intent, prepareCallback(m, mdl, intent))
+                    case Some(intent) => bindIntent(intent, prepareCallback(mo, mdl, intent))
                     case None => throw new NCE(
                         s"""@NCIntentRef("$refId") references unknown intent ID [""" +
                             s"mdlId=$mdlId, " +
                             s"origin=${mdl.getOrigin}, " +
                             s"refId=$refId, " +
                             s"callback=$mtdStr" +
-                        s"]")
+                        s"]"
+                    )
                 }
             }
         }
 
+        // Third, scan all methods for intent-callback bindings.
+        for (m <- getAllMethods(mdl))
+            processMethod(MethodOwner(method = m, objClassName = null, obj = mdl))
+
+        /**
+         *
+         * @param clazz
+         * @param getReferences
+         * @tparam T
+         */
+        def scanAdditionalClasses[T <: Annotation](clazz: Class[T], getReferences: T => Seq[Class[_]]): Unit = {
+            val anns = mdl.getClass.getAnnotationsByType(clazz)
+
+            if (anns != null && anns.nonEmpty) {
+                val refs = getReferences(anns.head)
+
+                if (refs == null || refs.isEmpty)
+                    throw new NCE(
+                        s"Additional reference in @${clazz.getSimpleName} annotation is empty [" +
+                            s"mdlId=$mdlId, " +
+                            s"origin=${mdl.getOrigin}" +
+                        s"]"
+                    )
+
+                for (ref <- refs if !Modifier.isAbstract(ref.getModifiers)) {
+                    processClass(ref)
+
+                    for (m <- getAllMethods(ref))
+                        processMethod(MethodOwner(method = m, objClassName = ref.getName, obj = null))
+                }
+            }
+        }
+
+        // Process @NCModelAddClasses annotation.
+        scanAdditionalClasses(CLS_MDL_CLS_REF, (a: NCModelAddClasses) => a.value())
+
+        // Process @NCModelAddPackages annotation.
+        scanAdditionalClasses(
+            CLS_MDL_PKGS_REF,
+            (a: NCModelAddPackage) =>
+                a.value().flatMap(p => {
+                    if (cl.getDefinedPackage(p) == null)
+                        throw new NCE(
+                            s"Invalid additional references in @${CLS_MDL_PKGS_REF.getSimpleName} annotation [" +
+                                s"mdlId=$mdlId, " +
+                                s"origin=${mdl.getOrigin}, " +
+                                s"package=$p" +
+                            s"]"
+                        )
+
+                    //noinspection UnstableApiUsage
+                    ClassPath.from(cl).getTopLevelClassesRecursive(p).asScala.map(_.load())
+                })
+        )
+
         val unusedIntents = intentDecls.filter(i => !intents.exists(_._1.id == i.id))
 
         if (unusedIntents.nonEmpty)
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/NCIntentSampleSpec.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/NCIntentSampleSpec.scala
index 5e14b07..2efbbd2 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/NCIntentSampleSpec.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/NCIntentSampleSpec.scala
@@ -19,7 +19,7 @@
 
 import org.apache.nlpcraft.NCTestElement
 import org.apache.nlpcraft.model.tools.test.NCTestAutoModelValidator
-import org.junit.jupiter.api.{Assertions, Test}
+import org.junit.jupiter.api.Test
 
 import java.util
 import scala.language.implicitConversions
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCModelNestedSamplesSpec.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCModelNestedSamplesSpec.scala
new file mode 100644
index 0000000..0295983
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCModelNestedSamplesSpec.scala
@@ -0,0 +1,64 @@
+/*
+ * 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.probe.mgrs.deploy
+
+import org.apache.nlpcraft.model.tools.embedded.NCEmbeddedProbe
+import org.apache.nlpcraft.model.{NCIntent, NCIntentSample, NCModelAdapter, NCResult}
+import org.apache.nlpcraft.probe.mgrs.model.NCModelManager
+import org.junit.jupiter.api.Test
+
+/**
+ *
+ */
+class NCModelNested extends NCModelAdapter("nlpcraft.samples.test.mdl", "Test Model", "1.0") {
+    /**
+     *
+     * @return
+     */
+    @NCIntentSample(Array("a"))
+    @NCIntent("intent=nested term={tok_id() == 'a'}*")
+    def m(): NCResult = NCResult.text("OK")
+}
+
+/**
+ *
+ */
+class NCModelWrapper extends NCModelNested
+
+/**
+ *
+ */
+class NCModelNestedSamplesSpec {
+    /**
+     *
+     */
+    @Test
+    def test(): Unit = {
+        try {
+            NCEmbeddedProbe.start("nlpcraft.conf", java.util.Collections.singleton(classOf[NCModelWrapper].getName))
+
+            val mdls = NCModelManager.getAllModels()
+
+            require(mdls.size == 1)
+
+            require(mdls.head.samples.nonEmpty)
+        }
+        finally
+            NCEmbeddedProbe.stop()
+    }
+}
\ No newline at end of file
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCModelReferencesSpec.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCModelReferencesSpec.scala
new file mode 100644
index 0000000..35af10e
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCModelReferencesSpec.scala
@@ -0,0 +1,123 @@
+/*
+ * 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.probe.mgrs.deploy
+
+import org.apache.nlpcraft.model.{NCElement, NCModelAdapter, NCModelAddPackage}
+import org.apache.nlpcraft.{NCTestContext, NCTestElement, NCTestEnvironment}
+import org.junit.jupiter.api.Test
+
+import java.util
+
+/**
+  *
+  */
+@NCTestEnvironment(model = classOf[org.apache.nlpcraft.probe.mgrs.deploy.scalatest.NCModelClassesWrapper], startClient = true)
+class NCModelClassesWrapperScalaSpec extends NCTestContext {
+    /**
+     *
+     */
+    @Test
+    def test(): Unit = {
+        checkIntent("scalaClass", "scalaClass")
+        checkIntent("scalaStatic", "scalaStatic")
+        checkFail("javaClass")
+        checkFail("javaStatic")
+    }
+}
+
+/**
+ *
+ */
+@NCTestEnvironment(model = classOf[org.apache.nlpcraft.probe.mgrs.deploy.scalatest.NCModelPackagesWrapper], startClient = true)
+class NCModelPackagesWrapperScalaSpec extends NCTestContext {
+    /**
+     *
+     */
+    @Test
+    def test(): Unit = {
+        checkIntent("scalaClass", "scalaClass")
+        checkIntent("scalaStatic", "scalaStatic")
+        checkFail("javaClass")
+        checkFail("javaStatic")
+    }
+}
+
+/**
+ *
+ */
+@NCTestEnvironment(model = classOf[org.apache.nlpcraft.probe.mgrs.deploy.javatest.NCModelClassesWrapper], startClient = true)
+class NCModelClassesWrapperJavaSpec extends NCTestContext {
+    /**
+     *
+     */
+    @Test
+    def test(): Unit = {
+        checkIntent("javaClass", "javaClass")
+        checkIntent("javaStatic", "javaStatic")
+        checkFail("scalaClass")
+        checkFail("scalaStatic")
+    }
+}
+
+/**
+ *
+ */
+@NCTestEnvironment(model = classOf[org.apache.nlpcraft.probe.mgrs.deploy.javatest.NCModelPackagesWrapper], startClient = true)
+class NCModelPackagesWrapperJavaSpec extends NCTestContext {
+    /**
+     *
+     */
+    @Test
+    def test(): Unit = {
+        checkIntent("javaClass", "javaClass")
+        checkIntent("javaStatic", "javaStatic")
+        checkFail("scalaClass")
+        checkFail("scalaStatic")
+    }
+}
+
+/**
+ *
+ */
+@NCModelAddPackage(Array("org.apache.nlpcraft.probe.mgrs.deploy"))
+class NCModelPackagesWrapperMix extends NCModelAdapter("nlpcraft.deploy.test.mix.mdl", "Test Model", "1.0") {
+    override def getElements: util.Set[NCElement] =
+        Set(
+            NCTestElement("scalaClass"),
+            NCTestElement("scalaStatic"),
+            NCTestElement("javaClass"),
+            NCTestElement("javaStatic")
+        )
+}
+
+/**
+ *
+ */
+@NCTestEnvironment(model = classOf[org.apache.nlpcraft.probe.mgrs.deploy.NCModelPackagesWrapperMix], startClient = true)
+class NCModelPackagesWrapperMixSpec extends NCTestContext {
+    /**
+     *
+     */
+    @Test
+    def test(): Unit = {
+        checkIntent("scalaClass", "scalaClass")
+        checkIntent("scalaStatic", "scalaStatic")
+        checkIntent("javaClass", "javaClass")
+        checkIntent("javaStatic", "javaStatic")
+    }
+}
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/javatest/NCModelClassesWrapper.java b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/javatest/NCModelClassesWrapper.java
new file mode 100644
index 0000000..d59e43a
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/javatest/NCModelClassesWrapper.java
@@ -0,0 +1,28 @@
+/*
+ * 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.probe.mgrs.deploy.javatest;
+
+import org.apache.nlpcraft.model.NCModelAddClasses;
+
+/**
+ *
+ */
+@NCModelAddClasses({NCNestedClass.class, NCNestedStatic.class})
+public class NCModelClassesWrapper extends NCModelDeploySpecAdapter {
+    // No-op.
+}
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/javatest/NCModelDeploySpecAdapter.java b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/javatest/NCModelDeploySpecAdapter.java
new file mode 100644
index 0000000..b4ab85a
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/javatest/NCModelDeploySpecAdapter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.probe.mgrs.deploy.javatest;
+
+import org.apache.nlpcraft.model.NCElement;
+import org.apache.nlpcraft.model.NCModelAdapter;
+
+import java.util.Set;
+
+/**
+ *
+ */
+class NCModelDeploySpecAdapter extends NCModelAdapter {
+    /**
+     *
+     */
+    public NCModelDeploySpecAdapter() {
+        super("nlpcraft.deploy.test.java", "Test Model", "1.0");
+    }
+
+    @Override
+    public Set<NCElement> getElements() {
+        return Set.of((NCElement) () -> "javaClass", (NCElement) () -> "javaStatic");
+    }
+}
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/javatest/NCModelPackagesWrapper.java b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/javatest/NCModelPackagesWrapper.java
new file mode 100644
index 0000000..a03fcb6
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/javatest/NCModelPackagesWrapper.java
@@ -0,0 +1,28 @@
+/*
+ * 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.probe.mgrs.deploy.javatest;
+
+import org.apache.nlpcraft.model.NCModelAddPackage;
+
+/**
+ *
+ */
+@NCModelAddPackage("org.apache.nlpcraft.probe.mgrs.deploy.javatest")
+public class NCModelPackagesWrapper extends NCModelDeploySpecAdapter {
+    // No-op.
+}
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/javatest/NCNestedClass.java b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/javatest/NCNestedClass.java
new file mode 100644
index 0000000..131f02e
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/javatest/NCNestedClass.java
@@ -0,0 +1,31 @@
+/*
+ * 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.probe.mgrs.deploy.javatest;
+
+import org.apache.nlpcraft.model.NCIntent;
+import org.apache.nlpcraft.model.NCResult;
+
+/**
+ *
+ */
+public class NCNestedClass {
+    @NCIntent("intent=javaClass term={tok_id() == 'javaClass'}")
+    public NCResult m() {
+        return NCResult.text("OK");
+    }
+}
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/javatest/NCNestedStatic.java b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/javatest/NCNestedStatic.java
new file mode 100644
index 0000000..a91faa9
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/javatest/NCNestedStatic.java
@@ -0,0 +1,31 @@
+/*
+ * 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.probe.mgrs.deploy.javatest;
+
+import org.apache.nlpcraft.model.NCIntent;
+import org.apache.nlpcraft.model.NCResult;
+
+/**
+ *
+ */
+public class NCNestedStatic {
+    @NCIntent("intent=javaStatic term={tok_id() == 'javaStatic'}")
+    public static NCResult m() {
+        return NCResult.text("OK");
+    }
+}
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/scalatest/NCModelClassesWrapper.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/scalatest/NCModelClassesWrapper.scala
new file mode 100644
index 0000000..6443deb
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/scalatest/NCModelClassesWrapper.scala
@@ -0,0 +1,26 @@
+/*
+ * 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.probe.mgrs.deploy.scalatest
+
+import org.apache.nlpcraft.model.NCModelAddClasses
+
+/**
+ *
+ */
+@NCModelAddClasses(Array(classOf[NCNestedClass], classOf[NCNestedStatic.type]))
+class NCModelClassesWrapper extends NCModelDeploySpecAdapter
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/scalatest/NCModelDeploySpecAdapter.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/scalatest/NCModelDeploySpecAdapter.scala
new file mode 100644
index 0000000..ba605e6
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/scalatest/NCModelDeploySpecAdapter.scala
@@ -0,0 +1,31 @@
+/*
+ * 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.probe.mgrs.deploy.scalatest
+
+import org.apache.nlpcraft.NCTestElement
+import org.apache.nlpcraft.model.{NCElement, NCModelAdapter}
+
+import java.util
+import scala.language.implicitConversions
+
+/**
+ *
+ */
+class NCModelDeploySpecAdapter extends NCModelAdapter("nlpcraft.deploy.test.scala", "Test Model", "1.0") {
+    override def getElements: util.Set[NCElement] = Set(NCTestElement("scalaClass"), NCTestElement("scalaStatic"))
+}
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/scalatest/NCModelPackagesWrapper.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/scalatest/NCModelPackagesWrapper.scala
new file mode 100644
index 0000000..5bff402
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/scalatest/NCModelPackagesWrapper.scala
@@ -0,0 +1,26 @@
+/*
+ * 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.probe.mgrs.deploy.scalatest
+
+import org.apache.nlpcraft.model.NCModelAddPackage
+
+/**
+ *
+ */
+@NCModelAddPackage(Array("org.apache.nlpcraft.probe.mgrs.deploy.scalatest"))
+class NCModelPackagesWrapper extends NCModelDeploySpecAdapter
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/scalatest/NCNestedClass.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/scalatest/NCNestedClass.scala
new file mode 100644
index 0000000..352777b
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/scalatest/NCNestedClass.scala
@@ -0,0 +1,31 @@
+/*
+ * 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.probe.mgrs.deploy.scalatest
+
+import org.apache.nlpcraft.model.{NCIntent, NCResult}
+
+/**
+ *
+ */
+class NCNestedClass {
+    /**
+     *
+     */
+    @NCIntent("intent=scalaClass term={tok_id() == 'scalaClass'}")
+    def m(): NCResult = NCResult.text("OK")
+}
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/scalatest/NCNestedStatic.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/scalatest/NCNestedStatic.scala
new file mode 100644
index 0000000..9790efb
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/deploy/scalatest/NCNestedStatic.scala
@@ -0,0 +1,32 @@
+/*
+ * 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.probe.mgrs.deploy.scalatest
+
+import org.apache.nlpcraft.model.{NCIntent, NCResult}
+
+/**
+ *
+ */
+object NCNestedStatic {
+    /**
+     *
+     * @return
+     */
+    @NCIntent("intent=scalaStatic term={tok_id() == 'scalaStatic'}")
+    def m(): NCResult = NCResult.text("OK")
+}
\ No newline at end of file