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_<server>_<slug>_<hash> 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.
This commit is contained in:
czhcc 2026-05-10 23:30:22 +08:00 committed by GitHub
parent adddf0402a
commit 18148aa724
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 56 additions and 3 deletions

View File

@ -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<List<ResolvedSkill>> getRuntimeStatus() {
return R.ok(skillRuntimeService.resolveAllSkillsStatus());
public R<List<Map<String, Object>>> getRuntimeStatus() {
List<ResolvedSkill> 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_<serverId>_<slug>_<hash6>`,
* also display the original MCP tool name in parentheses immediately following it.
*
* @param skill
* @return
*/
private Map<String, Object> toRuntimeStatusView(ResolvedSkill skill) {
Map<String, Object> view = objectMapper.convertValue(
skill,
new TypeReference<Map<String, Object>>() {}
);
if ("mcp".equalsIgnoreCase(skill.getSource())) {
List<String> displayTools = skill.getEffectiveAllowedTools().stream()
.map(mcpSkillBridge::decorateToolNameForDisplay)
.toList();
view.put("effectiveAllowedToolsDisplay", displayTools);
}
return view;
}
@Operation(summary = "刷新 active skills 缓存resync=true 时同步内置技能到 workspace")

View File

@ -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<McpServerEntity> listEnabledServers() {
try {
return mcpServerService.listEnabled();

View File

@ -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 */

View File

@ -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