f54a0339 by Ean Schuessler

Add ViewEntity support to MCP interface

- Add mantle.party.FindPartyView to McpBusinessServices artifact group for authorization
- Enhance ResourcesList service to include ViewEntities with special descriptions
- Improve ResourcesRead service with fallback entity discovery for ViewEntities
- ViewEntities provide pre-joined data for LLM convenience, eliminating manual joins
- Tested successfully: FindPartyView returns 100 records with contact info, addresses, emails, phones
1 parent bcac059c
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
54 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.order.OrderHeader" artifactTypeEnumId="AT_ENTITY"/> 54 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.order.OrderHeader" artifactTypeEnumId="AT_ENTITY"/>
55 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.order.OrderItem" artifactTypeEnumId="AT_ENTITY"/> 55 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.order.OrderItem" artifactTypeEnumId="AT_ENTITY"/>
56 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.party.Party" artifactTypeEnumId="AT_ENTITY"/> 56 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.party.Party" artifactTypeEnumId="AT_ENTITY"/>
57 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.party.FindPartyView" artifactTypeEnumId="AT_ENTITY"/>
57 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.account.Customer" artifactTypeEnumId="AT_ENTITY"/> 58 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.account.Customer" artifactTypeEnumId="AT_ENTITY"/>
58 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="UserAccount" artifactTypeEnumId="AT_ENTITY"/> 59 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="UserAccount" artifactTypeEnumId="AT_ENTITY"/>
59 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.ledger.FinancialAccount" artifactTypeEnumId="AT_ENTITY"/> 60 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.ledger.FinancialAccount" artifactTypeEnumId="AT_ENTITY"/>
......
...@@ -542,14 +542,33 @@ ...@@ -542,14 +542,33 @@
542 return userAccessibleEntities != null && userAccessibleEntities.contains(entityName.toString()) 542 return userAccessibleEntities != null && userAccessibleEntities.contains(entityName.toString())
543 } 543 }
544 544
545 // Add all permitted entities - let Moqui artifact security handle filtering 545 // Add all permitted entities including ViewEntities for LLM convenience
546 for (entityName in allEntityNames) { 546 def allEntityNames = ec.entity.getAllEntityNames()
547 def allViewNames = [] as Set<String>
548
549 // Get ViewEntities by checking entity definitions for view entities
550 def entityInfoList = ec.entity.getAllEntityInfo(0, true) // includeViewEntities=true
551 for (entityInfo in entityInfoList) {
552 if (entityInfo.isViewEntity) {
553 allViewNames.add(entityInfo.entityName)
554 }
555 }
556
557 // Combine real entities and ViewEntities
558 def allAccessibleEntities = allEntityNames + allViewNames
559
560 for (entityName in allAccessibleEntities) {
547 if (userHasEntityPermission(entityName)) { 561 if (userHasEntityPermission(entityName)) {
562 def description = "Moqui entity: ${entityName}"
563 if (entityName.contains("View")) {
564 description = "Moqui ViewEntity: ${entityName} (pre-joined data for LLM convenience)"
565 }
566
548 ec.logger.info("MCP ResourcesList: Adding entity: ${entityName}") 567 ec.logger.info("MCP ResourcesList: Adding entity: ${entityName}")
549 availableResources << [ 568 availableResources << [
550 uri: "entity://${entityName}", 569 uri: "entity://${entityName}",
551 name: entityName, 570 name: entityName,
552 description: "Moqui entity: ${entityName}", 571 description: description,
553 mimeType: "application/json" 572 mimeType: "application/json"
554 ] 573 ]
555 } 574 }
...@@ -592,13 +611,46 @@ ...@@ -592,13 +611,46 @@
592 611
593 def startTime = System.currentTimeMillis() 612 def startTime = System.currentTimeMillis()
594 try { 613 try {
595 // Get entity definition for field descriptions 614 // Try to get entity definition - handle both real entities and view entities
596 def entityInfoList = ec.entity.getAllEntityInfo(0, false) 615 def entityDef = null
597 def entityDef = entityInfoList.find { it.entityName == entityName } 616 try {
617 // First try getAllEntityInfo for detailed info
618 def entityInfoList = ec.entity.getAllEntityInfo(-1, true) // all entities, include view entities
619 entityDef = entityInfoList.find { it.entityName == entityName }
620
621 if (!entityDef) {
622 // If not found in detailed list, try basic entity check
623 if (ec.entity.isEntityDefined(entityName)) {
624 // Create minimal entity definition for basic query
625 entityDef = [
626 entityName: entityName,
627 packageName: entityName.split('\\.')[0],
628 description: "Entity: ${entityName}",
629 isViewEntity: entityName.contains('View'),
630 allFieldInfoList: []
631 ]
632 }
633 }
634 } catch (Exception e) {
635 ec.logger.warn("ResourcesRead: Error getting entity info for ${entityName}: ${e.message}")
636 // Fallback: try basic entity check
637 if (ec.entity.isEntityDefined(entityName)) {
638 entityDef = [
639 entityName: entityName,
640 packageName: entityName.split('\\.')[0],
641 description: "Entity: ${entityName}",
642 isViewEntity: entityName.contains('View'),
643 allFieldInfoList: []
644 ]
645 }
646 }
647
598 if (!entityDef) { 648 if (!entityDef) {
599 throw new Exception("Entity not found: ${entityName}") 649 throw new Exception("Entity not found: ${entityName}")
600 } 650 }
601 651
652 ec.logger.info("ResourcesRead: Found entity ${entityName}, isViewEntity=${entityDef.isViewEntity}")
653
602 // Query entity data (limited to prevent large responses) 654 // Query entity data (limited to prevent large responses)
603 def entityList = ec.entity.find(entityName) 655 def entityList = ec.entity.find(entityName)
604 .limit(100) 656 .limit(100)
...@@ -638,7 +690,8 @@ ...@@ -638,7 +690,8 @@
638 690
639 } catch (Exception e) { 691 } catch (Exception e) {
640 def executionTime = (System.currentTimeMillis() - startTime) / 1000.0 692 def executionTime = (System.currentTimeMillis() - startTime) / 1000.0
641 throw new Exception("Error reading resource ${uri}: ${e.message}") 693 ec.logger.warn("Error reading resource ${uri}: ${e.message}")
694 result = [error: "Error reading resource ${uri}: ${e.message}"]
642 } 695 }
643 ]]></script> 696 ]]></script>
644 </actions> 697 </actions>
......