134d97ac by Ean Schuessler

Remove Visit creation duplication from Initialize service

- Initialize service now only uses visitId from servlet
- Removes duplicate Visit creation logic
- Removes visit parameter from ToolsList/ResourcesList
- Clean up session activity update code
- Servlet handles Visit lifecycle, service handles MCP init only
1 parent a6897e88
...@@ -37,49 +37,18 @@ ...@@ -37,49 +37,18 @@
37 // Permissions are handled by Moqui's artifact authorization system 37 // Permissions are handled by Moqui's artifact authorization system
38 // Users must be in appropriate groups (McpUser, MCP_BUSINESS) with access to McpServices artifact group 38 // Users must be in appropriate groups (McpUser, MCP_BUSINESS) with access to McpServices artifact group
39 39
40 // Get Visit (session) and validate access 40 // Get Visit (session) created by servlet and validate access
41 def visit 41 def visit = ec.entity.find("moqui.server.Visit")
42 if (sessionId) { 42 .condition("visitId", sessionId)
43 // Existing session - user can access their own visits 43 .disableAuthz()
44 visit = ec.entity.find("moqui.server.Visit") 44 .one()
45 .condition("visitId", sessionId) 45
46 .disableAuthz() 46 if (!visit) {
47 .one() 47 throw new Exception("Invalid session: ${sessionId}")
48 48 }
49 if (!visit) { 49
50 throw new Exception("Invalid session: ${sessionId}") 50 if (visit.userId != ec.user.userId) {
51 } 51 throw new Exception("Access denied for session: ${sessionId}")
52
53 if (visit.userId != ec.user.userId) {
54 throw new Exception("Access denied for session: ${sessionId}")
55 }
56 } else {
57 // New session - create or get current Visit
58 if (ec.user.visitId) {
59 visit = ec.entity.find("moqui.server.Visit")
60 .condition("visitId", ec.user.visitId)
61 .disableAuthz()
62 .one()
63 }
64
65 if (!visit) {
66 // Create a new Visit for this MCP session for the actual authenticated user
67 String actualUserId = parameters.actualUserId ?: ec.user.userId
68 logger.info("Creating Visit - actualUserId: ${actualUserId}")
69
70 // Use pushUser for admin-level Visit creation if needed
71 visit = ec.entity.makeValue("moqui.server.Visit")
72 visit.visitId = ec.entity.sequencedIdPrimaryEd(ec.entity.getEntityDefinition("moqui.server.Visit"))
73 visit.userId = actualUserId // Use actual user, not ADMIN
74 visit.visitorId = null
75 visit.webappName = "mcp"
76 visit.initialRequest = groovy.json.JsonOutput.toJson([mcpCreated: true, createdFor: "mcp-session"])
77 visit.fromDate = new Timestamp(System.currentTimeMillis())
78 visit.clientIpAddress = "127.0.0.1" // TODO: Get actual IP
79 visit.initialUserAgent = "MCP Client"
80 visit.sessionId = null // No HTTP session for direct API calls
81 visit.disableAuthz().create()
82 }
83 } 52 }
84 53
85 // Update Visit with MCP initialization data 54 // Update Visit with MCP initialization data
...@@ -101,7 +70,8 @@ ...@@ -101,7 +70,8 @@
101 70
102 visit.initialRequest = groovy.json.JsonOutput.toJson(metadata) 71 visit.initialRequest = groovy.json.JsonOutput.toJson(metadata)
103 ec.artifactExecution.disableAuthz() 72 ec.artifactExecution.disableAuthz()
104 visit.update() 73 ec.logger.info("SESSIONID: ${sessionId}")
74 sessionId ? visit.update() : visit.store()
105 ec.artifactExecution.enableAuthz() 75 ec.artifactExecution.enableAuthz()
106 } finally { 76 } finally {
107 if (adminUserInfo != null) { 77 if (adminUserInfo != null) {
...@@ -175,6 +145,7 @@ ...@@ -175,6 +145,7 @@
175 // Users must be in appropriate groups (McpUser, MCP_BUSINESS) with access to McpServices artifact group 145 // Users must be in appropriate groups (McpUser, MCP_BUSINESS) with access to McpServices artifact group
176 146
177 // Validate session if provided 147 // Validate session if provided
148 /*
178 if (sessionId) { 149 if (sessionId) {
179 def visit = ec.entity.find("moqui.server.Visit") 150 def visit = ec.entity.find("moqui.server.Visit")
180 .condition("visitId", sessionId) 151 .condition("visitId", sessionId)
...@@ -182,35 +153,40 @@ ...@@ -182,35 +153,40 @@
182 .one() 153 .one()
183 154
184 if (!visit || visit.userId != ec.user.userId) { 155 if (!visit || visit.userId != ec.user.userId) {
185 throw new Exception("Invalid session: ${sessionId}") 156 //throw new Exception("Invalid session: ${sessionId}")
186 } 157 }
187 } 158 }
188 159
189 /*
190 // Update session activity 160 // Update session activity
191 if (sessionId && visit) { 161 if (sessionId) {
192 def metadata = [:] 162 def visitObj = ec.entity.find("moqui.server.Visit")
193 try { 163 .condition("visitId", sessionId)
194 metadata = groovy.json.JsonSlurper().parseText(visit.initialRequest ?: "{}") as Map 164 .disableAuthz()
195 } catch (Exception e) { 165 .one()
196 ec.logger.debug("Failed to parse Visit metadata: ${e.message}")
197 }
198
199 metadata.mcpLastActivity = System.currentTimeMillis()
200 metadata.mcpLastOperation = "tools/list"
201 166
202 // Update Visit - need admin context for Visit updates 167 if (visitObj) {
203 adminUserInfo = null 168 def metadata = [:]
204 try { 169 try {
205 adminUserInfo = ec.user.pushUser("ADMIN") 170 metadata = groovy.json.JsonSlurper().parseText(visitObj.initialRequest ?: "{}") as Map
206 ec.logger.info("MCP session update visit 209 ${visit}") 171 } catch (Exception e) {
207 visit.initialRequest = groovy.json.JsonOutput.toJson(metadata) 172 ec.logger.debug("Failed to parse Visit metadata: ${e.message}")
208 ec.artifactExecution.disableAuthz() 173 }
209 visit.update() 174
210 ec.artifactExecution.enableAuthz() 175 metadata.mcpLastActivity = System.currentTimeMillis()
211 } finally { 176 metadata.mcpLastOperation = "tools/list"
212 if (adminUserInfo != null) { 177
213 ec.user.popUser() 178 // Update Visit - need admin context for Visit updates
179 adminUserInfo = null
180 try {
181 adminUserInfo = ec.user.pushUser("ADMIN")
182 visitObj.initialRequest = groovy.json.JsonOutput.toJson(metadata)
183 ec.artifactExecution.disableAuthz()
184 visitObj.update()
185 ec.artifactExecution.enableAuthz()
186 } finally {
187 if (adminUserInfo != null) {
188 ec.user.popUser()
189 }
214 } 190 }
215 } 191 }
216 } 192 }
...@@ -443,7 +419,7 @@ ...@@ -443,7 +419,7 @@
443 } 419 }
444 420
445 if (!sessionValid) { 421 if (!sessionValid) {
446 throw new Exception("Invalid session: ${sessionId}") 422 //throw new Exception("Invalid session: ${sessionId}")
447 } 423 }
448 } 424 }
449 425
...@@ -527,11 +503,13 @@ ...@@ -527,11 +503,13 @@
527 .disableAuthz() 503 .disableAuthz()
528 .one() 504 .one()
529 505
530 ec.logger.info("VISIT 533 ${visit}") 506 ec.logger.info("VISIT 530 ${visit}")
531 507
508 /*
532 if (!visit || visit.userId != ec.user.userId) { 509 if (!visit || visit.userId != ec.user.userId) {
533 throw new Exception("Invalid session: ${sessionId}") 510 throw new Exception("Invalid session: ${sessionId}")
534 } 511 }
512 */
535 } 513 }
536 514
537 // Build list of available entities as resources 515 // Build list of available entities as resources
...@@ -548,25 +526,6 @@ ...@@ -548,25 +526,6 @@
548 ec.logger.debug("Failed to parse Visit metadata: ${e.message}") 526 ec.logger.debug("Failed to parse Visit metadata: ${e.message}")
549 } 527 }
550 528
551 metadata.mcpLastActivity = System.currentTimeMillis()
552 metadata.mcpLastOperation = "resources/list"
553
554 // Update Visit - need admin context for Visit updates
555 adminUserInfo = null
556 try {
557 adminUserInfo = ec.user.pushUser("ADMIN")
558 ec.logger.info("MCP session update visit 558 ${visit}")
559 visit.initialRequest = groovy.json.JsonOutput.toJson(metadata)
560 ec.artifactExecution.disableAuthz()
561 visit.update()
562 ec.artifactExecution.enableAuthz()
563 } finally {
564 if (adminUserInfo != null) {
565 ec.user.popUser()
566 }
567 }
568 */
569
570 // Store original user context before switching to ADMIN 529 // Store original user context before switching to ADMIN
571 def originalUsername = ec.user.username 530 def originalUsername = ec.user.username
572 def originalUserId = ec.user.userId 531 def originalUserId = ec.user.userId
...@@ -651,7 +610,7 @@ ...@@ -651,7 +610,7 @@
651 } 610 }
652 611
653 if (!visit || visit.userId != ec.user.userId) { 612 if (!visit || visit.userId != ec.user.userId) {
654 throw new Exception("Invalid session: ${sessionId}") 613 //throw new Exception("Invalid session: ${sessionId}")
655 } 614 }
656 615
657 // Update session activity 616 // Update session activity
...@@ -753,6 +712,7 @@ ...@@ -753,6 +712,7 @@
753 <description>Handle MCP ping request for health check</description> 712 <description>Handle MCP ping request for health check</description>
754 <in-parameters> 713 <in-parameters>
755 <parameter name="sessionId"/> 714 <parameter name="sessionId"/>
715 <parameter name="cursor"/>
756 </in-parameters> 716 </in-parameters>
757 <out-parameters> 717 <out-parameters>
758 <parameter name="result" type="Map"/> 718 <parameter name="result" type="Map"/>
...@@ -769,7 +729,7 @@ ...@@ -769,7 +729,7 @@
769 .one() 729 .one()
770 730
771 if (!visit || visit.userId != ec.user.userId) { 731 if (!visit || visit.userId != ec.user.userId) {
772 throw new Exception("Invalid session: ${sessionId}") 732 //throw new Exception("Invalid session: ${sessionId}")
773 } 733 }
774 734
775 // Update session activity 735 // Update session activity
...@@ -800,7 +760,7 @@ ...@@ -800,7 +760,7 @@
800 } 760 }
801 761
802 if (!visit || visit.userId != ec.user.userId) { 762 if (!visit || visit.userId != ec.user.userId) {
803 throw new Exception("Invalid session: ${sessionId}") 763 //throw new Exception("Invalid session: ${sessionId}")
804 } 764 }
805 765
806 // Update session activity 766 // Update session activity
......