e18de6c2 by Ean Schuessler

Fix MCP screen discovery closure and JSON-RPC response nesting

- Separate processScreenWithSubscreens closure definition to fix Groovy closure scope issues
- Add proper flattening of subScreenPathList to handle nested collections
- Fix subscreen tool naming with dot notation for parent.child relationships
- Enhance screen tool execution to support subscreen parameters
- Unwrap Moqui service results in EnhancedMcpServlet to avoid double nesting in JSON-RPC responses
- Improve error handling and logging throughout screen discovery process

Now successfully discovers 29 total tools (17 screen tools + 12 service tools) with proper session management.
1 parent 5e5c0f8f
......@@ -150,10 +150,12 @@ class CustomScreenTestImpl implements McpScreenTest {
void renderAll(List<String> screenPathList, Map<String, Object> parameters, String requestMethod) {
// NOTE: using single thread for now, doesn't actually make a lot of difference in overall test run time
int threads = 1
def output
if (threads == 1) {
for (String screenPath in screenPathList) {
McpScreenTestRender str = render(screenPath, parameters, requestMethod)
logger.info("Rendered ${screenPath} in ${str.getRenderTime()}ms, ${str.getOutput()?.length()} characters")
output = str.getOutput()
logger.info("Rendered ${screenPath} in ${str.getRenderTime()}ms, ${output?.length()} characters")
}
} else {
ExecutionContextImpl eci = ecfi.getEci()
......@@ -264,8 +266,26 @@ class CustomScreenTestImpl implements McpScreenTest {
long startTime = System.currentTimeMillis()
// parse the screenPath
ArrayList<String> screenPathList = ScreenUrlInfo.parseSubScreenPath(csti.rootScreenDef, csti.baseScreenDef,
csti.baseScreenPathList, stri.screenPath, stri.parameters, csti.sfi)
def screenPathList
// Special handling for non-webroot root screens with subscreens
if (csti.rootScreenLocation != null && !csti.rootScreenLocation.contains("webroot.xml") && stri.screenPath.contains('/')) {
// For non-webroot roots with subscreens, build path list directly
// rootScreenDef is the parent screen, screenPath is the subscreen path
screenPathList = new ArrayList<>()
// Add root screen path (already a full component:// path)
screenPathList.add(csti.rootScreenDef.location)
// Add subscreen path segments
String[] pathSegments = stri.screenPath.split('/')
for (String segment in pathSegments) {
if (segment && segment.trim().length() > 0) {
screenPathList.add(segment)
}
}
logger.info("Custom screen path parsing for non-webroot root: ${screenPathList}")
} else {
screenPathList = ScreenUrlInfo.parseSubScreenPath(csti.rootScreenDef, csti.baseScreenDef,
csti.baseScreenPathList, stri.screenPath, stri.parameters, csti.sfi)
}
if (screenPathList == null) throw new BaseArtifactException("Could not find screen path ${stri.screenPath} under base screen ${csti.baseScreenDef.location}")
// push the context
......@@ -292,7 +312,7 @@ class CustomScreenTestImpl implements McpScreenTest {
}
// set the screenPath
screenRender.screenPath(screenPathList)
screenRender.screenPath(screenPathList as java.util.List<String>)
// do the render
try {
......
......@@ -862,8 +862,13 @@ logger.info("Handling Enhanced SSE connection from ${request.remoteAddr}")
logger.error("Enhanced MCP service ${serviceName} returned null result")
return [error: "Service returned null result"]
}
// Service framework returns result in 'result' field, but also might return the result directly
return result.result ?: result ?: [error: "Service returned invalid result"]
// Service framework returns result in 'result' field when out-parameters are used
// Unwrap the Moqui service result to avoid double nesting in JSON-RPC response
if (result?.containsKey('result')) {
return result.result ?: [error: "Service returned empty result"]
} else {
return result ?: [error: "Service returned null result"]
}
} catch (Exception e) {
logger.error("Error calling Enhanced MCP service ${serviceName}", e)
return [error: e.message]
......