Hmbown/CodeWhale · error
skill " " is unknown — skills/list names the catalog
Error message
skill "${params?.uri ?? ""}" is unknown — skills/list names the catalog What it means
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.
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.
Example fix
// before
await mcp.request({ method: "skills/get", params: { uri: "skill://computer-use/automation" } }); // invented
// after
const { skills } = await mcp.request({ method: "skills/list" });
await mcp.request({ method: "skills/get", params: { uri: skills[0].uri } });
// or simply omit the uri for the root skill:
await mcp.request({ method: "skills/get" }); Defensive patterns
Strategy: validation
Validate before calling
// Before calling skills/get:
const { skills } = await client.request({ method: "skills/list" });
const known = new Set(skills.map((s) => s.uri));
if (params.uri !== undefined && !known.has(params.uri)) {
throw new Error(`Unknown skill URI; catalog: ${[...known].join(", ")}`);
} Type guard
function isKnownSkillUri(uri, skills) {
return uri === undefined || (typeof uri === 'string' && skills.some((s) => s.uri === uri));
} Try / catch
try {
const { skill } = await client.request({ method: "skills/get", params });
} catch (err) {
if (String(err.message).includes('is unknown')) {
// omit uri to get the root skill, or re-list the catalog
const { skill } = await client.request({ method: "skills/get" });
} else throw err;
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- resource " " is not part of the bundled skill pack —…
- bad_target
- .annotations is not a valid MCP annotations object
- .inputSchema must be a valid object-shaped MCP input schema
- .size must be an integer
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/3553a3d5a44992d1.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/mcp/server.mjs:1286
"resources/list"() {
return { resources: skillPack.map(({ uri, rel, mime, size }) => ({ uri, name: rel, mimeType: mime, size })) };
},
"resources/read"(params) {
const file = skillPack.find((f) => f.uri === params?.uri);
if (!file) throw paramError(`resource "${params?.uri ?? ""}" is not part of the bundled skill pack — resources/list names the readable URIs`);
return { contents: [{ uri: file.uri, mimeType: file.mime, text: file.text }] };
},
"skills/list"() {
return {
skills: [{
uri: SKILL_ROOT_URI, name: SKILL_NAME, description: SKILL_DESCRIPTION,
files: skillPack.map(({ uri, sha256, size }) => ({ uri, sha256, bytes: size })),
}],
};
},
"skills/get"(params) {
const entry = skillPack.find((f) => f.uri === (params?.uri ?? SKILL_ROOT_URI));
if (!entry) throw paramError(`skill "${params?.uri ?? ""}" is unknown — skills/list names the catalog`);
return {
skill: { uri: entry.uri, name: SKILL_NAME, description: SKILL_DESCRIPTION, frontmatter: entry.frontmatter, content: entry.text },
manifest: skillPack.map(({ uri, sha256, size }) => ({ uri, sha256, bytes: size })),
};
},
async "tools/call"(params) {
if (params?.name === "stop_computer_control") return callTool(params);
const previous = dispatch;
let release;
dispatch = new Promise((resolve) => { release = resolve; });
try {
await previous;
throwIfAborted();
return await callToolRecorded(params ?? {});
} catch (err) {
if (err?.code !== "cancelled") throw err;
return { content: [{ type: "text", text: JSON.stringify(fail(null, controlStopped ? "control_stopped" : "cancelled", err.message)) }], isError: true };
} finally { release(); }View on GitHub (pinned to 73e0f67d83)