fix: return 405 on POST /sse so MCP clients fall back to legacy SSE transport
Modern MCP clients (Claude Code, etc.) probe for Streamable HTTP transport by
POSTing an InitializeRequest to the server URL first. Per MCP spec 2025-11-25,
they fall back to legacy HTTP+SSE only on 400/404/405.
Previously, POST /sse fell through the SPA resource resolver (which had 'sse'
missing from EXCLUDED_PREFIXES) and returned 500, causing clients to abort
instead of retrying with the GET-based SSE handshake.
Fix:
- Add 'sse' to EXCLUDED_PREFIXES so the SPA resolver ignores that path
- Add explicit @PostMapping("/sse") returning 405 Method Not Allowed with
Allow: GET header — the correct signal for Streamable HTTP probe fallback
This commit is contained in:
@@ -5,6 +5,11 @@ import java.util.Set;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.web.servlet.resource.PathResourceResolver;
|
||||
@@ -15,11 +20,26 @@ import org.springframework.web.servlet.resource.PathResourceResolver;
|
||||
* explicitly excluded — Spring routes those first anyway, but we exclude them defensively so the
|
||||
* resource resolver does not attempt a fallback for them.
|
||||
*/
|
||||
/**
|
||||
* Explicit POST handler for the SSE endpoint. Modern MCP clients (e.g. Claude Code) probe for
|
||||
* Streamable HTTP transport by POSTing to the server URL first. Returning 405 here tells them this
|
||||
* server uses the legacy HTTP+SSE transport, triggering the correct GET-based SSE fallback.
|
||||
*/
|
||||
@RestController
|
||||
class McpSseMethodNotAllowed {
|
||||
@PostMapping("/sse")
|
||||
ResponseEntity<Void> rejectPost() {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add(HttpHeaders.ALLOW, "GET");
|
||||
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).headers(headers).build();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
private static final Set<String> EXCLUDED_PREFIXES =
|
||||
Set.of("api/", "mcp", "swagger-ui/", "v3/api-docs", "actuator/");
|
||||
Set.of("api/", "mcp", "sse", "swagger-ui/", "v3/api-docs", "actuator/");
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
|
||||
Reference in New Issue
Block a user