{"record":{"id":"2e28d12c6244d91c","repo":"Yeachan-Heo/oh-my-codex","slug":"unknown-descriptor-commandname-tool-parsed-t","errorCode":null,"errorMessage":"Unknown ${descriptor.commandName} tool: ${parsed.toolName}\n${buildDescriptorHelp(descriptor)}","messagePattern":"Unknown (.+?) tool: (.+?)\n(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/cli/mcp-parity.ts","lineNumber":149,"sourceCode":"  } catch {\n    return text;\n  }\n}\n\nasync function executeDescriptorCommand(\n  args: string[],\n  loadDescriptor: DescriptorLoader,\n): Promise<McpParityExecutionResult> {\n  const descriptor = await loadDescriptor();\n  const parsed = parseMcpCliArgs(args);\n  if (parsed.help || !parsed.toolName) {\n    return { ok: true, help: buildDescriptorHelp(descriptor) };\n  }\n\n  const toolName = descriptor.aliases?.[parsed.toolName] ?? parsed.toolName;\n  const allowedTools = new Set(descriptor.tools.map((tool) => tool.name));\n  if (!allowedTools.has(toolName)) {\n    throw new Error(\n      `Unknown ${descriptor.commandName} tool: ${parsed.toolName}\\n${buildDescriptorHelp(descriptor)}`,\n    );\n  }\n\n  const result = await descriptor.handle(\n    {\n      params: {\n        name: toolName,\n        arguments: parsed.input,\n      },\n    },\n    descriptor.handleOptions,\n  );\n  const payload = extractPayload(result);\n  return result.isError\n    ? { ok: false, error: payload }\n    : { ok: true, data: payload };\n}","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/mcp-parity.ts#L131-L167","documentation":"executeDescriptorCommand resolves the requested tool name (after alias mapping via descriptor.aliases) against the descriptor's registered tool set. If the resolved name is not in descriptor.tools, it throws with the command name, the requested tool name, and the full generated help text listing valid tools.","triggerScenarios":"Calling `mcp <command> <toolName>` where toolName is not registered and not an alias — typo'd names, tools from a different/older descriptor, or plugins that failed to load their tools.","commonSituations":"Typos (serch vs search); upgrading/downgrading versions where tool names changed; docs referencing tools not present in the installed build; alias removed between releases.","solutions":["Read the help text included in the error message — it enumerates every valid tool name","Fix the typo / use the exact tool name from the help output","Verify you're on the version whose tool set you expect (check changelog or --version)","If an alias is expected, confirm it still exists in descriptor.aliases for your version"],"exampleFix":"# before\nmycli mcp run-tool serach --input '{\"q\":\"x\"}'\n\n# after\nmycli mcp run-tool search --input '{\"q\":\"x\"}'","handlingStrategy":"try-catch","validationCode":"const validTools = new Set(['search', 'get', 'list']); // from descriptor help\nif (!validTools.has(requestedTool)) {\n  console.error(`Unknown tool. Valid: ${[...validTools].join(', ')}`);\n  process.exit(2);\n}","typeGuard":"function isKnownTool(name: string, descriptor: { tools: { name: string }[]; aliases?: Record<string, string> }): boolean {\n  const resolved = descriptor.aliases?.[name] ?? name;\n  return descriptor.tools.some((t) => t.name === resolved);\n}","tryCatchPattern":"try { await executeDescriptorCommand(descriptor, args); }\ncatch (e) {\n  if (/Unknown \\S+ tool:/.test(String(e))) { console.error(e.message); process.exit(2); } // message embeds full help\n  throw e;\n}","preventionTips":["Derive tool names programmatically from the descriptor/help, not hardcoded strings","Watch changelogs for tool renames across versions"],"tags":["cli","mcp","unknown-tool","usage"],"backgroundTag":"unknown-command-or-tool","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}