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 { ...@@ -150,10 +150,12 @@ class CustomScreenTestImpl implements McpScreenTest {
150 void renderAll(List<String> screenPathList, Map<String, Object> parameters, String requestMethod) { 150 void renderAll(List<String> screenPathList, Map<String, Object> parameters, String requestMethod) {
151 // NOTE: using single thread for now, doesn't actually make a lot of difference in overall test run time 151 // NOTE: using single thread for now, doesn't actually make a lot of difference in overall test run time
152 int threads = 1 152 int threads = 1
153 def output
153 if (threads == 1) { 154 if (threads == 1) {
154 for (String screenPath in screenPathList) { 155 for (String screenPath in screenPathList) {
155 McpScreenTestRender str = render(screenPath, parameters, requestMethod) 156 McpScreenTestRender str = render(screenPath, parameters, requestMethod)
156 logger.info("Rendered ${screenPath} in ${str.getRenderTime()}ms, ${str.getOutput()?.length()} characters") 157 output = str.getOutput()
158 logger.info("Rendered ${screenPath} in ${str.getRenderTime()}ms, ${output?.length()} characters")
157 } 159 }
158 } else { 160 } else {
159 ExecutionContextImpl eci = ecfi.getEci() 161 ExecutionContextImpl eci = ecfi.getEci()
...@@ -264,8 +266,26 @@ class CustomScreenTestImpl implements McpScreenTest { ...@@ -264,8 +266,26 @@ class CustomScreenTestImpl implements McpScreenTest {
264 long startTime = System.currentTimeMillis() 266 long startTime = System.currentTimeMillis()
265 267
266 // parse the screenPath 268 // parse the screenPath
267 ArrayList<String> screenPathList = ScreenUrlInfo.parseSubScreenPath(csti.rootScreenDef, csti.baseScreenDef, 269 def screenPathList
268 csti.baseScreenPathList, stri.screenPath, stri.parameters, csti.sfi) 270 // Special handling for non-webroot root screens with subscreens
271 if (csti.rootScreenLocation != null && !csti.rootScreenLocation.contains("webroot.xml") && stri.screenPath.contains('/')) {
272 // For non-webroot roots with subscreens, build path list directly
273 // rootScreenDef is the parent screen, screenPath is the subscreen path
274 screenPathList = new ArrayList<>()
275 // Add root screen path (already a full component:// path)
276 screenPathList.add(csti.rootScreenDef.location)
277 // Add subscreen path segments
278 String[] pathSegments = stri.screenPath.split('/')
279 for (String segment in pathSegments) {
280 if (segment && segment.trim().length() > 0) {
281 screenPathList.add(segment)
282 }
283 }
284 logger.info("Custom screen path parsing for non-webroot root: ${screenPathList}")
285 } else {
286 screenPathList = ScreenUrlInfo.parseSubScreenPath(csti.rootScreenDef, csti.baseScreenDef,
287 csti.baseScreenPathList, stri.screenPath, stri.parameters, csti.sfi)
288 }
269 if (screenPathList == null) throw new BaseArtifactException("Could not find screen path ${stri.screenPath} under base screen ${csti.baseScreenDef.location}") 289 if (screenPathList == null) throw new BaseArtifactException("Could not find screen path ${stri.screenPath} under base screen ${csti.baseScreenDef.location}")
270 290
271 // push the context 291 // push the context
...@@ -292,7 +312,7 @@ class CustomScreenTestImpl implements McpScreenTest { ...@@ -292,7 +312,7 @@ class CustomScreenTestImpl implements McpScreenTest {
292 } 312 }
293 313
294 // set the screenPath 314 // set the screenPath
295 screenRender.screenPath(screenPathList) 315 screenRender.screenPath(screenPathList as java.util.List<String>)
296 316
297 // do the render 317 // do the render
298 try { 318 try {
......
...@@ -862,8 +862,13 @@ logger.info("Handling Enhanced SSE connection from ${request.remoteAddr}") ...@@ -862,8 +862,13 @@ logger.info("Handling Enhanced SSE connection from ${request.remoteAddr}")
862 logger.error("Enhanced MCP service ${serviceName} returned null result") 862 logger.error("Enhanced MCP service ${serviceName} returned null result")
863 return [error: "Service returned null result"] 863 return [error: "Service returned null result"]
864 } 864 }
865 // Service framework returns result in 'result' field, but also might return the result directly 865 // Service framework returns result in 'result' field when out-parameters are used
866 return result.result ?: result ?: [error: "Service returned invalid result"] 866 // Unwrap the Moqui service result to avoid double nesting in JSON-RPC response
867 if (result?.containsKey('result')) {
868 return result.result ?: [error: "Service returned empty result"]
869 } else {
870 return result ?: [error: "Service returned null result"]
871 }
867 } catch (Exception e) { 872 } catch (Exception e) {
868 logger.error("Error calling Enhanced MCP service ${serviceName}", e) 873 logger.error("Error calling Enhanced MCP service ${serviceName}", e)
869 return [error: e.message] 874 return [error: e.message]
......