Skip to content

Commit 528e0b9

Browse files
committed
feat: introduce McpServerObjectMapperFactory for consistent ObjectMapper configuration
Signed-off-by: liugddx <[email protected]>
1 parent b452e89 commit 528e0b9

File tree

4 files changed

+45
-7
lines changed

4 files changed

+45
-7
lines changed

auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-common/src/main/java/org/springframework/ai/mcp/server/common/autoconfigure/McpServerAutoConfiguration.java

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121
import java.util.function.BiConsumer;
2222
import java.util.function.BiFunction;
2323

24+
import com.fasterxml.jackson.annotation.JsonInclude;
25+
import com.fasterxml.jackson.databind.DeserializationFeature;
2426
import com.fasterxml.jackson.databind.ObjectMapper;
27+
import com.fasterxml.jackson.databind.SerializationFeature;
28+
import com.fasterxml.jackson.databind.json.JsonMapper;
2529
import io.modelcontextprotocol.json.jackson.JacksonMcpJsonMapper;
2630
import io.modelcontextprotocol.server.McpAsyncServer;
2731
import io.modelcontextprotocol.server.McpAsyncServerExchange;
@@ -50,6 +54,7 @@
5054

5155
import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerChangeNotificationProperties;
5256
import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerProperties;
57+
import org.springframework.ai.util.JacksonUtils;
5358
import org.springframework.beans.factory.ObjectProvider;
5459
import org.springframework.boot.autoconfigure.AutoConfiguration;
5560
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -91,12 +96,42 @@ public class McpServerAutoConfiguration {
9196

9297
private static final LogAccessor logger = new LogAccessor(McpServerAutoConfiguration.class);
9398

99+
/**
100+
* Creates a configured ObjectMapper for MCP server JSON serialization.
101+
* <p>
102+
* This ObjectMapper is specifically configured for MCP protocol compliance with:
103+
* <ul>
104+
* <li>Lenient deserialization that doesn't fail on unknown properties</li>
105+
* <li>Proper handling of empty beans during serialization</li>
106+
* <li>Exclusion of null values from JSON output</li>
107+
* <li>Standard Jackson modules for Java 8, JSR-310, and Kotlin support</li>
108+
* </ul>
109+
* <p>
110+
* This bean can be overridden by providing a custom ObjectMapper bean with the name
111+
* "mcpServerObjectMapper".
112+
* @return configured ObjectMapper instance for MCP server operations
113+
*/
114+
@Bean(name = "mcpServerObjectMapper")
115+
@ConditionalOnMissingBean(name = "mcpServerObjectMapper")
116+
public ObjectMapper mcpServerObjectMapper() {
117+
return JsonMapper.builder()
118+
// Deserialization configuration
119+
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
120+
.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)
121+
// Serialization configuration
122+
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
123+
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
124+
.serializationInclusion(JsonInclude.Include.NON_NULL)
125+
// Register standard modules (Jdk8, JavaTime, ParameterNames, Kotlin if
126+
// available)
127+
.addModules(JacksonUtils.instantiateAvailableModules())
128+
.build();
129+
}
130+
94131
@Bean
95132
@ConditionalOnMissingBean
96-
public McpServerTransportProviderBase stdioServerTransport(ObjectProvider<ObjectMapper> objectMapperProvider) {
97-
ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new);
98-
99-
return new StdioServerTransportProvider(new JacksonMcpJsonMapper(objectMapper));
133+
public McpServerTransportProviderBase stdioServerTransport(ObjectMapper mcpServerObjectMapper) {
134+
return new StdioServerTransportProvider(new JacksonMcpJsonMapper(mcpServerObjectMapper));
100135
}
101136

102137
@Bean

auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webmvc/src/main/java/org/springframework/ai/mcp/server/autoconfigure/McpServerSseWebMvcAutoConfiguration.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.modelcontextprotocol.spec.McpServerTransportProvider;
2323

2424
import org.springframework.ai.mcp.server.common.autoconfigure.McpServerAutoConfiguration;
25+
import org.springframework.ai.mcp.server.common.autoconfigure.McpServerObjectMapperFactory;
2526
import org.springframework.ai.mcp.server.common.autoconfigure.McpServerStdioDisabledCondition;
2627
import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerSseProperties;
2728
import org.springframework.beans.factory.ObjectProvider;
@@ -78,7 +79,7 @@ public class McpServerSseWebMvcAutoConfiguration {
7879
public WebMvcSseServerTransportProvider webMvcSseServerTransportProvider(
7980
ObjectProvider<ObjectMapper> objectMapperProvider, McpServerSseProperties serverProperties) {
8081

81-
ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new);
82+
ObjectMapper objectMapper = McpServerObjectMapperFactory.getOrCreateObjectMapper(objectMapperProvider);
8283

8384
return WebMvcSseServerTransportProvider.builder()
8485
.jsonMapper(new JacksonMcpJsonMapper(objectMapper))

auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webmvc/src/main/java/org/springframework/ai/mcp/server/autoconfigure/McpServerStatelessWebMvcAutoConfiguration.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import io.modelcontextprotocol.server.transport.WebMvcStatelessServerTransport;
2222
import io.modelcontextprotocol.spec.McpSchema;
2323

24+
import org.springframework.ai.mcp.server.common.autoconfigure.McpServerObjectMapperFactory;
2425
import org.springframework.ai.mcp.server.common.autoconfigure.McpServerStatelessAutoConfiguration;
2526
import org.springframework.ai.mcp.server.common.autoconfigure.McpServerStdioDisabledCondition;
2627
import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerStreamableHttpProperties;
@@ -50,7 +51,7 @@ public class McpServerStatelessWebMvcAutoConfiguration {
5051
public WebMvcStatelessServerTransport webMvcStatelessServerTransport(
5152
ObjectProvider<ObjectMapper> objectMapperProvider, McpServerStreamableHttpProperties serverProperties) {
5253

53-
ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new);
54+
ObjectMapper objectMapper = McpServerObjectMapperFactory.getOrCreateObjectMapper(objectMapperProvider);
5455

5556
return WebMvcStatelessServerTransport.builder()
5657
.jsonMapper(new JacksonMcpJsonMapper(objectMapper))

auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webmvc/src/main/java/org/springframework/ai/mcp/server/autoconfigure/McpServerStreamableHttpWebMvcAutoConfiguration.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.modelcontextprotocol.spec.McpSchema;
2323

2424
import org.springframework.ai.mcp.server.common.autoconfigure.McpServerAutoConfiguration;
25+
import org.springframework.ai.mcp.server.common.autoconfigure.McpServerObjectMapperFactory;
2526
import org.springframework.ai.mcp.server.common.autoconfigure.McpServerStdioDisabledCondition;
2627
import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerProperties;
2728
import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerStreamableHttpProperties;
@@ -51,7 +52,7 @@ public class McpServerStreamableHttpWebMvcAutoConfiguration {
5152
public WebMvcStreamableServerTransportProvider webMvcStreamableServerTransportProvider(
5253
ObjectProvider<ObjectMapper> objectMapperProvider, McpServerStreamableHttpProperties serverProperties) {
5354

54-
ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new);
55+
ObjectMapper objectMapper = McpServerObjectMapperFactory.getOrCreateObjectMapper(objectMapperProvider);
5556

5657
return WebMvcStreamableServerTransportProvider.builder()
5758
.jsonMapper(new JacksonMcpJsonMapper(objectMapper))

0 commit comments

Comments
 (0)