Skip to content

Commit 03cd5f2

Browse files
refactor: simplify McpConfigurationChecker structure and error keys.
Split validation into focused helpers, prefix nested config keys for clearer errors, and remove unused merge-era check overloads. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent b3e9874 commit 03cd5f2

2 files changed

Lines changed: 48 additions & 115 deletions

File tree

src/main/java/com/github/thought2code/mcp/annotated/configuration/McpConfigurationChecker.java

Lines changed: 42 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,29 @@
1616
* @author codeboyzhou
1717
*/
1818
public final class McpConfigurationChecker {
19-
/**
20-
* Private constructor to prevent instantiation of this utility class.
21-
*
22-
* @throws UnsupportedOperationException always thrown when attempting to instantiate
23-
*/
19+
2420
private McpConfigurationChecker() {
2521
throw new UnsupportedOperationException("Utility class should not be instantiated");
2622
}
2723

2824
/**
2925
* Performs comprehensive validation of the MCP server configuration.
3026
*
31-
* <p>This method validates all required configuration properties including:
32-
*
33-
* <ul>
34-
* <li>Basic server information (enabled, mode, name, version, type, instructions)
35-
* <li>Timeout and capabilities settings
36-
* <li>Change notification configuration
37-
* <li>Mode-specific settings (SSE or STREAMABLE)
38-
* </ul>
39-
*
4027
* @param configuration the MCP server configuration to validate
4128
* @throws McpServerConfigurationException if any required configuration property is missing
4229
*/
4330
public static void check(McpServerConfiguration configuration) {
31+
checkCore(configuration);
32+
checkCapabilities(configuration.capabilities());
33+
checkChangeNotification(configuration.changeNotification());
34+
switch (configuration.mode()) {
35+
case SSE -> checkSse(configuration.sse());
36+
case STREAMABLE -> checkStreamable(configuration.streamable());
37+
case STDIO -> {}
38+
}
39+
}
40+
41+
private static void checkCore(McpServerConfiguration configuration) {
4442
checkNull("enabled", configuration.enabled());
4543
checkNull("mode", configuration.mode());
4644
checkBlank("name", configuration.name());
@@ -49,114 +47,56 @@ public static void check(McpServerConfiguration configuration) {
4947
checkBlank("instructions", configuration.instructions());
5048
checkNull("request-timeout", configuration.requestTimeout());
5149
checkNull("capabilities", configuration.capabilities());
52-
checkNull("resource", configuration.capabilities().resource());
53-
if (configuration.capabilities().resource()) {
54-
checkNull("subscribe-resource", configuration.capabilities().subscribeResource());
55-
}
56-
checkNull("prompt", configuration.capabilities().prompt());
57-
checkNull("tool", configuration.capabilities().tool());
58-
checkNull("completion", configuration.capabilities().completion());
5950
checkNull("change-notification", configuration.changeNotification());
60-
checkNull("resource", configuration.changeNotification().resource());
61-
checkNull("prompt", configuration.changeNotification().prompt());
62-
checkNull("tool", configuration.changeNotification().tool());
63-
if (configuration.mode() == ServerMode.SSE) {
64-
checkSse(configuration);
65-
}
66-
if (configuration.mode() == ServerMode.STREAMABLE) {
67-
checkNull("streamable", configuration.streamable());
68-
checkBlank("mcp-endpoint", configuration.streamable().mcpEndpoint());
69-
checkNull("disallow-delete", configuration.streamable().disallowDelete());
70-
checkNull("keep-alive-interval", configuration.streamable().keepAliveInterval());
71-
checkNull("port", configuration.streamable().port());
51+
}
52+
53+
private static void checkCapabilities(McpServerCapabilities capabilities) {
54+
checkNull("capabilities.resource", capabilities.resource());
55+
if (capabilities.resource()) {
56+
checkNull("capabilities.subscribe-resource", capabilities.subscribeResource());
7257
}
58+
checkNull("capabilities.prompt", capabilities.prompt());
59+
checkNull("capabilities.tool", capabilities.tool());
60+
checkNull("capabilities.completion", capabilities.completion());
61+
}
62+
63+
private static void checkChangeNotification(McpServerChangeNotification changeNotification) {
64+
checkNull("change-notification.resource", changeNotification.resource());
65+
checkNull("change-notification.prompt", changeNotification.prompt());
66+
checkNull("change-notification.tool", changeNotification.tool());
7367
}
7468

7569
/**
7670
* Validates SSE-specific configuration properties.
7771
*
78-
* @param configuration the MCP server configuration to validate
72+
* @param sse SSE configuration to validate
7973
* @deprecated HTTP SSE mode is deprecated; use {@link ServerMode#STREAMABLE} instead.
8074
*/
8175
@Deprecated(since = "0.16.0", forRemoval = true)
82-
private static void checkSse(McpServerConfiguration configuration) {
83-
checkNull("sse", configuration.sse());
84-
checkBlank("message-endpoint", configuration.sse().messageEndpoint());
85-
checkBlank("endpoint", configuration.sse().endpoint());
86-
checkBlank("base-url", configuration.sse().baseUrl());
87-
checkNull("port", configuration.sse().port());
76+
private static void checkSse(McpServerSSE sse) {
77+
checkNull("sse", sse);
78+
checkBlank("sse.message-endpoint", sse.messageEndpoint());
79+
checkBlank("sse.endpoint", sse.endpoint());
80+
checkBlank("sse.base-url", sse.baseUrl());
81+
checkNull("sse.port", sse.port());
8882
}
8983

90-
/**
91-
* Validates that at least one of two configuration values is not null.
92-
*
93-
* <p>This method is typically used to validate profile-based configurations where a base value
94-
* and a profile-specific value can both provide the same configuration property. At least one of
95-
* them must be non-null.
96-
*
97-
* @param <T> the type of the configuration values
98-
* @param configKey the configuration key name used for error reporting
99-
* @param baseValue the base configuration value
100-
* @param profileValue the profile-specific configuration value
101-
* @throws McpServerConfigurationException if both values are null
102-
*/
103-
public static <T> void checkNull(String configKey, T baseValue, T profileValue) {
104-
if (baseValue == null && profileValue == null) {
105-
throw new McpServerConfigurationException(
106-
String.format("Missing config key '%s' in the configuration file.", configKey));
107-
}
84+
private static void checkStreamable(McpServerStreamable streamable) {
85+
checkNull("streamable", streamable);
86+
checkBlank("streamable.mcp-endpoint", streamable.mcpEndpoint());
87+
checkNull("streamable.disallow-delete", streamable.disallowDelete());
88+
checkNull("streamable.keep-alive-interval", streamable.keepAliveInterval());
89+
checkNull("streamable.port", streamable.port());
10890
}
10991

110-
/**
111-
* Validates that a single configuration value is not null.
112-
*
113-
* <p>This method performs a simple null check on a configuration value and throws an exception if
114-
* the value is null, indicating that a required configuration property is missing.
115-
*
116-
* @param <T> the type of the configuration value
117-
* @param configKey the configuration key name used for error reporting
118-
* @param value the configuration value to validate
119-
* @throws McpServerConfigurationException if the value is null
120-
*/
121-
public static <T> void checkNull(String configKey, T value) {
92+
private static <T> void checkNull(String configKey, T value) {
12293
if (value == null) {
12394
throw new McpServerConfigurationException(
12495
String.format("Missing config key '%s' in the configuration file.", configKey));
12596
}
12697
}
12798

128-
/**
129-
* Validates that at least one of two string configuration values is not blank.
130-
*
131-
* <p>This method is typically used to validate profile-based configurations where a base value
132-
* and a profile-specific value can both provide the same string configuration property. At least
133-
* one of them must be non-blank. A value is considered blank if it is null, empty, or contains
134-
* only whitespace.
135-
*
136-
* @param configKey the configuration key name used for error reporting
137-
* @param baseValue the base configuration string value
138-
* @param profileValue the profile-specific configuration string value
139-
* @throws McpServerConfigurationException if both values are blank
140-
*/
141-
public static void checkBlank(String configKey, String baseValue, String profileValue) {
142-
if (StringHelper.isBlank(baseValue) && StringHelper.isBlank(profileValue)) {
143-
throw new McpServerConfigurationException(
144-
String.format("Missing config key '%s' in the configuration file.", configKey));
145-
}
146-
}
147-
148-
/**
149-
* Validates that a single string configuration value is not blank.
150-
*
151-
* <p>This method performs a blank check on a string configuration value and throws an exception
152-
* if the value is blank. A value is considered blank if it is null, empty, or contains only
153-
* whitespace.
154-
*
155-
* @param configKey the configuration key name used for error reporting
156-
* @param value the configuration string value to validate
157-
* @throws McpServerConfigurationException if the value is blank
158-
*/
159-
public static void checkBlank(String configKey, String value) {
99+
private static void checkBlank(String configKey, String value) {
160100
if (StringHelper.isBlank(value)) {
161101
throw new McpServerConfigurationException(
162102
String.format("Missing config key '%s' in the configuration file.", configKey));

src/test/java/com/github/thought2code/mcp/annotated/configuration/McpConfigurationCheckerTest.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ void check_shouldRejectMissingSubscribeResourceWhenResourceEnabled() {
5656
assertThrows(
5757
McpServerConfigurationException.class,
5858
() -> McpConfigurationChecker.check(configuration));
59-
assertTrue(exception.getMessage().contains("subscribe-resource"));
59+
assertTrue(exception.getMessage().contains("capabilities.subscribe-resource"));
6060
}
6161

6262
@Test
@@ -72,20 +72,13 @@ void check_shouldRejectMissingSseSettingsWhenModeIsSse() {
7272
}
7373

7474
@Test
75-
void checkNull_shouldRejectWhenBothValuesAreNull() {
76-
McpServerConfigurationException exception =
77-
assertThrows(
78-
McpServerConfigurationException.class,
79-
() -> McpConfigurationChecker.checkNull("mode", null, null));
80-
assertTrue(exception.getMessage().contains("mode"));
81-
}
82-
83-
@Test
84-
void checkBlank_shouldRejectWhenBothValuesAreBlank() {
75+
void check_shouldRejectMissingStreamableSettingsWhenModeIsStreamable() {
76+
McpServerConfiguration configuration =
77+
TestMcpConfigurations.baseBuilder().mode(ServerMode.STREAMABLE).streamable(null).build();
8578
McpServerConfigurationException exception =
8679
assertThrows(
8780
McpServerConfigurationException.class,
88-
() -> McpConfigurationChecker.checkBlank("name", " ", null));
89-
assertTrue(exception.getMessage().contains("name"));
81+
() -> McpConfigurationChecker.check(configuration));
82+
assertTrue(exception.getMessage().contains("streamable"));
9083
}
9184
}

0 commit comments

Comments
 (0)