Skip to content

Commit 614ccb8

Browse files
committed
refactor(configuration): Update YAMLConfigurationLoader and add some tests
1 parent 22d2bb9 commit 614ccb8

14 files changed

+228
-33
lines changed

src/main/java/com/github/codeboyzhou/mcp/declarative/McpServers.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.github.codeboyzhou.mcp.declarative.common.GuiceInjectorModule;
44
import com.github.codeboyzhou.mcp.declarative.configuration.McpServerConfiguration;
55
import com.github.codeboyzhou.mcp.declarative.configuration.YAMLConfigurationLoader;
6-
import com.github.codeboyzhou.mcp.declarative.enums.ServerMode;
76
import com.github.codeboyzhou.mcp.declarative.server.McpServerInfo;
87
import com.github.codeboyzhou.mcp.declarative.server.component.McpServerPromptFactory;
98
import com.github.codeboyzhou.mcp.declarative.server.component.McpServerResourceFactory;
@@ -76,17 +75,16 @@ private void doStartServer(McpServerConfiguration configuration) {
7675
return;
7776
}
7877

79-
AbstractConfigurableMcpServerFactory factory;
80-
final String mode = configuration.mode().name();
78+
AbstractConfigurableMcpServerFactory factory =
79+
switch (configuration.mode()) {
80+
case STDIO -> new ConfigurableMcpStdioServerFactory(configuration);
81+
case SSE -> new ConfigurableMcpSseServerFactory(configuration);
82+
case STREAMABLE -> new ConfigurableMcpStreamableServerFactory(configuration);
83+
};
8184

82-
if (configuration.stdio() || ServerMode.STDIO.name().equalsIgnoreCase(mode)) {
85+
// Ensure backward compatibility
86+
if (configuration.stdio()) {
8387
factory = new ConfigurableMcpStdioServerFactory(configuration);
84-
} else if (ServerMode.SSE.name().equalsIgnoreCase(mode)) {
85-
factory = new ConfigurableMcpSseServerFactory(configuration);
86-
} else if (ServerMode.STREAMABLE.name().equalsIgnoreCase(mode)) {
87-
factory = new ConfigurableMcpStreamableServerFactory(configuration);
88-
} else {
89-
throw new NullPointerException("factory is null, please check your configuration");
9088
}
9189

9290
McpSyncServer server = factory.create();
Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,29 @@
11
package com.github.codeboyzhou.mcp.declarative.configuration;
22

3-
import com.github.codeboyzhou.mcp.declarative.exception.McpServerException;
3+
import com.github.codeboyzhou.mcp.declarative.exception.McpServerConfigurationException;
44
import com.github.codeboyzhou.mcp.declarative.util.ObjectMappers;
5+
import java.io.File;
56
import java.net.URISyntaxException;
67
import java.net.URL;
78
import java.nio.file.Path;
89
import java.nio.file.Paths;
910
import org.slf4j.Logger;
1011
import org.slf4j.LoggerFactory;
1112

12-
public final class YAMLConfigurationLoader {
13+
public record YAMLConfigurationLoader(String configFileName) {
1314

1415
private static final Logger logger = LoggerFactory.getLogger(YAMLConfigurationLoader.class);
1516

16-
private static final String CONFIG_FILE_NAME = "mcp-server.yml";
17-
18-
private final String configFileName;
19-
20-
public YAMLConfigurationLoader(String configFileName) {
21-
this.configFileName = configFileName;
22-
}
17+
private static final String DEFAULT_CONFIG_FILE_NAME = "mcp-server.yml";
2318

2419
public YAMLConfigurationLoader() {
25-
this(CONFIG_FILE_NAME);
20+
this(DEFAULT_CONFIG_FILE_NAME);
2621
}
2722

2823
public McpServerConfiguration loadConfig() {
2924
Path configFilePath = getConfigFilePath(configFileName);
30-
McpServerConfiguration config =
31-
ObjectMappers.fromYaml(configFilePath.toFile(), McpServerConfiguration.class);
25+
File file = configFilePath.toFile();
26+
McpServerConfiguration config = ObjectMappers.fromYaml(file, McpServerConfiguration.class);
3227
logger.info("Configuration loaded successfully from file: {}", configFileName);
3328
return config;
3429
}
@@ -38,12 +33,12 @@ private Path getConfigFilePath(String fileName) {
3833
ClassLoader classLoader = YAMLConfigurationLoader.class.getClassLoader();
3934
URL configFileUrl = classLoader.getResource(fileName);
4035
if (configFileUrl == null) {
41-
throw new McpServerException("Configuration file not found: " + fileName);
36+
throw new McpServerConfigurationException("Configuration file not found: " + fileName);
4237
}
4338
return Paths.get(configFileUrl.toURI());
4439
} catch (URISyntaxException e) {
4540
// should never happen
46-
throw new McpServerException("Invalid configuration file path: " + fileName, e);
41+
throw new McpServerConfigurationException("Invalid configuration file: " + fileName, e);
4742
}
4843
}
4944
}
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.exception;
2+
3+
public class McpServerConfigurationException extends McpServerException {
4+
public McpServerConfigurationException(String message) {
5+
super(message);
6+
}
7+
8+
public McpServerConfigurationException(String message, Throwable cause) {
9+
super(message, cause);
10+
}
11+
}
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.exception;
2+
3+
public class McpServerJsonProcessingException extends McpServerException {
4+
public McpServerJsonProcessingException(String message) {
5+
super(message);
6+
}
7+
8+
public McpServerJsonProcessingException(String message, Throwable cause) {
9+
super(message, cause);
10+
}
11+
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.github.codeboyzhou.mcp.declarative.configuration.McpServerConfiguration;
44
import com.github.codeboyzhou.mcp.declarative.server.McpHttpServer;
5+
import com.github.codeboyzhou.mcp.declarative.util.ObjectMappers;
56
import io.modelcontextprotocol.server.McpServer;
67
import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider;
78

@@ -14,7 +15,9 @@ public ConfigurableMcpStreamableServerFactory(McpServerConfiguration configurati
1415
@Override
1516
public McpServer.SyncSpecification<?> specification() {
1617
HttpServletStreamableServerTransportProvider transportProvider =
17-
HttpServletStreamableServerTransportProvider.builder().build();
18+
HttpServletStreamableServerTransportProvider.builder()
19+
.objectMapper(ObjectMappers.JSON_MAPPER)
20+
.build();
1821
threadPool.execute(() -> new McpHttpServer().use(transportProvider).bind(8080).start());
1922
return McpServer.sync(transportProvider);
2023
}

src/main/java/com/github/codeboyzhou/mcp/declarative/util/ObjectMappers.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
import com.fasterxml.jackson.core.JsonProcessingException;
55
import com.fasterxml.jackson.databind.ObjectMapper;
66
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
7-
import com.github.codeboyzhou.mcp.declarative.exception.McpServerException;
7+
import com.github.codeboyzhou.mcp.declarative.exception.McpServerConfigurationException;
8+
import com.github.codeboyzhou.mcp.declarative.exception.McpServerJsonProcessingException;
89
import java.io.File;
910
import java.io.IOException;
1011
import org.jetbrains.annotations.VisibleForTesting;
@@ -24,15 +25,16 @@ public static String toJson(Object object) {
2425
try {
2526
return JSON_MAPPER.writeValueAsString(object);
2627
} catch (JsonProcessingException e) {
27-
throw new McpServerException("Error converting object to JSON", e);
28+
throw new McpServerJsonProcessingException("Error converting object to JSON", e);
2829
}
2930
}
3031

3132
public static <T> T fromYaml(File yamlFile, Class<T> valueType) {
3233
try {
3334
return YAML_MAPPER.readValue(yamlFile, valueType);
3435
} catch (IOException e) {
35-
throw new McpServerException("Error reading YAML file: " + yamlFile.getAbsolutePath(), e);
36+
final String path = yamlFile.getAbsolutePath();
37+
throw new McpServerConfigurationException("Error reading YAML file: " + path, e);
3638
}
3739
}
3840
}

src/test/java/com/github/codeboyzhou/mcp/declarative/McpServersTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
package com.github.codeboyzhou.mcp.declarative;
22

3+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
34
import static org.junit.jupiter.api.Assertions.assertEquals;
5+
import static org.junit.jupiter.api.Assertions.assertFalse;
6+
import static org.junit.jupiter.api.Assertions.assertSame;
7+
import static org.junit.jupiter.api.Assertions.assertThrows;
48
import static org.junit.jupiter.api.Assertions.assertTrue;
59

10+
import com.github.codeboyzhou.mcp.declarative.configuration.McpServerConfiguration;
11+
import com.github.codeboyzhou.mcp.declarative.configuration.YAMLConfigurationLoader;
12+
import com.github.codeboyzhou.mcp.declarative.enums.ServerMode;
13+
import com.github.codeboyzhou.mcp.declarative.exception.McpServerConfigurationException;
614
import com.github.codeboyzhou.mcp.declarative.server.factory.McpSseServerInfo;
715
import com.github.codeboyzhou.mcp.declarative.server.factory.McpStreamableServerInfo;
816
import com.github.codeboyzhou.mcp.declarative.test.TestSimpleMcpStdioServer;
@@ -94,6 +102,57 @@ void testStartStreamableServer_shouldSucceed() {
94102
}
95103
}
96104

105+
@Test
106+
void testStartServer_disabledMCP_shouldSucceed() {
107+
String configFileName = "test-mcp-server-disabled.yml";
108+
YAMLConfigurationLoader configLoader = new YAMLConfigurationLoader(configFileName);
109+
McpServerConfiguration configuration = configLoader.loadConfig();
110+
assertDoesNotThrow(() -> servers.startServer(configFileName));
111+
assertFalse(configuration.enabled());
112+
}
113+
114+
@Test
115+
void testStartServer_enableStdioMode_shouldSucceed() {
116+
String configFileName = "test-mcp-server-enable-stdio-mode.yml";
117+
YAMLConfigurationLoader configLoader = new YAMLConfigurationLoader(configFileName);
118+
McpServerConfiguration configuration = configLoader.loadConfig();
119+
assertDoesNotThrow(() -> servers.startServer(configFileName));
120+
assertSame(ServerMode.STDIO, configuration.mode());
121+
}
122+
123+
@Test
124+
void testStartServer_enableHttpSseMode_shouldSucceed() {
125+
String configFileName = "test-mcp-server-enable-http-sse-mode.yml";
126+
YAMLConfigurationLoader configLoader = new YAMLConfigurationLoader(configFileName);
127+
McpServerConfiguration configuration = configLoader.loadConfig();
128+
assertDoesNotThrow(() -> servers.startServer(configFileName));
129+
assertSame(ServerMode.SSE, configuration.mode());
130+
}
131+
132+
@Test
133+
void testStartServer_enableStreamableHttpMode_shouldSucceed() {
134+
String configFileName = "test-mcp-server-enable-streamable-http-mode.yml";
135+
YAMLConfigurationLoader configLoader = new YAMLConfigurationLoader(configFileName);
136+
McpServerConfiguration configuration = configLoader.loadConfig();
137+
assertDoesNotThrow(() -> servers.startServer(configFileName));
138+
assertSame(ServerMode.STREAMABLE, configuration.mode());
139+
}
140+
141+
@Test
142+
void testStartServer_enableUnknownMode_shouldThrowException() {
143+
String configFileName = "test-mcp-server-enable-unknown-mode.yml";
144+
assertThrows(McpServerConfigurationException.class, () -> servers.startServer(configFileName));
145+
}
146+
147+
@Test
148+
void testStartServer_useDefaultConfigFileName_shouldSucceed() {
149+
String configFileName = "mcp-server.yml";
150+
YAMLConfigurationLoader configLoader = new YAMLConfigurationLoader(configFileName);
151+
McpServerConfiguration configuration = configLoader.loadConfig();
152+
assertSame(ServerMode.STREAMABLE, configuration.mode());
153+
assertDoesNotThrow(() -> servers.startServer());
154+
}
155+
97156
private void verify(McpSyncClient client) {
98157
verifyServerInfo(client);
99158
verifyResourcesRegistered(client);

src/test/java/com/github/codeboyzhou/mcp/declarative/util/ObjectMappersTest.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
import static org.junit.jupiter.api.Assertions.assertThrows;
55
import static org.junit.jupiter.api.Assertions.assertTrue;
66

7-
import com.github.codeboyzhou.mcp.declarative.exception.McpServerException;
7+
import com.github.codeboyzhou.mcp.declarative.exception.McpServerConfigurationException;
8+
import com.github.codeboyzhou.mcp.declarative.exception.McpServerJsonProcessingException;
89
import java.io.File;
910
import java.io.FileWriter;
1011
import java.io.IOException;
@@ -32,8 +33,9 @@ void testToJson_shouldSucceed() {
3233
}
3334

3435
@Test
35-
void testToJson_shouldThrowException() throws Exception {
36-
assertThrows(McpServerException.class, () -> ObjectMappers.toJson(new CircularReference()));
36+
void testToJson_shouldThrowException() {
37+
CircularReference circularRef = new CircularReference();
38+
assertThrows(McpServerJsonProcessingException.class, () -> ObjectMappers.toJson(circularRef));
3739
}
3840

3941
@Test
@@ -50,7 +52,7 @@ void testFromYaml_shouldSucceed() throws IOException {
5052

5153
@Test
5254
void testFromYaml_shouldThrowException() {
53-
File file = new File("non-existent.yaml");
54-
assertThrows(McpServerException.class, () -> ObjectMappers.fromYaml(file, Map.class));
55+
File f = new File("non-existent.yaml");
56+
assertThrows(McpServerConfigurationException.class, () -> ObjectMappers.fromYaml(f, Map.class));
5557
}
5658
}

src/test/resources/mcp-server.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
enabled: true
2+
mode: STREAMABLE
3+
name: mcp-server
4+
version: 1.0.0
5+
type: SYNC
6+
request-timeout: 20000
7+
capabilities:
8+
resource: true
9+
prompt: true
10+
tool: true
11+
change-notification:
12+
resource: true
13+
prompt: true
14+
tool: true
15+
sse:
16+
message-endpoint: /mcp/message
17+
endpoint: /sse
18+
base-url: http://localhost:8080
19+
port: 8080
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
enabled: false
2+
mode: STREAMABLE
3+
name: mcp-server
4+
version: 1.0.0
5+
type: SYNC
6+
request-timeout: 20000
7+
capabilities:
8+
resource: true
9+
prompt: true
10+
tool: true
11+
change-notification:
12+
resource: true
13+
prompt: true
14+
tool: true
15+
sse:
16+
message-endpoint: /mcp/message
17+
endpoint: /sse
18+
base-url: http://localhost:8080
19+
port: 8080

0 commit comments

Comments
 (0)