Describe implements plugins.ToolCatalogAdapter.
(name string)
| 44 | |
| 45 | // Describe implements plugins.ToolCatalogAdapter. |
| 46 | func (a *toolCatalogPluginAdapter) Describe(name string) (string, error) { |
| 47 | if a.cli.pluginManager == nil { |
| 48 | return "", fmt.Errorf("plugin manager unavailable in this session") |
| 49 | } |
| 50 | want := strings.TrimSpace(name) |
| 51 | want = strings.TrimPrefix(want, "@") |
| 52 | |
| 53 | // MCP names are routed by their canonical mcp_ prefix. Matching is |
| 54 | // case-insensitive on our side; the stored name keeps the server's casing. |
| 55 | mcpToolset := a.visibleMCPTools() |
| 56 | if bare, ok := cutMCPPrefix(want); ok { |
| 57 | if tool, ok := findMCPTool(mcpToolset, bare); ok { |
| 58 | return renderMCPToolBlock(tool), nil |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | ps := a.cli.pluginManager.GetPlugins() |
| 63 | names := make([]string, 0, len(ps)+len(mcpToolset)) |
| 64 | for _, p := range ps { |
| 65 | if strings.EqualFold(p.Name(), "@"+want) { |
| 66 | return renderToolBlock(p, false), nil |
| 67 | } |
| 68 | names = append(names, p.Name()) |
| 69 | } |
| 70 | |
| 71 | // Bare-name fallback: models routinely drop the mcp_ prefix ("describe |
| 72 | // read_file"). Only reached when no plugin matched, so a shadowing |
| 73 | // builtin still wins. |
| 74 | if tool, ok := findMCPTool(mcpToolset, want); ok { |
| 75 | return renderMCPToolBlock(tool), nil |
| 76 | } |
| 77 | for _, t := range mcpToolset { |
| 78 | names = append(names, "mcp_"+t.Name) |
| 79 | } |
| 80 | sort.Strings(names) |
| 81 | return "", fmt.Errorf("unknown tool %q — available: %s", name, strings.Join(names, ", ")) |
| 82 | } |
| 83 | |
| 84 | // List implements plugins.ToolCatalogAdapter. |
| 85 | func (a *toolCatalogPluginAdapter) List() (string, error) { |