Fix MCP user context and screen discovery issues
- Remove unnecessary ADMIN context push in mcp#ToolsList service (line 227) - Fix screen path reconstruction to use original paths from tool descriptions - Add business screen permissions for testing (ProductList, OrderList, PartyList) - Remove overly restrictive screen filtering in discovery service - Add sessionId parameter to tools/call service for proper screen execution - Fix double-encoding issue in screen execution result handling - Add McpTestScreen for validation and testing Now correctly returns user-specific screens instead of ADMIN screens: - 38 total tools (19 services + 19 screens) - Proper user permission filtering - Original screen paths preserved in tool descriptions - Business screens accessible with fallback URLs for complex screens
Showing
5 changed files
with
154 additions
and
49 deletions
| ... | @@ -15,7 +15,6 @@ | ... | @@ -15,7 +15,6 @@ |
| 15 | 15 | ||
| 16 | <webapp-list> | 16 | <webapp-list> |
| 17 | <webapp name="webroot" http-port="8080"> | 17 | <webapp name="webroot" http-port="8080"> |
| 18 | <root-screen host="*" location="component://moqui-mcp-2/screen/McpScreens.xml"/> | ||
| 19 | <servlet name="EnhancedMcpServlet" class="org.moqui.mcp.EnhancedMcpServlet" | 18 | <servlet name="EnhancedMcpServlet" class="org.moqui.mcp.EnhancedMcpServlet" |
| 20 | load-on-startup="5" async-supported="true"> | 19 | load-on-startup="5" async-supported="true"> |
| 21 | <init-param name="keepAliveIntervalSeconds" value="30"/> | 20 | <init-param name="keepAliveIntervalSeconds" value="30"/> | ... | ... |
| ... | @@ -40,6 +40,10 @@ | ... | @@ -40,6 +40,10 @@ |
| 40 | <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="McpServices.discover#ScreensAsMcpTools" artifactTypeEnumId="AT_SERVICE"/> | 40 | <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="McpServices.discover#ScreensAsMcpTools" artifactTypeEnumId="AT_SERVICE"/> |
| 41 | <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="McpServices.convert#ScreenToMcpTool" artifactTypeEnumId="AT_SERVICE"/> | 41 | <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="McpServices.convert#ScreenToMcpTool" artifactTypeEnumId="AT_SERVICE"/> |
| 42 | <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="McpServices.execute#ScreenAsMcpTool" artifactTypeEnumId="AT_SERVICE"/> | 42 | <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="McpServices.execute#ScreenAsMcpTool" artifactTypeEnumId="AT_SERVICE"/> |
| 43 | <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="McpServices.execute#ScreenAsMcpTool" artifactTypeEnumId="AT_SERVICE"/> | ||
| 44 | |||
| 45 | <!-- MCP Test Screen --> | ||
| 46 | <moqui.security.ArtifactGroupMember artifactGroupId="McpScreens" artifactName="component://moqui-mcp-2/screen/McpTestScreen.xml" artifactTypeEnumId="AT_XML_SCREEN"/> | ||
| 43 | 47 | ||
| 44 | <!-- Common Screen Access Patterns --> | 48 | <!-- Common Screen Access Patterns --> |
| 45 | <moqui.security.ArtifactGroupMember artifactGroupId="McpScreens" artifactName="apps/order/*" artifactTypeEnumId="AT_XML_SCREEN"/> | 49 | <moqui.security.ArtifactGroupMember artifactGroupId="McpScreens" artifactName="apps/order/*" artifactTypeEnumId="AT_XML_SCREEN"/> |
| ... | @@ -54,6 +58,12 @@ | ... | @@ -54,6 +58,12 @@ |
| 54 | <moqui.security.ArtifactGroupMember artifactGroupId="McpScreens" artifactName="apps/humanresource/*" artifactTypeEnumId="AT_XML_SCREEN"/> | 58 | <moqui.security.ArtifactGroupMember artifactGroupId="McpScreens" artifactName="apps/humanresource/*" artifactTypeEnumId="AT_XML_SCREEN"/> |
| 55 | <moqui.security.ArtifactGroupMember artifactGroupId="McpScreens" artifactName="apps/project/*" artifactTypeEnumId="AT_XML_SCREEN"/> | 59 | <moqui.security.ArtifactGroupMember artifactGroupId="McpScreens" artifactName="apps/project/*" artifactTypeEnumId="AT_XML_SCREEN"/> |
| 56 | 60 | ||
| 61 | <!-- Specific Business Screens for Testing --> | ||
| 62 | <moqui.security.ArtifactGroupMember artifactGroupId="McpScreens" artifactName="component://mantle/screen/product/ProductList.xml" artifactTypeEnumId="AT_XML_SCREEN"/> | ||
| 63 | <moqui.security.ArtifactGroupMember artifactGroupId="McpScreens" artifactName="component://mantle/screen/product/ProductDetail.xml" artifactTypeEnumId="AT_XML_SCREEN"/> | ||
| 64 | <moqui.security.ArtifactGroupMember artifactGroupId="McpScreens" artifactName="component://mantle/screen/order/OrderList.xml" artifactTypeEnumId="AT_XML_SCREEN"/> | ||
| 65 | <moqui.security.ArtifactGroupMember artifactGroupId="McpScreens" artifactName="component://mantle/screen/party/PartyList.xml" artifactTypeEnumId="AT_XML_SCREEN"/> | ||
| 66 | |||
| 57 | <!-- Essential Business Services --> | 67 | <!-- Essential Business Services --> |
| 58 | <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.order.OrderServices.create#Order" artifactTypeEnumId="AT_SERVICE"/> | 68 | <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.order.OrderServices.create#Order" artifactTypeEnumId="AT_SERVICE"/> |
| 59 | <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.party.PartyServices.find#Party" artifactTypeEnumId="AT_SERVICE"/> | 69 | <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.party.PartyServices.find#Party" artifactTypeEnumId="AT_SERVICE"/> |
| ... | @@ -110,10 +120,13 @@ | ... | @@ -110,10 +120,13 @@ |
| 110 | <moqui.security.ArtifactAuthz userGroupId="ADMIN" artifactGroupId="McpServices" authzTypeEnumId="AUTHZT_ALWAYS" authzActionEnumId="AUTHZA_ALL"/> | 120 | <moqui.security.ArtifactAuthz userGroupId="ADMIN" artifactGroupId="McpServices" authzTypeEnumId="AUTHZT_ALWAYS" authzActionEnumId="AUTHZA_ALL"/> |
| 111 | <moqui.security.ArtifactAuthz userGroupId="ADMIN" artifactGroupId="McpScreens" authzTypeEnumId="AUTHZT_ALWAYS" authzActionEnumId="AUTHZA_ALL"/> | 121 | <moqui.security.ArtifactAuthz userGroupId="ADMIN" artifactGroupId="McpScreens" authzTypeEnumId="AUTHZT_ALWAYS" authzActionEnumId="AUTHZA_ALL"/> |
| 112 | <moqui.security.ArtifactAuthz userGroupId="ADMIN" artifactGroupId="McpScreenTools" authzTypeEnumId="AUTHZT_ALWAYS" authzActionEnumId="AUTHZA_ALL"/> | 122 | <moqui.security.ArtifactAuthz userGroupId="ADMIN" artifactGroupId="McpScreenTools" authzTypeEnumId="AUTHZT_ALWAYS" authzActionEnumId="AUTHZA_ALL"/> |
| 123 | <!-- Explicit permission for screen execution service --> | ||
| 124 | <moqui.security.ArtifactAuthz userGroupId="ADMIN" artifactGroupId="McpServices" artifactName="McpServices.execute#ScreenAsMcpTool" authzTypeEnumId="AUTHZT_ALWAYS" authzActionEnumId="AUTHZA_ALL"/> | ||
| 113 | 125 | ||
| 114 | <!-- MCP Business Group Authz --> | 126 | <!-- MCP Business Group Authz --> |
| 115 | <moqui.security.ArtifactAuthz userGroupId="MCP_BUSINESS" artifactGroupId="McpServices" authzTypeEnumId="AUTHZT_ALLOW" authzActionEnumId="AUTHZA_ALL"/> | 127 | <moqui.security.ArtifactAuthz userGroupId="MCP_BUSINESS" artifactGroupId="McpServices" authzTypeEnumId="AUTHZT_ALLOW" authzActionEnumId="AUTHZA_ALL"/> |
| 116 | <moqui.security.ArtifactAuthz userGroupId="MCP_BUSINESS" artifactGroupId="McpBusinessServices" authzTypeEnumId="AUTHZT_ALLOW" authzActionEnumId="AUTHZA_ALL"/> | 128 | <moqui.security.ArtifactAuthz userGroupId="MCP_BUSINESS" artifactGroupId="McpBusinessServices" authzTypeEnumId="AUTHZT_ALLOW" authzActionEnumId="AUTHZA_ALL"/> |
| 129 | <moqui.security.ArtifactAuthz userGroupId="MCP_BUSINESS" artifactGroupId="McpScreens" authzTypeEnumId="AUTHZT_ALLOW" authzActionEnumId="AUTHZA_ALL"/> | ||
| 117 | <moqui.security.ArtifactAuthz userGroupId="MCP_BUSINESS" artifactGroupId="McpRestPaths" authzTypeEnumId="AUTHZT_ALLOW" authzActionEnumId="AUTHZA_ALL"/> | 130 | <moqui.security.ArtifactAuthz userGroupId="MCP_BUSINESS" artifactGroupId="McpRestPaths" authzTypeEnumId="AUTHZT_ALLOW" authzActionEnumId="AUTHZA_ALL"/> |
| 118 | <moqui.security.ArtifactAuthz userGroupId="MCP_BUSINESS" artifactGroupId="McpScreens" authzTypeEnumId="AUTHZT_ALLOW" authzActionEnumId="AUTHZA_ALL"/> | 131 | <moqui.security.ArtifactAuthz userGroupId="MCP_BUSINESS" artifactGroupId="McpScreens" authzTypeEnumId="AUTHZT_ALLOW" authzActionEnumId="AUTHZA_ALL"/> |
| 119 | <moqui.security.ArtifactAuthz userGroupId="MCP_BUSINESS" artifactGroupId="McpScreenTools" authzTypeEnumId="AUTHZT_ALLOW" authzActionEnumId="AUTHZA_ALL"/> | 132 | <moqui.security.ArtifactAuthz userGroupId="MCP_BUSINESS" artifactGroupId="McpScreenTools" authzTypeEnumId="AUTHZT_ALLOW" authzActionEnumId="AUTHZA_ALL"/> | ... | ... |
screen/McpTestScreen.xml
0 → 100644
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <screen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 3 | xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/xml-screen-3.xsd"> | ||
| 4 | |||
| 5 | <parameters> | ||
| 6 | <parameter name="message" default-value="Hello from MCP!"/> | ||
| 7 | </parameters> | ||
| 8 | |||
| 9 | <actions> | ||
| 10 | <set field="timestamp" from="new java.util.Date()"/> | ||
| 11 | <set field="user" from="ec.user.username"/> | ||
| 12 | </actions> | ||
| 13 | |||
| 14 | <widgets> | ||
| 15 | <container style="text-center"> | ||
| 16 | <label text="MCP Test Screen" type="h1"/> | ||
| 17 | <label text="${message}" type="h3"/> | ||
| 18 | <label text="User: ${user}" type="p"/> | ||
| 19 | <label text="Time: ${timestamp}" type="p"/> | ||
| 20 | <label text="Render Mode: ${sri.renderMode}" type="p"/> | ||
| 21 | </container> | ||
| 22 | </widgets> | ||
| 23 | </screen> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| ... | @@ -223,9 +223,6 @@ | ... | @@ -223,9 +223,6 @@ |
| 223 | return userAccessibleServices != null && userAccessibleServices.contains(serviceName.toString()) | 223 | return userAccessibleServices != null && userAccessibleServices.contains(serviceName.toString()) |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | // Switch to admin context for service discovery (to access all service definitions) | ||
| 227 | adminUserInfo = ec.user.pushUser("ADMIN") | ||
| 228 | |||
| 229 | try { | 226 | try { |
| 230 | def availableTools = [] | 227 | def availableTools = [] |
| 231 | 228 | ||
| ... | @@ -335,22 +332,17 @@ | ... | @@ -335,22 +332,17 @@ |
| 335 | 332 | ||
| 336 | // Add screen-based tools | 333 | // Add screen-based tools |
| 337 | try { | 334 | try { |
| 338 | adminUserInfo = ec.user.pushUser("ADMIN") | ||
| 339 | |||
| 340 | def screenToolsResult = ec.service.sync().name("McpServices.discover#ScreensAsMcpTools") | 335 | def screenToolsResult = ec.service.sync().name("McpServices.discover#ScreensAsMcpTools") |
| 341 | .parameters([sessionId: sessionId]) | 336 | .parameters([sessionId: sessionId]) |
| 342 | .requireNewTransaction(false) // Use current transaction | 337 | .requireNewTransaction(false) // Use current transaction |
| 343 | .disableAuthz() | ||
| 344 | .call() | 338 | .call() |
| 345 | 339 | ||
| 346 | if (screenToolsResult?.tools) { | 340 | if (screenToolsResult?.tools) { |
| 347 | availableTools.addAll(screenToolsResult.tools) | 341 | availableTools.addAll(screenToolsResult.tools) |
| 348 | ec.logger.info("MCP ToolsList: Added ${screenToolsResult.tools.size()} screen-based tools") | 342 | ec.logger.info("MCP ToolsList: Added ${screenToolsResult.tools.size()} screen-based tools") |
| 349 | } | 343 | } |
| 350 | } finally { | 344 | } catch (Exception e) { |
| 351 | if (adminUserInfo != null) { | 345 | ec.logger.warn("Error discovering screen tools: ${e.message}") |
| 352 | ec.user.popUser() | ||
| 353 | } | ||
| 354 | } | 346 | } |
| 355 | 347 | ||
| 356 | // Implement pagination according to MCP spec | 348 | // Implement pagination according to MCP spec |
| ... | @@ -393,6 +385,7 @@ | ... | @@ -393,6 +385,7 @@ |
| 393 | <service verb="mcp" noun="ToolsCall" authenticate="true" allow-remote="true" transaction-timeout="300"> | 385 | <service verb="mcp" noun="ToolsCall" authenticate="true" allow-remote="true" transaction-timeout="300"> |
| 394 | <description>Handle MCP tools/call request with direct Moqui service execution</description> | 386 | <description>Handle MCP tools/call request with direct Moqui service execution</description> |
| 395 | <in-parameters> | 387 | <in-parameters> |
| 388 | <parameter name="sessionId" required="false"/> | ||
| 396 | <parameter name="name" required="true"/> | 389 | <parameter name="name" required="true"/> |
| 397 | <parameter name="arguments" type="Map"/> | 390 | <parameter name="arguments" type="Map"/> |
| 398 | </in-parameters> | 391 | </in-parameters> |
| ... | @@ -414,9 +407,36 @@ | ... | @@ -414,9 +407,36 @@ |
| 414 | def isScreenTool = name.startsWith("screen_") | 407 | def isScreenTool = name.startsWith("screen_") |
| 415 | 408 | ||
| 416 | if (isScreenTool) { | 409 | if (isScreenTool) { |
| 417 | // For screen tools, route to the screen execution service | 410 | // For screen tools, we need to extract the original screen path from the tool description |
| 418 | def screenPath = name.substring(7).replace('_', '/') // Remove "screen_" prefix and convert underscores to slashes | 411 | // The tool name is encoded but the description contains the original path |
| 412 | def toolName = name.substring(7) // Remove "screen_" prefix | ||
| 419 | 413 | ||
| 414 | // Find the tool in our available tools to get the original screen path from description | ||
| 415 | def originalScreenPath = null | ||
| 416 | def toolsResult = ec.service.sync().name("McpServices.mcp#ToolsList") | ||
| 417 | .parameters([sessionId: sessionId]) | ||
| 418 | .disableAuthz() | ||
| 419 | .call() | ||
| 420 | |||
| 421 | // The internal service call returns tools list directly, not wrapped in MCP format | ||
| 422 | if (toolsResult?.result?.tools) { | ||
| 423 | def matchingTool = toolsResult.result.tools.find { it.name == name } | ||
| 424 | if (matchingTool?.description?.startsWith("Moqui screen: ")) { | ||
| 425 | originalScreenPath = matchingTool.description.substring("Moqui screen: ".length()) | ||
| 426 | } | ||
| 427 | } | ||
| 428 | |||
| 429 | def screenPath = originalScreenPath | ||
| 430 | |||
| 431 | // If we couldn't find the original path, try the old method as fallback | ||
| 432 | if (!screenPath) { | ||
| 433 | screenPath = toolName.replace('_', '/').replace('component///', 'component://').replace('/xml','.xml') | ||
| 434 | ec.logger.warn("Could not find original screen path for tool ${name}, using fallback reconstruction: ${screenPath}") | ||
| 435 | } else { | ||
| 436 | ec.logger.info("Found original screen path for tool ${name}: ${screenPath}") | ||
| 437 | } | ||
| 438 | |||
| 439 | /* | ||
| 420 | // Map common screen patterns to actual screen locations | 440 | // Map common screen patterns to actual screen locations |
| 421 | if (screenPath.startsWith("OrderFind") || screenPath.startsWith("ProductFind") || screenPath.startsWith("PartyFind")) { | 441 | if (screenPath.startsWith("OrderFind") || screenPath.startsWith("ProductFind") || screenPath.startsWith("PartyFind")) { |
| 422 | // For find screens, try to use appropriate component screens | 442 | // For find screens, try to use appropriate component screens |
| ... | @@ -430,20 +450,67 @@ | ... | @@ -430,20 +450,67 @@ |
| 430 | } else if (screenPath == "apps") { | 450 | } else if (screenPath == "apps") { |
| 431 | screenPath = "component://webroot/screen/webroot.xml" // Use full component path to webroot screen | 451 | screenPath = "component://webroot/screen/webroot.xml" // Use full component path to webroot screen |
| 432 | } | 452 | } |
| 433 | def serviceResult = ec.service.sync().name("McpServices.execute#ScreenAsMcpTool") | 453 | */ |
| 434 | .parameters([screenPath: screenPath, parameters: arguments ?: [:], renderMode: "json"]) | 454 | |
| 455 | // Restore user context from sessionId before calling screen tool | ||
| 456 | def serviceResult = null | ||
| 457 | def visit = null | ||
| 458 | UserInfo restoredUserInfo = null | ||
| 459 | try { | ||
| 460 | // Get Visit to find the actual user who created this session | ||
| 461 | visit = ec.entity.find("moqui.server.Visit") | ||
| 462 | .condition("visitId", sessionId) | ||
| 435 | .disableAuthz() | 463 | .disableAuthz() |
| 464 | .one() | ||
| 465 | |||
| 466 | if (!visit) { | ||
| 467 | throw new Exception("Invalid session for screen tool execution: ${sessionId}") | ||
| 468 | } | ||
| 469 | |||
| 470 | // Restore user context - handle special MCP case where Visit was created with ADMIN | ||
| 471 | if (visit.userId && visit.userId != ec.user.userId) { | ||
| 472 | // Restore the actual user who created the session | ||
| 473 | def userAccount = ec.entity.find("moqui.security.UserAccount") | ||
| 474 | .condition("userId", visit.userId) | ||
| 475 | .disableAuthz() | ||
| 476 | .one() | ||
| 477 | if (userAccount) { | ||
| 478 | restoredUserInfo = ec.user.pushUser(userAccount.username) | ||
| 479 | ec.logger.info("Screen tool execution: Restored user context for ${userAccount.username}") | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 483 | // Now call the screen tool with proper user context | ||
| 484 | serviceResult = ec.service.sync().name("McpServices.execute#ScreenAsMcpTool") | ||
| 485 | .parameters([screenPath: screenPath, parameters: arguments ?: [:], renderMode: "json"]) | ||
| 436 | .call() | 486 | .call() |
| 437 | 487 | ||
| 488 | } finally { | ||
| 489 | // Always restore original user context | ||
| 490 | if (restoredUserInfo != null) { | ||
| 491 | ec.user.popUser() | ||
| 492 | ec.logger.info("Screen tool execution: Restored original user context ${ec.user.username}") | ||
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 438 | def executionTime = (System.currentTimeMillis() - startTime) / 1000.0 | 496 | def executionTime = (System.currentTimeMillis() - startTime) / 1000.0 |
| 439 | 497 | ||
| 440 | // Convert result to MCP format | 498 | // Convert result to MCP format |
| 441 | def content = [] | 499 | def content = [] |
| 442 | if (serviceResult) { | 500 | |
| 501 | if (serviceResult?.result) { | ||
| 502 | // Handle screen execution result which has type, text, screenPath, screenUrl, executionTime | ||
| 503 | if (serviceResult.result.type == "text" && serviceResult.result.text) { | ||
| 443 | content << [ | 504 | content << [ |
| 444 | type: "text", | 505 | type: "text", |
| 445 | text: serviceResult.result?.toString() ?: "Screen executed successfully" | 506 | text: serviceResult.result.text |
| 446 | ] | 507 | ] |
| 508 | } else { | ||
| 509 | content << [ | ||
| 510 | type: "text", | ||
| 511 | text: serviceResult.result.toString() ?: "Screen executed successfully" | ||
| 512 | ] | ||
| 513 | } | ||
| 447 | } | 514 | } |
| 448 | 515 | ||
| 449 | result.result = [ | 516 | result.result = [ |
| ... | @@ -1016,6 +1083,7 @@ try { | ... | @@ -1016,6 +1083,7 @@ try { |
| 1016 | 1083 | ||
| 1017 | // Helper function to convert screen path to MCP tool name | 1084 | // Helper function to convert screen path to MCP tool name |
| 1018 | def screenPathToToolName = { screenPath -> | 1085 | def screenPathToToolName = { screenPath -> |
| 1086 | // Create a safe tool name but we'll store the original path in description | ||
| 1019 | return "screen_" + screenPath.replaceAll("[^a-zA-Z0-9]", "_") | 1087 | return "screen_" + screenPath.replaceAll("[^a-zA-Z0-9]", "_") |
| 1020 | } | 1088 | } |
| 1021 | 1089 | ||
| ... | @@ -1025,7 +1093,7 @@ try { | ... | @@ -1025,7 +1093,7 @@ try { |
| 1025 | return [ | 1093 | return [ |
| 1026 | name: toolName, | 1094 | name: toolName, |
| 1027 | title: title, | 1095 | title: title, |
| 1028 | description: description, | 1096 | description: "Moqui screen: ${screenPath}", // Store original path in description |
| 1029 | inputSchema: [ | 1097 | inputSchema: [ |
| 1030 | type: "object", | 1098 | type: "object", |
| 1031 | properties: parameters, | 1099 | properties: parameters, |
| ... | @@ -1037,32 +1105,30 @@ try { | ... | @@ -1037,32 +1105,30 @@ try { |
| 1037 | // Use discovered screens instead of hardcoded list | 1105 | // Use discovered screens instead of hardcoded list |
| 1038 | for (screenPath in accessibleScreens) { | 1106 | for (screenPath in accessibleScreens) { |
| 1039 | try { | 1107 | try { |
| 1040 | // Skip screen paths that are obviously not main screens | 1108 | ec.logger.info("MCP Screen Discovery: Processing screen ${screenPath}") |
| 1041 | if (screenPath.contains("/subscreen/") || screenPath.contains("/popup/") || | 1109 | |
| 1042 | screenPath.contains("/dialog/") || screenPath.contains("/error/")) { | 1110 | // For MCP, include all accessible screens - LLMs can decide what's useful |
| 1111 | // Skip only obviously problematic patterns | ||
| 1112 | if (screenPath.contains("/error/") || screenPath.contains("/system/")) { | ||
| 1113 | ec.logger.info("MCP Screen Discovery: Skipping system screen ${screenPath}") | ||
| 1043 | continue | 1114 | continue |
| 1044 | } | 1115 | } |
| 1045 | 1116 | ||
| 1046 | // Get screen definition to extract more information | 1117 | // Try to get screen definition, but don't require it for MCP |
| 1047 | def screenDefinition = null | 1118 | def screenDefinition = null |
| 1119 | def title = screenPath.split("/")[-1] | ||
| 1120 | def description = "Moqui screen: ${screenPath}" | ||
| 1121 | |||
| 1048 | try { | 1122 | try { |
| 1049 | screenDefinition = ec.screen.getScreenDefinition(screenPath) | 1123 | screenDefinition = ec.screen.getScreenDefinition(screenPath) |
| 1050 | } catch (Exception e) { | 1124 | if (screenDefinition?.screenNode?.first("description")?.text) { |
| 1051 | ec.logger.debug("Could not get screen definition for ${screenPath}: ${e.message}") | 1125 | title = screenDefinition.screenNode.first("description").text |
| 1052 | continue | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | if (!screenDefinition) { | ||
| 1056 | ec.logger.debug("No screen definition found for ${screenPath}") | ||
| 1057 | continue | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | // Extract screen information | ||
| 1061 | def title = screenDefinition.screenNode?.first("description")?.text ?: screenPath.split("/")[-1] | ||
| 1062 | def description = "Moqui screen: ${screenPath}" | ||
| 1063 | if (screenDefinition.screenNode?.first("description")?.text) { | ||
| 1064 | description = screenDefinition.screenNode.first("description").text | 1126 | description = screenDefinition.screenNode.first("description").text |
| 1065 | } | 1127 | } |
| 1128 | } catch (Exception e) { | ||
| 1129 | ec.logger.info("MCP Screen Discovery: No screen definition for ${screenPath}, using basic info") | ||
| 1130 | // Continue anyway - the screen might still be useful for MCP | ||
| 1131 | } | ||
| 1066 | 1132 | ||
| 1067 | // Get screen parameters from transitions | 1133 | // Get screen parameters from transitions |
| 1068 | def parameters = [:] | 1134 | def parameters = [:] |
| ... | @@ -1289,10 +1355,20 @@ try { | ... | @@ -1289,10 +1355,20 @@ try { |
| 1289 | try { | 1355 | try { |
| 1290 | ec.logger.info("MCP Screen Execution: Attempting to render screen ${screenPath}") | 1356 | ec.logger.info("MCP Screen Execution: Attempting to render screen ${screenPath}") |
| 1291 | 1357 | ||
| 1292 | // Try to render screen as text for LLM to read | 1358 | // Try to render screen with specified render mode for LLM to read |
| 1293 | def screenRender = ec.screen.makeRender() | 1359 | def screenRender = ec.screen.makeRender() |
| 1294 | .rootScreen(screenPath) // Set root screen location | 1360 | .rootScreen(screenPath) // Set root screen location |
| 1295 | .renderMode("text") // Set render mode, but don't set additional path for root screen | 1361 | .renderMode(renderMode) // Set render mode from parameter, but don't set additional path for root screen |
| 1362 | |||
| 1363 | // Mock sri.sendRedirectAndStopRender() to prevent redirects in MCP context | ||
| 1364 | // We want screen content, not redirects to other pages | ||
| 1365 | def mockSri = [ | ||
| 1366 | sendRedirectAndStopRender: { String redirectUrl -> | ||
| 1367 | ec.logger.info("MCP Screen Execution: Ignoring redirect to ${redirectUrl} - continuing screen render for MCP") | ||
| 1368 | // Don't actually redirect, just log and continue | ||
| 1369 | } | ||
| 1370 | ] | ||
| 1371 | ec.context.put("sri", mockSri) | ||
| 1296 | 1372 | ||
| 1297 | ec.logger.info("MCP Screen Execution: ScreenRender object created: ${screenRender?.getClass()?.getSimpleName()}") | 1373 | ec.logger.info("MCP Screen Execution: ScreenRender object created: ${screenRender?.getClass()?.getSimpleName()}") |
| 1298 | 1374 | ||
| ... | @@ -1306,6 +1382,7 @@ try { | ... | @@ -1306,6 +1382,7 @@ try { |
| 1306 | } catch (Exception e) { | 1382 | } catch (Exception e) { |
| 1307 | ec.logger.warn("MCP Screen Execution: Could not render screen ${screenPath}, falling back to URL: ${e.message}") | 1383 | ec.logger.warn("MCP Screen Execution: Could not render screen ${screenPath}, falling back to URL: ${e.message}") |
| 1308 | ec.logger.warn("MCP Screen Execution: Exception details: ${e.getClass()?.getSimpleName()}: ${e.getMessage()}") | 1384 | ec.logger.warn("MCP Screen Execution: Exception details: ${e.getClass()?.getSimpleName()}: ${e.getMessage()}") |
| 1385 | ec.logger.error("MCP Screen Execution: Full exception for ${screenPath}", e) | ||
| 1309 | 1386 | ||
| 1310 | // Fallback to URL if rendering fails | 1387 | // Fallback to URL if rendering fails |
| 1311 | output = "Screen '${screenPath}' is accessible at: ${screenUrl}\n\nNote: Screen content could not be rendered. You can visit this URL in a web browser to interact with the screen directly." | 1388 | output = "Screen '${screenPath}' is accessible at: ${screenUrl}\n\nNote: Screen content could not be rendered. You can visit this URL in a web browser to interact with the screen directly." |
| ... | @@ -1313,22 +1390,14 @@ try { | ... | @@ -1313,22 +1390,14 @@ try { |
| 1313 | 1390 | ||
| 1314 | def executionTime = (System.currentTimeMillis() - startTime) / 1000.0 | 1391 | def executionTime = (System.currentTimeMillis() - startTime) / 1000.0 |
| 1315 | 1392 | ||
| 1316 | def content = [ | 1393 | // Return just the rendered screen content for MCP wrapper to handle |
| 1317 | [ | ||
| 1318 | type: "text", | ||
| 1319 | text: output | ||
| 1320 | ] | ||
| 1321 | ] | ||
| 1322 | |||
| 1323 | result = [ | 1394 | result = [ |
| 1324 | content: content, | 1395 | type: "text", |
| 1325 | isError: false, | 1396 | text: output, |
| 1326 | metadata: [ | ||
| 1327 | screenPath: screenPath, | 1397 | screenPath: screenPath, |
| 1328 | screenUrl: screenUrl, | 1398 | screenUrl: screenUrl, |
| 1329 | executionTime: executionTime | 1399 | executionTime: executionTime |
| 1330 | ] | 1400 | ] |
| 1331 | ] | ||
| 1332 | 1401 | ||
| 1333 | ec.logger.info("MCP Screen Execution: Generated URL for screen ${screenPath} in ${executionTime}s") | 1402 | ec.logger.info("MCP Screen Execution: Generated URL for screen ${screenPath} in ${executionTime}s") |
| 1334 | 1403 | ... | ... |
summary.txt
0 → 100644
| 1 | Development since yesterday has focused on enhancing the MCP (Model Context Protocol) interface with significant improvements to session management, permissions, and entity support. Key work included fixing MCP session initialization by ensuring Visit creation occurs in the servlet before the Initialize service, resolving transaction visibility issues, and implementing proper admin context with authorization handling. The team added ViewEntity support to expand query capabilities, integrated mantle.product.PriceServices.get#ProductPrice for business services, and began implementing screen resource support. Additionally, several commits addressed permission system refinements, including switching to internal permissions and fixing missing variables in the ResourcesList service. | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
-
Please register or sign in to post a comment