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.
Showing
1 changed file
with
62 additions
and
35 deletions
| ... | @@ -740,12 +740,17 @@ | ... | @@ -740,12 +740,17 @@ |
| 740 | 740 | ||
| 741 | ExecutionContext ec = context.ec | 741 | ExecutionContext ec = context.ec |
| 742 | 742 | ||
| 743 | // DEBUG: Log initial request details | 743 | // DEBUG: Log initial request details |
| 744 | ec.logger.info("=== MCP REQUEST DEBUG START ===") | 744 | ec.logger.info("=== MCP REQUEST DEBUG START ===") |
| 745 | ec.logger.info("MCP Request - Method: ${method}, ID: ${id}") | 745 | ec.logger.info("MCP Request - Method: ${method}, ID: ${id}") |
| 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,12 +767,15 @@ | ... | @@ -762,12 +767,15 @@ |
| 762 | ec.logger.warn("HTTP Request object is null!") | 767 | ec.logger.warn("HTTP Request object is null!") |
| 763 | } | 768 | } |
| 764 | 769 | ||
| 765 | // DEBUG: Log HTTP response details | 770 | def httpResponse = ec.web?.response |
| 766 | if (httpResponse) { | 771 | // DEBUG: Log HTTP response details |
| 767 | ec.logger.info("HTTP Response Status: ${httpResponse.status}") | 772 | if (httpResponse) { |
| 768 | } else { | 773 | ec.logger.info("HTTP Response Status: ${httpResponse.status}") |
| 769 | ec.logger.warn("HTTP Response object is null!") | 774 | ec.logger.info("HTTP Response committed: ${httpResponse.committed}") |
| 770 | } | 775 | ec.logger.info("HTTP Response content type: ${httpResponse.getContentType()}") |
| 776 | } else { | ||
| 777 | ec.logger.warn("HTTP Response object is null!") | ||
| 778 | } | ||
| 771 | 779 | ||
| 772 | // Validate HTTP method - only POST allowed for JSON-RPC messages | 780 | // Validate HTTP method - only POST allowed for JSON-RPC messages |
| 773 | def httpMethod = ec.web?.request?.method | 781 | def httpMethod = ec.web?.request?.method |
| ... | @@ -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 |
| ... | @@ -979,21 +1003,24 @@ | ... | @@ -979,21 +1003,24 @@ |
| 979 | ec.logger.info("Response includes result") | 1003 | ec.logger.info("Response includes result") |
| 980 | } | 1004 | } |
| 981 | 1005 | ||
| 982 | def jsonResponse = new JsonBuilder(responseObj).toString() | 1006 | def jsonResponse = new JsonBuilder(responseObj).toString() |
| 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 | 1010 | // MVP: Always return JSON-RPC 2.0, no streaming |
| 987 | 1011 | ec.logger.info("Creating JSON-RPC 2.0 response") | |
| 988 | // MVP: Always return JSON-RPC 2.0, no streaming | 1012 | if (httpResponse) { |
| 989 | ec.logger.info("Creating JSON-RPC 2.0 response") | 1013 | httpResponse.setContentType("application/json") |
| 990 | if (httpResponse) { | 1014 | httpResponse.setHeader("Content-Type", "application/json") |
| 991 | httpResponse.setContentType("application/json") | 1015 | ec.logger.info("Set JSON-RPC content type: ${httpResponse.getContentType()}") |
| 992 | httpResponse.setHeader("Content-Type", "application/json") | 1016 | ec.logger.info("Response committed before: ${httpResponse.committed}") |
| 993 | ec.logger.info("Set JSON-RPC content type") | 1017 | } else { |
| 994 | } | 1018 | ec.logger.error("HTTP Response object is null - cannot set headers!") |
| 995 | response = jsonResponse | 1019 | } |
| 996 | ec.logger.info("Created JSON-RPC response, length: ${response.length()}") | 1020 | response = jsonResponse |
| 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> | ... | ... |
-
Please register or sign in to post a comment