From 18148aa72409e06602f938eeabf23a45706fee46 Mon Sep 17 00:00:00 2001 From: czhcc Date: Sun, 10 May 2026 23:30:22 +0800 Subject: [PATCH] feat(skill-mcp): expose readable display name alongside prefixed MCP tool names Add a parallel effectiveAllowedToolsDisplay field on the runtime status payload so the SkillMarket detail drawer can render mcp___ with the raw tool name appended in parentheses, while leaving the original prefixed list unchanged for any caller that needs the machine name. McpSkillBridge#decorateToolNameForDisplay reverses a prefixed name via the per-server cached tool list; the frontend prefers the new display field and falls back to effectiveAllowedTools when the field is absent. --- .../skill/controller/SkillController.java | 33 +++++++++++++++++-- .../vip/mate/skill/mcp/McpSkillBridge.java | 20 +++++++++++ mateclaw-ui/src/types/index.ts | 2 ++ mateclaw-ui/src/views/SkillMarket.vue | 4 ++- 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/mateclaw-server/src/main/java/vip/mate/skill/controller/SkillController.java b/mateclaw-server/src/main/java/vip/mate/skill/controller/SkillController.java index a24a81d7..7f2c74f3 100644 --- a/mateclaw-server/src/main/java/vip/mate/skill/controller/SkillController.java +++ b/mateclaw-server/src/main/java/vip/mate/skill/controller/SkillController.java @@ -1,6 +1,8 @@ package vip.mate.skill.controller; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -57,6 +59,7 @@ public class SkillController { private final AgentBindingService agentBindingService; private final vip.mate.skill.mcp.McpSkillBridge mcpSkillBridge; private final vip.mate.skill.acp.AcpSkillBridge acpSkillBridge; + private final ObjectMapper objectMapper; @Operation(summary = "获取技能分页列表(RFC-042 §2.1)") @GetMapping @@ -392,8 +395,34 @@ public class SkillController { @Operation(summary = "获取所有技能的运行时解析状态(管理页面使用)") @GetMapping("/runtime/status") - public R> getRuntimeStatus() { - return R.ok(skillRuntimeService.resolveAllSkillsStatus()); + public R>> getRuntimeStatus() { + List skills = skillRuntimeService.resolveAllSkillsStatus(); + return R.ok(skills.stream().map(this::toRuntimeStatusView).toList()); + } + + /** + * Perform a display transformation on `effectiveAllowedTools` (returned to the frontend), + * and store the transformed result in `effectiveAllowedToolsDisplay`. + * In addition to displaying the tool name in the format `mcp___`, + * also display the original MCP tool name in parentheses immediately following it. + * + * @param skill + * @return + */ + private Map toRuntimeStatusView(ResolvedSkill skill) { + Map view = objectMapper.convertValue( + skill, + new TypeReference>() {} + ); + + if ("mcp".equalsIgnoreCase(skill.getSource())) { + List displayTools = skill.getEffectiveAllowedTools().stream() + .map(mcpSkillBridge::decorateToolNameForDisplay) + .toList(); + view.put("effectiveAllowedToolsDisplay", displayTools); + } + + return view; } @Operation(summary = "刷新 active skills 缓存,resync=true 时同步内置技能到 workspace") diff --git a/mateclaw-server/src/main/java/vip/mate/skill/mcp/McpSkillBridge.java b/mateclaw-server/src/main/java/vip/mate/skill/mcp/McpSkillBridge.java index d29eef6c..cfaf42a8 100644 --- a/mateclaw-server/src/main/java/vip/mate/skill/mcp/McpSkillBridge.java +++ b/mateclaw-server/src/main/java/vip/mate/skill/mcp/McpSkillBridge.java @@ -121,6 +121,26 @@ public class McpSkillBridge { } } + /** + * Add a "display-only" method to reverse-lookup a prefixed name back to its raw name. + * + * @param prefixedName + * @return + */ + public String decorateToolNameForDisplay(String prefixedName) { + McpToolNameResolver.ParsedRef ref = McpToolNameResolver.parse(prefixedName); + if (ref == null) return prefixedName; + + McpServerEntity server = mcpServerService.getById(ref.serverId()); + for (String raw : readToolRawNames(server)) { + String rebuilt = McpToolNameResolver.prefixedName(ref.serverId(), raw); + if (rebuilt.equals(prefixedName)) { + return prefixedName + " (" + raw + ")"; + } + } + return prefixedName; + } + private List listEnabledServers() { try { return mcpServerService.listEnabled(); diff --git a/mateclaw-ui/src/types/index.ts b/mateclaw-ui/src/types/index.ts index 567838b4..06e3962d 100644 --- a/mateclaw-ui/src/types/index.ts +++ b/mateclaw-ui/src/types/index.ts @@ -311,6 +311,8 @@ export interface SkillRuntimeStatus { activeFeatures?: string[] /** Tools advertised to the LLM after feature filtering */ effectiveAllowedTools?: string[] + /** Human-readable tool names for display after feature filtering */ + effectiveAllowedToolsDisplay?: string[] } /** RFC-090 §14.6 — typed view onto manifest_json */ diff --git a/mateclaw-ui/src/views/SkillMarket.vue b/mateclaw-ui/src/views/SkillMarket.vue index 8df27f2a..6508d0ea 100644 --- a/mateclaw-ui/src/views/SkillMarket.vue +++ b/mateclaw-ui/src/views/SkillMarket.vue @@ -807,7 +807,9 @@ const detailManifest = computed(() => detailRuntime.value?.manifest ?? null) const detailManifestPretty = computed(() => detailManifest.value ? JSON.stringify(detailManifest.value, null, 2) : '', ) -const detailEffectiveTools = computed(() => detailRuntime.value?.effectiveAllowedTools || []) +const detailEffectiveTools = computed(() => + detailRuntime.value?.effectiveAllowedToolsDisplay || detailRuntime.value?.effectiveAllowedTools || [], +) const detailToolsCount = computed(() => detailEffectiveTools.value.length) const detailFeatures = computed(() => { const m = detailManifest.value