@bbradar/mcp 0.1.11 → 0.1.13
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/README.md +7 -0
- package/dist/server.js +855 -326
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -70,13 +70,51 @@ const PROGRAM_RESOLUTION_STOP_WORDS = new Set([
|
|
|
70
70
|
"have",
|
|
71
71
|
"any"
|
|
72
72
|
]);
|
|
73
|
+
const TARGET_QUERY_STOP_WORDS = new Set([
|
|
74
|
+
"program",
|
|
75
|
+
"programs",
|
|
76
|
+
"target",
|
|
77
|
+
"targets",
|
|
78
|
+
"scope",
|
|
79
|
+
"scopes",
|
|
80
|
+
"asset",
|
|
81
|
+
"assets",
|
|
82
|
+
"endpoint",
|
|
83
|
+
"endpoints",
|
|
84
|
+
"url",
|
|
85
|
+
"urls",
|
|
86
|
+
"http",
|
|
87
|
+
"https",
|
|
88
|
+
"www",
|
|
89
|
+
"the",
|
|
90
|
+
"a",
|
|
91
|
+
"an",
|
|
92
|
+
"of",
|
|
93
|
+
"on",
|
|
94
|
+
"in",
|
|
95
|
+
"with",
|
|
96
|
+
"to",
|
|
97
|
+
"for",
|
|
98
|
+
"find",
|
|
99
|
+
"show",
|
|
100
|
+
"lookup",
|
|
101
|
+
"look",
|
|
102
|
+
"up",
|
|
103
|
+
"com",
|
|
104
|
+
"org",
|
|
105
|
+
"net",
|
|
106
|
+
"io"
|
|
107
|
+
]);
|
|
73
108
|
const targetListModeSchema = z.enum(["identifiers", "compact", "full"]);
|
|
74
109
|
const changeListModeSchema = z.enum(["compact", "full"]);
|
|
75
110
|
const programListModeSchema = z.enum(["compact", "full"]);
|
|
76
111
|
const rewardThresholdModeSchema = z.enum(["max_at_least", "min_at_least"]);
|
|
77
112
|
const vdpModeSchema = z.enum(["include", "exclude", "only_likely", "only_known_no_bounty"]);
|
|
78
|
-
const targetMatchModeSchema = z
|
|
113
|
+
const targetMatchModeSchema = z
|
|
114
|
+
.enum(["smart", "contains", "exact", "suffix", "wildcard"])
|
|
115
|
+
.describe("How to match target_query. smart is forgiving for URLs, wildcards, target names, and partial words.");
|
|
79
116
|
const programNameActionSchema = z.enum(["new_targets", "targets", "scope_delta", "brief", "target_breakdown"]);
|
|
117
|
+
const guideTopicSchema = z.enum(["start", "program", "discovery", "freshness", "targets", "compare", "export", "filters"]).default("start");
|
|
80
118
|
const readOnlyAnnotations = {
|
|
81
119
|
readOnlyHint: true,
|
|
82
120
|
destructiveHint: false,
|
|
@@ -84,17 +122,36 @@ const readOnlyAnnotations = {
|
|
|
84
122
|
openWorldHint: true
|
|
85
123
|
};
|
|
86
124
|
const TOOL_METRICS = new Map();
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
125
|
+
function filterValueSchema() {
|
|
126
|
+
return z
|
|
127
|
+
.string()
|
|
128
|
+
.trim()
|
|
129
|
+
.min(1)
|
|
130
|
+
.max(128)
|
|
131
|
+
.refine((value) => !/[,\u0000-\u001F\u007F]/.test(value), {
|
|
132
|
+
message: "Filter values cannot contain commas or control characters."
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function stringListSchema(description = "Optional filter values. Empty or omitted means all values; do not infer a platform filter.") {
|
|
136
|
+
return z
|
|
137
|
+
.preprocess(normalizeStringListInput, z.array(filterValueSchema()).max(50).default([]))
|
|
138
|
+
.describe(description);
|
|
139
|
+
}
|
|
140
|
+
function platformListSchema() {
|
|
141
|
+
return stringListSchema("Optional platform filters such as HackerOne, Bugcrowd, Intigriti, or HackenProof. Empty or omitted means all platforms.");
|
|
142
|
+
}
|
|
143
|
+
function tagListSchema() {
|
|
144
|
+
return stringListSchema("Optional combined scope/language/target-type tags. Empty or omitted means all tags.");
|
|
145
|
+
}
|
|
146
|
+
function scopeTagListSchema() {
|
|
147
|
+
return stringListSchema("Optional scope tags such as domain, api, mobile, smart-contract, or wildcard. Empty or omitted means all scope tags.");
|
|
148
|
+
}
|
|
149
|
+
function targetTypeListSchema() {
|
|
150
|
+
return stringListSchema("Optional target-type tags such as domain, api, mobile, smart-contract, url, or wildcard. Empty or omitted means all target types.");
|
|
151
|
+
}
|
|
152
|
+
function languageTagListSchema() {
|
|
153
|
+
return stringListSchema("Optional language or technology tags such as javascript, solidity, web3, php, or python. Empty or omitted means all languages.");
|
|
154
|
+
}
|
|
98
155
|
const acceptedTargetLimitSchema = z.number().int().min(1).max(MAX_TARGETS_PER_PROGRAM);
|
|
99
156
|
const searchTextSchema = z
|
|
100
157
|
.string()
|
|
@@ -104,6 +161,15 @@ const searchTextSchema = z
|
|
|
104
161
|
.refine((value) => !/[\u0000-\u001F\u007F]/.test(value), {
|
|
105
162
|
message: "Search text cannot contain control characters."
|
|
106
163
|
});
|
|
164
|
+
const targetQuerySchema = z
|
|
165
|
+
.string()
|
|
166
|
+
.trim()
|
|
167
|
+
.min(2)
|
|
168
|
+
.max(512)
|
|
169
|
+
.refine((value) => !/[\u0000-\u001F\u007F]/.test(value), {
|
|
170
|
+
message: "Target query cannot contain control characters."
|
|
171
|
+
})
|
|
172
|
+
.describe("Target identifier, domain, URL, wildcard, app/package/repo name, or partial target text. URLs and wildcards are normalized automatically.");
|
|
107
173
|
const isoDateTimeSchema = z.string().datetime({ offset: true });
|
|
108
174
|
const pageSchema = z.number().int().min(1).default(1);
|
|
109
175
|
const programsPageSizeSchema = z.number().int().min(1).max(MAX_PROGRAMS_PER_SEARCH).default(20);
|
|
@@ -133,69 +199,97 @@ const programIdSchema = z
|
|
|
133
199
|
message: "program_id cannot contain control characters."
|
|
134
200
|
})
|
|
135
201
|
.describe("platform:handle id.");
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
202
|
+
const programIdentifierSchema = z
|
|
203
|
+
.string()
|
|
204
|
+
.trim()
|
|
205
|
+
.min(2)
|
|
206
|
+
.max(256)
|
|
207
|
+
.refine((value) => !/[\u0000-\u001F\u007F]/.test(value), {
|
|
208
|
+
message: "Program identifiers cannot contain control characters."
|
|
209
|
+
})
|
|
210
|
+
.describe("BBRadar platform:handle program_id or a human program name/handle. Names are resolved automatically.");
|
|
211
|
+
function jsonRecordSchema() {
|
|
212
|
+
return z.record(z.unknown());
|
|
213
|
+
}
|
|
214
|
+
function rateLimitOutputSchema() {
|
|
215
|
+
return z.object({
|
|
216
|
+
remaining: z.number().int().min(0),
|
|
217
|
+
reset_at: z.string()
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
function toolTimingOutputSchema() {
|
|
221
|
+
return z.object({
|
|
222
|
+
tool_name: z.string(),
|
|
223
|
+
duration_ms: z.number().int().min(0)
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
function cacheOutputSchema() {
|
|
227
|
+
return z.object({
|
|
228
|
+
hit: z.boolean(),
|
|
229
|
+
coalesced_live_request: z.boolean().optional(),
|
|
230
|
+
expires_at: z.string().optional()
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
function errorOutputSchema() {
|
|
234
|
+
return z.object({
|
|
235
|
+
message: z.string(),
|
|
236
|
+
status: z.number().int().optional(),
|
|
237
|
+
detail: z.unknown().optional(),
|
|
238
|
+
errors: z.unknown().optional()
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
function programOutputSchema() {
|
|
242
|
+
return jsonRecordSchema();
|
|
243
|
+
}
|
|
244
|
+
function apiEnvelopeOutputShape() {
|
|
245
|
+
return {
|
|
246
|
+
request_id: z.string(),
|
|
247
|
+
upstream_request_id: z.string().optional(),
|
|
248
|
+
fetched_at: z.string().optional(),
|
|
249
|
+
cache: cacheOutputSchema().optional(),
|
|
250
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
251
|
+
mcp_rate_limit: rateLimitOutputSchema().optional(),
|
|
252
|
+
mcp_timing: toolTimingOutputSchema().optional(),
|
|
253
|
+
warnings: z.array(z.string()).optional(),
|
|
254
|
+
program_id_resolution: jsonRecordSchema().optional(),
|
|
255
|
+
error: errorOutputSchema().optional()
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function programListOutputShape() {
|
|
259
|
+
return {
|
|
260
|
+
...apiEnvelopeOutputShape(),
|
|
261
|
+
programs: z.array(programOutputSchema()).optional(),
|
|
262
|
+
meta: jsonRecordSchema().optional()
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function findProgramsOutputShape() {
|
|
266
|
+
return {
|
|
267
|
+
...programListOutputShape(),
|
|
268
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
269
|
+
filters: jsonRecordSchema().optional(),
|
|
270
|
+
warnings: z.array(z.string()).optional(),
|
|
271
|
+
errors: z.array(jsonRecordSchema()).optional(),
|
|
272
|
+
upstream_budget: jsonRecordSchema().optional(),
|
|
273
|
+
ranking_scope: jsonRecordSchema().optional()
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function decisionOutputShape() {
|
|
277
|
+
return {
|
|
278
|
+
...findProgramsOutputShape(),
|
|
279
|
+
partial_success: z.boolean().optional(),
|
|
280
|
+
program_ids_requested: z.array(z.string()).optional(),
|
|
281
|
+
failed_program_ids: z.array(z.string()).optional(),
|
|
282
|
+
compared_programs: z.array(programOutputSchema()).optional(),
|
|
283
|
+
activity: jsonRecordSchema().optional(),
|
|
284
|
+
changes: z.array(jsonRecordSchema()).optional()
|
|
285
|
+
};
|
|
286
|
+
}
|
|
193
287
|
const programCandidatePresetInputSchema = {
|
|
194
|
-
platforms:
|
|
195
|
-
tags:
|
|
196
|
-
scope_tags:
|
|
197
|
-
target_types:
|
|
198
|
-
language_tags:
|
|
288
|
+
platforms: platformListSchema(),
|
|
289
|
+
tags: tagListSchema(),
|
|
290
|
+
scope_tags: scopeTagListSchema(),
|
|
291
|
+
target_types: targetTypeListSchema(),
|
|
292
|
+
language_tags: languageTagListSchema(),
|
|
199
293
|
opportunity_levels: z.array(opportunityLevelSchema).max(4).default(["elite", "hot", "strong", "potential"]),
|
|
200
294
|
web3: z.boolean().default(false),
|
|
201
295
|
wildcard: z.boolean().default(false),
|
|
@@ -225,7 +319,9 @@ export function createBbradarServer(client, config) {
|
|
|
225
319
|
}, {
|
|
226
320
|
instructions: [
|
|
227
321
|
"Use BBRadar only for passive bug bounty intelligence; never scan or contact targets.",
|
|
228
|
-
"
|
|
322
|
+
"When unsure which BBRadar tool to use, call get_bbradar_guide first. It is local-only and cheap.",
|
|
323
|
+
"Simple default path: find_program_candidates for discovery, run_program_name_action for one named program, get_program_eligible_targets_by_name for target lists by program name.",
|
|
324
|
+
"Use advanced tools only after the user's goal is clear or the guide points to them.",
|
|
229
325
|
"Partial program name + action: run_program_name_action. Partial program name only: resolve_program.",
|
|
230
326
|
"Default to bounty-eligible in-scope data. Include out-of-scope, ineligible, VDP, disclosure-only, or no-bounty data only when the user explicitly asks.",
|
|
231
327
|
"Recently updated programs across platforms: find_recently_updated_programs. Do not infer a platform such as Immunefi unless the user explicitly names it.",
|
|
@@ -241,14 +337,14 @@ export function createBbradarServer(client, config) {
|
|
|
241
337
|
title: "Search BBRadar Programs",
|
|
242
338
|
description: `List active programs. page_size max ${MAX_PROGRAMS_PER_SEARCH}.`,
|
|
243
339
|
inputSchema: {
|
|
244
|
-
platforms:
|
|
245
|
-
tags:
|
|
340
|
+
platforms: platformListSchema(),
|
|
341
|
+
tags: tagListSchema(),
|
|
246
342
|
updated_since: isoDateTimeSchema.optional().describe("ISO datetime."),
|
|
247
343
|
vdp_mode: vdpModeSchema.default("exclude").describe("VDP/no-bounty filter mode."),
|
|
248
344
|
page: pageSchema,
|
|
249
345
|
page_size: programsPageSizeSchema
|
|
250
346
|
},
|
|
251
|
-
outputSchema: programListOutputShape,
|
|
347
|
+
outputSchema: programListOutputShape(),
|
|
252
348
|
annotations: readOnlyAnnotations
|
|
253
349
|
}, (args) => runTool("search_programs", config, rateLimiter, async () => {
|
|
254
350
|
const { vdp_mode, ...filters } = args;
|
|
@@ -265,14 +361,14 @@ export function createBbradarServer(client, config) {
|
|
|
265
361
|
}));
|
|
266
362
|
server.registerTool("get_program", {
|
|
267
363
|
title: "Get BBRadar Program",
|
|
268
|
-
description: "Get one program by
|
|
364
|
+
description: "Get one program by platform:handle program_id, handle, or human program name. Names are resolved automatically when unambiguous.",
|
|
269
365
|
inputSchema: {
|
|
270
|
-
program_id:
|
|
366
|
+
program_id: programIdentifierSchema
|
|
271
367
|
},
|
|
272
368
|
outputSchema: {
|
|
273
|
-
...apiEnvelopeOutputShape,
|
|
274
|
-
program: programOutputSchema.optional(),
|
|
275
|
-
meta: jsonRecordSchema.optional()
|
|
369
|
+
...apiEnvelopeOutputShape(),
|
|
370
|
+
program: programOutputSchema().optional(),
|
|
371
|
+
meta: jsonRecordSchema().optional()
|
|
276
372
|
},
|
|
277
373
|
annotations: readOnlyAnnotations
|
|
278
374
|
}, (args) => runTool("get_program", config, rateLimiter, async () => {
|
|
@@ -304,8 +400,8 @@ export function createBbradarServer(client, config) {
|
|
|
304
400
|
description: "Resolve partial name/handle text to likely program_ids. Use this when the user gives a program name fragment like Nado.",
|
|
305
401
|
inputSchema: {
|
|
306
402
|
query: searchTextSchema,
|
|
307
|
-
platforms:
|
|
308
|
-
tags:
|
|
403
|
+
platforms: platformListSchema(),
|
|
404
|
+
tags: tagListSchema(),
|
|
309
405
|
opportunity_levels: z.array(opportunityLevelSchema).max(4).default([]),
|
|
310
406
|
max_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
311
407
|
max_results: z.number().int().min(1).max(25).default(10),
|
|
@@ -313,28 +409,28 @@ export function createBbradarServer(client, config) {
|
|
|
313
409
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
314
410
|
},
|
|
315
411
|
outputSchema: {
|
|
316
|
-
...programListOutputShape,
|
|
317
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
318
|
-
query: jsonRecordSchema.optional(),
|
|
319
|
-
matches: z.array(jsonRecordSchema).optional(),
|
|
412
|
+
...programListOutputShape(),
|
|
413
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
414
|
+
query: jsonRecordSchema().optional(),
|
|
415
|
+
matches: z.array(jsonRecordSchema()).optional(),
|
|
320
416
|
warnings: z.array(z.string()).optional(),
|
|
321
|
-
upstream_budget: jsonRecordSchema.optional(),
|
|
322
|
-
ranking_scope: jsonRecordSchema.optional()
|
|
417
|
+
upstream_budget: jsonRecordSchema().optional(),
|
|
418
|
+
ranking_scope: jsonRecordSchema().optional()
|
|
323
419
|
},
|
|
324
420
|
annotations: readOnlyAnnotations
|
|
325
421
|
}, (args) => runTool("resolve_program", config, rateLimiter, async () => resolveProgram(client, config, args)));
|
|
326
422
|
server.registerTool("run_program_name_action", {
|
|
327
423
|
title: "Run Program Name Action",
|
|
328
|
-
description: "
|
|
424
|
+
description: "Start here for one named program. Resolves a partial program name, then runs targets, new_targets, scope_delta, brief, or target_breakdown.",
|
|
329
425
|
inputSchema: {
|
|
330
426
|
query: searchTextSchema,
|
|
331
427
|
action: programNameActionSchema.default("new_targets"),
|
|
332
|
-
platforms:
|
|
333
|
-
tags:
|
|
428
|
+
platforms: platformListSchema(),
|
|
429
|
+
tags: tagListSchema(),
|
|
334
430
|
opportunity_levels: z.array(opportunityLevelSchema).max(4).default([]),
|
|
335
431
|
since: isoDateTimeSchema.optional(),
|
|
336
|
-
target_type: filterValueSchema.optional(),
|
|
337
|
-
language_tags:
|
|
432
|
+
target_type: filterValueSchema().optional(),
|
|
433
|
+
language_tags: languageTagListSchema(),
|
|
338
434
|
include_out_of_scope: z.boolean().default(false),
|
|
339
435
|
include_ineligible: z.boolean().default(false),
|
|
340
436
|
page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
|
|
@@ -346,14 +442,14 @@ export function createBbradarServer(client, config) {
|
|
|
346
442
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
347
443
|
},
|
|
348
444
|
outputSchema: {
|
|
349
|
-
...apiEnvelopeOutputShape,
|
|
350
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
351
|
-
resolved_program: jsonRecordSchema.optional(),
|
|
445
|
+
...apiEnvelopeOutputShape(),
|
|
446
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
447
|
+
resolved_program: jsonRecordSchema().optional(),
|
|
352
448
|
action: z.string().optional(),
|
|
353
|
-
result: jsonRecordSchema.optional(),
|
|
449
|
+
result: jsonRecordSchema().optional(),
|
|
354
450
|
warnings: z.array(z.string()).optional(),
|
|
355
|
-
upstream_budget: jsonRecordSchema.optional(),
|
|
356
|
-
ranking_scope: jsonRecordSchema.optional()
|
|
451
|
+
upstream_budget: jsonRecordSchema().optional(),
|
|
452
|
+
ranking_scope: jsonRecordSchema().optional()
|
|
357
453
|
},
|
|
358
454
|
annotations: readOnlyAnnotations
|
|
359
455
|
}, (args) => runTool("run_program_name_action", config, rateLimiter, async () => runProgramNameAction(client, config, args)));
|
|
@@ -362,8 +458,8 @@ export function createBbradarServer(client, config) {
|
|
|
362
458
|
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.",
|
|
363
459
|
inputSchema: {
|
|
364
460
|
query: searchTextSchema,
|
|
365
|
-
platforms:
|
|
366
|
-
tags:
|
|
461
|
+
platforms: platformListSchema(),
|
|
462
|
+
tags: tagListSchema(),
|
|
367
463
|
opportunity_levels: z.array(opportunityLevelSchema).max(4).default([]),
|
|
368
464
|
max_resolve_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
369
465
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET),
|
|
@@ -376,23 +472,23 @@ export function createBbradarServer(client, config) {
|
|
|
376
472
|
output_mode: programListModeSchema.default("compact")
|
|
377
473
|
},
|
|
378
474
|
outputSchema: {
|
|
379
|
-
...apiEnvelopeOutputShape,
|
|
380
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
381
|
-
resolved_program: jsonRecordSchema.optional(),
|
|
475
|
+
...apiEnvelopeOutputShape(),
|
|
476
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
477
|
+
resolved_program: jsonRecordSchema().optional(),
|
|
382
478
|
program_id: z.string().optional(),
|
|
383
479
|
targets: z.array(z.unknown()).optional(),
|
|
384
480
|
warnings: z.array(z.string()).optional(),
|
|
385
|
-
upstream_budget: jsonRecordSchema.optional(),
|
|
386
|
-
ranking_scope: jsonRecordSchema.optional(),
|
|
387
|
-
meta: jsonRecordSchema.optional()
|
|
481
|
+
upstream_budget: jsonRecordSchema().optional(),
|
|
482
|
+
ranking_scope: jsonRecordSchema().optional(),
|
|
483
|
+
meta: jsonRecordSchema().optional()
|
|
388
484
|
},
|
|
389
485
|
annotations: readOnlyAnnotations
|
|
390
486
|
}, (args) => runTool("get_program_eligible_targets_by_name", config, rateLimiter, async () => getProgramEligibleTargetsByName(client, config, args)));
|
|
391
487
|
server.registerTool("get_program_targets", {
|
|
392
488
|
title: "Get Program Targets",
|
|
393
|
-
description: `Get active targets for one
|
|
489
|
+
description: `Get active targets for one program_id, handle, or human program name. This is a final source for eligible target lists; do not call summary/breakdown unless grouped counts are needed. For a program name query, get_program_eligible_targets_by_name is the simplest path. limit max ${MAX_TARGETS_PER_PROGRAM}.`,
|
|
394
490
|
inputSchema: {
|
|
395
|
-
program_id:
|
|
491
|
+
program_id: programIdentifierSchema,
|
|
396
492
|
include_out_of_scope: z.boolean().default(false),
|
|
397
493
|
include_ineligible: z.boolean().default(false),
|
|
398
494
|
strict_scope_filter: z.boolean().default(true),
|
|
@@ -401,10 +497,10 @@ export function createBbradarServer(client, config) {
|
|
|
401
497
|
output_mode: targetListModeSchema.default("compact")
|
|
402
498
|
},
|
|
403
499
|
outputSchema: {
|
|
404
|
-
...apiEnvelopeOutputShape,
|
|
500
|
+
...apiEnvelopeOutputShape(),
|
|
405
501
|
program_id: z.string().optional(),
|
|
406
502
|
targets: z.array(z.unknown()).optional(),
|
|
407
|
-
meta: jsonRecordSchema.optional()
|
|
503
|
+
meta: jsonRecordSchema().optional()
|
|
408
504
|
},
|
|
409
505
|
annotations: readOnlyAnnotations
|
|
410
506
|
}, (args) => runTool("get_program_targets", config, rateLimiter, async () => {
|
|
@@ -419,17 +515,17 @@ export function createBbradarServer(client, config) {
|
|
|
419
515
|
include_ineligible: z.boolean().default(false),
|
|
420
516
|
include_out_of_scope: z.boolean().default(false),
|
|
421
517
|
search: searchTextSchema.optional(),
|
|
422
|
-
platforms:
|
|
423
|
-
tags:
|
|
518
|
+
platforms: platformListSchema(),
|
|
519
|
+
tags: tagListSchema(),
|
|
424
520
|
page: pageSchema,
|
|
425
521
|
page_size: recentChangesPageSizeSchema,
|
|
426
522
|
output_mode: changeListModeSchema.default("compact")
|
|
427
523
|
},
|
|
428
524
|
outputSchema: {
|
|
429
|
-
...apiEnvelopeOutputShape,
|
|
430
|
-
changes: z.array(jsonRecordSchema).optional(),
|
|
431
|
-
meta: jsonRecordSchema.optional(),
|
|
432
|
-
facets: jsonRecordSchema.optional()
|
|
525
|
+
...apiEnvelopeOutputShape(),
|
|
526
|
+
changes: z.array(jsonRecordSchema()).optional(),
|
|
527
|
+
meta: jsonRecordSchema().optional(),
|
|
528
|
+
facets: jsonRecordSchema().optional()
|
|
433
529
|
},
|
|
434
530
|
annotations: readOnlyAnnotations
|
|
435
531
|
}, (args) => runTool("get_recent_changes", config, rateLimiter, async () => {
|
|
@@ -453,7 +549,7 @@ export function createBbradarServer(client, config) {
|
|
|
453
549
|
title: "Get Latest Added Targets",
|
|
454
550
|
description: "Latest added targets by type, optionally with that program's target list.",
|
|
455
551
|
inputSchema: {
|
|
456
|
-
target_type: filterValueSchema.default("domain"),
|
|
552
|
+
target_type: filterValueSchema().default("domain"),
|
|
457
553
|
include_out_of_scope: z.boolean().default(false).describe("Include out-of-scope."),
|
|
458
554
|
include_ineligible: z.boolean().default(false).describe("Include ineligible."),
|
|
459
555
|
recent_changes_page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
|
|
@@ -464,14 +560,14 @@ export function createBbradarServer(client, config) {
|
|
|
464
560
|
full_list_include_ineligible: z.boolean().default(false)
|
|
465
561
|
},
|
|
466
562
|
outputSchema: {
|
|
467
|
-
...apiEnvelopeOutputShape,
|
|
468
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
469
|
-
query: jsonRecordSchema.optional(),
|
|
563
|
+
...apiEnvelopeOutputShape(),
|
|
564
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
565
|
+
query: jsonRecordSchema().optional(),
|
|
470
566
|
latest_added_at: z.string().optional(),
|
|
471
|
-
program: programOutputSchema.optional(),
|
|
567
|
+
program: programOutputSchema().optional(),
|
|
472
568
|
new_targets: z.array(z.unknown()).optional(),
|
|
473
569
|
full_targets: z.array(z.unknown()).optional(),
|
|
474
|
-
meta: jsonRecordSchema.optional()
|
|
570
|
+
meta: jsonRecordSchema().optional()
|
|
475
571
|
},
|
|
476
572
|
annotations: readOnlyAnnotations
|
|
477
573
|
}, (args) => runTool("get_latest_added_targets", config, rateLimiter, async () => getLatestAddedTargets(client, config, args)));
|
|
@@ -479,18 +575,18 @@ export function createBbradarServer(client, config) {
|
|
|
479
575
|
title: "Get Program Scope Summary",
|
|
480
576
|
description: "Grouped compact scope for one program.",
|
|
481
577
|
inputSchema: {
|
|
482
|
-
program_id:
|
|
578
|
+
program_id: programIdentifierSchema,
|
|
483
579
|
target_list_mode: targetListModeSchema.default("identifiers"),
|
|
484
580
|
group_limit: z.number().int().min(1).max(MAX_TARGETS_PER_PROGRAM).default(MAX_TARGETS_PER_PROGRAM),
|
|
485
581
|
include_out_of_scope: z.boolean().default(false),
|
|
486
582
|
include_ineligible: z.boolean().default(false)
|
|
487
583
|
},
|
|
488
584
|
outputSchema: {
|
|
489
|
-
...apiEnvelopeOutputShape,
|
|
490
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
491
|
-
program: programOutputSchema.optional(),
|
|
492
|
-
scope: jsonRecordSchema.optional(),
|
|
493
|
-
meta: jsonRecordSchema.optional()
|
|
585
|
+
...apiEnvelopeOutputShape(),
|
|
586
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
587
|
+
program: programOutputSchema().optional(),
|
|
588
|
+
scope: jsonRecordSchema().optional(),
|
|
589
|
+
meta: jsonRecordSchema().optional()
|
|
494
590
|
},
|
|
495
591
|
annotations: readOnlyAnnotations
|
|
496
592
|
}, (args) => runTool("get_program_scope_summary", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => getProgramScopeSummary(client, config, { ...args, program_id: programId }), (indexFallback) => getProgramScopeSummaryFromIndexFallback(client, indexFallback, { ...args, program_id: indexFallback.resolvedProgramId }))));
|
|
@@ -498,18 +594,18 @@ export function createBbradarServer(client, config) {
|
|
|
498
594
|
title: "Get Program Target Breakdown",
|
|
499
595
|
description: "Target type, scope tag, language tag, and bucket counts for one program.",
|
|
500
596
|
inputSchema: {
|
|
501
|
-
program_id:
|
|
597
|
+
program_id: programIdentifierSchema,
|
|
502
598
|
target_list_mode: targetListModeSchema.default("identifiers"),
|
|
503
599
|
group_limit: z.number().int().min(1).max(MAX_TARGETS_PER_PROGRAM).default(100),
|
|
504
600
|
include_out_of_scope: z.boolean().default(false),
|
|
505
601
|
include_ineligible: z.boolean().default(false)
|
|
506
602
|
},
|
|
507
603
|
outputSchema: {
|
|
508
|
-
...apiEnvelopeOutputShape,
|
|
509
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
510
|
-
program: programOutputSchema.optional(),
|
|
511
|
-
breakdown: jsonRecordSchema.optional(),
|
|
512
|
-
meta: jsonRecordSchema.optional()
|
|
604
|
+
...apiEnvelopeOutputShape(),
|
|
605
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
606
|
+
program: programOutputSchema().optional(),
|
|
607
|
+
breakdown: jsonRecordSchema().optional(),
|
|
608
|
+
meta: jsonRecordSchema().optional()
|
|
513
609
|
},
|
|
514
610
|
annotations: readOnlyAnnotations
|
|
515
611
|
}, (args) => runTool("get_program_target_breakdown", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => getProgramTargetBreakdown(client, config, { ...args, program_id: programId }), (indexFallback) => getProgramTargetBreakdownFromIndexFallback(client, indexFallback, { ...args, program_id: indexFallback.resolvedProgramId }))));
|
|
@@ -517,11 +613,11 @@ export function createBbradarServer(client, config) {
|
|
|
517
613
|
title: "Get Program Scope Delta",
|
|
518
614
|
description: "Compact recent scope changes for one program.",
|
|
519
615
|
inputSchema: {
|
|
520
|
-
program_id:
|
|
616
|
+
program_id: programIdentifierSchema,
|
|
521
617
|
since: isoDateTimeSchema.optional(),
|
|
522
618
|
change_type: z.enum(["added", "updated", "removed"]).optional(),
|
|
523
|
-
target_type: filterValueSchema.optional(),
|
|
524
|
-
language_tags:
|
|
619
|
+
target_type: filterValueSchema().optional(),
|
|
620
|
+
language_tags: languageTagListSchema(),
|
|
525
621
|
include_removed: z.boolean().default(true),
|
|
526
622
|
include_out_of_scope: z.boolean().default(false),
|
|
527
623
|
include_ineligible: z.boolean().default(false),
|
|
@@ -530,12 +626,12 @@ export function createBbradarServer(client, config) {
|
|
|
530
626
|
target_list_mode: targetListModeSchema.default("identifiers")
|
|
531
627
|
},
|
|
532
628
|
outputSchema: {
|
|
533
|
-
...apiEnvelopeOutputShape,
|
|
534
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
535
|
-
program: programOutputSchema.optional(),
|
|
536
|
-
delta: jsonRecordSchema.optional(),
|
|
537
|
-
changes: z.array(jsonRecordSchema).optional(),
|
|
538
|
-
meta: jsonRecordSchema.optional()
|
|
629
|
+
...apiEnvelopeOutputShape(),
|
|
630
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
631
|
+
program: programOutputSchema().optional(),
|
|
632
|
+
delta: jsonRecordSchema().optional(),
|
|
633
|
+
changes: z.array(jsonRecordSchema()).optional(),
|
|
634
|
+
meta: jsonRecordSchema().optional()
|
|
539
635
|
},
|
|
540
636
|
annotations: readOnlyAnnotations
|
|
541
637
|
}, (args) => runTool("get_program_scope_delta", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => getProgramScopeDelta(client, config, { ...args, program_id: programId }), (indexFallback) => getProgramScopeDeltaFromIndexedProgram(client, config, { ...args, program_id: indexFallback.resolvedProgramId }, indexFallback.program))));
|
|
@@ -544,24 +640,24 @@ export function createBbradarServer(client, config) {
|
|
|
544
640
|
description: "Recent target changes grouped by program.",
|
|
545
641
|
inputSchema: {
|
|
546
642
|
change_type: z.enum(["added", "updated", "removed"]).optional(),
|
|
547
|
-
target_type: filterValueSchema.optional(),
|
|
643
|
+
target_type: filterValueSchema().optional(),
|
|
548
644
|
include_removed: z.boolean().default(true),
|
|
549
645
|
include_out_of_scope: z.boolean().default(false),
|
|
550
646
|
include_ineligible: z.boolean().default(false),
|
|
551
647
|
search: searchTextSchema.optional(),
|
|
552
|
-
platforms:
|
|
553
|
-
tags:
|
|
554
|
-
language_tags:
|
|
648
|
+
platforms: platformListSchema(),
|
|
649
|
+
tags: tagListSchema(),
|
|
650
|
+
language_tags: languageTagListSchema(),
|
|
555
651
|
page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
|
|
556
652
|
max_programs: z.number().int().min(1).max(50).default(10),
|
|
557
653
|
sample_size: z.number().int().min(1).max(20).default(5),
|
|
558
654
|
target_list_mode: targetListModeSchema.default("identifiers")
|
|
559
655
|
},
|
|
560
656
|
outputSchema: {
|
|
561
|
-
...apiEnvelopeOutputShape,
|
|
562
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
563
|
-
activity: z.array(jsonRecordSchema).optional(),
|
|
564
|
-
meta: jsonRecordSchema.optional()
|
|
657
|
+
...apiEnvelopeOutputShape(),
|
|
658
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
659
|
+
activity: z.array(jsonRecordSchema()).optional(),
|
|
660
|
+
meta: jsonRecordSchema().optional()
|
|
565
661
|
},
|
|
566
662
|
annotations: readOnlyAnnotations
|
|
567
663
|
}, (args) => runTool("get_recent_target_activity", config, rateLimiter, async () => getRecentTargetActivity(client, config, args)));
|
|
@@ -569,24 +665,24 @@ export function createBbradarServer(client, config) {
|
|
|
569
665
|
title: "Check Program New Targets",
|
|
570
666
|
description: "Yes/no new in-scope targets for one program.",
|
|
571
667
|
inputSchema: {
|
|
572
|
-
program_id:
|
|
668
|
+
program_id: programIdentifierSchema,
|
|
573
669
|
since: isoDateTimeSchema.optional().describe("ISO datetime."),
|
|
574
|
-
target_type: filterValueSchema.optional(),
|
|
670
|
+
target_type: filterValueSchema().optional(),
|
|
575
671
|
include_ineligible: z.boolean().default(false).describe("Include ineligible."),
|
|
576
672
|
page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
|
|
577
673
|
max_targets: acceptedTargetLimitSchema.default(25),
|
|
578
674
|
target_list_mode: targetListModeSchema.default("identifiers")
|
|
579
675
|
},
|
|
580
676
|
outputSchema: {
|
|
581
|
-
...apiEnvelopeOutputShape,
|
|
582
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
677
|
+
...apiEnvelopeOutputShape(),
|
|
678
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
583
679
|
program_id: z.string().optional(),
|
|
584
|
-
program: programOutputSchema.optional(),
|
|
680
|
+
program: programOutputSchema().optional(),
|
|
585
681
|
has_new_targets: z.boolean().optional(),
|
|
586
682
|
new_target_count: z.number().int().min(0).optional(),
|
|
587
683
|
latest_added_at: z.string().optional(),
|
|
588
684
|
new_targets: z.array(z.unknown()).optional(),
|
|
589
|
-
meta: jsonRecordSchema.optional()
|
|
685
|
+
meta: jsonRecordSchema().optional()
|
|
590
686
|
},
|
|
591
687
|
annotations: readOnlyAnnotations
|
|
592
688
|
}, (args) => runTool("check_program_new_targets", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => checkProgramNewTargets(client, config, { ...args, program_id: programId }), (indexFallback) => checkProgramNewTargetsFromIndexedProgram(client, config, { ...args, program_id: indexFallback.resolvedProgramId }, indexFallback.program))));
|
|
@@ -594,20 +690,20 @@ export function createBbradarServer(client, config) {
|
|
|
594
690
|
title: "Check Watchlist New Targets",
|
|
595
691
|
description: "New in-scope targets across many known program_ids.",
|
|
596
692
|
inputSchema: {
|
|
597
|
-
program_ids: z.array(
|
|
693
|
+
program_ids: z.array(programIdentifierSchema).min(1).max(50),
|
|
598
694
|
since: isoDateTimeSchema.optional(),
|
|
599
|
-
target_type: filterValueSchema.optional(),
|
|
695
|
+
target_type: filterValueSchema().optional(),
|
|
600
696
|
include_ineligible: z.boolean().default(false),
|
|
601
697
|
page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
|
|
602
698
|
max_targets_per_program: z.number().int().min(1).max(50).default(10),
|
|
603
699
|
target_list_mode: targetListModeSchema.default("identifiers")
|
|
604
700
|
},
|
|
605
701
|
outputSchema: {
|
|
606
|
-
...apiEnvelopeOutputShape,
|
|
607
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
608
|
-
programs: z.array(jsonRecordSchema).optional(),
|
|
702
|
+
...apiEnvelopeOutputShape(),
|
|
703
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
704
|
+
programs: z.array(jsonRecordSchema()).optional(),
|
|
609
705
|
any_new_targets: z.boolean().optional(),
|
|
610
|
-
meta: jsonRecordSchema.optional()
|
|
706
|
+
meta: jsonRecordSchema().optional()
|
|
611
707
|
},
|
|
612
708
|
annotations: readOnlyAnnotations
|
|
613
709
|
}, (args) => runTool("check_watchlist_new_targets", config, rateLimiter, async () => checkWatchlistNewTargets(client, config, args)));
|
|
@@ -616,15 +712,15 @@ export function createBbradarServer(client, config) {
|
|
|
616
712
|
description: `List programs in one opportunity tier. page_size max ${MAX_PROGRAMS_PER_SEARCH}.`,
|
|
617
713
|
inputSchema: {
|
|
618
714
|
level: opportunityLevelSchema.default("elite"),
|
|
619
|
-
platforms:
|
|
620
|
-
tags:
|
|
715
|
+
platforms: platformListSchema(),
|
|
716
|
+
tags: tagListSchema(),
|
|
621
717
|
updated_since: isoDateTimeSchema.optional(),
|
|
622
718
|
vdp_mode: vdpModeSchema.default("exclude").describe("VDP/no-bounty filter mode."),
|
|
623
719
|
page: pageSchema,
|
|
624
720
|
page_size: programsPageSizeSchema
|
|
625
721
|
},
|
|
626
722
|
outputSchema: {
|
|
627
|
-
...programListOutputShape,
|
|
723
|
+
...programListOutputShape(),
|
|
628
724
|
level: opportunityLevelSchema.optional()
|
|
629
725
|
},
|
|
630
726
|
annotations: readOnlyAnnotations
|
|
@@ -646,11 +742,11 @@ export function createBbradarServer(client, config) {
|
|
|
646
742
|
title: "Find BBRadar Programs",
|
|
647
743
|
description: "Ranked program discovery with filters and compact output.",
|
|
648
744
|
inputSchema: {
|
|
649
|
-
platforms:
|
|
650
|
-
tags:
|
|
651
|
-
scope_tags:
|
|
652
|
-
target_types:
|
|
653
|
-
language_tags:
|
|
745
|
+
platforms: platformListSchema(),
|
|
746
|
+
tags: tagListSchema(),
|
|
747
|
+
scope_tags: scopeTagListSchema(),
|
|
748
|
+
target_types: targetTypeListSchema(),
|
|
749
|
+
language_tags: languageTagListSchema(),
|
|
654
750
|
web3: z.boolean().default(false).describe("Require Web3 signals."),
|
|
655
751
|
opportunity_levels: z.array(opportunityLevelSchema).max(4).default([]),
|
|
656
752
|
updated_since: isoDateTimeSchema.optional(),
|
|
@@ -684,7 +780,7 @@ export function createBbradarServer(client, config) {
|
|
|
684
780
|
output_mode: programListModeSchema.default("compact"),
|
|
685
781
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
686
782
|
},
|
|
687
|
-
outputSchema: findProgramsOutputShape,
|
|
783
|
+
outputSchema: findProgramsOutputShape(),
|
|
688
784
|
annotations: readOnlyAnnotations
|
|
689
785
|
}, (args) => runTool("find_programs", config, rateLimiter, async () => findPrograms(client, config, {
|
|
690
786
|
...args,
|
|
@@ -694,8 +790,8 @@ export function createBbradarServer(client, config) {
|
|
|
694
790
|
title: "Find Wildcard Programs",
|
|
695
791
|
description: "Wildcard program shortlist.",
|
|
696
792
|
inputSchema: {
|
|
697
|
-
platforms:
|
|
698
|
-
tags:
|
|
793
|
+
platforms: platformListSchema(),
|
|
794
|
+
tags: tagListSchema(),
|
|
699
795
|
updated_since: isoDateTimeSchema.optional(),
|
|
700
796
|
max_public_report_count: z.number().int().min(0).default(25),
|
|
701
797
|
require_known_report_count: z.boolean().default(true),
|
|
@@ -710,7 +806,7 @@ export function createBbradarServer(client, config) {
|
|
|
710
806
|
target_sample_size: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(5),
|
|
711
807
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
712
808
|
},
|
|
713
|
-
outputSchema: findProgramsOutputShape,
|
|
809
|
+
outputSchema: findProgramsOutputShape(),
|
|
714
810
|
annotations: readOnlyAnnotations
|
|
715
811
|
}, (args) => runTool("find_wildcard_programs", config, rateLimiter, async () => findPrograms(client, config, {
|
|
716
812
|
platforms: args.platforms,
|
|
@@ -754,8 +850,8 @@ export function createBbradarServer(client, config) {
|
|
|
754
850
|
title: "Find Web3 Programs",
|
|
755
851
|
description: "Web3/blockchain/smart-contract program shortlist.",
|
|
756
852
|
inputSchema: {
|
|
757
|
-
platforms:
|
|
758
|
-
tags:
|
|
853
|
+
platforms: platformListSchema(),
|
|
854
|
+
tags: tagListSchema(),
|
|
759
855
|
updated_since: isoDateTimeSchema.optional(),
|
|
760
856
|
max_public_report_count: z.number().int().min(0).optional(),
|
|
761
857
|
require_known_report_count: z.boolean().default(false),
|
|
@@ -771,7 +867,7 @@ export function createBbradarServer(client, config) {
|
|
|
771
867
|
target_sample_size: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(5),
|
|
772
868
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
773
869
|
},
|
|
774
|
-
outputSchema: findProgramsOutputShape,
|
|
870
|
+
outputSchema: findProgramsOutputShape(),
|
|
775
871
|
annotations: readOnlyAnnotations
|
|
776
872
|
}, (args) => runTool("find_web3_programs", config, rateLimiter, async () => findPrograms(client, config, {
|
|
777
873
|
platforms: args.platforms,
|
|
@@ -815,10 +911,10 @@ export function createBbradarServer(client, config) {
|
|
|
815
911
|
title: "Find Reward Programs",
|
|
816
912
|
description: "Programs matching reward thresholds.",
|
|
817
913
|
inputSchema: {
|
|
818
|
-
platforms:
|
|
819
|
-
tags:
|
|
820
|
-
target_types:
|
|
821
|
-
language_tags:
|
|
914
|
+
platforms: platformListSchema(),
|
|
915
|
+
tags: tagListSchema(),
|
|
916
|
+
target_types: targetTypeListSchema(),
|
|
917
|
+
language_tags: languageTagListSchema(),
|
|
822
918
|
min_reward: z.number().int().min(0).default(5_000),
|
|
823
919
|
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
824
920
|
web3: z.boolean().default(false),
|
|
@@ -837,15 +933,15 @@ export function createBbradarServer(client, config) {
|
|
|
837
933
|
output_mode: programListModeSchema.default("compact"),
|
|
838
934
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
839
935
|
},
|
|
840
|
-
outputSchema: findProgramsOutputShape,
|
|
936
|
+
outputSchema: findProgramsOutputShape(),
|
|
841
937
|
annotations: readOnlyAnnotations
|
|
842
938
|
}, (args) => runTool("find_reward_programs", config, rateLimiter, async () => findRewardPrograms(client, config, args)));
|
|
843
939
|
server.registerTool("find_web3_contests", {
|
|
844
940
|
title: "Find Web3 Contests",
|
|
845
941
|
description: "Web3 contest or competitive audit shortlist.",
|
|
846
942
|
inputSchema: {
|
|
847
|
-
platforms:
|
|
848
|
-
tags:
|
|
943
|
+
platforms: platformListSchema(),
|
|
944
|
+
tags: tagListSchema(),
|
|
849
945
|
min_reward: z.number().int().min(0).optional(),
|
|
850
946
|
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
851
947
|
require_contest_signal: z.boolean().default(true),
|
|
@@ -860,17 +956,17 @@ export function createBbradarServer(client, config) {
|
|
|
860
956
|
output_mode: programListModeSchema.default("compact"),
|
|
861
957
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(30)
|
|
862
958
|
},
|
|
863
|
-
outputSchema: findProgramsOutputShape,
|
|
959
|
+
outputSchema: findProgramsOutputShape(),
|
|
864
960
|
annotations: readOnlyAnnotations
|
|
865
961
|
}, (args) => runTool("find_web3_contests", config, rateLimiter, async () => findWeb3Contests(client, config, args)));
|
|
866
962
|
server.registerTool("find_language_programs", {
|
|
867
963
|
title: "Find Language Programs",
|
|
868
964
|
description: "Language-specific program shortlist.",
|
|
869
965
|
inputSchema: {
|
|
870
|
-
platforms:
|
|
871
|
-
tags:
|
|
872
|
-
language_tags: z.array(filterValueSchema).min(1).max(50),
|
|
873
|
-
target_types:
|
|
966
|
+
platforms: platformListSchema(),
|
|
967
|
+
tags: tagListSchema(),
|
|
968
|
+
language_tags: z.array(filterValueSchema()).min(1).max(50),
|
|
969
|
+
target_types: targetTypeListSchema(),
|
|
874
970
|
min_reward: z.number().int().min(0).optional(),
|
|
875
971
|
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
876
972
|
require_bounty: z.boolean().default(false),
|
|
@@ -887,18 +983,18 @@ export function createBbradarServer(client, config) {
|
|
|
887
983
|
output_mode: programListModeSchema.default("compact"),
|
|
888
984
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
889
985
|
},
|
|
890
|
-
outputSchema: findProgramsOutputShape,
|
|
986
|
+
outputSchema: findProgramsOutputShape(),
|
|
891
987
|
annotations: readOnlyAnnotations
|
|
892
988
|
}, (args) => runTool("find_language_programs", config, rateLimiter, async () => findLanguagePrograms(client, config, args)));
|
|
893
989
|
server.registerTool("find_target_type_programs", {
|
|
894
990
|
title: "Find Target Type Programs",
|
|
895
991
|
description: "Target-type program shortlist.",
|
|
896
992
|
inputSchema: {
|
|
897
|
-
platforms:
|
|
898
|
-
tags:
|
|
899
|
-
target_types: z.array(filterValueSchema).min(1).max(50),
|
|
900
|
-
scope_tags:
|
|
901
|
-
language_tags:
|
|
993
|
+
platforms: platformListSchema(),
|
|
994
|
+
tags: tagListSchema(),
|
|
995
|
+
target_types: z.array(filterValueSchema()).min(1).max(50),
|
|
996
|
+
scope_tags: scopeTagListSchema(),
|
|
997
|
+
language_tags: languageTagListSchema(),
|
|
902
998
|
min_reward: z.number().int().min(0).optional(),
|
|
903
999
|
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
904
1000
|
require_bounty: z.boolean().default(false),
|
|
@@ -913,17 +1009,17 @@ export function createBbradarServer(client, config) {
|
|
|
913
1009
|
output_mode: programListModeSchema.default("compact"),
|
|
914
1010
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
915
1011
|
},
|
|
916
|
-
outputSchema: findProgramsOutputShape,
|
|
1012
|
+
outputSchema: findProgramsOutputShape(),
|
|
917
1013
|
annotations: readOnlyAnnotations
|
|
918
1014
|
}, (args) => runTool("find_target_type_programs", config, rateLimiter, async () => findTargetTypePrograms(client, config, args)));
|
|
919
1015
|
server.registerTool("find_vdp_programs", {
|
|
920
1016
|
title: "Find VDP Programs",
|
|
921
1017
|
description: "VDP/disclosure-only/no-bounty program shortlist.",
|
|
922
1018
|
inputSchema: {
|
|
923
|
-
platforms:
|
|
924
|
-
tags:
|
|
925
|
-
target_types:
|
|
926
|
-
language_tags:
|
|
1019
|
+
platforms: platformListSchema(),
|
|
1020
|
+
tags: tagListSchema(),
|
|
1021
|
+
target_types: targetTypeListSchema(),
|
|
1022
|
+
language_tags: languageTagListSchema(),
|
|
927
1023
|
vdp_mode: z.enum(["only_likely", "only_known_no_bounty"]).default("only_likely"),
|
|
928
1024
|
max_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
929
1025
|
max_results: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(10),
|
|
@@ -933,17 +1029,17 @@ export function createBbradarServer(client, config) {
|
|
|
933
1029
|
output_mode: programListModeSchema.default("compact"),
|
|
934
1030
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
935
1031
|
},
|
|
936
|
-
outputSchema: findProgramsOutputShape,
|
|
1032
|
+
outputSchema: findProgramsOutputShape(),
|
|
937
1033
|
annotations: readOnlyAnnotations
|
|
938
1034
|
}, (args) => runTool("find_vdp_programs", config, rateLimiter, async () => findVdpPrograms(client, config, args)));
|
|
939
1035
|
server.registerTool("find_paid_programs", {
|
|
940
1036
|
title: "Find Paid Programs",
|
|
941
1037
|
description: "Paid bounty program shortlist.",
|
|
942
1038
|
inputSchema: {
|
|
943
|
-
platforms:
|
|
944
|
-
tags:
|
|
945
|
-
target_types:
|
|
946
|
-
language_tags:
|
|
1039
|
+
platforms: platformListSchema(),
|
|
1040
|
+
tags: tagListSchema(),
|
|
1041
|
+
target_types: targetTypeListSchema(),
|
|
1042
|
+
language_tags: languageTagListSchema(),
|
|
947
1043
|
min_reward: z.number().int().min(0).optional(),
|
|
948
1044
|
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
949
1045
|
min_eligible_targets: z.number().int().min(0).default(1),
|
|
@@ -956,18 +1052,18 @@ export function createBbradarServer(client, config) {
|
|
|
956
1052
|
output_mode: programListModeSchema.default("compact"),
|
|
957
1053
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
958
1054
|
},
|
|
959
|
-
outputSchema: findProgramsOutputShape,
|
|
1055
|
+
outputSchema: findProgramsOutputShape(),
|
|
960
1056
|
annotations: readOnlyAnnotations
|
|
961
1057
|
}, (args) => runTool("find_paid_programs", config, rateLimiter, async () => findPaidPrograms(client, config, args)));
|
|
962
1058
|
server.registerTool("find_stack_matches", {
|
|
963
1059
|
title: "Find Stack Matches",
|
|
964
1060
|
description: "Combined stack/language/type/reward shortlist.",
|
|
965
1061
|
inputSchema: {
|
|
966
|
-
platforms:
|
|
967
|
-
tags:
|
|
968
|
-
language_tags:
|
|
969
|
-
target_types:
|
|
970
|
-
scope_tags:
|
|
1062
|
+
platforms: platformListSchema(),
|
|
1063
|
+
tags: tagListSchema(),
|
|
1064
|
+
language_tags: languageTagListSchema(),
|
|
1065
|
+
target_types: targetTypeListSchema(),
|
|
1066
|
+
scope_tags: scopeTagListSchema(),
|
|
971
1067
|
min_reward: z.number().int().min(0).optional(),
|
|
972
1068
|
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
973
1069
|
web3: z.boolean().default(false),
|
|
@@ -986,17 +1082,17 @@ export function createBbradarServer(client, config) {
|
|
|
986
1082
|
output_mode: programListModeSchema.default("compact"),
|
|
987
1083
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(30)
|
|
988
1084
|
},
|
|
989
|
-
outputSchema: findProgramsOutputShape,
|
|
1085
|
+
outputSchema: findProgramsOutputShape(),
|
|
990
1086
|
annotations: readOnlyAnnotations
|
|
991
1087
|
}, (args) => runTool("find_stack_matches", config, rateLimiter, async () => findStackMatches(client, config, args)));
|
|
992
1088
|
server.registerTool("find_low_noise_programs", {
|
|
993
1089
|
title: "Find Low Noise Programs",
|
|
994
1090
|
description: "Low-public-report program shortlist.",
|
|
995
1091
|
inputSchema: {
|
|
996
|
-
platforms:
|
|
997
|
-
tags:
|
|
998
|
-
target_types:
|
|
999
|
-
language_tags:
|
|
1092
|
+
platforms: platformListSchema(),
|
|
1093
|
+
tags: tagListSchema(),
|
|
1094
|
+
target_types: targetTypeListSchema(),
|
|
1095
|
+
language_tags: languageTagListSchema(),
|
|
1000
1096
|
min_reward: z.number().int().min(0).optional(),
|
|
1001
1097
|
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
1002
1098
|
max_public_report_count: z.number().int().min(0).default(5),
|
|
@@ -1012,43 +1108,43 @@ export function createBbradarServer(client, config) {
|
|
|
1012
1108
|
output_mode: programListModeSchema.default("compact"),
|
|
1013
1109
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
1014
1110
|
},
|
|
1015
|
-
outputSchema: findProgramsOutputShape,
|
|
1111
|
+
outputSchema: findProgramsOutputShape(),
|
|
1016
1112
|
annotations: readOnlyAnnotations
|
|
1017
1113
|
}, (args) => runTool("find_low_noise_programs", config, rateLimiter, async () => findLowNoisePrograms(client, config, args)));
|
|
1018
1114
|
server.registerTool("find_recent_by_type", {
|
|
1019
1115
|
title: "Find Recent By Type",
|
|
1020
1116
|
description: "Recent changes by target type grouped by program.",
|
|
1021
1117
|
inputSchema: {
|
|
1022
|
-
target_type: filterValueSchema,
|
|
1118
|
+
target_type: filterValueSchema(),
|
|
1023
1119
|
change_type: z.enum(["added", "updated", "removed"]).default("added"),
|
|
1024
1120
|
include_removed: z.boolean().default(false),
|
|
1025
1121
|
include_out_of_scope: z.boolean().default(false),
|
|
1026
1122
|
include_ineligible: z.boolean().default(false),
|
|
1027
1123
|
search: searchTextSchema.optional(),
|
|
1028
|
-
platforms:
|
|
1029
|
-
tags:
|
|
1030
|
-
language_tags:
|
|
1124
|
+
platforms: platformListSchema(),
|
|
1125
|
+
tags: tagListSchema(),
|
|
1126
|
+
language_tags: languageTagListSchema(),
|
|
1031
1127
|
page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
|
|
1032
1128
|
max_programs: z.number().int().min(1).max(50).default(10),
|
|
1033
1129
|
sample_size: z.number().int().min(1).max(20).default(5),
|
|
1034
1130
|
target_list_mode: targetListModeSchema.default("identifiers")
|
|
1035
1131
|
},
|
|
1036
1132
|
outputSchema: {
|
|
1037
|
-
...apiEnvelopeOutputShape,
|
|
1038
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
1039
|
-
activity: z.array(jsonRecordSchema).optional(),
|
|
1040
|
-
meta: jsonRecordSchema.optional()
|
|
1133
|
+
...apiEnvelopeOutputShape(),
|
|
1134
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
1135
|
+
activity: z.array(jsonRecordSchema()).optional(),
|
|
1136
|
+
meta: jsonRecordSchema().optional()
|
|
1041
1137
|
},
|
|
1042
1138
|
annotations: readOnlyAnnotations
|
|
1043
1139
|
}, (args) => runTool("find_recent_by_type", config, rateLimiter, async () => findRecentByType(client, config, args)));
|
|
1044
1140
|
server.registerTool("find_programs_by_target", {
|
|
1045
1141
|
title: "Find Programs By Target",
|
|
1046
|
-
description: "Reverse lookup target/domain ownership.",
|
|
1142
|
+
description: "Reverse lookup target/domain ownership. Accepts domains, URLs, wildcards, target names, and partial target text.",
|
|
1047
1143
|
inputSchema: {
|
|
1048
|
-
target_query:
|
|
1049
|
-
match_mode: targetMatchModeSchema.default("
|
|
1050
|
-
platforms:
|
|
1051
|
-
tags:
|
|
1144
|
+
target_query: targetQuerySchema,
|
|
1145
|
+
match_mode: targetMatchModeSchema.default("smart"),
|
|
1146
|
+
platforms: platformListSchema(),
|
|
1147
|
+
tags: tagListSchema(),
|
|
1052
1148
|
use_api_search: z.boolean().default(true),
|
|
1053
1149
|
max_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(2),
|
|
1054
1150
|
max_results: z.number().int().min(1).max(25).default(10),
|
|
@@ -1059,33 +1155,33 @@ export function createBbradarServer(client, config) {
|
|
|
1059
1155
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(30)
|
|
1060
1156
|
},
|
|
1061
1157
|
outputSchema: {
|
|
1062
|
-
...findProgramsOutputShape,
|
|
1063
|
-
matches: z.array(jsonRecordSchema).optional()
|
|
1158
|
+
...findProgramsOutputShape(),
|
|
1159
|
+
matches: z.array(jsonRecordSchema()).optional()
|
|
1064
1160
|
},
|
|
1065
1161
|
annotations: readOnlyAnnotations
|
|
1066
1162
|
}, (args) => runTool("find_programs_by_target", config, rateLimiter, async () => findProgramsByTarget(client, config, args)));
|
|
1067
1163
|
server.registerTool("find_program_candidates", {
|
|
1068
1164
|
title: "Find BBRadar Program Candidates",
|
|
1069
|
-
description: "Default ranked
|
|
1165
|
+
description: "Start here for broad program discovery. Default ranked hunt-candidate preset with safe filters and compact output.",
|
|
1070
1166
|
inputSchema: programCandidatePresetInputSchema,
|
|
1071
|
-
outputSchema: findProgramsOutputShape,
|
|
1167
|
+
outputSchema: findProgramsOutputShape(),
|
|
1072
1168
|
annotations: readOnlyAnnotations
|
|
1073
1169
|
}, (args) => runTool("find_program_candidates", config, rateLimiter, async () => findProgramCandidates(client, config, args)));
|
|
1074
1170
|
server.registerTool("find_hunt_candidates", {
|
|
1075
1171
|
title: "Find BBRadar Hunt Candidates",
|
|
1076
|
-
description: "Legacy alias for find_program_candidates.",
|
|
1172
|
+
description: "Legacy alias for find_program_candidates. Prefer find_program_candidates for new calls.",
|
|
1077
1173
|
inputSchema: programCandidatePresetInputSchema,
|
|
1078
|
-
outputSchema: findProgramsOutputShape,
|
|
1174
|
+
outputSchema: findProgramsOutputShape(),
|
|
1079
1175
|
annotations: readOnlyAnnotations
|
|
1080
1176
|
}, (args) => runTool("find_hunt_candidates", config, rateLimiter, async () => findProgramCandidates(client, config, args)));
|
|
1081
1177
|
server.registerTool("find_fresh_hunt_candidates", {
|
|
1082
1178
|
title: "Find Fresh Hunt Candidates",
|
|
1083
1179
|
description: "Freshness-first hunt shortlist.",
|
|
1084
1180
|
inputSchema: {
|
|
1085
|
-
platforms:
|
|
1086
|
-
tags:
|
|
1087
|
-
target_types: z.array(filterValueSchema).max(50).default(["domain"]),
|
|
1088
|
-
language_tags:
|
|
1181
|
+
platforms: platformListSchema(),
|
|
1182
|
+
tags: tagListSchema(),
|
|
1183
|
+
target_types: z.array(filterValueSchema()).max(50).default(["domain"]),
|
|
1184
|
+
language_tags: languageTagListSchema(),
|
|
1089
1185
|
opportunity_levels: z.array(opportunityLevelSchema).max(4).default(["elite", "hot", "strong", "potential"]),
|
|
1090
1186
|
min_added_7d: z.number().int().min(0).default(1),
|
|
1091
1187
|
max_public_report_count: z.number().int().min(0).default(25),
|
|
@@ -1107,7 +1203,7 @@ export function createBbradarServer(client, config) {
|
|
|
1107
1203
|
output_mode: programListModeSchema.default("compact"),
|
|
1108
1204
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(30)
|
|
1109
1205
|
},
|
|
1110
|
-
outputSchema: findProgramsOutputShape,
|
|
1206
|
+
outputSchema: findProgramsOutputShape(),
|
|
1111
1207
|
annotations: readOnlyAnnotations
|
|
1112
1208
|
}, (args) => runTool("find_fresh_hunt_candidates", config, rateLimiter, async () => findPrograms(client, config, {
|
|
1113
1209
|
platforms: args.platforms,
|
|
@@ -1151,10 +1247,10 @@ export function createBbradarServer(client, config) {
|
|
|
1151
1247
|
title: "Find Recently Updated Programs",
|
|
1152
1248
|
description: "Recently updated program shortlist across all platforms by default. Platform filters are applied only when explicitly provided.",
|
|
1153
1249
|
inputSchema: {
|
|
1154
|
-
platforms:
|
|
1155
|
-
tags:
|
|
1156
|
-
target_types:
|
|
1157
|
-
language_tags:
|
|
1250
|
+
platforms: platformListSchema(),
|
|
1251
|
+
tags: tagListSchema(),
|
|
1252
|
+
target_types: targetTypeListSchema(),
|
|
1253
|
+
language_tags: languageTagListSchema(),
|
|
1158
1254
|
web3: z.boolean().default(false),
|
|
1159
1255
|
updated_since: isoDateTimeSchema.optional(),
|
|
1160
1256
|
require_bounty: z.boolean().default(false),
|
|
@@ -1169,35 +1265,50 @@ export function createBbradarServer(client, config) {
|
|
|
1169
1265
|
output_mode: programListModeSchema.default("compact"),
|
|
1170
1266
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
1171
1267
|
},
|
|
1172
|
-
outputSchema: findProgramsOutputShape,
|
|
1268
|
+
outputSchema: findProgramsOutputShape(),
|
|
1173
1269
|
annotations: readOnlyAnnotations
|
|
1174
1270
|
}, (args) => runTool("find_recently_updated_programs", config, rateLimiter, async () => findRecentlyUpdatedPrograms(client, config, args)));
|
|
1175
1271
|
server.registerTool("list_filters", {
|
|
1176
1272
|
title: "List BBRadar Filters",
|
|
1177
1273
|
description: "Discover observed filter values.",
|
|
1178
1274
|
inputSchema: {
|
|
1179
|
-
platforms:
|
|
1180
|
-
tags:
|
|
1275
|
+
platforms: platformListSchema(),
|
|
1276
|
+
tags: tagListSchema(),
|
|
1181
1277
|
max_pages: z.number().int().min(1).max(MAX_FILTER_DISCOVERY_PAGES).default(DEFAULT_FILTER_DISCOVERY_PAGES),
|
|
1182
1278
|
include_target_facets: z.boolean().default(true),
|
|
1183
1279
|
target_sample_programs: z.number().int().min(0).max(MAX_FILTER_TARGET_SAMPLE_PROGRAMS).default(DEFAULT_FILTER_TARGET_SAMPLE_PROGRAMS),
|
|
1184
1280
|
upstream_request_budget: z.number().int().min(1).max(MAX_FIND_UPSTREAM_REQUEST_BUDGET).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
1185
1281
|
},
|
|
1186
1282
|
outputSchema: {
|
|
1187
|
-
...apiEnvelopeOutputShape,
|
|
1188
|
-
filters: jsonRecordSchema.optional(),
|
|
1189
|
-
observed_from: jsonRecordSchema.optional(),
|
|
1190
|
-
upstream_budget: jsonRecordSchema.optional()
|
|
1283
|
+
...apiEnvelopeOutputShape(),
|
|
1284
|
+
filters: jsonRecordSchema().optional(),
|
|
1285
|
+
observed_from: jsonRecordSchema().optional(),
|
|
1286
|
+
upstream_budget: jsonRecordSchema().optional()
|
|
1191
1287
|
},
|
|
1192
1288
|
annotations: readOnlyAnnotations
|
|
1193
1289
|
}, (args) => runTool("list_filters", config, rateLimiter, async () => listFilters(client, args)));
|
|
1290
|
+
server.registerTool("get_bbradar_guide", {
|
|
1291
|
+
title: "Get BBRadar Tool Guide",
|
|
1292
|
+
description: "Local quick-start guide for choosing BBRadar tools. Call this when the user's request is ambiguous or when a smaller model may need a decision tree.",
|
|
1293
|
+
inputSchema: {
|
|
1294
|
+
topic: guideTopicSchema.describe("Guide focus area. Use start for the compact default decision tree.")
|
|
1295
|
+
},
|
|
1296
|
+
outputSchema: {
|
|
1297
|
+
...apiEnvelopeOutputShape(),
|
|
1298
|
+
guide: jsonRecordSchema().optional()
|
|
1299
|
+
},
|
|
1300
|
+
annotations: readOnlyAnnotations
|
|
1301
|
+
}, (args) => runTool("get_bbradar_guide", config, rateLimiter, async () => ({
|
|
1302
|
+
request_id: randomUUID(),
|
|
1303
|
+
guide: bbradarGuide(args.topic)
|
|
1304
|
+
})));
|
|
1194
1305
|
server.registerTool("get_mcp_status", {
|
|
1195
1306
|
title: "Get BBRadar MCP Status",
|
|
1196
1307
|
description: "Local MCP status and timing metrics.",
|
|
1197
1308
|
inputSchema: {},
|
|
1198
1309
|
outputSchema: {
|
|
1199
|
-
...apiEnvelopeOutputShape,
|
|
1200
|
-
status: jsonRecordSchema.optional()
|
|
1310
|
+
...apiEnvelopeOutputShape(),
|
|
1311
|
+
status: jsonRecordSchema().optional()
|
|
1201
1312
|
},
|
|
1202
1313
|
annotations: readOnlyAnnotations
|
|
1203
1314
|
}, () => runTool("get_mcp_status", config, rateLimiter, async () => ({
|
|
@@ -1226,41 +1337,41 @@ export function createBbradarServer(client, config) {
|
|
|
1226
1337
|
title: "Compare BBRadar Programs",
|
|
1227
1338
|
description: "Compare programs.",
|
|
1228
1339
|
inputSchema: {
|
|
1229
|
-
program_ids: z.array(
|
|
1340
|
+
program_ids: z.array(programIdentifierSchema).min(2).max(10),
|
|
1230
1341
|
include_target_samples: z.boolean().default(false),
|
|
1231
1342
|
target_sample_size: z.number().int().min(1).max(MAX_FIND_TARGET_SAMPLES).default(5),
|
|
1232
1343
|
only_in_scope_targets: z.boolean().default(true),
|
|
1233
1344
|
only_bounty_eligible_targets: z.boolean().default(true),
|
|
1234
1345
|
output_mode: programListModeSchema.default("compact")
|
|
1235
1346
|
},
|
|
1236
|
-
outputSchema: decisionOutputShape,
|
|
1347
|
+
outputSchema: decisionOutputShape(),
|
|
1237
1348
|
annotations: readOnlyAnnotations
|
|
1238
1349
|
}, (args) => runTool("compare_programs", config, rateLimiter, async () => comparePrograms(client, config, args)));
|
|
1239
1350
|
server.registerTool("compare_programs_compact", {
|
|
1240
1351
|
title: "Compare Programs Compact",
|
|
1241
1352
|
description: "Compact program comparison.",
|
|
1242
1353
|
inputSchema: {
|
|
1243
|
-
program_ids: z.array(
|
|
1354
|
+
program_ids: z.array(programIdentifierSchema).min(2).max(10),
|
|
1244
1355
|
include_target_samples: z.boolean().default(false),
|
|
1245
1356
|
target_sample_size: z.number().int().min(1).max(5).default(3),
|
|
1246
1357
|
only_in_scope_targets: z.boolean().default(true),
|
|
1247
1358
|
only_bounty_eligible_targets: z.boolean().default(true)
|
|
1248
1359
|
},
|
|
1249
|
-
outputSchema: decisionOutputShape,
|
|
1360
|
+
outputSchema: decisionOutputShape(),
|
|
1250
1361
|
annotations: readOnlyAnnotations
|
|
1251
1362
|
}, (args) => runTool("compare_programs_compact", config, rateLimiter, async () => comparePrograms(client, config, { ...args, output_mode: "compact" })));
|
|
1252
1363
|
server.registerTool("summarize_program_activity", {
|
|
1253
1364
|
title: "Summarize Program Activity",
|
|
1254
1365
|
description: "Summarize one program's activity.",
|
|
1255
1366
|
inputSchema: {
|
|
1256
|
-
program_id:
|
|
1367
|
+
program_id: programIdentifierSchema,
|
|
1257
1368
|
recent_changes_limit: z.number().int().min(0).max(MAX_RECENT_CHANGES).default(25)
|
|
1258
1369
|
},
|
|
1259
1370
|
outputSchema: {
|
|
1260
|
-
...apiEnvelopeOutputShape,
|
|
1261
|
-
program: programOutputSchema.optional(),
|
|
1262
|
-
activity: jsonRecordSchema.optional(),
|
|
1263
|
-
changes: z.array(jsonRecordSchema).optional()
|
|
1371
|
+
...apiEnvelopeOutputShape(),
|
|
1372
|
+
program: programOutputSchema().optional(),
|
|
1373
|
+
activity: jsonRecordSchema().optional(),
|
|
1374
|
+
changes: z.array(jsonRecordSchema()).optional()
|
|
1264
1375
|
},
|
|
1265
1376
|
annotations: readOnlyAnnotations
|
|
1266
1377
|
}, (args) => runTool("summarize_program_activity", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => summarizeProgramActivity(client, config, programId, args.recent_changes_limit))));
|
|
@@ -1268,7 +1379,7 @@ export function createBbradarServer(client, config) {
|
|
|
1268
1379
|
title: "Get Program Brief",
|
|
1269
1380
|
description: "Compact worth-hunting brief for one program.",
|
|
1270
1381
|
inputSchema: {
|
|
1271
|
-
program_id:
|
|
1382
|
+
program_id: programIdentifierSchema,
|
|
1272
1383
|
target_sample_size: z.number().int().min(1).max(50).default(10),
|
|
1273
1384
|
target_list_mode: targetListModeSchema.default("compact"),
|
|
1274
1385
|
include_recent_changes: z.boolean().default(true),
|
|
@@ -1277,13 +1388,13 @@ export function createBbradarServer(client, config) {
|
|
|
1277
1388
|
include_ineligible: z.boolean().default(false)
|
|
1278
1389
|
},
|
|
1279
1390
|
outputSchema: {
|
|
1280
|
-
...apiEnvelopeOutputShape,
|
|
1281
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
1282
|
-
program: programOutputSchema.optional(),
|
|
1283
|
-
brief: jsonRecordSchema.optional(),
|
|
1284
|
-
target_samples: jsonRecordSchema.optional(),
|
|
1285
|
-
recent_changes: z.array(jsonRecordSchema).optional(),
|
|
1286
|
-
meta: jsonRecordSchema.optional()
|
|
1391
|
+
...apiEnvelopeOutputShape(),
|
|
1392
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
1393
|
+
program: programOutputSchema().optional(),
|
|
1394
|
+
brief: jsonRecordSchema().optional(),
|
|
1395
|
+
target_samples: jsonRecordSchema().optional(),
|
|
1396
|
+
recent_changes: z.array(jsonRecordSchema()).optional(),
|
|
1397
|
+
meta: jsonRecordSchema().optional()
|
|
1287
1398
|
},
|
|
1288
1399
|
annotations: readOnlyAnnotations
|
|
1289
1400
|
}, (args) => runTool("get_program_brief", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => getProgramBrief(client, config, { ...args, program_id: programId }))));
|
|
@@ -1291,8 +1402,8 @@ export function createBbradarServer(client, config) {
|
|
|
1291
1402
|
title: "Find Recently Added Wildcards",
|
|
1292
1403
|
description: "Recently added wildcard programs.",
|
|
1293
1404
|
inputSchema: {
|
|
1294
|
-
platforms:
|
|
1295
|
-
tags:
|
|
1405
|
+
platforms: platformListSchema(),
|
|
1406
|
+
tags: tagListSchema(),
|
|
1296
1407
|
min_added_7d: z.number().int().min(1).default(1),
|
|
1297
1408
|
max_public_report_count: z.number().int().min(0).optional(),
|
|
1298
1409
|
require_bounty: z.boolean().default(true),
|
|
@@ -1300,7 +1411,7 @@ export function createBbradarServer(client, config) {
|
|
|
1300
1411
|
max_results: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(10),
|
|
1301
1412
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
1302
1413
|
},
|
|
1303
|
-
outputSchema: findProgramsOutputShape,
|
|
1414
|
+
outputSchema: findProgramsOutputShape(),
|
|
1304
1415
|
annotations: readOnlyAnnotations
|
|
1305
1416
|
}, (args) => runTool("find_recently_added_wildcards", config, rateLimiter, async () => findPrograms(client, config, {
|
|
1306
1417
|
platforms: args.platforms,
|
|
@@ -1343,10 +1454,10 @@ export function createBbradarServer(client, config) {
|
|
|
1343
1454
|
title: "Find Low Competition High Reward",
|
|
1344
1455
|
description: "Legacy low-report high-reward shortlist.",
|
|
1345
1456
|
inputSchema: {
|
|
1346
|
-
platforms:
|
|
1347
|
-
tags:
|
|
1348
|
-
target_types:
|
|
1349
|
-
language_tags:
|
|
1457
|
+
platforms: platformListSchema(),
|
|
1458
|
+
tags: tagListSchema(),
|
|
1459
|
+
target_types: targetTypeListSchema(),
|
|
1460
|
+
language_tags: languageTagListSchema(),
|
|
1350
1461
|
max_public_report_count: z.number().int().min(0).default(10),
|
|
1351
1462
|
min_bounty_max: z.number().int().min(0).default(5_000),
|
|
1352
1463
|
min_eligible_targets: z.number().int().min(0).default(1),
|
|
@@ -1359,7 +1470,7 @@ export function createBbradarServer(client, config) {
|
|
|
1359
1470
|
max_results: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(10),
|
|
1360
1471
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
1361
1472
|
},
|
|
1362
|
-
outputSchema: findProgramsOutputShape,
|
|
1473
|
+
outputSchema: findProgramsOutputShape(),
|
|
1363
1474
|
annotations: readOnlyAnnotations
|
|
1364
1475
|
}, (args) => runTool("find_low_competition_high_reward", config, rateLimiter, async () => findPrograms(client, config, {
|
|
1365
1476
|
platforms: args.platforms,
|
|
@@ -1402,17 +1513,17 @@ export function createBbradarServer(client, config) {
|
|
|
1402
1513
|
title: "Get Program Delta",
|
|
1403
1514
|
description: "Recent target changes for one program.",
|
|
1404
1515
|
inputSchema: {
|
|
1405
|
-
program_id:
|
|
1516
|
+
program_id: programIdentifierSchema,
|
|
1406
1517
|
include_removed: z.boolean().default(true),
|
|
1407
1518
|
include_ineligible: z.boolean().default(false),
|
|
1408
1519
|
include_out_of_scope: z.boolean().default(false),
|
|
1409
1520
|
page_size: recentChangesPageSizeSchema.default(50)
|
|
1410
1521
|
},
|
|
1411
1522
|
outputSchema: {
|
|
1412
|
-
...apiEnvelopeOutputShape,
|
|
1413
|
-
program: programOutputSchema.optional(),
|
|
1414
|
-
changes: z.array(jsonRecordSchema).optional(),
|
|
1415
|
-
meta: jsonRecordSchema.optional()
|
|
1523
|
+
...apiEnvelopeOutputShape(),
|
|
1524
|
+
program: programOutputSchema().optional(),
|
|
1525
|
+
changes: z.array(jsonRecordSchema()).optional(),
|
|
1526
|
+
meta: jsonRecordSchema().optional()
|
|
1416
1527
|
},
|
|
1417
1528
|
annotations: readOnlyAnnotations
|
|
1418
1529
|
}, (args) => runTool("get_program_delta", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => getProgramDelta(client, config, { ...args, program_id: programId }))));
|
|
@@ -1420,9 +1531,9 @@ export function createBbradarServer(client, config) {
|
|
|
1420
1531
|
title: "Export BBRadar Targets",
|
|
1421
1532
|
description: `Read-only target export. limit max ${MAX_TARGET_EXPORT}.`,
|
|
1422
1533
|
inputSchema: {
|
|
1423
|
-
program_ids: z.array(
|
|
1424
|
-
platforms:
|
|
1425
|
-
tags:
|
|
1534
|
+
program_ids: z.array(programIdentifierSchema).max(50).default([]),
|
|
1535
|
+
platforms: platformListSchema(),
|
|
1536
|
+
tags: tagListSchema(),
|
|
1426
1537
|
opportunity_levels: z.array(z.enum(["elite", "hot", "strong", "potential"])).max(4).default([]),
|
|
1427
1538
|
include_out_of_scope: z.boolean().default(false),
|
|
1428
1539
|
include_ineligible: z.boolean().default(false),
|
|
@@ -1430,19 +1541,26 @@ export function createBbradarServer(client, config) {
|
|
|
1430
1541
|
limit: z.number().int().min(1).max(MAX_TARGET_EXPORT).default(MAX_TARGET_EXPORT)
|
|
1431
1542
|
},
|
|
1432
1543
|
outputSchema: {
|
|
1433
|
-
...apiEnvelopeOutputShape,
|
|
1544
|
+
...apiEnvelopeOutputShape(),
|
|
1434
1545
|
export: z.unknown().optional(),
|
|
1435
|
-
meta: jsonRecordSchema.optional()
|
|
1546
|
+
meta: jsonRecordSchema().optional()
|
|
1436
1547
|
},
|
|
1437
1548
|
annotations: readOnlyAnnotations
|
|
1438
1549
|
}, (args) => runTool("export_targets", config, rateLimiter, async () => {
|
|
1550
|
+
const resolvedProgramIds = await resolveProgramIdentifiers(client, config, args.program_ids);
|
|
1551
|
+
const exportArgs = {
|
|
1552
|
+
...args,
|
|
1553
|
+
program_ids: resolvedProgramIds.entries.map((entry) => entry.resolvedProgramId)
|
|
1554
|
+
};
|
|
1439
1555
|
try {
|
|
1440
|
-
const api = await client.exportTargets(compactRecord(
|
|
1556
|
+
const api = await client.exportTargets(compactRecord(exportArgs));
|
|
1441
1557
|
const exportId = randomUUID();
|
|
1442
1558
|
const sanitizedExport = sanitizeTargetsForExport(api.data, args.limit);
|
|
1443
1559
|
const resourceUri = exportResourceUri(exportId);
|
|
1444
1560
|
rememberExport(exportStore, exportId, sanitizedExport);
|
|
1445
1561
|
return withApiMetadata(api, {
|
|
1562
|
+
source_requests: resolvedProgramIds.sourceRequests.length > 0 ? resolvedProgramIds.sourceRequests : undefined,
|
|
1563
|
+
warnings: resolvedProgramIds.warnings.length > 0 ? resolvedProgramIds.warnings : undefined,
|
|
1446
1564
|
export: {
|
|
1447
1565
|
export_id: exportId,
|
|
1448
1566
|
resource_uri: resourceUri,
|
|
@@ -1456,13 +1574,168 @@ export function createBbradarServer(client, config) {
|
|
|
1456
1574
|
if (!(error instanceof BBRadarApiError) || (error.status !== 404 && error.status !== 405)) {
|
|
1457
1575
|
throw error;
|
|
1458
1576
|
}
|
|
1459
|
-
|
|
1577
|
+
const fallbackPayload = await exportTargetsFromProgramTargetsFallback(client, config, exportStore, exportArgs, error);
|
|
1578
|
+
const fallbackSourceRequests = readArray(fallbackPayload.source_requests).filter((request) => readObject(request) !== undefined);
|
|
1579
|
+
const fallbackWarnings = readArray(fallbackPayload.warnings).filter((warning) => typeof warning === "string");
|
|
1580
|
+
return stripUndefined({
|
|
1581
|
+
...fallbackPayload,
|
|
1582
|
+
source_requests: [...resolvedProgramIds.sourceRequests, ...fallbackSourceRequests],
|
|
1583
|
+
warnings: uniqueStrings([...resolvedProgramIds.warnings, ...fallbackWarnings])
|
|
1584
|
+
});
|
|
1460
1585
|
}
|
|
1461
1586
|
}));
|
|
1462
1587
|
registerPrompts(server);
|
|
1463
1588
|
registerResources(server, client, config, exportStore);
|
|
1464
1589
|
return server;
|
|
1465
1590
|
}
|
|
1591
|
+
function bbradarGuide(topic) {
|
|
1592
|
+
const primaryEntryPoints = [
|
|
1593
|
+
{
|
|
1594
|
+
tool: "find_program_candidates",
|
|
1595
|
+
use_when: "The user wants good programs to hunt, broad discovery, or does not name one exact program.",
|
|
1596
|
+
minimum_arguments: {},
|
|
1597
|
+
useful_arguments: ["platforms", "target_types", "language_tags", "min_bounty_max", "max_public_report_count", "fresh_launch_days", "has_api_scope"]
|
|
1598
|
+
},
|
|
1599
|
+
{
|
|
1600
|
+
tool: "run_program_name_action",
|
|
1601
|
+
use_when: "The user names one program and asks for a brief, targets, new targets, scope delta, or target breakdown.",
|
|
1602
|
+
minimum_arguments: { query: "program name or handle", action: "brief" },
|
|
1603
|
+
action_values: ["brief", "targets", "new_targets", "scope_delta", "target_breakdown"]
|
|
1604
|
+
},
|
|
1605
|
+
{
|
|
1606
|
+
tool: "get_program_eligible_targets_by_name",
|
|
1607
|
+
use_when: "The user names one program and asks for in-scope bounty-eligible targets.",
|
|
1608
|
+
minimum_arguments: { query: "program name or handle" },
|
|
1609
|
+
useful_arguments: ["target_list_mode", "limit", "strict_scope_filter"]
|
|
1610
|
+
},
|
|
1611
|
+
{
|
|
1612
|
+
tool: "get_bbradar_guide",
|
|
1613
|
+
use_when: "The tool choice is unclear.",
|
|
1614
|
+
minimum_arguments: { topic: "start" }
|
|
1615
|
+
}
|
|
1616
|
+
];
|
|
1617
|
+
const decisionTree = [
|
|
1618
|
+
{
|
|
1619
|
+
user_goal: "find programs to hunt",
|
|
1620
|
+
first_tool: "find_program_candidates",
|
|
1621
|
+
then: "Use get_program_brief on the best program_id if the user wants details."
|
|
1622
|
+
},
|
|
1623
|
+
{
|
|
1624
|
+
user_goal: "named program summary",
|
|
1625
|
+
first_tool: "run_program_name_action",
|
|
1626
|
+
arguments: { query: "program name", action: "brief" }
|
|
1627
|
+
},
|
|
1628
|
+
{
|
|
1629
|
+
user_goal: "named program targets",
|
|
1630
|
+
first_tool: "get_program_eligible_targets_by_name",
|
|
1631
|
+
arguments: { query: "program name", target_list_mode: "identifiers" }
|
|
1632
|
+
},
|
|
1633
|
+
{
|
|
1634
|
+
user_goal: "find which program owns a target",
|
|
1635
|
+
first_tool: "find_programs_by_target",
|
|
1636
|
+
arguments: { target_query: "domain, URL, wildcard, package/repo name, or short target phrase", match_mode: "smart" }
|
|
1637
|
+
},
|
|
1638
|
+
{
|
|
1639
|
+
user_goal: "fresh or recently changed scope",
|
|
1640
|
+
first_tool: "find_recently_updated_programs",
|
|
1641
|
+
then: "Use get_latest_added_targets for global added targets, or check_program_new_targets when the user names one program."
|
|
1642
|
+
},
|
|
1643
|
+
{
|
|
1644
|
+
user_goal: "compare a short list",
|
|
1645
|
+
first_tool: "compare_programs_compact",
|
|
1646
|
+
arguments: { program_ids: ["Platform:handle", "Platform:handle"] }
|
|
1647
|
+
},
|
|
1648
|
+
{
|
|
1649
|
+
user_goal: "export targets",
|
|
1650
|
+
first_tool: "export_targets",
|
|
1651
|
+
then: "Use the returned bbradar://exports resource_uri for the full local export payload."
|
|
1652
|
+
},
|
|
1653
|
+
{
|
|
1654
|
+
user_goal: "unknown filter values",
|
|
1655
|
+
first_tool: "list_filters",
|
|
1656
|
+
then: "Reuse observed platforms, scope tags, target types, and language tags in discovery tools."
|
|
1657
|
+
}
|
|
1658
|
+
];
|
|
1659
|
+
const advancedTools = {
|
|
1660
|
+
discovery: ["find_stack_matches", "find_language_programs", "find_target_type_programs", "find_reward_programs", "find_paid_programs", "find_web3_contests", "find_low_noise_programs"],
|
|
1661
|
+
freshness: ["find_recently_updated_programs", "find_fresh_hunt_candidates", "get_latest_added_targets", "get_recent_target_activity", "get_program_scope_delta", "check_watchlist_new_targets"],
|
|
1662
|
+
details: ["get_program_brief", "get_program_scope_summary", "get_program_target_breakdown", "find_programs_by_target"],
|
|
1663
|
+
exports: ["export_targets"]
|
|
1664
|
+
};
|
|
1665
|
+
const filters = {
|
|
1666
|
+
platforms: "Only set when the user explicitly names a platform. Do not infer Immunefi, HackerOne, etc.",
|
|
1667
|
+
tags: "Broad combined tag filter across scope, language, and target-type signals.",
|
|
1668
|
+
scope_tags: "Scope category signals such as domain, api, mobile, smart-contract, wildcard.",
|
|
1669
|
+
target_types: "Target-kind signals such as domain, api, url, mobile, smart-contract.",
|
|
1670
|
+
language_tags: "Technology/language signals such as javascript, solidity, web3, php, python.",
|
|
1671
|
+
vdp_mode: "Default exclude. Include VDP/no-bounty data only when asked."
|
|
1672
|
+
};
|
|
1673
|
+
const examples = [
|
|
1674
|
+
{
|
|
1675
|
+
user_says: "best low-noise API programs",
|
|
1676
|
+
call: { tool: "find_program_candidates", arguments: { has_api_scope: true, max_public_report_count: 5, target_types: ["api"] } }
|
|
1677
|
+
},
|
|
1678
|
+
{
|
|
1679
|
+
user_says: "targets for Nado",
|
|
1680
|
+
call: { tool: "get_program_eligible_targets_by_name", arguments: { query: "Nado", target_list_mode: "identifiers" } }
|
|
1681
|
+
},
|
|
1682
|
+
{
|
|
1683
|
+
user_says: "what changed on Aztec",
|
|
1684
|
+
call: { tool: "run_program_name_action", arguments: { query: "Aztec", action: "scope_delta" } }
|
|
1685
|
+
},
|
|
1686
|
+
{
|
|
1687
|
+
user_says: "fresh solidity programs",
|
|
1688
|
+
call: { tool: "find_fresh_hunt_candidates", arguments: { language_tags: ["solidity"], target_types: ["smart-contract"] } }
|
|
1689
|
+
},
|
|
1690
|
+
{
|
|
1691
|
+
user_says: "who owns https://api.example.com/v1/users",
|
|
1692
|
+
call: { tool: "find_programs_by_target", arguments: { target_query: "https://api.example.com/v1/users" } }
|
|
1693
|
+
}
|
|
1694
|
+
];
|
|
1695
|
+
return {
|
|
1696
|
+
topic,
|
|
1697
|
+
rule_zero: "BBRadar is passive intelligence only. Never scan or contact targets.",
|
|
1698
|
+
defaults: {
|
|
1699
|
+
output_mode: "compact",
|
|
1700
|
+
target_list_mode: "identifiers",
|
|
1701
|
+
target_match_mode: "smart",
|
|
1702
|
+
include_out_of_scope: false,
|
|
1703
|
+
include_ineligible: false,
|
|
1704
|
+
require_bounty: true
|
|
1705
|
+
},
|
|
1706
|
+
primary_entry_points: topic === "start" ? primaryEntryPoints : primaryEntryPoints.filter((entry) => entry.tool !== "get_bbradar_guide"),
|
|
1707
|
+
decision_tree: decisionTree.filter((entry) => topic === "start" || guideEntryMatchesTopic(topic, entry)),
|
|
1708
|
+
filters,
|
|
1709
|
+
advanced_tools: advancedTools,
|
|
1710
|
+
examples
|
|
1711
|
+
};
|
|
1712
|
+
}
|
|
1713
|
+
function guideEntryMatchesTopic(topic, entry) {
|
|
1714
|
+
const goal = typeof entry.user_goal === "string" ? entry.user_goal : "";
|
|
1715
|
+
const firstTool = typeof entry.first_tool === "string" ? entry.first_tool : "";
|
|
1716
|
+
if (topic === "program") {
|
|
1717
|
+
return goal.includes("named program") || firstTool === "get_program_brief";
|
|
1718
|
+
}
|
|
1719
|
+
if (topic === "discovery") {
|
|
1720
|
+
return goal.includes("find programs");
|
|
1721
|
+
}
|
|
1722
|
+
if (topic === "freshness") {
|
|
1723
|
+
return goal.includes("fresh") || goal.includes("changed");
|
|
1724
|
+
}
|
|
1725
|
+
if (topic === "targets") {
|
|
1726
|
+
return goal.includes("target") || firstTool === "find_programs_by_target";
|
|
1727
|
+
}
|
|
1728
|
+
if (topic === "compare") {
|
|
1729
|
+
return goal.includes("compare");
|
|
1730
|
+
}
|
|
1731
|
+
if (topic === "export") {
|
|
1732
|
+
return goal.includes("export");
|
|
1733
|
+
}
|
|
1734
|
+
if (topic === "filters") {
|
|
1735
|
+
return goal.includes("filter");
|
|
1736
|
+
}
|
|
1737
|
+
return true;
|
|
1738
|
+
}
|
|
1466
1739
|
function registerResources(server, client, config, exportStore) {
|
|
1467
1740
|
server.registerResource("target-export", new ResourceTemplate("bbradar://exports{?export_id}", { list: undefined }), {
|
|
1468
1741
|
title: "BBRadar Target Export",
|
|
@@ -1568,6 +1841,20 @@ async function getProgramWithIdFallback(client, config, requestedProgramId) {
|
|
|
1568
1841
|
fallback: cachedFallback
|
|
1569
1842
|
};
|
|
1570
1843
|
}
|
|
1844
|
+
if (!isStrictProgramId(requestedProgramId)) {
|
|
1845
|
+
const fallback = await resolveProgramNameIdentifier(client, config, requestedProgramId, "compact");
|
|
1846
|
+
if (fallback) {
|
|
1847
|
+
rememberProgramFallback(config, fallback);
|
|
1848
|
+
return {
|
|
1849
|
+
api: await client.getProgram(fallback.resolvedProgramId),
|
|
1850
|
+
programId: fallback.resolvedProgramId,
|
|
1851
|
+
sourceRequests: fallback.sourceRequests,
|
|
1852
|
+
warnings: fallback.warnings,
|
|
1853
|
+
fallback
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
throw unresolvedProgramNameError(requestedProgramId);
|
|
1857
|
+
}
|
|
1571
1858
|
try {
|
|
1572
1859
|
return {
|
|
1573
1860
|
api: await client.getProgram(requestedProgramId),
|
|
@@ -1619,6 +1906,39 @@ async function runProgramIdTool(client, config, requestedProgramId, callback, in
|
|
|
1619
1906
|
return withProgramIndexFallbackMetadata(payload, indexFallback);
|
|
1620
1907
|
}
|
|
1621
1908
|
}
|
|
1909
|
+
if (!isStrictProgramId(requestedProgramId)) {
|
|
1910
|
+
const fallback = await resolveProgramNameIdentifier(client, config, requestedProgramId, indexFallbackCallback ? "full" : "compact");
|
|
1911
|
+
if (fallback) {
|
|
1912
|
+
try {
|
|
1913
|
+
rememberProgramFallback(config, fallback);
|
|
1914
|
+
const payload = await callback(fallback.resolvedProgramId);
|
|
1915
|
+
return withProgramIdFallbackMetadata(payload, fallback);
|
|
1916
|
+
}
|
|
1917
|
+
catch (fallbackError) {
|
|
1918
|
+
if (!isProgramNotFoundError(fallbackError) || !indexFallbackCallback) {
|
|
1919
|
+
throw fallbackError;
|
|
1920
|
+
}
|
|
1921
|
+
const indexFallback = createProgramIndexFallbackFromStaleResolution(fallback, requestedProgramId, fallbackError) ??
|
|
1922
|
+
(await resolveIndexedProgramId(client, config, fallback.resolvedProgramId, fallbackError));
|
|
1923
|
+
if (!indexFallback) {
|
|
1924
|
+
throw fallbackError;
|
|
1925
|
+
}
|
|
1926
|
+
const combinedFallback = indexFallback.requestedProgramId === requestedProgramId
|
|
1927
|
+
? indexFallback
|
|
1928
|
+
: {
|
|
1929
|
+
...indexFallback,
|
|
1930
|
+
requestedProgramId,
|
|
1931
|
+
reason: fallback.reason,
|
|
1932
|
+
sourceRequests: [...fallback.sourceRequests, ...indexFallback.sourceRequests],
|
|
1933
|
+
warnings: uniqueStrings([...fallback.warnings, ...indexFallback.warnings])
|
|
1934
|
+
};
|
|
1935
|
+
rememberProgramFallback(config, combinedFallback);
|
|
1936
|
+
const payload = await indexFallbackCallback(combinedFallback);
|
|
1937
|
+
return withProgramIndexFallbackMetadata(payload, combinedFallback);
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
throw unresolvedProgramNameError(requestedProgramId);
|
|
1941
|
+
}
|
|
1622
1942
|
try {
|
|
1623
1943
|
return await callback(requestedProgramId);
|
|
1624
1944
|
}
|
|
@@ -1776,6 +2096,7 @@ async function resolveIndexedProgramId(client, config, requestedProgramId, stale
|
|
|
1776
2096
|
requestedProgramId,
|
|
1777
2097
|
resolvedProgramId: match.resolvedProgramId,
|
|
1778
2098
|
query,
|
|
2099
|
+
reason: "stale_program_id",
|
|
1779
2100
|
staleRequestId: staleApiError?.requestId,
|
|
1780
2101
|
staleUpstreamRequestId: staleApiError?.upstreamRequestId,
|
|
1781
2102
|
sourceRequests,
|
|
@@ -1784,6 +2105,38 @@ async function resolveIndexedProgramId(client, config, requestedProgramId, stale
|
|
|
1784
2105
|
program: match.program
|
|
1785
2106
|
};
|
|
1786
2107
|
}
|
|
2108
|
+
async function resolveProgramNameIdentifier(client, config, requestedProgramName, outputMode) {
|
|
2109
|
+
const query = programSearchText(requestedProgramName);
|
|
2110
|
+
if (query.length < 2) {
|
|
2111
|
+
return undefined;
|
|
2112
|
+
}
|
|
2113
|
+
const resolution = await resolveProgram(client, config, {
|
|
2114
|
+
query,
|
|
2115
|
+
platforms: [],
|
|
2116
|
+
tags: [],
|
|
2117
|
+
opportunity_levels: [],
|
|
2118
|
+
max_pages: STALE_PROGRAM_ID_RESOLVE_PAGES,
|
|
2119
|
+
max_results: 5,
|
|
2120
|
+
output_mode: outputMode,
|
|
2121
|
+
upstream_request_budget: STALE_PROGRAM_ID_RESOLVE_BUDGET
|
|
2122
|
+
});
|
|
2123
|
+
const match = selectProgramIdFallbackMatch(requestedProgramName, resolution);
|
|
2124
|
+
if (!match) {
|
|
2125
|
+
return undefined;
|
|
2126
|
+
}
|
|
2127
|
+
const sourceRequests = readArray(resolution.source_requests).filter((request) => readObject(request) !== undefined);
|
|
2128
|
+
const warnings = readArray(resolution.warnings).filter((warning) => typeof warning === "string");
|
|
2129
|
+
const fallbackWarning = `Resolved program name ${requestedProgramName} to program_id ${match.resolvedProgramId}.`;
|
|
2130
|
+
return {
|
|
2131
|
+
requestedProgramId: requestedProgramName,
|
|
2132
|
+
resolvedProgramId: match.resolvedProgramId,
|
|
2133
|
+
query,
|
|
2134
|
+
reason: "program_name",
|
|
2135
|
+
sourceRequests,
|
|
2136
|
+
warnings: uniqueStrings([...warnings, fallbackWarning]),
|
|
2137
|
+
resolution
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
1787
2140
|
async function resolveStaleProgramId(client, config, requestedProgramId, staleError) {
|
|
1788
2141
|
const query = programSearchText(requestedProgramId);
|
|
1789
2142
|
if (query.length < 2) {
|
|
@@ -1811,6 +2164,7 @@ async function resolveStaleProgramId(client, config, requestedProgramId, staleEr
|
|
|
1811
2164
|
requestedProgramId,
|
|
1812
2165
|
resolvedProgramId: match.resolvedProgramId,
|
|
1813
2166
|
query,
|
|
2167
|
+
reason: "stale_program_id",
|
|
1814
2168
|
staleRequestId: staleApiError?.requestId,
|
|
1815
2169
|
staleUpstreamRequestId: staleApiError?.upstreamRequestId,
|
|
1816
2170
|
sourceRequests,
|
|
@@ -1818,6 +2172,32 @@ async function resolveStaleProgramId(client, config, requestedProgramId, staleEr
|
|
|
1818
2172
|
resolution
|
|
1819
2173
|
};
|
|
1820
2174
|
}
|
|
2175
|
+
async function resolveProgramIdentifiers(client, config, identifiers) {
|
|
2176
|
+
const entries = [];
|
|
2177
|
+
const sourceRequests = [];
|
|
2178
|
+
const warnings = [];
|
|
2179
|
+
for (const identifier of identifiers) {
|
|
2180
|
+
if (isStrictProgramId(identifier)) {
|
|
2181
|
+
entries.push({ requestedProgramId: identifier, resolvedProgramId: identifier, resolved: false });
|
|
2182
|
+
continue;
|
|
2183
|
+
}
|
|
2184
|
+
const fallback = await resolveProgramNameIdentifier(client, config, identifier, "compact");
|
|
2185
|
+
if (!fallback) {
|
|
2186
|
+
entries.push({ requestedProgramId: identifier, resolvedProgramId: identifier, resolved: false });
|
|
2187
|
+
warnings.push(`Could not resolve program name ${identifier}; used it as provided.`);
|
|
2188
|
+
continue;
|
|
2189
|
+
}
|
|
2190
|
+
entries.push({ requestedProgramId: identifier, resolvedProgramId: fallback.resolvedProgramId, resolved: true });
|
|
2191
|
+
sourceRequests.push(...fallback.sourceRequests);
|
|
2192
|
+
warnings.push(...fallback.warnings);
|
|
2193
|
+
rememberProgramFallback(config, fallback);
|
|
2194
|
+
}
|
|
2195
|
+
return {
|
|
2196
|
+
entries,
|
|
2197
|
+
sourceRequests,
|
|
2198
|
+
warnings: uniqueStrings(warnings)
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
1821
2201
|
function selectProgramIdFallbackMatch(requestedProgramId, resolution) {
|
|
1822
2202
|
const requestedHandle = normalizeTag(programHandleText(requestedProgramId));
|
|
1823
2203
|
const requestedSearchText = normalizeTag(programSearchText(requestedProgramId));
|
|
@@ -1829,6 +2209,7 @@ function selectProgramIdFallbackMatch(requestedProgramId, resolution) {
|
|
|
1829
2209
|
const program = readObject(entry.program);
|
|
1830
2210
|
const resolvedProgramId = stringField(entry, "program_id") ?? stringField(program, "id");
|
|
1831
2211
|
const candidateHandle = normalizeTag(stringField(program, "handle") ?? "");
|
|
2212
|
+
const candidateName = normalizeTag(stringField(program, "name") ?? "");
|
|
1832
2213
|
const candidateSearchText = resolvedProgramId ? normalizeTag(programSearchText(resolvedProgramId)) : "";
|
|
1833
2214
|
const candidatePlatform = normalizeTag(stringField(program, "platform") ?? (resolvedProgramId ? programPlatformText(resolvedProgramId) : ""));
|
|
1834
2215
|
const score = readNumber(entry.score) ?? 0;
|
|
@@ -1842,16 +2223,24 @@ function selectProgramIdFallbackMatch(requestedProgramId, resolution) {
|
|
|
1842
2223
|
candidateHandle.startsWith(`${requestedHandle}-`) ||
|
|
1843
2224
|
candidateSearchText.startsWith(`${requestedSearchText}_`) ||
|
|
1844
2225
|
candidateSearchText.startsWith(`${requestedSearchText}-`));
|
|
2226
|
+
const looseNameMatch = requestedPlatform.length === 0 &&
|
|
2227
|
+
score >= 85 &&
|
|
2228
|
+
(candidateHandle === requestedHandle ||
|
|
2229
|
+
candidateSearchText === requestedSearchText ||
|
|
2230
|
+
candidateName === requestedSearchText ||
|
|
2231
|
+
candidateName.includes(requestedSearchText) ||
|
|
2232
|
+
(candidateHandle.length > 0 && requestedSearchText.includes(candidateHandle)));
|
|
1845
2233
|
return {
|
|
1846
2234
|
resolvedProgramId,
|
|
1847
2235
|
score,
|
|
1848
2236
|
exactHandleMatch,
|
|
1849
|
-
partialHandleMatch
|
|
2237
|
+
partialHandleMatch,
|
|
2238
|
+
looseNameMatch
|
|
1850
2239
|
};
|
|
1851
2240
|
})
|
|
1852
2241
|
.filter((entry) => entry.resolvedProgramId !== undefined &&
|
|
1853
2242
|
normalizeTag(entry.resolvedProgramId) !== normalizeTag(requestedProgramId) &&
|
|
1854
|
-
(entry.exactHandleMatch || entry.partialHandleMatch) &&
|
|
2243
|
+
(entry.exactHandleMatch || entry.partialHandleMatch || entry.looseNameMatch) &&
|
|
1855
2244
|
entry.score >= 80)
|
|
1856
2245
|
.sort((left, right) => right.score - left.score || left.resolvedProgramId.localeCompare(right.resolvedProgramId));
|
|
1857
2246
|
if (candidates.length === 0) {
|
|
@@ -1876,6 +2265,7 @@ function selectProgramIndexFallbackMatch(requestedProgramId, resolution) {
|
|
|
1876
2265
|
const program = readObject(entry.program);
|
|
1877
2266
|
const resolvedProgramId = stringField(entry, "program_id") ?? stringField(program, "id");
|
|
1878
2267
|
const candidateHandle = normalizeTag(stringField(program, "handle") ?? (resolvedProgramId ? programHandleText(resolvedProgramId) : ""));
|
|
2268
|
+
const candidateName = normalizeTag(stringField(program, "name") ?? "");
|
|
1879
2269
|
const candidatePlatform = normalizeTag(stringField(program, "platform") ?? (resolvedProgramId ? programPlatformText(resolvedProgramId) : ""));
|
|
1880
2270
|
const score = readNumber(entry.score) ?? 0;
|
|
1881
2271
|
const idMatch = resolvedProgramId !== undefined && normalizeTag(resolvedProgramId) === requestedId;
|
|
@@ -1886,11 +2276,17 @@ function selectProgramIndexFallbackMatch(requestedProgramId, resolution) {
|
|
|
1886
2276
|
requestedPlatform.length > 0 &&
|
|
1887
2277
|
candidatePlatform === requestedPlatform &&
|
|
1888
2278
|
(candidateHandle.startsWith(`${requestedHandle}_`) || candidateHandle.startsWith(`${requestedHandle}-`));
|
|
2279
|
+
const looseNameMatch = requestedPlatform.length === 0 &&
|
|
2280
|
+
score >= 85 &&
|
|
2281
|
+
(candidateHandle === requestedHandle ||
|
|
2282
|
+
candidateName === requestedHandle ||
|
|
2283
|
+
candidateName.includes(requestedHandle) ||
|
|
2284
|
+
(candidateHandle.length > 0 && requestedHandle.includes(candidateHandle)));
|
|
1889
2285
|
return {
|
|
1890
2286
|
resolvedProgramId,
|
|
1891
2287
|
program,
|
|
1892
2288
|
score,
|
|
1893
|
-
exactMatch: idMatch || platformHandleMatch || partialPlatformHandleMatch
|
|
2289
|
+
exactMatch: idMatch || platformHandleMatch || partialPlatformHandleMatch || looseNameMatch
|
|
1894
2290
|
};
|
|
1895
2291
|
})
|
|
1896
2292
|
.filter((entry) => entry.resolvedProgramId !== undefined && entry.program !== undefined && entry.exactMatch && entry.score >= 80)
|
|
@@ -1914,8 +2310,8 @@ function withProgramIdFallbackMetadata(payload, fallback) {
|
|
|
1914
2310
|
requested_program_id: fallback.requestedProgramId,
|
|
1915
2311
|
resolved_program_id: fallback.resolvedProgramId,
|
|
1916
2312
|
query: fallback.query,
|
|
1917
|
-
reason: "stale_program_id",
|
|
1918
|
-
stale_status: 404,
|
|
2313
|
+
reason: fallback.reason ?? "stale_program_id",
|
|
2314
|
+
stale_status: fallback.staleRequestId || fallback.staleUpstreamRequestId ? 404 : undefined,
|
|
1919
2315
|
stale_request_id: fallback.staleRequestId,
|
|
1920
2316
|
stale_upstream_request_id: fallback.staleUpstreamRequestId,
|
|
1921
2317
|
resolution_request_id: stringField(fallback.resolution, "request_id")
|
|
@@ -2799,7 +3195,7 @@ async function searchProgramsByTargetApi(client, config, input, warnings) {
|
|
|
2799
3195
|
try {
|
|
2800
3196
|
api = await client.searchTargets({
|
|
2801
3197
|
q: input.target_query,
|
|
2802
|
-
match_mode: input.match_mode,
|
|
3198
|
+
match_mode: upstreamTargetMatchMode(input.match_mode),
|
|
2803
3199
|
platforms: input.platforms,
|
|
2804
3200
|
tags: input.tags,
|
|
2805
3201
|
include_out_of_scope: input.include_out_of_scope,
|
|
@@ -2845,12 +3241,17 @@ async function searchProgramsByTargetApi(client, config, input, warnings) {
|
|
|
2845
3241
|
matching_targets: formatTargetList(entry.targets.slice(0, input.max_targets_per_program), input.target_list_mode),
|
|
2846
3242
|
matching_targets_has_more: entry.targets.length > input.max_targets_per_program
|
|
2847
3243
|
}));
|
|
3244
|
+
if (matches.length === 0 && input.match_mode === "smart") {
|
|
3245
|
+
warnings.push("Direct target search returned no smart matches; fell back to sampled program target lists.");
|
|
3246
|
+
return undefined;
|
|
3247
|
+
}
|
|
2848
3248
|
const sanitizedMeta = readObject(sanitizeJson(data?.meta));
|
|
2849
3249
|
return stripUndefined({
|
|
2850
3250
|
request_id: randomUUID(),
|
|
2851
3251
|
source_requests: [
|
|
2852
3252
|
{
|
|
2853
3253
|
source: "target_search",
|
|
3254
|
+
api_match_mode: upstreamTargetMatchMode(input.match_mode),
|
|
2854
3255
|
request_id: api.requestId,
|
|
2855
3256
|
upstream_request_id: api.upstreamRequestId,
|
|
2856
3257
|
...apiSourceMetadata(api)
|
|
@@ -4064,7 +4465,8 @@ async function checkProgramNewTargetsFromIndexedProgram(client, config, input, i
|
|
|
4064
4465
|
});
|
|
4065
4466
|
}
|
|
4066
4467
|
async function checkWatchlistNewTargets(client, config, input) {
|
|
4067
|
-
const
|
|
4468
|
+
const resolvedProgramIds = await resolveProgramIdentifiers(client, config, input.program_ids);
|
|
4469
|
+
const watchlist = new Set(resolvedProgramIds.entries.map((entry) => entry.resolvedProgramId));
|
|
4068
4470
|
const api = await client.getRecentChanges({
|
|
4069
4471
|
change_type: "added",
|
|
4070
4472
|
include_removed: false,
|
|
@@ -4079,8 +4481,8 @@ async function checkWatchlistNewTargets(client, config, input) {
|
|
|
4079
4481
|
const sinceTimestamp = input.since ? Date.parse(input.since) : undefined;
|
|
4080
4482
|
const changes = readArray(data?.results).map((change) => sanitizeChange(change, config.webBaseUrl));
|
|
4081
4483
|
const grouped = new Map();
|
|
4082
|
-
for (const
|
|
4083
|
-
grouped.set(
|
|
4484
|
+
for (const entry of resolvedProgramIds.entries) {
|
|
4485
|
+
grouped.set(entry.resolvedProgramId, {
|
|
4084
4486
|
program: undefined,
|
|
4085
4487
|
changes: [],
|
|
4086
4488
|
targets: [],
|
|
@@ -4118,12 +4520,13 @@ async function checkWatchlistNewTargets(client, config, input) {
|
|
|
4118
4520
|
existing.latestAddedAt = stringField(change, "changed_at");
|
|
4119
4521
|
}
|
|
4120
4522
|
}
|
|
4121
|
-
const programs =
|
|
4122
|
-
const entry = grouped.get(
|
|
4523
|
+
const programs = resolvedProgramIds.entries.map((programEntry) => {
|
|
4524
|
+
const entry = grouped.get(programEntry.resolvedProgramId);
|
|
4123
4525
|
const targets = entry?.targets ?? [];
|
|
4124
4526
|
const limitedTargets = targets.slice(0, input.max_targets_per_program);
|
|
4125
4527
|
return stripUndefined({
|
|
4126
|
-
program_id:
|
|
4528
|
+
program_id: programEntry.resolvedProgramId,
|
|
4529
|
+
requested_program_id: programEntry.resolved ? programEntry.requestedProgramId : undefined,
|
|
4127
4530
|
program: entry?.program ? formatProgram(entry.program, "compact") : undefined,
|
|
4128
4531
|
has_new_targets: targets.length > 0,
|
|
4129
4532
|
new_target_count: targets.length,
|
|
@@ -4135,6 +4538,7 @@ async function checkWatchlistNewTargets(client, config, input) {
|
|
|
4135
4538
|
return stripUndefined({
|
|
4136
4539
|
request_id: randomUUID(),
|
|
4137
4540
|
source_requests: [
|
|
4541
|
+
...resolvedProgramIds.sourceRequests,
|
|
4138
4542
|
{
|
|
4139
4543
|
source: "recent_changes",
|
|
4140
4544
|
request_id: api.requestId,
|
|
@@ -4144,6 +4548,7 @@ async function checkWatchlistNewTargets(client, config, input) {
|
|
|
4144
4548
|
],
|
|
4145
4549
|
any_new_targets: programs.some((program) => program.has_new_targets === true),
|
|
4146
4550
|
programs,
|
|
4551
|
+
warnings: resolvedProgramIds.warnings.length > 0 ? resolvedProgramIds.warnings : undefined,
|
|
4147
4552
|
meta: stripUndefined({
|
|
4148
4553
|
recent_changes_scanned: changes.length,
|
|
4149
4554
|
watchlist_size: input.program_ids.length,
|
|
@@ -4274,7 +4679,7 @@ function addProgramResourceLinks(program) {
|
|
|
4274
4679
|
};
|
|
4275
4680
|
}
|
|
4276
4681
|
function parseProgramIdFromResourceUri(uri) {
|
|
4277
|
-
return
|
|
4682
|
+
return programIdentifierSchema.parse(uri.searchParams.get("program_id") ?? "");
|
|
4278
4683
|
}
|
|
4279
4684
|
function programResourceUri(programId) {
|
|
4280
4685
|
return `bbradar://program?program_id=${encodeURIComponent(programId)}`;
|
|
@@ -4858,7 +5263,7 @@ function programFetchError(programId, error) {
|
|
|
4858
5263
|
message: error.message,
|
|
4859
5264
|
detail: sanitizeJson(error.detail),
|
|
4860
5265
|
errors: sanitizeJson(error.errors),
|
|
4861
|
-
suggested_fix: "
|
|
5266
|
+
suggested_fix: "Use a BBRadar platform:handle program_id or call resolve_program with the human program name and retry with one returned program_id."
|
|
4862
5267
|
});
|
|
4863
5268
|
}
|
|
4864
5269
|
return {
|
|
@@ -4884,6 +5289,12 @@ function apiOperationError(operation, error) {
|
|
|
4884
5289
|
message: error instanceof Error ? error.message : String(error)
|
|
4885
5290
|
};
|
|
4886
5291
|
}
|
|
5292
|
+
function unresolvedProgramNameError(programName) {
|
|
5293
|
+
return new Error(`Could not resolve program name "${programName}" to one unambiguous BBRadar program_id. Call resolve_program with the name, then retry with a returned platform:handle program_id.`);
|
|
5294
|
+
}
|
|
5295
|
+
function isStrictProgramId(value) {
|
|
5296
|
+
return programIdSchema.safeParse(value).success;
|
|
5297
|
+
}
|
|
4887
5298
|
function programSearchText(programId) {
|
|
4888
5299
|
const colonIndex = programId.indexOf(":");
|
|
4889
5300
|
const handle = colonIndex >= 0 ? programId.slice(colonIndex + 1) : programId;
|
|
@@ -5613,7 +6024,7 @@ Find the best BBRadar program candidates.
|
|
|
5613
6024
|
title: "Summarize Program Scope",
|
|
5614
6025
|
description: "Summarize BBRadar target data for one program.",
|
|
5615
6026
|
argsSchema: {
|
|
5616
|
-
program_id:
|
|
6027
|
+
program_id: programIdentifierSchema
|
|
5617
6028
|
}
|
|
5618
6029
|
}, (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.
|
|
5619
6030
|
|
|
@@ -5628,7 +6039,7 @@ Summarize the program scope:
|
|
|
5628
6039
|
title: "Prepare Passive Recon Plan",
|
|
5629
6040
|
description: "Create a passive-only plan from BBRadar program and target data. Use only when planning is explicitly requested.",
|
|
5630
6041
|
argsSchema: {
|
|
5631
|
-
program_id:
|
|
6042
|
+
program_id: programIdentifierSchema.optional(),
|
|
5632
6043
|
focus: z.string().optional().describe("Optional focus area such as APIs, domains, mobile, or cloud.")
|
|
5633
6044
|
}
|
|
5634
6045
|
}, (args) => promptResult(`Prepare a passive-only plan from BBRadar data. Program id JSON string, if present: ${promptJson(args.program_id ?? "")}.
|
|
@@ -5871,32 +6282,150 @@ function targetMatchesRequestedType(target, targetType) {
|
|
|
5871
6282
|
return actualTargetType === normalizedTargetType || readStringArrayField(target, "scope_tags").map(normalizeTag).includes(normalizedTargetType);
|
|
5872
6283
|
}
|
|
5873
6284
|
function targetMatchesQuery(target, query, mode) {
|
|
5874
|
-
const
|
|
5875
|
-
if (
|
|
6285
|
+
const profile = targetSearchProfile(target);
|
|
6286
|
+
if (profile.values.length === 0) {
|
|
6287
|
+
return false;
|
|
6288
|
+
}
|
|
6289
|
+
const queryValues = targetQueryVariants(query);
|
|
6290
|
+
if (queryValues.length === 0) {
|
|
5876
6291
|
return false;
|
|
5877
6292
|
}
|
|
5878
|
-
const normalizedIdentifier = normalizeTargetIdentifierForMatch(identifier);
|
|
5879
|
-
const normalizedQuery = normalizeTargetIdentifierForMatch(query);
|
|
5880
6293
|
if (mode === "exact") {
|
|
5881
|
-
return
|
|
6294
|
+
return profile.values.some((value) => queryValues.some((queryValue) => value === queryValue));
|
|
5882
6295
|
}
|
|
5883
6296
|
if (mode === "suffix") {
|
|
5884
|
-
return (
|
|
5885
|
-
normalizedIdentifier.endsWith(`.${normalizedQuery}`) ||
|
|
5886
|
-
normalizedQuery.endsWith(`.${normalizedIdentifier}`));
|
|
6297
|
+
return profile.values.some((value) => queryValues.some((queryValue) => targetSuffixMatch(value, queryValue)));
|
|
5887
6298
|
}
|
|
5888
6299
|
if (mode === "wildcard") {
|
|
5889
|
-
return
|
|
6300
|
+
return profile.values.some((value) => queryValues.some((queryValue) => targetWildcardMatch(value, queryValue)));
|
|
6301
|
+
}
|
|
6302
|
+
const containsMatch = profile.values.some((value) => queryValues.some((queryValue) => value.includes(queryValue) || queryValue.includes(value)));
|
|
6303
|
+
if (mode === "contains") {
|
|
6304
|
+
return containsMatch;
|
|
6305
|
+
}
|
|
6306
|
+
return (containsMatch ||
|
|
6307
|
+
profile.values.some((value) => queryValues.some((queryValue) => targetSuffixMatch(value, queryValue) || targetWildcardMatch(value, queryValue))) ||
|
|
6308
|
+
targetTokensMatch(profile.tokens, query));
|
|
6309
|
+
}
|
|
6310
|
+
function upstreamTargetMatchMode(mode) {
|
|
6311
|
+
return mode === "smart" ? "contains" : mode;
|
|
6312
|
+
}
|
|
6313
|
+
function targetSearchProfile(target) {
|
|
6314
|
+
const scalarFields = [
|
|
6315
|
+
"identifier",
|
|
6316
|
+
"display_name",
|
|
6317
|
+
"normalized_identifier",
|
|
6318
|
+
"target_type",
|
|
6319
|
+
"name",
|
|
6320
|
+
"url",
|
|
6321
|
+
"uri",
|
|
6322
|
+
"link",
|
|
6323
|
+
"repository",
|
|
6324
|
+
"repo",
|
|
6325
|
+
"package",
|
|
6326
|
+
"package_name",
|
|
6327
|
+
"bundle_id",
|
|
6328
|
+
"app_id",
|
|
6329
|
+
"asset",
|
|
6330
|
+
"asset_identifier",
|
|
6331
|
+
"technology"
|
|
6332
|
+
];
|
|
6333
|
+
const rawValues = [
|
|
6334
|
+
...scalarFields.map((field) => stringField(target, field)),
|
|
6335
|
+
...readStringArrayField(target, "scope_tags"),
|
|
6336
|
+
...readStringArrayField(target, "language_tags"),
|
|
6337
|
+
...readStringArrayField(target, "tags")
|
|
6338
|
+
].filter((value) => value !== undefined);
|
|
6339
|
+
const values = uniqueStrings(rawValues.flatMap(targetStringVariants));
|
|
6340
|
+
return {
|
|
6341
|
+
values,
|
|
6342
|
+
tokens: targetTokenSet([...rawValues, ...values].join(" "))
|
|
6343
|
+
};
|
|
6344
|
+
}
|
|
6345
|
+
function targetQueryVariants(query) {
|
|
6346
|
+
return targetStringVariants(query);
|
|
6347
|
+
}
|
|
6348
|
+
function targetStringVariants(value) {
|
|
6349
|
+
const normalized = normalizeTargetIdentifierForMatch(value);
|
|
6350
|
+
if (!normalized) {
|
|
6351
|
+
return [];
|
|
5890
6352
|
}
|
|
5891
|
-
|
|
6353
|
+
const withoutWildcard = stripLeadingTargetWildcard(normalized);
|
|
6354
|
+
const host = targetHostVariant(normalized);
|
|
6355
|
+
const pathVariants = targetPathVariants(normalized);
|
|
6356
|
+
const tokenPhrase = [...targetTokenSet(normalized)].join(" ");
|
|
6357
|
+
return uniqueStrings([
|
|
6358
|
+
normalized,
|
|
6359
|
+
withoutWildcard,
|
|
6360
|
+
host,
|
|
6361
|
+
host ? stripLeadingTargetWildcard(host) : undefined,
|
|
6362
|
+
...pathVariants,
|
|
6363
|
+
tokenPhrase.length >= 2 ? tokenPhrase : undefined
|
|
6364
|
+
].filter((entry) => entry !== undefined && entry.length >= 2));
|
|
6365
|
+
}
|
|
6366
|
+
function targetSuffixMatch(value, query) {
|
|
6367
|
+
const normalizedValue = stripLeadingTargetWildcard(value);
|
|
6368
|
+
const normalizedQuery = stripLeadingTargetWildcard(query);
|
|
6369
|
+
return (normalizedValue === normalizedQuery ||
|
|
6370
|
+
normalizedValue.endsWith(`.${normalizedQuery}`) ||
|
|
6371
|
+
normalizedQuery.endsWith(`.${normalizedValue}`));
|
|
6372
|
+
}
|
|
6373
|
+
function targetWildcardMatch(value, query) {
|
|
6374
|
+
return wildcardTargetMatch(query, value) || wildcardTargetMatch(value, query);
|
|
6375
|
+
}
|
|
6376
|
+
function targetTokensMatch(targetTokens, query) {
|
|
6377
|
+
const queryTokens = [...targetTokenSet(query)];
|
|
6378
|
+
if (queryTokens.length === 0) {
|
|
6379
|
+
return false;
|
|
6380
|
+
}
|
|
6381
|
+
const overlap = queryTokens.filter((token) => targetTokens.has(token)).length;
|
|
6382
|
+
if (queryTokens.length <= 2) {
|
|
6383
|
+
return overlap === queryTokens.length;
|
|
6384
|
+
}
|
|
6385
|
+
return overlap >= Math.ceil(queryTokens.length * 0.67);
|
|
6386
|
+
}
|
|
6387
|
+
function targetTokenSet(value) {
|
|
6388
|
+
const tokens = [...tokenSet(normalizeTargetIdentifierForMatch(value))];
|
|
6389
|
+
const meaningfulTokens = tokens.filter((token) => !TARGET_QUERY_STOP_WORDS.has(token));
|
|
6390
|
+
return new Set(meaningfulTokens.length > 0 ? meaningfulTokens : tokens);
|
|
5892
6391
|
}
|
|
5893
6392
|
function normalizeTargetIdentifierForMatch(value) {
|
|
5894
|
-
|
|
6393
|
+
const normalized = value
|
|
5895
6394
|
.trim()
|
|
5896
6395
|
.toLowerCase()
|
|
5897
|
-
.replace(
|
|
5898
|
-
.replace(/^
|
|
5899
|
-
.replace(
|
|
6396
|
+
.replace(/\\/g, "/")
|
|
6397
|
+
.replace(/^['"`<]+|['"`>]+$/g, "")
|
|
6398
|
+
.replace(/^[a-z][a-z0-9+.-]*:\/\//, "")
|
|
6399
|
+
.replace(/^www\./, "");
|
|
6400
|
+
const queryStart = normalized.search(/[?#]/);
|
|
6401
|
+
return (queryStart >= 0 ? normalized.slice(0, queryStart) : normalized).replace(/\/+$/, "");
|
|
6402
|
+
}
|
|
6403
|
+
function stripLeadingTargetWildcard(value) {
|
|
6404
|
+
return value.replace(/^\*\./, "").replace(/^\*\/?/, "");
|
|
6405
|
+
}
|
|
6406
|
+
function targetHostVariant(value) {
|
|
6407
|
+
const withoutWildcard = stripLeadingTargetWildcard(value);
|
|
6408
|
+
const slashIndex = withoutWildcard.indexOf("/");
|
|
6409
|
+
if (slashIndex <= 0) {
|
|
6410
|
+
return undefined;
|
|
6411
|
+
}
|
|
6412
|
+
const host = withoutWildcard.slice(0, slashIndex).replace(/^www\./, "");
|
|
6413
|
+
return host.includes(".") || host === "localhost" ? host : undefined;
|
|
6414
|
+
}
|
|
6415
|
+
function targetPathVariants(value) {
|
|
6416
|
+
const withoutWildcard = stripLeadingTargetWildcard(value);
|
|
6417
|
+
const segments = withoutWildcard.split("/").filter(Boolean);
|
|
6418
|
+
if (segments.length <= 1) {
|
|
6419
|
+
return [];
|
|
6420
|
+
}
|
|
6421
|
+
const pathSegments = targetHostVariant(value) ? segments.slice(1) : segments;
|
|
6422
|
+
const lastSegment = pathSegments.at(-1);
|
|
6423
|
+
const lastWithoutExtension = lastSegment?.replace(/\.[a-z0-9]{1,12}$/i, "");
|
|
6424
|
+
return uniqueStrings([
|
|
6425
|
+
pathSegments.join("/"),
|
|
6426
|
+
lastSegment,
|
|
6427
|
+
lastWithoutExtension && lastWithoutExtension !== lastSegment ? lastWithoutExtension : undefined
|
|
6428
|
+
].filter((entry) => entry !== undefined && entry.length >= 2));
|
|
5900
6429
|
}
|
|
5901
6430
|
function wildcardTargetMatch(pattern, value) {
|
|
5902
6431
|
if (!pattern.includes("*")) {
|