McpServices.mcp.RenderScreenNarrative.xml 5.61 KB
<?xml version="1.0" encoding="UTF-8"?>
<!--
This software is in the public domain under CC0 1.0 Universal plus a 
Grant of Patent License.

To the extent possible under law, author(s) have dedicated all
copyright and related and neighboring rights to this software to the
public domain worldwide. This software is distributed without any
warranty.
-->
<services xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/service-3.xsd">

    <service verb="mcp" noun="RenderScreenNarrative" authenticate="true" allow-remote="true" transaction-timeout="120">
        <description>Render a screen with semantic state extraction and UI narrative generation.</description>
        <in-parameters>
            <parameter name="path" required="true"/>
            <parameter name="parameters" type="Map"/>
            <parameter name="renderMode" default="mcp"/>
            <parameter name="sessionId"/>
        </in-parameters>
        <out-parameters>
            <parameter name="result" type="Map"/>
        </out-parameters>
        <actions>
            <script><![CDATA[
                import org.moqui.context.ExecutionContext
                
                ExecutionContext ec = context.ec
                def renderedContent = null
                def semanticState = null
                def uiNarrative = null
                
                // Resolve screen path
                def resolveResult = ec.service.sync().name("McpServices.mcp#ResolveScreenPath")
                    .parameter("path", path).call()
                
                def screenPath = resolveResult.screenPath
                def subscreenName = resolveResult.subscreenName
                
                if (!screenPath) {
                    result = [renderedContent: null, semanticState: null, uiNarrative: null]
                    return
                }
                
                // Build render parameters
                def screenCallParams = [
                    path: screenPath,
                    parameters: parameters ?: [:],
                    renderMode: renderMode ?: "mcp",
                    sessionId: sessionId
                ]
                if (subscreenName) screenCallParams.subscreenName = subscreenName
                
                // Render screen using ScreenAsMcpTool
                try {
                    def serviceResult = ec.service.sync().name("McpServices.execute#ScreenAsMcpTool")
                        .parameters(screenCallParams).call()
                    
                    // Extract semantic state from rendered result
                    if (serviceResult) {
                        def resultObj = null
                        if (serviceResult.containsKey('content') && serviceResult.content && serviceResult.content.size() > 0) {
                            def rawText = serviceResult.content[0].text
                            if (rawText && rawText.startsWith("{")) {
                                try { resultObj = new groovy.json.JsonSlurper().parseText(rawText) } catch(e) {}
                            }
                            renderedContent = rawText
                        } else if (serviceResult.containsKey('result') && serviceResult.result && serviceResult.result.content && serviceResult.result.content.size() > 0) {
                            def rawText = serviceResult.result.content[0].text
                            if (rawText && rawText.startsWith("{")) {
                                try { resultObj = new groovy.json.JsonSlurper().parseText(rawText) } catch(e) {}
                            }
                            renderedContent = rawText
                        }
                        
                        // Generate UI narrative if we have semantic state
                        if (resultObj && resultObj.semanticState) {
                            semanticState = resultObj.semanticState
                            
                            try {
                                def narrativeBuilder = new org.moqui.mcp.UiNarrativeBuilder()
                                def screenDefForNarrative = ec.screen.getScreenDefinition(screenPath)
                                
                                uiNarrative = narrativeBuilder.buildNarrative(
                                    screenDefForNarrative,
                                    semanticState,
                                    path
                                )
                                ec.logger.info("RenderScreenNarrative: Generated UI narrative for ${path}")
                            } catch (Exception e) {
                                ec.logger.warn("RenderScreenNarrative: Failed to generate UI narrative: ${e.message}")
                            }
                            
                            // Truncate content if we have UI narrative to save tokens
                            if (renderedContent && renderedContent.length() > 500) {
                                renderedContent = renderedContent.take(500) + "... (truncated, see uiNarrative for actions)"
                            }
                        }
                    }
                } catch (Exception e) {
                    ec.logger.warn("RenderScreenNarrative: Error rendering screen ${path}: ${e.message}")
                    renderedContent = "RENDER_ERROR: ${e.message}"
                }
                
                result = [
                    renderedContent: renderedContent,
                    semanticState: semanticState,
                    uiNarrative: uiNarrative
                ]
            ]]></script>
        </actions>
    </service>

</services>