Skip to content
Open
2 changes: 1 addition & 1 deletion dubbo-plugin/dubbo-mcp/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

<artifactId>dubbo-mcp</artifactId>
<properties>
<mcp.version>0.11.2</mcp.version>
<mcp.version>0.18.2</mcp.version>
<skip_maven_deploy>false</skip_maven_deploy>
</properties>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
import java.util.concurrent.ExecutorService;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.json.McpJsonMapper;
import io.modelcontextprotocol.json.jackson2.JacksonMcpJsonMapper;
import io.modelcontextprotocol.server.McpAsyncServer;
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.spec.McpSchema;
Expand All @@ -54,6 +56,9 @@ public class McpApplicationDeployListener implements ApplicationDeployListener {

private static final ErrorTypeAwareLogger logger =
LoggerFactory.getErrorTypeAwareLogger(McpApplicationDeployListener.class);

private static final McpJsonMapper MCP_JSON_MAPPER = createMcpJsonMapper();

private DubboServiceToolRegistry toolRegistry;

private boolean mcpEnable = true;
Expand Down Expand Up @@ -106,12 +111,12 @@ public void onStarted(ApplicationModel applicationModel) {
globalConf.getInt(McpConstant.SETTINGS_MCP_SESSION_TIMEOUT, McpConstant.DEFAULT_SESSION_TIMEOUT);
if ("streamable".equals(protocol)) {
dubboMcpStreamableTransportProvider =
new DubboMcpStreamableTransportProvider(new ObjectMapper(), sessionTimeout);
new DubboMcpStreamableTransportProvider(MCP_JSON_MAPPER, sessionTimeout);
mcpAsyncServer = McpServer.async(getDubboMcpStreamableTransportProvider())
.capabilities(serverCapabilities)
.build();
} else if ("sse".equals(protocol)) {
dubboMcpSseTransportProvider = new DubboMcpSseTransportProvider(new ObjectMapper(), sessionTimeout);
dubboMcpSseTransportProvider = new DubboMcpSseTransportProvider(MCP_JSON_MAPPER, sessionTimeout);
mcpAsyncServer = McpServer.async(getDubboMcpSseTransportProvider())
.capabilities(serverCapabilities)
.build();
Expand Down Expand Up @@ -245,4 +250,10 @@ private int getRegisterPort() {
}
return NetUtils.getAvailablePort();
}

private static McpJsonMapper createMcpJsonMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL);
return new JacksonMcpJsonMapper(objectMapper);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,13 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.spec.McpSchema;

public class DubboOpenApiToolConverter {

private static final ErrorTypeAwareLogger logger =
LoggerFactory.getErrorTypeAwareLogger(DubboOpenApiToolConverter.class);
private final DefaultOpenAPIService openApiService;
private final ObjectMapper objectMapper = new ObjectMapper();
private final Map<String, Operation> opCache = new ConcurrentHashMap<>();

public DubboOpenApiToolConverter(DefaultOpenAPIService openApiService) {
Expand Down Expand Up @@ -107,20 +105,12 @@ private McpSchema.Tool convertOperationToMcpTool(
String toolName = generateToolName(op, toolConfig);
String desc = generateToolDescription(op, toolConfig, path, method);

Map<String, Object> paramsSchemaMap = extractParameterSchema(op);
String schemaJson;
try {
schemaJson = objectMapper.writeValueAsString(paramsSchemaMap);
} catch (Exception e) {
logger.error(
LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION,
"Failed to serialize parameter schema for tool {}: {}",
opId,
e.getMessage(),
e);
schemaJson = "{\"type\":\"object\",\"properties\":{}}";
}
return new McpSchema.Tool(toolName, desc, schemaJson);
McpSchema.JsonSchema inputSchema = extractParameterSchema(op);
return McpSchema.Tool.builder()
.name(toolName)
.description(desc)
.inputSchema(inputSchema)
.build();
}

private String generateToolName(Operation op, McpServiceFilter.McpToolConfig toolConfig) {
Expand Down Expand Up @@ -155,10 +145,8 @@ private String generateToolDescription(
return desc;
}

private Map<String, Object> extractParameterSchema(Operation op) {
Map<String, Object> schema = new HashMap<>();
private McpSchema.JsonSchema extractParameterSchema(Operation op) {
Map<String, Object> props = new HashMap<>();
schema.put(McpConstant.SCHEMA_PROPERTY_TYPE, JsonSchemaType.OBJECT_SCHEMA.getJsonSchemaType());

if (op.getParameters() != null) {
for (Parameter apiParam : op.getParameters()) {
Expand Down Expand Up @@ -313,8 +301,8 @@ private Map<String, Object> extractParameterSchema(Operation op) {
}
});
}
schema.put(McpConstant.SCHEMA_PROPERTY_PROPERTIES, props);
return schema;
return new McpSchema.JsonSchema(
JsonSchemaType.OBJECT_SCHEMA.getJsonSchemaType(), props, null, null, null, null);
Comment thread
zrlw marked this conversation as resolved.
}

private Map<String, Object> convertOpenApiSchemaToMcpMap(Schema openApiSchema) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.McpAsyncServer;
import io.modelcontextprotocol.server.McpAsyncServerExchange;
import io.modelcontextprotocol.server.McpServerFeatures;
Expand All @@ -60,7 +59,6 @@ public class DubboServiceToolRegistry {
private final McpServiceFilter mcpServiceFilter;
private final Map<String, McpServerFeatures.AsyncToolSpecification> registeredTools = new ConcurrentHashMap<>();
private final Map<String, Set<String>> serviceToToolsMapping = new ConcurrentHashMap<>();
private final ObjectMapper objectMapper;

public DubboServiceToolRegistry(
McpAsyncServer mcpServer,
Expand All @@ -71,7 +69,6 @@ public DubboServiceToolRegistry(
this.toolConverter = toolConverter;
this.genericCaller = genericCaller;
this.mcpServiceFilter = mcpServiceFilter;
this.objectMapper = new ObjectMapper();
}

public int registerService(ProviderModel providerModel) {
Expand Down Expand Up @@ -191,7 +188,11 @@ private String registerMethodAsTool(
description = generateDefaultDescription(method, providerModel);
}

McpSchema.Tool mcpTool = new McpSchema.Tool(toolName, description, generateToolSchema(method));
McpSchema.Tool mcpTool = McpSchema.Tool.builder()
.name(toolName)
.description(description)
.inputSchema(generateToolSchema(method))
.build();

McpServerFeatures.AsyncToolSpecification toolSpec =
createMethodToolSpecification(mcpTool, providerModel, method, url);
Expand Down Expand Up @@ -369,32 +370,15 @@ private String generateDefaultDescription(Method method, ProviderModel providerM
providerModel.getServiceModel().getInterfaceName());
}

private String generateToolSchema(Method method) {
Map<String, Object> schemaMap = new HashMap<>();
schemaMap.put(McpConstant.SCHEMA_PROPERTY_TYPE, JsonSchemaType.OBJECT_SCHEMA.getJsonSchemaType());

private McpSchema.JsonSchema generateToolSchema(Method method) {
Map<String, Object> properties = new HashMap<>();
List<String> requiredParams = new ArrayList<>();

generateSchemaFromMethodSignature(method, properties, requiredParams);

schemaMap.put(McpConstant.SCHEMA_PROPERTY_PROPERTIES, properties);

if (!requiredParams.isEmpty()) {
schemaMap.put(McpConstant.SCHEMA_PROPERTY_REQUIRED, requiredParams);
}

try {
return objectMapper.writeValueAsString(schemaMap);
} catch (Exception e) {
logger.error(
LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION,
"",
"",
"Failed to generate tool schema for method " + method.getName() + ": " + e.getMessage(),
e);
return "{\"type\":\"object\",\"properties\":{}}";
}
List<String> schemaRequiredParams = requiredParams.isEmpty() ? null : requiredParams;
return new McpSchema.JsonSchema(
JsonSchemaType.OBJECT_SCHEMA.getJsonSchemaType(), properties, schemaRequiredParams, null, null, null);
}

private void generateSchemaFromMethodSignature(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.json.McpJsonMapper;
import io.modelcontextprotocol.json.TypeRef;
import io.modelcontextprotocol.spec.McpError;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpServerSession;
Expand Down Expand Up @@ -66,14 +66,14 @@ public class DubboMcpSseTransportProvider implements McpServerTransportProvider

private McpServerSession.Factory sessionFactory;

private final ObjectMapper objectMapper;
private final McpJsonMapper mcpJsonMapper;

/**
* session cache, default expire time is 60 seconds
*/
private final ExpiringMap<String, McpServerSession> sessions;

public DubboMcpSseTransportProvider(ObjectMapper objectMapper, Integer expireSeconds) {
public DubboMcpSseTransportProvider(McpJsonMapper mcpJsonMapper, Integer expireSeconds) {
if (expireSeconds != null) {
if (expireSeconds < 60) {
expireSeconds = 60;
Expand All @@ -82,12 +82,12 @@ public DubboMcpSseTransportProvider(ObjectMapper objectMapper, Integer expireSec
expireSeconds = 60;
}
sessions = new ExpiringMap<>(expireSeconds, 30);
this.objectMapper = objectMapper;
this.mcpJsonMapper = java.util.Objects.requireNonNull(mcpJsonMapper, "mcpJsonMapper must not be null");
sessions.getExpireThread().startExpiryIfNotStarted();
}

public DubboMcpSseTransportProvider(ObjectMapper objectMapper) {
this(objectMapper, 60);
public DubboMcpSseTransportProvider(McpJsonMapper mcpJsonMapper) {
this(mcpJsonMapper, 60);
}

@Override
Expand Down Expand Up @@ -147,7 +147,7 @@ public void handleMessage() {
refreshSessionExpire(session);
try {
McpSchema.JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(
objectMapper, IOUtils.read(request.inputStream(), String.valueOf(StandardCharsets.UTF_8)));
mcpJsonMapper, IOUtils.read(request.inputStream(), StandardCharsets.UTF_8.name()));
session.handle(message).block();
response.setStatus(HttpStatus.OK.getCode());
} catch (IOException e) {
Expand All @@ -160,7 +160,7 @@ private void handleSseConnection(StreamObserver<ServerSentEvent<String>> respons
// Handle the SSE connection
// This is where you would set up the SSE stream and send events to the client
DubboMcpSessionTransport dubboMcpSessionTransport =
new DubboMcpSessionTransport(responseObserver, objectMapper);
new DubboMcpSessionTransport(responseObserver, mcpJsonMapper);
McpServerSession mcpServerSession = sessionFactory.create(dubboMcpSessionTransport);
sessions.put(mcpServerSession.getId(), mcpServerSession);
Configuration conf = ConfigurationUtils.getGlobalConfiguration(ApplicationModel.defaultModel());
Expand All @@ -179,14 +179,14 @@ private void sendEvent(StreamObserver<ServerSentEvent<String>> responseObserver,

private static class DubboMcpSessionTransport implements McpServerTransport {

private final ObjectMapper JSON;
private final McpJsonMapper mcpJsonMapper;

private final StreamObserver<ServerSentEvent<String>> responseObserver;

public DubboMcpSessionTransport(
StreamObserver<ServerSentEvent<String>> responseObserver, ObjectMapper objectMapper) {
StreamObserver<ServerSentEvent<String>> responseObserver, McpJsonMapper mcpJsonMapper) {
this.responseObserver = responseObserver;
this.JSON = objectMapper;
this.mcpJsonMapper = mcpJsonMapper;
}
Comment thread
zrlw marked this conversation as resolved.

@Override
Expand All @@ -203,7 +203,7 @@ public Mono<Void> closeGracefully() {
public Mono<Void> sendMessage(McpSchema.JSONRPCMessage message) {
return Mono.fromRunnable(() -> {
try {
String jsonText = JSON.writeValueAsString(message);
String jsonText = mcpJsonMapper.writeValueAsString(message);
responseObserver.onNext(ServerSentEvent.<String>builder()
.event(MESSAGE_EVENT_TYPE)
.data(jsonText)
Expand All @@ -215,8 +215,8 @@ public Mono<Void> sendMessage(McpSchema.JSONRPCMessage message) {
}

@Override
public <T> T unmarshalFrom(Object data, TypeReference<T> typeRef) {
return JSON.convertValue(data, typeRef);
public <T> T unmarshalFrom(Object data, TypeRef<T> typeRef) {
return mcpJsonMapper.convertValue(data, typeRef);
}
}
}
Loading
Loading