diff --git a/src/tools/skill-mcp/parse-skill-mcp-arguments.ts b/src/tools/skill-mcp/parse-skill-mcp-arguments.ts new file mode 100644 index 000000000..20e0b8158 --- /dev/null +++ b/src/tools/skill-mcp/parse-skill-mcp-arguments.ts @@ -0,0 +1,25 @@ +export function parseSkillMcpArguments( + argsJson: string | Record | undefined, +): Record { + if (!argsJson) return {} + if (typeof argsJson === "object" && argsJson !== null) { + return argsJson + } + + try { + const jsonString = argsJson.startsWith("'") && argsJson.endsWith("'") ? argsJson.slice(1, -1) : argsJson + const parsed = JSON.parse(jsonString) + if (typeof parsed !== "object" || parsed === null) { + throw new Error("Arguments must be a JSON object") + } + + return parsed as Record + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + throw new Error( + `Invalid arguments JSON: ${errorMessage}\n\n` + + `Expected a valid JSON object, e.g.: '{"key": "value"}'\n` + + `Received: ${argsJson}`, + ) + } +} diff --git a/src/tools/skill-mcp/tools.ts b/src/tools/skill-mcp/tools.ts index 2e1876575..25720baf8 100644 --- a/src/tools/skill-mcp/tools.ts +++ b/src/tools/skill-mcp/tools.ts @@ -1,6 +1,7 @@ import { tool, type ToolDefinition } from "@opencode-ai/plugin" import type { ToolContext } from "@opencode-ai/plugin/tool" import { BUILTIN_MCP_TOOL_HINTS, SKILL_MCP_DESCRIPTION } from "./constants" +import { parseSkillMcpArguments } from "./parse-skill-mcp-arguments" import type { SkillMcpArgs } from "./types" import type { SkillMcpManager, SkillMcpClientInfo, SkillMcpServerContext } from "../../features/skill-mcp-manager" import type { LoadedSkill } from "../../features/opencode-skill-loader/types" @@ -82,30 +83,6 @@ function formatBuiltinMcpHint(mcpName: string): string | null { ) } -function parseArguments(argsJson: string | Record | undefined): Record { - if (!argsJson) return {} - if (typeof argsJson === "object" && argsJson !== null) { - return argsJson - } - try { - // Strip outer single quotes if present (common in LLM output) - const jsonStr = argsJson.startsWith("'") && argsJson.endsWith("'") ? argsJson.slice(1, -1) : argsJson - - const parsed = JSON.parse(jsonStr) - if (typeof parsed !== "object" || parsed === null) { - throw new Error("Arguments must be a JSON object") - } - return parsed as Record - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error) - throw new Error( - `Invalid arguments JSON: ${errorMessage}\n\n` + - `Expected a valid JSON object, e.g.: '{"key": "value"}'\n` + - `Received: ${argsJson}`, - ) - } -} - export function applyGrepFilter(output: string, pattern: string | undefined): string { if (!pattern) return output try { @@ -174,7 +151,7 @@ export function createSkillMcpTool(options: SkillMcpToolOptions): ToolDefinition skillName: found.skill.name, } - const parsedArgs = parseArguments(args.arguments) + const parsedArgs = parseSkillMcpArguments(args.arguments) let output: string switch (operation.type) {