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:
parent
0db4c0f70d
commit
3fa4152287
1 changed files with 58 additions and 17 deletions
|
|
@ -82,8 +82,16 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv
|
||||||
*/
|
*/
|
||||||
public static final String DEFAULT_SSE_ENDPOINT = "/sse";
|
public static final String DEFAULT_SSE_ENDPOINT = "/sse";
|
||||||
|
|
||||||
|
public static final String DEFAULT_BASE_URL = "";
|
||||||
|
|
||||||
private final ObjectMapper objectMapper;
|
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 messageEndpoint;
|
||||||
|
|
||||||
private final String sseEndpoint;
|
private final String sseEndpoint;
|
||||||
|
|
@ -102,29 +110,6 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv
|
||||||
*/
|
*/
|
||||||
private volatile boolean isClosing = false;
|
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
|
* Constructs a new WebFlux SSE server transport provider instance with the default
|
||||||
* SSE endpoint.
|
* SSE endpoint.
|
||||||
|
|
@ -139,6 +124,46 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv
|
||||||
this(objectMapper, messageEndpoint, DEFAULT_SSE_ENDPOINT);
|
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
|
@Override
|
||||||
public void setSessionFactory(McpServerSession.Factory sessionFactory) {
|
public void setSessionFactory(McpServerSession.Factory sessionFactory) {
|
||||||
this.sessionFactory = sessionFactory;
|
this.sessionFactory = sessionFactory;
|
||||||
|
|
@ -179,7 +204,8 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv
|
||||||
.then();
|
.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.
|
// doing that.
|
||||||
/**
|
/**
|
||||||
* Initiates a graceful shutdown of all the sessions. This method ensures all active
|
* 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);
|
logger.debug("Sending initial endpoint event to session: {}", sessionId);
|
||||||
sink.next(ServerSentEvent.builder()
|
sink.next(ServerSentEvent.builder()
|
||||||
.event(ENDPOINT_EVENT_TYPE)
|
.event(ENDPOINT_EVENT_TYPE)
|
||||||
.data(messageEndpoint + "?sessionId=" + sessionId)
|
.data(this.baseUrl + this.messageEndpoint + "?sessionId=" + sessionId)
|
||||||
.build());
|
.build());
|
||||||
sink.onCancel(() -> {
|
sink.onCancel(() -> {
|
||||||
logger.debug("Session {} cancelled", sessionId);
|
logger.debug("Session {} cancelled", sessionId);
|
||||||
|
|
@ -360,6 +386,8 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv
|
||||||
|
|
||||||
private ObjectMapper objectMapper;
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
private String baseUrl = DEFAULT_BASE_URL;
|
||||||
|
|
||||||
private String messageEndpoint;
|
private String messageEndpoint;
|
||||||
|
|
||||||
private String sseEndpoint = DEFAULT_SSE_ENDPOINT;
|
private String sseEndpoint = DEFAULT_SSE_ENDPOINT;
|
||||||
|
|
@ -377,6 +405,19 @@ public class WebFluxSseServerTransportProvider implements McpServerTransportProv
|
||||||
return this;
|
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.
|
* Sets the endpoint URI where clients should send their JSON-RPC messages.
|
||||||
* @param messageEndpoint The message endpoint URI. Must not be null.
|
* @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(objectMapper, "ObjectMapper must be set");
|
||||||
Assert.notNull(messageEndpoint, "Message endpoint must be set");
|
Assert.notNull(messageEndpoint, "Message endpoint must be set");
|
||||||
|
|
||||||
return new WebFluxSseServerTransportProvider(objectMapper, messageEndpoint, sseEndpoint);
|
return new WebFluxSseServerTransportProvider(objectMapper, baseUrl, messageEndpoint, sseEndpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue