Skip to content

Commit 52676cd

Browse files
committed
wip wip
Signed-off-by: Daniel Garnier-Moiroux <[email protected]>
1 parent 2401b7e commit 52676cd

File tree

4 files changed

+70
-33
lines changed

4 files changed

+70
-33
lines changed

auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webflux/src/test/java/org/springframework/ai/mcp/server/autoconfigure/StreamableMcpAnnotationsWithLLMIT.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.springframework.ai.mcp.client.common.autoconfigure.McpToolCallbackAutoConfiguration;
5050
import org.springframework.ai.mcp.client.common.autoconfigure.annotations.McpClientAnnotationScannerAutoConfiguration;
5151
import org.springframework.ai.mcp.client.webflux.autoconfigure.StreamableHttpWebFluxTransportAutoConfiguration;
52+
import org.springframework.ai.mcp.server.autoconfigure.capabilities.McpHandlerConfiguration;
5253
import org.springframework.ai.mcp.server.autoconfigure.capabilities.McpHandlerService;
5354
import org.springframework.ai.mcp.server.common.autoconfigure.McpServerAutoConfiguration;
5455
import org.springframework.ai.mcp.server.common.autoconfigure.ToolCallbackConverterAutoConfiguration;
@@ -238,7 +239,8 @@ public String weather(McpSyncRequestContext ctx, @McpToolParam String cityName)
238239
ctx.ping(); // call client ping
239240

240241
// call elicitation
241-
var elicitationResult = ctx.elicit(e -> e.message("Test message"), McpHandlerService.ElicitInput.class);
242+
var elicitationResult = ctx.elicit(e -> e.message("Test message"),
243+
McpHandlerConfiguration.ElicitInput.class);
242244

243245
ctx.progress(p -> p.progress(0.50).total(1.0).message("elicitation completed"));
244246

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package org.springframework.ai.mcp.server.autoconfigure.capabilities;
2+
3+
import io.modelcontextprotocol.spec.McpSchema;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import org.springaicommunity.mcp.annotation.McpElicitation;
7+
import org.springaicommunity.mcp.context.StructuredElicitResult;
8+
9+
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
10+
import org.springframework.context.annotation.Bean;
11+
import org.springframework.context.annotation.Configuration;
12+
import org.springframework.context.annotation.Scope;
13+
import org.springframework.web.context.annotation.RequestScope;
14+
15+
@Configuration
16+
public class McpHandlerConfiguration {
17+
18+
private static final Logger logger = LoggerFactory.getLogger(McpHandlerConfiguration.class);
19+
20+
record ElicitationHandler() {
21+
22+
@McpElicitation(clients = "server1")
23+
public StructuredElicitResult<ElicitInput> elicitationHandler(McpSchema.ElicitRequest request) {
24+
logger.info("MCP ELICITATION: {}", request);
25+
ElicitInput elicitData = new ElicitInput(request.message());
26+
return StructuredElicitResult.builder().structuredContent(elicitData).build();
27+
}
28+
29+
}
30+
31+
public record ElicitInput(String message) {
32+
}
33+
34+
public static class Foo {
35+
36+
}
37+
38+
public static class Bar {
39+
40+
}
41+
42+
@Bean
43+
ElicitationHandler elicitationHandler() {
44+
return new ElicitationHandler();
45+
}
46+
47+
// Ensure that we don't blow up on non-singleton beans
48+
@Bean
49+
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
50+
Foo foo() {
51+
return new Foo();
52+
}
53+
54+
// Ensure that we don't blow up on non-singleton beans
55+
@Bean
56+
@RequestScope
57+
Bar bar(Foo foo) {
58+
return new Bar();
59+
}
60+
61+
}

auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-webflux/src/test/java/org/springframework/ai/mcp/server/autoconfigure/capabilities/McpHandlerService.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919
import io.modelcontextprotocol.spec.McpSchema;
2020
import org.slf4j.Logger;
2121
import org.slf4j.LoggerFactory;
22-
import org.springaicommunity.mcp.annotation.McpElicitation;
2322
import org.springaicommunity.mcp.annotation.McpSampling;
24-
import org.springaicommunity.mcp.context.StructuredElicitResult;
2523

2624
import org.springframework.ai.chat.client.ChatClient;
2725
import org.springframework.stereotype.Service;
@@ -51,14 +49,4 @@ public McpSchema.CreateMessageResult samplingHandler(McpSchema.CreateMessageRequ
5149
.build();
5250
}
5351

54-
@McpElicitation(clients = "server1")
55-
public StructuredElicitResult<ElicitInput> elicitationHandler(McpSchema.ElicitRequest request) {
56-
logger.info("MCP ELICITATION: {}", request);
57-
ElicitInput elicitData = new ElicitInput(request.message());
58-
return StructuredElicitResult.builder().structuredContent(elicitData).build();
59-
}
60-
61-
public record ElicitInput(String message) {
62-
}
63-
6452
}

mcp/mcp-annotations-spring/src/main/java/org/springframework/ai/mcp/annotation/spring/AbstractClientMcpHandlerRegistry.java

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@
3535
import org.springaicommunity.mcp.annotation.McpSampling;
3636
import org.springaicommunity.mcp.annotation.McpToolListChanged;
3737

38+
import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
3839
import org.springframework.beans.BeansException;
39-
import org.springframework.beans.factory.config.BeanDefinition;
4040
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
4141
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
4242
import org.springframework.core.annotation.AnnotationUtils;
@@ -70,8 +70,11 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
7070
Map<String, List<String>> elicitationClientToAnnotatedBeans = new HashMap<>();
7171
Map<String, List<String>> samplingClientToAnnotatedBeans = new HashMap<>();
7272
for (var beanName : beanFactory.getBeanDefinitionNames()) {
73-
var definition = beanFactory.getBeanDefinition(beanName);
74-
var foundAnnotations = scan(getBeanClass(definition, beanFactory.getBeanClassLoader()));
73+
if (!beanFactory.getBeanDefinition(beanName).isSingleton()) {
74+
// Only process singleton beans, not scoped beans
75+
continue;
76+
}
77+
var foundAnnotations = scan(AutoProxyUtils.determineTargetClass(beanFactory, beanName));
7578
if (!foundAnnotations.isEmpty()) {
7679
this.allAnnotatedBeans.add(beanName);
7780
}
@@ -118,23 +121,6 @@ else if (foundAnnotation instanceof McpElicitation elicitation) {
118121
.collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().build()));
119122
}
120123

121-
private static Class<?> getBeanClass(BeanDefinition definition, ClassLoader beanClassLoader) {
122-
if (definition.getResolvableType().resolve() != null) {
123-
return definition.getResolvableType().resolve();
124-
}
125-
// @Component beans registered by component scanning do not have a resolvable type
126-
// We try to resolve them using the beanClassName (which might be null)
127-
if (beanClassLoader != null && definition.getBeanClassName() != null) {
128-
try {
129-
return Class.forName(definition.getBeanClassName(), false, beanClassLoader);
130-
}
131-
catch (ClassNotFoundException ignored) {
132-
133-
}
134-
}
135-
return null;
136-
}
137-
138124
protected List<Annotation> scan(Class<?> beanClass) {
139125
List<Annotation> foundAnnotations = new ArrayList<>();
140126

0 commit comments

Comments
 (0)