Skip to content

Commit 0b6ee27

Browse files
committed
[fit] add notification/tools/list_changed method in MCP server
1 parent 17a0e84 commit 0b6ee27

File tree

3 files changed

+43
-1
lines changed

3 files changed

+43
-1
lines changed

framework/fel/java/plugins/tool-mcp-server/src/main/java/modelengine/fel/tool/mcp/server/McpController.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,15 @@
4848
* @since 2025-05-13
4949
*/
5050
@Component
51-
public class McpController {
51+
public class McpController implements McpServer.ToolsChangedObserver {
5252
private static final Logger log = Logger.get(McpController.class);
5353
private static final String MESSAGE_PATH = "/mcp/message";
5454
private static final String EVENT_ENDPOINT = "endpoint";
5555
private static final String EVENT_MESSAGE = "message";
5656
private static final String METHOD_INITIALIZE = "initialize";
5757
private static final String METHOD_TOOLS_LIST = "tools/list";
5858
private static final String METHOD_TOOLS_CALL = "tools/call";
59+
private static final String METHOD_NOTIFICATION_TOOLS_CHANGED = "notifications/tools/list_changed";
5960
private static final String RESPONSE_OK = StringUtils.EMPTY;
6061

6162
private final Map<String, Emitter<TextEvent>> emitters = new ConcurrentHashMap<>();
@@ -79,6 +80,7 @@ public McpController(@Value("${base-url}") String baseUrl, @Fit(alias = "json")
7980
this.baseUrl = notBlank(baseUrl, "The base URL for MCP server cannot be blank.");
8081
this.serializer = notNull(serializer, "The json serializer cannot be null.");
8182
notNull(mcpServer, "The MCP server cannot be null.");
83+
mcpServer.registerToolsChangedObserver(this);
8284

8385
this.methodHandlers.put(METHOD_INITIALIZE, new InitializeHandler(mcpServer));
8486
this.methodHandlers.put(METHOD_TOOLS_LIST, new ToolListHandler(mcpServer));
@@ -170,4 +172,16 @@ public Object receiveMcpMessage(@RequestQuery(name = "sessionId") String session
170172
log.info("Send MCP message. [response={}]", serialized);
171173
return RESPONSE_OK;
172174
}
175+
176+
@Override
177+
public void onToolsChanged() {
178+
JsonRpcEntity notification = new JsonRpcEntity();
179+
notification.setMethod(METHOD_NOTIFICATION_TOOLS_CHANGED);
180+
String serialized = this.serializer.serialize(notification);
181+
this.emitters.forEach((sessionId, emitter) -> {
182+
TextEvent textEvent = TextEvent.custom().id(sessionId).event(EVENT_MESSAGE).data(serialized).build();
183+
emitter.emit(textEvent);
184+
log.info("Send MCP notification: tools changed. [sessionId={}]", sessionId);
185+
});
186+
}
173187
}

framework/fel/java/plugins/tool-mcp-server/src/main/java/modelengine/fel/tool/mcp/server/McpServer.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,21 @@ public interface McpServer {
4040
* @return The tool result as a {@link Object}.
4141
*/
4242
Object callTool(String name, Map<String, Object> arguments);
43+
44+
/**
45+
* Registers MCP Server Tools Changed Observer.
46+
*
47+
* @param observer The MCP Server Tools Changed Observer as a {@link ToolsChangedObserver}.
48+
*/
49+
void registerToolsChangedObserver(ToolsChangedObserver observer);
50+
51+
/**
52+
* Represents the MCP Server Tools Changed Observer.
53+
*/
54+
interface ToolsChangedObserver {
55+
/**
56+
* Called when MCP Server Tools changed.
57+
*/
58+
void onToolsChanged();
59+
}
4360
}

framework/fel/java/plugins/tool-mcp-server/src/main/java/modelengine/fel/tool/mcp/server/support/DefaultMcpServer.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import modelengine.fitframework.util.MapUtils;
1919
import modelengine.fitframework.util.StringUtils;
2020

21+
import java.util.ArrayList;
2122
import java.util.List;
2223
import java.util.Map;
2324
import java.util.concurrent.ConcurrentHashMap;
@@ -34,6 +35,7 @@ public class DefaultMcpServer implements McpServer, ToolChangedObserver {
3435

3536
private final ToolExecuteService toolExecuteService;
3637
private final Map<String, ToolEntity> tools = new ConcurrentHashMap<>();
38+
private final List<ToolsChangedObserver> toolsChangedObservers = new ArrayList<>();
3739

3840
/**
3941
* Constructs a new instance of the DefaultMcpServer class.
@@ -72,6 +74,13 @@ public Object callTool(String name, Map<String, Object> arguments) {
7274
return result;
7375
}
7476

77+
@Override
78+
public void registerToolsChangedObserver(ToolsChangedObserver observer) {
79+
if (observer != null) {
80+
this.toolsChangedObservers.add(observer);
81+
}
82+
}
83+
7584
@Override
7685
public void onToolAdded(String name, String description, Map<String, Object> schema) {
7786
if (StringUtils.isBlank(name)) {
@@ -92,6 +101,7 @@ public void onToolAdded(String name, String description, Map<String, Object> sch
92101
tool.setInputSchema(schema);
93102
this.tools.put(name, tool);
94103
log.info("Tool added to MCP server. [toolName={}, description={}, schema={}]", name, description, schema);
104+
this.toolsChangedObservers.forEach(ToolsChangedObserver::onToolsChanged);
95105
}
96106

97107
@Override
@@ -102,5 +112,6 @@ public void onToolRemoved(String name) {
102112
}
103113
this.tools.remove(name);
104114
log.info("Tool removed from MCP server. [toolName={}]", name);
115+
this.toolsChangedObservers.forEach(ToolsChangedObserver::onToolsChanged);
105116
}
106117
}

0 commit comments

Comments
 (0)