Skip to content

Commit 01c67cc

Browse files
committed
feat(i18n): Introduce McpI18nEnabled annotation to enable/disable i18n support
- Refactor getDescription method to resolveComponentAttributeValue for better reusability
1 parent 516e831 commit 01c67cc

File tree

6 files changed

+58
-25
lines changed

6 files changed

+58
-25
lines changed
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.annotation;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Target(ElementType.TYPE)
9+
@Retention(RetentionPolicy.RUNTIME)
10+
public @interface McpI18nEnabled {
11+
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.github.codeboyzhou.mcp.declarative.common;
22

33
import com.github.codeboyzhou.mcp.declarative.annotation.McpComponentScan;
4+
import com.github.codeboyzhou.mcp.declarative.annotation.McpI18nEnabled;
45
import com.github.codeboyzhou.mcp.declarative.annotation.McpPrompts;
56
import com.github.codeboyzhou.mcp.declarative.annotation.McpResources;
67
import com.github.codeboyzhou.mcp.declarative.annotation.McpTools;
@@ -10,6 +11,7 @@
1011
import com.google.inject.AbstractModule;
1112
import com.google.inject.Provides;
1213
import com.google.inject.Singleton;
14+
import com.google.inject.name.Names;
1315
import org.reflections.Reflections;
1416

1517
import static com.google.inject.Scopes.SINGLETON;
@@ -19,6 +21,8 @@
1921

2022
public final class GuiceInjectorModule extends AbstractModule {
2123

24+
public static final String VARIABLE_NAME_I18N_ENABLED = "i18nEnabled";
25+
2226
private final Class<?> applicationMainClass;
2327

2428
public GuiceInjectorModule(Class<?> applicationMainClass) {
@@ -41,10 +45,15 @@ protected void configure() {
4145
reflections.getTypesAnnotatedWith(McpResources.class).forEach(clazz -> bind(clazz).in(SINGLETON));
4246
reflections.getTypesAnnotatedWith(McpPrompts.class).forEach(clazz -> bind(clazz).in(SINGLETON));
4347
reflections.getTypesAnnotatedWith(McpTools.class).forEach(clazz -> bind(clazz).in(SINGLETON));
48+
4449
// Bind all implementations of McpServerComponentFactory
4550
bind(McpServerResourceFactory.class).in(SINGLETON);
4651
bind(McpServerPromptFactory.class).in(SINGLETON);
4752
bind(McpServerToolFactory.class).in(SINGLETON);
53+
54+
// Bind for boolean variable: i18nEnabled
55+
final boolean i18nEnabled = applicationMainClass.isAnnotationPresent(McpI18nEnabled.class);
56+
bind(Boolean.class).annotatedWith(Names.named(VARIABLE_NAME_I18N_ENABLED)).toInstance(i18nEnabled);
4857
}
4958

5059
private String determineBasePackage(McpComponentScan scan, Class<?> applicationMainClass) {

src/main/java/com/github/codeboyzhou/mcp/declarative/server/factory/AbstractMcpServerComponentFactory.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,41 @@
11
package com.github.codeboyzhou.mcp.declarative.server.factory;
22

3+
import com.github.codeboyzhou.mcp.declarative.util.StringHelper;
34
import com.google.inject.Injector;
5+
import com.google.inject.name.Named;
46
import org.slf4j.Logger;
57
import org.slf4j.LoggerFactory;
68

79
import java.util.Locale;
810
import java.util.ResourceBundle;
911

12+
import static com.github.codeboyzhou.mcp.declarative.common.GuiceInjectorModule.VARIABLE_NAME_I18N_ENABLED;
13+
1014
public abstract class AbstractMcpServerComponentFactory<T> implements McpServerComponentFactory<T> {
1115

1216
private static final Logger logger = LoggerFactory.getLogger(AbstractMcpServerComponentFactory.class);
1317

1418
private static final String RESOURCE_BUNDLE_BASE_NAME = "i18n/mcp_server_component_descriptions";
1519

16-
protected static final String NO_TITLE_SPECIFIED = "No title specified";
20+
protected static final String NOT_SPECIFIED = "Not Specified";
1721

1822
protected final Injector injector;
1923

24+
protected final Boolean i18nEnabled;
25+
2026
private final ResourceBundle bundle;
2127

22-
protected AbstractMcpServerComponentFactory(Injector injector) {
28+
protected AbstractMcpServerComponentFactory(Injector injector, @Named(VARIABLE_NAME_I18N_ENABLED) Boolean i18nEnabled) {
2329
this.injector = injector;
30+
this.i18nEnabled = i18nEnabled;
2431
this.bundle = loadResourceBundle();
2532
}
2633

27-
protected String getDescription(String descriptionI18nKey, String description) {
28-
if (!descriptionI18nKey.isBlank() && bundle != null && bundle.containsKey(descriptionI18nKey)) {
29-
return bundle.getString(descriptionI18nKey);
30-
}
31-
if (!description.isBlank()) {
32-
return description;
34+
protected String resolveComponentAttributeValue(String attributeLiteralValue) {
35+
if (i18nEnabled && bundle != null && bundle.containsKey(attributeLiteralValue)) {
36+
return bundle.getString(attributeLiteralValue);
3337
}
34-
return "No description provided.";
38+
return StringHelper.defaultIfBlank(attributeLiteralValue, NOT_SPECIFIED);
3539
}
3640

3741
private ResourceBundle loadResourceBundle() {

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.github.codeboyzhou.mcp.declarative.util.TypeConverter;
1010
import com.google.inject.Inject;
1111
import com.google.inject.Injector;
12+
import com.google.inject.name.Named;
1213
import io.modelcontextprotocol.server.McpAsyncServer;
1314
import io.modelcontextprotocol.server.McpServerFeatures;
1415
import io.modelcontextprotocol.spec.McpSchema;
@@ -26,21 +27,23 @@
2627
import java.util.Set;
2728
import java.util.stream.Stream;
2829

30+
import static com.github.codeboyzhou.mcp.declarative.common.GuiceInjectorModule.VARIABLE_NAME_I18N_ENABLED;
31+
2932
public class McpServerPromptFactory extends AbstractMcpServerComponentFactory<McpServerFeatures.AsyncPromptSpecification> {
3033

3134
private static final Logger logger = LoggerFactory.getLogger(McpServerPromptFactory.class);
3235

3336
@Inject
34-
protected McpServerPromptFactory(Injector injector) {
35-
super(injector);
37+
protected McpServerPromptFactory(Injector injector, @Named(VARIABLE_NAME_I18N_ENABLED) Boolean i18nEnabled) {
38+
super(injector, i18nEnabled);
3639
}
3740

3841
@Override
3942
public McpServerFeatures.AsyncPromptSpecification create(Class<?> clazz, Method method) {
4043
McpPrompt promptMethod = method.getAnnotation(McpPrompt.class);
4144
final String name = StringHelper.defaultIfBlank(promptMethod.name(), method.getName());
42-
final String title = StringHelper.defaultIfBlank(promptMethod.title(), NO_TITLE_SPECIFIED);
43-
final String description = getDescription(promptMethod.descriptionI18nKey(), promptMethod.description());
45+
final String title = resolveComponentAttributeValue(promptMethod.title());
46+
final String description = resolveComponentAttributeValue(promptMethod.description());
4447
List<McpSchema.PromptArgument> promptArguments = createPromptArguments(method);
4548
McpSchema.Prompt prompt = new McpSchema.Prompt(name, title, description, promptArguments);
4649
logger.debug("Registering prompt: {}", JsonHelper.toJson(prompt));
@@ -85,8 +88,8 @@ private List<McpSchema.PromptArgument> createPromptArguments(Method method) {
8588
for (Parameter param : params) {
8689
McpPromptParam promptParam = param.getAnnotation(McpPromptParam.class);
8790
final String name = promptParam.name();
88-
final String title = StringHelper.defaultIfBlank(promptParam.title(), NO_TITLE_SPECIFIED);
89-
final String description = getDescription(promptParam.descriptionI18nKey(), promptParam.description());
91+
final String title = resolveComponentAttributeValue(promptParam.title());
92+
final String description = resolveComponentAttributeValue(promptParam.description());
9093
final boolean required = promptParam.required();
9194
McpSchema.PromptArgument promptArgument = new McpSchema.PromptArgument(name, title, description, required);
9295
promptArguments.add(promptArgument);

src/main/java/com/github/codeboyzhou/mcp/declarative/server/factory/McpServerResourceFactory.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.github.codeboyzhou.mcp.declarative.util.StringHelper;
88
import com.google.inject.Inject;
99
import com.google.inject.Injector;
10+
import com.google.inject.name.Named;
1011
import io.modelcontextprotocol.server.McpAsyncServer;
1112
import io.modelcontextprotocol.server.McpServerFeatures;
1213
import io.modelcontextprotocol.spec.McpSchema;
@@ -19,21 +20,23 @@
1920
import java.util.List;
2021
import java.util.Set;
2122

23+
import static com.github.codeboyzhou.mcp.declarative.common.GuiceInjectorModule.VARIABLE_NAME_I18N_ENABLED;
24+
2225
public class McpServerResourceFactory extends AbstractMcpServerComponentFactory<McpServerFeatures.AsyncResourceSpecification> {
2326

2427
private static final Logger logger = LoggerFactory.getLogger(McpServerResourceFactory.class);
2528

2629
@Inject
27-
protected McpServerResourceFactory(Injector injector) {
28-
super(injector);
30+
protected McpServerResourceFactory(Injector injector, @Named(VARIABLE_NAME_I18N_ENABLED) Boolean i18nEnabled) {
31+
super(injector, i18nEnabled);
2932
}
3033

3134
@Override
3235
public McpServerFeatures.AsyncResourceSpecification create(Class<?> clazz, Method method) {
3336
McpResource res = method.getAnnotation(McpResource.class);
3437
final String name = StringHelper.defaultIfBlank(res.name(), method.getName());
35-
final String title = StringHelper.defaultIfBlank(res.title(), NO_TITLE_SPECIFIED);
36-
final String description = getDescription(res.descriptionI18nKey(), res.description());
38+
final String title = resolveComponentAttributeValue(res.title());
39+
final String description = resolveComponentAttributeValue(res.description());
3740
McpSchema.Annotations annotations = new McpSchema.Annotations(List.of(res.roles()), res.priority());
3841
McpSchema.Resource resource = new McpSchema.Resource(res.uri(), name, title, description, res.mimeType(), null, annotations);
3942
logger.debug("Registering resource: {}", JsonHelper.toJson(resource));

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.github.codeboyzhou.mcp.declarative.util.TypeConverter;
1313
import com.google.inject.Inject;
1414
import com.google.inject.Injector;
15+
import com.google.inject.name.Named;
1516
import io.modelcontextprotocol.server.McpAsyncServer;
1617
import io.modelcontextprotocol.server.McpServerFeatures;
1718
import io.modelcontextprotocol.spec.McpSchema;
@@ -31,21 +32,23 @@
3132
import java.util.Set;
3233
import java.util.stream.Stream;
3334

35+
import static com.github.codeboyzhou.mcp.declarative.common.GuiceInjectorModule.VARIABLE_NAME_I18N_ENABLED;
36+
3437
public class McpServerToolFactory extends AbstractMcpServerComponentFactory<McpServerFeatures.AsyncToolSpecification> {
3538

3639
private static final Logger logger = LoggerFactory.getLogger(McpServerToolFactory.class);
3740

3841
@Inject
39-
protected McpServerToolFactory(Injector injector) {
40-
super(injector);
42+
protected McpServerToolFactory(Injector injector, @Named(VARIABLE_NAME_I18N_ENABLED) Boolean i18nEnabled) {
43+
super(injector, i18nEnabled);
4144
}
4245

4346
@Override
4447
public McpServerFeatures.AsyncToolSpecification create(Class<?> clazz, Method method) {
4548
McpTool toolMethod = method.getAnnotation(McpTool.class);
4649
final String name = StringHelper.defaultIfBlank(toolMethod.name(), method.getName());
47-
final String title = StringHelper.defaultIfBlank(toolMethod.title(), NO_TITLE_SPECIFIED);
48-
final String description = getDescription(toolMethod.descriptionI18nKey(), toolMethod.description());
50+
final String title = resolveComponentAttributeValue(toolMethod.title());
51+
final String description = resolveComponentAttributeValue(toolMethod.description());
4952
McpSchema.JsonSchema paramSchema = createJsonSchema(method);
5053
McpSchema.Tool tool = McpSchema.Tool.builder().name(name).title(title).description(description).inputSchema(paramSchema).build();
5154
logger.debug("Registering tool: {}", JsonHelper.toJson(tool));
@@ -100,7 +103,7 @@ private McpSchema.JsonSchema createJsonSchema(Method method) {
100103

101104
if (parameterType.getAnnotation(McpJsonSchemaDefinition.class) == null) {
102105
property.put("type", parameterType.getSimpleName().toLowerCase());
103-
property.put("description", getDescription(toolParam.descriptionI18nKey(), toolParam.description()));
106+
property.put("description", resolveComponentAttributeValue(toolParam.description()));
104107
} else {
105108
final String parameterTypeSimpleName = parameterType.getSimpleName();
106109
property.put("$ref", "#/definitions/" + parameterTypeSimpleName);
@@ -137,7 +140,7 @@ private Map<String, Object> createJsonSchemaDefinition(Class<?> definitionClass)
137140

138141
Map<String, Object> fieldProperties = new HashMap<>();
139142
fieldProperties.put("type", field.getType().getSimpleName().toLowerCase());
140-
fieldProperties.put("description", getDescription(property.descriptionI18nKey(), property.description()));
143+
fieldProperties.put("description", resolveComponentAttributeValue(property.description()));
141144

142145
final String fieldName = StringHelper.defaultIfBlank(property.name(), field.getName());
143146
properties.put(fieldName, fieldProperties);

0 commit comments

Comments
 (0)