@bbradar/mcp 0.1.8 → 0.1.10
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 +380 -75
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { BBRadarApiError } from "./bbradarClient.js";
|
|
5
|
-
import { stripUndefined } from "./json.js";
|
|
5
|
+
import { stableStringify, stripUndefined } from "./json.js";
|
|
6
6
|
import { FixedWindowRateLimiter } from "./rateLimit.js";
|
|
7
7
|
import { readArray, readBoolean, readNumber, readObject, readString, sanitizeJson, sanitizeChange, sanitizeProgram, sanitizeTarget, sanitizeTargetsForExport } from "./sanitize.js";
|
|
8
8
|
const SERVER_VERSION = "0.1.0";
|
|
@@ -276,10 +276,18 @@ export function createBbradarServer(client, config) {
|
|
|
276
276
|
annotations: readOnlyAnnotations
|
|
277
277
|
}, (args) => runTool("get_program", config, rateLimiter, async () => {
|
|
278
278
|
return runProgramIdTool(client, config, args.program_id, async (programId) => {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
279
|
+
try {
|
|
280
|
+
const api = await client.getProgram(programId);
|
|
281
|
+
return withApiMetadata(api, {
|
|
282
|
+
program: addProgramResourceLinks(sanitizeProgram(api.data, config.webBaseUrl))
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
if (!isProgramNotFoundError(error)) {
|
|
287
|
+
throw error;
|
|
288
|
+
}
|
|
289
|
+
return getProgramFromTargetsOnlyFallback(client, programId, error);
|
|
290
|
+
}
|
|
283
291
|
}, async (indexFallback) => ({
|
|
284
292
|
request_id: randomUUID(),
|
|
285
293
|
warnings: [`Returned program metadata from the BBRadar program index because the per-program detail endpoint returned 404 for ${indexFallback.resolvedProgramId}.`],
|
|
@@ -348,9 +356,40 @@ export function createBbradarServer(client, config) {
|
|
|
348
356
|
},
|
|
349
357
|
annotations: readOnlyAnnotations
|
|
350
358
|
}, (args) => runTool("run_program_name_action", config, rateLimiter, async () => runProgramNameAction(client, config, args)));
|
|
359
|
+
server.registerTool("get_program_eligible_targets_by_name", {
|
|
360
|
+
title: "Get Eligible Targets By Program Name",
|
|
361
|
+
description: "Fast one-call path for user asks like 'find in-scope eligible targets for Aztec': resolve the program name, fetch targets, and return filtered in-scope bounty-eligible targets. Prefer this over separate resolve_program + get_program_targets calls when the user asks for a target list by name.",
|
|
362
|
+
inputSchema: {
|
|
363
|
+
query: searchTextSchema,
|
|
364
|
+
platforms: stringListSchema,
|
|
365
|
+
tags: stringListSchema,
|
|
366
|
+
opportunity_levels: z.array(opportunityLevelSchema).max(4).default([]),
|
|
367
|
+
max_resolve_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
368
|
+
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET),
|
|
369
|
+
include_out_of_scope: z.boolean().default(false),
|
|
370
|
+
include_ineligible: z.boolean().default(false),
|
|
371
|
+
strict_scope_filter: z.boolean().default(true),
|
|
372
|
+
offset: z.number().int().min(0).default(0),
|
|
373
|
+
limit: z.number().int().min(1).max(MAX_TARGETS_PER_PROGRAM).default(MAX_TARGETS_PER_PROGRAM),
|
|
374
|
+
target_list_mode: targetListModeSchema.default("identifiers"),
|
|
375
|
+
output_mode: programListModeSchema.default("compact")
|
|
376
|
+
},
|
|
377
|
+
outputSchema: {
|
|
378
|
+
...apiEnvelopeOutputShape,
|
|
379
|
+
source_requests: z.array(jsonRecordSchema).optional(),
|
|
380
|
+
resolved_program: jsonRecordSchema.optional(),
|
|
381
|
+
program_id: z.string().optional(),
|
|
382
|
+
targets: z.array(z.unknown()).optional(),
|
|
383
|
+
warnings: z.array(z.string()).optional(),
|
|
384
|
+
upstream_budget: jsonRecordSchema.optional(),
|
|
385
|
+
ranking_scope: jsonRecordSchema.optional(),
|
|
386
|
+
meta: jsonRecordSchema.optional()
|
|
387
|
+
},
|
|
388
|
+
annotations: readOnlyAnnotations
|
|
389
|
+
}, (args) => runTool("get_program_eligible_targets_by_name", config, rateLimiter, async () => getProgramEligibleTargetsByName(client, config, args)));
|
|
351
390
|
server.registerTool("get_program_targets", {
|
|
352
391
|
title: "Get Program Targets",
|
|
353
|
-
description: `Get active targets for one program. limit max ${MAX_TARGETS_PER_PROGRAM}.`,
|
|
392
|
+
description: `Get active targets for one known program_id. This is a final source for eligible target lists; do not call summary/breakdown unless grouped counts are needed. For a program name query, prefer get_program_eligible_targets_by_name. limit max ${MAX_TARGETS_PER_PROGRAM}.`,
|
|
354
393
|
inputSchema: {
|
|
355
394
|
program_id: programIdSchema,
|
|
356
395
|
include_out_of_scope: z.boolean().default(false),
|
|
@@ -1491,6 +1530,7 @@ function registerResources(server, client, config, exportStore) {
|
|
|
1491
1530
|
}
|
|
1492
1531
|
const programFallbackCache = new Map();
|
|
1493
1532
|
const programTargetsFallbackCaches = new WeakMap();
|
|
1533
|
+
const resolveProgramCache = new Map();
|
|
1494
1534
|
async function getProgramWithIdFallback(client, config, requestedProgramId) {
|
|
1495
1535
|
const cachedFallback = getCachedProgramFallback(config, requestedProgramId);
|
|
1496
1536
|
if (cachedFallback && !isProgramIndexFallback(cachedFallback)) {
|
|
@@ -1873,6 +1913,10 @@ function withProgramIndexFallbackMetadata(payload, fallback) {
|
|
|
1873
1913
|
};
|
|
1874
1914
|
}
|
|
1875
1915
|
async function resolveProgram(client, config, input) {
|
|
1916
|
+
const cached = getCachedResolveProgram(config, input);
|
|
1917
|
+
if (cached) {
|
|
1918
|
+
return cached;
|
|
1919
|
+
}
|
|
1876
1920
|
const warnings = [];
|
|
1877
1921
|
const budget = {
|
|
1878
1922
|
initial: input.upstream_request_budget,
|
|
@@ -1882,6 +1926,8 @@ async function resolveProgram(client, config, input) {
|
|
|
1882
1926
|
const searchQueries = programResolutionSearchQueries(input.query);
|
|
1883
1927
|
const collected = await collectProgramsForResolution(client, {
|
|
1884
1928
|
searchQueries,
|
|
1929
|
+
isHighConfidenceMatch: (programs) => hasHighConfidenceProgramResolutionMatch(programs, config.webBaseUrl, input.query),
|
|
1930
|
+
scorePrograms: (programs) => bestProgramResolutionScore(programs, config.webBaseUrl, input.query),
|
|
1885
1931
|
platforms: input.platforms,
|
|
1886
1932
|
tags: input.tags,
|
|
1887
1933
|
opportunity_levels: input.opportunity_levels,
|
|
@@ -1899,7 +1945,7 @@ async function resolveProgram(client, config, input) {
|
|
|
1899
1945
|
.sort((left, right) => right.match.score - left.match.score || compareProgramCandidates(left.candidate, right.candidate, "best_hunt_value"))
|
|
1900
1946
|
.slice(0, input.max_results);
|
|
1901
1947
|
const programs = matches.map((entry) => formatProgram(entry.candidate.program, input.output_mode));
|
|
1902
|
-
|
|
1948
|
+
const payload = stripUndefined({
|
|
1903
1949
|
request_id: randomUUID(),
|
|
1904
1950
|
source_requests: collected.sourceRequests,
|
|
1905
1951
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
@@ -1933,6 +1979,75 @@ async function resolveProgram(client, config, input) {
|
|
|
1933
1979
|
upstream_requests_scanned: collected.requestsScanned
|
|
1934
1980
|
}
|
|
1935
1981
|
});
|
|
1982
|
+
rememberResolveProgram(config, input, payload);
|
|
1983
|
+
return payload;
|
|
1984
|
+
}
|
|
1985
|
+
function getCachedResolveProgram(config, input) {
|
|
1986
|
+
if (config.cacheTtlMs <= 0 || config.cacheMaxEntries <= 0) {
|
|
1987
|
+
return undefined;
|
|
1988
|
+
}
|
|
1989
|
+
const key = resolveProgramCacheKey(config, input);
|
|
1990
|
+
const cached = resolveProgramCache.get(key);
|
|
1991
|
+
if (!cached) {
|
|
1992
|
+
return undefined;
|
|
1993
|
+
}
|
|
1994
|
+
if (cached.expiresAt <= Date.now()) {
|
|
1995
|
+
resolveProgramCache.delete(key);
|
|
1996
|
+
return undefined;
|
|
1997
|
+
}
|
|
1998
|
+
const sourceRequests = readArray(cached.payload.source_requests).filter((request) => readObject(request) !== undefined);
|
|
1999
|
+
const warnings = readArray(cached.payload.warnings).filter((warning) => typeof warning === "string");
|
|
2000
|
+
return stripUndefined({
|
|
2001
|
+
...cached.payload,
|
|
2002
|
+
request_id: randomUUID(),
|
|
2003
|
+
cache: {
|
|
2004
|
+
hit: true,
|
|
2005
|
+
expires_at: new Date(cached.expiresAt).toISOString()
|
|
2006
|
+
},
|
|
2007
|
+
source_requests: sourceRequests.map((request) => ({
|
|
2008
|
+
...request,
|
|
2009
|
+
cache_hit: true,
|
|
2010
|
+
cache_expires_at: new Date(cached.expiresAt).toISOString()
|
|
2011
|
+
})),
|
|
2012
|
+
warnings: uniqueStrings([...warnings, `Reused cached program resolution for ${input.query}.`])
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
2015
|
+
function rememberResolveProgram(config, input, payload) {
|
|
2016
|
+
if (config.cacheTtlMs <= 0 || config.cacheMaxEntries <= 0) {
|
|
2017
|
+
return;
|
|
2018
|
+
}
|
|
2019
|
+
pruneResolveProgramCache();
|
|
2020
|
+
resolveProgramCache.set(resolveProgramCacheKey(config, input), {
|
|
2021
|
+
expiresAt: Date.now() + config.cacheTtlMs,
|
|
2022
|
+
payload: { ...payload }
|
|
2023
|
+
});
|
|
2024
|
+
if (resolveProgramCache.size > PROGRAM_FALLBACK_CACHE_MAX_ENTRIES) {
|
|
2025
|
+
const oldestKey = resolveProgramCache.keys().next().value;
|
|
2026
|
+
if (oldestKey) {
|
|
2027
|
+
resolveProgramCache.delete(oldestKey);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
function pruneResolveProgramCache() {
|
|
2032
|
+
const now = Date.now();
|
|
2033
|
+
for (const [key, cached] of resolveProgramCache) {
|
|
2034
|
+
if (cached.expiresAt <= now) {
|
|
2035
|
+
resolveProgramCache.delete(key);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
function resolveProgramCacheKey(config, input) {
|
|
2040
|
+
return stableStringify({
|
|
2041
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
2042
|
+
query: normalizeTag(input.query),
|
|
2043
|
+
platforms: input.platforms.map(normalizeTag).sort(),
|
|
2044
|
+
tags: input.tags.map(normalizeTag).sort(),
|
|
2045
|
+
opportunity_levels: [...input.opportunity_levels].sort(),
|
|
2046
|
+
max_pages: input.max_pages,
|
|
2047
|
+
max_results: input.max_results,
|
|
2048
|
+
output_mode: input.output_mode,
|
|
2049
|
+
upstream_request_budget: input.upstream_request_budget
|
|
2050
|
+
});
|
|
1936
2051
|
}
|
|
1937
2052
|
async function runProgramNameAction(client, config, input) {
|
|
1938
2053
|
const resolution = await resolveProgram(client, config, {
|
|
@@ -1977,6 +2092,76 @@ async function runProgramNameAction(client, config, input) {
|
|
|
1977
2092
|
ranking_scope: readObject(resolution.ranking_scope)
|
|
1978
2093
|
});
|
|
1979
2094
|
}
|
|
2095
|
+
async function getProgramEligibleTargetsByName(client, config, input) {
|
|
2096
|
+
const resolution = await resolveProgram(client, config, {
|
|
2097
|
+
query: input.query,
|
|
2098
|
+
platforms: input.platforms,
|
|
2099
|
+
tags: input.tags,
|
|
2100
|
+
opportunity_levels: input.opportunity_levels,
|
|
2101
|
+
max_pages: input.max_resolve_pages,
|
|
2102
|
+
max_results: 1,
|
|
2103
|
+
output_mode: input.output_mode,
|
|
2104
|
+
upstream_request_budget: input.upstream_request_budget
|
|
2105
|
+
});
|
|
2106
|
+
const bestMatch = readObject(readArray(resolution.matches)[0]);
|
|
2107
|
+
const programId = stringField(bestMatch, "program_id");
|
|
2108
|
+
const resolvedProgram = readObject(bestMatch?.program);
|
|
2109
|
+
const resolutionSourceRequests = readArray(resolution.source_requests).filter((request) => readObject(request) !== undefined);
|
|
2110
|
+
const resolutionWarnings = readArray(resolution.warnings).filter((warning) => typeof warning === "string");
|
|
2111
|
+
if (!programId) {
|
|
2112
|
+
return stripUndefined({
|
|
2113
|
+
request_id: randomUUID(),
|
|
2114
|
+
source_requests: resolutionSourceRequests,
|
|
2115
|
+
warnings: uniqueStrings([...resolutionWarnings, "No matching program_id was resolved for the requested program name."]),
|
|
2116
|
+
resolved_program: undefined,
|
|
2117
|
+
targets: [],
|
|
2118
|
+
upstream_budget: readObject(resolution.upstream_budget),
|
|
2119
|
+
ranking_scope: readObject(resolution.ranking_scope),
|
|
2120
|
+
meta: {
|
|
2121
|
+
found: false,
|
|
2122
|
+
returned: 0,
|
|
2123
|
+
query: input.query
|
|
2124
|
+
}
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2127
|
+
const targetsPayload = await runProgramIdTool(client, config, programId, (resolvedProgramId) => getProgramTargetsPayload(client, {
|
|
2128
|
+
include_out_of_scope: input.include_out_of_scope,
|
|
2129
|
+
include_ineligible: input.include_ineligible,
|
|
2130
|
+
strict_scope_filter: input.strict_scope_filter,
|
|
2131
|
+
offset: input.offset,
|
|
2132
|
+
limit: input.limit,
|
|
2133
|
+
output_mode: input.target_list_mode
|
|
2134
|
+
}, resolvedProgramId), (indexFallback) => getProgramTargetsExportFallbackPayload(client, {
|
|
2135
|
+
include_out_of_scope: input.include_out_of_scope,
|
|
2136
|
+
include_ineligible: input.include_ineligible,
|
|
2137
|
+
strict_scope_filter: input.strict_scope_filter,
|
|
2138
|
+
offset: input.offset,
|
|
2139
|
+
limit: input.limit,
|
|
2140
|
+
output_mode: input.target_list_mode
|
|
2141
|
+
}, indexFallback.resolvedProgramId));
|
|
2142
|
+
const targetSourceRequests = readArray(targetsPayload.source_requests).filter((request) => readObject(request) !== undefined);
|
|
2143
|
+
const targetWarnings = readArray(targetsPayload.warnings).filter((warning) => typeof warning === "string");
|
|
2144
|
+
const targetMeta = readObject(targetsPayload.meta);
|
|
2145
|
+
return stripUndefined({
|
|
2146
|
+
request_id: randomUUID(),
|
|
2147
|
+
source_requests: [...resolutionSourceRequests, ...targetSourceRequests],
|
|
2148
|
+
warnings: uniqueStrings([...resolutionWarnings, ...targetWarnings]),
|
|
2149
|
+
resolved_program: resolvedProgram,
|
|
2150
|
+
program_id: stringField(targetsPayload, "program_id") ?? programId,
|
|
2151
|
+
targets: readArray(targetsPayload.targets),
|
|
2152
|
+
upstream_budget: readObject(resolution.upstream_budget),
|
|
2153
|
+
ranking_scope: readObject(resolution.ranking_scope),
|
|
2154
|
+
program_id_resolution: readObject(targetsPayload.program_id_resolution),
|
|
2155
|
+
meta: stripUndefined({
|
|
2156
|
+
...(targetMeta ?? {}),
|
|
2157
|
+
found: true,
|
|
2158
|
+
query: input.query,
|
|
2159
|
+
target_filter: "in_scope_and_eligible_by_default",
|
|
2160
|
+
resolve_request_id: stringField(resolution, "request_id"),
|
|
2161
|
+
targets_request_id: stringField(targetsPayload, "request_id")
|
|
2162
|
+
})
|
|
2163
|
+
});
|
|
2164
|
+
}
|
|
1980
2165
|
async function runResolvedProgramAction(client, config, programId, input) {
|
|
1981
2166
|
if (input.action === "scope_delta") {
|
|
1982
2167
|
return getProgramScopeDelta(client, config, {
|
|
@@ -3159,6 +3344,93 @@ async function fetchProgramTargetsFromExportFallback(client, programId) {
|
|
|
3159
3344
|
};
|
|
3160
3345
|
}
|
|
3161
3346
|
}
|
|
3347
|
+
async function getProgramFromTargetsOnlyFallback(client, programId, programError) {
|
|
3348
|
+
const targetsApi = await client.getProgramTargets(programId);
|
|
3349
|
+
const programApiError = programError instanceof BBRadarApiError ? programError : undefined;
|
|
3350
|
+
return stripUndefined({
|
|
3351
|
+
request_id: targetsApi.requestId,
|
|
3352
|
+
fetched_at: targetsApi.fetchedAt,
|
|
3353
|
+
cache: readObject(stripUndefined({
|
|
3354
|
+
hit: targetsApi.cached,
|
|
3355
|
+
coalesced_live_request: targetsApi.coalesced,
|
|
3356
|
+
expires_at: targetsApi.cacheExpiresAt
|
|
3357
|
+
})),
|
|
3358
|
+
source_requests: [
|
|
3359
|
+
stripUndefined({
|
|
3360
|
+
source: "program",
|
|
3361
|
+
program_id: programId,
|
|
3362
|
+
failed: true,
|
|
3363
|
+
request_id: programApiError?.requestId,
|
|
3364
|
+
upstream_request_id: programApiError?.upstreamRequestId,
|
|
3365
|
+
status: programApiError?.status
|
|
3366
|
+
}),
|
|
3367
|
+
{
|
|
3368
|
+
source: "program_targets",
|
|
3369
|
+
program_id: programId,
|
|
3370
|
+
request_id: targetsApi.requestId,
|
|
3371
|
+
upstream_request_id: targetsApi.upstreamRequestId,
|
|
3372
|
+
...apiSourceMetadata(targetsApi)
|
|
3373
|
+
}
|
|
3374
|
+
],
|
|
3375
|
+
warnings: [`Returned minimal program metadata because the per-program detail endpoint returned 404 for ${programId}, but the targets endpoint is available.`],
|
|
3376
|
+
program: addProgramResourceLinks({ id: programId }),
|
|
3377
|
+
meta: {
|
|
3378
|
+
detail_source: "program_targets",
|
|
3379
|
+
detail_endpoint_unavailable: true
|
|
3380
|
+
}
|
|
3381
|
+
});
|
|
3382
|
+
}
|
|
3383
|
+
async function fetchProgramDetailsAndTargets(client, config, programId) {
|
|
3384
|
+
const [programResult, targetsResult] = await Promise.allSettled([client.getProgram(programId), client.getProgramTargets(programId)]);
|
|
3385
|
+
if (targetsResult.status === "rejected") {
|
|
3386
|
+
throw targetsResult.reason;
|
|
3387
|
+
}
|
|
3388
|
+
const targetsApi = targetsResult.value;
|
|
3389
|
+
const targetSourceRequest = {
|
|
3390
|
+
source: "program_targets",
|
|
3391
|
+
request_id: targetsApi.requestId,
|
|
3392
|
+
upstream_request_id: targetsApi.upstreamRequestId,
|
|
3393
|
+
...apiSourceMetadata(targetsApi)
|
|
3394
|
+
};
|
|
3395
|
+
if (programResult.status === "fulfilled") {
|
|
3396
|
+
return {
|
|
3397
|
+
program: addProgramResourceLinks(sanitizeProgram(programResult.value.data, config.webBaseUrl)),
|
|
3398
|
+
targetsApi,
|
|
3399
|
+
sourceRequests: [
|
|
3400
|
+
{
|
|
3401
|
+
source: "program",
|
|
3402
|
+
request_id: programResult.value.requestId,
|
|
3403
|
+
upstream_request_id: programResult.value.upstreamRequestId,
|
|
3404
|
+
...apiSourceMetadata(programResult.value)
|
|
3405
|
+
},
|
|
3406
|
+
targetSourceRequest
|
|
3407
|
+
],
|
|
3408
|
+
warnings: [],
|
|
3409
|
+
programDetailUnavailable: false
|
|
3410
|
+
};
|
|
3411
|
+
}
|
|
3412
|
+
if (!isProgramNotFoundError(programResult.reason)) {
|
|
3413
|
+
throw programResult.reason;
|
|
3414
|
+
}
|
|
3415
|
+
const programApiError = programResult.reason instanceof BBRadarApiError ? programResult.reason : undefined;
|
|
3416
|
+
return {
|
|
3417
|
+
program: addProgramResourceLinks({ id: programId }),
|
|
3418
|
+
targetsApi,
|
|
3419
|
+
sourceRequests: [
|
|
3420
|
+
stripUndefined({
|
|
3421
|
+
source: "program",
|
|
3422
|
+
program_id: programId,
|
|
3423
|
+
failed: true,
|
|
3424
|
+
request_id: programApiError?.requestId,
|
|
3425
|
+
upstream_request_id: programApiError?.upstreamRequestId,
|
|
3426
|
+
status: programApiError?.status
|
|
3427
|
+
}),
|
|
3428
|
+
targetSourceRequest
|
|
3429
|
+
],
|
|
3430
|
+
warnings: [`Used targets endpoint with minimal program metadata because the per-program detail endpoint returned 404 for ${programId}.`],
|
|
3431
|
+
programDetailUnavailable: true
|
|
3432
|
+
};
|
|
3433
|
+
}
|
|
3162
3434
|
async function exportTargetsFromProgramTargetsFallback(client, config, exportStore, input, exportError) {
|
|
3163
3435
|
const warnings = ["Target export endpoint is unavailable; assembled a fallback export from per-program target endpoints."];
|
|
3164
3436
|
const sourceRequests = [
|
|
@@ -3282,8 +3554,8 @@ function readExportTargetRows(data) {
|
|
|
3282
3554
|
.find((rows) => rows.length > 0) ?? [];
|
|
3283
3555
|
}
|
|
3284
3556
|
async function getProgramScopeSummary(client, config, input) {
|
|
3285
|
-
const
|
|
3286
|
-
const targetsData = readObject(targetsApi.data);
|
|
3557
|
+
const lookup = await fetchProgramDetailsAndTargets(client, config, input.program_id);
|
|
3558
|
+
const targetsData = readObject(lookup.targetsApi.data);
|
|
3287
3559
|
const rawTargets = readArray(targetsData?.targets);
|
|
3288
3560
|
const targets = rawTargets
|
|
3289
3561
|
.map(sanitizeTarget)
|
|
@@ -3292,31 +3564,21 @@ async function getProgramScopeSummary(client, config, input) {
|
|
|
3292
3564
|
include_ineligible: input.include_ineligible,
|
|
3293
3565
|
strict_scope_filter: false
|
|
3294
3566
|
}));
|
|
3295
|
-
return {
|
|
3567
|
+
return stripUndefined({
|
|
3296
3568
|
request_id: randomUUID(),
|
|
3297
|
-
source_requests:
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
request_id: programApi.requestId,
|
|
3301
|
-
upstream_request_id: programApi.upstreamRequestId,
|
|
3302
|
-
...apiSourceMetadata(programApi)
|
|
3303
|
-
},
|
|
3304
|
-
{
|
|
3305
|
-
source: "program_targets",
|
|
3306
|
-
request_id: targetsApi.requestId,
|
|
3307
|
-
upstream_request_id: targetsApi.upstreamRequestId,
|
|
3308
|
-
...apiSourceMetadata(targetsApi)
|
|
3309
|
-
}
|
|
3310
|
-
],
|
|
3311
|
-
program: formatProgram(addProgramResourceLinks(sanitizeProgram(programApi.data, config.webBaseUrl)), "compact"),
|
|
3569
|
+
source_requests: lookup.sourceRequests,
|
|
3570
|
+
warnings: lookup.warnings.length > 0 ? lookup.warnings : undefined,
|
|
3571
|
+
program: formatProgram(lookup.program, "compact"),
|
|
3312
3572
|
scope: buildTargetScopeSummary(targets, input.target_list_mode, input.group_limit, input.include_out_of_scope, input.include_ineligible),
|
|
3313
3573
|
meta: {
|
|
3314
3574
|
total_active_targets: rawTargets.length,
|
|
3315
3575
|
total_after_filters: targets.length,
|
|
3316
3576
|
target_list_mode: input.target_list_mode,
|
|
3317
|
-
group_limit: input.group_limit
|
|
3577
|
+
group_limit: input.group_limit,
|
|
3578
|
+
target_source: "program_targets",
|
|
3579
|
+
detail_endpoint_unavailable: lookup.programDetailUnavailable || undefined
|
|
3318
3580
|
}
|
|
3319
|
-
};
|
|
3581
|
+
});
|
|
3320
3582
|
}
|
|
3321
3583
|
async function getProgramScopeSummaryFromIndexFallback(client, indexFallback, input) {
|
|
3322
3584
|
const exportLookup = await fetchProgramTargetsWithEndpointFallback(client, input.program_id);
|
|
@@ -3346,8 +3608,8 @@ async function getProgramScopeSummaryFromIndexFallback(client, indexFallback, in
|
|
|
3346
3608
|
});
|
|
3347
3609
|
}
|
|
3348
3610
|
async function getProgramTargetBreakdown(client, config, input) {
|
|
3349
|
-
const
|
|
3350
|
-
const targetsData = readObject(targetsApi.data);
|
|
3611
|
+
const lookup = await fetchProgramDetailsAndTargets(client, config, input.program_id);
|
|
3612
|
+
const targetsData = readObject(lookup.targetsApi.data);
|
|
3351
3613
|
const rawTargets = readArray(targetsData?.targets);
|
|
3352
3614
|
const targets = rawTargets
|
|
3353
3615
|
.map(sanitizeTarget)
|
|
@@ -3357,23 +3619,11 @@ async function getProgramTargetBreakdown(client, config, input) {
|
|
|
3357
3619
|
strict_scope_filter: false
|
|
3358
3620
|
}));
|
|
3359
3621
|
const scope = buildTargetScopeSummary(targets, input.target_list_mode, input.group_limit, input.include_out_of_scope, input.include_ineligible);
|
|
3360
|
-
return {
|
|
3622
|
+
return stripUndefined({
|
|
3361
3623
|
request_id: randomUUID(),
|
|
3362
|
-
source_requests:
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
request_id: programApi.requestId,
|
|
3366
|
-
upstream_request_id: programApi.upstreamRequestId,
|
|
3367
|
-
...apiSourceMetadata(programApi)
|
|
3368
|
-
},
|
|
3369
|
-
{
|
|
3370
|
-
source: "program_targets",
|
|
3371
|
-
request_id: targetsApi.requestId,
|
|
3372
|
-
upstream_request_id: targetsApi.upstreamRequestId,
|
|
3373
|
-
...apiSourceMetadata(targetsApi)
|
|
3374
|
-
}
|
|
3375
|
-
],
|
|
3376
|
-
program: formatProgram(addProgramResourceLinks(sanitizeProgram(programApi.data, config.webBaseUrl)), "compact"),
|
|
3624
|
+
source_requests: lookup.sourceRequests,
|
|
3625
|
+
warnings: lookup.warnings.length > 0 ? lookup.warnings : undefined,
|
|
3626
|
+
program: formatProgram(lookup.program, "compact"),
|
|
3377
3627
|
breakdown: {
|
|
3378
3628
|
target_counts: readObject(scope.target_counts),
|
|
3379
3629
|
by_target_type: countTargetValues(targets, (target) => stringField(target, "target_type")),
|
|
@@ -3387,9 +3637,11 @@ async function getProgramTargetBreakdown(client, config, input) {
|
|
|
3387
3637
|
total_active_targets: rawTargets.length,
|
|
3388
3638
|
total_after_filters: targets.length,
|
|
3389
3639
|
target_list_mode: input.target_list_mode,
|
|
3390
|
-
group_limit: input.group_limit
|
|
3640
|
+
group_limit: input.group_limit,
|
|
3641
|
+
target_source: "program_targets",
|
|
3642
|
+
detail_endpoint_unavailable: lookup.programDetailUnavailable || undefined
|
|
3391
3643
|
}
|
|
3392
|
-
};
|
|
3644
|
+
});
|
|
3393
3645
|
}
|
|
3394
3646
|
async function getProgramTargetBreakdownFromIndexFallback(client, indexFallback, input) {
|
|
3395
3647
|
const exportLookup = await fetchProgramTargetsWithEndpointFallback(client, input.program_id);
|
|
@@ -3839,14 +4091,13 @@ async function checkWatchlistNewTargets(client, config, input) {
|
|
|
3839
4091
|
});
|
|
3840
4092
|
}
|
|
3841
4093
|
async function getProgramBrief(client, config, input) {
|
|
3842
|
-
const
|
|
3843
|
-
const targetsPromise = client.getProgramTargets(input.program_id);
|
|
4094
|
+
const detailsAndTargetsPromise = fetchProgramDetailsAndTargets(client, config, input.program_id);
|
|
3844
4095
|
const changesPromise = input.include_recent_changes && input.recent_changes_limit > 0
|
|
3845
4096
|
? fetchProgramChanges(client, config, input.program_id, input.recent_changes_limit, true, input.include_ineligible, input.include_out_of_scope)
|
|
3846
4097
|
: Promise.resolve(undefined);
|
|
3847
|
-
const [
|
|
3848
|
-
const candidate = toProgramCandidate(
|
|
3849
|
-
const targetsData = readObject(targetsApi.data);
|
|
4098
|
+
const [lookup, changes] = await Promise.all([detailsAndTargetsPromise, changesPromise]);
|
|
4099
|
+
const candidate = toProgramCandidate(lookup.program, config.webBaseUrl);
|
|
4100
|
+
const targetsData = readObject(lookup.targetsApi.data);
|
|
3850
4101
|
const rawTargets = readArray(targetsData?.targets);
|
|
3851
4102
|
const targets = rawTargets
|
|
3852
4103
|
.map(sanitizeTarget)
|
|
@@ -3858,21 +4109,8 @@ async function getProgramBrief(client, config, input) {
|
|
|
3858
4109
|
const scope = buildTargetScopeSummary(targets, input.target_list_mode, input.target_sample_size, input.include_out_of_scope, input.include_ineligible);
|
|
3859
4110
|
return stripUndefined({
|
|
3860
4111
|
request_id: randomUUID(),
|
|
3861
|
-
source_requests: [
|
|
3862
|
-
|
|
3863
|
-
source: "program",
|
|
3864
|
-
request_id: programApi.requestId,
|
|
3865
|
-
upstream_request_id: programApi.upstreamRequestId,
|
|
3866
|
-
...apiSourceMetadata(programApi)
|
|
3867
|
-
},
|
|
3868
|
-
{
|
|
3869
|
-
source: "program_targets",
|
|
3870
|
-
request_id: targetsApi.requestId,
|
|
3871
|
-
upstream_request_id: targetsApi.upstreamRequestId,
|
|
3872
|
-
...apiSourceMetadata(targetsApi)
|
|
3873
|
-
},
|
|
3874
|
-
...(changes?.sourceRequests ?? [])
|
|
3875
|
-
],
|
|
4112
|
+
source_requests: [...lookup.sourceRequests, ...(changes?.sourceRequests ?? [])],
|
|
4113
|
+
warnings: lookup.warnings.length > 0 ? lookup.warnings : undefined,
|
|
3876
4114
|
program: formatProgram(candidate.program, "compact"),
|
|
3877
4115
|
brief: {
|
|
3878
4116
|
rank_factors: rankFactors(candidate),
|
|
@@ -3888,7 +4126,9 @@ async function getProgramBrief(client, config, input) {
|
|
|
3888
4126
|
total_active_targets: rawTargets.length,
|
|
3889
4127
|
total_targets_after_filters: targets.length,
|
|
3890
4128
|
recent_changes_returned: changes?.changes.length ?? 0,
|
|
3891
|
-
target_list_mode: input.target_list_mode
|
|
4129
|
+
target_list_mode: input.target_list_mode,
|
|
4130
|
+
target_source: "program_targets",
|
|
4131
|
+
detail_endpoint_unavailable: lookup.programDetailUnavailable || undefined
|
|
3892
4132
|
}
|
|
3893
4133
|
});
|
|
3894
4134
|
}
|
|
@@ -4485,6 +4725,12 @@ function programResolutionMatch(candidate, query) {
|
|
|
4485
4725
|
reasons: [...reasons]
|
|
4486
4726
|
};
|
|
4487
4727
|
}
|
|
4728
|
+
function hasHighConfidenceProgramResolutionMatch(programs, webBaseUrl, query) {
|
|
4729
|
+
return programs.some((program) => programResolutionMatch(toProgramCandidate(program, webBaseUrl), query).score >= 85);
|
|
4730
|
+
}
|
|
4731
|
+
function bestProgramResolutionScore(programs, webBaseUrl, query) {
|
|
4732
|
+
return programs.reduce((best, program) => Math.max(best, programResolutionMatch(toProgramCandidate(program, webBaseUrl), query).score), 0);
|
|
4733
|
+
}
|
|
4488
4734
|
function programResolutionSearchQueries(query) {
|
|
4489
4735
|
return programResolutionQueryVariants(query).slice(0, MAX_RESOLVE_SEARCH_QUERIES);
|
|
4490
4736
|
}
|
|
@@ -4492,11 +4738,20 @@ function programResolutionQueryVariants(query) {
|
|
|
4492
4738
|
const normalized = normalizeTag(query);
|
|
4493
4739
|
const meaningfulTokens = [...meaningfulProgramQueryTokens(query)];
|
|
4494
4740
|
const meaningfulPhrase = meaningfulTokens.join(" ");
|
|
4741
|
+
const singleTokenNameVariants = meaningfulTokens.length === 1
|
|
4742
|
+
? [
|
|
4743
|
+
`${meaningfulTokens[0]} network`,
|
|
4744
|
+
`${meaningfulTokens[0]} protocol`,
|
|
4745
|
+
`${meaningfulTokens[0]} bug bounty`,
|
|
4746
|
+
`${meaningfulTokens[0]} bounty`
|
|
4747
|
+
]
|
|
4748
|
+
: [];
|
|
4495
4749
|
const values = [
|
|
4496
4750
|
normalized,
|
|
4497
4751
|
meaningfulPhrase,
|
|
4498
4752
|
meaningfulTokens.join("-"),
|
|
4499
|
-
...meaningfulTokens
|
|
4753
|
+
...meaningfulTokens,
|
|
4754
|
+
...singleTokenNameVariants
|
|
4500
4755
|
];
|
|
4501
4756
|
return uniqueStrings(values.filter((value) => value.length >= 2));
|
|
4502
4757
|
}
|
|
@@ -4711,22 +4966,48 @@ async function collectPrograms(client, options) {
|
|
|
4711
4966
|
}
|
|
4712
4967
|
async function collectProgramsForResolution(client, options) {
|
|
4713
4968
|
const collections = [];
|
|
4969
|
+
const queryCollections = [];
|
|
4714
4970
|
const queries = options.searchQueries.length > 0 ? options.searchQueries : [undefined];
|
|
4715
4971
|
for (const search of queries) {
|
|
4716
4972
|
if (options.budget.remaining <= 0) {
|
|
4717
4973
|
addUniqueWarning(options.warnings, "Upstream request budget was exhausted before all program name search variants could be checked.");
|
|
4718
4974
|
break;
|
|
4719
4975
|
}
|
|
4720
|
-
|
|
4976
|
+
const collection = await collectPrograms(client, {
|
|
4721
4977
|
search,
|
|
4722
4978
|
platforms: options.platforms,
|
|
4723
4979
|
tags: options.tags,
|
|
4724
4980
|
updated_since: undefined,
|
|
4725
4981
|
opportunity_levels: options.opportunity_levels,
|
|
4726
|
-
max_pages:
|
|
4982
|
+
max_pages: 1,
|
|
4727
4983
|
budget: options.budget,
|
|
4728
4984
|
warnings: options.warnings
|
|
4729
|
-
})
|
|
4985
|
+
});
|
|
4986
|
+
collections.push(collection);
|
|
4987
|
+
queryCollections.push({ search, collection });
|
|
4988
|
+
if (options.isHighConfidenceMatch?.(mergeProgramCollections(collections).programs)) {
|
|
4989
|
+
return mergeProgramCollections(collections);
|
|
4990
|
+
}
|
|
4991
|
+
}
|
|
4992
|
+
if (options.max_pages > 1) {
|
|
4993
|
+
const search = bestResolutionDeepSearch(queryCollections, options.scorePrograms) ?? queries[0];
|
|
4994
|
+
if (options.budget.remaining <= 0) {
|
|
4995
|
+
addUniqueWarning(options.warnings, "Upstream request budget was exhausted before more program name result pages could be checked.");
|
|
4996
|
+
}
|
|
4997
|
+
else if (search !== undefined || queries.length > 0) {
|
|
4998
|
+
const collection = await collectPrograms(client, {
|
|
4999
|
+
search,
|
|
5000
|
+
platforms: options.platforms,
|
|
5001
|
+
tags: options.tags,
|
|
5002
|
+
updated_since: undefined,
|
|
5003
|
+
opportunity_levels: options.opportunity_levels,
|
|
5004
|
+
max_pages: options.max_pages,
|
|
5005
|
+
start_page: 2,
|
|
5006
|
+
budget: options.budget,
|
|
5007
|
+
warnings: options.warnings
|
|
5008
|
+
});
|
|
5009
|
+
collections.push(collection);
|
|
5010
|
+
}
|
|
4730
5011
|
}
|
|
4731
5012
|
if (collections.length === 0) {
|
|
4732
5013
|
return {
|
|
@@ -4739,6 +5020,16 @@ async function collectProgramsForResolution(client, options) {
|
|
|
4739
5020
|
}
|
|
4740
5021
|
return mergeProgramCollections(collections);
|
|
4741
5022
|
}
|
|
5023
|
+
function bestResolutionDeepSearch(queryCollections, scorePrograms) {
|
|
5024
|
+
if (queryCollections.length === 0) {
|
|
5025
|
+
return undefined;
|
|
5026
|
+
}
|
|
5027
|
+
if (!scorePrograms) {
|
|
5028
|
+
return queryCollections[0]?.search;
|
|
5029
|
+
}
|
|
5030
|
+
return [...queryCollections]
|
|
5031
|
+
.sort((left, right) => scorePrograms(right.collection.programs) - scorePrograms(left.collection.programs))[0]?.search;
|
|
5032
|
+
}
|
|
4742
5033
|
function mergeProgramCollections(collections) {
|
|
4743
5034
|
const programs = new Map();
|
|
4744
5035
|
const totalPagesBySource = {};
|
|
@@ -4766,6 +5057,18 @@ async function collectProgramSources(client, sources, options) {
|
|
|
4766
5057
|
return pages;
|
|
4767
5058
|
}
|
|
4768
5059
|
async function collectProgramSource(client, source, sourceIndex, options) {
|
|
5060
|
+
const startPage = Math.max(1, options.start_page ?? 1);
|
|
5061
|
+
if (startPage > 1) {
|
|
5062
|
+
const pageNumbers = Array.from({ length: Math.max(0, options.max_pages - startPage + 1) }, (_, index) => startPage + index);
|
|
5063
|
+
const pages = [];
|
|
5064
|
+
await mapWithConcurrency(pageNumbers, PROGRAM_COLLECTION_CONCURRENCY, async (page) => {
|
|
5065
|
+
const result = await fetchProgramPage(client, source, sourceIndex, page, options);
|
|
5066
|
+
if (result) {
|
|
5067
|
+
pages.push(result);
|
|
5068
|
+
}
|
|
5069
|
+
});
|
|
5070
|
+
return pages;
|
|
5071
|
+
}
|
|
4769
5072
|
const firstPage = await fetchProgramPage(client, source, sourceIndex, 1, options);
|
|
4770
5073
|
if (!firstPage) {
|
|
4771
5074
|
return [];
|
|
@@ -5230,7 +5533,8 @@ Find the best BBRadar program candidates.
|
|
|
5230
5533
|
- Apply tag filters from this JSON string if provided: ${promptJson(args.tags ?? "")}.
|
|
5231
5534
|
- Keep the shortlist to this JSON value: ${promptJson(args.max_programs ?? "10")}.
|
|
5232
5535
|
- Compare freshness, opportunity score, reward range, public report count, target counts, and scope tags.
|
|
5233
|
-
- For
|
|
5536
|
+
- For a user asking for an in-scope or bounty-eligible target list by program name, call get_program_eligible_targets_by_name once and treat its target list as final.
|
|
5537
|
+
- For an already known program_id, call get_program_targets directly for exact target lists. Use get_program_scope_summary or get_program_target_breakdown only when grouped counts are needed.
|
|
5234
5538
|
- Return a ranked list with concise rationale, likely target themes, and BBRadar URLs.
|
|
5235
5539
|
- Stay passive-only; do not recommend scanning, probing, exploit attempts, or direct contact with targets.`));
|
|
5236
5540
|
server.registerPrompt("summarize_program_scope", {
|
|
@@ -5239,7 +5543,7 @@ Find the best BBRadar program candidates.
|
|
|
5239
5543
|
argsSchema: {
|
|
5240
5544
|
program_id: programIdSchema
|
|
5241
5545
|
}
|
|
5242
|
-
}, (args) => promptResult(`Use
|
|
5546
|
+
}, (args) => promptResult(`Use get_program_targets for the program_id in this JSON string: ${promptJson(args.program_id)} with include_out_of_scope=false, include_ineligible=false, and limit=${MAX_TARGETS_PER_PROGRAM}. Do not call get_program first unless program metadata is explicitly needed. Only request out-of-scope or ineligible targets if the user explicitly asks for them.
|
|
5243
5547
|
|
|
5244
5548
|
Summarize the program scope:
|
|
5245
5549
|
- Program name, platform, reward range, public report count, first seen date, last updated date, and BBRadar URL.
|
|
@@ -5260,7 +5564,8 @@ Summarize the program scope:
|
|
|
5260
5564
|
Do not invoke local bug bounty skills, methodology files, worklogs, or non-BBRadar tools unless the user explicitly asks for external methodology.
|
|
5261
5565
|
|
|
5262
5566
|
Use BBRadar MCP data first:
|
|
5263
|
-
- If a program_id is provided, call get_program
|
|
5567
|
+
- If a program_id is provided, call get_program_targets first. Call get_program only if metadata is explicitly needed.
|
|
5568
|
+
- If the user provides a program name and asks for in-scope or bounty-eligible targets, call get_program_eligible_targets_by_name once.
|
|
5264
5569
|
- If no program_id is provided, call get_opportunities and select a small set of candidate programs before planning.
|
|
5265
5570
|
- Focus area JSON string: ${promptJson(args.focus ?? "highest-signal in-scope bounty-eligible targets")}.
|
|
5266
5571
|
|