Skip to content

Commit 55723cc

Browse files
fix(search): fall back to local search when semantic results don't match MCP tools (#352)
* handle the zero tool results in the semantic search * Address the Co-Pilot feedback
1 parent 34d3792 commit 55723cc

2 files changed

Lines changed: 65 additions & 0 deletions

File tree

src/toolsets.test.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,66 @@ describe('StackOneToolSet', () => {
845845
// No matching tools from semantic search
846846
expect(tools.length).toBe(0);
847847
});
848+
849+
it('auto mode falls back to local search when semantic results do not match MCP tools', async () => {
850+
const toolset = new StackOneToolSet({
851+
baseUrl: TEST_BASE_URL,
852+
apiKey: 'test-key',
853+
accountId: 'mixed',
854+
search: {},
855+
});
856+
857+
// Semantic returns results with IDs that won't match any MCP tool names
858+
server.use(
859+
http.post(`${TEST_BASE_URL}/actions/search`, () => {
860+
return HttpResponse.json({
861+
results: [
862+
{
863+
id: 'unknown_1.0.0_nonexistent_action_global',
864+
similarity_score: 0.95,
865+
},
866+
],
867+
total_count: 1,
868+
query: 'list employees',
869+
});
870+
}),
871+
);
872+
873+
const tools = await toolset.searchTools('list employees');
874+
875+
// Should fall back to local search and return results (not empty)
876+
expect(tools.length).toBeGreaterThan(0);
877+
});
878+
879+
it('semantic mode does not fall back when results do not match MCP tools', async () => {
880+
const toolset = new StackOneToolSet({
881+
baseUrl: TEST_BASE_URL,
882+
apiKey: 'test-key',
883+
accountId: 'mixed',
884+
search: {},
885+
});
886+
887+
// Semantic returns results with IDs that won't match any MCP tool names
888+
server.use(
889+
http.post(`${TEST_BASE_URL}/actions/search`, () => {
890+
return HttpResponse.json({
891+
results: [
892+
{
893+
id: 'unknown_1.0.0_nonexistent_action_global',
894+
similarity_score: 0.95,
895+
},
896+
],
897+
total_count: 1,
898+
query: 'list employees',
899+
});
900+
}),
901+
);
902+
903+
const tools = await toolset.searchTools('list employees', { search: 'semantic' });
904+
905+
// Semantic mode should return empty, not fall back
906+
expect(tools.length).toBe(0);
907+
});
848908
});
849909

850910
describe('searchActionNames', () => {

src/toolsets.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,11 @@ export class StackOneToolSet {
832832
(actionOrder.get(b.name) ?? Number.POSITIVE_INFINITY),
833833
);
834834

835+
// Auto mode: if semantic returned results but none matched MCP tools, fall back to local
836+
if (search === 'auto' && matchedTools.length === 0) {
837+
return this.localSearch(query, allTools, mergedOptions);
838+
}
839+
835840
return new Tools(matchedTools);
836841
} catch (error) {
837842
if (error instanceof SemanticSearchError) {

0 commit comments

Comments
 (0)