From 65367b24cbbba23563481c50c6a61a7ae2189919 Mon Sep 17 00:00:00 2001 From: jade Date: Tue, 7 Jul 2026 10:20:27 +0900 Subject: [PATCH] feat: Add Tool Docs Generator API for markdown catalog --- .../controller/McpRouterController.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/controller/McpRouterController.java b/axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/controller/McpRouterController.java index 704c048..61805e6 100644 --- a/axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/controller/McpRouterController.java +++ b/axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/controller/McpRouterController.java @@ -93,6 +93,71 @@ public class McpRouterController { return ResponseEntity.ok(response); } + @GetMapping(value = "/tools/docs/markdown", produces = "text/markdown;charset=UTF-8") + public ResponseEntity generateToolsMarkdown() { + List tools = redisRegistryService.getAllTools(); + + StringBuilder md = new StringBuilder(); + md.append("# \uD83E\uDD16 Shinhan AI Tool Catalog\n\n"); + md.append("**총 등록된 툴:** ").append(tools.size()).append("개\n"); + java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + md.append("**마지막 업데이트:** ").append(java.time.LocalDateTime.now().format(formatter)).append("\n\n"); + md.append("---\n\n"); + + // Group by Domain Group + Map> groupedTools = new java.util.HashMap<>(); + for (ToolMetadata tool : tools) { + String group = tool.getDomainGroup() != null ? tool.getDomainGroup() : "기타 (Others)"; + groupedTools.computeIfAbsent(group, k -> new java.util.ArrayList<>()).add(tool); + } + + for (Map.Entry> entry : groupedTools.entrySet()) { + md.append("## \uD83D\uDCC1 도메인: ").append(entry.getKey()).append("\n\n"); + + int index = 1; + for (ToolMetadata tool : entry.getValue()) { + md.append("### ").append(index++).append(". ").append(tool.getToolName()).append("\n"); + if (tool.getDescription() != null) { + md.append("- **설명**: ").append(tool.getDescription()).append("\n"); + } + md.append("- **연동 방식**: `").append(tool.getIntegrationType() != null ? tool.getIntegrationType() : "DIRECT").append("`\n\n"); + + // Parameters Table + if (tool.getParametersSchema() != null && tool.getParametersSchema().containsKey("properties")) { + md.append("#### \u2699\uFE0F 파라미터 (Parameters)\n"); + md.append("| 파라미터명 | 타입 | 필수 여부 | 설명 |\n"); + md.append("|---|---|---|---|\n"); + + Map properties = (Map) tool.getParametersSchema().get("properties"); + List required = (List) tool.getParametersSchema().get("required"); + + for (Map.Entry prop : properties.entrySet()) { + String name = prop.getKey(); + Map details = (Map) prop.getValue(); + String type = details.containsKey("type") ? String.valueOf(details.get("type")) : "string"; + String desc = details.containsKey("description") ? String.valueOf(details.get("description")) : ""; + String req = (required != null && required.contains(name)) ? "Y" : "N"; + + md.append("| `").append(name).append("` | `").append(type).append("` | ").append(req).append(" | ").append(desc).append(" |\n"); + } + md.append("\n"); + } + + // Action Prompts + if (tool.getActionPrompts() != null && !tool.getActionPrompts().isEmpty()) { + md.append("#### \uD83D\uDCAC 프롬프트 예시 (Action Prompts)\n"); + for (Map.Entry prompt : tool.getActionPrompts().entrySet()) { + md.append("- \"").append(prompt.getValue()).append("\"\n"); + } + md.append("\n"); + } + md.append("---\n\n"); + } + } + + return ResponseEntity.ok(md.toString()); + } + @PostMapping("/registry/register") public ResponseEntity registerTool(@RequestBody ToolMetadata meta) { redisRegistryService.saveTool(meta);