Use real web sessions for action execution
Remove mock/test session requirement when action != null This allows database updates to persist when action is executed through MCP.
Showing
1 changed file
with
90 additions
and
86 deletions
| ... | @@ -606,7 +606,8 @@ | ... | @@ -606,7 +606,8 @@ |
| 606 | <description>Execute a screen as an MCP tool</description> | 606 | <description>Execute a screen as an MCP tool</description> |
| 607 | <in-parameters> | 607 | <in-parameters> |
| 608 | <parameter name="screenPath" required="true"/> | 608 | <parameter name="screenPath" required="true"/> |
| 609 | <parameter name="parameters" type="Map"><description>Parameters to pass to the screen</description></parameter> | 609 | <parameter name="parameters" type="Map"><description>Parameters to pass to screen</description></parameter> |
| 610 | <parameter name="action"><description>Action being processed: if not null, use real screen rendering instead of test mock</description></parameter> | ||
| 610 | <parameter name="renderMode" default="mcp"><description>Render mode: mcp, text, html, xml, vuet, qvt</description></parameter> | 611 | <parameter name="renderMode" default="mcp"><description>Render mode: mcp, text, html, xml, vuet, qvt</description></parameter> |
| 611 | <parameter name="sessionId"><description>Session ID for user context restoration</description></parameter> | 612 | <parameter name="sessionId"><description>Session ID for user context restoration</description></parameter> |
| 612 | <parameter name="subscreenName"><description>Optional subscreen name for dot notation paths</description></parameter> | 613 | <parameter name="subscreenName"><description>Optional subscreen name for dot notation paths</description></parameter> |
| ... | @@ -623,109 +624,112 @@ ExecutionContext ec = context.ec | ... | @@ -623,109 +624,112 @@ ExecutionContext ec = context.ec |
| 623 | 624 | ||
| 624 | def startTime = System.currentTimeMillis() | 625 | def startTime = System.currentTimeMillis() |
| 625 | 626 | ||
| 626 | // Set parameters in context | 627 | // Set parameters in context |
| 627 | if (parameters) { | 628 | if (parameters) { |
| 628 | ec.context.putAll(parameters) | 629 | ec.context.putAll(parameters) |
| 629 | } | 630 | } |
| 630 | |||
| 631 | // Try to render screen content for LLM consumption | ||
| 632 | def output = null | ||
| 633 | def screenUrl = "http://localhost:8080/${screenPath}" | ||
| 634 | def isError = false | ||
| 635 | |||
| 636 | try { | ||
| 637 | ec.logger.info("MCP Screen Execution: Attempting to render screen ${screenPath} using ScreenTest with proper root screen") | ||
| 638 | 631 | ||
| 639 | // For ScreenTest to work properly, we need to use the correct root screen | 632 | // Check if action is being processed - use real screen rendering if so |
| 640 | def testScreenPath = screenPath | 633 | def isActionExecution = parameters?.action != null |
| 641 | def rootScreen = "component://webroot/screen/webroot.xml" | ||
| 642 | 634 | ||
| 643 | def targetScreenDef = null | 635 | // Try to render screen content for LLM consumption |
| 644 | def isStandalone = false | 636 | def output = null |
| 637 | def screenUrl = "http://localhost:8080/${screenPath}" | ||
| 638 | def isError = false | ||
| 645 | 639 | ||
| 646 | if (screenPath.startsWith("component://")) { | 640 | try { |
| 647 | def pathAfterComponent = screenPath.substring(12).replace('.xml','') // Remove "component://" | 641 | ec.logger.info("MCP Screen Execution: Attempting to render screen ${screenPath} using ScreenTest with proper root screen, action=${parameters?.action}") |
| 648 | def pathParts = pathAfterComponent.split("/") | ||
| 649 | |||
| 650 | // Check if the target screen itself is standalone | ||
| 651 | try { | ||
| 652 | targetScreenDef = ec.screen.getScreenDefinition(screenPath) | ||
| 653 | if (targetScreenDef?.screenNode) { | ||
| 654 | def standaloneAttr = targetScreenDef.screenNode.attribute('standalone') | ||
| 655 | isStandalone = standaloneAttr == "true" | ||
| 656 | } | ||
| 657 | |||
| 658 | if (isStandalone) { | ||
| 659 | rootScreen = screenPath | ||
| 660 | testScreenPath = "" | ||
| 661 | } | ||
| 662 | } catch (Exception e) { | ||
| 663 | ec.logger.warn("MCP Screen Execution: Error checking target screen ${screenPath}: ${e.message}") | ||
| 664 | } | ||
| 665 | 642 | ||
| 666 | if (!isStandalone) { | 643 | // For ScreenTest to work properly, we need to use correct root screen |
| 667 | // Check if the screen path itself is a valid screen definition | 644 | def testScreenPath = screenPath |
| 645 | def rootScreen = "component://webroot/screen/webroot.xml" | ||
| 646 | |||
| 647 | def targetScreenDef = null | ||
| 648 | def isStandalone = false | ||
| 649 | |||
| 650 | if (screenPath.startsWith("component://")) { | ||
| 651 | def pathAfterComponent = screenPath.substring(12).replace('.xml','') // Remove "component://" | ||
| 652 | def pathParts = pathAfterComponent.split("/") | ||
| 653 | |||
| 654 | // Check if target screen itself is standalone | ||
| 668 | try { | 655 | try { |
| 669 | if (ec.screen.getScreenDefinition(screenPath)) { | 656 | targetScreenDef = ec.screen.getScreenDefinition(screenPath) |
| 657 | if (targetScreenDef?.screenNode) { | ||
| 658 | def standaloneAttr = targetScreenDef.screenNode.attribute('standalone') | ||
| 659 | isStandalone = standaloneAttr == "true" | ||
| 660 | } | ||
| 661 | |||
| 662 | if (isStandalone) { | ||
| 670 | rootScreen = screenPath | 663 | rootScreen = screenPath |
| 671 | testScreenPath = "" | 664 | testScreenPath = "" |
| 672 | } else { | 665 | } |
| 673 | // Original component root logic | 666 | } catch (Exception e) { |
| 674 | if (pathAfterComponent.startsWith("webroot/screen/")) { | 667 | ec.logger.warn("MCP Screen Execution: Error checking target screen ${screenPath}: ${e.message}") |
| 675 | rootScreen = "component://webroot/screen/webroot.xml" | 668 | } |
| 676 | testScreenPath = pathAfterComponent.substring("webroot/screen/".length()) | 669 | |
| 677 | if (testScreenPath.startsWith("webroot/")) { | 670 | if (!isStandalone) { |
| 678 | testScreenPath = testScreenPath.substring("webroot/".length()) | 671 | // Check if screen path itself is a valid screen definition |
| 679 | } | 672 | try { |
| 673 | if (ec.screen.getScreenDefinition(screenPath)) { | ||
| 674 | rootScreen = screenPath | ||
| 675 | testScreenPath = "" | ||
| 680 | } else { | 676 | } else { |
| 681 | def componentName = pathParts[0] | 677 | // Original component root logic |
| 682 | def remainingPath = pathParts[1..-1].join("/") | 678 | if (pathAfterComponent.startsWith("webroot/screen/")) { |
| 683 | 679 | rootScreen = "component://webroot/screen/webroot.xml" | |
| 684 | // Try to find the actual root screen for this component | 680 | testScreenPath = pathAfterComponent.substring("webroot/screen/".length()) |
| 685 | def componentRootScreen = null | 681 | if (testScreenPath.startsWith("webroot/")) { |
| 686 | def possibleRootScreens = ["${componentName}.xml", "${componentName}Admin.xml", "${componentName}Root.xml"] | 682 | testScreenPath = testScreenPath.substring("webroot/".length()) |
| 687 | 683 | } | |
| 688 | for (rootScreenName in possibleRootScreens) { | ||
| 689 | def candidateRoot = "component://${componentName}/screen/${rootScreenName}" | ||
| 690 | try { | ||
| 691 | if (ec.screen.getScreenDefinition(candidateRoot)) { | ||
| 692 | componentRootScreen = candidateRoot | ||
| 693 | break | ||
| 694 | } | ||
| 695 | } catch (Exception e) {} | ||
| 696 | } | ||
| 697 | |||
| 698 | if (componentRootScreen) { | ||
| 699 | rootScreen = componentRootScreen | ||
| 700 | testScreenPath = remainingPath | ||
| 701 | } else { | 684 | } else { |
| 702 | rootScreen = screenPath | 685 | rootScreen = screenPath |
| 703 | testScreenPath = "" | 686 | testScreenPath = "" |
| 704 | } | 687 | } |
| 705 | } | 688 | } |
| 706 | } | 689 | } catch (Exception e) {} |
| 707 | } catch (Exception e) { | ||
| 708 | // Same as above fallback | ||
| 709 | rootScreen = screenPath | ||
| 710 | testScreenPath = "" | ||
| 711 | } | 690 | } |
| 691 | |||
| 692 | } else { | ||
| 693 | rootScreen = screenPath | ||
| 694 | testScreenPath = "" | ||
| 712 | } | 695 | } |
| 713 | } | ||
| 714 | 696 | ||
| 715 | // Regular screen rendering with current user context - use our custom ScreenTestImpl | 697 | // Regular screen rendering with current user context - use real rendering if action is being processed |
| 716 | def screenTest = new org.moqui.mcp.CustomScreenTestImpl(ec.ecfi) | 698 | def screenTest = null |
| 699 | def screenUrl = "http://localhost:8080/${screenPath}" | ||
| 700 | |||
| 701 | if (isActionExecution) { | ||
| 702 | // Action is being processed - use real screen rendering with database access | ||
| 703 | ec.logger.info("MCP Screen Execution: Action detected, using real screen rendering for ${screenPath}") | ||
| 704 | screenTest = ec.screen.makeTestScreen() | ||
| 717 | .rootScreen(rootScreen) | 705 | .rootScreen(rootScreen) |
| 718 | .renderMode(renderMode ? renderMode : "mcp") | 706 | .renderMode(renderMode ? renderMode : "mcp") |
| 719 | .auth(ec.user.username) | 707 | .auth(ec.user.username) |
| 720 | 708 | ||
| 721 | def renderParams = parameters ?: [:] | 709 | def renderParams = parameters ?: [:] |
| 722 | renderParams.userId = ec.user.userId | 710 | renderParams.userId = ec.user.userId |
| 723 | renderParams.username = ec.user.username | 711 | renderParams.username = ec.user.username |
| 724 | 712 | ||
| 725 | def relativePath = subscreenName ? subscreenName.replaceAll('_','/') : testScreenPath | 713 | def relativePath = subscreenName ? subscreenName.replaceAll('_','/') : testScreenPath |
| 726 | ec.logger.info("TESTRENDER root=${rootScreen} path=${relativePath} params=${renderParams}") | 714 | ec.logger.info("REALRENDER root=${rootScreen} path=${relativePath} params=${renderParams}") |
| 727 | 715 | ||
| 728 | def testRender = screenTest.render(relativePath, renderParams, "POST") | 716 | def realRender = screenTest.render(relativePath, renderParams, "POST") |
| 717 | } else { | ||
| 718 | // Regular browse - use ScreenTest mock | ||
| 719 | ec.logger.info("MCP Screen Execution: No action detected, using ScreenTest mock") | ||
| 720 | screenTest = new org.moqui.mcp.CustomScreenTestImpl(ec.ecfi) | ||
| 721 | .rootScreen(rootScreen) | ||
| 722 | .renderMode(renderMode ? renderMode : "mcp") | ||
| 723 | .auth(ec.user.username) | ||
| 724 | |||
| 725 | def renderParams = parameters ?: [:] | ||
| 726 | renderParams.userId = ec.user.userId | ||
| 727 | renderParams.username = ec.user.username | ||
| 728 | |||
| 729 | def relativePath = subscreenName ? subscreenName.replaceAll('_','/') : testScreenPath | ||
| 730 | ec.logger.info("TESTRENDER root=${rootScreen} path=${relativePath} params=${renderParams}") | ||
| 731 | |||
| 732 | def testRender = screenTest.render(relativePath, renderParams, "POST") | ||
| 729 | output = testRender.getOutput() | 733 | output = testRender.getOutput() |
| 730 | ec.logger.info("MCP Screen Execution: Successfully rendered screen ${screenPath}, output length: ${output?.length() ?: 0}") | 734 | ec.logger.info("MCP Screen Execution: Successfully rendered screen ${screenPath}, output length: ${output?.length() ?: 0}") |
| 731 | 735 | ... | ... |
-
Please register or sign in to post a comment