Skip to content

Commit f41212f

Browse files
committed
refactor(server): Enhance parameter conversion in McpServerPromptFactory and McpServerToolFactory with dedicated converters
1 parent 9f0617a commit f41212f

File tree

9 files changed

+112
-56
lines changed

9 files changed

+112
-56
lines changed

src/main/java/com/github/codeboyzhou/mcp/declarative/di/DependencyInjectorProvider.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ public enum DependencyInjectorProvider {
66
private volatile DependencyInjector injector;
77

88
public DependencyInjectorProvider initialize(DependencyInjector injector) {
9-
synchronized (this) {
10-
if (this.injector == null) {
11-
this.injector = injector;
9+
if (this.injector == null) {
10+
synchronized (this) {
11+
if (this.injector == null) {
12+
this.injector = injector;
13+
}
1214
}
1315
}
1416
return this;

src/main/java/com/github/codeboyzhou/mcp/declarative/di/GuiceDependencyInjector.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@ public <T> T getInstance(Class<T> type) {
2020
throw new IllegalStateException("GuiceDependencyInjector is not initialized");
2121
}
2222

23-
@Override
24-
public boolean isInitialized() {
25-
return injector != null;
26-
}
27-
2823
@Override
2924
public <T> T getVariable(Class<T> type, String name) {
3025
if (isInitialized()) {
3126
return injector.getInstance(Key.get(type, Names.named(name)));
3227
}
3328
throw new IllegalStateException("GuiceDependencyInjector is not initialized");
3429
}
30+
31+
@Override
32+
public boolean isInitialized() {
33+
return injector != null;
34+
}
3535
}

src/main/java/com/github/codeboyzhou/mcp/declarative/di/GuiceInjectorModule.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import com.github.codeboyzhou.mcp.declarative.server.component.McpServerPromptFactory;
1414
import com.github.codeboyzhou.mcp.declarative.server.component.McpServerResourceFactory;
1515
import com.github.codeboyzhou.mcp.declarative.server.component.McpServerToolFactory;
16+
import com.github.codeboyzhou.mcp.declarative.server.converter.McpPromptParameterConverter;
17+
import com.github.codeboyzhou.mcp.declarative.server.converter.McpToolParameterConverter;
1618
import com.github.codeboyzhou.mcp.declarative.server.factory.McpSseServerFactory;
1719
import com.github.codeboyzhou.mcp.declarative.server.factory.McpStdioServerFactory;
1820
import com.github.codeboyzhou.mcp.declarative.server.factory.McpStreamableServerFactory;
@@ -47,6 +49,10 @@ protected void configure() {
4749
bind(McpServerPromptFactory.class).in(SINGLETON);
4850
bind(McpServerToolFactory.class).in(SINGLETON);
4951

52+
// Bind all implementations of ParameterConverter
53+
bind(McpPromptParameterConverter.class).in(SINGLETON);
54+
bind(McpToolParameterConverter.class).in(SINGLETON);
55+
5056
// Bind all implementations of McpServerFactory
5157
bind(McpStdioServerFactory.class).in(SINGLETON);
5258
bind(McpSseServerFactory.class).in(SINGLETON);

src/main/java/com/github/codeboyzhou/mcp/declarative/server/component/McpServerPromptFactory.java

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
import com.github.codeboyzhou.mcp.declarative.annotation.McpPromptParam;
55
import com.github.codeboyzhou.mcp.declarative.reflect.MethodMetadata;
66
import com.github.codeboyzhou.mcp.declarative.reflect.ReflectionCache;
7+
import com.github.codeboyzhou.mcp.declarative.server.converter.McpPromptParameterConverter;
78
import com.github.codeboyzhou.mcp.declarative.util.ObjectMappers;
89
import com.github.codeboyzhou.mcp.declarative.util.Strings;
9-
import com.github.codeboyzhou.mcp.declarative.util.Types;
1010
import io.modelcontextprotocol.server.McpServerFeatures;
1111
import io.modelcontextprotocol.spec.McpSchema;
1212
import java.lang.reflect.Method;
@@ -40,15 +40,18 @@ public McpServerFeatures.SyncPromptSpecification create(Class<?> clazz, Method m
4040
ObjectMappers.toJson(prompt),
4141
ReflectionCache.INSTANCE.isCached(method));
4242

43+
Object instance = injector.getInstance(clazz);
44+
McpPromptParameterConverter converter = injector.getInstance(McpPromptParameterConverter.class);
45+
4346
return new McpServerFeatures.SyncPromptSpecification(
4447
prompt,
4548
(exchange, request) -> {
4649
Object result;
4750
try {
48-
Object instance = injector.getInstance(clazz);
49-
List<Object> typedValues = asTypedParameters(metadata, request.arguments());
51+
Map<String, Object> arguments = request.arguments();
52+
List<Object> convertedParams = converter.convertAllParameters(metadata, arguments);
5053
// Use cached method for invocation
51-
result = metadata.getMethod().invoke(instance, typedValues.toArray());
54+
result = metadata.getMethod().invoke(instance, convertedParams.toArray());
5255
} catch (Exception e) {
5356
log.error("Error invoking prompt method {}", metadata.getMethodSignature(), e);
5457
result = e + ": " + e.getMessage();
@@ -77,24 +80,4 @@ private List<McpSchema.PromptArgument> createPromptArguments(MethodMetadata meta
7780

7881
return promptArguments;
7982
}
80-
81-
private List<Object> asTypedParameters(MethodMetadata metadata, Map<String, Object> arguments) {
82-
Parameter[] methodParams = metadata.getParameters();
83-
List<Object> typedValues = new ArrayList<>(methodParams.length);
84-
85-
for (Parameter param : methodParams) {
86-
Object rawValue = null;
87-
if (param.isAnnotationPresent(McpPromptParam.class)) {
88-
McpPromptParam promptParam = param.getAnnotation(McpPromptParam.class);
89-
rawValue = arguments.get(promptParam.name());
90-
}
91-
// Fill in a default value when the parameter is not specified or unannotated
92-
// to ensure that the parameter type is correct when calling method.invoke()
93-
Class<?> targetType = param.getType();
94-
Object typedValue = Types.convert(rawValue, targetType);
95-
typedValues.add(typedValue);
96-
}
97-
98-
return typedValues;
99-
}
10083
}

src/main/java/com/github/codeboyzhou/mcp/declarative/server/component/McpServerToolFactory.java

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
import com.github.codeboyzhou.mcp.declarative.enums.JsonSchemaDataType;
88
import com.github.codeboyzhou.mcp.declarative.reflect.MethodMetadata;
99
import com.github.codeboyzhou.mcp.declarative.reflect.ReflectionCache;
10+
import com.github.codeboyzhou.mcp.declarative.server.converter.McpToolParameterConverter;
1011
import com.github.codeboyzhou.mcp.declarative.util.ObjectMappers;
1112
import com.github.codeboyzhou.mcp.declarative.util.Strings;
12-
import com.github.codeboyzhou.mcp.declarative.util.Types;
1313
import io.modelcontextprotocol.server.McpServerFeatures;
1414
import io.modelcontextprotocol.spec.McpSchema;
1515
import java.lang.reflect.Field;
@@ -54,17 +54,20 @@ public McpServerFeatures.SyncToolSpecification create(Class<?> clazz, Method met
5454
ObjectMappers.toJson(tool),
5555
ReflectionCache.INSTANCE.isCached(method));
5656

57+
Object instance = injector.getInstance(clazz);
58+
McpToolParameterConverter converter = injector.getInstance(McpToolParameterConverter.class);
59+
5760
return McpServerFeatures.SyncToolSpecification.builder()
5861
.tool(tool)
5962
.callHandler(
6063
(exchange, request) -> {
6164
Object result;
6265
boolean isError = false;
6366
try {
64-
Object instance = injector.getInstance(clazz);
65-
List<Object> typedValues = asTypedParameters(metadata, request.arguments());
67+
Map<String, Object> arguments = request.arguments();
68+
List<Object> convertedParams = converter.convertAllParameters(metadata, arguments);
6669
// Use cached method for invocation
67-
result = metadata.getMethod().invoke(instance, typedValues.toArray());
70+
result = metadata.getMethod().invoke(instance, convertedParams.toArray());
6871
} catch (Exception e) {
6972
log.error("Error invoking tool method {}", metadata.getMethodSignature(), e);
7073
result = e + ": " + e.getMessage();
@@ -153,24 +156,4 @@ private Map<String, Object> createJsonSchemaDefinition(Class<?> definitionClass)
153156

154157
return definitionJsonSchema;
155158
}
156-
157-
private List<Object> asTypedParameters(MethodMetadata metadata, Map<String, Object> arguments) {
158-
Parameter[] methodParams = metadata.getParameters();
159-
List<Object> typedValues = new ArrayList<>(methodParams.length);
160-
161-
for (Parameter param : methodParams) {
162-
Object rawValue = null;
163-
if (param.isAnnotationPresent(McpToolParam.class)) {
164-
McpToolParam toolParam = param.getAnnotation(McpToolParam.class);
165-
rawValue = arguments.get(toolParam.name());
166-
}
167-
// Fill in a default value when the parameter is not specified or unannotated
168-
// to ensure that the parameter type is correct when calling method.invoke()
169-
Class<?> targetType = param.getType();
170-
Object typedValue = Types.convert(rawValue, targetType);
171-
typedValues.add(typedValue);
172-
}
173-
174-
return typedValues;
175-
}
176159
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.github.codeboyzhou.mcp.declarative.server.converter;
2+
3+
import com.github.codeboyzhou.mcp.declarative.reflect.MethodMetadata;
4+
import com.github.codeboyzhou.mcp.declarative.util.Types;
5+
import java.lang.annotation.Annotation;
6+
import java.lang.reflect.Parameter;
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
import java.util.Map;
10+
11+
public abstract class AbstractParameterConverter<A extends Annotation>
12+
implements ParameterConverter<A> {
13+
14+
public List<Object> convertAllParameters(MethodMetadata metadata, Map<String, Object> args) {
15+
Parameter[] params = metadata.getParameters();
16+
List<Object> result = new ArrayList<>(params.length);
17+
18+
for (Parameter param : params) {
19+
A annotation = param.getAnnotation(getAnnotationType());
20+
Object converted;
21+
// Fill in a default value when the parameter is not specified or unannotated
22+
// to ensure that the parameter type is correct when calling method.invoke()
23+
if (annotation == null) {
24+
converted = Types.convert(null, param.getType());
25+
} else {
26+
converted = convert(param, annotation, args);
27+
}
28+
result.add(converted);
29+
}
30+
31+
return result;
32+
}
33+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.github.codeboyzhou.mcp.declarative.server.converter;
2+
3+
import com.github.codeboyzhou.mcp.declarative.annotation.McpPromptParam;
4+
import com.github.codeboyzhou.mcp.declarative.util.Types;
5+
import java.lang.reflect.Parameter;
6+
import java.util.Map;
7+
8+
public class McpPromptParameterConverter extends AbstractParameterConverter<McpPromptParam> {
9+
@Override
10+
public Object convert(Parameter parameter, McpPromptParam annotation, Map<String, Object> args) {
11+
Object rawValue = args.get(annotation.name());
12+
return Types.convert(rawValue, parameter.getType());
13+
}
14+
15+
@Override
16+
public Class<McpPromptParam> getAnnotationType() {
17+
return McpPromptParam.class;
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.github.codeboyzhou.mcp.declarative.server.converter;
2+
3+
import com.github.codeboyzhou.mcp.declarative.annotation.McpToolParam;
4+
import com.github.codeboyzhou.mcp.declarative.util.Types;
5+
import java.lang.reflect.Parameter;
6+
import java.util.Map;
7+
8+
public class McpToolParameterConverter extends AbstractParameterConverter<McpToolParam> {
9+
@Override
10+
public Object convert(Parameter parameter, McpToolParam annotation, Map<String, Object> args) {
11+
Object rawValue = args.get(annotation.name());
12+
return Types.convert(rawValue, parameter.getType());
13+
}
14+
15+
@Override
16+
public Class<McpToolParam> getAnnotationType() {
17+
return McpToolParam.class;
18+
}
19+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.github.codeboyzhou.mcp.declarative.server.converter;
2+
3+
import java.lang.annotation.Annotation;
4+
import java.lang.reflect.Parameter;
5+
import java.util.Map;
6+
7+
public interface ParameterConverter<A extends Annotation> {
8+
Object convert(Parameter parameter, A annotation, Map<String, Object> args);
9+
10+
Class<A> getAnnotationType();
11+
}

0 commit comments

Comments
 (0)