Skip to content

Commit c4dd471

Browse files
committed
Implement AbstractStatelessIntegrationTests with Servlet
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
1 parent 305e4cb commit c4dd471

1 file changed

Lines changed: 104 additions & 0 deletions

File tree

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2024 - 2026 the original author or authors.
3+
*/
4+
5+
package io.modelcontextprotocol.server;
6+
7+
import java.time.Duration;
8+
import java.util.Map;
9+
import java.util.stream.Stream;
10+
11+
import io.modelcontextprotocol.AbstractStatelessIntegrationTests;
12+
import io.modelcontextprotocol.client.McpClient;
13+
import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport;
14+
import io.modelcontextprotocol.common.McpTransportContext;
15+
import io.modelcontextprotocol.server.McpServer.StatelessAsyncSpecification;
16+
import io.modelcontextprotocol.server.McpServer.StatelessSyncSpecification;
17+
import io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport;
18+
import io.modelcontextprotocol.server.transport.TomcatTestUtil;
19+
import jakarta.servlet.http.HttpServletRequest;
20+
import org.apache.catalina.LifecycleException;
21+
import org.apache.catalina.LifecycleState;
22+
import org.apache.catalina.startup.Tomcat;
23+
import org.junit.jupiter.api.AfterEach;
24+
import org.junit.jupiter.api.BeforeEach;
25+
import org.junit.jupiter.api.Timeout;
26+
import org.junit.jupiter.params.provider.Arguments;
27+
28+
import static org.assertj.core.api.Assertions.assertThat;
29+
30+
/**
31+
* Runs the shared stateless integration suite over the servlet stateless transport.
32+
*/
33+
@Timeout(15)
34+
class HttpServletStatelessSharedIntegrationTests extends AbstractStatelessIntegrationTests {
35+
36+
private static final int PORT = TomcatTestUtil.findAvailablePort();
37+
38+
private static final String MESSAGE_ENDPOINT = "/mcp/message";
39+
40+
static McpTransportContextExtractor<HttpServletRequest> TEST_CONTEXT_EXTRACTOR = (request) -> McpTransportContext
41+
.create(Map.of("important", "value"));
42+
43+
private HttpServletStatelessServerTransport mcpServerTransport;
44+
45+
private Tomcat tomcat;
46+
47+
static Stream<Arguments> clientsForTesting() {
48+
return Stream.of(Arguments.of("httpclient"));
49+
}
50+
51+
@BeforeEach
52+
public void before() {
53+
this.mcpServerTransport = HttpServletStatelessServerTransport.builder()
54+
.contextExtractor(TEST_CONTEXT_EXTRACTOR)
55+
.messageEndpoint(MESSAGE_ENDPOINT)
56+
.build();
57+
58+
this.tomcat = TomcatTestUtil.createTomcatServer("", PORT, this.mcpServerTransport);
59+
try {
60+
this.tomcat.start();
61+
assertThat(this.tomcat.getServer().getState()).isEqualTo(LifecycleState.STARTED);
62+
}
63+
catch (Exception e) {
64+
throw new RuntimeException("Failed to start Tomcat", e);
65+
}
66+
67+
prepareClients(PORT, MESSAGE_ENDPOINT);
68+
}
69+
70+
@Override
71+
protected void prepareClients(int port, String mcpEndpoint) {
72+
this.clientBuilders.put("httpclient", McpClient
73+
.sync(HttpClientStreamableHttpTransport.builder("http://localhost:" + port).endpoint(mcpEndpoint).build())
74+
.initializationTimeout(Duration.ofHours(10))
75+
.requestTimeout(Duration.ofHours(10)));
76+
}
77+
78+
@Override
79+
protected StatelessAsyncSpecification prepareAsyncServerBuilder() {
80+
return McpServer.async(this.mcpServerTransport);
81+
}
82+
83+
@Override
84+
protected StatelessSyncSpecification prepareSyncServerBuilder() {
85+
return McpServer.sync(this.mcpServerTransport);
86+
}
87+
88+
@AfterEach
89+
public void after() {
90+
if (this.mcpServerTransport != null) {
91+
this.mcpServerTransport.closeGracefully().block();
92+
}
93+
if (this.tomcat != null) {
94+
try {
95+
this.tomcat.stop();
96+
this.tomcat.destroy();
97+
}
98+
catch (LifecycleException e) {
99+
throw new RuntimeException("Failed to stop Tomcat", e);
100+
}
101+
}
102+
}
103+
104+
}

0 commit comments

Comments
 (0)