Skip to content

Commit 3926b8e

Browse files
committed
Use an error handler to process a response received on the HTTP connection.
Signed-off-by: Santiago Pericas-Geertsen <[email protected]>
1 parent 65be14b commit 3926b8e

File tree

2 files changed

+46
-18
lines changed

2 files changed

+46
-18
lines changed

integrations/mcp/server/src/main/java/io/helidon/integrations/mcp/server/McpHttpFeature.java

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ public McpHttpFeature(McpHttpFeatureConfig config) {
102102
builder.method(McpJsonRPC.METHOD_NOTIFICATION_INITIALIZED, this::notificationInitRpc);
103103
builder.method(McpJsonRPC.METHOD_NOTIFICATION_CANCELED, this::notificationCancelRpc);
104104

105+
builder.error(this::handleErrorRequest);
106+
105107
jsonRpcHandlers = builder.build();
106108
}
107109

@@ -169,6 +171,30 @@ private McpSession findSession(JsonRpcRequest req) {
169171
}
170172
}
171173

174+
/**
175+
* If we receive what looks like a response on the error handler,
176+
* pass it to the session.
177+
*
178+
* @param req the HTTP request
179+
* @param object the invalid JSON-RPC request
180+
* @return whether error was handled or not
181+
*/
182+
private boolean handleErrorRequest(ServerRequest req, JsonObject object) {
183+
try {
184+
if (object.containsKey("result") || object.containsKey("error")) {
185+
String sessionId = req.query().get("sessionId");
186+
McpSession session = sessions.get(sessionId);
187+
if (session != null) {
188+
session.handleResponse(object);
189+
return true;
190+
}
191+
}
192+
} catch (Exception e) {
193+
// falls through
194+
}
195+
return false;
196+
}
197+
172198
private void notificationInitRpc(JsonRpcRequest req, JsonRpcResponse res) {
173199
McpSession session = findSession(req);
174200
if (session == null) {
@@ -193,7 +219,7 @@ private void pingRpc(JsonRpcRequest req, JsonRpcResponse res) {
193219
res.status(Status.NOT_FOUND_404).send();
194220
return;
195221
}
196-
session.enqueue(res.result(PING_PONG).asJsonObject());
222+
session.enqueue(res.result(PING_PONG));
197223
}
198224

199225
private void initializeRpc(JsonRpcRequest req, JsonRpcResponse res) {
@@ -228,7 +254,7 @@ private void initializeRpc(JsonRpcRequest req, JsonRpcResponse res) {
228254
.add("instructions", "")
229255
.build();
230256

231-
session.enqueue(res.result(result).asJsonObject());
257+
session.enqueue(res.result(result));
232258
}
233259

234260
private void toolsListRpc(JsonRpcRequest req, JsonRpcResponse res) {
@@ -246,7 +272,7 @@ private void toolsListRpc(JsonRpcRequest req, JsonRpcResponse res) {
246272
.add("tools", builder.build())
247273
.build();
248274

249-
session.enqueue(res.result(result).asJsonObject());
275+
session.enqueue(res.result(result));
250276
}
251277

252278
private void toolsCallRpc(JsonRpcRequest req, JsonRpcResponse res) {
@@ -270,7 +296,7 @@ private void toolsCallRpc(JsonRpcRequest req, JsonRpcResponse res) {
270296
.build())
271297
.orElse(null);
272298

273-
session.enqueue(res.result(result).asJsonObject());
299+
session.enqueue(res.result(result));
274300
}
275301

276302
private void resourcesListRpc(JsonRpcRequest req, JsonRpcResponse res) {
@@ -284,11 +310,11 @@ private void resourcesListRpc(JsonRpcRequest req, JsonRpcResponse res) {
284310
this.config.resources().stream()
285311
.map(Jsonable::json)
286312
.forEach(builder::add);
287-
JsonObject result = Json.createObjectBuilder()
313+
JsonObject result = Json.createObjectBuilder()
288314
.add("resources", builder.build())
289315
.build();
290316

291-
session.enqueue(res.result(result).asJsonObject());
317+
session.enqueue(res.result(result));
292318
}
293319

294320
private void resourcesReadRpc(JsonRpcRequest req, JsonRpcResponse res) {
@@ -310,7 +336,7 @@ private void resourcesReadRpc(JsonRpcRequest req, JsonRpcResponse res) {
310336
.build())
311337
.orElse(null);
312338

313-
session.enqueue(res.result(result).asJsonObject());
339+
session.enqueue(res.result(result));
314340
}
315341

316342
private void resourceSubscribeRpc(JsonRpcRequest req, JsonRpcResponse res) {
@@ -337,7 +363,7 @@ private void resourceTemplateListRpc(JsonRpcRequest req, JsonRpcResponse res) {
337363
.add("resourceTemplates", Json.createArrayBuilder(templates))
338364
.build();
339365

340-
session.enqueue(res.result(result).asJsonObject());
366+
session.enqueue(res.result(result));
341367
}
342368

343369
private void promptsListRpc(JsonRpcRequest req, JsonRpcResponse res) {
@@ -351,11 +377,11 @@ private void promptsListRpc(JsonRpcRequest req, JsonRpcResponse res) {
351377
this.config.prompts().stream()
352378
.map(Jsonable::json)
353379
.forEach(builder::add);
354-
JsonObject result = Json.createObjectBuilder()
380+
JsonObject result = Json.createObjectBuilder()
355381
.add("prompts", builder.build())
356382
.build();
357383

358-
session.enqueue(res.result(result).asJsonObject());
384+
session.enqueue(res.result(result));
359385
}
360386

361387
private void promptsGetRpc(JsonRpcRequest req, JsonRpcResponse res) {
@@ -370,14 +396,14 @@ private void promptsGetRpc(JsonRpcRequest req, JsonRpcResponse res) {
370396
.filter(p -> Objects.equals(p.name(), params.getString("name")))
371397
.findFirst();
372398
McpParameters parameters = new McpParameters(params.getJsonObject("arguments"), "arguments");
373-
JsonObject result = prompt.map(value -> Json.createObjectBuilder()
399+
JsonObject result = prompt.map(value -> Json.createObjectBuilder()
374400
.add("description", value.description())
375401
.add("messages", Json.createArrayBuilder()
376402
.add(value.prompt(parameters).json()))
377403
.build())
378404
.orElse(null);
379405

380-
session.enqueue(res.result(result).asJsonObject());
406+
session.enqueue(res.result(result));
381407
}
382408

383409
private void loggingRpc(JsonRpcRequest req, JsonRpcResponse res) {
@@ -395,12 +421,12 @@ private void completionRpc(JsonRpcRequest req, JsonRpcResponse res) {
395421
JsonObject reference = params.getJsonObject("ref");
396422
Optional<String> search = parseCompletionName(reference);
397423
if (search.isEmpty()) {
398-
JsonObject result = Json.createObjectBuilder()
424+
JsonObject result = Json.createObjectBuilder()
399425
.add("error", Json.createObjectBuilder()
400426
.add("code", McpJsonRPC.INVALID_REQUEST)
401427
.add("message", "Completion reference not found"))
402428
.build();
403-
session.enqueue(res.result(result).asJsonObject());
429+
session.enqueue(res.result(result));
404430
res.send();
405431
return;
406432
}
@@ -419,7 +445,7 @@ private void completionRpc(JsonRpcRequest req, JsonRpcResponse res) {
419445
.build())
420446
.orElse(null);
421447

422-
session.enqueue(res.result(result).asJsonObject());
448+
session.enqueue(res.result(result));
423449
}
424450

425451
private Optional<String> parseCompletionName(JsonObject completion) {

integrations/mcp/server/src/main/java/io/helidon/integrations/mcp/server/McpSession.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import java.util.function.Consumer;
2626

2727
import io.helidon.common.UncheckedException;
28+
import io.helidon.http.Status;
29+
import io.helidon.webserver.jsonrpc.JsonRpcResponse;
2830

2931
import jakarta.json.Json;
3032
import jakarta.json.JsonObject;
@@ -83,7 +85,7 @@ void disconnect() {
8385
}
8486

8587
// TODO: Response to server notifications?
86-
private void handleResponse(JsonObject response) {
88+
void handleResponse(JsonObject response) {
8789
pendingResponses.remove(response.getString("id"));
8890
}
8991

@@ -109,9 +111,9 @@ public AtomicReference<JsonObject> clientCapabilities() {
109111
return clientCapabilities;
110112
}
111113

112-
void enqueue(JsonObject message) {
114+
void enqueue(JsonRpcResponse res) {
113115
try {
114-
queue.put(message);
116+
queue.put(res.status(Status.ACCEPTED_202).asJsonObject());
115117
} catch (InterruptedException e) {
116118
throw new UncheckedException(e);
117119
}

0 commit comments

Comments
 (0)