feat(webflux): Add base URL support to WebFluxSseServerTransportProvider (#102)

Adds the ability to specify a base URL prefix for message endpoints in the
WebFlux SSE server transport provider. This enhancement allows for proper
URL construction when the server is running behind a proxy or in a context
with a base path.

- Add new constructor with baseUrl parameter
- Add basePath() method to Builder class
- Modify SSE endpoint event to include baseUrl prefix

Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
This commit is contained in:
zhangzhenhua 2025-04-02 13:54:14 +08:00 • committed by Christian Tzolov
commit 3fa4152287

View file

@ -82,8 +82,16 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv
*/
public static final String DEFAULT_SSE_ENDPOINT = "/sse";
public static final String DEFAULT_BASE_URL = "";
private final ObjectMapper objectMapper;
/**
* Base URL for the message endpoint. This is used to construct the full URL for
* clients to send their JSON-RPC messages.
*/
private final String baseUrl;
private final String messageEndpoint;
private final String sseEndpoint;
@ -102,29 +110,6 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv
*/
private volatile boolean isClosing = false;
/**
* Constructs a new WebFlux SSE server transport provider instance.
* @param objectMapper The ObjectMapper to use for JSON serialization/deserialization
* of MCP messages. Must not be null.
* @param messageEndpoint The endpoint URI where clients should send their JSON-RPC
* messages. This endpoint will be communicated to clients during SSE connection
* setup. Must not be null.
* @throws IllegalArgumentException if either parameter is null
*/
public WebFluxSseServerTransportProvider(ObjectMapper objectMapper, String messageEndpoint, String sseEndpoint) {
Assert.notNull(objectMapper, "ObjectMapper must not be null");
Assert.notNull(messageEndpoint, "Message endpoint must not be null");
Assert.notNull(sseEndpoint, "SSE endpoint must not be null");
this.objectMapper = objectMapper;
this.messageEndpoint = messageEndpoint;
this.sseEndpoint = sseEndpoint;
this.routerFunction = RouterFunctions.route()
.GET(this.sseEndpoint, this::handleSseConnection)
.POST(this.messageEndpoint, this::handleMessage)
.build();
}
/**
* Constructs a new WebFlux SSE server transport provider instance with the default
* SSE endpoint.
@ -139,6 +124,46 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv
this(objectMapper, messageEndpoint, DEFAULT_SSE_ENDPOINT);
}
/**
* Constructs a new WebFlux SSE server transport provider instance.
* @param objectMapper The ObjectMapper to use for JSON serialization/deserialization
* of MCP messages. Must not be null.
* @param messageEndpoint The endpoint URI where clients should send their JSON-RPC
* messages. This endpoint will be communicated to clients during SSE connection
* setup. Must not be null.
* @throws IllegalArgumentException if either parameter is null
*/
public WebFluxSseServerTransportProvider(ObjectMapper objectMapper, String messageEndpoint, String sseEndpoint) {
this(objectMapper, DEFAULT_BASE_URL, messageEndpoint, sseEndpoint);
}
/**
* Constructs a new WebFlux SSE server transport provider instance.
* @param objectMapper The ObjectMapper to use for JSON serialization/deserialization
* of MCP messages. Must not be null.
* @param baseUrl webflux messag base path
* @param messageEndpoint The endpoint URI where clients should send their JSON-RPC
* messages. This endpoint will be communicated to clients during SSE connection
* setup. Must not be null.
* @throws IllegalArgumentException if either parameter is null
*/
public WebFluxSseServerTransportProvider(ObjectMapper objectMapper, String baseUrl, String messageEndpoint,
String sseEndpoint) {
Assert.notNull(objectMapper, "ObjectMapper must not be null");
Assert.notNull(baseUrl, "Message base path must not be null");
Assert.notNull(messageEndpoint, "Message endpoint must not be null");
Assert.notNull(sseEndpoint, "SSE endpoint must not be null");
this.objectMapper = objectMapper;
this.baseUrl = baseUrl;
this.messageEndpoint = messageEndpoint;
this.sseEndpoint = sseEndpoint;
this.routerFunction = RouterFunctions.route()
.GET(this.sseEndpoint, this::handleSseConnection)
.POST(this.messageEndpoint, this::handleMessage)
.build();
}
@Override
public void setSessionFactory(McpServerSession.Factory sessionFactory) {
this.sessionFactory = sessionFactory;
@ -179,7 +204,8 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv
.then();
}
// FIXME: This javadoc makes claims about using isClosing flag but it's not actually
// FIXME: This javadoc makes claims about using isClosing flag but it's not
// actually
// doing that.
/**
* Initiates a graceful shutdown of all the sessions. This method ensures all active
@ -245,7 +271,7 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv
logger.debug("Sending initial endpoint event to session: {}", sessionId);
sink.next(ServerSentEvent.builder()
.event(ENDPOINT_EVENT_TYPE)
.data(messageEndpoint + "?sessionId=" + sessionId)
.data(this.baseUrl + this.messageEndpoint + "?sessionId=" + sessionId)
.build());
sink.onCancel(() -> {
logger.debug("Session {} cancelled", sessionId);
@ -360,6 +386,8 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv
private ObjectMapper objectMapper;
private String baseUrl = DEFAULT_BASE_URL;
private String messageEndpoint;
private String sseEndpoint = DEFAULT_SSE_ENDPOINT;
@ -377,6 +405,19 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv
return this;
}
/**
* Sets the project basePath as endpoint prefix where clients should send their
* JSON-RPC messages
* @param baseUrl the message basePath . Must not be null.
* @return this builder instance
* @throws IllegalArgumentException if basePath is null
*/
public Builder basePath(String baseUrl) {
Assert.notNull(baseUrl, "basePath must not be null");
this.baseUrl = baseUrl;
return this;
}
/**
* Sets the endpoint URI where clients should send their JSON-RPC messages.
* @param messageEndpoint The message endpoint URI. Must not be null.
@ -411,7 +452,7 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv
Assert.notNull(objectMapper, "ObjectMapper must be set");
Assert.notNull(messageEndpoint, "Message endpoint must be set");
return new WebFluxSseServerTransportProvider(objectMapper, messageEndpoint, sseEndpoint);
return new WebFluxSseServerTransportProvider(objectMapper, baseUrl, messageEndpoint, sseEndpoint);
}
}