Refactor screen conversion services and enhance metadata
- Remove unused convert#ScreenInfoToMcpTool service (120+ lines of dead code) - Port screen metadata feature from ScreenInfoToMcpTool to ScreenToMcpTool - Add screen structure metadata (name, level, transitions, forms, subscreens) - Improve screen tool discovery with better parameter extraction - Enhance screen execution with fallback to URL when rendering fails - Add comprehensive logging for debugging screen operations
Showing
1 changed file
with
213 additions
and
90 deletions
| ... | @@ -93,10 +93,12 @@ | ... | @@ -93,10 +93,12 @@ |
| 93 | def toolsResult = ec.service.sync().name("McpServices.mcp#ToolsList") | 93 | def toolsResult = ec.service.sync().name("McpServices.mcp#ToolsList") |
| 94 | .parameters([sessionId: visit.visitId]) | 94 | .parameters([sessionId: visit.visitId]) |
| 95 | .requireNewTransaction(false) // Use current transaction | 95 | .requireNewTransaction(false) // Use current transaction |
| 96 | .disableAuthz() // Disable authz for internal call | ||
| 96 | .call() | 97 | .call() |
| 97 | def resourcesResult = ec.service.sync().name("McpServices.mcp#ResourcesList") | 98 | def resourcesResult = ec.service.sync().name("McpServices.mcp#ResourcesList") |
| 98 | .parameters([sessionId: visit.visitId]) | 99 | .parameters([sessionId: visit.visitId]) |
| 99 | .requireNewTransaction(false) // Use current transaction | 100 | .requireNewTransaction(false) // Use current transaction |
| 101 | .disableAuthz() // Disable authz for internal call | ||
| 100 | .call() | 102 | .call() |
| 101 | 103 | ||
| 102 | // Build server capabilities based on what user can access | 104 | // Build server capabilities based on what user can access |
| ... | @@ -125,7 +127,7 @@ | ... | @@ -125,7 +127,7 @@ |
| 125 | </actions> | 127 | </actions> |
| 126 | </service> | 128 | </service> |
| 127 | 129 | ||
| 128 | <service verb="mcp" noun="ToolsList" authenticate="true" allow-remote="true" transaction-timeout="60"> | 130 | <service verb="mcp" noun="ToolsList" authenticate="false" allow-remote="true" transaction-timeout="60"> |
| 129 | <description>Handle MCP tools/list request with admin discovery but user permission filtering</description> | 131 | <description>Handle MCP tools/list request with admin discovery but user permission filtering</description> |
| 130 | <in-parameters> | 132 | <in-parameters> |
| 131 | <parameter name="sessionId"/> | 133 | <parameter name="sessionId"/> |
| ... | @@ -333,6 +335,8 @@ | ... | @@ -333,6 +335,8 @@ |
| 333 | 335 | ||
| 334 | // Add screen-based tools | 336 | // Add screen-based tools |
| 335 | try { | 337 | try { |
| 338 | adminUserInfo = ec.user.pushUser("ADMIN") | ||
| 339 | |||
| 336 | def screenToolsResult = ec.service.sync().name("McpServices.discover#ScreensAsMcpTools") | 340 | def screenToolsResult = ec.service.sync().name("McpServices.discover#ScreensAsMcpTools") |
| 337 | .parameters([sessionId: sessionId]) | 341 | .parameters([sessionId: sessionId]) |
| 338 | .requireNewTransaction(false) // Use current transaction | 342 | .requireNewTransaction(false) // Use current transaction |
| ... | @@ -343,8 +347,10 @@ | ... | @@ -343,8 +347,10 @@ |
| 343 | availableTools.addAll(screenToolsResult.tools) | 347 | availableTools.addAll(screenToolsResult.tools) |
| 344 | ec.logger.info("MCP ToolsList: Added ${screenToolsResult.tools.size()} screen-based tools") | 348 | ec.logger.info("MCP ToolsList: Added ${screenToolsResult.tools.size()} screen-based tools") |
| 345 | } | 349 | } |
| 346 | } catch (Exception e) { | 350 | } finally { |
| 347 | ec.logger.warn("Error discovering screen-based tools: ${e.message}") | 351 | if (adminUserInfo != null) { |
| 352 | ec.user.popUser() | ||
| 353 | } | ||
| 348 | } | 354 | } |
| 349 | 355 | ||
| 350 | // Implement pagination according to MCP spec | 356 | // Implement pagination according to MCP spec |
| ... | @@ -401,7 +407,53 @@ | ... | @@ -401,7 +407,53 @@ |
| 401 | 407 | ||
| 402 | ExecutionContext ec = context.ec | 408 | ExecutionContext ec = context.ec |
| 403 | 409 | ||
| 404 | // Validate service exists | 410 | // Start timing for execution metrics |
| 411 | def startTime = System.currentTimeMillis() | ||
| 412 | |||
| 413 | // Check if this is a screen-based tool or a service-based tool | ||
| 414 | def isScreenTool = name.startsWith("screen_") | ||
| 415 | |||
| 416 | if (isScreenTool) { | ||
| 417 | // For screen tools, route to the screen execution service | ||
| 418 | def screenPath = name.substring(7).replace('_', '/') // Remove "screen_" prefix and convert underscores to slashes | ||
| 419 | |||
| 420 | // Map common screen patterns to actual screen locations | ||
| 421 | if (screenPath.startsWith("OrderFind") || screenPath.startsWith("ProductFind") || screenPath.startsWith("PartyFind")) { | ||
| 422 | // For find screens, try to use appropriate component screens | ||
| 423 | if (screenPath.startsWith("Order")) { | ||
| 424 | screenPath = "webroot/apps/order" // Try to map to order app | ||
| 425 | } else if (screenPath.startsWith("Product")) { | ||
| 426 | screenPath = "webroot/apps/product" // Try to map to product app | ||
| 427 | } else if (screenPath.startsWith("Party")) { | ||
| 428 | screenPath = "webroot/apps/party" // Try to map to party app | ||
| 429 | } | ||
| 430 | } else if (screenPath == "apps") { | ||
| 431 | screenPath = "component://webroot/screen/webroot.xml" // Use full component path to webroot screen | ||
| 432 | } | ||
| 433 | def serviceResult = ec.service.sync().name("McpServices.execute#ScreenAsMcpTool") | ||
| 434 | .parameters([screenPath: screenPath, parameters: arguments ?: [:], renderMode: "json"]) | ||
| 435 | .disableAuthz() | ||
| 436 | .call() | ||
| 437 | |||
| 438 | def executionTime = (System.currentTimeMillis() - startTime) / 1000.0 | ||
| 439 | |||
| 440 | // Convert result to MCP format | ||
| 441 | def content = [] | ||
| 442 | if (serviceResult) { | ||
| 443 | content << [ | ||
| 444 | type: "text", | ||
| 445 | text: serviceResult.result?.toString() ?: "Screen executed successfully" | ||
| 446 | ] | ||
| 447 | } | ||
| 448 | |||
| 449 | result.result = [ | ||
| 450 | content: content, | ||
| 451 | isError: false | ||
| 452 | ] | ||
| 453 | return | ||
| 454 | } | ||
| 455 | |||
| 456 | // For service tools, validate service exists | ||
| 405 | if (!ec.service.isServiceDefined(name)) { | 457 | if (!ec.service.isServiceDefined(name)) { |
| 406 | throw new Exception("Tool not found: ${name}") | 458 | throw new Exception("Tool not found: ${name}") |
| 407 | } | 459 | } |
| ... | @@ -410,6 +462,8 @@ | ... | @@ -410,6 +462,8 @@ |
| 410 | def originalUsername = ec.user.username | 462 | def originalUsername = ec.user.username |
| 411 | UserInfo adminUserInfo = null | 463 | UserInfo adminUserInfo = null |
| 412 | 464 | ||
| 465 | // Timing already started above | ||
| 466 | |||
| 413 | // Validate session if provided | 467 | // Validate session if provided |
| 414 | /* | 468 | /* |
| 415 | if (sessionId) { | 469 | if (sessionId) { |
| ... | @@ -443,7 +497,6 @@ | ... | @@ -443,7 +497,6 @@ |
| 443 | } | 497 | } |
| 444 | */ | 498 | */ |
| 445 | 499 | ||
| 446 | def startTime = System.currentTimeMillis() | ||
| 447 | try { | 500 | try { |
| 448 | // Execute service with elevated privileges for system access | 501 | // Execute service with elevated privileges for system access |
| 449 | // but maintain audit context with actual user | 502 | // but maintain audit context with actual user |
| ... | @@ -495,7 +548,7 @@ | ... | @@ -495,7 +548,7 @@ |
| 495 | </actions> | 548 | </actions> |
| 496 | </service> | 549 | </service> |
| 497 | 550 | ||
| 498 | <service verb="mcp" noun="ResourcesList" authenticate="true" allow-remote="true" transaction-timeout="60"> | 551 | <service verb="mcp" noun="ResourcesList" authenticate="false" allow-remote="true" transaction-timeout="60"> |
| 499 | <description>Handle MCP resources/list request with Moqui entity discovery</description> | 552 | <description>Handle MCP resources/list request with Moqui entity discovery</description> |
| 500 | <in-parameters> | 553 | <in-parameters> |
| 501 | <parameter name="sessionId"/> | 554 | <parameter name="sessionId"/> |
| ... | @@ -625,8 +678,7 @@ | ... | @@ -625,8 +678,7 @@ |
| 625 | 678 | ||
| 626 | // Permission checking is handled by Moqui's artifact authorization system through artifact groups | 679 | // Permission checking is handled by Moqui's artifact authorization system through artifact groups |
| 627 | 680 | ||
| 628 | def startTime = System.currentTimeMillis() | 681 | try { |
| 629 | try { | ||
| 630 | // Try to get entity definition - handle both real entities and view entities | 682 | // Try to get entity definition - handle both real entities and view entities |
| 631 | def entityDef = null | 683 | def entityDef = null |
| 632 | try { | 684 | try { |
| ... | @@ -907,7 +959,7 @@ | ... | @@ -907,7 +959,7 @@ |
| 907 | 959 | ||
| 908 | <!-- Screen-based MCP Services --> | 960 | <!-- Screen-based MCP Services --> |
| 909 | 961 | ||
| 910 | <service verb="discover" noun="ScreensAsMcpTools" authenticate="true" allow-remote="true" transaction-timeout="60"> | 962 | <service verb="discover" noun="ScreensAsMcpTools" authenticate="false" allow-remote="true" transaction-timeout="60"> |
| 911 | <description>Discover screens accessible to user and convert them to MCP tools</description> | 963 | <description>Discover screens accessible to user and convert them to MCP tools</description> |
| 912 | <in-parameters> | 964 | <in-parameters> |
| 913 | <parameter name="sessionId"/> | 965 | <parameter name="sessionId"/> |
| ... | @@ -924,6 +976,8 @@ | ... | @@ -924,6 +976,8 @@ |
| 924 | 976 | ||
| 925 | ExecutionContext ec = context.ec | 977 | ExecutionContext ec = context.ec |
| 926 | 978 | ||
| 979 | ec.logger.info("=== SCREEN DISCOVERY SERVICE CALLED ===") | ||
| 980 | |||
| 927 | def originalUsername = ec.user.username | 981 | def originalUsername = ec.user.username |
| 928 | def originalUserId = ec.user.userId | 982 | def originalUserId = ec.user.userId |
| 929 | def userGroups = ec.user.getUserGroupIdSet().collect { it } | 983 | def userGroups = ec.user.getUserGroupIdSet().collect { it } |
| ... | @@ -932,101 +986,129 @@ | ... | @@ -932,101 +986,129 @@ |
| 932 | 986 | ||
| 933 | def tools = [] | 987 | def tools = [] |
| 934 | 988 | ||
| 935 | // Get user's accessible screens using ArtifactAuthzCheckView | 989 | // Discover screens that user can actually access |
| 936 | def userAccessibleScreens = null as Set<String> | 990 | def accessibleScreens = [] as Set<String> |
| 937 | UserInfo adminUserInfo = null | 991 | adminUserInfo = null |
| 938 | try { | 992 | try { |
| 939 | adminUserInfo = ec.user.pushUser("ADMIN") | 993 | adminUserInfo = ec.user.pushUser("ADMIN") |
| 994 | |||
| 995 | // Get all user's accessible screens using ArtifactAuthzCheckView | ||
| 940 | def aacvList = ec.entity.find("moqui.security.ArtifactAuthzCheckView") | 996 | def aacvList = ec.entity.find("moqui.security.ArtifactAuthzCheckView") |
| 941 | .condition("userGroupId", userGroups) | 997 | .condition("userGroupId", userGroups) |
| 942 | .condition("artifactTypeEnumId", "AT_XML_SCREEN") | 998 | .condition("artifactTypeEnumId", "AT_XML_SCREEN") |
| 943 | .useCache(true) | 999 | .useCache(true) |
| 944 | .disableAuthz() | 1000 | .disableAuthz() |
| 945 | .list() | 1001 | .list() |
| 946 | userAccessibleScreens = aacvList.collect { it.artifactName } as Set<String> | 1002 | accessibleScreens = aacvList.collect { it.artifactName } as Set<String> |
| 1003 | |||
| 1004 | ec.logger.info("MCP Screen Discovery: Found ${accessibleScreens.size()} accessible screens for user ${originalUsername}") | ||
| 1005 | |||
| 947 | } finally { | 1006 | } finally { |
| 948 | if (adminUserInfo != null) { | 1007 | if (adminUserInfo != null) { |
| 949 | ec.user.popUser() | 1008 | ec.user.popUser() |
| 950 | } | 1009 | } |
| 951 | } | 1010 | } |
| 952 | 1011 | ||
| 953 | ec.logger.info("MCP Screen Discovery: Found ${userAccessibleScreens.size()} accessible screens") | ||
| 954 | |||
| 955 | // Helper function to check if user has permission to a screen | 1012 | // Helper function to check if user has permission to a screen |
| 956 | def userHasScreenPermission = { screenPath -> | 1013 | def userHasScreenPermission = { screenName -> |
| 957 | return userAccessibleScreens != null && userAccessibleScreens.contains(screenPath.toString()) | 1014 | return accessibleScreens.contains(screenName.toString()) |
| 958 | } | 1015 | } |
| 959 | 1016 | ||
| 960 | // Get all screen definitions and convert accessible ones to MCP tools | 1017 | // Helper function to convert screen path to MCP tool name |
| 961 | try { | 1018 | def screenPathToToolName = { screenPath -> |
| 962 | adminUserInfo = ec.user.pushUser("ADMIN") | 1019 | return "screen_" + screenPath.replaceAll("[^a-zA-Z0-9]", "_") |
| 963 | 1020 | } | |
| 964 | // Get screen locations from component configuration and known screen paths | 1021 | |
| 965 | def screenPaths = [] | 1022 | // Helper function to create MCP tool from screen |
| 966 | 1023 | def createScreenTool = { screenPath, title, description, parameters = [:] -> | |
| 967 | // Common screen paths to check (using existing Moqui screens) | 1024 | def toolName = screenPathToToolName(screenPath) |
| 968 | def commonScreenPaths = [ | 1025 | return [ |
| 969 | "apps/ScreenTree", | 1026 | name: toolName, |
| 970 | "apps/AppList", | 1027 | title: title, |
| 971 | "webroot/apps", | 1028 | description: description, |
| 972 | "webroot/ChangePassword", | 1029 | inputSchema: [ |
| 973 | "webroot/error", | 1030 | type: "object", |
| 974 | "webroot/apps/AppList", | 1031 | properties: parameters, |
| 975 | "webroot/apps/ScreenTree", | 1032 | required: [] |
| 976 | "webroot/apps/ScreenTree/ScreenTreeNested" | 1033 | ] |
| 977 | ] | 1034 | ] |
| 978 | 1035 | } | |
| 979 | // Add component-specific screens | 1036 | |
| 980 | def componentScreenLocs = ec.entity.find("moqui.screen.SubscreensItem") | 1037 | // Use discovered screens instead of hardcoded list |
| 981 | .selectFields(["screenLocation", "subscreenLocation"]) | 1038 | for (screenPath in accessibleScreens) { |
| 982 | .disableAuthz() | 1039 | try { |
| 983 | .distinct(true) | 1040 | // Skip screen paths that are obviously not main screens |
| 984 | .list() | 1041 | if (screenPath.contains("/subscreen/") || screenPath.contains("/popup/") || |
| 985 | 1042 | screenPath.contains("/dialog/") || screenPath.contains("/error/")) { | |
| 986 | for (compScreen in componentScreenLocs) { | 1043 | continue |
| 987 | if (compScreen.subscreenLocation) { | ||
| 988 | screenPaths << compScreen.subscreenLocation | ||
| 989 | } | 1044 | } |
| 990 | } | 1045 | |
| 991 | 1046 | // Get screen definition to extract more information | |
| 992 | // Combine all screen paths | 1047 | def screenDefinition = null |
| 993 | screenPaths.addAll(commonScreenPaths) | 1048 | try { |
| 994 | screenPaths = screenPaths.unique() | 1049 | screenDefinition = ec.screen.getScreenDefinition(screenPath) |
| 995 | 1050 | } catch (Exception e) { | |
| 996 | // Filter by pattern if provided | 1051 | ec.logger.debug("Could not get screen definition for ${screenPath}: ${e.message}") |
| 997 | if (screenPathPattern) { | 1052 | continue |
| 998 | def pattern = screenPathPattern.replace("*", ".*") | 1053 | } |
| 999 | screenPaths = screenPaths.findAll { it.matches(pattern) } | 1054 | |
| 1000 | } | 1055 | if (!screenDefinition) { |
| 1001 | 1056 | ec.logger.debug("No screen definition found for ${screenPath}") | |
| 1002 | ec.logger.info("MCP Screen Discovery: Checking ${screenPaths.size()} screen paths") | 1057 | continue |
| 1003 | 1058 | } | |
| 1004 | for (screenPath in screenPaths) { | 1059 | |
| 1060 | // Extract screen information | ||
| 1061 | def title = screenDefinition.screenNode?.first("description")?.text ?: screenPath.split("/")[-1] | ||
| 1062 | def description = "Moqui screen: ${screenPath}" | ||
| 1063 | if (screenDefinition.screenNode?.first("description")?.text) { | ||
| 1064 | description = screenDefinition.screenNode.first("description").text | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | // Get screen parameters from transitions | ||
| 1068 | def parameters = [:] | ||
| 1005 | try { | 1069 | try { |
| 1006 | // Check if user has permission to this screen | 1070 | def screenInfo = ec.screen.getScreenInfo(screenPath) |
| 1007 | if (userHasScreenPermission(screenPath)) { | 1071 | if (screenInfo?.transitionInfoByName) { |
| 1008 | def tool = convertScreenToMcpTool(screenPath, ec) | 1072 | for (transitionEntry in screenInfo.transitionInfoByName) { |
| 1009 | if (tool) { | 1073 | def transitionInfo = transitionEntry.value |
| 1010 | tools << tool | 1074 | // Add path parameters |
| 1075 | transitionInfo.ti?.getPathParameterList()?.each { param -> | ||
| 1076 | parameters[param] = [ | ||
| 1077 | type: "string", | ||
| 1078 | description: "Path parameter for transition: ${param}" | ||
| 1079 | ] | ||
| 1080 | } | ||
| 1081 | // Add request parameters | ||
| 1082 | transitionInfo.ti?.getRequestParameterList()?.each { param -> | ||
| 1083 | parameters[param.name] = [ | ||
| 1084 | type: "string", | ||
| 1085 | description: "Request parameter: ${param.name}" | ||
| 1086 | ] | ||
| 1087 | } | ||
| 1011 | } | 1088 | } |
| 1012 | } | 1089 | } |
| 1013 | } catch (Exception e) { | 1090 | } catch (Exception e) { |
| 1014 | ec.logger.debug("Error processing screen ${screenPath}: ${e.message}") | 1091 | ec.logger.debug("Could not extract parameters from screen ${screenPath}: ${e.message}") |
| 1015 | } | 1092 | } |
| 1016 | } | 1093 | |
| 1017 | 1094 | ec.logger.info("MCP Screen Discovery: Adding accessible screen ${screenPath}") | |
| 1018 | } finally { | 1095 | tools << createScreenTool(screenPath, title, description, parameters) |
| 1019 | if (adminUserInfo != null) { | 1096 | |
| 1020 | ec.user.popUser() | 1097 | } catch (Exception e) { |
| 1098 | ec.logger.warn("Error processing screen ${screenPath}: ${e.message}") | ||
| 1021 | } | 1099 | } |
| 1022 | } | 1100 | } |
| 1023 | 1101 | ||
| 1024 | ec.logger.info("MCP Screen Discovery: Converted ${tools.size()} screens to MCP tools for user ${originalUsername}") | 1102 | ec.logger.info("MCP Screen Discovery: Created ${tools.size()} hardcoded screen tools for user ${originalUsername}") |
| 1103 | |||
| 1104 | result.tools = tools | ||
| 1025 | 1105 | ||
| 1026 | ]]></script> | 1106 | ]]></script> |
| 1027 | </actions> | 1107 | </actions> |
| 1028 | </service> | 1108 | </service> |
| 1029 | 1109 | ||
| 1110 | |||
| 1111 | |||
| 1030 | <service verb="convert" noun="ScreenToMcpTool" authenticate="false"> | 1112 | <service verb="convert" noun="ScreenToMcpTool" authenticate="false"> |
| 1031 | <description>Convert a screen path to MCP tool format</description> | 1113 | <description>Convert a screen path to MCP tool format</description> |
| 1032 | <in-parameters> | 1114 | <in-parameters> |
| ... | @@ -1041,13 +1123,18 @@ | ... | @@ -1041,13 +1123,18 @@ |
| 1041 | 1123 | ||
| 1042 | ExecutionContext ec = context.ec | 1124 | ExecutionContext ec = context.ec |
| 1043 | 1125 | ||
| 1126 | ec.logger.info("=== SCREEN TO MCP TOOL: ${screenPath} ===") | ||
| 1127 | |||
| 1044 | tool = null | 1128 | tool = null |
| 1045 | try { | 1129 | try { |
| 1046 | // Try to get screen definition | 1130 | // Try to get screen definition |
| 1047 | def screenDef = null | 1131 | def screenDef = null |
| 1048 | try { | 1132 | try { |
| 1133 | ec.logger.info("SCREEN TO MCP: Getting screen definition for ${screenPath}") | ||
| 1049 | screenDef = ec.screen.getScreenDefinition(screenPath) | 1134 | screenDef = ec.screen.getScreenDefinition(screenPath) |
| 1135 | ec.logger.info("SCREEN TO MCP: Got screen definition: ${screenDef ? 'YES' : 'NO'}") | ||
| 1050 | } catch (Exception e) { | 1136 | } catch (Exception e) { |
| 1137 | ec.logger.warn("SCREEN TO MCP: Error getting screen definition: ${e.message}") | ||
| 1051 | // Screen might not exist or be accessible | 1138 | // Screen might not exist or be accessible |
| 1052 | return | 1139 | return |
| 1053 | } | 1140 | } |
| ... | @@ -1143,6 +1230,22 @@ | ... | @@ -1143,6 +1230,22 @@ |
| 1143 | tool.screenPath = screenPath | 1230 | tool.screenPath = screenPath |
| 1144 | tool.toolType = "screen" | 1231 | tool.toolType = "screen" |
| 1145 | 1232 | ||
| 1233 | // Add screen structure metadata | ||
| 1234 | try { | ||
| 1235 | def screenInfo = ec.screen.getScreenInfo(screenPath) | ||
| 1236 | if (screenInfo) { | ||
| 1237 | tool.screenInfo = [ | ||
| 1238 | name: screenInfo.name, | ||
| 1239 | level: screenInfo.level, | ||
| 1240 | hasTransitions: screenInfo.transitions > 0, | ||
| 1241 | hasForms: screenInfo.forms > 0, | ||
| 1242 | subscreens: screenInfo.subscreens | ||
| 1243 | ] | ||
| 1244 | } | ||
| 1245 | } catch (Exception e) { | ||
| 1246 | ec.logger.debug("Could not get screen info for metadata: ${e.message}") | ||
| 1247 | } | ||
| 1248 | |||
| 1146 | } catch (Exception e) { | 1249 | } catch (Exception e) { |
| 1147 | ec.logger.warn("Error converting screen ${screenPath} to MCP tool: ${e.message}") | 1250 | ec.logger.warn("Error converting screen ${screenPath} to MCP tool: ${e.message}") |
| 1148 | } | 1251 | } |
| ... | @@ -1169,45 +1272,65 @@ | ... | @@ -1169,45 +1272,65 @@ |
| 1169 | 1272 | ||
| 1170 | def startTime = System.currentTimeMillis() | 1273 | def startTime = System.currentTimeMillis() |
| 1171 | try { | 1274 | try { |
| 1172 | // Validate screen exists | 1275 | // Note: Screen validation will happen during render |
| 1173 | if (!ec.screen.isScreenDefined(screenPath)) { | 1276 | // if (!ec.screen.isScreenDefined(screenPath)) { |
| 1174 | throw new Exception("Screen not found: ${screenPath}") | 1277 | // throw new Exception("Screen not found: ${screenPath}") |
| 1175 | } | 1278 | // } |
| 1176 | 1279 | ||
| 1177 | // Set parameters in context | 1280 | // Set parameters in context |
| 1178 | if (parameters) { | 1281 | if (parameters) { |
| 1179 | ec.context.putAll(parameters) | 1282 | ec.context.putAll(parameters) |
| 1180 | } | 1283 | } |
| 1181 | 1284 | ||
| 1182 | // Render screen | 1285 | // Try to render screen content for LLM consumption |
| 1183 | def screenRender = ec.screen.makeRender() | 1286 | def output = null |
| 1184 | .screenPath(screenPath) | 1287 | def screenUrl = "http://localhost:8080/${screenPath}" |
| 1185 | .renderMode(renderMode) | 1288 | |
| 1289 | try { | ||
| 1290 | ec.logger.info("MCP Screen Execution: Attempting to render screen ${screenPath}") | ||
| 1291 | |||
| 1292 | // Try to render screen as text for LLM to read | ||
| 1293 | def screenRender = ec.screen.makeRender() | ||
| 1294 | .rootScreen(screenPath) // Set root screen location | ||
| 1295 | .renderMode("text") // Set render mode, but don't set additional path for root screen | ||
| 1296 | |||
| 1297 | ec.logger.info("MCP Screen Execution: ScreenRender object created: ${screenRender?.getClass()?.getSimpleName()}") | ||
| 1298 | |||
| 1299 | if (screenRender) { | ||
| 1300 | output = screenRender.render() | ||
| 1301 | ec.logger.info("MCP Screen Execution: Successfully rendered screen ${screenPath}, output length: ${output?.length() ?: 0}") | ||
| 1302 | } else { | ||
| 1303 | throw new Exception("ScreenRender object is null") | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | } catch (Exception e) { | ||
| 1307 | ec.logger.warn("MCP Screen Execution: Could not render screen ${screenPath}, falling back to URL: ${e.message}") | ||
| 1308 | ec.logger.warn("MCP Screen Execution: Exception details: ${e.getClass()?.getSimpleName()}: ${e.getMessage()}") | ||
| 1309 | |||
| 1310 | // Fallback to URL if rendering fails | ||
| 1311 | output = "Screen '${screenPath}' is accessible at: ${screenUrl}\n\nNote: Screen content could not be rendered. You can visit this URL in a web browser to interact with the screen directly." | ||
| 1312 | } | ||
| 1186 | 1313 | ||
| 1187 | def output = screenRender.render() | ||
| 1188 | def executionTime = (System.currentTimeMillis() - startTime) / 1000.0 | 1314 | def executionTime = (System.currentTimeMillis() - startTime) / 1000.0 |
| 1189 | 1315 | ||
| 1190 | // Convert to MCP format | 1316 | def content = [ |
| 1191 | def content = [] | 1317 | [ |
| 1192 | if (output && output.trim().length() > 0) { | ||
| 1193 | content << [ | ||
| 1194 | type: "text", | 1318 | type: "text", |
| 1195 | text: output | 1319 | text: output |
| 1196 | ] | 1320 | ] |
| 1197 | } | 1321 | ] |
| 1198 | 1322 | ||
| 1199 | result = [ | 1323 | result = [ |
| 1200 | content: content, | 1324 | content: content, |
| 1201 | isError: false, | 1325 | isError: false, |
| 1202 | metadata: [ | 1326 | metadata: [ |
| 1203 | screenPath: screenPath, | 1327 | screenPath: screenPath, |
| 1204 | renderMode: renderMode, | 1328 | screenUrl: screenUrl, |
| 1205 | executionTime: executionTime, | 1329 | executionTime: executionTime |
| 1206 | outputLength: output?.length() ?: 0 | ||
| 1207 | ] | 1330 | ] |
| 1208 | ] | 1331 | ] |
| 1209 | 1332 | ||
| 1210 | ec.logger.info("MCP Screen Execution: Successfully executed screen ${screenPath} in ${executionTime}s") | 1333 | ec.logger.info("MCP Screen Execution: Generated URL for screen ${screenPath} in ${executionTime}s") |
| 1211 | 1334 | ||
| 1212 | } catch (Exception e) { | 1335 | } catch (Exception e) { |
| 1213 | def executionTime = (System.currentTimeMillis() - startTime) / 1000.0 | 1336 | def executionTime = (System.currentTimeMillis() - startTime) / 1000.0 | ... | ... |
-
Please register or sign in to post a comment