@chainlink/mcp-server
Version:
Prototype MCP Server for CLL
169 lines • 7.45 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.searchDataFeedsDataSources = void 0;
const logger_1 = require("../../../utils/logger");
const strings_1 = require("../shared/strings");
const matching_1 = require("../shared/matching");
const load_1 = require("./load");
function calculateFeedsRelevance(feed, query, _chainId, networkType, networkName, networkQueryString, preferredEnv, parsedPair) {
let score = 0;
const queryLower = query.toLowerCase();
if (preferredEnv && networkType === preferredEnv)
score += 50;
if (parsedPair && feed?.name) {
const nameNormalized = feed.name
.toUpperCase()
.replace(/\s+/g, "");
const pairSlash = `${parsedPair.base}/${parsedPair.quote}`.toUpperCase();
const pairCompact = `${parsedPair.base}${parsedPair.quote}`.toUpperCase();
if (nameNormalized.includes(pairSlash.replace(/\s+/g, "")) ||
nameNormalized.includes(pairCompact)) {
score += 200;
}
}
if (feed.name && feed.name.toLowerCase().includes(queryLower))
score += 100;
if (feed.assetName && feed.assetName.toLowerCase().includes(queryLower))
score += 80;
if (feed.feedType && feed.feedType.toLowerCase().includes(queryLower))
score += 60;
const feedNameWords = (feed.name || "").toLowerCase().split(/[\s\/]+/);
const assetNameWords = (feed.assetName || "").toLowerCase().split(/[\s\/]+/);
const queryWords = queryLower.split(/\s+/);
for (const queryWord of queryWords) {
if (queryWord.length > 2) {
if (feedNameWords.some((word) => word.includes(queryWord)))
score += 40;
if (assetNameWords.some((word) => word.includes(queryWord)))
score += 30;
}
}
if (!preferredEnv && networkType === "mainnet")
score += 5;
// Boost if chain id or query string tokens appear in query
const qNormChain = (0, strings_1.removeSeparatorsForMatching)(queryLower);
const idNorm = (0, strings_1.removeSeparatorsForMatching)((_chainId || "").toLowerCase());
if (idNorm && (qNormChain.includes(idNorm) || idNorm.includes(qNormChain))) {
score += 60;
}
if (networkQueryString) {
const qs = networkQueryString.toLowerCase();
const qsNorm = (0, strings_1.removeSeparatorsForMatching)(qs);
if (qNormChain.includes(qsNorm) || qsNorm.includes(qNormChain)) {
score += 120;
}
for (const part of qs.split(/[-_\s]+/)) {
if (part.length > 2 && queryLower.includes(part))
score += 100;
}
}
// Boost when network name matches query (e.g., "Fuji" testnet)
if (networkName) {
const nn = networkName.toLowerCase();
const nnNorm = (0, strings_1.removeSeparatorsForMatching)(nn);
const qNorm = (0, strings_1.removeSeparatorsForMatching)(queryLower);
if (qNorm.includes(nnNorm) || nnNorm.includes(qNorm)) {
score += 200;
}
// Specific boost for common testnet aliases within network names
const aliases = [
"fuji",
"sepolia",
"holesky",
"amoy",
"mumbai",
"devnet",
"testnet",
];
if (aliases.some((a) => nn.includes(a) && queryLower.includes(a))) {
score += 150;
}
}
if ((queryLower.includes("address") ||
queryLower.includes("contract") ||
queryLower.includes("proxy")) &&
feed.proxyAddress) {
score += 40;
}
if (queryLower.includes("price") && feed.feedType === "Crypto")
score += 20;
return score;
}
const searchDataFeedsDataSources = async (query) => {
const results = [];
try {
const preferredEnv = (0, strings_1.extractEnvironmentFromQuery)(query);
const parsedPair = (0, strings_1.parseAssetPairFromQuery)(query);
const feedsIndex = await (0, load_1.loadDataFeedsIndex)();
if (!feedsIndex) {
logger_1.Logger.debug("Data Feeds index not available");
return results;
}
// Centralized detection from shared keyword index
const chainNames = await (0, matching_1.detectChainsFromQuery)(query, "dataFeeds");
let chainsToSearch = [];
if (chainNames.length > 0) {
chainsToSearch = (0, matching_1.matchChainsFromQuery)(chainNames, feedsIndex.chains, "dataFeeds");
logger_1.Logger.debug(`Found chains to search for feeds: ${chainsToSearch.join(", ")}`);
}
else {
const chainsWithCounts = (feedsIndex.chains || [])
.map((chain) => ({
chainId: chain.chainId,
count: (typeof chain.feedCount === "number" && chain.feedCount) ||
(typeof chain.totalFeeds === "number" && chain.totalFeeds) ||
0,
}))
.sort((a, b) => b.count - a.count);
if (chainsWithCounts.length > 0 && chainsWithCounts[0].count > 0) {
chainsToSearch = chainsWithCounts
.slice(0, 6)
.map((c) => c.chainId);
logger_1.Logger.debug(`No specific chains detected, searching top chains by feed count: ${chainsToSearch.join(", ")}`);
}
else {
const popularChains = [
"ethereum",
"arbitrum",
"polygon",
"avalanche",
"base",
"optimism",
];
chainsToSearch = popularChains.filter((chainId) => feedsIndex.chains.some((chain) => chain.chainId === chainId));
logger_1.Logger.debug(`No specific chains detected, searching popular chains: ${chainsToSearch.join(", ")}`);
}
}
for (const chainId of chainsToSearch) {
const chainFeedsData = await (0, load_1.loadDataFeedsData)(chainId);
if (!chainFeedsData)
continue;
for (const network of chainFeedsData.networks || []) {
for (const feed of network.feeds || []) {
const relevance = calculateFeedsRelevance(feed, query, chainId, network.networkType, network.networkName, network.queryString, preferredEnv || undefined, parsedPair);
if (relevance > 0) {
results.push({
chainId: chainId,
chainLabel: chainFeedsData.chainLabel,
networkName: network.networkName,
networkType: network.networkType,
queryString: network.queryString,
feed: feed,
relevance: relevance,
dataType: "feeds",
});
}
}
}
}
logger_1.Logger.debug(`Found ${results.length} relevant Data Feeds results`);
}
catch (error) {
logger_1.Logger.log("warn", `Failed to search Data Feeds configuration: ${error}`);
}
return results
.sort((a, b) => (b.relevance || 0) - (a.relevance || 0))
.slice(0, 30);
};
exports.searchDataFeedsDataSources = searchDataFeedsDataSources;
//# sourceMappingURL=search.js.map