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
This commit is contained in:
Claude 2026-04-09 15:24:56 +00:00
parent 086256eb8d
commit 21ddc2821c
No known key found for this signature in database
5 changed files with 36 additions and 7 deletions

View file

@ -83,7 +83,10 @@ describe('McpProtocolService', () => {
},
{
provide: SkillService,
useValue: { findFlatSkillsByNames: jest.fn().mockResolvedValue([]) },
useValue: {
findFlatSkillsByNames: jest.fn().mockResolvedValue([]),
findAllFlatSkills: jest.fn().mockResolvedValue([]),
},
},
],
}).compile();

View file

@ -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),
},

View file

@ -18,6 +18,7 @@ export {
LOAD_SKILL_TOOL_NAME,
createLoadSkillTool,
loadSkillInputSchema,
type ListAvailableSkillNamesFunction,
type LoadSkillFunction,
type LoadSkillInput,
type LoadSkillResult,

View file

@ -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.`,
};
}

View file

@ -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);
},
),
};