Skip to content

Commit 8fd9829

Browse files
committed
Depend on SNAPSHOT version of Helidon. Initial switch to SSE connection to support MCP features.
1 parent 9ebe34c commit 8fd9829

File tree

10 files changed

+186
-39
lines changed

10 files changed

+186
-39
lines changed

examples/calendar/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<parent>
2424
<groupId>io.helidon.applications</groupId>
2525
<artifactId>helidon-se</artifactId>
26-
<version>4.3.0-M1</version>
26+
<version>4.3.0-SNAPSHOT</version>
2727
<relativePath/>
2828
</parent>
2929

examples/calendar/src/test/java/io/helidon/extensions/mcp/examples/calendar/StreamableHttpClientTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package io.helidon.extensions.mcp.examples.calendar;
1818

19+
import java.time.Duration;
20+
1921
import io.helidon.webserver.WebServer;
2022
import io.helidon.webserver.testing.junit5.ServerTest;
2123

@@ -30,7 +32,9 @@ class StreamableHttpClientTest extends BaseTest {
3032
StreamableHttpClientTest(WebServer server) {
3133
this.client = McpClient.sync(HttpClientStreamableHttpTransport.builder("http://localhost:" + server.port())
3234
.endpoint("/calendar")
33-
.build()).build();
35+
.build())
36+
.requestTimeout(Duration.ofSeconds(100000))
37+
.build();
3438
client.initialize();
3539
}
3640

examples/weather-application/mcp-client/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<parent>
2424
<groupId>io.helidon.applications</groupId>
2525
<artifactId>helidon-se</artifactId>
26-
<version>4.3.0-M1</version>
26+
<version>4.3.0-SNAPSHOT</version>
2727
<relativePath/>
2828
</parent>
2929

examples/weather-application/mcp-server-declarative/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<parent>
2424
<groupId>io.helidon.applications</groupId>
2525
<artifactId>helidon-se</artifactId>
26-
<version>4.3.0-M1</version>
26+
<version>4.3.0-SNAPSHOT</version>
2727
<relativePath/>
2828
</parent>
2929

examples/weather-application/mcp-server/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<parent>
2424
<groupId>io.helidon.applications</groupId>
2525
<artifactId>helidon-se</artifactId>
26-
<version>4.3.0-M1</version>
26+
<version>4.3.0-SNAPSHOT</version>
2727
<relativePath/>
2828
</parent>
2929

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060

6161
<properties>
6262
<version.java>21</version.java>
63-
<helidon.version>4.3.0-M1</helidon.version>
63+
<helidon.version>4.3.0-SNAPSHOT</helidon.version>
6464

6565
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
6666
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

server/src/main/java/io/helidon/extensions/mcp/server/McpFeatures.java

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,33 @@
1616

1717
package io.helidon.extensions.mcp.server;
1818

19+
import java.util.Objects;
20+
import java.util.Optional;
21+
22+
import io.helidon.webserver.jsonrpc.JsonRpcResponse;
23+
import io.helidon.webserver.sse.SseSink;
24+
1925
/**
2026
* Support for optional client features like {@link McpProgress} and {@link McpLogger}.
2127
*/
2228
public final class McpFeatures {
23-
private final McpProgress progress;
24-
private final McpLogger logger;
29+
private final JsonRpcResponse response;
30+
private final McpSession session;
31+
32+
private McpProgress progress;
33+
private McpLogger logger;
34+
private SseSink sseSink;
2535

2636
McpFeatures(McpSession session) {
27-
this.logger = new McpLogger(session);
28-
this.progress = new McpProgress(session);
37+
Objects.requireNonNull(session, "session is null");
38+
this.session = session;
39+
this.response = null;
40+
}
41+
42+
McpFeatures(JsonRpcResponse response) {
43+
Objects.requireNonNull(response, "response is null");
44+
this.response = response;
45+
this.session = null;
2946
}
3047

3148
/**
@@ -34,6 +51,14 @@ public final class McpFeatures {
3451
* @return progress
3552
*/
3653
public McpProgress progress() {
54+
if (progress == null) {
55+
if (response != null) {
56+
sseSink = getOrCreateSseSink();
57+
progress = new McpProgress(sseSink);
58+
} else if (session != null) {
59+
progress = new McpProgress(session);
60+
}
61+
}
3762
return progress;
3863
}
3964

@@ -43,6 +68,33 @@ public McpProgress progress() {
4368
* @return logging
4469
*/
4570
public McpLogger logger() {
71+
if (logger == null) {
72+
if (response != null) {
73+
sseSink = getOrCreateSseSink();
74+
logger = new McpLogger(sseSink);
75+
} else if (session != null) {
76+
logger = new McpLogger(session);
77+
}
78+
}
4679
return logger;
4780
}
81+
82+
/**
83+
* Get access to underlying SSE sink, if available. This method is package private.
84+
*
85+
* @return optional SSE sink
86+
*/
87+
Optional<SseSink> sseSink() {
88+
return Optional.ofNullable(sseSink);
89+
}
90+
91+
/**
92+
* Get or create an SSE sink.
93+
*
94+
* @return the SSE sink
95+
*/
96+
private SseSink getOrCreateSseSink() {
97+
Objects.requireNonNull(response, "response is null");
98+
return sseSink != null ? sseSink : response.sink(SseSink.TYPE);
99+
}
48100
}

server/src/main/java/io/helidon/extensions/mcp/server/McpLogger.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,30 @@
1818

1919
import java.util.Objects;
2020

21+
import io.helidon.http.sse.SseEvent;
22+
import io.helidon.webserver.sse.SseSink;
23+
2124
/**
2225
* Mcp logger to send notification to the client.
2326
*/
2427
public final class McpLogger {
2528
private final String name;
2629
private final McpSession session;
30+
private final SseSink sseSink;
2731
private Level level;
2832

2933
McpLogger(McpSession session) {
34+
Objects.requireNonNull(session, "session is null");
3035
this.session = session;
36+
this.sseSink = null;
37+
this.level = Level.INFO;
38+
this.name = "helidon-logger";
39+
}
40+
41+
McpLogger(SseSink sseSink) {
42+
Objects.requireNonNull(sseSink, "sseSink is null");
43+
this.session = null;
44+
this.sseSink = sseSink;
3145
this.level = Level.INFO;
3246
this.name = "helidon-logger";
3347
}
@@ -42,7 +56,14 @@ public void log(Level level, String message) {
4256
Objects.requireNonNull(level, "level must not be null");
4357
Objects.requireNonNull(message, "message must not be null");
4458
if (level.ordinal() >= this.level.ordinal()) {
45-
session.send(McpJsonRpc.createLoggingNotification(level, name, message));
59+
if (sseSink != null) {
60+
sseSink.emit(SseEvent.builder()
61+
.name("message")
62+
.data(McpJsonRpc.createLoggingNotification(level, name, message))
63+
.build());
64+
} else if (session != null) {
65+
session.send(McpJsonRpc.createLoggingNotification(level, name, message));
66+
}
4667
}
4768
}
4869

server/src/main/java/io/helidon/extensions/mcp/server/McpProgress.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,34 @@
1616

1717
package io.helidon.extensions.mcp.server;
1818

19+
import java.util.Objects;
20+
21+
import io.helidon.http.sse.SseEvent;
22+
import io.helidon.webserver.sse.SseSink;
23+
1924
/**
2025
* Progress notification to the client.
2126
*/
2227
public final class McpProgress {
2328
private final McpSession session;
29+
private final SseSink sseSink;
30+
2431
private int total;
2532
private int tokenInt;
2633
private String token;
2734
private boolean isSending;
2835

2936
McpProgress(McpSession session) {
37+
Objects.requireNonNull(session, "session is null");
3038
this.session = session;
39+
this.sseSink = null;
40+
this.token = "";
41+
}
42+
43+
McpProgress(SseSink sseSink) {
44+
Objects.requireNonNull(sseSink, "sseSink is null");
45+
this.session = null;
46+
this.sseSink = sseSink;
3147
this.token = "";
3248
}
3349

@@ -50,7 +66,14 @@ public void send(int progress) {
5066
return;
5167
}
5268
if (isSending) {
53-
session.send(McpJsonRpc.toJson(this, progress));
69+
if (sseSink != null) {
70+
sseSink.emit(SseEvent.builder()
71+
.name("message")
72+
.data(McpJsonRpc.toJson(this, progress))
73+
.build());
74+
} else if (session != null) {
75+
session.send(McpJsonRpc.toJson(this, progress));
76+
}
5477
}
5578
if (progress >= total) {
5679
isSending = false;

0 commit comments

Comments
 (0)