2fb806c3 by Ean Schuessler

Fix screen execution notification queuing - actually queue results instead of just logging

1 parent cb2032a3
...@@ -758,535 +758,6 @@ try { ...@@ -758,535 +758,6 @@ try {
758 </actions> 758 </actions>
759 </service> 759 </service>
760 760
761
762
763
764 <!-- Screen-based MCP Services -->
765
766 <service verb="discover" noun="ScreensAsMcpTools" authenticate="false" allow-remote="true" transaction-timeout="60">
767 <description>Discover screens accessible to user and convert them to MCP tools</description>
768 <in-parameters>
769 <parameter name="sessionId"/>
770 <parameter name="screenPathPattern" required="false"><description>Optional pattern to filter screen paths (supports wildcards)</description></parameter>
771 </in-parameters>
772 <out-parameters>
773 <parameter name="tools" type="List"/>
774 </out-parameters>
775 <actions>
776 <script><![CDATA[
777 import org.moqui.context.ExecutionContext
778 import org.moqui.impl.context.UserFacadeImpl.UserInfo
779 import org.moqui.impl.screen.ScreenDefinition
780
781 ExecutionContext ec = context.ec
782
783 ec.logger.info("=== SCREEN DISCOVERY SERVICE CALLED ===")
784
785 def originalUsername = ec.user.username
786 def originalUserId = ec.user.userId
787 def userGroups = ec.user.getUserGroupIdSet().collect { it }
788
789 ec.logger.info("MCP Screen Discovery: Starting for user ${originalUsername} (${originalUserId}) with groups ${userGroups}")
790
791 def tools = []
792
793 // Discover screens that user can actually access
794 def accessibleScreens = [] as Set<String>
795 adminUserInfo = null
796 try {
797 adminUserInfo = ec.user.pushUser("ADMIN")
798
799 // Get all user's accessible screens using ArtifactAuthzCheckView
800 def aacvList = ec.entity.find("moqui.security.ArtifactAuthzCheckView")
801 .condition("userGroupId", userGroups)
802 .condition("artifactTypeEnumId", "AT_XML_SCREEN")
803 .useCache(true)
804 .disableAuthz()
805 .list()
806 accessibleScreens = aacvList.collect { it.artifactName } as Set<String>
807
808 ec.logger.info("MCP Screen Discovery: Found ${accessibleScreens.size()} accessible screens for user ${originalUsername}")
809
810 } finally {
811 if (adminUserInfo != null) {
812 ec.user.popUser()
813 }
814 }
815
816 // Helper function to check if user has permission to a screen
817 def userHasScreenPermission = { screenName ->
818 return accessibleScreens.contains(screenName.toString())
819 }
820
821 // Helper function to convert screen path to MCP tool name
822 def screenPathToToolName = { screenPath ->
823 // Clean Encoding: strip component:// and .xml, replace / with _
824 // Preserves hyphens for readability.
825 // component://moqui-mcp-2/screen/McpTestScreen.xml -> screen_moqui-mcp-2_screen_McpTestScreen
826 def cleanPath = screenPath
827 if (cleanPath.startsWith("component://")) cleanPath = cleanPath.substring(12)
828 if (cleanPath.endsWith(".xml")) cleanPath = cleanPath.substring(0, cleanPath.length() - 4)
829
830 return "screen_" + cleanPath.replace('/', '_')
831 }
832
833 // Helper function to convert screen path to MCP tool name with subscreen support
834 def screenPathToToolNameWithSubscreens = { screenPath, parentScreenPath = null ->
835 // If we have a parent screen path, this is a subscreen - use dot notation
836 if (parentScreenPath) {
837 def parentCleanPath = parentScreenPath
838 if (parentCleanPath.startsWith("component://")) parentCleanPath = parentCleanPath.substring(12)
839 if (parentCleanPath.endsWith(".xml")) parentCleanPath = parentCleanPath.substring(0, parentCleanPath.length() - 4)
840
841 // Extract subscreen name from the full screen path
842 def subscreenName = screenPath.split("/")[-1]
843 if (subscreenName.endsWith(".xml")) subscreenName = subscreenName.substring(0, subscreenName.length() - 4)
844
845 return "screen_" + parentCleanPath.replace('/', '_') + "." + subscreenName
846 }
847
848 // Regular screen path conversion for main screens
849 return screenPathToToolName(screenPath)
850 }
851
852 // Helper function to create MCP tool from screen
853 def createScreenTool = { screenPath, title, description, parameters = [:] ->
854 def toolName = screenPathToToolName(screenPath)
855 return [
856 name: toolName,
857 title: title,
858 description: title, // Use title as description
859 inputSchema: [
860 type: "object",
861 properties: parameters,
862 required: []
863 ]
864 ]
865 }
866
867 // Helper function to recursively process screens and create tools directly
868 def processScreenWithSubscreens
869 processScreenWithSubscreens = { screenPath, parentScreenPath = null, processedScreens = null, toolsAccumulator = null, parentToolName = null, level = 1 ->
870 ec.logger.info("MCP Screen Discovery: Processing screen ${screenPath} (parent: ${parentScreenPath}, parentToolName: ${parentToolName}, level: ${level})")
871
872 // Initialize processedScreens and toolsAccumulator if null
873 if (processedScreens == null) processedScreens = [] as Set<String>
874 if (toolsAccumulator == null) toolsAccumulator = []
875
876 if (processedScreens.contains(screenPath)) {
877 ec.logger.info("MCP Screen Discovery: Already processed ${screenPath}, skipping")
878 return
879 }
880
881 processedScreens.add(screenPath)
882
883 try {
884 // Skip problematic patterns early
885 if (screenPath.contains("/error/") || screenPath.contains("/system/")) {
886 ec.logger.info("MCP Screen Discovery: Skipping system screen ${screenPath}")
887 return
888 }
889
890 // Determine if this is a subscreen
891 def isSubscreen = parentScreenPath != null
892
893 // Try to get screen definition
894 def screenDefinition = null
895 def title = screenPath.split("/")[-1]
896 def description = "Moqui screen: ${screenPath}"
897
898 try {
899 screenDefinition = ec.screen.getScreenDefinition(screenPath)
900 if (screenDefinition?.screenNode?.attribute('default-menu-title')) {
901 title = screenDefinition.screenNode.attribute('default-menu-title')
902 description = "Moqui screen: ${screenPath} (${title})"
903 }
904 } catch (Exception e) {
905 ec.logger.info("MCP Screen Discovery: No screen definition for ${screenPath}, using basic info")
906 }
907
908 // Get screen parameters from transitions
909 def parameters = [:]
910 try {
911 def screenInfo = ec.screen.getScreenInfo(screenPath)
912 if (screenInfo?.transitionInfoByName) {
913 for (transitionEntry in screenInfo.transitionInfoByName) {
914 def transitionInfo = transitionEntry.value
915 if (transitionInfo?.ti) {
916 transitionInfo.ti.getPathParameterList()?.each { param ->
917 parameters[param] = [
918 type: "string",
919 description: "Path parameter for transition: ${param}"
920 ]
921 }
922 transitionInfo.ti.getRequestParameterList()?.each { param ->
923 parameters[param.name] = [
924 type: "string",
925 description: "Request parameter: ${param.name}"
926 ]
927 }
928 }
929 }
930 }
931 } catch (Exception e) {
932 ec.logger.debug("Could not extract parameters from screen ${screenPath}: ${e.message}")
933 }
934
935 // Create tool with proper naming
936 def toolName
937 if (isSubscreen && parentToolName) {
938 // Use the passed hierarchical parent tool name
939 def subscreenName = screenPath.split("/")[-1]
940 if (subscreenName.endsWith(".xml")) subscreenName = subscreenName.substring(0, subscreenName.length() - 4)
941
942 // Use dot for first level subscreens (level 1), underscore for deeper levels (level 2+)
943 def separator = (level == 1) ? "." : "_"
944 toolName = parentToolName + separator + subscreenName
945 ec.logger.info("MCP Screen Discovery: Creating subscreen tool ${toolName} for ${screenPath} (parentToolName: ${parentToolName}, level: ${level}, separator: ${separator})")
946 } else if (isSubscreen && parentScreenPath) {
947 toolName = screenPathToToolNameWithSubscreens(screenPath, parentScreenPath)
948 ec.logger.info("MCP Screen Discovery: Creating subscreen tool ${toolName} for ${screenPath} (parent: ${parentScreenPath})")
949 } else {
950 toolName = screenPathToToolName(screenPath)
951 ec.logger.info("MCP Screen Discovery: Creating main screen tool ${toolName} for ${screenPath}")
952 }
953
954 def tool = [
955 name: toolName,
956 title: title,
957 description: title, // Use title as description instead of redundant path
958 inputSchema: [
959 type: "object",
960 properties: parameters,
961 required: []
962 ]
963 ]
964
965 ec.logger.info("MCP Screen Discovery: Adding accessible screen tool ${toolName} for ${screenPath}")
966 toolsAccumulator << tool
967
968 // Recursively process subscreens
969 try {
970 def screenInfoList = ec.screen.getScreenInfoList(screenPath, 1)
971 def screenInfo = screenInfoList?.first()
972 ec.logger.info("MCP Screen Discovery: SCREENINFO for ${screenPath}: ${screenInfo}")
973 if (screenInfo?.subscreenInfoByName) {
974 ec.logger.info("MCP Screen Discovery: Found ${screenInfo.subscreenInfoByName.size()} subscreens for ${screenPath}: ${screenInfo.subscreenInfoByName.keySet()}")
975 for (subScreenEntry in screenInfo.subscreenInfoByName) {
976 ec.logger.info("MCP Screen Discovery: ===== Processing subscreen ${subScreenEntry.key} from parent ${screenPath} =====")
977 def subScreenInfo = subScreenEntry.value
978 def subScreenPathList = subScreenInfo?.screenPath
979 ec.logger.info("MCP Screen Discovery: Processing subscreen entry - key: ${subScreenEntry.key}, location: ${subScreenPathList}, full info: ${subScreenInfo}")
980
981 // TODO: Fix these hard coded discoveries
982 // Special debug for Catalog.xml
983 if (screenPath.contains("Catalog.xml")) {
984 ec.logger.info("MCP Screen Discovery: *** CATALOG DEBUG *** Processing ${subScreenEntry.key} from Catalog.xml")
985 }
986
987 // Special handling for known Catalog.xml subscreens that point to SimpleScreens
988 def knownLocations = [
989 "dashboard": "component://SimpleScreens/screen/SimpleScreens/Catalog/dashboard.xml",
990 "Category": "component://SimpleScreens/screen/SimpleScreens/Catalog/Category.xml",
991 "Feature": "component://SimpleScreens/screen/SimpleScreens/Catalog/Feature.xml",
992 "FeatureGroup": "component://SimpleScreens/screen/SimpleScreens/Catalog/FeatureGroup.xml",
993 "Product": "component://SimpleScreens/screen/SimpleScreens/Catalog/Product.xml",
994 "Search": "component://SimpleScreens/screen/SimpleScreens/Catalog/Search.xml"
995 ]
996
997 // Try to get the actual subscreen location from multiple sources
998 def actualSubScreenPath = null
999
1000 // First, try known locations for Catalog.xml subscreens
1001 if (screenPath.contains("Catalog.xml") && knownLocations[subScreenEntry.key]) {
1002 actualSubScreenPath = knownLocations[subScreenEntry.key]
1003 ec.logger.info("MCP Screen Discovery: Using known location for ${subScreenEntry.key}: ${actualSubScreenPath}")
1004 }
1005
1006 // Then try to get location from subScreenInfo object (most reliable)
1007 if (!actualSubScreenPath && subScreenInfo?.screenPath) {
1008 // subScreenInfo.screenPath might be a list or string
1009 if (subScreenInfo.screenPath instanceof List) {
1010 // For automatic discovery screens, the path might be constructed differently
1011 def pathList = subScreenInfo.screenPath
1012 ec.logger.info("MCP Screen Discovery: SubScreenInfo path list for ${subScreenEntry.key}: ${pathList}")
1013
1014 // Try to find a valid screen path from the list
1015 for (path in pathList) {
1016 if (path && path.toString().contains(".xml")) {
1017 actualSubScreenPath = path.toString()
1018 break
1019 }
1020 }
1021 } else {
1022 actualSubScreenPath = subScreenInfo.screenPath.toString()
1023 }
1024 ec.logger.info("MCP Screen Discovery: SubScreenInfo location for ${subScreenEntry.key}: ${actualSubScreenPath}")
1025 }
1026
1027
1028 } catch (Exception childrenException) {
1029 ec.logger.info("MCP Screen Discovery: children() approach failed: ${childrenException.message}")
1030 // Fallback: iterate through node values
1031 subscreensNode.each { child ->
1032 if (child.name() == "subscreens-item") {
1033 subscreenItems << child
1034 }
1035 }
1036 }
1037 }
1038
1039 ec.logger.info("MCP Screen Discovery: Found ${subscreenItems.size()} subscreen-item elements: ${subscreenItems*.attributes()}")
1040 def subscreenItem = subscreenItems.find {
1041 it.attribute('name') == subScreenEntry.key
1042 }
1043 ec.logger.info("MCP Screen Discovery: Looking for subscreen item with name '${subScreenEntry.key}', found: ${subscreenItem?.attributes()}")
1044 if (subscreenItem?.attribute('location')) {
1045 actualSubScreenPath = subscreenItem.attribute('location')
1046 ec.logger.info("MCP Screen Discovery: Found actual subscreen location for ${subScreenEntry.key}: ${actualSubScreenPath}")
1047 } else {
1048 ec.logger.info("MCP Screen Discovery: Subscreen item found but no location attribute")
1049 }
1050 }
1051 } else {
1052 ec.logger.info("MCP Screen Discovery: Parent screen def has no screenNode")
1053 }
1054 } catch (Exception e) {
1055 ec.logger.info("MCP Screen Discovery: Could not get subscreen location for ${subScreenEntry.key}: ${e.message}")
1056 e.printStackTrace()
1057 }
1058 }
1059
1060 // Fallback: try to construct from screenPathList if we couldn't get the actual location
1061 if (!actualSubScreenPath && subScreenPathList) {
1062 // The first element should be subscreen name
1063 def subscreenName = subScreenEntry.key
1064 // Try to construct a reasonable path based on CURRENT screen being processed (not always the original parent)
1065 def currentScreenPath = screenPath // This is the current screen whose subscreens we're processing
1066
1067 // Generic fallback: construct based on current screen path
1068 def lastSlash = currentScreenPath.lastIndexOf('/')
1069 if (lastSlash > 0) {
1070 def basePath = currentScreenPath.substring(0, lastSlash + 1)
1071 actualSubScreenPath = basePath + subscreenName + ".xml"
1072 }
1073 ec.logger.info("MCP Screen Discovery: Constructed fallback subscreen location for ${subScreenEntry.key}: ${actualSubScreenPath}")
1074 }
1075
1076 if (actualSubScreenPath && !processedScreens.contains(actualSubScreenPath)) {
1077 ec.logger.info("MCP Screen Discovery: Processing subscreen path: ${actualSubScreenPath} ${screenPath}")
1078 processScreenWithSubscreens(actualSubScreenPath, screenPath, processedScreens, toolsAccumulator, toolName, level + 1)
1079 } else if (!actualSubScreenPath) {
1080 ec.logger.info("MCP Screen Discovery: Subscreen entry ${subScreenEntry.key} has no location, trying automatic discovery")
1081 // For screens without explicit location (like Product.xml), try automatic discovery
1082 // The subscreen location is typically based on parent screen location + subscreen name
1083 def lastSlash = screenPath.lastIndexOf('/')
1084 if (lastSlash > 0) {
1085 def basePath = screenPath.substring(0, lastSlash + 1)
1086 def autoSubScreenPath = basePath + subScreenEntry.key + ".xml"
1087 ec.logger.info("MCP Screen Discovery: Trying automatic subscreen discovery for ${subScreenEntry.key} at ${autoSubScreenPath}")
1088
1089 processScreenWithSubscreens(autoSubScreenPath, screenPath, processedScreens, toolsAccumulator, toolName, level + 1)
1090 }
1091 } else if (processedScreens.contains(actualSubScreenPath)) {
1092 ec.logger.info("MCP Screen Discovery: Subscreen ${actualSubScreenPath} already processed, skipping")
1093 }
1094 }
1095 } else {
1096 ec.logger.info("MCP Screen Discovery: No subscreens found for ${screenPath}")
1097 }
1098 } catch (Exception e) {
1099 ec.logger.info("MCP Screen Discovery: Could not get subscreens for ${screenPath}: ${e.message}")
1100 ec.logger.error("MCP Screen Discovery: Subscreen discovery error details:", e)
1101 }
1102
1103 } catch (Exception e) {
1104 ec.logger.warn("Error processing screen ${screenPath}: ${e.message}")
1105 }
1106 }
1107
1108 // Process all accessible screens recursively and create tools directly
1109 def processedScreens = [] as Set<String>
1110 ec.logger.info("MCP Screen Discovery: Starting recursive processing from ${accessibleScreens.size()} base screens")
1111 for (screenPath in accessibleScreens) {
1112 ec.logger.info("MCP Screen Discovery: SCREEN PATH ${screenPath}")
1113 processScreenWithSubscreens(screenPath, null, processedScreens, tools, null, 0)
1114 }
1115 ec.logger.info("MCP Screen Discovery: Recursive processing found ${tools.size()} total tools")
1116
1117 // Note: All screens have already been processed by processScreenWithSubscreens above
1118 // The recursive approach handles both parent screens and their subscreens in a single pass
1119 // No need for additional processing here
1120
1121
1122 ec.logger.info("MCP Screen Discovery: Created ${tools.size()} screen tools for user ${originalUsername}")
1123
1124 result.tools = tools
1125
1126 ]]></script>
1127 </actions>
1128 </service>
1129
1130
1131
1132 <service verb="convert" noun="ScreenToMcpTool" authenticate="false">
1133 <description>Convert a screen path to MCP tool format</description>
1134 <in-parameters>
1135 <parameter name="screenPath" required="true"/>
1136 </in-parameters>
1137 <out-parameters>
1138 <parameter name="tool" type="Map"/>
1139 </out-parameters>
1140 <actions>
1141 <script><![CDATA[
1142 import org.moqui.context.ExecutionContext
1143
1144 ExecutionContext ec = context.ec
1145
1146 ec.logger.info("=== SCREEN TO MCP TOOL: ${screenPath} ===")
1147
1148 tool = null
1149 try {
1150 // Try to get screen definition
1151 def screenDef = null
1152 try {
1153 ec.logger.info("SCREEN TO MCP: Getting screen definition for ${screenPath}")
1154 screenDef = ec.screen.getScreenDefinition(screenPath)
1155 ec.logger.info("SCREEN TO MCP: Got screen definition: ${screenDef ? 'YES' : 'NO'}")
1156 } catch (Exception e) {
1157 ec.logger.warn("SCREEN TO MCP: Error getting screen definition: ${e.message}")
1158 // Screen might not exist or be accessible
1159 return
1160 }
1161
1162 if (!screenDef) {
1163 return
1164 }
1165
1166 // Extract screen information
1167 // Clean Encoding
1168 def cleanPath = screenPath
1169 if (cleanPath.startsWith("component://")) cleanPath = cleanPath.substring(12)
1170 if (cleanPath.endsWith(".xml")) cleanPath = cleanPath.substring(0, cleanPath.length() - 4)
1171
1172 def screenName = cleanPath.replace('/', '_')
1173 def title = screenPath.split("/")[-1]
1174 def description = "Moqui screen: ${screenPath}"
1175
1176 // Safely get screen description - screen XML doesn't have description elements
1177 try {
1178 if (screenDef?.screenNode?.attribute('default-menu-title')) {
1179 description = screenDef.screenNode.attribute('default-menu-title')
1180 }
1181 } catch (Exception e) {
1182 ec.logger.debug("Could not get screen title: ${e.message}")
1183 }
1184
1185 // Get screen parameters from transitions and forms
1186 def parameters = [:]
1187 def required = []
1188
1189 try {
1190 // Get transitions for parameter discovery
1191 def transitions = screenDef.getTransitionMap()
1192 transitions.each { transitionName, transition ->
1193 transition.getPathParameterList().each { param ->
1194 parameters[param] = [
1195 type: "string",
1196 description: "Path parameter: ${param}"
1197 ]
1198 required << param
1199 }
1200
1201 // Get single service parameters if transition calls a service
1202 def serviceName = transition.getSingleServiceName()
1203 if (serviceName) {
1204 try {
1205 def serviceDef = ec.service.getServiceDefinition(serviceName)
1206 if (serviceDef) {
1207 def inParamNames = serviceDef.getInParameterNames()
1208 for (paramName in inParamNames) {
1209 def paramNode = serviceDef.getInParameter(paramName)
1210 def paramType = paramNode?.attribute('type') ?: 'String'
1211 def paramDesc = paramNode.first("description")?.text ?: "Parameter from service ${serviceName}"
1212
1213 // Convert Moqui type to JSON Schema type
1214 def typeMap = [
1215 "text-short": "string",
1216 "text-medium": "string",
1217 "text-long": "string",
1218 "text-very-long": "string",
1219 "id": "string",
1220 "id-long": "string",
1221 "number-integer": "integer",
1222 "number-decimal": "number",
1223 "number-float": "number",
1224 "date": "string",
1225 "date-time": "string",
1226 "date-time-nano": "string",
1227 "boolean": "boolean",
1228 "text-indicator": "boolean"
1229 ]
1230 def jsonSchemaType = typeMap[paramType] ?: "string"
1231
1232 parameters[paramName] = [
1233 type: jsonSchemaType,
1234 description: paramDesc
1235 ]
1236
1237 if (paramNode?.attribute('required') == "true") {
1238 required << paramName
1239 }
1240 }
1241 }
1242 } catch (Exception e) {
1243 ec.logger.debug("Error getting service definition for ${serviceName}: ${e.message}")
1244 }
1245 }
1246 }
1247 } catch (Exception e) {
1248 ec.logger.debug("Error getting transitions for screen ${screenPath}: ${e.message}")
1249 }
1250
1251 // Build MCP tool
1252 tool = [
1253 name: "screen_${screenName}",
1254 title: title,
1255 description: title, // Use title as description
1256 inputSchema: [
1257 type: "object",
1258 properties: parameters,
1259 required: required.unique()
1260 ]
1261 ]
1262
1263 // Add screen metadata
1264 tool.screenPath = screenPath
1265 tool.toolType = "screen"
1266
1267 // Add screen structure metadata
1268 try {
1269 def screenInfo = ec.screen.getScreenInfo(screenPath)
1270 if (screenInfo) {
1271 tool.screenInfo = [
1272 name: screenInfo.name,
1273 level: screenInfo.level,
1274 hasTransitions: screenInfo.transitions > 0,
1275 hasForms: screenInfo.forms > 0,
1276 subscreens: screenInfo.subscreens
1277 ]
1278 }
1279 } catch (Exception e) {
1280 ec.logger.debug("Could not get screen info for metadata: ${e.message}")
1281 }
1282
1283 } catch (Exception e) {
1284 ec.logger.warn("Error converting screen ${screenPath} to MCP tool: ${e.message}")
1285 }
1286 ]]></script>
1287 </actions>
1288 </service>
1289
1290 <service verb="execute" noun="ScreenAsMcpTool" authenticate="true" allow-remote="true" transaction-timeout="120"> 761 <service verb="execute" noun="ScreenAsMcpTool" authenticate="true" allow-remote="true" transaction-timeout="120">
1291 <description>Execute a screen as an MCP tool</description> 762 <description>Execute a screen as an MCP tool</description>
1292 <in-parameters> 763 <in-parameters>
...@@ -1656,7 +1127,27 @@ def startTime = System.currentTimeMillis() ...@@ -1656,7 +1127,27 @@ def startTime = System.currentTimeMillis()
1656 isError: false 1127 isError: false
1657 ] 1128 ]
1658 1129
1130 // Queue result as notification for real-time delivery
1131 try {
1132 def servlet = ec.getWeb()?.getServletContext()?.getAttribute("enhancedMcpServlet")
1133 if (servlet && sessionId) {
1134 def notification = [
1135 method: "notifications/tool_result",
1136 params: [
1137 toolName: "screen_" + screenPath.replace("/", "_").replace(".", "_"),
1138 result: result,
1139 executionTime: executionTime,
1140 timestamp: System.currentTimeMillis()
1141 ]
1142 ]
1143 servlet.queueNotification(sessionId, notification)
1659 ec.logger.info("MCP Screen Execution: Queued result as notification for screen ${screenPath} in ${executionTime}s") 1144 ec.logger.info("MCP Screen Execution: Queued result as notification for screen ${screenPath} in ${executionTime}s")
1145 } else {
1146 ec.logger.warn("MCP Screen Execution: No servlet or sessionId available for notification queuing")
1147 }
1148 } catch (Exception e) {
1149 ec.logger.warn("MCP Screen Execution: Failed to queue notification: ${e.message}")
1150 }
1660 ]]></script> 1151 ]]></script>
1661 </actions> 1152 </actions>
1662 </service> 1153 </service>
...@@ -2006,10 +1497,10 @@ def startTime = System.currentTimeMillis() ...@@ -2006,10 +1497,10 @@ def startTime = System.currentTimeMillis()
2006 } 1497 }
2007 ec.logger.info("list#Tools: Creating subscreen tool ${toolName} for ${screenPath} (parentToolName: ${parentToolName}, level: ${level})") 1498 ec.logger.info("list#Tools: Creating subscreen tool ${toolName} for ${screenPath} (parentToolName: ${parentToolName}, level: ${level})")
2008 } else if (isSubscreen && parentScreenPath) { 1499 } else if (isSubscreen && parentScreenPath) {
2009 toolName = screenPathToToolNameWithSubscreens(screenPath, parentScreenPath) 1500 toolName = parentToolName + screenPathToToolNameWithSubscreens(screenPath, parentScreenPath)
2010 ec.logger.info("list#Tools: Creating subscreen tool ${toolName} for ${screenPath} (parent: ${parentScreenPath})") 1501 ec.logger.info("list#Tools: Creating subscreen tool ${toolName} for ${screenPath} (parent: ${parentScreenPath})")
2011 } else { 1502 } else {
2012 toolName = screenPathToToolName(screenPath) 1503 toolName = parentToolName + screenPathToToolName(screenPath)
2013 ec.logger.info("list#Tools: Creating main screen tool ${toolName} for ${screenPath}") 1504 ec.logger.info("list#Tools: Creating main screen tool ${toolName} for ${screenPath}")
2014 } 1505 }
2015 1506
...@@ -2121,6 +1612,7 @@ def startTime = System.currentTimeMillis() ...@@ -2121,6 +1612,7 @@ def startTime = System.currentTimeMillis()
2121 } 1612 }
2122 1613
2123 if (actualSubScreenPath) { 1614 if (actualSubScreenPath) {
1615 ec.logger.info("list#Tools: Adding subscreen ${actualSubScreenPath} ${screenPath} ${toolName} ${level+1}")
2124 processScreenWithSubscreens(actualSubScreenPath, screenPath, processedScreens, toolsAccumulator, toolName, level + 1) 1616 processScreenWithSubscreens(actualSubScreenPath, screenPath, processedScreens, toolsAccumulator, toolName, level + 1)
2125 } else if (!actualSubScreenPath) { 1617 } else if (!actualSubScreenPath) {
2126 // For screens without explicit location, try automatic discovery 1618 // For screens without explicit location, try automatic discovery
...@@ -2128,6 +1620,7 @@ def startTime = System.currentTimeMillis() ...@@ -2128,6 +1620,7 @@ def startTime = System.currentTimeMillis()
2128 if (lastSlash > 0) { 1620 if (lastSlash > 0) {
2129 def basePath = screenPath.substring(0, lastSlash + 1) 1621 def basePath = screenPath.substring(0, lastSlash + 1)
2130 def autoSubScreenPath = basePath + subScreenEntry.key + ".xml" 1622 def autoSubScreenPath = basePath + subScreenEntry.key + ".xml"
1623 ec.logger.info("list#Tools: Constructed fallback path for ${subScreenEntry.key}: ${actualSubScreenPath}")
2131 processScreenWithSubscreens(autoSubScreenPath, screenPath, processedScreens, toolsAccumulator, toolName, level + 1) 1624 processScreenWithSubscreens(autoSubScreenPath, screenPath, processedScreens, toolsAccumulator, toolName, level + 1)
2132 } 1625 }
2133 } 1626 }
...@@ -2146,7 +1639,9 @@ def startTime = System.currentTimeMillis() ...@@ -2146,7 +1639,9 @@ def startTime = System.currentTimeMillis()
2146 def processedScreens = [] as Set<String> 1639 def processedScreens = [] as Set<String>
2147 ec.logger.info("list#Tools: Starting recursive processing from ${allScreens.size()} base screens") 1640 ec.logger.info("list#Tools: Starting recursive processing from ${allScreens.size()} base screens")
2148 for (screenPath in allScreens) { 1641 for (screenPath in allScreens) {
2149 processScreenWithSubscreens(screenPath, null, processedScreens, tools, null, 0) 1642 def parentToolPath = 'screen_' + screenPath.split('/')[-3..-3].join('_').replace('.xml', '') + '_'
1643 ec.logger.info("TOPSCREEN: ${parentToolPath}")
1644 processScreenWithSubscreens(screenPath, null, processedScreens, tools, parentToolPath, 0)
2150 } 1645 }
2151 ec.logger.info("list#Tools: Recursive processing found ${tools.size()} total tools") 1646 ec.logger.info("list#Tools: Recursive processing found ${tools.size()} total tools")
2152 1647
......