Skip to content

Commit e7da474

Browse files
committed
fix: make MCP task recall tolerate search backend outages
1 parent 577f701 commit e7da474

2 files changed

Lines changed: 66 additions & 7 deletions

File tree

server/src/services/memory/recall.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,51 @@ test('recallForTask returns no-matches when semantic hits are fully filtered out
133133
assert.deepEqual(result.global_memories, []);
134134
});
135135

136+
test('recallForTask returns an empty recall with warning when semantic search backend is unavailable', async () => {
137+
const db = {
138+
exec(sql: string) {
139+
if (sql.includes('SELECT canonical_key FROM project_key_aliases')) {
140+
return [{ columns: ['canonical_key'], values: [] }];
141+
}
142+
if (sql.includes('FROM project_key_aliases')) {
143+
return [{
144+
columns: ['alias_key'],
145+
values: [['git:project-a']],
146+
}];
147+
}
148+
throw new Error(`Unexpected SQL: ${sql}`);
149+
},
150+
};
151+
152+
const result = await recallForTask(
153+
{
154+
mode: 'task',
155+
task: {
156+
goal: 'Evaluate Glama MCP listing quality',
157+
task_kind: 'config',
158+
project_key: 'git:project-a',
159+
},
160+
options: {
161+
project_limit: 3,
162+
global_limit: 0,
163+
include_relations: false,
164+
},
165+
},
166+
{
167+
db: db as never,
168+
semanticSearch: async () => {
169+
throw new Error('Cannot connect to API: connect ECONNREFUSED 127.0.0.1:11434');
170+
},
171+
},
172+
);
173+
174+
assert.equal(result.reason, 'no-matches');
175+
assert.deepEqual(result.project_memories, []);
176+
assert.deepEqual(result.global_memories, []);
177+
assert.match(result.warnings.join('\n'), /Semantic memory recall unavailable/);
178+
assert.match(result.warnings.join('\n'), /ECONNREFUSED/);
179+
});
180+
136181
test('recallForTask derives project_key from project_dir when the caller does not provide one', async () => {
137182
const projectDir = process.cwd().replace(/\\/g, '/');
138183
const derivedProjectKey = deriveProjectKey(

server/src/services/memory/recall.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,27 @@ export async function recallForTask(
6868
);
6969
}
7070

71-
const hits = await search(
72-
[request.task.goal, ...(request.task.error_signatures ?? [])]
73-
.filter(Boolean)
74-
.join('\n'),
75-
Math.max((projectLimit + globalLimit) * 3, 1),
76-
includeRelations,
77-
);
71+
let hits: Awaited<ReturnType<typeof semanticSearch>>;
72+
try {
73+
hits = await search(
74+
[request.task.goal, ...(request.task.error_signatures ?? [])]
75+
.filter(Boolean)
76+
.join('\n'),
77+
Math.max((projectLimit + globalLimit) * 3, 1),
78+
includeRelations,
79+
);
80+
} catch (error) {
81+
const message = error instanceof Error ? error.message : String(error);
82+
warnings.push(`Semantic memory recall unavailable: ${message}`);
83+
return {
84+
mode: request.mode,
85+
project_key: canonicalProjectKey,
86+
reason: canonicalProjectKey ? 'no-matches' : 'no-project-key',
87+
warnings,
88+
project_memories: [],
89+
global_memories: [],
90+
};
91+
}
7892
const noteIds = [...new Set(hits.map((hit) => hit.noteId))];
7993

8094
if (noteIds.length === 0) {

0 commit comments

Comments
 (0)