00839b54 by Ean Schuessler

Fix MCP service response handling to return proper JSON responses

- Change response variable assignments from 'result' to 'response' to match out-parameter definition
- Fix empty response body issue where opencode was receiving '{}' instead of JSON-RPC responses
- Update error handling to use direct HTTP response writing for validation errors
- Add comprehensive debug logging for HTTP request/response handling
- Ensure MCP responses are properly serialized and returned via Moqui REST framework

Resolves content-type complaints from opencode by returning properly formatted JSON-RPC responses instead of empty objects.
1 parent d55e1a70
...@@ -746,6 +746,11 @@ ...@@ -746,6 +746,11 @@
746 ec.logger.info("MCP Request - Params: ${params}") 746 ec.logger.info("MCP Request - Params: ${params}")
747 ec.logger.info("MCP Request - User: ${ec.user.username}, UserID: ${ec.user.userId}") 747 ec.logger.info("MCP Request - User: ${ec.user.username}, UserID: ${ec.user.userId}")
748 ec.logger.info("MCP Request - Current Time: ${ec.user.getNowTimestamp()}") 748 ec.logger.info("MCP Request - Current Time: ${ec.user.getNowTimestamp()}")
749 ec.logger.info("MCP Request - JSONRPC: ${jsonrpc}")
750 ec.logger.info("MCP Request - All context keys: ${context.keySet()}")
751 ec.logger.info("MCP Request - ec.web exists: ${ec.web != null}")
752 ec.logger.info("MCP Request - ec.web.request exists: ${ec.web?.request != null}")
753 ec.logger.info("MCP Request - ec.web.response exists: ${ec.web?.response != null}")
749 754
750 // DEBUG: Log HTTP request details 755 // DEBUG: Log HTTP request details
751 def httpRequest = ec.web?.request 756 def httpRequest = ec.web?.request
...@@ -762,9 +767,12 @@ ...@@ -762,9 +767,12 @@
762 ec.logger.warn("HTTP Request object is null!") 767 ec.logger.warn("HTTP Request object is null!")
763 } 768 }
764 769
770 def httpResponse = ec.web?.response
765 // DEBUG: Log HTTP response details 771 // DEBUG: Log HTTP response details
766 if (httpResponse) { 772 if (httpResponse) {
767 ec.logger.info("HTTP Response Status: ${httpResponse.status}") 773 ec.logger.info("HTTP Response Status: ${httpResponse.status}")
774 ec.logger.info("HTTP Response committed: ${httpResponse.committed}")
775 ec.logger.info("HTTP Response content type: ${httpResponse.getContentType()}")
768 } else { 776 } else {
769 ec.logger.warn("HTTP Response object is null!") 777 ec.logger.warn("HTTP Response object is null!")
770 } 778 }
...@@ -776,7 +784,9 @@ ...@@ -776,7 +784,9 @@
776 ec.logger.warn("Invalid HTTP method: ${httpMethod}, expected POST") 784 ec.logger.warn("Invalid HTTP method: ${httpMethod}, expected POST")
777 ec.web?.response?.setStatus(405) // Method Not Allowed 785 ec.web?.response?.setStatus(405) // Method Not Allowed
778 ec.web?.response?.setHeader("Allow", "POST") 786 ec.web?.response?.setHeader("Allow", "POST")
779 response = "Method Not Allowed. Use POST for JSON-RPC messages." 787 ec.web?.response?.setContentType("text/plain")
788 ec.web?.response?.getWriter()?.write("Method Not Allowed. Use POST for JSON-RPC messages.")
789 ec.web?.response?.getWriter()?.flush()
780 return 790 return
781 } 791 }
782 ec.logger.info("HTTP method validation passed") 792 ec.logger.info("HTTP method validation passed")
...@@ -785,9 +795,11 @@ ...@@ -785,9 +795,11 @@
785 def contentType = ec.web?.request?.getContentType() 795 def contentType = ec.web?.request?.getContentType()
786 ec.logger.info("Validating Content-Type: ${contentType}") 796 ec.logger.info("Validating Content-Type: ${contentType}")
787 if (!contentType?.contains("application/json")) { 797 if (!contentType?.contains("application/json")) {
788 ec.logger.warn("Invalid Content-Type: ${contentType}, expected application/json") 798 ec.logger.warn("Invalid Content-Type: ${contentType}, expected application/json or application/json-rpc")
789 ec.web?.response?.setStatus(415) // Unsupported Media Type 799 ec.web?.response?.setStatus(415) // Unsupported Media Type
790 response = "Content-Type must be application/json for JSON-RPC messages" 800 ec.web?.response?.setContentType("text/plain")
801 ec.web?.response?.getWriter()?.write("Content-Type must be application/json or application/json-rpc for JSON-RPC messages")
802 ec.web?.response?.getWriter()?.flush()
791 return 803 return
792 } 804 }
793 ec.logger.info("Content-Type validation passed") 805 ec.logger.info("Content-Type validation passed")
...@@ -798,7 +810,9 @@ ...@@ -798,7 +810,9 @@
798 if (!acceptHeader?.contains("application/json")) { 810 if (!acceptHeader?.contains("application/json")) {
799 ec.logger.warn("Invalid Accept header: ${acceptHeader}") 811 ec.logger.warn("Invalid Accept header: ${acceptHeader}")
800 ec.web?.response?.setStatus(406) // Not Acceptable 812 ec.web?.response?.setStatus(406) // Not Acceptable
801 response = "Accept header must include application/json for JSON-RPC" 813 ec.web?.response?.setContentType("text/plain")
814 ec.web?.response?.getWriter()?.write("Accept header must include application/json for JSON-RPC")
815 ec.web?.response?.getWriter()?.flush()
802 return 816 return
803 } 817 }
804 ec.logger.info("Accept header validation passed") 818 ec.logger.info("Accept header validation passed")
...@@ -806,7 +820,9 @@ ...@@ -806,7 +820,9 @@
806 // Validate Content-Type header for POST requests 820 // Validate Content-Type header for POST requests
807 if (!contentType?.contains("application/json")) { 821 if (!contentType?.contains("application/json")) {
808 ec.web?.response?.setStatus(415) // Unsupported Media Type 822 ec.web?.response?.setStatus(415) // Unsupported Media Type
809 response = "Content-Type must be application/json for JSON-RPC messages" 823 ec.web?.response?.setContentType("text/plain")
824 ec.web?.response?.getWriter()?.write("Content-Type must be application/json for JSON-RPC messages")
825 ec.web?.response?.getWriter()?.flush()
810 return 826 return
811 } 827 }
812 828
...@@ -820,13 +836,17 @@ ...@@ -820,13 +836,17 @@
820 if (!originValid) { 836 if (!originValid) {
821 ec.logger.warn("Invalid Origin header rejected: ${originHeader}") 837 ec.logger.warn("Invalid Origin header rejected: ${originHeader}")
822 ec.web?.response?.setStatus(403) // Forbidden 838 ec.web?.response?.setStatus(403) // Forbidden
823 response = "Invalid Origin header" 839 ec.web?.response?.setContentType("text/plain")
840 ec.web?.response?.getWriter()?.write("Invalid Origin header")
841 ec.web?.response?.getWriter()?.flush()
824 return 842 return
825 } 843 }
826 } catch (Exception e) { 844 } catch (Exception e) {
827 ec.logger.error("Error during Origin validation", e) 845 ec.logger.error("Error during Origin validation", e)
828 ec.web?.response?.setStatus(500) // Internal Server Error 846 ec.web?.response?.setStatus(500) // Internal Server Error
829 response = "Error during Origin validation: ${e.message}" 847 ec.web?.response?.setContentType("text/plain")
848 ec.web?.response?.getWriter()?.write("Error during Origin validation: ${e.message}")
849 ec.web?.response?.getWriter()?.flush()
830 return 850 return
831 } 851 }
832 } else { 852 } else {
...@@ -849,7 +869,9 @@ ...@@ -849,7 +869,9 @@
849 if (!isInitialize && !sessionId) { 869 if (!isInitialize && !sessionId) {
850 ec.logger.warn("Missing session ID for non-initialization request") 870 ec.logger.warn("Missing session ID for non-initialization request")
851 ec.web?.response?.setStatus(400) // Bad Request 871 ec.web?.response?.setStatus(400) // Bad Request
852 response = "Mcp-Session-Id header required for non-initialization requests" 872 ec.web?.response?.setContentType("text/plain")
873 ec.web?.response?.getWriter()?.write("Mcp-Session-Id header required for non-initialization requests")
874 ec.web?.response?.getWriter()?.flush()
853 return 875 return
854 } 876 }
855 877
...@@ -878,7 +900,9 @@ ...@@ -878,7 +900,9 @@
878 ec.logger.info("Built JSON-RPC error response: ${errorResponse}") 900 ec.logger.info("Built JSON-RPC error response: ${errorResponse}")
879 901
880 if (wantsStreaming) { 902 if (wantsStreaming) {
881 response = "event: error\ndata: ${errorResponse}\n\n" 903 ec.web?.response?.setContentType("text/event-stream")
904 ec.web?.response?.getWriter()?.write("event: error\ndata: ${errorResponse}\n\n")
905 ec.web?.response?.getWriter()?.flush()
882 ec.logger.info("Returning streaming error response") 906 ec.logger.info("Returning streaming error response")
883 } else { 907 } else {
884 response = errorResponse 908 response = errorResponse
...@@ -983,17 +1007,20 @@ ...@@ -983,17 +1007,20 @@
983 ec.logger.info("Built JSON response: ${jsonResponse}") 1007 ec.logger.info("Built JSON response: ${jsonResponse}")
984 ec.logger.info("JSON response length: ${jsonResponse.length()}") 1008 ec.logger.info("JSON response length: ${jsonResponse.length()}")
985 1009
986 def httpResponse = ec.web?.response
987
988 // MVP: Always return JSON-RPC 2.0, no streaming 1010 // MVP: Always return JSON-RPC 2.0, no streaming
989 ec.logger.info("Creating JSON-RPC 2.0 response") 1011 ec.logger.info("Creating JSON-RPC 2.0 response")
990 if (httpResponse) { 1012 if (httpResponse) {
991 httpResponse.setContentType("application/json") 1013 httpResponse.setContentType("application/json")
992 httpResponse.setHeader("Content-Type", "application/json") 1014 httpResponse.setHeader("Content-Type", "application/json")
993 ec.logger.info("Set JSON-RPC content type") 1015 ec.logger.info("Set JSON-RPC content type: ${httpResponse.getContentType()}")
1016 ec.logger.info("Response committed before: ${httpResponse.committed}")
1017 } else {
1018 ec.logger.error("HTTP Response object is null - cannot set headers!")
994 } 1019 }
995 response = jsonResponse 1020 response = jsonResponse
996 ec.logger.info("Created JSON-RPC response, length: ${response.length()}") 1021 ec.logger.info("Created JSON-RPC response, length: ${response.length()}")
1022 ec.logger.info("Response object type: ${response?.getClass()}")
1023 ec.logger.info("Response object is null: ${response == null}")
997 1024
998 ec.logger.info("=== MCP REQUEST DEBUG END ===") 1025 ec.logger.info("=== MCP REQUEST DEBUG END ===")
999 ]]></script> 1026 ]]></script>
......