Compare commits

...
Author SHA1 Message Date
Claude 21ddc2821c fix(ai-chat): replace hardcoded skill names with dynamic lookup in load_skills error
The load_skills tool error message hardcoded a static list of "available"
skills regardless of what actually existed in the workspace. This caused
the AI to retry loading nonexistent skills in a loop since the error
falsely claimed they were available.

https://claude.ai/code/session_01VYNqWTUP2dDJBHMhFjjCKe
2026-04-09 15:24:56 +00:00
5 changed files with 36 additions and 7 deletions
@@ -83,7 +83,10 @@ describe('McpProtocolService', () => {
},
{
provide: SkillService,
useValue: { findFlatSkillsByNames: jest.fn().mockResolvedValue([]) },
useValue: {
findFlatSkillsByNames: jest.fn().mockResolvedValue([]),
findAllFlatSkills: jest.fn().mockResolvedValue([]),
},
},
],
}).compile();
@@ -148,8 +148,16 @@ export class McpProtocolService {
inputSchema: executeToolInputSchema,
},
[LOAD_SKILL_TOOL_NAME]: {
...createLoadSkillTool((names) =>
this.skillService.findFlatSkillsByNames(names, workspace.id),
...createLoadSkillTool(
(names) =>
this.skillService.findFlatSkillsByNames(names, workspace.id),
async () => {
const skills = await this.skillService.findAllFlatSkills(
workspace.id,
);
return skills.map((skill) => skill.name);
},
),
inputSchema: zodSchema(loadSkillInputSchema),
},
@@ -18,6 +18,7 @@ export {
LOAD_SKILL_TOOL_NAME,
createLoadSkillTool,
loadSkillInputSchema,
type ListAvailableSkillNamesFunction,
type LoadSkillFunction,
type LoadSkillInput,
type LoadSkillResult,
@@ -24,8 +24,12 @@ export type LoadSkillResult = {
};
export type LoadSkillFunction = (names: string[]) => Promise<FlatSkill[]>;
export type ListAvailableSkillNamesFunction = () => Promise<string[]>;
export const createLoadSkillTool = (loadSkills: LoadSkillFunction) => ({
export const createLoadSkillTool = (
loadSkills: LoadSkillFunction,
listAvailableSkillNames: ListAvailableSkillNamesFunction,
) => ({
description:
'Load specialized skills for complex tasks. Returns detailed step-by-step instructions for building workflows, dashboards, manipulating data, or managing metadata. Call this before attempting complex operations.',
inputSchema: loadSkillInputSchema,
@@ -35,9 +39,14 @@ export const createLoadSkillTool = (loadSkills: LoadSkillFunction) => ({
const skills = await loadSkills(skillNames);
if (skills.length === 0) {
const availableNames = await listAvailableSkillNames();
return {
skills: [],
message: `No skills found with names: ${skillNames.join(', ')}. Available skills: workflow-building, data-manipulation, dashboard-building, metadata-building, research, code-interpreter, xlsx, pdf, docx, pptx.`,
message:
availableNames.length > 0
? `No skills found with names: ${skillNames.join(', ')}. Available skills: ${availableNames.join(', ')}.`
: `No skills found with names: ${skillNames.join(', ')}. No skills are currently available.`,
};
}
@@ -200,8 +200,16 @@ export class ChatExecutionService {
toolContext,
directTools,
),
[LOAD_SKILL_TOOL_NAME]: createLoadSkillTool((skillNames) =>
this.skillService.findFlatSkillsByNames(skillNames, workspace.id),
[LOAD_SKILL_TOOL_NAME]: createLoadSkillTool(
(skillNames) =>
this.skillService.findFlatSkillsByNames(skillNames, workspace.id),
async () => {
const skills = await this.skillService.findAllFlatSkills(
workspace.id,
);
return skills.map((skill) => skill.name);
},
),
};