{"record":{"id":"3553a3d5a44992d1","repo":"Hmbown/CodeWhale","slug":"skill-params-uri-is-unknown-skills-list-names-the-catalog","errorCode":null,"errorMessage":"skill \"${params?.uri ?? \"\"}\" is unknown — skills/list names the catalog","messagePattern":"skill \"(.+?)\" is unknown — skills/list names the catalog","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/mcp/server.mjs","lineNumber":1286,"sourceCode":"  \"resources/list\"() {\n    return { resources: skillPack.map(({ uri, rel, mime, size }) => ({ uri, name: rel, mimeType: mime, size })) };\n  },\n  \"resources/read\"(params) {\n    const file = skillPack.find((f) => f.uri === params?.uri);\n    if (!file) throw paramError(`resource \"${params?.uri ?? \"\"}\" is not part of the bundled skill pack — resources/list names the readable URIs`);\n    return { contents: [{ uri: file.uri, mimeType: file.mime, text: file.text }] };\n  },\n  \"skills/list\"() {\n    return {\n      skills: [{\n        uri: SKILL_ROOT_URI, name: SKILL_NAME, description: SKILL_DESCRIPTION,\n        files: skillPack.map(({ uri, sha256, size }) => ({ uri, sha256, bytes: size })),\n      }],\n    };\n  },\n  \"skills/get\"(params) {\n    const entry = skillPack.find((f) => f.uri === (params?.uri ?? SKILL_ROOT_URI));\n    if (!entry) throw paramError(`skill \"${params?.uri ?? \"\"}\" is unknown — skills/list names the catalog`);\n    return {\n      skill: { uri: entry.uri, name: SKILL_NAME, description: SKILL_DESCRIPTION, frontmatter: entry.frontmatter, content: entry.text },\n      manifest: skillPack.map(({ uri, sha256, size }) => ({ uri, sha256, bytes: size })),\n    };\n  },\n  async \"tools/call\"(params) {\n    if (params?.name === \"stop_computer_control\") return callTool(params);\n    const previous = dispatch;\n    let release;\n    dispatch = new Promise((resolve) => { release = resolve; });\n    try {\n      await previous;\n      throwIfAborted();\n      return await callToolRecorded(params ?? {});\n    } catch (err) {\n      if (err?.code !== \"cancelled\") throw err;\n      return { content: [{ type: \"text\", text: JSON.stringify(fail(null, controlStopped ? \"control_stopped\" : \"cancelled\", err.message)) }], isError: true };\n    } finally { release(); }","sourceCodeStart":1268,"sourceCodeEnd":1304,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/mcp/server.mjs#L1268-L1304","documentation":"The computer-use MCP server implements skills/get by finding the skillPack entry matching `params.uri`, defaulting to SKILL_ROOT_URI when the uri is omitted. If no entry matches, it throws a paramError stating the skill is unknown and that skills/list names the catalog. Only URIs present in the bundled skill pack are valid skill handles.","triggerScenarios":"A client calls skills/get with a uri not present in skillPack: a hallucinated or constructed skill URI, a stale URI from a previous plugin build, a typo'd or differently-cased string, or per-file URIs assumed to be skill-level URIs when only SKILL_ROOT_URI is the named skill (individual pack files are found only if registered as entries).","commonSituations":"An LLM client invents a skill name and requests its uri directly instead of listing first; tooling caches the skill catalog across plugin updates so old URIs 404; a developer passes a resource URI (from resources/list) to skills/get, mixing the two catalogs.","solutions":["Call skills/list and use one of the returned skill URIs verbatim in skills/get.","If you want the top-level skill, omit params.uri — the handler defaults to SKILL_ROOT_URI.","Refresh any cached catalog in the client after plugin updates and re-fetch skills/list.","Verify you are not passing a resources/* URI into skills/get; the resource and skill lookups share skillPack but are separate endpoints with different contracts."],"exampleFix":"// before\nawait mcp.request({ method: \"skills/get\", params: { uri: \"skill://computer-use/automation\" } }); // invented\n// after\nconst { skills } = await mcp.request({ method: \"skills/list\" });\nawait mcp.request({ method: \"skills/get\", params: { uri: skills[0].uri } });\n// or simply omit the uri for the root skill:\nawait mcp.request({ method: \"skills/get\" });","handlingStrategy":"validation","validationCode":"// Before calling skills/get:\nconst { skills } = await client.request({ method: \"skills/list\" });\nconst known = new Set(skills.map((s) => s.uri));\nif (params.uri !== undefined && !known.has(params.uri)) {\n  throw new Error(`Unknown skill URI; catalog: ${[...known].join(\", \")}`);\n}","typeGuard":"function isKnownSkillUri(uri, skills) {\n  return uri === undefined || (typeof uri === 'string' && skills.some((s) => s.uri === uri));\n}","tryCatchPattern":"try {\n  const { skill } = await client.request({ method: \"skills/get\", params });\n} catch (err) {\n  if (String(err.message).includes('is unknown')) {\n    // omit uri to get the root skill, or re-list the catalog\n    const { skill } = await client.request({ method: \"skills/get\" });\n  } else throw err;\n}","preventionTips":["Fetch skills/list first and treat it as the only source of valid skill URIs.","Omit params.uri entirely when you want the root skill (it defaults to SKILL_ROOT_URI).","Do not mix resources/list URIs into skills/get.","Re-list the catalog after plugin updates instead of trusting cached URIs or LLM-invented names."],"tags":["mcp","skills","lookup-failed","validation"],"backgroundTag":"entity-not-found","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}