@elliotding/ai-agent-mcp 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/search/coordinator.d.ts +26 -0
- package/dist/search/coordinator.d.ts.map +1 -0
- package/dist/search/coordinator.js +85 -0
- package/dist/search/coordinator.js.map +1 -0
- package/dist/search/index.d.ts +8 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +13 -0
- package/dist/search/index.js.map +1 -0
- package/dist/search/tier1-keyword-match.d.ts +51 -0
- package/dist/search/tier1-keyword-match.d.ts.map +1 -0
- package/dist/search/tier1-keyword-match.js +132 -0
- package/dist/search/tier1-keyword-match.js.map +1 -0
- package/dist/search/tier2-fuzzy-search.d.ts +29 -0
- package/dist/search/tier2-fuzzy-search.d.ts.map +1 -0
- package/dist/search/tier2-fuzzy-search.js +105 -0
- package/dist/search/tier2-fuzzy-search.js.map +1 -0
- package/dist/tools/search-resources.d.ts.map +1 -1
- package/dist/tools/search-resources.js +15 -3
- package/dist/tools/search-resources.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search Coordinator
|
|
3
|
+
* Orchestrates Tier 1 + Tier 2 search and merges results
|
|
4
|
+
*/
|
|
5
|
+
import type { ResourceCandidate, ScoredResult } from './tier1-keyword-match';
|
|
6
|
+
export declare class SearchCoordinator {
|
|
7
|
+
private tier1;
|
|
8
|
+
private tier2;
|
|
9
|
+
constructor();
|
|
10
|
+
/**
|
|
11
|
+
* Merge results from multiple tiers (dedup + keep highest score)
|
|
12
|
+
*/
|
|
13
|
+
private mergeResults;
|
|
14
|
+
/**
|
|
15
|
+
* Execute enhanced search (two-tier architecture)
|
|
16
|
+
*
|
|
17
|
+
* Strategy:
|
|
18
|
+
* 1. Tier 1: Precise keyword matching
|
|
19
|
+
* 2. If Tier 1 has enough high-quality results (≥3 and top score ≥70), return directly
|
|
20
|
+
* 3. Otherwise, run Tier 2 fuzzy search to supplement
|
|
21
|
+
* 4. Merge and deduplicate
|
|
22
|
+
* 5. Filter very low scores (< 20)
|
|
23
|
+
*/
|
|
24
|
+
enhancedSearch(query: string, apiResults: ResourceCandidate[], maxResults?: number): ScoredResult[];
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=coordinator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinator.d.ts","sourceRoot":"","sources":["../../src/search/coordinator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE7E,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,KAAK,CAAgB;;IAO7B;;OAEG;IACH,OAAO,CAAC,YAAY;IA6BpB;;;;;;;;;OASG;IACH,cAAc,CACZ,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,iBAAiB,EAAE,EAC/B,UAAU,GAAE,MAAW,GACtB,YAAY,EAAE;CAwClB"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Search Coordinator
|
|
4
|
+
* Orchestrates Tier 1 + Tier 2 search and merges results
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.SearchCoordinator = void 0;
|
|
8
|
+
const logger_1 = require("../utils/logger");
|
|
9
|
+
const tier1_keyword_match_1 = require("./tier1-keyword-match");
|
|
10
|
+
const tier2_fuzzy_search_1 = require("./tier2-fuzzy-search");
|
|
11
|
+
class SearchCoordinator {
|
|
12
|
+
tier1;
|
|
13
|
+
tier2;
|
|
14
|
+
constructor() {
|
|
15
|
+
this.tier1 = new tier1_keyword_match_1.KeywordMatcher();
|
|
16
|
+
this.tier2 = new tier2_fuzzy_search_1.FuzzySearcher();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Merge results from multiple tiers (dedup + keep highest score)
|
|
20
|
+
*/
|
|
21
|
+
mergeResults(resultSets) {
|
|
22
|
+
const resultMap = new Map(); // key: resource.id
|
|
23
|
+
for (const results of resultSets) {
|
|
24
|
+
for (const result of results) {
|
|
25
|
+
const existing = resultMap.get(result.id);
|
|
26
|
+
if (existing) {
|
|
27
|
+
// Keep higher score, same score prefer lower tier (higher priority)
|
|
28
|
+
if (result.score > existing.score ||
|
|
29
|
+
(result.score === existing.score && result.match_tier < existing.match_tier)) {
|
|
30
|
+
resultMap.set(result.id, result);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
resultMap.set(result.id, result);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Sort: score desc → tier asc → name alpha
|
|
39
|
+
return Array.from(resultMap.values()).sort((a, b) => {
|
|
40
|
+
if (a.score !== b.score)
|
|
41
|
+
return b.score - a.score;
|
|
42
|
+
if (a.match_tier !== b.match_tier)
|
|
43
|
+
return a.match_tier - b.match_tier;
|
|
44
|
+
return a.name.localeCompare(b.name);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Execute enhanced search (two-tier architecture)
|
|
49
|
+
*
|
|
50
|
+
* Strategy:
|
|
51
|
+
* 1. Tier 1: Precise keyword matching
|
|
52
|
+
* 2. If Tier 1 has enough high-quality results (≥3 and top score ≥70), return directly
|
|
53
|
+
* 3. Otherwise, run Tier 2 fuzzy search to supplement
|
|
54
|
+
* 4. Merge and deduplicate
|
|
55
|
+
* 5. Filter very low scores (< 20)
|
|
56
|
+
*/
|
|
57
|
+
enhancedSearch(query, apiResults, maxResults = 10) {
|
|
58
|
+
logger_1.logger.info({ query, candidateCount: apiResults.length }, 'Enhanced search started');
|
|
59
|
+
// 1. Tier 1: Precise keyword matching
|
|
60
|
+
const tier1Results = this.tier1.search(query, apiResults);
|
|
61
|
+
// Filter low scores immediately
|
|
62
|
+
const tier1Filtered = tier1Results.filter((r) => r.score >= 20);
|
|
63
|
+
// If Tier 1 has enough high-quality results, return directly
|
|
64
|
+
if (tier1Filtered.length >= 3 && tier1Filtered[0] && tier1Filtered[0].score >= 70) {
|
|
65
|
+
logger_1.logger.info({ tier1Count: tier1Filtered.length, topScore: tier1Filtered[0].score }, 'Tier 1 sufficient, skipping Tier 2');
|
|
66
|
+
return tier1Filtered.slice(0, maxResults);
|
|
67
|
+
}
|
|
68
|
+
// 2. Tier 2: Fuzzy search to supplement
|
|
69
|
+
const tier2Results = this.tier2.search(query, apiResults);
|
|
70
|
+
// 3. Merge and deduplicate
|
|
71
|
+
const merged = this.mergeResults([tier1Filtered, tier2Results]);
|
|
72
|
+
// 4. Filter very low scores (< 20) and limit count
|
|
73
|
+
const filtered = merged.filter((r) => r.score >= 20).slice(0, maxResults);
|
|
74
|
+
logger_1.logger.info({
|
|
75
|
+
tier1Count: tier1Filtered.length,
|
|
76
|
+
tier2Count: tier2Results.length,
|
|
77
|
+
mergedCount: merged.length,
|
|
78
|
+
finalCount: filtered.length,
|
|
79
|
+
topScore: filtered.length > 0 && filtered[0] ? filtered[0].score : undefined,
|
|
80
|
+
}, 'Enhanced search completed');
|
|
81
|
+
return filtered;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.SearchCoordinator = SearchCoordinator;
|
|
85
|
+
//# sourceMappingURL=coordinator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinator.js","sourceRoot":"","sources":["../../src/search/coordinator.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,4CAAyC;AACzC,+DAAuD;AACvD,6DAAqD;AAGrD,MAAa,iBAAiB;IACpB,KAAK,CAAiB;IACtB,KAAK,CAAgB;IAE7B;QACE,IAAI,CAAC,KAAK,GAAG,IAAI,oCAAc,EAAE,CAAC;QAClC,IAAI,CAAC,KAAK,GAAG,IAAI,kCAAa,EAAE,CAAC;IACnC,CAAC;IAED;;OAEG;IACK,YAAY,CAAC,UAA4B;QAC/C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAwB,CAAC,CAAC,mBAAmB;QAEtE,KAAK,MAAM,OAAO,IAAI,UAAU,EAAE,CAAC;YACjC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAE1C,IAAI,QAAQ,EAAE,CAAC;oBACb,oEAAoE;oBACpE,IACE,MAAM,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK;wBAC7B,CAAC,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,KAAK,IAAI,MAAM,CAAC,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC,EAC5E,CAAC;wBACD,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;oBACnC,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;gBACnC,CAAC;YACH,CAAC;QACH,CAAC;QAED,2CAA2C;QAC3C,OAAO,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAClD,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK;gBAAE,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;YAClD,IAAI,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,UAAU;gBAAE,OAAO,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;YACtE,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;OASG;IACH,cAAc,CACZ,KAAa,EACb,UAA+B,EAC/B,aAAqB,EAAE;QAEvB,eAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,UAAU,CAAC,MAAM,EAAE,EAAE,yBAAyB,CAAC,CAAC;QAErF,sCAAsC;QACtC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAE1D,gCAAgC;QAChC,MAAM,aAAa,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QAEhE,6DAA6D;QAC7D,IAAI,aAAa,CAAC,MAAM,IAAI,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;YAClF,eAAM,CAAC,IAAI,CACT,EAAE,UAAU,EAAE,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,EACtE,oCAAoC,CACrC,CAAC;YACF,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QAC5C,CAAC;QAED,wCAAwC;QACxC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAE1D,2BAA2B;QAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC;QAEhE,mDAAmD;QACnD,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QAE1E,eAAM,CAAC,IAAI,CACT;YACE,UAAU,EAAE,aAAa,CAAC,MAAM;YAChC,UAAU,EAAE,YAAY,CAAC,MAAM;YAC/B,WAAW,EAAE,MAAM,CAAC,MAAM;YAC1B,UAAU,EAAE,QAAQ,CAAC,MAAM;YAC3B,QAAQ,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;SAC7E,EACD,2BAA2B,CAC5B,CAAC;QAEF,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF;AA/FD,8CA+FC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search Module Exports
|
|
3
|
+
*/
|
|
4
|
+
export { SearchCoordinator } from './coordinator';
|
|
5
|
+
export { KeywordMatcher } from './tier1-keyword-match';
|
|
6
|
+
export { FuzzySearcher } from './tier2-fuzzy-search';
|
|
7
|
+
export type { ResourceCandidate, ScoredResult } from './tier1-keyword-match';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/search/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Search Module Exports
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FuzzySearcher = exports.KeywordMatcher = exports.SearchCoordinator = void 0;
|
|
7
|
+
var coordinator_1 = require("./coordinator");
|
|
8
|
+
Object.defineProperty(exports, "SearchCoordinator", { enumerable: true, get: function () { return coordinator_1.SearchCoordinator; } });
|
|
9
|
+
var tier1_keyword_match_1 = require("./tier1-keyword-match");
|
|
10
|
+
Object.defineProperty(exports, "KeywordMatcher", { enumerable: true, get: function () { return tier1_keyword_match_1.KeywordMatcher; } });
|
|
11
|
+
var tier2_fuzzy_search_1 = require("./tier2-fuzzy-search");
|
|
12
|
+
Object.defineProperty(exports, "FuzzySearcher", { enumerable: true, get: function () { return tier2_fuzzy_search_1.FuzzySearcher; } });
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/search/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,6CAAkD;AAAzC,gHAAA,iBAAiB,OAAA;AAC1B,6DAAuD;AAA9C,qHAAA,cAAc,OAAA;AACvB,2DAAqD;AAA5C,mHAAA,aAAa,OAAA"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier 1: Keyword Matcher
|
|
3
|
+
* Precise keyword matching with weighted scoring (name > description)
|
|
4
|
+
*/
|
|
5
|
+
export interface ResourceCandidate {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
type: string;
|
|
10
|
+
team: string;
|
|
11
|
+
version: string;
|
|
12
|
+
is_subscribed: boolean;
|
|
13
|
+
download_url?: string;
|
|
14
|
+
metadata?: unknown;
|
|
15
|
+
}
|
|
16
|
+
export interface ScoredResult extends ResourceCandidate {
|
|
17
|
+
score: number;
|
|
18
|
+
match_tier: 1 | 2;
|
|
19
|
+
match_reason: string;
|
|
20
|
+
excerpt?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare class KeywordMatcher {
|
|
23
|
+
private readonly STOP_WORDS;
|
|
24
|
+
/**
|
|
25
|
+
* Extract keywords from query (supports Chinese and English)
|
|
26
|
+
*/
|
|
27
|
+
private extractKeywords;
|
|
28
|
+
/**
|
|
29
|
+
* Escape special regex characters
|
|
30
|
+
*/
|
|
31
|
+
private escapeRegex;
|
|
32
|
+
/**
|
|
33
|
+
* Calculate match score with weighted fields
|
|
34
|
+
*
|
|
35
|
+
* Scoring rules:
|
|
36
|
+
* - name field weight: 3x
|
|
37
|
+
* - description field weight: 1x
|
|
38
|
+
* - Whole word match > partial match
|
|
39
|
+
* - If name doesn't match at all, force 70% penalty
|
|
40
|
+
*/
|
|
41
|
+
private calculateScore;
|
|
42
|
+
/**
|
|
43
|
+
* Build human-readable match reason
|
|
44
|
+
*/
|
|
45
|
+
private buildMatchReason;
|
|
46
|
+
/**
|
|
47
|
+
* Execute Tier 1 search
|
|
48
|
+
*/
|
|
49
|
+
search(query: string, candidates: ResourceCandidate[]): ScoredResult[];
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=tier1-keyword-match.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tier1-keyword-match.d.ts","sourceRoot":"","sources":["../../src/search/tier1-keyword-match.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,YAAa,SAAQ,iBAAiB;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAOxB;IAEH;;OAEG;IACH,OAAO,CAAC,eAAe;IAYvB;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;;;;;;;OAQG;IACH,OAAO,CAAC,cAAc;IA6CtB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,EAAE,GAAG,YAAY,EAAE;CAgCvE"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tier 1: Keyword Matcher
|
|
4
|
+
* Precise keyword matching with weighted scoring (name > description)
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.KeywordMatcher = void 0;
|
|
8
|
+
const logger_1 = require("../utils/logger");
|
|
9
|
+
class KeywordMatcher {
|
|
10
|
+
STOP_WORDS = new Set([
|
|
11
|
+
// English stop words
|
|
12
|
+
'a', 'an', 'and', 'are', 'as', 'at', 'be', 'by', 'for', 'from',
|
|
13
|
+
'has', 'he', 'in', 'is', 'it', 'its', 'of', 'on', 'that', 'the',
|
|
14
|
+
'to', 'was', 'will', 'with',
|
|
15
|
+
// Chinese stop words
|
|
16
|
+
'的', '是', '在', '和', '了', '有', '与', '这', '个', '我',
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* Extract keywords from query (supports Chinese and English)
|
|
20
|
+
*/
|
|
21
|
+
extractKeywords(query) {
|
|
22
|
+
// Split by spaces, underscores, hyphens, and Chinese characters
|
|
23
|
+
const words = query
|
|
24
|
+
.toLowerCase()
|
|
25
|
+
.split(/[\s_\-]+/)
|
|
26
|
+
.map((w) => w.trim())
|
|
27
|
+
.filter((w) => w.length > 0);
|
|
28
|
+
// Filter out stop words
|
|
29
|
+
return words.filter((w) => !this.STOP_WORDS.has(w));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Escape special regex characters
|
|
33
|
+
*/
|
|
34
|
+
escapeRegex(str) {
|
|
35
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Calculate match score with weighted fields
|
|
39
|
+
*
|
|
40
|
+
* Scoring rules:
|
|
41
|
+
* - name field weight: 3x
|
|
42
|
+
* - description field weight: 1x
|
|
43
|
+
* - Whole word match > partial match
|
|
44
|
+
* - If name doesn't match at all, force 70% penalty
|
|
45
|
+
*/
|
|
46
|
+
calculateScore(keywords, resource) {
|
|
47
|
+
const lowerName = resource.name.toLowerCase();
|
|
48
|
+
const lowerDesc = resource.description.toLowerCase();
|
|
49
|
+
let nameMatchCount = 0;
|
|
50
|
+
let descMatchCount = 0;
|
|
51
|
+
for (const keyword of keywords) {
|
|
52
|
+
// Prefer whole word matching (using regex \b boundary)
|
|
53
|
+
const wordBoundaryRegex = new RegExp(`\\b${this.escapeRegex(keyword)}\\b`, 'i');
|
|
54
|
+
if (wordBoundaryRegex.test(resource.name)) {
|
|
55
|
+
nameMatchCount++;
|
|
56
|
+
}
|
|
57
|
+
else if (lowerName.includes(keyword)) {
|
|
58
|
+
nameMatchCount += 0.7; // Partial match penalty
|
|
59
|
+
}
|
|
60
|
+
if (wordBoundaryRegex.test(resource.description)) {
|
|
61
|
+
descMatchCount++;
|
|
62
|
+
}
|
|
63
|
+
else if (lowerDesc.includes(keyword)) {
|
|
64
|
+
descMatchCount += 0.5; // Description partial match penalty
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// ✅ Critical rule: If name doesn't match at all, force penalty
|
|
68
|
+
if (nameMatchCount === 0 && descMatchCount > 0) {
|
|
69
|
+
// Description matches but name doesn't, only give 30% of base score
|
|
70
|
+
// This ensures resources like "release-log-review" (only mentions "build info" in description)
|
|
71
|
+
// don't get high scores (e.g., 25 → 7)
|
|
72
|
+
const baseScore = (descMatchCount * 1) / keywords.length * 25;
|
|
73
|
+
return Math.floor(baseScore * 0.3); // Force 70% penalty
|
|
74
|
+
}
|
|
75
|
+
// Name has matches, calculate normally
|
|
76
|
+
const weightedScore = (nameMatchCount * 3 + descMatchCount * 1) / keywords.length;
|
|
77
|
+
let score = Math.floor(weightedScore * 25); // Map to 0-100
|
|
78
|
+
// Bonus: All keywords match in name
|
|
79
|
+
if (nameMatchCount >= keywords.length && keywords.length > 1) {
|
|
80
|
+
score = Math.min(100, score + 20);
|
|
81
|
+
}
|
|
82
|
+
return score;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Build human-readable match reason
|
|
86
|
+
*/
|
|
87
|
+
buildMatchReason(keywords, resource) {
|
|
88
|
+
const lowerName = resource.name.toLowerCase();
|
|
89
|
+
const lowerDesc = resource.description.toLowerCase();
|
|
90
|
+
const nameMatches = keywords.filter((k) => lowerName.includes(k));
|
|
91
|
+
const descMatches = keywords.filter((k) => lowerDesc.includes(k) && !lowerName.includes(k));
|
|
92
|
+
if (nameMatches.length === keywords.length) {
|
|
93
|
+
return `Name matches all keywords: ${nameMatches.join(', ')}`;
|
|
94
|
+
}
|
|
95
|
+
if (nameMatches.length > 0) {
|
|
96
|
+
return `Name matches: ${nameMatches.join(', ')}`;
|
|
97
|
+
}
|
|
98
|
+
if (descMatches.length > 0) {
|
|
99
|
+
return `Description matches: ${descMatches.join(', ')}`;
|
|
100
|
+
}
|
|
101
|
+
return 'No keyword match';
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Execute Tier 1 search
|
|
105
|
+
*/
|
|
106
|
+
search(query, candidates) {
|
|
107
|
+
const keywords = this.extractKeywords(query);
|
|
108
|
+
if (keywords.length === 0) {
|
|
109
|
+
logger_1.logger.debug({ query }, 'No valid keywords extracted');
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
logger_1.logger.debug({ query, keywords }, 'Tier 1 keyword search started');
|
|
113
|
+
const results = [];
|
|
114
|
+
for (const resource of candidates) {
|
|
115
|
+
const score = this.calculateScore(keywords, resource);
|
|
116
|
+
if (score > 0) {
|
|
117
|
+
results.push({
|
|
118
|
+
...resource,
|
|
119
|
+
score,
|
|
120
|
+
match_tier: 1,
|
|
121
|
+
match_reason: this.buildMatchReason(keywords, resource),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Sort by score descending
|
|
126
|
+
const sorted = results.sort((a, b) => b.score - a.score);
|
|
127
|
+
logger_1.logger.debug({ resultCount: sorted.length, topScore: sorted[0]?.score }, 'Tier 1 search completed');
|
|
128
|
+
return sorted;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
exports.KeywordMatcher = KeywordMatcher;
|
|
132
|
+
//# sourceMappingURL=tier1-keyword-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tier1-keyword-match.js","sourceRoot":"","sources":["../../src/search/tier1-keyword-match.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier 2: Fuzzy Searcher
|
|
3
|
+
* Semantic fuzzy search powered by Fuse.js
|
|
4
|
+
*/
|
|
5
|
+
import type { ResourceCandidate, ScoredResult } from './tier1-keyword-match';
|
|
6
|
+
export declare class FuzzySearcher {
|
|
7
|
+
private readonly fuseOptions;
|
|
8
|
+
/**
|
|
9
|
+
* Prepare searchable data structure
|
|
10
|
+
*/
|
|
11
|
+
private prepareSearchableData;
|
|
12
|
+
/**
|
|
13
|
+
* Convert Fuse score (0=best) to normal score (100=best)
|
|
14
|
+
*/
|
|
15
|
+
private convertFuseScore;
|
|
16
|
+
/**
|
|
17
|
+
* Extract excerpt (matched context)
|
|
18
|
+
*/
|
|
19
|
+
private extractExcerpt;
|
|
20
|
+
/**
|
|
21
|
+
* Build match reason
|
|
22
|
+
*/
|
|
23
|
+
private buildMatchReason;
|
|
24
|
+
/**
|
|
25
|
+
* Execute Tier 2 search
|
|
26
|
+
*/
|
|
27
|
+
search(query: string, candidates: ResourceCandidate[]): ScoredResult[];
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=tier2-fuzzy-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tier2-fuzzy-search.d.ts","sourceRoot":"","sources":["../../src/search/tier2-fuzzy-search.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAM7E,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAY1B;IAEF;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAO7B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAKxB;;OAEG;IACH,OAAO,CAAC,cAAc;IAqBtB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAcxB;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,EAAE,GAAG,YAAY,EAAE;CA8BvE"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tier 2: Fuzzy Searcher
|
|
4
|
+
* Semantic fuzzy search powered by Fuse.js
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.FuzzySearcher = void 0;
|
|
11
|
+
const fuse_js_1 = __importDefault(require("fuse.js"));
|
|
12
|
+
const logger_1 = require("../utils/logger");
|
|
13
|
+
class FuzzySearcher {
|
|
14
|
+
fuseOptions = {
|
|
15
|
+
keys: [
|
|
16
|
+
{ name: 'name', weight: 0.5 }, // name has highest weight
|
|
17
|
+
{ name: 'description', weight: 0.3 }, // description second
|
|
18
|
+
{ name: 'searchableContent', weight: 0.2 }, // combined content lowest
|
|
19
|
+
],
|
|
20
|
+
threshold: 0.35, // Balanced threshold (0.3 too strict for Chinese, 0.4 too loose)
|
|
21
|
+
includeScore: true, // Return match scores
|
|
22
|
+
minMatchCharLength: 2, // Keep 2 for Chinese (Chinese chars are 2-3 bytes)
|
|
23
|
+
ignoreLocation: true, // Don't restrict match position
|
|
24
|
+
useExtendedSearch: false, // Don't need advanced query syntax
|
|
25
|
+
distance: 100, // Slightly relaxed for Chinese (was 50)
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Prepare searchable data structure
|
|
29
|
+
*/
|
|
30
|
+
prepareSearchableData(candidates) {
|
|
31
|
+
return candidates.map((resource) => ({
|
|
32
|
+
...resource,
|
|
33
|
+
searchableContent: `${resource.name} ${resource.description}`,
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Convert Fuse score (0=best) to normal score (100=best)
|
|
38
|
+
*/
|
|
39
|
+
convertFuseScore(fuseScore) {
|
|
40
|
+
const score = fuseScore ?? 0;
|
|
41
|
+
return Math.max(0, Math.min(100, Math.floor((1 - score) * 100)));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Extract excerpt (matched context)
|
|
45
|
+
*/
|
|
46
|
+
extractExcerpt(text, query, maxLength = 150) {
|
|
47
|
+
const lowerText = text.toLowerCase();
|
|
48
|
+
const lowerQuery = query.toLowerCase();
|
|
49
|
+
const index = lowerText.indexOf(lowerQuery);
|
|
50
|
+
if (index === -1) {
|
|
51
|
+
// No exact match, return first 150 chars
|
|
52
|
+
return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
|
|
53
|
+
}
|
|
54
|
+
// Extract 50 chars before and after match
|
|
55
|
+
const start = Math.max(0, index - 50);
|
|
56
|
+
const end = Math.min(text.length, index + query.length + 50);
|
|
57
|
+
let excerpt = text.substring(start, end);
|
|
58
|
+
if (start > 0)
|
|
59
|
+
excerpt = '...' + excerpt;
|
|
60
|
+
if (end < text.length)
|
|
61
|
+
excerpt = excerpt + '...';
|
|
62
|
+
return excerpt;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Build match reason
|
|
66
|
+
*/
|
|
67
|
+
buildMatchReason(query, resource) {
|
|
68
|
+
const lowerName = resource.name.toLowerCase();
|
|
69
|
+
const lowerDesc = resource.description.toLowerCase();
|
|
70
|
+
const lowerQuery = query.toLowerCase();
|
|
71
|
+
if (lowerName.includes(lowerQuery)) {
|
|
72
|
+
return `Name contains: "${query}"`;
|
|
73
|
+
}
|
|
74
|
+
if (lowerDesc.includes(lowerQuery)) {
|
|
75
|
+
return `Description mentions: "${query}"`;
|
|
76
|
+
}
|
|
77
|
+
return `Content semantically matches: "${query}"`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Execute Tier 2 search
|
|
81
|
+
*/
|
|
82
|
+
search(query, candidates) {
|
|
83
|
+
logger_1.logger.debug({ query, candidateCount: candidates.length }, 'Tier 2 fuzzy search started');
|
|
84
|
+
const searchableData = this.prepareSearchableData(candidates);
|
|
85
|
+
const fuse = new fuse_js_1.default(searchableData, this.fuseOptions);
|
|
86
|
+
const fuseResults = fuse.search(query);
|
|
87
|
+
const results = fuseResults.map((result) => {
|
|
88
|
+
const score = this.convertFuseScore(result.score);
|
|
89
|
+
const resource = result.item;
|
|
90
|
+
return {
|
|
91
|
+
...resource,
|
|
92
|
+
score,
|
|
93
|
+
match_tier: 2,
|
|
94
|
+
match_reason: this.buildMatchReason(query, resource),
|
|
95
|
+
excerpt: this.extractExcerpt(resource.description, query),
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
// Filter low scores (< 40 for Tier 2, stricter than Tier 1)
|
|
99
|
+
const filtered = results.filter((r) => r.score >= 40);
|
|
100
|
+
logger_1.logger.debug({ resultCount: filtered.length, topScore: filtered[0]?.score }, 'Tier 2 search completed');
|
|
101
|
+
return filtered;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.FuzzySearcher = FuzzySearcher;
|
|
105
|
+
//# sourceMappingURL=tier2-fuzzy-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tier2-fuzzy-search.js","sourceRoot":"","sources":["../../src/search/tier2-fuzzy-search.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-resources.d.ts","sourceRoot":"","sources":["../../src/tools/search-resources.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAyB,qBAAqB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"search-resources.d.ts","sourceRoot":"","sources":["../../src/tools/search-resources.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAyB,qBAAqB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAmD/F,wBAAsB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC,CAkHjG;AAGD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6B/B,CAAC"}
|
|
@@ -11,6 +11,9 @@ const client_1 = require("../api/client");
|
|
|
11
11
|
const manager_1 = require("../filesystem/manager");
|
|
12
12
|
const cursor_paths_js_1 = require("../utils/cursor-paths.js");
|
|
13
13
|
const errors_1 = require("../types/errors");
|
|
14
|
+
const search_1 = require("../search");
|
|
15
|
+
// Search coordinator singleton
|
|
16
|
+
const searchCoordinator = new search_1.SearchCoordinator();
|
|
14
17
|
// Simple in-memory cache
|
|
15
18
|
const searchCache = new Map();
|
|
16
19
|
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
@@ -76,8 +79,17 @@ async function searchResources(params) {
|
|
|
76
79
|
type: typedParams.type,
|
|
77
80
|
keyword: typedParams.keyword,
|
|
78
81
|
}, typedParams.user_token);
|
|
82
|
+
// ✅ MCP Server-side enhanced search (Tier 1 + Tier 2)
|
|
83
|
+
logger_1.logger.debug({ apiResultCount: searchResults.results.length }, 'Applying MCP-side search enhancement...');
|
|
84
|
+
const enhancedResults = searchCoordinator.enhancedSearch(typedParams.keyword || '', searchResults.results, 20 // maxResults
|
|
85
|
+
);
|
|
86
|
+
logger_1.logger.info({
|
|
87
|
+
apiResults: searchResults.results.length,
|
|
88
|
+
enhancedResults: enhancedResults.length,
|
|
89
|
+
topScore: enhancedResults[0]?.score
|
|
90
|
+
}, 'Search enhancement applied');
|
|
79
91
|
// Check subscription and installation status for each result
|
|
80
|
-
const
|
|
92
|
+
const finalResults = await Promise.all(enhancedResults.map(async (resource) => {
|
|
81
93
|
// Check if installed locally in the Cursor directory for this resource type
|
|
82
94
|
let isInstalled = false;
|
|
83
95
|
try {
|
|
@@ -95,8 +107,8 @@ async function searchResources(params) {
|
|
|
95
107
|
}));
|
|
96
108
|
// Build final result
|
|
97
109
|
const result = {
|
|
98
|
-
total:
|
|
99
|
-
results:
|
|
110
|
+
total: finalResults.length,
|
|
111
|
+
results: finalResults,
|
|
100
112
|
};
|
|
101
113
|
// Cache the results
|
|
102
114
|
cacheResults(cacheKey, result);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-resources.js","sourceRoot":"","sources":["../../src/tools/search-resources.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;
|
|
1
|
+
{"version":3,"file":"search-resources.js","sourceRoot":"","sources":["../../src/tools/search-resources.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AA0DH,0CAkHC;AA1KD,4CAAsD;AACtD,0CAA0C;AAC1C,mDAA0D;AAC1D,8DAAiE;AACjE,4CAAiD;AAEjD,sCAA8C;AAE9C,+BAA+B;AAC/B,MAAM,iBAAiB,GAAG,IAAI,0BAAiB,EAAE,CAAC;AAElD,yBAAyB;AACzB,MAAM,WAAW,GAAG,IAAI,GAAG,EAAiE,CAAC;AAC7F,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;AAE7C;;GAEG;AACH,SAAS,WAAW,CAAC,MAA6B;IAChD,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE;QACvB,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE;QACvB,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE;KAC9B,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,QAAgB;IACxC,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,SAAS,GAAG,SAAS,EAAE,CAAC;QACxD,eAAM,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,EAAE,kBAAkB,CAAC,CAAC;QAC/C,OAAO,MAAM,CAAC,OAAO,CAAC;IACxB,CAAC;IAED,IAAI,MAAM,EAAE,CAAC;QACX,6BAA6B;QAC7B,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7B,eAAM,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,EAAE,+BAA+B,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,QAAgB,EAAE,OAA8B;IACpE,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE;QACxB,OAAO;QACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC,CAAC;IACH,eAAM,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,uBAAuB,CAAC,CAAC;AAC5E,CAAC;AAEM,KAAK,UAAU,eAAe,CAAC,MAAe;IACnD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,4BAA4B;IAC5B,MAAM,WAAW,GAAG,MAA+B,CAAC;IAEpD,eAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,EAAE,yBAAyB,CAAC,CAAC;IAE7E,IAAI,CAAC;QACH,qBAAqB;QACrB,MAAM,QAAQ,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;QAE1C,oBAAoB;QACpB,MAAM,YAAY,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAChD,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YACxC,IAAA,oBAAW,EAAC,kBAAkB,EAAE,SAAS,EAAE,MAAiC,EAAE,QAAQ,CAAC,CAAC;YAExF,eAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,wCAAwC,CAAC,CAAC;YAE7G,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,IAAI,EAAE,YAAY;aACnB,CAAC;QACJ,CAAC;QAED,iBAAiB;QACjB,eAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,EAAE,wBAAwB,CAAC,CAAC;QAEzH,MAAM,aAAa,GAAG,MAAM,kBAAS,CAAC,eAAe,CACnD;YACE,IAAI,EAAE,WAAW,CAAC,IAAI;YACtB,IAAI,EAAE,WAAW,CAAC,IAAI;YACtB,OAAO,EAAE,WAAW,CAAC,OAAO;SAC7B,EACD,WAAW,CAAC,UAAU,CACvB,CAAC;QAEF,sDAAsD;QACtD,eAAM,CAAC,KAAK,CAAC,EAAE,cAAc,EAAE,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,yCAAyC,CAAC,CAAC;QAE1G,MAAM,eAAe,GAAG,iBAAiB,CAAC,cAAc,CACtD,WAAW,CAAC,OAAO,IAAI,EAAE,EACzB,aAAa,CAAC,OAAO,EACrB,EAAE,CAAC,aAAa;SACjB,CAAC;QAEF,eAAM,CAAC,IAAI,CACT;YACE,UAAU,EAAE,aAAa,CAAC,OAAO,CAAC,MAAM;YACxC,eAAe,EAAE,eAAe,CAAC,MAAM;YACvC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,KAAK;SACpC,EACD,4BAA4B,CAC7B,CAAC;QAEF,6DAA6D;QAC7D,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,GAAG,CACpC,eAAe,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;YACrC,4EAA4E;YAC5E,IAAI,WAAW,GAAG,KAAK,CAAC;YACxB,IAAI,CAAC;gBACH,MAAM,YAAY,GAAG,IAAA,uCAAqB,EAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACzE,WAAW,GAAG,MAAM,2BAAiB,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;YACjE,CAAC;YAAC,MAAM,CAAC;gBACP,6DAA6D;gBAC7D,WAAW,GAAG,KAAK,CAAC;YACtB,CAAC;YAED,OAAO;gBACL,GAAG,QAAQ;gBACX,YAAY,EAAE,WAAW;aAC1B,CAAC;QACJ,CAAC,CAAC,CACH,CAAC;QAEF,qBAAqB;QACrB,MAAM,MAAM,GAA0B;YACpC,KAAK,EAAE,YAAY,CAAC,MAAM;YAC1B,OAAO,EAAE,YAAY;SACtB,CAAC;QAEF,oBAAoB;QACpB,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAE/B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QACxC,IAAA,oBAAW,EAAC,kBAAkB,EAAE,SAAS,EAAE,MAAiC,EAAE,QAAQ,CAAC,CAAC;QAExF,eAAM,CAAC,IAAI,CACT;YACE,IAAI,EAAE,WAAW,CAAC,IAAI;YACtB,IAAI,EAAE,WAAW,CAAC,IAAI;YACtB,OAAO,EAAE,WAAW,CAAC,OAAO;YAC5B,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,QAAQ;YACR,MAAM,EAAE,KAAK;SACd,EACD,yCAAyC,CAC1C,CAAC;QAEF,OAAO;YACL,OAAO,EAAE,IAAI;YACb,IAAI,EAAE,MAAM;SACb,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,eAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,UAAU,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,yBAAyB,CAAC,CAAC;QAC3I,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE;gBACL,IAAI,EAAE,KAAK,YAAY,uBAAc,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe;gBACpE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAChE;SACF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,+BAA+B;AAClB,QAAA,mBAAmB,GAAG;IACjC,IAAI,EAAE,kBAAkB;IACxB,WAAW,EAAE,0DAA0D;IACvE,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,oCAAoC;aAClD;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,yBAAyB;gBACtC,IAAI,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;aAC9C;YACD,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,sDAAsD;aACpE;YACD,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,8EAA8E;oBAC9E,iFAAiF;aACpF;SACF;QACD,QAAQ,EAAE,CAAC,SAAS,CAAC;KACtB;IACD,OAAO,EAAE,eAAe;CACzB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elliotding/ai-agent-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "CSP AI Agent MCP Server - Centralized AI tools distribution and management",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"axios": "^1.6.0",
|
|
44
44
|
"dotenv": "^16.4.0",
|
|
45
45
|
"fastify": "^5.8.2",
|
|
46
|
+
"fuse.js": "^7.1.0",
|
|
46
47
|
"ioredis": "^5.10.0",
|
|
47
48
|
"lru-cache": "^11.2.6",
|
|
48
49
|
"pino": "^8.19.0",
|