-
Notifications
You must be signed in to change notification settings - Fork 8
Description
I am using streamable Http transport (v 0.8.1) and have multiple tool methods registered from same class (e.g toolA(), toolB() etc). When the call is made to the server wrong tool method is getting invoked. for example, when toolA is called, toolB gets invoked.
The problem can be traced to #McpServerComponentRegister where tool registration happens. The componentClass is McpServerTool, for which only one instance is created (outside the loop).
private <T> void register(
Class<? extends Annotation> annotationClass,
Class<? extends McpServerComponent<T>> componentClass,
BiConsumer<McpSyncServer, T> serverAddComponent) {
Set<Method> methods = reflections.getMethodsAnnotatedWith(annotationClass);
McpServerComponent<T> component = injector.getInstance(componentClass);
for (Method method : methods) {
serverAddComponent.accept(server.get(), component.create(method));
}
}
In McpServerTool's create method, methodCache is looked up and stored as instance field. At the end of this method, this is included as part of callHandler(). So multiple calls to the create method corrupts this instance and the last method's methodCache is stored, which is later used for invocation. The same problem may be applicable for Prompt and Resource since the approach is the same.
@Override
public McpServerFeatures.SyncToolSpecification create(Method method) {
// Use reflection cache for performance optimization
methodCache = ReflectionCache.INSTANCE.getMethodMetadata(method);
instance = injector.getInstance(methodCache.getDeclaringClass());
McpTool toolMethod = methodCache.getMcpToolAnnotation();
......
return McpServerFeatures.SyncToolSpecification.builder().tool(tool).callHandler(this).build();
A simple (naive?) fix would be to create an instance of the componentClass for each method. It doesn't look like instance of the class (like McpServerTool) are meant to be shared, since they include state related to the method. I tried this fix and it seem to work.
private <T> void register(...) {
Set<Method> methods = reflections.getMethodsAnnotatedWith(annotationClass);
// McpServerComponent<T> component = injector.getInstance(c);
for (Method method : methods) {
McpServerComponent<T> component = componentClass.newInstance(); // handle exception
serverAddComponent.accept(server.get(), component.create(method));
}
}