Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions cs25-service/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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

# 메타 정보
Expand All @@ -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 \
Expand All @@ -47,4 +39,4 @@ COPY --from=builder /build/cs25-service/build/libs/*.jar app.jar
EXPOSE 8080

# 실행
ENTRYPOINT ["java", "-jar", "/apps/app.jar"]
ENTRYPOINT ["java", "-jar", "/apps/app.jar"]
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<McpSyncClient> mcpClients;

private final ObjectMapper objectMapper;

public JsonNode search(String query, int count, int offset) {
Expand All @@ -40,36 +40,23 @@ public JsonNode search(String query, int count, int offset) {
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 툴을 찾을 수 없습니다.");
}
}
6 changes: 4 additions & 2 deletions cs25-service/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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=60s
spring.ai.mcp.client.request-timeout=45s
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
Expand Down