feat: Add property-based fallback routing for unregistered tools

This commit is contained in:
jade
2026-07-08 16:32:23 +09:00
parent 056447075c
commit 8db282037b
4 changed files with 50 additions and 3 deletions

View File

@@ -0,0 +1,16 @@
package io.shinhanlife.axhub.biz.mcp.gateway.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Data
@Configuration
@ConfigurationProperties(prefix = "mcp.gateway.fallback")
public class GatewayFallbackProperties {
private Map<String, String> routes = new HashMap<>();
private String defaultUrl;
}

View File

@@ -3,6 +3,7 @@ package io.shinhanlife.axhub.biz.mcp.gateway.service;
import io.shinhanlife.axhub.common.mcp.security.SecurityProperties;
import io.shinhanlife.axhub.biz.mcp.gateway.registry.RedisRegistryService;
import io.shinhanlife.axhub.biz.mcp.gateway.dto.ToolMetadata;
import io.shinhanlife.axhub.biz.mcp.gateway.config.GatewayFallbackProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@@ -17,6 +18,7 @@ public class ToolPlanner {
private final RedisRegistryService redisRegistryService;
private final SecurityProperties securityProperties;
private final GatewayFallbackProperties fallbackProperties;
/**
* 요청(payload)을 분석하여 실행해야 할 Tool의 계획을 생성합니다.
@@ -37,8 +39,30 @@ public class ToolPlanner {
var toolMetadata = redisRegistryService.getTool(toolName);
if (toolMetadata == null) {
log.warn(" [Planner] 등록되지 않은 툴 요청: {}", toolName);
throw new RuntimeException("해당 툴(" + toolName + ")이 레지스트리에 존재하지 않습니다.");
log.warn(" [Planner] 등록되지 않은 툴 요청: {}. Fallback 라우팅 규칙을 확인합니다.", toolName);
String fallbackPodUrl = fallbackProperties.getDefaultUrl();
if (fallbackProperties.getRoutes() != null) {
for (Map.Entry<String, String> entry : fallbackProperties.getRoutes().entrySet()) {
if (toolName.contains(entry.getKey())) {
fallbackPodUrl = entry.getValue();
break;
}
}
}
if (fallbackPodUrl == null || fallbackPodUrl.isEmpty()) {
log.error(" [Planner] Fallback 라우팅 대상이 아닙니다. 툴: {}", toolName);
throw new RuntimeException("해당 툴(" + toolName + ")이 레지스트리에 존재하지 않습니다.");
}
log.info(" [Planner] Fallback 라우팅 매칭됨: {} -> {}", toolName, fallbackPodUrl);
toolMetadata = ToolMetadata.builder()
.toolName(toolName)
.integrationType("DIRECT")
.podUrl(fallbackPodUrl)
.build();
}
// 2-1. [신규] 도메인 그룹핑 기반 권한 검증

View File

@@ -1,4 +1,11 @@
spring.application.name=axhub-admin
spring.main.web-application-type=reactive
# Gateway Fallback Routing (For unregistered tools)
mcp.gateway.fallback.routes.email=http://tool-email:8083
mcp.gateway.fallback.routes.sms=http://tool-sms:8082
mcp.gateway.fallback.default-url=http://tool-other:8084
server.port=8081
server.http2.enabled=true

View File

@@ -17,7 +17,7 @@ public class TemplateUtilityService extends AbstractMcpToolService {
name = "get_template_file_url",
description = "특정 템플릿의 양식 파일(엑셀, 워드 등)을 다운로드 받을 수 있는 시스템 URL을 반환합니다. AI는 이 URL을 사용자에게 마크다운 링크 형태로 제공해야 합니다.",
prompt = "요청하신 템플릿 양식 파일 다운로드 URL은 다음과 같습니다. 클릭하여 다운로드하세요:",
visible = false
register = false
)
public Map<String, Object> getTemplateFileUrl(TemplateDownloadReq data) {
try {