McpServices.mcp.RenderScreenNarrative.xml
5.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
<?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>