Skip to content

Commit 80f7fd4

Browse files
committed
Don't make it a hard dependency that initialize needs to be sent
1 parent b392514 commit 80f7fd4

3 files changed

Lines changed: 53 additions & 45 deletions

File tree

mcp/mcp-server/src/main/java/software/amazon/smithy/java/mcp/server/HttpMcpProxy.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,7 @@ public CompletableFuture<JsonRpcResponse> rpc(JsonRpcRequest request) {
104104
byte[] body = JSON_CODEC.serializeToString(request).getBytes(StandardCharsets.UTF_8);
105105
LOG.trace("Sending HTTP request to {}", endpoint);
106106

107-
String protocolVersionHeader = protocolVersion != null
108-
? protocolVersion
109-
: ProtocolVersion.defaultVersion().identifier();
107+
String protocolVersionHeader = getProtocolVersion().identifier();
110108

111109
HttpRequest httpRequest = HttpRequest.builder()
112110
.uri(endpoint)

mcp/mcp-server/src/main/java/software/amazon/smithy/java/mcp/server/McpServerProxy.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.Objects;
1010
import java.util.concurrent.CompletableFuture;
1111
import java.util.concurrent.atomic.AtomicInteger;
12+
import java.util.concurrent.atomic.AtomicReference;
1213
import java.util.function.Consumer;
1314
import software.amazon.smithy.java.core.schema.SerializableStruct;
1415
import software.amazon.smithy.java.core.schema.ShapeBuilder;
@@ -22,8 +23,9 @@ public abstract class McpServerProxy {
2223

2324
private static final AtomicInteger ID_GENERATOR = new AtomicInteger(0);
2425

25-
protected Consumer<JsonRpcResponse> notificationConsumer;
26-
protected volatile String protocolVersion;
26+
private final AtomicReference<Consumer<JsonRpcResponse>> notificationConsumer = new AtomicReference<>();
27+
private final AtomicReference<ProtocolVersion> protocolVersion =
28+
new AtomicReference<>(ProtocolVersion.defaultVersion());
2729

2830
public List<ToolInfo> listTools() {
2931
JsonRpcRequest request = JsonRpcRequest.builder()
@@ -47,15 +49,19 @@ public List<ToolInfo> listTools() {
4749
public void initialize(
4850
Consumer<JsonRpcResponse> notificationConsumer,
4951
JsonRpcRequest initializeRequest,
50-
String protocolVersion
52+
ProtocolVersion protocolVersion
5153
) {
5254

5355
var result = Objects.requireNonNull(rpc(initializeRequest).join());
5456
if (result.getError() != null) {
5557
throw new RuntimeException("Error during initialization: " + result.getError().getMessage());
5658
}
57-
this.notificationConsumer = notificationConsumer;
58-
this.protocolVersion = protocolVersion;
59+
this.notificationConsumer.set(notificationConsumer);
60+
this.protocolVersion.set(protocolVersion);
61+
}
62+
63+
protected final ProtocolVersion getProtocolVersion() {
64+
return protocolVersion.get();
5965
}
6066

6167
abstract CompletableFuture<JsonRpcResponse> rpc(JsonRpcRequest request);
@@ -85,7 +91,10 @@ protected Document generateRequestId() {
8591
}
8692

8793
protected void notify(JsonRpcResponse response) {
88-
notificationConsumer.accept(response);
94+
var nc = notificationConsumer.get();
95+
if (nc != null) {
96+
nc.accept(response);
97+
}
8998
}
9099

91100
public abstract String name();

mcp/mcp-server/src/main/java/software/amazon/smithy/java/mcp/server/McpService.java

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public final class McpService {
7979
private final Map<String, Service> services;
8080
private final AtomicReference<JsonRpcRequest> initializeRequest = new AtomicReference<>();
8181
private final ToolFilter toolFilter;
82-
private volatile boolean proxiesInitialized = false;
82+
private final AtomicReference<Boolean> proxiesInitialized = new AtomicReference<>(false);
8383
private final McpMetricsObserver metricsObserver;
8484

8585
McpService(
@@ -118,13 +118,19 @@ public JsonRpcResponse handleRequest(
118118
) {
119119
try {
120120
validate(req);
121-
return switch (req.getMethod()) {
121+
var method = req.getMethod();
122+
return switch (method) {
122123
case "initialize" -> handleInitialize(req);
123-
case "prompts/list" -> handlePromptsList(req);
124-
case "prompts/get" -> handlePromptsGet(req);
125-
case "tools/list" -> handleToolsList(req, protocolVersion);
126-
case "tools/call" -> handleToolsCall(req, asyncResponseCallback, protocolVersion);
127-
default -> null; // Notifications or unknown methods
124+
default -> {
125+
initializeProxies(rpcResponse -> {});
126+
yield switch (method) {
127+
case "prompts/list" -> handlePromptsList(req);
128+
case "prompts/get" -> handlePromptsGet(req);
129+
case "tools/list" -> handleToolsList(req, protocolVersion);
130+
case "tools/call" -> handleToolsCall(req, asyncResponseCallback, protocolVersion);
131+
default -> null; // Notifications or unknown methods
132+
};
133+
}
128134
};
129135
} catch (Exception e) {
130136
return createErrorResponse(req, e);
@@ -166,15 +172,9 @@ private JsonRpcResponse handleInitialize(JsonRpcRequest req) {
166172
clientTitle);
167173
}
168174

169-
this.initializeRequest.set(req);
175+
this.initializeRequest.compareAndSet(null, req);
170176

171-
// Initialize proxies lazily after we have a real initialize request
172-
if (!proxiesInitialized) {
173-
initializeProxies(response -> {
174-
// Proxies are initialized, no additional response needed
175-
});
176-
proxiesInitialized = true;
177-
}
177+
initializeProxies(rpcResponse -> {});
178178

179179
var maybeVersion = req.getParams().getMember("protocolVersion");
180180
String pv = null;
@@ -305,31 +305,32 @@ public void startProxies() {
305305
* Initializes proxies with the actual initialize request.
306306
*/
307307
public void initializeProxies(Consumer<JsonRpcResponse> responseWriter) {
308-
JsonRpcRequest initRequest = initializeRequest.get();
309-
if (initRequest == null) {
310-
LOG.warn("Cannot initialize proxies: no initialize request received yet");
311-
return;
312-
}
313-
314-
String protocolVersion = null;
315-
var maybeVersion = initRequest.getParams().getMember("protocolVersion");
316-
if (maybeVersion != null) {
317-
var version = ProtocolVersion.version(maybeVersion.asString());
318-
if (!(version instanceof ProtocolVersion.UnknownVersion)) {
319-
protocolVersion = version.identifier();
308+
if (proxiesInitialized.compareAndSet(false, true)) {
309+
JsonRpcRequest initRequest = initializeRequest.get();
310+
var protocolVersion = ProtocolVersion.defaultVersion();
311+
if (initRequest != null) {
312+
var maybeVersion = initRequest.getParams().getMember("protocolVersion");
313+
if (maybeVersion != null) {
314+
var pv = ProtocolVersion.version(maybeVersion.asString());
315+
if (!(pv instanceof ProtocolVersion.UnknownVersion)) {
316+
protocolVersion = pv;
317+
}
318+
}
320319
}
321-
}
322320

323-
for (McpServerProxy proxy : proxies.values()) {
324-
try {
325-
proxy.initialize(responseWriter, initRequest, protocolVersion);
321+
for (McpServerProxy proxy : proxies.values()) {
322+
try {
323+
if (initRequest != null) {
324+
proxy.initialize(responseWriter, initRequest, protocolVersion);
325+
}
326326

327-
List<ToolInfo> proxyTools = proxy.listTools();
328-
for (var toolInfo : proxyTools) {
329-
tools.put(toolInfo.getName(), new Tool(toolInfo, proxy.name(), proxy));
327+
List<ToolInfo> proxyTools = proxy.listTools();
328+
for (var toolInfo : proxyTools) {
329+
tools.put(toolInfo.getName(), new Tool(toolInfo, proxy.name(), proxy));
330+
}
331+
} catch (Exception e) {
332+
LOG.error("Failed to initialize proxy: " + proxy.name(), e);
330333
}
331-
} catch (Exception e) {
332-
LOG.error("Failed to initialize proxy: " + proxy.name(), e);
333334
}
334335
}
335336
}

0 commit comments

Comments
 (0)