feat: add structured logging support with severity levels
- Add client and server-side logging capabilities with configurable severity levels
- Implement logging message notifications with DEBUG to EMERGENCY levels
- Add logging consumers for client-side log handling
- Update documentation with logging examples and configuration
- Add comprehensive tests for logging functionality
- Fix typo in README.md (notificaiotn -> notification)
- Centralize MCP method names as constants
- Move all JSON-RPC method names to McpSchema class as static constants
- Replace hardcoded method strings with constant references in McpAsyncClient
and McpAsyncServer
- Update test cases to use new method name constants
Resolves #35
This commit is contained in:
parent
a5394fffd2
commit
86659344fc
1 changed files with 861 additions and 228 deletions
|
|
@ -4,6 +4,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.mcp.server.McpAsyncServer;
|
||||
import org.springframework.ai.mcp.server.McpServer;
|
||||
|
|
@ -15,6 +17,7 @@ import org.springframework.ai.mcp.server.transport.StdioServerTransport;
|
|||
import org.springframework.ai.mcp.spec.McpSchema;
|
||||
import org.springframework.ai.mcp.spec.McpSchema.CallToolResult;
|
||||
import org.springframework.ai.mcp.spec.McpSchema.GetPromptResult;
|
||||
import org.springframework.ai.mcp.spec.McpSchema.LoggingMessageNotification;
|
||||
import org.springframework.ai.mcp.spec.McpSchema.PromptMessage;
|
||||
import org.springframework.ai.mcp.spec.McpSchema.Role;
|
||||
import org.springframework.ai.mcp.spec.McpSchema.TextContent;
|
||||
|
|
@ -27,6 +30,8 @@ import org.springframework.web.reactive.function.server.RouterFunction;
|
|||
@Configuration
|
||||
public class McpServerConfig {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(McpServerConfig.class);
|
||||
|
||||
// STDIO transport
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "transport", name = "mode", havingValue = "stdio")
|
||||
|
|
@ -41,7 +46,8 @@ public class McpServerConfig {
|
|||
return new SseServerTransport(new ObjectMapper(), "/mcp/message");
|
||||
}
|
||||
|
||||
// Router function for SSE transport used by Spring WebFlux to start an HTTP server.
|
||||
// Router function for SSE transport used by Spring WebFlux to start an HTTP
|
||||
// server.
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "transport", name = "mode", havingValue = "sse")
|
||||
public RouterFunction<?> mcpRouterFunction(SseServerTransport transport) {
|
||||
|
|
@ -56,16 +62,20 @@ public class McpServerConfig {
|
|||
.resources(false, true) // No subscribe support, but list changes notifications
|
||||
.tools(true) // Tool support with list changes notifications
|
||||
.prompts(true) // Prompt support with list changes notifications
|
||||
.logging() // Logging support
|
||||
.build();
|
||||
|
||||
// Create the server with both tool and resource capabilities
|
||||
return McpServer.using(transport)
|
||||
var server = McpServer.using(transport)
|
||||
.info("MCP Demo Server", "1.0.0")
|
||||
.capabilities(capabilities)
|
||||
.resources(systemInfoResourceRegistration())
|
||||
.prompts(greetingPromptRegistration())
|
||||
.tools(weatherToolRegistration(), calculatorToolRegistration())
|
||||
.tools(calculatorToolRegistration())
|
||||
.async();
|
||||
|
||||
server.addTool(weatherToolRegistration(server));
|
||||
return server; // @formatter:on
|
||||
} // @formatter:on
|
||||
|
||||
private static ResourceRegistration systemInfoResourceRegistration() {
|
||||
|
|
@ -120,13 +130,27 @@ public class McpServerConfig {
|
|||
});
|
||||
}
|
||||
|
||||
private static ToolRegistration weatherToolRegistration() {
|
||||
private static ToolRegistration weatherToolRegistration(McpAsyncServer server) {
|
||||
return new ToolRegistration(
|
||||
new McpSchema.Tool("weather", "Weather forecast tool by location", Map.of("city", "String")),
|
||||
(arguments) -> {
|
||||
String city = (String) arguments.get("city");
|
||||
TextContent content = new TextContent("Weather forecast for " + city + " is sunny");
|
||||
return new CallToolResult(List.of(content), false);
|
||||
|
||||
// Create the result
|
||||
var result = new CallToolResult(
|
||||
List.of(new TextContent("Weather forecast for " + city + " is sunny")), false);
|
||||
|
||||
// Send the logging notification and ignore its completion
|
||||
server
|
||||
.loggingNotification(LoggingMessageNotification.builder()
|
||||
.data("This is a log message from the weather tool")
|
||||
.build())
|
||||
.subscribe(null, error -> {
|
||||
// Log any errors but don't fail the operation
|
||||
logger.error("Failed to send logging notification", error);
|
||||
});
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue