Skip to content

Commit 24e4c4e

Browse files
committed
fix(#7): IllegalArgumentException: object is not an instance of declaring class (when split the tool/prompt/resource methods into multi classes)
1 parent af11d65 commit 24e4c4e

17 files changed

+529
-310
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.github.codeboyzhou.mcp.declarative.reflect;
2+
3+
import com.github.codeboyzhou.mcp.declarative.common.Immutable;
4+
import org.jetbrains.annotations.NotNull;
5+
6+
public record InvocationResult(@NotNull Object result, Immutable<Exception> exception) {
7+
8+
public boolean isError() {
9+
return exception != null && exception.get() != null;
10+
}
11+
12+
public static Builder builder() {
13+
return new Builder();
14+
}
15+
16+
public static final class Builder {
17+
18+
private Object result;
19+
20+
private Immutable<Exception> exception;
21+
22+
public Builder result(Object result) {
23+
this.result = result;
24+
return this;
25+
}
26+
27+
public Builder exception(Exception exception) {
28+
this.exception = Immutable.of(exception);
29+
return this;
30+
}
31+
32+
public InvocationResult build() {
33+
return new InvocationResult(result, exception);
34+
}
35+
}
36+
}

src/main/java/com/github/codeboyzhou/mcp/declarative/reflect/MethodMetadata.java renamed to src/main/java/com/github/codeboyzhou/mcp/declarative/reflect/MethodCache.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import java.lang.reflect.Parameter;
99
import java.util.Objects;
1010

11-
public final class MethodMetadata {
11+
public final class MethodCache {
1212

1313
private final Immutable<Method> method;
1414

@@ -26,7 +26,7 @@ public final class MethodMetadata {
2626

2727
private final McpTool mcpToolAnnotation;
2828

29-
public MethodMetadata(Method method) {
29+
public MethodCache(Method method) {
3030
this.method = Immutable.of(method);
3131
this.methodName = method.getName();
3232
this.declaringClass = method.getDeclaringClass();
@@ -37,8 +37,8 @@ public MethodMetadata(Method method) {
3737
this.mcpToolAnnotation = method.getAnnotation(McpTool.class);
3838
}
3939

40-
public static MethodMetadata of(Method method) {
41-
return new MethodMetadata(method);
40+
public static MethodCache of(Method method) {
41+
return new MethodCache(method);
4242
}
4343

4444
public Method getMethod() {
@@ -81,7 +81,7 @@ public boolean equals(Object obj) {
8181
if (obj == null || getClass() != obj.getClass()) {
8282
return false;
8383
}
84-
MethodMetadata that = (MethodMetadata) obj;
84+
MethodCache that = (MethodCache) obj;
8585
return Objects.equals(method, that.method);
8686
}
8787

@@ -92,6 +92,6 @@ public int hashCode() {
9292

9393
@Override
9494
public String toString() {
95-
return String.format("MethodMetadata{methodSignature=%s}", methodSignature);
95+
return String.format("MethodCache{methodSignature=%s}", methodSignature);
9696
}
9797
}

src/main/java/com/github/codeboyzhou/mcp/declarative/reflect/ReflectionCache.java

Lines changed: 0 additions & 28 deletions
This file was deleted.

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,13 @@
1010
import org.slf4j.Logger;
1111
import org.slf4j.LoggerFactory;
1212

13-
public abstract class AbstractMcpServerComponent<T, U, R>
14-
implements McpServerComponent<T>, McpServerComponentHandler<U, R> {
13+
public abstract class AbstractMcpServerComponent<T> implements McpServerComponent<T> {
1514

1615
private static final Logger log = LoggerFactory.getLogger(AbstractMcpServerComponent.class);
1716

1817
private static final String RESOURCE_BUNDLE_BASE_NAME = "i18n/mcp_server_component_descriptions";
1918

20-
protected static final String NOT_SPECIFIED = "Not Specified";
19+
protected static final String NOT_SPECIFIED = "Not specified";
2120

2221
protected final DependencyInjector injector;
2322

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

Lines changed: 0 additions & 8 deletions
This file was deleted.

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

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
import com.github.codeboyzhou.mcp.declarative.annotation.McpPrompt;
44
import com.github.codeboyzhou.mcp.declarative.annotation.McpPromptParam;
5-
import com.github.codeboyzhou.mcp.declarative.reflect.MethodMetadata;
6-
import com.github.codeboyzhou.mcp.declarative.reflect.ReflectionCache;
5+
import com.github.codeboyzhou.mcp.declarative.reflect.InvocationResult;
6+
import com.github.codeboyzhou.mcp.declarative.reflect.MethodCache;
77
import com.github.codeboyzhou.mcp.declarative.server.converter.McpPromptParameterConverter;
88
import com.github.codeboyzhou.mcp.declarative.util.JacksonHelper;
9+
import com.github.codeboyzhou.mcp.declarative.util.ReflectionHelper;
910
import com.github.codeboyzhou.mcp.declarative.util.StringHelper;
1011
import io.modelcontextprotocol.server.McpServerFeatures;
11-
import io.modelcontextprotocol.server.McpSyncServerExchange;
1212
import io.modelcontextprotocol.spec.McpSchema;
1313
import java.lang.reflect.Method;
1414
import java.lang.reflect.Parameter;
@@ -19,26 +19,21 @@
1919
import org.slf4j.LoggerFactory;
2020

2121
public class McpServerPrompt
22-
extends AbstractMcpServerComponent<
23-
McpServerFeatures.SyncPromptSpecification,
24-
McpSchema.GetPromptRequest,
25-
McpSchema.GetPromptResult> {
22+
extends AbstractMcpServerComponent<McpServerFeatures.SyncPromptSpecification> {
2623

2724
private static final Logger log = LoggerFactory.getLogger(McpServerPrompt.class);
2825

29-
private final McpPromptParameterConverter converter;
30-
31-
private Object instance;
26+
private final McpPromptParameterConverter parameterConverter;
3227

3328
public McpServerPrompt() {
34-
this.converter = injector.getInstance(McpPromptParameterConverter.class);
29+
this.parameterConverter = injector.getInstance(McpPromptParameterConverter.class);
3530
}
3631

3732
@Override
3833
public McpServerFeatures.SyncPromptSpecification create(Method method) {
3934
// Use reflection cache for performance optimization
40-
MethodMetadata methodCache = ReflectionCache.INSTANCE.getMethodMetadata(method);
41-
instance = injector.getInstance(methodCache.getDeclaringClass());
35+
MethodCache methodCache = ReflectionHelper.INSTANCE.getOrCache(method);
36+
Object instance = injector.getInstance(methodCache.getDeclaringClass());
4237

4338
McpPrompt promptMethod = methodCache.getMcpPromptAnnotation();
4439
final String name =
@@ -49,34 +44,23 @@ public McpServerFeatures.SyncPromptSpecification create(Method method) {
4944
List<McpSchema.PromptArgument> promptArgs = createPromptArguments(methodCache.getParameters());
5045
McpSchema.Prompt prompt = new McpSchema.Prompt(name, title, description, promptArgs);
5146

52-
log.debug(
53-
"Registering prompt: {} (Cached: {})",
54-
JacksonHelper.toJsonString(prompt),
55-
ReflectionCache.INSTANCE.isCached(method));
47+
log.debug("Registering prompt: {}", JacksonHelper.toJsonString(prompt));
5648

5749
return new McpServerFeatures.SyncPromptSpecification(
58-
prompt, (exchange, request) -> invoke(method, description, exchange, request));
50+
prompt, (exchange, request) -> invoke(instance, methodCache, description, request));
5951
}
6052

61-
@Override
62-
public McpSchema.GetPromptResult invoke(
63-
Method method,
53+
private McpSchema.GetPromptResult invoke(
54+
Object instance,
55+
MethodCache methodCache,
6456
String description,
65-
McpSyncServerExchange exchange,
6657
McpSchema.GetPromptRequest request) {
6758

68-
Object result;
69-
MethodMetadata methodCache = ReflectionCache.INSTANCE.getMethodMetadata(method);
70-
try {
71-
Map<String, Object> arguments = request.arguments();
72-
List<Object> convertedParams = converter.convertAllParameters(methodCache, arguments);
73-
// Use cached method for invocation
74-
result = methodCache.getMethod().invoke(instance, convertedParams.toArray());
75-
} catch (Exception e) {
76-
log.error("Error invoking prompt method: {}", methodCache.getMethodSignature(), e);
77-
result = e + ": " + e.getMessage();
78-
}
79-
McpSchema.Content content = new McpSchema.TextContent(result.toString());
59+
Map<String, Object> arguments = request.arguments();
60+
List<Object> params = parameterConverter.convertAll(methodCache.getParameters(), arguments);
61+
InvocationResult invocation = ReflectionHelper.INSTANCE.invoke(instance, methodCache, params);
62+
63+
McpSchema.Content content = new McpSchema.TextContent(invocation.result().toString());
8064
McpSchema.PromptMessage message = new McpSchema.PromptMessage(McpSchema.Role.USER, content);
8165
return new McpSchema.GetPromptResult(description, List.of(message));
8266
}
Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,35 @@
11
package com.github.codeboyzhou.mcp.declarative.server.component;
22

33
import com.github.codeboyzhou.mcp.declarative.annotation.McpResource;
4-
import com.github.codeboyzhou.mcp.declarative.reflect.MethodMetadata;
5-
import com.github.codeboyzhou.mcp.declarative.reflect.ReflectionCache;
4+
import com.github.codeboyzhou.mcp.declarative.reflect.InvocationResult;
5+
import com.github.codeboyzhou.mcp.declarative.reflect.MethodCache;
66
import com.github.codeboyzhou.mcp.declarative.util.JacksonHelper;
7+
import com.github.codeboyzhou.mcp.declarative.util.ReflectionHelper;
78
import com.github.codeboyzhou.mcp.declarative.util.StringHelper;
89
import io.modelcontextprotocol.server.McpServerFeatures;
9-
import io.modelcontextprotocol.server.McpSyncServerExchange;
1010
import io.modelcontextprotocol.spec.McpSchema;
1111
import java.lang.reflect.Method;
1212
import java.util.List;
1313
import org.slf4j.Logger;
1414
import org.slf4j.LoggerFactory;
1515

1616
public class McpServerResource
17-
extends AbstractMcpServerComponent<
18-
McpServerFeatures.SyncResourceSpecification,
19-
McpSchema.ReadResourceRequest,
20-
McpSchema.ReadResourceResult> {
17+
extends AbstractMcpServerComponent<McpServerFeatures.SyncResourceSpecification> {
2118

2219
private static final Logger log = LoggerFactory.getLogger(McpServerResource.class);
2320

24-
private Object instance;
25-
26-
private McpSchema.Resource resource;
27-
2821
@Override
2922
public McpServerFeatures.SyncResourceSpecification create(Method method) {
3023
// Use reflection cache for performance optimization
31-
MethodMetadata methodCache = ReflectionCache.INSTANCE.getMethodMetadata(method);
32-
instance = injector.getInstance(methodCache.getDeclaringClass());
24+
MethodCache methodCache = ReflectionHelper.INSTANCE.getOrCache(method);
25+
Object instance = injector.getInstance(methodCache.getDeclaringClass());
3326

3427
McpResource res = methodCache.getMcpResourceAnnotation();
3528
final String name = StringHelper.defaultIfBlank(res.name(), methodCache.getMethodName());
3629
final String title = resolveComponentAttributeValue(res.title());
3730
final String description = resolveComponentAttributeValue(res.description());
3831

39-
resource =
32+
McpSchema.Resource resource =
4033
McpSchema.Resource.builder()
4134
.uri(res.uri())
4235
.name(name)
@@ -46,33 +39,20 @@ public McpServerFeatures.SyncResourceSpecification create(Method method) {
4639
.annotations(new McpSchema.Annotations(List.of(res.roles()), res.priority()))
4740
.build();
4841

49-
log.debug(
50-
"Registering resource: {} (Cached: {})",
51-
JacksonHelper.toJsonString(resource),
52-
ReflectionCache.INSTANCE.isCached(method));
42+
log.debug("Registering resource: {}", JacksonHelper.toJsonString(resource));
5343

5444
return new McpServerFeatures.SyncResourceSpecification(
55-
resource, (exchange, request) -> invoke(method, description, exchange, request));
45+
resource, (exchange, request) -> invoke(instance, methodCache, resource));
5646
}
5747

58-
@Override
59-
public McpSchema.ReadResourceResult invoke(
60-
Method method,
61-
String description,
62-
McpSyncServerExchange exchange,
63-
McpSchema.ReadResourceRequest request) {
48+
private McpSchema.ReadResourceResult invoke(
49+
Object instance, MethodCache methodCache, McpSchema.Resource resource) {
6450

65-
Object result;
66-
MethodMetadata methodCache = ReflectionCache.INSTANCE.getMethodMetadata(method);
67-
try {
68-
// Use cached method for invocation
69-
result = methodCache.getMethod().invoke(instance);
70-
} catch (Exception e) {
71-
log.error("Error invoking resource method: {}", methodCache.getMethodSignature(), e);
72-
result = e + ": " + e.getMessage();
73-
}
74-
McpSchema.ResourceContents contents =
75-
new McpSchema.TextResourceContents(resource.uri(), resource.mimeType(), result.toString());
51+
InvocationResult invocation = ReflectionHelper.INSTANCE.invoke(instance, methodCache);
52+
final String uri = resource.uri();
53+
final String mimeType = resource.mimeType();
54+
final String text = invocation.result().toString();
55+
McpSchema.ResourceContents contents = new McpSchema.TextResourceContents(uri, mimeType, text);
7656
return new McpSchema.ReadResourceResult(List.of(contents));
7757
}
7858
}

0 commit comments

Comments
 (0)