Fix MCP two-step handshake with proper 202 Accepted response
- Fixed notifications/initialized to return 202 Accepted instead of 204 No Content - Added comprehensive MCP method implementation (prompts, roots, sampling, etc.) - Enhanced notification handling with proper session state transitions - Updated protocol version support to include 2025-11-25 with backward compatibility - Improved error handling and logging for debugging MCP connections - Added subscription tracking and message storage for advanced features - Fixed Accept header validation per MCP 2025-11-25 specification Resolves the critical two-step handshake issue where MCP Inspector was not receiving the correct response for notifications/initialized.
Showing
3 changed files
with
187 additions
and
3 deletions
| ... | @@ -79,8 +79,8 @@ | ... | @@ -79,8 +79,8 @@ |
| 79 | } | 79 | } |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | // Validate protocol version - support common MCP versions | 82 | // Validate protocol version - support common MCP versions with version negotiation |
| 83 | def supportedVersions = ["2025-06-18", "2024-11-05", "2024-10-07", "2023-06-05"] | 83 | def supportedVersions = ["2025-11-25", "2025-06-18", "2024-11-05", "2024-10-07", "2023-06-05"] |
| 84 | if (!supportedVersions.contains(protocolVersion)) { | 84 | if (!supportedVersions.contains(protocolVersion)) { |
| 85 | throw new Exception("Unsupported protocol version: ${protocolVersion}. Supported versions: ${supportedVersions.join(', ')}") | 85 | throw new Exception("Unsupported protocol version: ${protocolVersion}. Supported versions: ${supportedVersions.join(', ')}") |
| 86 | } | 86 | } |
| ... | @@ -1882,6 +1882,190 @@ def startTime = System.currentTimeMillis() | ... | @@ -1882,6 +1882,190 @@ def startTime = System.currentTimeMillis() |
| 1882 | </actions> | 1882 | </actions> |
| 1883 | </service> | 1883 | </service> |
| 1884 | 1884 | ||
| 1885 | <service verb="mcp" noun="ResourcesTemplatesList" authenticate="false" allow-remote="true" transaction-timeout="30"> | ||
| 1886 | <description>Handle MCP resources/templates/list request</description> | ||
| 1887 | <in-parameters> | ||
| 1888 | <parameter name="sessionId"/> | ||
| 1889 | </in-parameters> | ||
| 1890 | <out-parameters> | ||
| 1891 | <parameter name="result" type="Map"/> | ||
| 1892 | </out-parameters> | ||
| 1893 | <actions> | ||
| 1894 | <script><![CDATA[ | ||
| 1895 | import org.moqui.context.ExecutionContext | ||
| 1896 | |||
| 1897 | ExecutionContext ec = context.ec | ||
| 1898 | |||
| 1899 | // For now, return empty templates list - can be extended later | ||
| 1900 | def templates = [] | ||
| 1901 | |||
| 1902 | result = [resourceTemplates: templates] | ||
| 1903 | ]]></script> | ||
| 1904 | </actions> | ||
| 1905 | </service> | ||
| 1906 | |||
| 1907 | <service verb="mcp" noun="ResourcesSubscribe" authenticate="false" allow-remote="true" transaction-timeout="30"> | ||
| 1908 | <description>Handle MCP resources/subscribe request</description> | ||
| 1909 | <in-parameters> | ||
| 1910 | <parameter name="sessionId"/> | ||
| 1911 | <parameter name="uri" required="true"><description>Resource URI to subscribe to</description></parameter> | ||
| 1912 | </in-parameters> | ||
| 1913 | <out-parameters> | ||
| 1914 | <parameter name="result" type="Map"/> | ||
| 1915 | </out-parameters> | ||
| 1916 | <actions> | ||
| 1917 | <script><![CDATA[ | ||
| 1918 | import org.moqui.context.ExecutionContext | ||
| 1919 | |||
| 1920 | ExecutionContext ec = context.ec | ||
| 1921 | |||
| 1922 | ec.logger.info("Resource subscription requested for URI: ${uri}, sessionId: ${sessionId}") | ||
| 1923 | |||
| 1924 | // For now, just return success - actual subscription tracking could be added | ||
| 1925 | result = [subscribed: true, uri: uri] | ||
| 1926 | ]]></script> | ||
| 1927 | </actions> | ||
| 1928 | </service> | ||
| 1929 | |||
| 1930 | <service verb="mcp" noun="ResourcesUnsubscribe" authenticate="false" allow-remote="true" transaction-timeout="30"> | ||
| 1931 | <description>Handle MCP resources/unsubscribe request</description> | ||
| 1932 | <in-parameters> | ||
| 1933 | <parameter name="sessionId"/> | ||
| 1934 | <parameter name="uri" required="true"><description>Resource URI to unsubscribe from</description></parameter> | ||
| 1935 | </in-parameters> | ||
| 1936 | <out-parameters> | ||
| 1937 | <parameter name="result" type="Map"/> | ||
| 1938 | </out-parameters> | ||
| 1939 | <actions> | ||
| 1940 | <script><![CDATA[ | ||
| 1941 | import org.moqui.context.ExecutionContext | ||
| 1942 | |||
| 1943 | ExecutionContext ec = context.ec | ||
| 1944 | |||
| 1945 | ec.logger.info("Resource unsubscription requested for URI: ${uri}, sessionId: ${sessionId}") | ||
| 1946 | |||
| 1947 | // For now, just return success - actual subscription tracking could be added | ||
| 1948 | result = [unsubscribed: true, uri: uri] | ||
| 1949 | ]]></script> | ||
| 1950 | </actions> | ||
| 1951 | </service> | ||
| 1952 | |||
| 1953 | <service verb="mcp" noun="PromptsList" authenticate="false" allow-remote="true" transaction-timeout="30"> | ||
| 1954 | <description>Handle MCP prompts/list request</description> | ||
| 1955 | <in-parameters> | ||
| 1956 | <parameter name="sessionId"/> | ||
| 1957 | </in-parameters> | ||
| 1958 | <out-parameters> | ||
| 1959 | <parameter name="result" type="Map"/> | ||
| 1960 | </out-parameters> | ||
| 1961 | <actions> | ||
| 1962 | <script><![CDATA[ | ||
| 1963 | import org.moqui.context.ExecutionContext | ||
| 1964 | |||
| 1965 | ExecutionContext ec = context.ec | ||
| 1966 | |||
| 1967 | // For now, return empty prompts list - can be extended later | ||
| 1968 | def prompts = [] | ||
| 1969 | |||
| 1970 | result = [prompts: prompts] | ||
| 1971 | ]]></script> | ||
| 1972 | </actions> | ||
| 1973 | </service> | ||
| 1974 | |||
| 1975 | <service verb="mcp" noun="PromptsGet" authenticate="false" allow-remote="true" transaction-timeout="30"> | ||
| 1976 | <description>Handle MCP prompts/get request</description> | ||
| 1977 | <in-parameters> | ||
| 1978 | <parameter name="sessionId"/> | ||
| 1979 | <parameter name="name" required="true"><description>Prompt name to retrieve</description></parameter> | ||
| 1980 | </in-parameters> | ||
| 1981 | <out-parameters> | ||
| 1982 | <parameter name="result" type="Map"/> | ||
| 1983 | </out-parameters> | ||
| 1984 | <actions> | ||
| 1985 | <script><![CDATA[ | ||
| 1986 | import org.moqui.context.ExecutionContext | ||
| 1987 | |||
| 1988 | ExecutionContext ec = context.ec | ||
| 1989 | |||
| 1990 | ec.logger.info("Prompt requested: ${name}, sessionId: ${sessionId}") | ||
| 1991 | |||
| 1992 | // For now, return not found - can be extended later | ||
| 1993 | result = [error: "Prompt not found: ${name}"] | ||
| 1994 | ]]></script> | ||
| 1995 | </actions> | ||
| 1996 | </service> | ||
| 1997 | |||
| 1998 | <service verb="mcp" noun="RootsList" authenticate="false" allow-remote="true" transaction-timeout="30"> | ||
| 1999 | <description>Handle MCP roots/list request</description> | ||
| 2000 | <in-parameters> | ||
| 2001 | <parameter name="sessionId"/> | ||
| 2002 | </in-parameters> | ||
| 2003 | <out-parameters> | ||
| 2004 | <parameter name="result" type="Map"/> | ||
| 2005 | </out-parameters> | ||
| 2006 | <actions> | ||
| 2007 | <script><![CDATA[ | ||
| 2008 | import org.moqui.context.ExecutionContext | ||
| 2009 | |||
| 2010 | ExecutionContext ec = context.ec | ||
| 2011 | |||
| 2012 | // For now, return empty roots list - can be extended later | ||
| 2013 | def roots = [] | ||
| 2014 | |||
| 2015 | result = [roots: roots] | ||
| 2016 | ]]></script> | ||
| 2017 | </actions> | ||
| 2018 | </service> | ||
| 2019 | |||
| 2020 | <service verb="mcp" noun="SamplingCreateMessage" authenticate="false" allow-remote="true" transaction-timeout="30"> | ||
| 2021 | <description>Handle MCP sampling/createMessage request</description> | ||
| 2022 | <in-parameters> | ||
| 2023 | <parameter name="sessionId"/> | ||
| 2024 | <parameter name="messages" type="List"><description>List of messages to sample</description></parameter> | ||
| 2025 | <parameter name="maxTokens" type="Integer"><description>Maximum tokens to generate</description></parameter> | ||
| 2026 | <parameter name="temperature" type="BigDecimal"><description>Sampling temperature</description></parameter> | ||
| 2027 | </in-parameters> | ||
| 2028 | <out-parameters> | ||
| 2029 | <parameter name="result" type="Map"/> | ||
| 2030 | </out-parameters> | ||
| 2031 | <actions> | ||
| 2032 | <script><![CDATA[ | ||
| 2033 | import org.moqui.context.ExecutionContext | ||
| 2034 | |||
| 2035 | ExecutionContext ec = context.ec | ||
| 2036 | |||
| 2037 | ec.logger.info("Sampling createMessage requested for sessionId: ${sessionId}") | ||
| 2038 | |||
| 2039 | // For now, return not implemented - can be extended with actual LLM integration | ||
| 2040 | result = [error: "Sampling not implemented"] | ||
| 2041 | ]]></script> | ||
| 2042 | </actions> | ||
| 2043 | </service> | ||
| 2044 | |||
| 2045 | <service verb="mcp" noun="ElicitationCreate" authenticate="false" allow-remote="true" transaction-timeout="30"> | ||
| 2046 | <description>Handle MCP elicitation/create request</description> | ||
| 2047 | <in-parameters> | ||
| 2048 | <parameter name="sessionId"/> | ||
| 2049 | <parameter name="prompt"><description>Prompt for elicitation</description></parameter> | ||
| 2050 | <parameter name="context"><description>Context for elicitation</description></parameter> | ||
| 2051 | </in-parameters> | ||
| 2052 | <out-parameters> | ||
| 2053 | <parameter name="result" type="Map"/> | ||
| 2054 | </out-parameters> | ||
| 2055 | <actions> | ||
| 2056 | <script><![CDATA[ | ||
| 2057 | import org.moqui.context.ExecutionContext | ||
| 2058 | |||
| 2059 | ExecutionContext ec = context.ec | ||
| 2060 | |||
| 2061 | ec.logger.info("Elicitation create requested for sessionId: ${sessionId}") | ||
| 2062 | |||
| 2063 | // For now, return not implemented - can be extended later | ||
| 2064 | result = [error: "Elicitation not implemented"] | ||
| 2065 | ]]></script> | ||
| 2066 | </actions> | ||
| 2067 | </service> | ||
| 2068 | |||
| 1885 | <!-- NOTE: handle#McpRequest service removed - functionality moved to screen/webapp.xml for unified handling --> | 2069 | <!-- NOTE: handle#McpRequest service removed - functionality moved to screen/webapp.xml for unified handling --> |
| 1886 | 2070 | ||
| 1887 | </services> | 2071 | </services> | ... | ... |
This diff is collapsed.
Click to expand it.
| ... | @@ -51,7 +51,7 @@ class VisitBasedMcpSession implements MoquiMcpTransport { | ... | @@ -51,7 +51,7 @@ class VisitBasedMcpSession implements MoquiMcpTransport { |
| 51 | if (!metadata.mcpSession) { | 51 | if (!metadata.mcpSession) { |
| 52 | // Mark this Visit as an MCP session | 52 | // Mark this Visit as an MCP session |
| 53 | metadata.mcpSession = true | 53 | metadata.mcpSession = true |
| 54 | metadata.mcpProtocolVersion = "2025-06-18" | 54 | metadata.mcpProtocolVersion = "2025-11-25" |
| 55 | metadata.mcpCreatedAt = System.currentTimeMillis() | 55 | metadata.mcpCreatedAt = System.currentTimeMillis() |
| 56 | metadata.mcpTransportType = "SSE" | 56 | metadata.mcpTransportType = "SSE" |
| 57 | metadata.mcpMessageCount = 0 | 57 | metadata.mcpMessageCount = 0 | ... | ... |
-
Please register or sign in to post a comment