forked from kimhyungsik/ax_hub_mcp_tool
feat: expose tool timeout and retry metadata
All checks were successful
Deploy Tools / deploy (push) Successful in 1m27s
All checks were successful
Deploy Tools / deploy (push) Successful in 1m27s
This commit is contained in:
@@ -29,6 +29,9 @@ public @interface GrowToolHint {
|
||||
String mappingId() default "";
|
||||
String inputSchemaResource() default "";
|
||||
String outputSchemaResource() default "";
|
||||
long timeoutMillis() default 5000L;
|
||||
boolean retryEnabled() default true;
|
||||
int retryMaxAttempts() default 3;
|
||||
|
||||
// Meta 정보 추가 (보고용 샘플)
|
||||
String displayDescription() default "";
|
||||
|
||||
@@ -7,6 +7,8 @@ import java.util.List;
|
||||
public record ToolManifestMeta(
|
||||
String version,
|
||||
long timeoutMillis,
|
||||
boolean retryEnabled,
|
||||
int retryMaxAttempts,
|
||||
boolean enabled,
|
||||
List<String> exampleQueries,
|
||||
List<String> tags,
|
||||
|
||||
@@ -21,7 +21,9 @@ import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
public class ToolManifestService {
|
||||
|
||||
private static final long DEFAULT_TIMEOUT_MILLIS = 300000L;
|
||||
private static final long DEFAULT_TIMEOUT_MILLIS = 5000L;
|
||||
private static final boolean DEFAULT_RETRY_ENABLED = true;
|
||||
private static final int DEFAULT_RETRY_MAX_ATTEMPTS = 3;
|
||||
private static final AtomicLong LAST_ISSUED_REVISION = new AtomicLong();
|
||||
private final Supplier<List<ToolMetadata>> toolSupplier;
|
||||
private final ObjectMapper objectMapper;
|
||||
@@ -88,7 +90,9 @@ public class ToolManifestService {
|
||||
new ToolManifestAnnotations(title, isTrue(tool.getReadOnlyHint()), isTrue(tool.getDestructiveHint()),
|
||||
isTrue(tool.getIdempotentHint()), isTrue(tool.getOpenWorldHint())),
|
||||
new ToolManifestMeta(defaultString(tool.getSemver(), "1.0.0"),
|
||||
tool.getTimeoutMillis() == null ? DEFAULT_TIMEOUT_MILLIS : tool.getTimeoutMillis(),
|
||||
positiveOrDefault(tool.getTimeoutMillis(), DEFAULT_TIMEOUT_MILLIS),
|
||||
tool.getRetryEnabled() == null ? DEFAULT_RETRY_ENABLED : tool.getRetryEnabled(),
|
||||
positiveOrDefault(tool.getRetryMaxAttempts(), DEFAULT_RETRY_MAX_ATTEMPTS),
|
||||
tool.getEnabled() == null || tool.getEnabled(),
|
||||
defaultList(tool.getExampleQueries()), defaultList(tool.getTags()),
|
||||
tool.getMciServiceId(), defaultList(tool.getRequiredEnvKeys()), tool.getOwnerOrg(),
|
||||
@@ -126,6 +130,14 @@ public class ToolManifestService {
|
||||
return lastRevision;
|
||||
}
|
||||
|
||||
private long positiveOrDefault(Long value, long defaultValue) {
|
||||
return value != null && value > 0 ? value : defaultValue;
|
||||
}
|
||||
|
||||
private int positiveOrDefault(Integer value, int defaultValue) {
|
||||
return value != null && value > 0 ? value : defaultValue;
|
||||
}
|
||||
|
||||
private String fingerprint(String bundleId, List<ToolManifestItem> tools) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(Map.of("bundleId", bundleId, "tools", tools));
|
||||
|
||||
@@ -40,9 +40,20 @@ public final class ToolMetadataMcpMapper {
|
||||
put(meta, "legacy_interface_id", metadata.getMciServiceId());
|
||||
put(meta, "required_env_keys", metadata.getRequiredEnvKeys());
|
||||
put(meta, "owner_org", metadata.getOwnerOrg());
|
||||
meta.put("timeoutMillis", positiveOrDefault(metadata.getTimeoutMillis(), 5000L));
|
||||
meta.put("retryEnabled", metadata.getRetryEnabled() == null || metadata.getRetryEnabled());
|
||||
meta.put("retryMaxAttempts", positiveOrDefault(metadata.getRetryMaxAttempts(), 3));
|
||||
return Map.copyOf(meta);
|
||||
}
|
||||
|
||||
private static long positiveOrDefault(Long value, long defaultValue) {
|
||||
return value != null && value > 0 ? value : defaultValue;
|
||||
}
|
||||
|
||||
private static int positiveOrDefault(Integer value, int defaultValue) {
|
||||
return value != null && value > 0 ? value : defaultValue;
|
||||
}
|
||||
|
||||
private static void put(Map<String, Object> target, String key, Object value) {
|
||||
if (value instanceof String text && !text.isBlank()) {
|
||||
target.put(key, text);
|
||||
|
||||
@@ -130,7 +130,9 @@ public class ToolRegistryHeartbeatSender {
|
||||
meta.setDisplayName(displayName);
|
||||
meta.setName(subToolName);
|
||||
meta.setSemver("1.0.0");
|
||||
meta.setTimeoutMillis(5000L);
|
||||
meta.setTimeoutMillis(hintAnnotation == null ? 5000L : positiveOrDefault(hintAnnotation.timeoutMillis(), 5000L));
|
||||
meta.setRetryEnabled(hintAnnotation == null || hintAnnotation.retryEnabled());
|
||||
meta.setRetryMaxAttempts(hintAnnotation == null ? 3 : positiveOrDefault(hintAnnotation.retryMaxAttempts(), 3));
|
||||
meta.setEnabled(true);
|
||||
meta.setDescription(functionAnnotation.description());
|
||||
|
||||
@@ -217,6 +219,14 @@ public class ToolRegistryHeartbeatSender {
|
||||
log.info(" [ToolScanner] 총 {}개 Tool 메타데이터 생성 완료", allScannedTools.size());
|
||||
}
|
||||
|
||||
private long positiveOrDefault(long value, long defaultValue) {
|
||||
return value > 0 ? value : defaultValue;
|
||||
}
|
||||
|
||||
private int positiveOrDefault(int value, int defaultValue) {
|
||||
return value > 0 ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @McpTool / @GrowToolHint 검색
|
||||
* <p>
|
||||
|
||||
@@ -231,7 +231,10 @@ public class ToolScaffolder {
|
||||
.append("\", description = \"").append(javaText(option(tool.description(), ""))).append("\")\n")
|
||||
.append(" @GrowToolHint(\n")
|
||||
.append(" requiresApproval = ").append(isMutation).append(",\n")
|
||||
.append(" categoryKey = \"").append(tool.group().toLowerCase(Locale.ROOT)).append("\",\n");
|
||||
.append(" categoryKey = \"").append(tool.group().toLowerCase(Locale.ROOT)).append("\",\n")
|
||||
.append(" timeoutMillis = 5000L,\n")
|
||||
.append(" retryEnabled = true,\n")
|
||||
.append(" retryMaxAttempts = 3,\n");
|
||||
if (tool.interfaceId() != null && !tool.interfaceId().isBlank()) {
|
||||
methods.append(" mappingId = \"").append(javaText(tool.interfaceId())).append("\",\n");
|
||||
}
|
||||
@@ -383,7 +386,10 @@ public class ToolScaffolder {
|
||||
.append("\", description = \"").append(javaText(option(tool.description(), ""))).append("\")\n")
|
||||
.append(" @GrowToolHint(\n")
|
||||
.append(" requiresApproval = ").append(isMutation).append(",\n")
|
||||
.append(" categoryKey = \"").append(tool.group().toLowerCase(Locale.ROOT)).append("\",\n");
|
||||
.append(" categoryKey = \"").append(tool.group().toLowerCase(Locale.ROOT)).append("\",\n")
|
||||
.append(" timeoutMillis = 5000L,\n")
|
||||
.append(" retryEnabled = true,\n")
|
||||
.append(" retryMaxAttempts = 3,\n");
|
||||
String mappingId = option(tool.interfaceId(), tool.httpApiName());
|
||||
if (mappingId != null && !mappingId.isBlank()) {
|
||||
declBuilder.append(" mappingId = \"").append(javaText(mappingId)).append("\",\n");
|
||||
@@ -550,7 +556,7 @@ public class ToolScaffolder {
|
||||
String schemaResourceDirectory = "classpath:tool-schemas/" + group.toLowerCase() + "/";
|
||||
String inputSchemaResource = useSchemaResource ? schemaResourceDirectory + toKebabCase(baseName) + "-resource-input-schema.json" : null;
|
||||
String outputSchemaResource = useSchemaResource ? schemaResourceDirectory + toKebabCase(baseName) + "-resource-output-schema.json" : null;
|
||||
String defaultWorkspace = "c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\dat-was-datmt";
|
||||
String defaultWorkspace = "c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\dat-was-dasmt";
|
||||
String workspace = getOrAsk(args, 10, scanner, "11. 대상 프로젝트 워크스페이스 경로 (default: " + defaultWorkspace + "): ");
|
||||
if (workspace.trim().isEmpty()) {
|
||||
workspace = defaultWorkspace;
|
||||
@@ -766,6 +772,9 @@ public class ToolScaffolder {
|
||||
}
|
||||
sb.append(" requiresApproval = ").append(isMutation).append(",\n");
|
||||
sb.append(" categoryKey = \"").append(group.toLowerCase(Locale.ROOT)).append("\",\n");
|
||||
sb.append(" timeoutMillis = 5000L,\n");
|
||||
sb.append(" retryEnabled = true,\n");
|
||||
sb.append(" retryMaxAttempts = 3,\n");
|
||||
if (interfaceId != null && !interfaceId.isBlank()) {
|
||||
sb.append(" mappingId = \"").append(interfaceId).append("\",\n");
|
||||
}
|
||||
|
||||
@@ -123,6 +123,8 @@ public class ToolMetadata {
|
||||
|
||||
@Builder.Default
|
||||
private Boolean retryEnabled = true;
|
||||
@Builder.Default
|
||||
private Integer retryMaxAttempts = 3;
|
||||
|
||||
@Builder.Default
|
||||
private Integer circuitBreakerFailureThreshold = 0;
|
||||
|
||||
@@ -266,7 +266,7 @@
|
||||
'X-Request-Id': request,
|
||||
'X-Request-Time': new Date().toISOString(),
|
||||
'X-Vrtl-Praf-No': 'V100001',
|
||||
'X-App-Code': 'DATMT',
|
||||
'X-App-Code': 'DASMT',
|
||||
'X-Project-Code': 'AXHUB',
|
||||
'X-User-Ip': '127.0.0.1',
|
||||
'X-Caller-Ip': '127.0.0.1',
|
||||
|
||||
Reference in New Issue
Block a user