diff --git a/cs25-service/Dockerfile b/cs25-service/Dockerfile index 2b014b15..e962b60f 100644 --- a/cs25-service/Dockerfile +++ b/cs25-service/Dockerfile @@ -11,12 +11,7 @@ COPY cs25-entity cs25-entity/ COPY cs25-common cs25-common/ # 테스트 생략하여 빌드 안정화 -# (빌드 시 MCP 비활성화 + gradlew 실행 권한 + 테스트 스킵) -ENV SPRING_AI_MCP_CLIENT_ENABLED=false \ - SPRING_AI_MCP_CLIENT_INITIALIZED=false -RUN chmod +x ./gradlew -RUN ./gradlew :cs25-service:bootJar --stacktrace --no-daemon -x test - +RUN ./gradlew :cs25-service:bootJar --stacktrace --no-daemon FROM eclipse-temurin:17-jre-jammy # 메타 정보 @@ -30,10 +25,7 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends curl ca-certificates gnupg bash \ && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ && apt-get install -y --no-install-recommends nodejs \ - && npm install -g @modelcontextprotocol/server-brave-search@0.2.1 \ - && ln -sf "$(npm root -g)/.bin/server-brave-search" /usr/local/bin/server-brave-search \ - && chmod +x /usr/local/bin/server-brave-search \ - && /usr/local/bin/server-brave-search --help || true \ + && npm install -g @modelcontextprotocol/server-brave-search \ && npm cache clean --force \ && apt-get purge -y gnupg \ && apt-get autoremove -y --purge \ diff --git a/cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java b/cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java index 4f6bb103..d11ca3f3 100644 --- a/cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java +++ b/cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java @@ -6,7 +6,6 @@ import io.modelcontextprotocol.spec.McpSchema.CallToolRequest; import io.modelcontextprotocol.spec.McpSchema.CallToolResult; import io.modelcontextprotocol.spec.McpSchema.ListToolsResult; -import java.time.Duration; import java.util.List; import java.util.Map; import lombok.RequiredArgsConstructor; @@ -19,8 +18,9 @@ public class BraveSearchMcpService { private static final String BRAVE_WEB_TOOL = "brave_web_search"; - private static final Duration INIT_TIMEOUT = Duration.ofSeconds(60); + private final List mcpClients; + private final ObjectMapper objectMapper; public JsonNode search(String query, int count, int offset) { @@ -36,40 +36,28 @@ public JsonNode search(String query, int count, int offset) { JsonNode content = objectMapper.valueToTree(result.content()); log.info("[Brave MCP Response Raw content]: {}", content.toPrettyString()); + if (content != null && content.isArray()) { var root = objectMapper.createObjectNode(); root.set("results", content); return root; - } - } - private void ensureInitialized(McpSyncClient client) { - if (!client.isInitialized()) { - synchronized (client) { // 다중 스레드 초기화 경합 방지 - if (!client.isInitialized()) { - log.debug("MCP 클라이언트 초기화 시작…"); - client.initialize(); // 매개변수 없는 버전 - log.debug("MCP 클라이언트 초기화 완료"); - } - } - } + return content != null ? content : objectMapper.createObjectNode(); } private McpSyncClient resolveBraveClient() { for (McpSyncClient client : mcpClients) { - try { - ensureInitialized(client); // 초기화 - ListToolsResult tools = client.listTools(); - if (tools != null && tools.tools() != null && - tools.tools().stream() - .anyMatch(t -> BRAVE_WEB_TOOL.equalsIgnoreCase(t.name()))) { + ListToolsResult tools = client.listTools(); + if (tools != null && tools.tools() != null) { + boolean found = tools.tools().stream() + .anyMatch(tool -> BRAVE_WEB_TOOL.equalsIgnoreCase(tool.name())); + if (found) { return client; } - } catch (Exception e) { - log.debug("Brave MCP 클라이언트 후보 실패: {}", e.toString()); } } - throw new IllegalStateException("Brave MCP 서버에서 '" + BRAVE_WEB_TOOL + "' 툴을 찾을 수 없습니다."); + + throw new IllegalStateException("Brave MCP 서버에서 brave_web_search 툴을 찾을 수 없습니다."); } } \ No newline at end of file diff --git a/cs25-service/src/main/resources/application.properties b/cs25-service/src/main/resources/application.properties index d7a99e7b..f5ea13aa 100644 --- a/cs25-service/src/main/resources/application.properties +++ b/cs25-service/src/main/resources/application.properties @@ -65,12 +65,14 @@ spring.ai.chat.client.enabled=false # MCP spring.ai.mcp.client.enabled=true spring.ai.mcp.client.type=SYNC -spring.ai.mcp.client.request-timeout=45s +spring.ai.mcp.client.request-timeout=60s spring.ai.mcp.client.root-change-notification=false # STDIO Connect: Brave Search -spring.ai.mcp.client.stdio.connections.brave.command=/usr/local/bin/server-brave-search +spring.ai.mcp.client.stdio.connections.brave.command=server-brave-search spring.ai.mcp.client.stdio.connections.brave.args[0]=--stdio spring.ai.mcp.client.stdio.connections.brave.env.BRAVE_API_KEY=${BRAVE_API_KEY} +spring.ai.mcp.client.initialized=false +spring.autoconfigure.exclude=org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration #MAIL spring.mail.host=smtp.gmail.com spring.mail.port=587