@bbradar/mcp 0.1.7 → 0.1.8
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/server.js +217 -10
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -29,8 +29,9 @@ const DEFAULT_FILTER_TARGET_SAMPLE_PROGRAMS = 10;
|
|
|
29
29
|
const MAX_FILTER_TARGET_SAMPLE_PROGRAMS = 25;
|
|
30
30
|
const MAX_LOCAL_EXPORT_RESOURCES = 25;
|
|
31
31
|
const EXPORT_PREVIEW_LIMIT = 25;
|
|
32
|
-
const STALE_PROGRAM_ID_RESOLVE_PAGES =
|
|
33
|
-
const STALE_PROGRAM_ID_RESOLVE_BUDGET =
|
|
32
|
+
const STALE_PROGRAM_ID_RESOLVE_PAGES = 1;
|
|
33
|
+
const STALE_PROGRAM_ID_RESOLVE_BUDGET = 1;
|
|
34
|
+
const PROGRAM_FALLBACK_CACHE_MAX_ENTRIES = 200;
|
|
34
35
|
const MAX_RESOLVE_SEARCH_QUERIES = 4;
|
|
35
36
|
const SDK_VERSION = "1.29.0";
|
|
36
37
|
const WEB3_TAGS = ["web3", "crypto", "blockchain", "smart-contract", "smart contract", "defi", "nft", "ethereum", "solana", "solidity"];
|
|
@@ -1488,7 +1489,19 @@ function registerResources(server, client, config, exportStore) {
|
|
|
1488
1489
|
}));
|
|
1489
1490
|
});
|
|
1490
1491
|
}
|
|
1492
|
+
const programFallbackCache = new Map();
|
|
1493
|
+
const programTargetsFallbackCaches = new WeakMap();
|
|
1491
1494
|
async function getProgramWithIdFallback(client, config, requestedProgramId) {
|
|
1495
|
+
const cachedFallback = getCachedProgramFallback(config, requestedProgramId);
|
|
1496
|
+
if (cachedFallback && !isProgramIndexFallback(cachedFallback)) {
|
|
1497
|
+
return {
|
|
1498
|
+
api: await client.getProgram(cachedFallback.resolvedProgramId),
|
|
1499
|
+
programId: cachedFallback.resolvedProgramId,
|
|
1500
|
+
sourceRequests: cachedFallback.sourceRequests,
|
|
1501
|
+
warnings: cachedFallback.warnings,
|
|
1502
|
+
fallback: cachedFallback
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1492
1505
|
try {
|
|
1493
1506
|
return {
|
|
1494
1507
|
api: await client.getProgram(requestedProgramId),
|
|
@@ -1505,6 +1518,7 @@ async function getProgramWithIdFallback(client, config, requestedProgramId) {
|
|
|
1505
1518
|
if (!fallback) {
|
|
1506
1519
|
throw error;
|
|
1507
1520
|
}
|
|
1521
|
+
rememberProgramFallback(config, fallback);
|
|
1508
1522
|
return {
|
|
1509
1523
|
api: await client.getProgram(fallback.resolvedProgramId),
|
|
1510
1524
|
programId: fallback.resolvedProgramId,
|
|
@@ -1515,6 +1529,30 @@ async function getProgramWithIdFallback(client, config, requestedProgramId) {
|
|
|
1515
1529
|
}
|
|
1516
1530
|
}
|
|
1517
1531
|
async function runProgramIdTool(client, config, requestedProgramId, callback, indexFallbackCallback) {
|
|
1532
|
+
const cachedFallback = getCachedProgramFallback(config, requestedProgramId);
|
|
1533
|
+
if (cachedFallback) {
|
|
1534
|
+
if (isProgramIndexFallback(cachedFallback) && indexFallbackCallback) {
|
|
1535
|
+
const payload = await indexFallbackCallback(cachedFallback);
|
|
1536
|
+
return withProgramIndexFallbackMetadata(payload, cachedFallback);
|
|
1537
|
+
}
|
|
1538
|
+
try {
|
|
1539
|
+
const payload = await callback(cachedFallback.resolvedProgramId);
|
|
1540
|
+
return withProgramIdFallbackMetadata(payload, cachedFallback);
|
|
1541
|
+
}
|
|
1542
|
+
catch (cachedFallbackError) {
|
|
1543
|
+
if (!isProgramNotFoundError(cachedFallbackError) || !indexFallbackCallback) {
|
|
1544
|
+
throw cachedFallbackError;
|
|
1545
|
+
}
|
|
1546
|
+
const indexFallback = createProgramIndexFallbackFromStaleResolution(cachedFallback, requestedProgramId, cachedFallbackError) ??
|
|
1547
|
+
(await resolveIndexedProgramId(client, config, cachedFallback.resolvedProgramId, cachedFallbackError));
|
|
1548
|
+
if (!indexFallback) {
|
|
1549
|
+
throw cachedFallbackError;
|
|
1550
|
+
}
|
|
1551
|
+
rememberProgramFallback(config, indexFallback);
|
|
1552
|
+
const payload = await indexFallbackCallback(indexFallback);
|
|
1553
|
+
return withProgramIndexFallbackMetadata(payload, indexFallback);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1518
1556
|
try {
|
|
1519
1557
|
return await callback(requestedProgramId);
|
|
1520
1558
|
}
|
|
@@ -1531,10 +1569,12 @@ async function runProgramIdTool(client, config, requestedProgramId, callback, in
|
|
|
1531
1569
|
if (!indexFallback) {
|
|
1532
1570
|
throw error;
|
|
1533
1571
|
}
|
|
1572
|
+
rememberProgramFallback(config, indexFallback);
|
|
1534
1573
|
const payload = await indexFallbackCallback(indexFallback);
|
|
1535
1574
|
return withProgramIndexFallbackMetadata(payload, indexFallback);
|
|
1536
1575
|
}
|
|
1537
1576
|
try {
|
|
1577
|
+
rememberProgramFallback(config, fallback);
|
|
1538
1578
|
const payload = await callback(fallback.resolvedProgramId);
|
|
1539
1579
|
return withProgramIdFallbackMetadata(payload, fallback);
|
|
1540
1580
|
}
|
|
@@ -1542,21 +1582,107 @@ async function runProgramIdTool(client, config, requestedProgramId, callback, in
|
|
|
1542
1582
|
if (!isProgramNotFoundError(fallbackError) || !indexFallbackCallback) {
|
|
1543
1583
|
throw fallbackError;
|
|
1544
1584
|
}
|
|
1545
|
-
const indexFallback =
|
|
1585
|
+
const indexFallback = createProgramIndexFallbackFromStaleResolution(fallback, requestedProgramId, fallbackError) ??
|
|
1586
|
+
(await resolveIndexedProgramId(client, config, fallback.resolvedProgramId, fallbackError));
|
|
1546
1587
|
if (!indexFallback) {
|
|
1547
1588
|
throw fallbackError;
|
|
1548
1589
|
}
|
|
1549
|
-
const combinedFallback =
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1590
|
+
const combinedFallback = indexFallback.requestedProgramId === requestedProgramId
|
|
1591
|
+
? indexFallback
|
|
1592
|
+
: {
|
|
1593
|
+
...indexFallback,
|
|
1594
|
+
requestedProgramId,
|
|
1595
|
+
sourceRequests: [...fallback.sourceRequests, ...indexFallback.sourceRequests],
|
|
1596
|
+
warnings: uniqueStrings([...fallback.warnings, ...indexFallback.warnings])
|
|
1597
|
+
};
|
|
1598
|
+
rememberProgramFallback(config, combinedFallback);
|
|
1555
1599
|
const payload = await indexFallbackCallback(combinedFallback);
|
|
1556
1600
|
return withProgramIndexFallbackMetadata(payload, combinedFallback);
|
|
1557
1601
|
}
|
|
1558
1602
|
}
|
|
1559
1603
|
}
|
|
1604
|
+
function getCachedProgramFallback(config, requestedProgramId) {
|
|
1605
|
+
if (config.cacheTtlMs <= 0 || config.cacheMaxEntries <= 0) {
|
|
1606
|
+
return undefined;
|
|
1607
|
+
}
|
|
1608
|
+
const key = programFallbackCacheKey(config, requestedProgramId);
|
|
1609
|
+
const cached = programFallbackCache.get(key);
|
|
1610
|
+
if (!cached) {
|
|
1611
|
+
return undefined;
|
|
1612
|
+
}
|
|
1613
|
+
if (cached.expiresAt <= Date.now()) {
|
|
1614
|
+
programFallbackCache.delete(key);
|
|
1615
|
+
return undefined;
|
|
1616
|
+
}
|
|
1617
|
+
return cloneProgramFallbackForRequest(cached.fallback, requestedProgramId, true);
|
|
1618
|
+
}
|
|
1619
|
+
function rememberProgramFallback(config, fallback) {
|
|
1620
|
+
if (config.cacheTtlMs <= 0 || config.cacheMaxEntries <= 0) {
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
pruneProgramFallbackCache();
|
|
1624
|
+
const key = programFallbackCacheKey(config, fallback.requestedProgramId);
|
|
1625
|
+
programFallbackCache.set(key, {
|
|
1626
|
+
expiresAt: Date.now() + config.cacheTtlMs,
|
|
1627
|
+
fallback: cloneProgramFallbackForRequest(fallback, fallback.requestedProgramId, false)
|
|
1628
|
+
});
|
|
1629
|
+
if (programFallbackCache.size > PROGRAM_FALLBACK_CACHE_MAX_ENTRIES) {
|
|
1630
|
+
const oldestKey = programFallbackCache.keys().next().value;
|
|
1631
|
+
if (oldestKey) {
|
|
1632
|
+
programFallbackCache.delete(oldestKey);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
function pruneProgramFallbackCache() {
|
|
1637
|
+
const now = Date.now();
|
|
1638
|
+
for (const [key, cached] of programFallbackCache) {
|
|
1639
|
+
if (cached.expiresAt <= now) {
|
|
1640
|
+
programFallbackCache.delete(key);
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
function programFallbackCacheKey(config, requestedProgramId) {
|
|
1645
|
+
return `${config.apiBaseUrl}\n${normalizeTag(requestedProgramId)}`;
|
|
1646
|
+
}
|
|
1647
|
+
function cloneProgramFallbackForRequest(fallback, requestedProgramId, fromCache) {
|
|
1648
|
+
const warnings = fromCache
|
|
1649
|
+
? uniqueStrings([...fallback.warnings, `Reused cached program_id resolution for ${requestedProgramId}.`])
|
|
1650
|
+
: [...fallback.warnings];
|
|
1651
|
+
const cloned = {
|
|
1652
|
+
...fallback,
|
|
1653
|
+
requestedProgramId,
|
|
1654
|
+
warnings,
|
|
1655
|
+
sourceRequests: [...fallback.sourceRequests],
|
|
1656
|
+
resolution: { ...fallback.resolution }
|
|
1657
|
+
};
|
|
1658
|
+
if (isProgramIndexFallback(fallback)) {
|
|
1659
|
+
const indexClone = {
|
|
1660
|
+
...cloned,
|
|
1661
|
+
program: { ...fallback.program }
|
|
1662
|
+
};
|
|
1663
|
+
return indexClone;
|
|
1664
|
+
}
|
|
1665
|
+
return cloned;
|
|
1666
|
+
}
|
|
1667
|
+
function isProgramIndexFallback(fallback) {
|
|
1668
|
+
return readObject(fallback.program) !== undefined;
|
|
1669
|
+
}
|
|
1670
|
+
function createProgramIndexFallbackFromStaleResolution(fallback, requestedProgramId, detailError) {
|
|
1671
|
+
const match = selectProgramIndexFallbackMatch(fallback.resolvedProgramId, fallback.resolution);
|
|
1672
|
+
if (!match) {
|
|
1673
|
+
return undefined;
|
|
1674
|
+
}
|
|
1675
|
+
const detailApiError = detailError instanceof BBRadarApiError ? detailError : undefined;
|
|
1676
|
+
const indexWarning = `program_id ${fallback.resolvedProgramId} exists in the BBRadar program index, but the per-program detail endpoint returned 404.`;
|
|
1677
|
+
return {
|
|
1678
|
+
...fallback,
|
|
1679
|
+
requestedProgramId,
|
|
1680
|
+
staleRequestId: fallback.staleRequestId ?? detailApiError?.requestId,
|
|
1681
|
+
staleUpstreamRequestId: fallback.staleUpstreamRequestId ?? detailApiError?.upstreamRequestId,
|
|
1682
|
+
warnings: uniqueStrings([...fallback.warnings, indexWarning]),
|
|
1683
|
+
program: match.program
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1560
1686
|
async function resolveIndexedProgramId(client, config, requestedProgramId, staleError) {
|
|
1561
1687
|
const query = programSearchText(requestedProgramId);
|
|
1562
1688
|
if (query.length < 2) {
|
|
@@ -2862,6 +2988,10 @@ async function getProgramTargetsExportFallbackPayload(client, input, programId)
|
|
|
2862
2988
|
});
|
|
2863
2989
|
}
|
|
2864
2990
|
async function fetchProgramTargetsWithEndpointFallback(client, programId) {
|
|
2991
|
+
const cachedLookup = getCachedProgramTargetsFallback(client, programId);
|
|
2992
|
+
if (cachedLookup) {
|
|
2993
|
+
return cachedLookup;
|
|
2994
|
+
}
|
|
2865
2995
|
try {
|
|
2866
2996
|
const api = await client.getProgramTargets(programId);
|
|
2867
2997
|
const data = readObject(api.data);
|
|
@@ -2894,9 +3024,86 @@ async function fetchProgramTargetsWithEndpointFallback(client, programId) {
|
|
|
2894
3024
|
if (!isProgramNotFoundError(error)) {
|
|
2895
3025
|
throw error;
|
|
2896
3026
|
}
|
|
2897
|
-
|
|
3027
|
+
const lookup = await fetchProgramTargetsFromExportFallback(client, programId);
|
|
3028
|
+
rememberProgramTargetsFallback(client, programId, lookup);
|
|
3029
|
+
return lookup;
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
function getCachedProgramTargetsFallback(client, programId) {
|
|
3033
|
+
const diagnostics = client.diagnostics();
|
|
3034
|
+
if (!diagnostics.response_cache.enabled) {
|
|
3035
|
+
return undefined;
|
|
3036
|
+
}
|
|
3037
|
+
const cache = programTargetsFallbackCaches.get(client);
|
|
3038
|
+
const key = programTargetsFallbackCacheKey(programId);
|
|
3039
|
+
const cached = cache?.get(key);
|
|
3040
|
+
if (!cached) {
|
|
3041
|
+
return undefined;
|
|
3042
|
+
}
|
|
3043
|
+
if (cached.expiresAt <= Date.now()) {
|
|
3044
|
+
cache?.delete(key);
|
|
3045
|
+
return undefined;
|
|
3046
|
+
}
|
|
3047
|
+
return {
|
|
3048
|
+
...cached.lookup,
|
|
3049
|
+
cache: readObject(stripUndefined({
|
|
3050
|
+
...(cached.lookup.cache ?? {}),
|
|
3051
|
+
hit: true,
|
|
3052
|
+
expires_at: new Date(cached.expiresAt).toISOString()
|
|
3053
|
+
})),
|
|
3054
|
+
sourceRequests: cached.lookup.sourceRequests.map((request) => ({
|
|
3055
|
+
...request,
|
|
3056
|
+
cache_hit: true,
|
|
3057
|
+
cache_expires_at: new Date(cached.expiresAt).toISOString()
|
|
3058
|
+
})),
|
|
3059
|
+
warnings: uniqueStrings([...cached.lookup.warnings, `Reused cached target fallback for ${programId}.`])
|
|
3060
|
+
};
|
|
3061
|
+
}
|
|
3062
|
+
function rememberProgramTargetsFallback(client, programId, lookup) {
|
|
3063
|
+
const diagnostics = client.diagnostics();
|
|
3064
|
+
if (!diagnostics.response_cache.enabled || lookup.unavailable) {
|
|
3065
|
+
return;
|
|
3066
|
+
}
|
|
3067
|
+
const cache = programTargetsFallbackCacheForClient(client);
|
|
3068
|
+
pruneProgramTargetsFallbackCache(cache);
|
|
3069
|
+
const ttlMs = cacheTtlMsFromDiagnostics(diagnostics.response_cache);
|
|
3070
|
+
if (ttlMs <= 0) {
|
|
3071
|
+
return;
|
|
3072
|
+
}
|
|
3073
|
+
cache.set(programTargetsFallbackCacheKey(programId), {
|
|
3074
|
+
expiresAt: Date.now() + ttlMs,
|
|
3075
|
+
lookup
|
|
3076
|
+
});
|
|
3077
|
+
if (cache.size > PROGRAM_FALLBACK_CACHE_MAX_ENTRIES) {
|
|
3078
|
+
const oldestKey = cache.keys().next().value;
|
|
3079
|
+
if (oldestKey) {
|
|
3080
|
+
cache.delete(oldestKey);
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
function programTargetsFallbackCacheForClient(client) {
|
|
3085
|
+
const existing = programTargetsFallbackCaches.get(client);
|
|
3086
|
+
if (existing) {
|
|
3087
|
+
return existing;
|
|
3088
|
+
}
|
|
3089
|
+
const cache = new Map();
|
|
3090
|
+
programTargetsFallbackCaches.set(client, cache);
|
|
3091
|
+
return cache;
|
|
3092
|
+
}
|
|
3093
|
+
function pruneProgramTargetsFallbackCache(cache) {
|
|
3094
|
+
const now = Date.now();
|
|
3095
|
+
for (const [key, cached] of cache) {
|
|
3096
|
+
if (cached.expiresAt <= now) {
|
|
3097
|
+
cache.delete(key);
|
|
3098
|
+
}
|
|
2898
3099
|
}
|
|
2899
3100
|
}
|
|
3101
|
+
function programTargetsFallbackCacheKey(programId) {
|
|
3102
|
+
return normalizeTag(programId);
|
|
3103
|
+
}
|
|
3104
|
+
function cacheTtlMsFromDiagnostics(cache) {
|
|
3105
|
+
return cache.enabled ? cache.ttl_ms : 0;
|
|
3106
|
+
}
|
|
2900
3107
|
async function fetchProgramTargetsFromExportFallback(client, programId) {
|
|
2901
3108
|
try {
|
|
2902
3109
|
const api = await client.exportTargets({
|