Skip to content

Commit faaec9b

Browse files
committed
[fel] Add MCP error handle
1 parent 39b7c0c commit faaec9b

File tree

2 files changed

+107
-13
lines changed

2 files changed

+107
-13
lines changed

framework/fel/java/plugins/tool-mcp-client/src/main/java/modelengine/fel/tool/mcp/client/support/DefaultMcpClient.java

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public class DefaultMcpClient implements McpClient {
7777
private final Object toolsLock = LockUtils.newSynchronizedLock();
7878
private final Map<Long, Consumer<JsonRpc.Response<Long>>> responseConsumers = new ConcurrentHashMap<>();
7979
private final Map<Long, Boolean> pendingRequests = new ConcurrentHashMap<>();
80-
private final Map<Long, Object> pendingResults = new ConcurrentHashMap<>();
80+
private final Map<Long, Result> pendingResults = new ConcurrentHashMap<>();
8181

8282
private volatile Subscription subscription;
8383
private volatile ThreadPoolScheduler pingScheduler;
@@ -197,10 +197,6 @@ private void initializedMcpServer(JsonRpc.Response<Long> response) {
197197
response.error());
198198
throw new IllegalStateException(response.error().toString());
199199
}
200-
synchronized (this.initializedLock) {
201-
this.initialized = true;
202-
this.initializedLock.notifyAll();
203-
}
204200
this.recordServerSchema(response);
205201
HttpClassicClientRequest request =
206202
this.client.createRequest(HttpRequestMethod.POST, this.baseUri + this.messageEndpoint);
@@ -225,6 +221,10 @@ private void initializedMcpServer(JsonRpc.Response<Long> response) {
225221
} catch (IOException e) {
226222
throw new IllegalStateException(e);
227223
}
224+
synchronized (this.initializedLock) {
225+
this.initialized = true;
226+
this.initializedLock.notifyAll();
227+
}
228228
this.pingScheduler = ThreadPoolScheduler.custom()
229229
.threadPoolName("mcp-client-ping-" + this.name)
230230
.awaitTermination(3, TimeUnit.SECONDS)
@@ -262,16 +262,24 @@ public List<Tool> getTools() {
262262
while (this.pendingRequests.get(requestId)) {
263263
ThreadUtils.sleep(100);
264264
}
265-
synchronized (this.toolsLock) {
266-
return this.tools;
265+
Result result = this.pendingResults.remove(requestId);
266+
this.pendingRequests.remove(requestId);
267+
if (result.isSuccess()) {
268+
synchronized (this.toolsLock) {
269+
return this.tools;
270+
}
271+
} else {
272+
throw new IllegalStateException(result.getError());
267273
}
274+
268275
}
269276

270277
private void getTools0(JsonRpc.Response<Long> response) {
271278
if (response.error() != null) {
272-
log.error("Failed to get tools list from MCP server. [sessionId={}, response={}]",
279+
String error = StringUtils.format("Failed to get tools list from MCP server. [sessionId={0}, response={1}]",
273280
this.sessionId,
274281
response);
282+
this.pendingResults.put(response.id(), Result.error(error));
275283
this.pendingRequests.put(response.id(), false);
276284
return;
277285
}
@@ -303,32 +311,46 @@ public Object callTool(String name, Map<String, Object> arguments) {
303311
while (this.pendingRequests.get(requestId)) {
304312
ThreadUtils.sleep(100);
305313
}
306-
return this.pendingResults.get(requestId);
314+
Result result = this.pendingResults.remove(requestId);
315+
this.pendingRequests.remove(requestId);
316+
if (result.isSuccess()) {
317+
return result.getContent();
318+
} else {
319+
throw new IllegalStateException(result.getError());
320+
}
307321
}
308322

309323
private void callTools0(JsonRpc.Response<Long> response) {
310324
if (response.error() != null) {
311-
log.error("Failed to call tool from MCP server. [sessionId={}, response={}]", this.sessionId, response);
325+
String error = StringUtils.format("Failed to call tool from MCP server. [sessionId={0}, response={1}]",
326+
this.sessionId,
327+
response);
328+
this.pendingResults.put(response.id(), Result.error(error));
312329
this.pendingRequests.put(response.id(), false);
313330
return;
314331
}
315332
Map<String, Object> result = cast(response.result());
316333
boolean isError = cast(result.get("isError"));
317334
if (isError) {
318-
log.error("Failed to call tool from MCP server. [sessionId={}, result={}]", this.sessionId, result);
335+
String error = StringUtils.format("Failed to call tool from MCP server. [sessionId={0}, result={1}]",
336+
this.sessionId,
337+
result);
338+
this.pendingResults.put(response.id(), Result.error(error));
319339
this.pendingRequests.put(response.id(), false);
320340
return;
321341
}
322342
List<Map<String, Object>> rawContents = cast(result.get("content"));
323343
if (CollectionUtils.isEmpty(rawContents)) {
324-
log.error("Failed to call tool from MCP server: no result returned. [sessionId={}, result={}]",
344+
String error = StringUtils.format(
345+
"Failed to call tool from MCP server: no result returned. [sessionId={0}, result={1}]",
325346
this.sessionId,
326347
result);
348+
this.pendingResults.put(response.id(), Result.error(error));
327349
this.pendingRequests.put(response.id(), false);
328350
return;
329351
}
330352
Map<String, Object> rawContent = rawContents.get(0);
331-
this.pendingResults.put(response.id(), rawContent.get("text"));
353+
this.pendingResults.put(response.id(), Result.success(rawContent.get("text")));
332354
this.pendingRequests.put(response.id(), false);
333355
}
334356

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
3+
* This file is a part of the ModelEngine Project.
4+
* Licensed under the MIT License. See License.txt in the project root for license information.
5+
*--------------------------------------------------------------------------------------------*/
6+
7+
package modelengine.fel.tool.mcp.client.support;
8+
9+
/**
10+
* 表示调用 MCP 的结果。
11+
*
12+
* @author 季聿阶
13+
* @since 2025-08-04
14+
*/
15+
public class Result {
16+
private final boolean success;
17+
private final Object content;
18+
private final String error;
19+
20+
private Result(boolean success, Object content, String error) {
21+
this.success = success;
22+
this.content = content;
23+
this.error = error;
24+
}
25+
26+
/**
27+
* 创建一个成功的结果。
28+
*
29+
* @param content 表示成功结果的内容的 {@link Object}。
30+
* @return 表示成功结果的对象的 {@link Result}。
31+
*/
32+
public static Result success(Object content) {
33+
return new Result(true, content, null);
34+
}
35+
36+
/**
37+
* 创建一个失败的结果。
38+
*
39+
* @param error 表示错误结果的信息的 {@link String}。
40+
* @return 表示错误结果的对象的 {@link Result}。
41+
*/
42+
public static Result error(String error) {
43+
return new Result(false, null, error);
44+
}
45+
46+
/**
47+
* 获取结果是否成功。
48+
*
49+
* @return 如果结果成功,则返回 {@code true};否则返回 {@code false}。
50+
*/
51+
public boolean isSuccess() {
52+
return this.success;
53+
}
54+
55+
/**
56+
* 获取结果内容。
57+
*
58+
* @return 表示结果内容的 {@link Object}。
59+
*/
60+
public Object getContent() {
61+
return this.content;
62+
}
63+
64+
/**
65+
* 获取结果错误信息。
66+
*
67+
* @return 表示错误信息的 {@link String}。
68+
*/
69+
public String getError() {
70+
return this.error;
71+
}
72+
}

0 commit comments

Comments
 (0)