com.h2database
diff --git a/trueref-adapters/src/main/java/com/trueref/adapter/in/mcp/McpConfig.java b/trueref-adapters/src/main/java/com/trueref/adapter/in/mcp/McpConfig.java
index 9bb9bbe..e936600 100644
--- a/trueref-adapters/src/main/java/com/trueref/adapter/in/mcp/McpConfig.java
+++ b/trueref-adapters/src/main/java/com/trueref/adapter/in/mcp/McpConfig.java
@@ -1,22 +1,47 @@
package com.trueref.adapter.in.mcp;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.modelcontextprotocol.json.jackson2.JacksonMcpJsonMapper;
+import io.modelcontextprotocol.server.transport.WebMvcStreamableServerTransportProvider;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.function.RouterFunction;
+import org.springframework.web.servlet.function.ServerResponse;
/**
- * Registers the trueref MCP tool callbacks with Spring AI's MCP WebMVC auto-configuration. The
- * {@link MethodToolCallbackProvider} scans {@link TrueRefMcpTools} for methods annotated with
- * {@link org.springframework.ai.tool.annotation.Tool} and publishes them on the MCP endpoint
- * configured in {@code application.yml} (POST {@code /mcp} via
- * {@code spring.ai.mcp.server.sse-message-endpoint}).
+ * Wires the MCP Streamable HTTP transport (2025-03-26 spec) and registers trueref tool callbacks.
+ *
+ * Spring AI 1.0.0 only ships {@link io.modelcontextprotocol.server.transport.WebMvcSseServerTransportProvider}
+ * (legacy SSE). We exclude {@code McpWebMvcServerAutoConfiguration} in {@code application.yml} and
+ * register {@link WebMvcStreamableServerTransportProvider} directly — Spring AI's
+ * {@code McpServerAutoConfiguration} accepts any {@code McpServerTransportProvider} bean.
+ *
+ *
Clients connect with {@code type: http} at {@code POST /mcp} (JSON-RPC) and optionally open
+ * a long-poll GET {@code /mcp} stream for server-initiated notifications.
*/
@Configuration
@EnableConfigurationProperties(McpProperties.class)
public class McpConfig {
+ /** Streamable HTTP transport — handles both POST (JSON-RPC) and GET (SSE stream) on /mcp. */
+ @Bean
+ public WebMvcStreamableServerTransportProvider streamableTransportProvider(ObjectMapper objectMapper) {
+ return WebMvcStreamableServerTransportProvider.builder()
+ .jsonMapper(new JacksonMcpJsonMapper(objectMapper))
+ .mcpEndpoint("/mcp")
+ .build();
+ }
+
+ /** Registers the transport's GET+POST routes with Spring MVC. */
+ @Bean
+ public RouterFunction mcpStreamableRoutes(
+ WebMvcStreamableServerTransportProvider transport) {
+ return transport.getRouterFunction();
+ }
+
@Bean
public ToolCallbackProvider trueRefMcpToolCallbacks(TrueRefMcpTools tools) {
return MethodToolCallbackProvider.builder().toolObjects(tools).build();
diff --git a/trueref-adapters/src/main/java/com/trueref/adapter/in/rest/WebConfig.java b/trueref-adapters/src/main/java/com/trueref/adapter/in/rest/WebConfig.java
index ea95632..071d9b4 100644
--- a/trueref-adapters/src/main/java/com/trueref/adapter/in/rest/WebConfig.java
+++ b/trueref-adapters/src/main/java/com/trueref/adapter/in/rest/WebConfig.java
@@ -5,11 +5,6 @@ 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;
@@ -20,26 +15,11 @@ 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 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 EXCLUDED_PREFIXES =
- Set.of("api/", "mcp", "sse", "swagger-ui/", "v3/api-docs", "actuator/");
+ Set.of("api/", "mcp", "swagger-ui/", "v3/api-docs", "actuator/");
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
diff --git a/trueref-bootstrap/src/main/resources/application.yml b/trueref-bootstrap/src/main/resources/application.yml
index f771192..c6f8bee 100644
--- a/trueref-bootstrap/src/main/resources/application.yml
+++ b/trueref-bootstrap/src/main/resources/application.yml
@@ -19,12 +19,13 @@ spring:
locations: classpath:db/migration
mvc:
async:
- request-timeout: 0 # SSE streams must not time out
- # Spring AI MCP server. In Spring AI 1.0.0 the WebMVC transport is SSE-based
- # (WebMvcSseServerTransportProvider) — the closest available transport to the 2025-03-26
- # "Streamable HTTP" spec; there is no separate "protocol: streamable" property in this
- # starter. JSON-RPC POSTs land on `sse-message-endpoint` (/mcp); server-initiated
- # notifications stream over `sse-endpoint` (/sse). See com.trueref.adapter.in.mcp.
+ request-timeout: 0 # MCP GET streams must not time out
+ # Spring AI MCP server — Streamable HTTP transport (MCP spec 2025-03-26).
+ # McpWebMvcServerAutoConfiguration (SSE transport) is excluded below;
+ # WebMvcStreamableServerTransportProvider is wired manually in McpConfig.
+ # Clients connect with type: http at POST /mcp.
+ autoconfigure:
+ exclude: org.springframework.ai.mcp.server.autoconfigure.McpWebMvcServerAutoConfiguration
ai:
mcp:
server:
@@ -32,8 +33,6 @@ spring:
name: trueref
version: 0.1.0
type: SYNC
- sse-message-endpoint: /mcp
- sse-endpoint: /sse
server:
port: 8080