refactor: apply new Naming standard (uid, categoryKey, subToolName) across modules

This commit is contained in:
jade
2026-07-11 00:37:29 +09:00
parent 406f093044
commit b9aa8e0169
25 changed files with 135 additions and 117 deletions

View File

@@ -36,7 +36,10 @@ import java.util.Map;
public class ToolMetadata {
// 1. Tool 기본 정보
private String toolName; // 툴 고유 명칭 (예: CustomerSearchTool)
private String uid; // UUID 형식의 고유 식별자
private String semver; // 버전 (예: 1.0.0)
private String name; // 사람이 읽는 라벨 (1-128자)
private String subToolName; // MCP 서브툴 명칭 (64자 이하, 예: CustomerSearchTool)
private String description; // 툴의 목적 및 설명 (LLM 프롬프트에 활용 가능)
// 2. 파라미터 스키마 (JSON Schema 형태의 Map)
@@ -45,8 +48,8 @@ public class ToolMetadata {
// 2-0. 프론트엔드 UI용 함수별 프롬프트 매핑 (추가됨)
private Map<String, String> actionPrompts;
// 2-1. 도메인 부서 그룹명 (권한 관리에 사용)
private String domainGroup;
// 2-1. 도메인 부서 그룹명 (category_key, 슬러그 형식)
private String categoryKey;
// 2-2. 툴 처리 엔드포인트 URI 경로 (예: /api/tool/customer-info)
private String endpoint;

View File

@@ -109,11 +109,11 @@ public class McpRouterController {
public ResponseEntity<JsonRpcResponse> listTools() {
List<ToolMetadata> activeTools = redisRegistryService.getAllTools()
.stream()
.filter(ToolMetadata::isVisible)
.filter(ToolMetadata::getVisible)
.collect(java.util.stream.Collectors.toList());
java.util.Set<String> knownTools = activeTools.stream()
.map(ToolMetadata::getToolName)
.map(ToolMetadata::getUid)
.collect(java.util.stream.Collectors.toSet());
java.util.Set<String> fallbackUrls = new java.util.HashSet<>(gatewayFallbackProperties.getRoutes().values());
@@ -130,9 +130,9 @@ public class McpRouterController {
if (localTools != null) {
for (ToolMetadata t : localTools) {
if (!Boolean.TRUE.equals(t.getIsRegistered()) && Boolean.TRUE.equals(t.getVisible()) && !knownTools.contains(t.getToolName())) {
if (!Boolean.TRUE.equals(t.getIsRegistered()) && Boolean.TRUE.equals(t.getVisible()) && !knownTools.contains(t.getUid())) {
activeTools.add(t);
knownTools.add(t.getToolName());
knownTools.add(t.getUid());
}
}
}
@@ -152,7 +152,7 @@ public class McpRouterController {
public ResponseEntity<String> generateToolsMarkdown() {
List<ToolMetadata> tools = redisRegistryService.getAllTools()
.stream()
.filter(ToolMetadata::isVisible)
.filter(ToolMetadata::getVisible)
.collect(java.util.stream.Collectors.toList());
StringBuilder md = new StringBuilder();
@@ -165,7 +165,7 @@ public class McpRouterController {
// Group by Domain Group
Map<String, List<ToolMetadata>> groupedTools = new java.util.HashMap<>();
for (ToolMetadata tool : tools) {
String group = tool.getDomainGroup() != null ? tool.getDomainGroup() : "기타 (Others)";
String group = tool.getCategoryKey() != null ? tool.getCategoryKey() : "기타 (Others)";
groupedTools.computeIfAbsent(group, k -> new java.util.ArrayList<>()).add(tool);
}
@@ -174,7 +174,7 @@ public class McpRouterController {
int index = 1;
for (ToolMetadata tool : entry.getValue()) {
md.append("### ").append(index++).append(". ").append(tool.getToolName()).append("\n");
md.append("### ").append(index++).append(". ").append(tool.getUid()).append("\n");
if (tool.getDescription() != null) {
md.append("- **설명**: ").append(tool.getDescription()).append("\n");
}
@@ -223,14 +223,14 @@ public class McpRouterController {
}
@PostMapping("/registry/deregister")
public ResponseEntity<String> deregisterTool(@RequestBody String toolName) {
redisRegistryService.removeTool(toolName);
public ResponseEntity<String> deregisterTool(@RequestBody String uid) {
redisRegistryService.removeTool(uid);
return ResponseEntity.ok("Deregistered");
}
@PostMapping("/registry/heartbeat")
public ResponseEntity<String> heartbeat(@RequestBody String toolName) {
boolean success = redisRegistryService.refreshHeartbeat(toolName);
public ResponseEntity<String> heartbeat(@RequestBody String uid) {
boolean success = redisRegistryService.refreshHeartbeat(uid);
if (success) {
return ResponseEntity.ok("Heartbeat updated");
} else {

View File

@@ -39,28 +39,28 @@ public class RedisRegistryService {
* 툴 등록 및 갱신 (TTL 기반으로 60초 뒤 자동 만료)
*/
public void saveTool(ToolMetadata meta) {
String key = KEY_PREFIX + meta.getToolName();
String key = KEY_PREFIX + meta.getUid();
meta.setLastHeartbeat(System.currentTimeMillis());
redisTemplate.opsForValue().set(key, meta, DEFAULT_TTL);
log.info(" [RedisRegistry] 툴 등록 완료: {}", meta.getToolName());
log.info(" [RedisRegistry] 툴 등록 완료: {}", meta.getUid());
}
/**
* 하트비트 갱신 (TTL 초기화)
*/
public boolean refreshHeartbeat(String toolName) {
String key = KEY_PREFIX + toolName;
public boolean refreshHeartbeat(String uid) {
String key = KEY_PREFIX + uid;
Boolean exists = redisTemplate.expire(key, DEFAULT_TTL);
if (Boolean.TRUE.equals(exists)) {
log.debug(" [RedisRegistry] 하트비트 갱신: {}", toolName);
log.debug(" [RedisRegistry] 하트비트 갱신: {}", uid);
return true;
} else {
log.warn(" [RedisRegistry] 존재하지 않는 툴에 대한 하트비트 요청: {}", toolName);
log.warn(" [RedisRegistry] 존재하지 않는 툴에 대한 하트비트 요청: {}", uid);
return false;
}
}
public List<String> getAvailablePods(String toolName) {
ToolMetadata tool = getTool(toolName);
public List<String> getAvailablePods(String uid) {
ToolMetadata tool = getTool(uid);
if (tool != null && tool.getPodUrl() != null) {
return List.of(tool.getPodUrl());
@@ -71,8 +71,8 @@ public class RedisRegistryService {
/**
* 실행 시 툴 정보 조회 (Tool Execution 시 참조)
*/
public ToolMetadata getTool(String toolName) {
return redisTemplate.opsForValue().get(KEY_PREFIX + toolName);
public ToolMetadata getTool(String uid) {
return redisTemplate.opsForValue().get(KEY_PREFIX + uid);
}
/**
@@ -90,8 +90,8 @@ public class RedisRegistryService {
/**
* 툴 명시적 제거 (Deregister)
*/
public void removeTool(String toolName) {
redisTemplate.delete(KEY_PREFIX + toolName);
log.info(" [RedisRegistry] 툴 삭제 완료: {}", toolName);
public void removeTool(String uid) {
redisTemplate.delete(KEY_PREFIX + uid);
log.info(" [RedisRegistry] 툴 삭제 완료: {}", uid);
}
}

View File

@@ -73,19 +73,19 @@ public class ToolPlanner {
log.info(" [Planner] Fallback 라우팅 매칭됨: {} -> {}", toolName, fallbackPodUrl);
toolMetadata = ToolMetadata.builder()
.toolName(toolName)
.uid(toolName)
.integrationType("DIRECT")
.podUrl(fallbackPodUrl)
.build();
}
// 2-1. [신규] 도메인 그룹핑 기반 권한 검증
if (tenantId != null && toolMetadata.getDomainGroup() != null) {
if (tenantId != null && toolMetadata.getCategoryKey() != null) {
List<String> allowedDomains = securityProperties.getTenantDomains().get(tenantId);
if (allowedDomains == null ||
(!allowedDomains.contains("ALL") && !allowedDomains.contains(toolMetadata.getDomainGroup()))) {
log.warn(" [Planner] 권한 거부 - Tenant: {}, Request Domain: {}", tenantId, toolMetadata.getDomainGroup());
throw new SecurityException("해당 도메인(" + toolMetadata.getDomainGroup() + ")의 툴을 실행할 권한이 없습니다.");
(!allowedDomains.contains("ALL") && !allowedDomains.contains(toolMetadata.getCategoryKey()))) {
log.warn(" [Planner] 권한 거부 - Tenant: {}, Request Domain: {}", tenantId, toolMetadata.getCategoryKey());
throw new SecurityException("해당 도메인(" + toolMetadata.getCategoryKey() + ")의 툴을 실행할 권한이 없습니다.");
}
}