From deb904bbc4b25da3a6ff3686b19be071f8bf6739 Mon Sep 17 00:00:00 2001 From: acamq <179265037+acamq@users.noreply.github.com> Date: Fri, 27 Feb 2026 10:03:20 -0700 Subject: [PATCH] fix(skill-mcp): clarify builtin MCP error hint --- src/tools/skill-mcp/builtin-mcp-hint.test.ts | 45 ++++++++++++++++++++ src/tools/skill-mcp/constants.ts | 6 +++ src/tools/skill-mcp/tools.ts | 17 +++++++- 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/tools/skill-mcp/builtin-mcp-hint.test.ts diff --git a/src/tools/skill-mcp/builtin-mcp-hint.test.ts b/src/tools/skill-mcp/builtin-mcp-hint.test.ts new file mode 100644 index 000000000..6f96b339d --- /dev/null +++ b/src/tools/skill-mcp/builtin-mcp-hint.test.ts @@ -0,0 +1,45 @@ +import { describe, it, expect } from "bun:test" + +import { SkillMcpManager } from "../../features/skill-mcp-manager" +import { createSkillMcpTool } from "./tools" + +const mockContext = { + sessionID: "test-session", + messageID: "msg-1", + agent: "test-agent", + directory: "/test", + worktree: "/test", + abort: new AbortController().signal, + metadata: () => {}, + ask: async () => {}, +} + +describe("skill_mcp builtin MCP hint", () => { + it("returns builtin hint for context7", async () => { + const tool = createSkillMcpTool({ + manager: new SkillMcpManager(), + getLoadedSkills: () => [], + getSessionID: () => "session", + }) + + await expect( + tool.execute({ mcp_name: "context7", tool_name: "resolve-library-id" }, mockContext), + ).rejects.toThrow(/builtin MCP/) + + await expect( + tool.execute({ mcp_name: "context7", tool_name: "resolve-library-id" }, mockContext), + ).rejects.toThrow(/context7_resolve-library-id/) + }) + + it("keeps skill-loading hint for unknown MCP names", async () => { + const tool = createSkillMcpTool({ + manager: new SkillMcpManager(), + getLoadedSkills: () => [], + getSessionID: () => "session", + }) + + await expect( + tool.execute({ mcp_name: "unknown-mcp", tool_name: "x" }, mockContext), + ).rejects.toThrow(/Load the skill first/) + }) +}) diff --git a/src/tools/skill-mcp/constants.ts b/src/tools/skill-mcp/constants.ts index 4df4f4d40..e2d13cf0b 100644 --- a/src/tools/skill-mcp/constants.ts +++ b/src/tools/skill-mcp/constants.ts @@ -1,3 +1,9 @@ export const SKILL_MCP_TOOL_NAME = "skill_mcp" export const SKILL_MCP_DESCRIPTION = `Invoke MCP server operations from skill-embedded MCPs. Requires mcp_name plus exactly one of: tool_name, resource_name, or prompt_name.` + +export const BUILTIN_MCP_TOOL_HINTS: Record = { + context7: ["context7_resolve-library-id", "context7_query-docs"], + websearch: ["websearch_web_search_exa"], + grep_app: ["grep_app_searchGitHub"], +} diff --git a/src/tools/skill-mcp/tools.ts b/src/tools/skill-mcp/tools.ts index 96dddaa75..9791501fe 100644 --- a/src/tools/skill-mcp/tools.ts +++ b/src/tools/skill-mcp/tools.ts @@ -1,5 +1,5 @@ import { tool, type ToolDefinition } from "@opencode-ai/plugin" -import { SKILL_MCP_DESCRIPTION } from "./constants" +import { BUILTIN_MCP_TOOL_HINTS, SKILL_MCP_DESCRIPTION } from "./constants" import type { SkillMcpArgs } from "./types" import type { SkillMcpManager, SkillMcpClientInfo, SkillMcpServerContext } from "../../features/skill-mcp-manager" import type { LoadedSkill } from "../../features/opencode-skill-loader/types" @@ -71,6 +71,16 @@ function formatAvailableMcps(skills: LoadedSkill[]): string { return mcps.length > 0 ? mcps.join("\n") : " (none found)" } +function formatBuiltinMcpHint(mcpName: string): string | null { + const nativeTools = BUILTIN_MCP_TOOL_HINTS[mcpName] + if (!nativeTools) return null + return ( + `"${mcpName}" is a builtin MCP, not a skill MCP.\n` + + `Use the native tools directly:\n` + + nativeTools.map((toolName) => ` - ${toolName}`).join("\n") + ) +} + function parseArguments(argsJson: string | Record | undefined): Record { if (!argsJson) return {} if (typeof argsJson === "object" && argsJson !== null) { @@ -132,6 +142,11 @@ export function createSkillMcpTool(options: SkillMcpToolOptions): ToolDefinition const found = findMcpServer(args.mcp_name, skills) if (!found) { + const builtinHint = formatBuiltinMcpHint(args.mcp_name) + if (builtinHint) { + throw new Error(builtinHint) + } + throw new Error( `MCP server "${args.mcp_name}" not found.\n\n` + `Available MCP servers in loaded skills:\n` +