b54f0e3c by Ean Schuessler

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.
1 parent 0fc4e236
...@@ -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
......