Skip to content

Commit 8b303cc

Browse files
refactor: harden component registration diagnostics and log templates
Unify duplicate-component error messaging across compile-time and runtime paths, and centralize generated invoker failure log templates to improve observability without exposing internal exceptions to clients. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 562c12c commit 8b303cc

8 files changed

Lines changed: 121 additions & 0 deletions

File tree

src/main/java/com/github/thought2code/mcp/annotated/server/component/AnnotationProcessor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,8 @@ private void writeProvider() throws IOException {
350350
"import com.github.thought2code.mcp.annotated.server.component.resource.ResourceInvoker;\n");
351351
writer.write(
352352
"import com.github.thought2code.mcp.annotated.server.component.ComponentProvider;\n");
353+
writer.write(
354+
"import com.github.thought2code.mcp.annotated.server.component.InvocationLogMessageHelper;\n");
353355
writer.write(
354356
"import com.github.thought2code.mcp.annotated.server.component.tool.ToolDefinition;\n");
355357
writer.write(
@@ -363,7 +365,10 @@ private void writeProvider() throws IOException {
363365
writer.write("import java.util.LinkedHashMap;\n");
364366
writer.write("import java.util.List;\n");
365367
writer.write("import java.util.Map;\n\n");
368+
writer.write("import org.slf4j.Logger;\n");
369+
writer.write("import org.slf4j.LoggerFactory;\n\n");
366370
writer.write("public final class " + className + " implements ComponentProvider {\n\n");
371+
writer.write(" private static final Logger log = LoggerFactory.getLogger(" + className + ".class);\n\n");
367372

368373
writer.write(" @Override\n");
369374
writer.write(" public List<ToolDefinition> tools() {\n");
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.github.thought2code.mcp.annotated.server.component;
2+
3+
/** Shared invocation failure log-message templates for generated component invokers. */
4+
public final class InvocationLogMessageHelper {
5+
6+
private InvocationLogMessageHelper() {}
7+
8+
public static final String TOOL_INVOCATION_FAILED =
9+
"Tool invocation failed for sourceMethod={}";
10+
11+
public static final String PROMPT_INVOCATION_FAILED =
12+
"Prompt invocation failed for sourceMethod={}";
13+
14+
public static final String RESOURCE_INVOCATION_FAILED =
15+
"Resource invocation failed for sourceMethod={}";
16+
17+
public static final String COMPLETION_INVOCATION_FAILED =
18+
"Completion invocation failed for sourceMethod={}";
19+
}

src/main/java/com/github/thought2code/mcp/annotated/server/component/completion/CompletionCodegen.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ private static void writeCompletionDefinitionMethod(
6161

6262
private static void writeCompletionInvoker(
6363
Writer writer, ExecutableElement method, int index, Support support) throws IOException {
64+
String sourceMethod = support.sourceMethod(method);
6465
TypeElement owner = (TypeElement) method.getEnclosingElement();
6566
String ownerType = owner.getQualifiedName().toString();
6667

@@ -86,6 +87,10 @@ private static void writeCompletionInvoker(
8687
writer.write(
8788
" return Invocation.builder().result(result == null ? resultIfNull : result).build();\n");
8889
writer.write(" } catch (Exception e) {\n");
90+
writer.write(
91+
" log.error(InvocationLogMessageHelper.COMPLETION_INVOCATION_FAILED, \""
92+
+ support.escape(sourceMethod)
93+
+ "\", e);\n");
8994
writer.write(
9095
" return Invocation.builder().result(McpServerError.METHOD_INVOCATION_ERROR.toString()).isError(true).build();\n");
9196
writer.write(" }\n");

src/main/java/com/github/thought2code/mcp/annotated/server/component/prompt/PromptCodegen.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ private static void writePromptDefinitionMethod(
100100

101101
private static void writePromptInvoker(
102102
Writer writer, ExecutableElement method, int index, Support support) throws IOException {
103+
String sourceMethod = support.sourceMethod(method);
103104
TypeElement owner = (TypeElement) method.getEnclosingElement();
104105
String ownerType = owner.getQualifiedName().toString();
105106
boolean returnsVoid = method.getReturnType().getKind() == TypeKind.VOID;
@@ -168,6 +169,10 @@ private static void writePromptInvoker(
168169
" return Invocation.builder().result(result == null ? resultIfNull : result).build();\n");
169170
}
170171
writer.write(" } catch (Exception e) {\n");
172+
writer.write(
173+
" log.error(InvocationLogMessageHelper.PROMPT_INVOCATION_FAILED, \""
174+
+ support.escape(sourceMethod)
175+
+ "\", e);\n");
171176
writer.write(
172177
" return Invocation.builder().result(McpServerError.METHOD_INVOCATION_ERROR.toString()).isError(true).build();\n");
173178
writer.write(" }\n");

src/main/java/com/github/thought2code/mcp/annotated/server/component/resource/ResourceCodegen.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ private static void writeResourceDefinitionMethod(
8181

8282
private static void writeResourceInvoker(
8383
Writer writer, ExecutableElement method, int index, Support support) throws IOException {
84+
String sourceMethod = support.sourceMethod(method);
8485
TypeElement owner = (TypeElement) method.getEnclosingElement();
8586
String ownerType = owner.getQualifiedName().toString();
8687
boolean returnsVoid = method.getReturnType().getKind() == TypeKind.VOID;
@@ -110,6 +111,10 @@ private static void writeResourceInvoker(
110111
" return Invocation.builder().result(result == null ? resultIfNull : result).build();\n");
111112
}
112113
writer.write(" } catch (Exception e) {\n");
114+
writer.write(
115+
" log.error(InvocationLogMessageHelper.RESOURCE_INVOCATION_FAILED, \""
116+
+ support.escape(sourceMethod)
117+
+ "\", e);\n");
113118
writer.write(
114119
" return Invocation.builder().result(McpServerError.METHOD_INVOCATION_ERROR.toString()).isError(true).build();\n");
115120
writer.write(" }\n");

src/main/java/com/github/thought2code/mcp/annotated/server/component/tool/ToolCodegen.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ private static void writeOutputSchemaMethod(
184184

185185
private static void writeInvoker(
186186
Writer writer, ExecutableElement method, int index, Support support) throws IOException {
187+
String sourceMethod = support.sourceMethod(method);
187188
TypeElement owner = (TypeElement) method.getEnclosingElement();
188189
String ownerType = owner.getQualifiedName().toString();
189190
boolean returnsVoid = method.getReturnType().getKind() == TypeKind.VOID;
@@ -251,6 +252,10 @@ private static void writeInvoker(
251252
" return Invocation.builder().result(result == null ? resultIfNull : result).build();\n");
252253
}
253254
writer.write(" } catch (Exception e) {\n");
255+
writer.write(
256+
" log.error(InvocationLogMessageHelper.TOOL_INVOCATION_FAILED, \""
257+
+ support.escape(sourceMethod)
258+
+ "\", e);\n");
254259
writer.write(
255260
" return Invocation.builder().result(McpServerError.METHOD_INVOCATION_ERROR.toString()).isError(true).build();\n");
256261
writer.write(" }\n");
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.github.thought2code.mcp.annotated.server.component;
2+
3+
import static org.junit.jupiter.api.Assertions.assertTrue;
4+
5+
import java.io.IOException;
6+
import java.nio.file.Files;
7+
import java.nio.file.Path;
8+
import java.util.stream.Stream;
9+
import org.junit.jupiter.params.ParameterizedTest;
10+
import org.junit.jupiter.params.provider.Arguments;
11+
import org.junit.jupiter.params.provider.MethodSource;
12+
13+
class InvocationCodegenTemplateGuardTest {
14+
15+
@ParameterizedTest
16+
@MethodSource("codegenReferenceCases")
17+
void codegenTemplates_shouldReferenceInvocationLogMessageHelper(
18+
String relativePath, String expectedSnippet) throws IOException {
19+
Path file = Path.of(relativePath);
20+
String source = Files.readString(file);
21+
assertTrue(
22+
source.contains(expectedSnippet),
23+
() -> "Expected snippet not found in " + relativePath + ": " + expectedSnippet);
24+
}
25+
26+
private static Stream<Arguments> codegenReferenceCases() {
27+
return Stream.of(
28+
Arguments.of(
29+
"src/main/java/com/github/thought2code/mcp/annotated/server/component/tool/ToolCodegen.java",
30+
"InvocationLogMessageHelper.TOOL_INVOCATION_FAILED"),
31+
Arguments.of(
32+
"src/main/java/com/github/thought2code/mcp/annotated/server/component/prompt/PromptCodegen.java",
33+
"InvocationLogMessageHelper.PROMPT_INVOCATION_FAILED"),
34+
Arguments.of(
35+
"src/main/java/com/github/thought2code/mcp/annotated/server/component/resource/ResourceCodegen.java",
36+
"InvocationLogMessageHelper.RESOURCE_INVOCATION_FAILED"),
37+
Arguments.of(
38+
"src/main/java/com/github/thought2code/mcp/annotated/server/component/completion/CompletionCodegen.java",
39+
"InvocationLogMessageHelper.COMPLETION_INVOCATION_FAILED"),
40+
Arguments.of(
41+
"src/main/java/com/github/thought2code/mcp/annotated/server/component/AnnotationProcessor.java",
42+
"import com.github.thought2code.mcp.annotated.server.component.InvocationLogMessageHelper;"));
43+
}
44+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.github.thought2code.mcp.annotated.server.component;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import java.util.stream.Stream;
6+
import org.junit.jupiter.params.ParameterizedTest;
7+
import org.junit.jupiter.params.provider.Arguments;
8+
import org.junit.jupiter.params.provider.MethodSource;
9+
10+
class InvocationLogMessageHelperTest {
11+
12+
@ParameterizedTest
13+
@MethodSource("invocationMessageCases")
14+
void invocationLogMessageConstants_shouldMatchExpectedText(String key, String expected) {
15+
String actual =
16+
switch (key) {
17+
case "tool" -> InvocationLogMessageHelper.TOOL_INVOCATION_FAILED;
18+
case "prompt" -> InvocationLogMessageHelper.PROMPT_INVOCATION_FAILED;
19+
case "resource" -> InvocationLogMessageHelper.RESOURCE_INVOCATION_FAILED;
20+
case "completion" -> InvocationLogMessageHelper.COMPLETION_INVOCATION_FAILED;
21+
default -> throw new IllegalArgumentException("Unsupported key: " + key);
22+
};
23+
assertEquals(expected, actual);
24+
}
25+
26+
private static Stream<Arguments> invocationMessageCases() {
27+
return Stream.of(
28+
Arguments.of("tool", "Tool invocation failed for sourceMethod={}"),
29+
Arguments.of("prompt", "Prompt invocation failed for sourceMethod={}"),
30+
Arguments.of("resource", "Resource invocation failed for sourceMethod={}"),
31+
Arguments.of("completion", "Completion invocation failed for sourceMethod={}"));
32+
}
33+
}

0 commit comments

Comments
 (0)