Skip to content

Commit e8a2664

Browse files
committed
feat(mcp): Add MCP logging support and refactor code
- Add logging consumer to MCP client specification - Implement logging notifications for sampling start/finish in WeatherService - Refactor WeatherService to use McpToolUtils and StringBuilder Signed-off-by: Christian Tzolov <[email protected]>
1 parent 7e04dc2 commit e8a2664

File tree

3 files changed

+46
-28
lines changed
  • model-context-protocol
    • sampling
    • web-search/brave-starter/src/main/java/org/springframework/ai/mcp/samples/brave

3 files changed

+46
-28
lines changed

model-context-protocol/sampling/mcp-sampling-client/src/main/java/org/springframework/ai/mcp/samples/client/McpClientApplication.java

+5
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ public CommandLineRunner predefinedQuestions(OpenAiChatModel openAiChatModel,
6868
McpSyncClientCustomizer samplingCustomizer(Map<String, ChatClient> chatClients) {
6969

7070
return (name, mcpClientSpec) -> {
71+
72+
mcpClientSpec = mcpClientSpec.loggingConsumer(logingMessage -> {
73+
System.out.println("MCP LOGGING: [" + logingMessage.level() + "] " + logingMessage.data());
74+
});
75+
7176
mcpClientSpec.sampling(llmRequest -> {
7277
var userPrompt = ((McpSchema.TextContent) llmRequest.messages().get(0).content()).text();
7378
String modelHint = llmRequest.modelPreferences().hints().get(0).name();

model-context-protocol/sampling/mcp-weather-webmvc-server/src/main/java/org/springframework/ai/mcp/sample/server/WeatherService.java

+41-27
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@
2121
import io.modelcontextprotocol.server.McpSyncServerExchange;
2222
import io.modelcontextprotocol.spec.McpSchema;
2323
import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult;
24+
import io.modelcontextprotocol.spec.McpSchema.LoggingLevel;
25+
import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification;
2426
import io.modelcontextprotocol.spec.McpSchema.ModelPreferences;
2527
import org.slf4j.Logger;
2628

2729
import org.springframework.ai.chat.model.ToolContext;
30+
import org.springframework.ai.mcp.McpToolUtils;
2831
import org.springframework.ai.model.ModelOptionsUtils;
2932
import org.springframework.ai.tool.annotation.Tool;
3033
import org.springframework.ai.tool.annotation.ToolParam;
@@ -72,43 +75,54 @@ public String getTemperature(@ToolParam(description = "The location latitude") d
7275

7376
public String callMcpSampling(ToolContext toolContext, WeatherResponse weatherResponse) {
7477

75-
String openAiWeatherPoem = "<no OpenAI poem>";
76-
String anthropicWeatherPoem = "<no Anthropic poem>";
78+
StringBuilder openAiWeatherPoem = new StringBuilder();
79+
StringBuilder anthropicWeatherPoem = new StringBuilder();
7780

78-
if (toolContext != null && toolContext.getContext().containsKey("exchange")) {
81+
McpToolUtils.getMcpExchange(toolContext)
82+
.ifPresent(exchange -> {
7983

80-
// Spring AI MCP Auto-configuration injects the McpSyncServerExchange into the ToolContext under the key "exchange"
81-
McpSyncServerExchange exchange = (McpSyncServerExchange) toolContext.getContext().get("exchange");
82-
if (exchange.getClientCapabilities().sampling() != null) {
83-
var messageRequestBuilder = McpSchema.CreateMessageRequest.builder()
84-
.systemPrompt("You are a poet!")
85-
.messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
86-
new McpSchema.TextContent(
87-
"Please write a poem about thius weather forecast (temperature is in Celsious). Use markdown format :\n "
88-
+ ModelOptionsUtils.toJsonStringPrettyPrinter(weatherResponse)))));
84+
exchange.loggingNotification(LoggingMessageNotification.builder()
85+
.level(LoggingLevel.INFO)
86+
.data("Start sampling")
87+
.build());
8988

90-
var opeAiLlmMessageRequest = messageRequestBuilder
91-
.modelPreferences(ModelPreferences.builder().addHint("openai").build())
92-
.build();
93-
CreateMessageResult openAiLlmResponse = exchange.createMessage(opeAiLlmMessageRequest);
89+
if (exchange.getClientCapabilities().sampling() != null) {
90+
var messageRequestBuilder = McpSchema.CreateMessageRequest.builder()
91+
.systemPrompt("You are a poet!")
92+
.messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
93+
new McpSchema.TextContent(
94+
"Please write a poem about thius weather forecast (temperature is in Celsious). Use markdown format :\n "
95+
+ ModelOptionsUtils
96+
.toJsonStringPrettyPrinter(weatherResponse)))));
9497

95-
openAiWeatherPoem = ((McpSchema.TextContent) openAiLlmResponse.content()).text();
98+
var opeAiLlmMessageRequest = messageRequestBuilder
99+
.modelPreferences(ModelPreferences.builder().addHint("openai").build())
100+
.build();
101+
CreateMessageResult openAiLlmResponse = exchange.createMessage(opeAiLlmMessageRequest);
96102

97-
var anthropicLlmMessageRequest = messageRequestBuilder
98-
.modelPreferences(ModelPreferences.builder().addHint("anthropic").build())
99-
.build();
100-
CreateMessageResult anthropicAiLlmResponse = exchange.createMessage(anthropicLlmMessageRequest);
103+
openAiWeatherPoem.append(((McpSchema.TextContent) openAiLlmResponse.content()).text());
101104

102-
anthropicWeatherPoem = ((McpSchema.TextContent) anthropicAiLlmResponse.content()).text();
105+
var anthropicLlmMessageRequest = messageRequestBuilder
106+
.modelPreferences(ModelPreferences.builder().addHint("anthropic").build())
107+
.build();
108+
CreateMessageResult anthropicAiLlmResponse = exchange.createMessage(anthropicLlmMessageRequest);
103109

104-
}
105-
}
110+
anthropicWeatherPoem.append(((McpSchema.TextContent) anthropicAiLlmResponse.content()).text());
111+
112+
}
113+
114+
exchange.loggingNotification(LoggingMessageNotification.builder()
115+
.level(LoggingLevel.INFO)
116+
.data("Finish Sampling")
117+
.build());
118+
119+
});
106120

107-
String responseWithPoems = "OpenAI poem about the weather: " + openAiWeatherPoem + "\n\n" +
108-
"Anthropic poem about the weather: " + anthropicWeatherPoem + "\n"
121+
String responseWithPoems = "OpenAI poem about the weather: " + openAiWeatherPoem.toString() + "\n\n" +
122+
"Anthropic poem about the weather: " + anthropicWeatherPoem.toString() + "\n"
109123
+ ModelOptionsUtils.toJsonStringPrettyPrinter(weatherResponse);
110124

111-
logger.info(anthropicWeatherPoem, responseWithPoems);
125+
logger.info(anthropicWeatherPoem.toString(), responseWithPoems.toString());
112126

113127
return responseWithPoems;
114128

model-context-protocol/web-search/brave-starter/src/main/java/org/springframework/ai/mcp/samples/brave/Application.java

-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
import org.springframework.ai.chat.client.ChatClient;
2323
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
24-
import org.springframework.ai.tool.ToolCallbackProvider;
2524
import org.springframework.boot.CommandLineRunner;
2625
import org.springframework.boot.SpringApplication;
2726
import org.springframework.boot.autoconfigure.SpringBootApplication;

0 commit comments

Comments
 (0)