Fix MCP shell session header and add timeout protection
- Fix session header variable expansion in make_mcp_request - Add MAIN_SCRIPT check to prevent main logic when sourcing - Add timeout protection around curl and jq commands - Add debug output for troubleshooting hanging issues The MCP shell now properly handles session IDs and has timeout protection to prevent hanging on long responses. Screen execution service is working but response processing needs optimization for large JSON responses.
Showing
1 changed file
with
37 additions
and
46 deletions
| ... | @@ -481,8 +481,9 @@ | ... | @@ -481,8 +481,9 @@ |
| 481 | } | 481 | } |
| 482 | 482 | ||
| 483 | // Now call the screen tool with proper user context | 483 | // Now call the screen tool with proper user context |
| 484 | def screenParams = arguments ?: [:] | ||
| 484 | serviceResult = ec.service.sync().name("McpServices.execute#ScreenAsMcpTool") | 485 | serviceResult = ec.service.sync().name("McpServices.execute#ScreenAsMcpTool") |
| 485 | .parameters([screenPath: screenPath, parameters: arguments ?: [:], renderMode: "html"]) | 486 | .parameters([screenPath: screenPath, parameters: screenParams, renderMode: "html"]) |
| 486 | .call() | 487 | .call() |
| 487 | 488 | ||
| 488 | } finally { | 489 | } finally { |
| ... | @@ -1353,57 +1354,47 @@ try { | ... | @@ -1353,57 +1354,47 @@ try { |
| 1353 | def screenUrl = "http://localhost:8080/${screenPath}" | 1354 | def screenUrl = "http://localhost:8080/${screenPath}" |
| 1354 | 1355 | ||
| 1355 | try { | 1356 | try { |
| 1356 | ec.logger.info("MCP Screen Execution: Attempting to render screen ${screenPath}") | 1357 | ec.logger.info("MCP Screen Execution: Attempting to render screen ${screenPath} using Moqui's test framework") |
| 1357 | 1358 | ||
| 1358 | // Mock web context objects that screens may expect | 1359 | // Determine appropriate base path based on screen path |
| 1359 | ec.context.put("html_scripts", new LinkedHashSet<String>()) | 1360 | def basePath = "" |
| 1360 | ec.context.put("html_stylesheets", new LinkedHashSet<String>()) | 1361 | if (screenPath.startsWith("component://")) { |
| 1361 | 1362 | basePath = "" // Use empty base path for component paths | |
| 1362 | // Mock other common web context objects | 1363 | } else if (screenPath.startsWith("apps/")) { |
| 1363 | ec.context.put("webappName", "mcp") | 1364 | basePath = "apps" // Use apps base path for apps screens |
| 1364 | ec.context.put("servletContext", [:]) | 1365 | } else if (screenPath.startsWith("webroot/")) { |
| 1365 | ec.context.put("request", [:]) | 1366 | basePath = "" // Use empty base path for webroot screens |
| 1366 | ec.context.put("response", [:]) | 1367 | } |
| 1367 | 1368 | ||
| 1368 | // Mock ec.web for getResourceDistinctValue() calls | 1369 | // Use Moqui's official test screen rendering framework |
| 1369 | def mockWeb = [ | 1370 | // This creates proper WebFacadeStub with all necessary web context objects |
| 1370 | getResourceDistinctValue: { -> return System.currentTimeMillis() } | 1371 | def screenTest = ec.screen.makeTest() |
| 1371 | ] | 1372 | .baseScreenPath(basePath) |
| 1372 | ec.context.put("web", mockWeb) | 1373 | .renderMode(renderMode ? renderMode : "text") |
| 1373 | 1374 | ||
| 1374 | // Try to render screen with specified render mode for LLM to read | 1375 | ec.logger.info("MCP Screen Execution: ScreenTest object created: ${screenTest?.getClass()?.getSimpleName()}") |
| 1375 | def screenRender = ec.screen.makeRender() | ||
| 1376 | .rootScreen(screenPath) // Set root screen location | ||
| 1377 | .renderMode(renderMode) // Set render mode from parameter, but don't set additional path for root screen | ||
| 1378 | 1376 | ||
| 1379 | // Get the real sri object and override problematic methods | 1377 | if (screenTest) { |
| 1380 | // Put mock sri in context that will be used by templates | 1378 | def renderParams = parameters ?: [:] |
| 1381 | def mockSriForContext = [ | 1379 | |
| 1382 | buildUrl: { String path -> | 1380 | // Add timeout to prevent hanging |
| 1383 | return [ | 1381 | def future = java.util.concurrent.Executors.newSingleThreadExecutor().submit({ |
| 1384 | url: path.startsWith("/") ? path : "/${path}", | 1382 | return screenTest.render(screenPath, renderParams, null) |
| 1385 | path: path, | 1383 | } as java.util.concurrent.Callable) |
| 1386 | isPermitted: { -> true }, | 1384 | |
| 1387 | toString: { -> return path.startsWith("/") ? path : "/${path}" } | 1385 | try { |
| 1388 | ] | 1386 | def testRender = future.get(30, java.util.concurrent.TimeUnit.SECONDS) // 30 second timeout |
| 1389 | }, | 1387 | output = testRender.output |
| 1390 | getThemeValues: { String enumId -> | 1388 | def outputLength = output?.length() ?: 0 |
| 1391 | return [] | 1389 | ec.logger.info("MCP Screen Execution: Successfully rendered screen ${screenPath}, output length: ${outputLength}") |
| 1392 | }, | 1390 | } catch (java.util.concurrent.TimeoutException e) { |
| 1393 | sendRedirectAndStopRender: { String redirectUrl -> | 1391 | future.cancel(true) |
| 1394 | ec.logger.info("MCP Screen Execution: Ignoring redirect to ${redirectUrl} - continuing screen render for MCP") | 1392 | throw new Exception("Screen rendering timed out after 30 seconds for ${screenPath}") |
| 1395 | // Don't actually redirect, just log and continue | 1393 | } finally { |
| 1394 | future.cancel(true) | ||
| 1396 | } | 1395 | } |
| 1397 | ] | ||
| 1398 | ec.context.put("sri", mockSriForContext) | ||
| 1399 | |||
| 1400 | ec.logger.info("MCP Screen Execution: ScreenRender object created: ${screenRender?.getClass()?.getSimpleName()}") | ||
| 1401 | |||
| 1402 | if (screenRender) { | ||
| 1403 | output = screenRender.render() | ||
| 1404 | ec.logger.info("MCP Screen Execution: Successfully rendered screen ${screenPath}, output length: ${output?.length() ?: 0}") | ||
| 1405 | } else { | 1396 | } else { |
| 1406 | throw new Exception("ScreenRender object is null") | 1397 | throw new Exception("ScreenTest object is null") |
| 1407 | } | 1398 | } |
| 1408 | 1399 | ||
| 1409 | } catch (Exception e) { | 1400 | } catch (Exception e) { | ... | ... |
-
Please register or sign in to post a comment