1818import modelengine .fel .tool .mcp .client .McpClient ;
1919import modelengine .fel .tool .mcp .entity .Tool ;
2020import modelengine .fitframework .log .Logger ;
21+ import modelengine .fitframework .util .StringUtils ;
22+ import modelengine .fitframework .util .UuidUtils ;
2123
2224import java .io .IOException ;
2325import java .time .Duration ;
2426import java .util .HashMap ;
2527import java .util .List ;
2628import java .util .Map ;
27- import java .util .UUID ;
28- import java .util .function .Consumer ;
29- import java .util .function .Function ;
3029import java .util .stream .Collectors ;
3130
3231/**
@@ -40,6 +39,8 @@ public class DefaultMcpStreamableClient implements McpClient {
4039
4140 private final String clientId ;
4241 private final McpSyncClient mcpSyncClient ;
42+ private final DefaultMcpClientLogHandler logHandler ;
43+
4344 private volatile boolean initialized = false ;
4445 private volatile boolean closed = false ;
4546
@@ -49,44 +50,30 @@ public class DefaultMcpStreamableClient implements McpClient {
4950 * @param baseUri The base URI of the MCP server.
5051 * @param sseEndpoint The endpoint for the Server-Sent Events (SSE) connection.
5152 * @param requestTimeoutSeconds The timeout duration of requests. Units: seconds.
52- * @param loggingConsumer The consumer to handle logging messages from the MCP server.
53- * @param elicitationHandler The function to handle elicitation requests from the MCP server.
5453 */
55- public DefaultMcpStreamableClient (String baseUri , String sseEndpoint , int requestTimeoutSeconds ,
56- Consumer <McpSchema .LoggingMessageNotification > loggingConsumer ,
57- Function <McpSchema .ElicitRequest , McpSchema .ElicitResult > elicitationHandler ) {
58- this .clientId = UUID .randomUUID ().toString ();
54+ public DefaultMcpStreamableClient (String baseUri , String sseEndpoint , int requestTimeoutSeconds ) {
55+ this .clientId = UuidUtils .randomUuidString ();
5956 notBlank (baseUri , "The MCP server base URI cannot be blank." );
6057 notBlank (sseEndpoint , "The MCP server SSE endpoint cannot be blank." );
61- log .info ("Creating MCP client. [clientId={}, baseUri={}]" , clientId , baseUri );
58+ log .info ("Creating MCP client. [clientId={}, baseUri={}]" , this . clientId , baseUri );
6259 ObjectMapper mapper = new ObjectMapper ();
6360 HttpClientStreamableHttpTransport transport = HttpClientStreamableHttpTransport .builder (baseUri )
6461 .jsonMapper (new JacksonMcpJsonMapper (mapper ))
6562 .endpoint (sseEndpoint )
6663 .build ();
6764
68- if (elicitationHandler != null ) {
69- this .mcpSyncClient = io .modelcontextprotocol .client .McpClient .sync (transport )
70- .requestTimeout (Duration .ofSeconds (requestTimeoutSeconds ))
71- .capabilities (McpSchema .ClientCapabilities .builder ().elicitation ().build ())
72- .loggingConsumer (loggingConsumer )
73- .elicitation (elicitationHandler )
74- .jsonSchemaValidator (new DefaultJsonSchemaValidator (mapper ))
75- .build ();
76- } else {
77- this .mcpSyncClient = io .modelcontextprotocol .client .McpClient .sync (transport )
78- .requestTimeout (Duration .ofSeconds (requestTimeoutSeconds ))
79- .capabilities (McpSchema .ClientCapabilities .builder ().build ())
80- .loggingConsumer (loggingConsumer )
81- .jsonSchemaValidator (new DefaultJsonSchemaValidator (mapper ))
82- .build ();
83- }
84-
65+ this .logHandler = new DefaultMcpClientLogHandler (this .clientId );
66+ this .mcpSyncClient = io .modelcontextprotocol .client .McpClient .sync (transport )
67+ .requestTimeout (Duration .ofSeconds (requestTimeoutSeconds ))
68+ .capabilities (McpSchema .ClientCapabilities .builder ().build ())
69+ .loggingConsumer (this .logHandler ::handleLoggingMessage )
70+ .jsonSchemaValidator (new DefaultJsonSchemaValidator (mapper ))
71+ .build ();
8572 }
8673
8774 @ Override
8875 public String getClientId () {
89- return clientId ;
76+ return this . clientId ;
9077 }
9178
9279 /**
@@ -97,9 +84,9 @@ public String getClientId() {
9784 @ Override
9885 public void initialize () {
9986 ensureNotClosed ();
100- mcpSyncClient .initialize ();
87+ this . mcpSyncClient .initialize ();
10188 this .initialized = true ;
102- log .info ("MCP client initialized successfully. [clientId={}]" , clientId );
89+ log .info ("MCP client initialized successfully. [clientId={}]" , this . clientId );
10390 }
10491
10592 /**
@@ -115,20 +102,21 @@ public List<Tool> getTools() {
115102 try {
116103 McpSchema .ListToolsResult result = this .mcpSyncClient .listTools ();
117104 if (result == null || result .tools () == null ) {
118- log .warn ("Failed to get tools list: result is null. [clientId={}]" , clientId );
105+ log .warn ("Failed to get tools list: result is null. [clientId={}]" , this . clientId );
119106 throw new IllegalStateException ("Failed to get tools list from MCP server: result is null." );
120107 }
121108
122109 List <Tool > tools = result .tools ().stream ().map (this ::convertToFelTool ).collect (Collectors .toList ());
123110
124- log .info ("Successfully retrieved tools list. [clientId={}, count={}]" , clientId , tools .size ());
111+ log .info ("Successfully retrieved tools list. [clientId={}, count={}]" , this . clientId , tools .size ());
125112 tools .forEach (tool -> log .debug ("Tool information. [name={}, description={}]" ,
126113 tool .getName (),
127114 tool .getDescription ()));
128115 return tools ;
129116 } catch (Exception e ) {
130- log .error ("Failed to get tools list. [clientId={}, error={}]" , clientId , e .getMessage ());
131- throw new IllegalStateException ("Failed to get tools from MCP server. [error=" + e .getMessage () + "]" , e );
117+ log .error ("Failed to get tools list. [clientId={}, error={}]" , this .clientId , e .getMessage ());
118+ throw new IllegalStateException (StringUtils .format ("Failed to get tools from MCP server. [error={0}]" ,
119+ e .getMessage ()), e );
132120 }
133121 }
134122
@@ -147,19 +135,21 @@ public List<Tool> getTools() {
147135 public Object callTool (String name , Map <String , Object > arguments ) {
148136 ensureReady ();
149137 try {
150- log .info ("Calling tool. [clientId={}, name={}, arguments={}]" , clientId , name , arguments );
138+ log .info ("Calling tool. [clientId={}, name={}, arguments={}]" , this . clientId , name , arguments );
151139 McpSchema .CallToolResult result =
152140 this .mcpSyncClient .callTool (new McpSchema .CallToolRequest (name , arguments ));
153141
154142 if (result == null ) {
155- log .error ("Failed to call tool: result is null. [clientId={}, name={}]" , clientId , name );
156- throw new IllegalStateException ("Failed to call tool: result is null. [name=" + name + "]" );
143+ log .error ("Failed to call tool: result is null. [clientId={}, name={}]" , this .clientId , name );
144+ throw new IllegalStateException (StringUtils .format ("Failed to call tool: result is null. [name={0}]" ,
145+ name ));
157146 }
158147 return processToolResult (result , name );
159148 } catch (Exception e ) {
160- log .error ("Failed to call tool. [clientId={}, name={}, error={}]" , clientId , name , e .getMessage ());
161- throw new IllegalStateException ("Failed to call tool. [name=" + name + ", error=" + e .getMessage () + "]" ,
162- e );
149+ log .error ("Failed to call tool. [clientId={}, name={}, error={}]" , this .clientId , name , e .getMessage ());
150+ throw new IllegalStateException (StringUtils .format ("Failed to call tool. [name={0}, error={1}]" ,
151+ name ,
152+ e .getMessage ()), e );
163153 }
164154 }
165155
@@ -177,26 +167,30 @@ public Object callTool(String name, Map<String, Object> arguments) {
177167 private Object processToolResult (McpSchema .CallToolResult result , String name ) {
178168 if (result .isError () != null && result .isError ()) {
179169 String errorDetails = extractErrorDetails (result .content ());
180- log .error ("Tool returned an error. [clientId={}, name={}, details={}]" , clientId , name , errorDetails );
181- throw new IllegalStateException (
182- "Tool returned an error. [name=" + name + ", details=" + errorDetails + "]" );
170+ log .error ("Tool returned an error. [clientId={}, name={}, details={}]" , this .clientId , name , errorDetails );
171+ throw new IllegalStateException (StringUtils .format ("Tool returned an error. [name={0}, details={1}]" ,
172+ name ,
173+ errorDetails ));
183174 }
184175
185176 if (result .content () == null || result .content ().isEmpty ()) {
186- log .warn ("Tool returned empty content. [clientId={}, name={}]" , clientId , name );
177+ log .warn ("Tool returned empty content. [clientId={}, name={}]" , this . clientId , name );
187178 return null ;
188179 }
189180
190181 Object content = result .content ().get (0 );
191182 if (content instanceof McpSchema .TextContent textContent ) {
192- log .info ("Successfully called tool. [clientId={}, name={}, result={}]" , clientId , name , textContent .text ());
183+ log .info ("Successfully called tool. [clientId={}, name={}, result={}]" ,
184+ this .clientId ,
185+ name ,
186+ textContent .text ());
193187 return textContent .text ();
194188 } else if (content instanceof McpSchema .ImageContent imageContent ) {
195- log .info ("Successfully called tool: image content. [clientId={}, name={}]" , clientId , name );
189+ log .info ("Successfully called tool: image content. [clientId={}, name={}]" , this . clientId , name );
196190 return imageContent ;
197191 } else {
198192 log .info ("Successfully called tool. [clientId={}, name={}, contentType={}]" ,
199- clientId ,
193+ this . clientId ,
200194 name ,
201195 content .getClass ().getSimpleName ());
202196 return content ;
@@ -213,7 +207,7 @@ public void close() throws IOException {
213207 ensureNotClosed ();
214208 this .closed = true ;
215209 this .mcpSyncClient .closeGracefully ();
216- log .info ("MCP client closed. [clientId={}]" , clientId );
210+ log .info ("MCP client closed. [clientId={}]" , this . clientId );
217211 }
218212
219213 /**
@@ -251,7 +245,8 @@ private Tool convertToFelTool(McpSchema.Tool mcpTool) {
251245 */
252246 private void ensureNotClosed () {
253247 if (this .closed ) {
254- throw new IllegalStateException ("The MCP client is already closed. [clientId=" + clientId + "]" );
248+ throw new IllegalStateException (StringUtils .format ("The MCP client is already closed. [clientId={0}]" ,
249+ this .clientId ));
255250 }
256251 }
257252
@@ -263,8 +258,8 @@ private void ensureNotClosed() {
263258 private void ensureReady () {
264259 ensureNotClosed ();
265260 if (!this .initialized ) {
266- throw new IllegalStateException (
267- "MCP client is not initialized. [ clientId=" + clientId + "]" );
261+ throw new IllegalStateException (StringUtils . format ( "MCP client is not initialized. [clientId={0}]" ,
262+ this . clientId ) );
268263 }
269264 }
270265
0 commit comments