mcp-resend 1.0.0-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/LICENSE +190 -0
- package/README.md +257 -0
- package/data/embeddings.json +245241 -0
- package/dist/config/environment.d.ts +152 -0
- package/dist/config/environment.d.ts.map +1 -0
- package/dist/config/environment.js +217 -0
- package/dist/config/environment.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +119 -0
- package/dist/index.js.map +1 -0
- package/dist/services/rate-limiter.d.ts +90 -0
- package/dist/services/rate-limiter.d.ts.map +1 -0
- package/dist/services/rate-limiter.js +267 -0
- package/dist/services/rate-limiter.js.map +1 -0
- package/dist/services/tool-registry.d.ts +219 -0
- package/dist/services/tool-registry.d.ts.map +1 -0
- package/dist/services/tool-registry.js +459 -0
- package/dist/services/tool-registry.js.map +1 -0
- package/dist/tools/docs/embeddings-loader.d.ts +69 -0
- package/dist/tools/docs/embeddings-loader.d.ts.map +1 -0
- package/dist/tools/docs/embeddings-loader.js +218 -0
- package/dist/tools/docs/embeddings-loader.js.map +1 -0
- package/dist/tools/docs/errors.d.ts +75 -0
- package/dist/tools/docs/errors.d.ts.map +1 -0
- package/dist/tools/docs/errors.js +145 -0
- package/dist/tools/docs/errors.js.map +1 -0
- package/dist/tools/docs/index.d.ts +11 -0
- package/dist/tools/docs/index.d.ts.map +1 -0
- package/dist/tools/docs/index.js +14 -0
- package/dist/tools/docs/index.js.map +1 -0
- package/dist/tools/docs/metrics.d.ts +94 -0
- package/dist/tools/docs/metrics.d.ts.map +1 -0
- package/dist/tools/docs/metrics.js +174 -0
- package/dist/tools/docs/metrics.js.map +1 -0
- package/dist/tools/docs/search-docs-tool.d.ts +54 -0
- package/dist/tools/docs/search-docs-tool.d.ts.map +1 -0
- package/dist/tools/docs/search-docs-tool.js +231 -0
- package/dist/tools/docs/search-docs-tool.js.map +1 -0
- package/dist/tools/docs/types.d.ts +70 -0
- package/dist/tools/docs/types.d.ts.map +1 -0
- package/dist/tools/docs/types.js +7 -0
- package/dist/tools/docs/types.js.map +1 -0
- package/dist/tools/docs/vector-search.d.ts +80 -0
- package/dist/tools/docs/vector-search.d.ts.map +1 -0
- package/dist/tools/docs/vector-search.js +297 -0
- package/dist/tools/docs/vector-search.js.map +1 -0
- package/dist/tools/index.d.ts +27 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +3413 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/utils/mcp-errors.d.ts +109 -0
- package/dist/utils/mcp-errors.d.ts.map +1 -0
- package/dist/utils/mcp-errors.js +306 -0
- package/dist/utils/mcp-errors.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics tracking for documentation search.
|
|
3
|
+
* Collects search performance metrics and outputs them as JSON lines to stderr.
|
|
4
|
+
*/
|
|
5
|
+
/** Rolling window of recent searches (max 100) */
|
|
6
|
+
const searchHistory = [];
|
|
7
|
+
/** Maximum entries in rolling window */
|
|
8
|
+
const MAX_HISTORY_SIZE = 100;
|
|
9
|
+
/** Flag to track if embeddings were loaded from cache */
|
|
10
|
+
let embeddingsCacheHit = false;
|
|
11
|
+
/**
|
|
12
|
+
* Output a metric as a JSON line to stderr.
|
|
13
|
+
*
|
|
14
|
+
* @param metric - The metric to output
|
|
15
|
+
*/
|
|
16
|
+
function emitMetric(metric) {
|
|
17
|
+
console.error(JSON.stringify(metric));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Record a successful search and emit metric.
|
|
21
|
+
*
|
|
22
|
+
* @param latencyMs - Search latency in milliseconds
|
|
23
|
+
* @param resultCount - Number of results returned
|
|
24
|
+
* @param searchType - Type of search performed
|
|
25
|
+
* @param cacheHit - Whether embeddings were cached
|
|
26
|
+
*/
|
|
27
|
+
export function recordSearch(latencyMs, resultCount, searchType, cacheHit) {
|
|
28
|
+
const entry = {
|
|
29
|
+
timestamp: Date.now(),
|
|
30
|
+
latencyMs,
|
|
31
|
+
resultCount,
|
|
32
|
+
searchType,
|
|
33
|
+
cacheHit,
|
|
34
|
+
success: true,
|
|
35
|
+
};
|
|
36
|
+
// Add to rolling window
|
|
37
|
+
searchHistory.push(entry);
|
|
38
|
+
if (searchHistory.length > MAX_HISTORY_SIZE) {
|
|
39
|
+
searchHistory.shift();
|
|
40
|
+
}
|
|
41
|
+
// Emit metric
|
|
42
|
+
emitMetric({
|
|
43
|
+
_type: "metric",
|
|
44
|
+
name: "search_completed",
|
|
45
|
+
timestamp: new Date().toISOString(),
|
|
46
|
+
search_latency_ms: latencyMs,
|
|
47
|
+
result_count: resultCount,
|
|
48
|
+
search_type: searchType,
|
|
49
|
+
cache_hit: cacheHit,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Record a search error and emit metric.
|
|
54
|
+
*
|
|
55
|
+
* @param latencyMs - Time until error in milliseconds
|
|
56
|
+
* @param errorCode - Error code from DocsErrorCode
|
|
57
|
+
* @param context - Additional error context
|
|
58
|
+
*/
|
|
59
|
+
export function recordSearchError(latencyMs, errorCode, context) {
|
|
60
|
+
const entry = {
|
|
61
|
+
timestamp: Date.now(),
|
|
62
|
+
latencyMs,
|
|
63
|
+
resultCount: 0,
|
|
64
|
+
searchType: "semantic",
|
|
65
|
+
cacheHit: embeddingsCacheHit,
|
|
66
|
+
success: false,
|
|
67
|
+
};
|
|
68
|
+
// Add to rolling window
|
|
69
|
+
searchHistory.push(entry);
|
|
70
|
+
if (searchHistory.length > MAX_HISTORY_SIZE) {
|
|
71
|
+
searchHistory.shift();
|
|
72
|
+
}
|
|
73
|
+
// Emit metric
|
|
74
|
+
emitMetric({
|
|
75
|
+
_type: "metric",
|
|
76
|
+
name: "search_error",
|
|
77
|
+
timestamp: new Date().toISOString(),
|
|
78
|
+
search_latency_ms: latencyMs,
|
|
79
|
+
error_code: errorCode,
|
|
80
|
+
context,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Record model loaded event.
|
|
85
|
+
*
|
|
86
|
+
* @param latencyMs - Model load time in milliseconds
|
|
87
|
+
*/
|
|
88
|
+
export function recordModelLoaded(latencyMs) {
|
|
89
|
+
emitMetric({
|
|
90
|
+
_type: "metric",
|
|
91
|
+
name: "model_loaded",
|
|
92
|
+
timestamp: new Date().toISOString(),
|
|
93
|
+
search_latency_ms: latencyMs,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Record embeddings loaded event.
|
|
98
|
+
*
|
|
99
|
+
* @param latencyMs - Embeddings load time in milliseconds
|
|
100
|
+
* @param cacheHit - Whether loaded from cache
|
|
101
|
+
* @param chunkCount - Number of chunks loaded
|
|
102
|
+
*/
|
|
103
|
+
export function recordEmbeddingsLoaded(latencyMs, cacheHit, chunkCount) {
|
|
104
|
+
embeddingsCacheHit = cacheHit;
|
|
105
|
+
emitMetric({
|
|
106
|
+
_type: "metric",
|
|
107
|
+
name: "embeddings_loaded",
|
|
108
|
+
timestamp: new Date().toISOString(),
|
|
109
|
+
search_latency_ms: latencyMs,
|
|
110
|
+
cache_hit: cacheHit,
|
|
111
|
+
context: { chunk_count: chunkCount },
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Check if embeddings are cached (for cache_hit metric).
|
|
116
|
+
*
|
|
117
|
+
* @param isCached - Whether embeddings are currently cached
|
|
118
|
+
*/
|
|
119
|
+
export function setEmbeddingsCacheStatus(isCached) {
|
|
120
|
+
embeddingsCacheHit = isCached;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get aggregated statistics from the rolling window.
|
|
124
|
+
*
|
|
125
|
+
* @returns Statistics object with averages and counts
|
|
126
|
+
*/
|
|
127
|
+
export function getSearchStats() {
|
|
128
|
+
if (searchHistory.length === 0) {
|
|
129
|
+
return {
|
|
130
|
+
totalSearches: 0,
|
|
131
|
+
successfulSearches: 0,
|
|
132
|
+
failedSearches: 0,
|
|
133
|
+
averageLatencyMs: 0,
|
|
134
|
+
averageResultCount: 0,
|
|
135
|
+
searchTypeDistribution: {},
|
|
136
|
+
cacheHitRate: 0,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const successful = searchHistory.filter((e) => e.success);
|
|
140
|
+
const failed = searchHistory.filter((e) => !e.success);
|
|
141
|
+
const cacheHits = searchHistory.filter((e) => e.cacheHit);
|
|
142
|
+
const totalLatency = searchHistory.reduce((sum, e) => sum + e.latencyMs, 0);
|
|
143
|
+
const totalResults = successful.reduce((sum, e) => sum + e.resultCount, 0);
|
|
144
|
+
const typeDistribution = {};
|
|
145
|
+
for (const entry of searchHistory) {
|
|
146
|
+
typeDistribution[entry.searchType] = (typeDistribution[entry.searchType] || 0) + 1;
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
totalSearches: searchHistory.length,
|
|
150
|
+
successfulSearches: successful.length,
|
|
151
|
+
failedSearches: failed.length,
|
|
152
|
+
averageLatencyMs: Math.round(totalLatency / searchHistory.length),
|
|
153
|
+
averageResultCount: successful.length > 0 ? Math.round((totalResults / successful.length) * 10) / 10 : 0,
|
|
154
|
+
searchTypeDistribution: typeDistribution,
|
|
155
|
+
cacheHitRate: Math.round((cacheHits.length / searchHistory.length) * 100) / 100,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Clear metrics history.
|
|
160
|
+
* Primarily for testing purposes.
|
|
161
|
+
*/
|
|
162
|
+
export function clearMetricsHistory() {
|
|
163
|
+
searchHistory.length = 0;
|
|
164
|
+
embeddingsCacheHit = false;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Get the current rolling window size.
|
|
168
|
+
*
|
|
169
|
+
* @returns Number of entries in the rolling window
|
|
170
|
+
*/
|
|
171
|
+
export function getHistorySize() {
|
|
172
|
+
return searchHistory.length;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../../src/tools/docs/metrics.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAiDH,kDAAkD;AAClD,MAAM,aAAa,GAAkB,EAAE,CAAC;AAExC,wCAAwC;AACxC,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAE7B,yDAAyD;AACzD,IAAI,kBAAkB,GAAG,KAAK,CAAC;AAE/B;;;;GAIG;AACH,SAAS,UAAU,CAAC,MAAqB;IACvC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,SAAiB,EACjB,WAAmB,EACnB,UAA6C,EAC7C,QAAiB;IAEjB,MAAM,KAAK,GAAgB;QACzB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,SAAS;QACT,WAAW;QACX,UAAU;QACV,QAAQ;QACR,OAAO,EAAE,IAAI;KACd,CAAC;IAEF,wBAAwB;IACxB,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,aAAa,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;QAC5C,aAAa,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,cAAc;IACd,UAAU,CAAC;QACT,KAAK,EAAE,QAAQ;QACf,IAAI,EAAE,kBAAkB;QACxB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,iBAAiB,EAAE,SAAS;QAC5B,YAAY,EAAE,WAAW;QACzB,WAAW,EAAE,UAAU;QACvB,SAAS,EAAE,QAAQ;KACpB,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,SAAiB,EACjB,SAAiB,EACjB,OAAiC;IAEjC,MAAM,KAAK,GAAgB;QACzB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,SAAS;QACT,WAAW,EAAE,CAAC;QACd,UAAU,EAAE,UAAU;QACtB,QAAQ,EAAE,kBAAkB;QAC5B,OAAO,EAAE,KAAK;KACf,CAAC;IAEF,wBAAwB;IACxB,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,aAAa,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;QAC5C,aAAa,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,cAAc;IACd,UAAU,CAAC;QACT,KAAK,EAAE,QAAQ;QACf,IAAI,EAAE,cAAc;QACpB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,iBAAiB,EAAE,SAAS;QAC5B,UAAU,EAAE,SAAS;QACrB,OAAO;KACR,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,UAAU,CAAC;QACT,KAAK,EAAE,QAAQ;QACf,IAAI,EAAE,cAAc;QACpB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,iBAAiB,EAAE,SAAS;KAC7B,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,SAAiB,EACjB,QAAiB,EACjB,UAAkB;IAElB,kBAAkB,GAAG,QAAQ,CAAC;IAE9B,UAAU,CAAC;QACT,KAAK,EAAE,QAAQ;QACf,IAAI,EAAE,mBAAmB;QACzB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,iBAAiB,EAAE,SAAS;QAC5B,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE;KACrC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,QAAiB;IACxD,kBAAkB,GAAG,QAAQ,CAAC;AAChC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAS5B,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO;YACL,aAAa,EAAE,CAAC;YAChB,kBAAkB,EAAE,CAAC;YACrB,cAAc,EAAE,CAAC;YACjB,gBAAgB,EAAE,CAAC;YACnB,kBAAkB,EAAE,CAAC;YACrB,sBAAsB,EAAE,EAAE;YAC1B,YAAY,EAAE,CAAC;SAChB,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAE1D,MAAM,YAAY,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAC5E,MAAM,YAAY,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAE3E,MAAM,gBAAgB,GAA2B,EAAE,CAAC;IACpD,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;QAClC,gBAAgB,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IACrF,CAAC;IAED,OAAO;QACL,aAAa,EAAE,aAAa,CAAC,MAAM;QACnC,kBAAkB,EAAE,UAAU,CAAC,MAAM;QACrC,cAAc,EAAE,MAAM,CAAC,MAAM;QAC7B,gBAAgB,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,aAAa,CAAC,MAAM,CAAC;QACjE,kBAAkB,EAChB,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;QACtF,sBAAsB,EAAE,gBAAgB;QACxC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG;KAChF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB;IACjC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;IACzB,kBAAkB,GAAG,KAAK,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO,aAAa,CAAC,MAAM,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool implementation for searching Resend documentation.
|
|
3
|
+
* Provides semantic search with keyword fallback for documentation queries.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Get the MCP tool definition for search_resend_documentation.
|
|
7
|
+
* Includes annotations for tool behavior hints.
|
|
8
|
+
*
|
|
9
|
+
* @returns Tool definition object
|
|
10
|
+
*/
|
|
11
|
+
export declare function getDefinition(): {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: "object";
|
|
16
|
+
properties: {
|
|
17
|
+
query: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
minLength: number;
|
|
21
|
+
maxLength: number;
|
|
22
|
+
};
|
|
23
|
+
limit: {
|
|
24
|
+
type: string;
|
|
25
|
+
description: string;
|
|
26
|
+
minimum: number;
|
|
27
|
+
maximum: number;
|
|
28
|
+
default: number;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
required: string[];
|
|
32
|
+
};
|
|
33
|
+
annotations: {
|
|
34
|
+
title: string;
|
|
35
|
+
readOnlyHint: boolean;
|
|
36
|
+
destructiveHint: boolean;
|
|
37
|
+
idempotentHint: boolean;
|
|
38
|
+
openWorldHint: boolean;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Execute a documentation search.
|
|
43
|
+
*
|
|
44
|
+
* @param args - Raw arguments from MCP call
|
|
45
|
+
* @returns MCP tool response with search results or error
|
|
46
|
+
*/
|
|
47
|
+
export declare function execute(args: unknown): Promise<{
|
|
48
|
+
content: Array<{
|
|
49
|
+
type: "text";
|
|
50
|
+
text: string;
|
|
51
|
+
}>;
|
|
52
|
+
isError?: boolean;
|
|
53
|
+
}>;
|
|
54
|
+
//# sourceMappingURL=search-docs-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-docs-tool.d.ts","sourceRoot":"","sources":["../../../src/tools/docs/search-docs-tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAkDH;;;;;GAKG;AACH,wBAAgB,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoC5B;AAED;;;;;GAKG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACpD,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC,CA0ID"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool implementation for searching Resend documentation.
|
|
3
|
+
* Provides semantic search with keyword fallback for documentation queries.
|
|
4
|
+
*/
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { semanticSearch, keywordSearch, truncateToTokenBudget } from "./vector-search.js";
|
|
7
|
+
import { isEmbeddingsLoaded, getFreshnessWarning } from "./embeddings-loader.js";
|
|
8
|
+
import { toDocsError, formatErrorResponse } from "./errors.js";
|
|
9
|
+
import { recordSearch, recordSearchError, setEmbeddingsCacheStatus } from "./metrics.js";
|
|
10
|
+
// NOTE: Use console.error for logging - stdout is reserved for MCP protocol
|
|
11
|
+
const log = (message) => console.error(`[docs-search] ${message}`);
|
|
12
|
+
/** Minimum relevance threshold for semantic search results */
|
|
13
|
+
const SEMANTIC_RELEVANCE_THRESHOLD = 0.3;
|
|
14
|
+
/** Maximum total tokens across all results */
|
|
15
|
+
const MAX_TOTAL_TOKENS = 2800;
|
|
16
|
+
/** Default number of results to return */
|
|
17
|
+
const DEFAULT_LIMIT = 3;
|
|
18
|
+
/** Minimum query length */
|
|
19
|
+
const MIN_QUERY_LENGTH = 3;
|
|
20
|
+
/** Maximum query length */
|
|
21
|
+
const MAX_QUERY_LENGTH = 500;
|
|
22
|
+
/** Maximum results allowed */
|
|
23
|
+
const MAX_LIMIT = 10;
|
|
24
|
+
/**
|
|
25
|
+
* Zod schema for input validation.
|
|
26
|
+
*/
|
|
27
|
+
const inputSchema = z.object({
|
|
28
|
+
query: z
|
|
29
|
+
.string()
|
|
30
|
+
.min(MIN_QUERY_LENGTH, `Query must be at least ${MIN_QUERY_LENGTH} characters`)
|
|
31
|
+
.max(MAX_QUERY_LENGTH, `Query must be at most ${MAX_QUERY_LENGTH} characters`)
|
|
32
|
+
.describe("Search query or question about Resend API usage"),
|
|
33
|
+
limit: z
|
|
34
|
+
.number()
|
|
35
|
+
.int()
|
|
36
|
+
.min(1, "Limit must be at least 1")
|
|
37
|
+
.max(MAX_LIMIT, `Limit must be at most ${MAX_LIMIT}`)
|
|
38
|
+
.default(DEFAULT_LIMIT)
|
|
39
|
+
.describe("Maximum number of results to return (1-10, default: 3)"),
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* Get the MCP tool definition for search_resend_documentation.
|
|
43
|
+
* Includes annotations for tool behavior hints.
|
|
44
|
+
*
|
|
45
|
+
* @returns Tool definition object
|
|
46
|
+
*/
|
|
47
|
+
export function getDefinition() {
|
|
48
|
+
return {
|
|
49
|
+
name: "search_resend_documentation",
|
|
50
|
+
description: "Search Resend API documentation using semantic search. Returns relevant documentation " +
|
|
51
|
+
"excerpts for API usage, code examples, error handling, and best practices. Use this " +
|
|
52
|
+
"when you need information about sending emails, managing domains, contacts, audiences, " +
|
|
53
|
+
"templates, or any other Resend API features.",
|
|
54
|
+
inputSchema: {
|
|
55
|
+
type: "object",
|
|
56
|
+
properties: {
|
|
57
|
+
query: {
|
|
58
|
+
type: "string",
|
|
59
|
+
description: "Search query or question about Resend API usage",
|
|
60
|
+
minLength: MIN_QUERY_LENGTH,
|
|
61
|
+
maxLength: MAX_QUERY_LENGTH,
|
|
62
|
+
},
|
|
63
|
+
limit: {
|
|
64
|
+
type: "integer",
|
|
65
|
+
description: "Maximum number of results to return (1-10, default: 3)",
|
|
66
|
+
minimum: 1,
|
|
67
|
+
maximum: MAX_LIMIT,
|
|
68
|
+
default: DEFAULT_LIMIT,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
required: ["query"],
|
|
72
|
+
},
|
|
73
|
+
annotations: {
|
|
74
|
+
// Tool behavior hints for MCP clients
|
|
75
|
+
title: "Search Resend Documentation",
|
|
76
|
+
readOnlyHint: true,
|
|
77
|
+
destructiveHint: false,
|
|
78
|
+
idempotentHint: true,
|
|
79
|
+
openWorldHint: false,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Execute a documentation search.
|
|
85
|
+
*
|
|
86
|
+
* @param args - Raw arguments from MCP call
|
|
87
|
+
* @returns MCP tool response with search results or error
|
|
88
|
+
*/
|
|
89
|
+
export async function execute(args) {
|
|
90
|
+
const startTime = Date.now();
|
|
91
|
+
// Validate input
|
|
92
|
+
const parseResult = inputSchema.safeParse(args);
|
|
93
|
+
if (!parseResult.success) {
|
|
94
|
+
const errorMessage = parseResult.error.errors
|
|
95
|
+
.map((e) => `${e.path.join(".")}: ${e.message}`)
|
|
96
|
+
.join("; ");
|
|
97
|
+
const latencyMs = Date.now() - startTime;
|
|
98
|
+
recordSearchError(latencyMs, "VALIDATION_ERROR", { errors: parseResult.error.errors });
|
|
99
|
+
return {
|
|
100
|
+
content: [
|
|
101
|
+
{
|
|
102
|
+
type: "text",
|
|
103
|
+
text: JSON.stringify({
|
|
104
|
+
error: true,
|
|
105
|
+
code: "VALIDATION_ERROR",
|
|
106
|
+
message: `Invalid input: ${errorMessage}`,
|
|
107
|
+
hint: "Provide a 'query' string (3-500 chars) and optional 'limit' (1-10)",
|
|
108
|
+
recoverable: false,
|
|
109
|
+
}, null, 2),
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
isError: true,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const input = parseResult.data;
|
|
116
|
+
log(`Searching documentation: "${input.query}" (limit: ${input.limit})`);
|
|
117
|
+
// Track cache status for metrics
|
|
118
|
+
const wasCached = isEmbeddingsLoaded();
|
|
119
|
+
setEmbeddingsCacheStatus(wasCached);
|
|
120
|
+
try {
|
|
121
|
+
// Try semantic search first
|
|
122
|
+
let results = await semanticSearch(input.query, input.limit);
|
|
123
|
+
let searchType = "semantic";
|
|
124
|
+
// Check if best semantic result meets quality threshold
|
|
125
|
+
// Fall back to keyword search if no results OR best result is below threshold
|
|
126
|
+
const bestScore = results[0]?.score ?? 0;
|
|
127
|
+
if (results.length === 0 || bestScore < SEMANTIC_RELEVANCE_THRESHOLD) {
|
|
128
|
+
// Fall back to keyword search
|
|
129
|
+
log(`Semantic search returned low relevance results (best: ${bestScore.toFixed(3)}), falling back to keyword search`);
|
|
130
|
+
const keywordResults = await keywordSearch(input.query, input.limit);
|
|
131
|
+
if (keywordResults.length > 0) {
|
|
132
|
+
// Use keyword results, mark as keyword search type
|
|
133
|
+
results = keywordResults;
|
|
134
|
+
searchType = "keyword";
|
|
135
|
+
}
|
|
136
|
+
else if (results.length === 0) {
|
|
137
|
+
// No results from either search
|
|
138
|
+
const latencyMs = Date.now() - startTime;
|
|
139
|
+
recordSearch(latencyMs, 0, "keyword", wasCached);
|
|
140
|
+
const noResultsResponse = {
|
|
141
|
+
query: input.query,
|
|
142
|
+
searchType: "keyword",
|
|
143
|
+
resultCount: 0,
|
|
144
|
+
totalTokens: 0,
|
|
145
|
+
results: [],
|
|
146
|
+
};
|
|
147
|
+
return {
|
|
148
|
+
content: [
|
|
149
|
+
{
|
|
150
|
+
type: "text",
|
|
151
|
+
text: JSON.stringify(noResultsResponse, null, 2),
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
// If keyword search returned nothing but we have some semantic results,
|
|
157
|
+
// keep the semantic results even if below threshold
|
|
158
|
+
}
|
|
159
|
+
// Apply token budget
|
|
160
|
+
const searchResults = applyTokenBudget(results, MAX_TOTAL_TOKENS);
|
|
161
|
+
// Calculate total tokens
|
|
162
|
+
const totalTokens = searchResults.reduce((sum, r) => sum + r.tokenCount, 0);
|
|
163
|
+
// Record successful search metrics
|
|
164
|
+
const latencyMs = Date.now() - startTime;
|
|
165
|
+
recordSearch(latencyMs, searchResults.length, searchType, wasCached);
|
|
166
|
+
// Check for freshness warning
|
|
167
|
+
const freshnessWarning = getFreshnessWarning();
|
|
168
|
+
// Build response
|
|
169
|
+
const response = {
|
|
170
|
+
query: input.query,
|
|
171
|
+
searchType,
|
|
172
|
+
resultCount: searchResults.length,
|
|
173
|
+
totalTokens,
|
|
174
|
+
results: searchResults,
|
|
175
|
+
};
|
|
176
|
+
// Add freshness warning if present
|
|
177
|
+
if (freshnessWarning) {
|
|
178
|
+
response.warning = freshnessWarning;
|
|
179
|
+
}
|
|
180
|
+
log(`Search complete: ${searchResults.length} results, ${totalTokens} tokens, type: ${searchType}, latency: ${latencyMs}ms`);
|
|
181
|
+
return {
|
|
182
|
+
content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
const latencyMs = Date.now() - startTime;
|
|
187
|
+
// Convert to structured DocsError
|
|
188
|
+
const docsError = toDocsError(error);
|
|
189
|
+
log(`Search error [${docsError.code}]: ${docsError.message}`);
|
|
190
|
+
// Record error metrics
|
|
191
|
+
recordSearchError(latencyMs, docsError.code, { message: docsError.message });
|
|
192
|
+
return {
|
|
193
|
+
content: [{ type: "text", text: JSON.stringify(formatErrorResponse(docsError), null, 2) }],
|
|
194
|
+
isError: true,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Apply token budget to search results.
|
|
200
|
+
* Truncates results to fit within the total token limit.
|
|
201
|
+
*
|
|
202
|
+
* @param results - Scored chunks from search
|
|
203
|
+
* @param maxTotalTokens - Maximum total tokens allowed
|
|
204
|
+
* @returns Array of SearchResult objects within budget
|
|
205
|
+
*/
|
|
206
|
+
function applyTokenBudget(results, maxTotalTokens) {
|
|
207
|
+
const searchResults = [];
|
|
208
|
+
let remainingTokens = maxTotalTokens;
|
|
209
|
+
for (const result of results) {
|
|
210
|
+
if (remainingTokens <= 0) {
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
let excerpt = result.content;
|
|
214
|
+
let tokenCount = result.tokenCount;
|
|
215
|
+
// Truncate if needed to fit remaining budget
|
|
216
|
+
if (tokenCount > remainingTokens) {
|
|
217
|
+
excerpt = truncateToTokenBudget(excerpt, remainingTokens);
|
|
218
|
+
tokenCount = remainingTokens;
|
|
219
|
+
}
|
|
220
|
+
searchResults.push({
|
|
221
|
+
section: result.section,
|
|
222
|
+
excerpt,
|
|
223
|
+
relevance: Math.round(result.score * 1000) / 1000, // Round to 3 decimal places
|
|
224
|
+
tokenCount,
|
|
225
|
+
sourceUrl: result.sourceUrl,
|
|
226
|
+
});
|
|
227
|
+
remainingTokens -= tokenCount;
|
|
228
|
+
}
|
|
229
|
+
return searchResults;
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=search-docs-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-docs-tool.js","sourceRoot":"","sources":["../../../src/tools/docs/search-docs-tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime types for the documentation search tool.
|
|
3
|
+
* These types are used during search execution, distinct from the build-time types
|
|
4
|
+
* in scripts/types.ts used for embedding generation.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* A search result returned to the user.
|
|
8
|
+
* Contains the relevant excerpt and metadata about the match.
|
|
9
|
+
*/
|
|
10
|
+
export interface SearchResult {
|
|
11
|
+
/** Section header or title of the matched documentation */
|
|
12
|
+
section: string;
|
|
13
|
+
/** Relevant excerpt from the documentation (may be truncated) */
|
|
14
|
+
excerpt: string;
|
|
15
|
+
/** Relevance score (0-1 for semantic, simple match for keyword) */
|
|
16
|
+
relevance: number;
|
|
17
|
+
/** Estimated token count of the excerpt */
|
|
18
|
+
tokenCount: number;
|
|
19
|
+
/** Optional source URL for the documentation */
|
|
20
|
+
sourceUrl?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Internal representation of a chunk with its similarity score.
|
|
24
|
+
* Used during search ranking before transforming to SearchResult.
|
|
25
|
+
*/
|
|
26
|
+
export interface ScoredChunk {
|
|
27
|
+
/** Unique identifier for the chunk */
|
|
28
|
+
id: string;
|
|
29
|
+
/** Section header or title */
|
|
30
|
+
section: string;
|
|
31
|
+
/** Full content of the chunk */
|
|
32
|
+
content: string;
|
|
33
|
+
/** Token count of the chunk */
|
|
34
|
+
tokenCount: number;
|
|
35
|
+
/** Similarity score (cosine similarity for semantic search) */
|
|
36
|
+
score: number;
|
|
37
|
+
/** Optional source URL */
|
|
38
|
+
sourceUrl?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Successful search response structure.
|
|
42
|
+
*/
|
|
43
|
+
export interface SearchResponse {
|
|
44
|
+
/** The search query that was executed */
|
|
45
|
+
query: string;
|
|
46
|
+
/** Type of search performed */
|
|
47
|
+
searchType: "semantic" | "keyword" | "hybrid";
|
|
48
|
+
/** Number of results returned */
|
|
49
|
+
resultCount: number;
|
|
50
|
+
/** Total tokens across all results */
|
|
51
|
+
totalTokens: number;
|
|
52
|
+
/** The search results */
|
|
53
|
+
results: SearchResult[];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Error response structure for search failures.
|
|
57
|
+
*/
|
|
58
|
+
export interface SearchErrorResponse {
|
|
59
|
+
/** Error indicator */
|
|
60
|
+
error: true;
|
|
61
|
+
/** Error message */
|
|
62
|
+
message: string;
|
|
63
|
+
/** Optional hint for resolving the error */
|
|
64
|
+
hint?: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Union type for search responses.
|
|
68
|
+
*/
|
|
69
|
+
export type SearchResponseType = SearchResponse | SearchErrorResponse;
|
|
70
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/tools/docs/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,sCAAsC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,UAAU,EAAE,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC9C,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,yBAAyB;IACzB,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,sBAAsB;IACtB,KAAK,EAAE,IAAI,CAAC;IACZ,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,cAAc,GAAG,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/tools/docs/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core vector search functionality for documentation.
|
|
3
|
+
* Provides semantic search using embeddings and keyword search as fallback.
|
|
4
|
+
*/
|
|
5
|
+
import type { ScoredChunk } from "./types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Type definition for the transformer pipeline.
|
|
8
|
+
* We use dynamic import to load @xenova/transformers.
|
|
9
|
+
*/
|
|
10
|
+
type Pipeline = (texts: string[], options?: {
|
|
11
|
+
pooling: string;
|
|
12
|
+
normalize: boolean;
|
|
13
|
+
}) => Promise<{
|
|
14
|
+
data: Float32Array;
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* Load the embedding model (Xenova/all-MiniLM-L6-v2).
|
|
18
|
+
* Lazy loads on first call and caches for subsequent use.
|
|
19
|
+
* Includes timeout and retry logic for robustness.
|
|
20
|
+
*
|
|
21
|
+
* @returns Promise resolving to the embedding pipeline function
|
|
22
|
+
*/
|
|
23
|
+
export declare function loadEmbedder(): Promise<Pipeline>;
|
|
24
|
+
/**
|
|
25
|
+
* Generate embedding vector for a query string.
|
|
26
|
+
*
|
|
27
|
+
* @param query - The text to embed
|
|
28
|
+
* @returns Promise resolving to 384-dimensional embedding vector
|
|
29
|
+
*/
|
|
30
|
+
export declare function embedQuery(query: string): Promise<number[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Compute cosine similarity between two vectors.
|
|
33
|
+
* Assumes vectors are already normalized (which they are from the model).
|
|
34
|
+
*
|
|
35
|
+
* @param a - First vector
|
|
36
|
+
* @param b - Second vector
|
|
37
|
+
* @returns Cosine similarity score between -1 and 1
|
|
38
|
+
*/
|
|
39
|
+
export declare function cosineSimilarity(a: number[], b: number[]): number;
|
|
40
|
+
/**
|
|
41
|
+
* Perform semantic search using vector embeddings.
|
|
42
|
+
*
|
|
43
|
+
* @param query - The search query
|
|
44
|
+
* @param limit - Maximum number of results to return
|
|
45
|
+
* @param maxTokensPerResult - Optional max tokens per result for truncation
|
|
46
|
+
* @returns Promise resolving to array of scored chunks
|
|
47
|
+
*/
|
|
48
|
+
export declare function semanticSearch(query: string, limit: number, maxTokensPerResult?: number): Promise<ScoredChunk[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Perform keyword-based search as fallback.
|
|
51
|
+
* Uses simple case-insensitive substring matching with basic relevance scoring.
|
|
52
|
+
*
|
|
53
|
+
* @param query - The search query
|
|
54
|
+
* @param limit - Maximum number of results to return
|
|
55
|
+
* @returns Promise resolving to array of scored chunks
|
|
56
|
+
*/
|
|
57
|
+
export declare function keywordSearch(query: string, limit: number): Promise<ScoredChunk[]>;
|
|
58
|
+
/**
|
|
59
|
+
* Truncate content to fit within a token budget.
|
|
60
|
+
* Uses approximate token estimation (4 chars per token).
|
|
61
|
+
*
|
|
62
|
+
* @param content - The content to truncate
|
|
63
|
+
* @param maxTokens - Maximum tokens allowed
|
|
64
|
+
* @returns Truncated content with ellipsis if needed
|
|
65
|
+
*/
|
|
66
|
+
export declare function truncateToTokenBudget(content: string, maxTokens: number): string;
|
|
67
|
+
/**
|
|
68
|
+
* Get statistics about the loaded embeddings.
|
|
69
|
+
* Useful for debugging and status reporting.
|
|
70
|
+
*
|
|
71
|
+
* @returns Object with embeddings statistics, or null if not loaded
|
|
72
|
+
*/
|
|
73
|
+
export declare function getEmbeddingsStats(): Promise<{
|
|
74
|
+
chunkCount: number;
|
|
75
|
+
dimensions: number;
|
|
76
|
+
model: string;
|
|
77
|
+
generatedAt: string;
|
|
78
|
+
} | null>;
|
|
79
|
+
export {};
|
|
80
|
+
//# sourceMappingURL=vector-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vector-search.d.ts","sourceRoot":"","sources":["../../../src/tools/docs/vector-search.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAgB9C;;;GAGG;AACH,KAAK,QAAQ,GAAG,CACd,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,CAAC,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,KAC9C,OAAO,CAAC;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC;AAuDrC;;;;;;GAMG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC,CAkEtD;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAUjE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAejE;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,kBAAkB,CAAC,EAAE,MAAM,GAC1B,OAAO,CAAC,WAAW,EAAE,CAAC,CAqCxB;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,EAAE,CAAC,CAgExB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAyBhF;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,IAAI,CAAC,CAYR"}
|