@oss-scout/mcp 0.3.0 → 0.4.0

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.
@@ -35817,8 +35817,11 @@ var ScoutPreferencesSchema = external_exports3.object({
35817
35817
  maxIssueAgeDays: external_exports3.number().default(90),
35818
35818
  includeDocIssues: external_exports3.boolean().default(true),
35819
35819
  minRepoScoreThreshold: external_exports3.number().default(4),
35820
+ interPhaseDelayMs: external_exports3.number().min(0).max(12e4).default(3e4),
35820
35821
  persistence: PersistenceModeSchema.default("local"),
35821
- defaultStrategy: external_exports3.array(SearchStrategySchema).optional()
35822
+ defaultStrategy: external_exports3.array(SearchStrategySchema).optional(),
35823
+ broadPhaseDelayMs: external_exports3.number().min(0).max(3e5).default(9e4),
35824
+ skipBroadWhenSufficientResults: external_exports3.number().int().min(0).max(100).default(15)
35822
35825
  });
35823
35826
  var ScoutStateSchema = external_exports3.object({
35824
35827
  version: external_exports3.literal(1),
@@ -36833,7 +36836,6 @@ var IssueVetter = class {
36833
36836
  var MODULE8 = "search-phases";
36834
36837
  var GITHUB_MAX_BOOLEAN_OPS = 5;
36835
36838
  var INTER_QUERY_DELAY_MS = 2e3;
36836
- var BATCH_SIZE = 3;
36837
36839
  function chunkLabels(labels, reservedOps = 0) {
36838
36840
  const maxPerChunk = GITHUB_MAX_BOOLEAN_OPS - reservedOps + 1;
36839
36841
  if (maxPerChunk < 1) {
@@ -36879,26 +36881,138 @@ function interleaveArrays(arrays) {
36879
36881
  }
36880
36882
  return result;
36881
36883
  }
36882
- function batchRepos(repos, batchSize) {
36883
- const batches = [];
36884
- for (let i = 0; i < repos.length; i += batchSize) {
36885
- batches.push(repos.slice(i, i + batchSize));
36886
- }
36887
- return batches;
36888
- }
36889
36884
  var SEARCH_CACHE_TTL_MS = 15 * 60 * 1e3;
36890
36885
  async function cachedSearchIssues(octokit, params) {
36891
36886
  const cacheKey = `search:${params.q}:${params.sort}:${params.order}:${params.per_page}`;
36892
- return cachedTimeBased(getHttpCache(), cacheKey, SEARCH_CACHE_TTL_MS, async () => {
36893
- const tracker = getSearchBudgetTracker();
36894
- await tracker.waitForBudget();
36887
+ const cache = getHttpCache();
36888
+ const cached2 = cache.getIfFresh(cacheKey, SEARCH_CACHE_TTL_MS);
36889
+ if (cached2) {
36890
+ debug(MODULE8, `Search cache hit for query`);
36891
+ return cached2;
36892
+ }
36893
+ const tracker = getSearchBudgetTracker();
36894
+ await tracker.waitForBudget();
36895
+ let data;
36896
+ try {
36897
+ const response = await octokit.search.issuesAndPullRequests(params);
36898
+ data = response.data;
36899
+ } finally {
36900
+ tracker.recordCall();
36901
+ }
36902
+ if (data.items.length > 0) {
36903
+ cache.set(cacheKey, "", data);
36904
+ } else {
36905
+ debug(MODULE8, `Skipping cache for empty search result (possible rate limit artifact)`);
36906
+ }
36907
+ return data;
36908
+ }
36909
+ async function fetchIssuesFromMaintainedRepos(octokit, repos, minStars, maxResults) {
36910
+ const items = [];
36911
+ for (const repoFullName of repos) {
36912
+ if (items.length >= maxResults * 3)
36913
+ break;
36914
+ const [owner, repo] = repoFullName.split("/");
36915
+ if (!owner || !repo)
36916
+ continue;
36895
36917
  try {
36896
- const { data } = await octokit.search.issuesAndPullRequests(params);
36897
- return data;
36898
- } finally {
36899
- tracker.recordCall();
36918
+ const { data: repoData } = await octokit.repos.get({ owner, repo });
36919
+ if (!repoData.pushed_at)
36920
+ continue;
36921
+ const pushedAt = new Date(repoData.pushed_at);
36922
+ const thirtyDaysAgo = /* @__PURE__ */ new Date();
36923
+ thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
36924
+ if (pushedAt < thirtyDaysAgo)
36925
+ continue;
36926
+ if ((repoData.stargazers_count ?? 0) < minStars)
36927
+ continue;
36928
+ if (repoData.archived)
36929
+ continue;
36930
+ const { data: issues } = await octokit.issues.listForRepo({
36931
+ owner,
36932
+ repo,
36933
+ state: "open",
36934
+ sort: "created",
36935
+ direction: "desc",
36936
+ per_page: 5
36937
+ });
36938
+ const realIssues = issues.filter((i) => !i.pull_request && !i.assignee);
36939
+ for (const issue2 of realIssues) {
36940
+ items.push({
36941
+ html_url: issue2.html_url,
36942
+ repository_url: `https://api.github.com/repos/${repoFullName}`,
36943
+ updated_at: issue2.updated_at ?? "",
36944
+ title: issue2.title,
36945
+ labels: issue2.labels
36946
+ });
36947
+ }
36948
+ await sleep(INTER_QUERY_DELAY_MS);
36949
+ } catch (error48) {
36950
+ if (getHttpStatusCode(error48) === 401)
36951
+ throw error48;
36952
+ if (isRateLimitError(error48)) {
36953
+ warn(MODULE8, `Rate limit hit fetching issues from ${repoFullName}:`, errorMessage(error48));
36954
+ break;
36955
+ }
36956
+ warn(MODULE8, `Error fetching issues from ${repoFullName}:`, errorMessage(error48));
36900
36957
  }
36901
- });
36958
+ }
36959
+ return items;
36960
+ }
36961
+ async function fetchIssuesFromKnownRepos(octokit, vetter, repos, labels, maxResults, priority, filterFn) {
36962
+ const candidates = [];
36963
+ let failedRepos = 0;
36964
+ let rateLimitFailures = 0;
36965
+ for (let i = 0; i < repos.length; i++) {
36966
+ if (candidates.length >= maxResults)
36967
+ break;
36968
+ if (i > 0)
36969
+ await sleep(INTER_QUERY_DELAY_MS);
36970
+ const repoFullName = repos[i];
36971
+ const [owner, repo] = repoFullName.split("/");
36972
+ try {
36973
+ const response = await octokit.issues.listForRepo({
36974
+ owner,
36975
+ repo,
36976
+ state: "open",
36977
+ sort: "created",
36978
+ direction: "desc",
36979
+ per_page: 5,
36980
+ ...labels.length > 0 ? { labels: labels.join(",") } : {}
36981
+ });
36982
+ const issuesOnly = response.data.filter((item) => !("pull_request" in item) && !item.assignee);
36983
+ const mapped = issuesOnly.map((issue2) => ({
36984
+ html_url: issue2.html_url,
36985
+ repository_url: `https://api.github.com/repos/${repoFullName}`,
36986
+ updated_at: issue2.updated_at ?? "",
36987
+ title: issue2.title,
36988
+ labels: issue2.labels
36989
+ }));
36990
+ if (mapped.length > 0) {
36991
+ const filtered = filterFn(mapped);
36992
+ if (filtered.length > 0) {
36993
+ const remainingNeeded = maxResults - candidates.length;
36994
+ const { candidates: vetted, rateLimitHit: vetRateLimitHit } = await vetter.vetIssuesParallel(filtered.slice(0, remainingNeeded * 2).map((item) => item.html_url), remainingNeeded, priority);
36995
+ candidates.push(...vetted);
36996
+ if (vetRateLimitHit)
36997
+ rateLimitFailures++;
36998
+ }
36999
+ }
37000
+ } catch (error48) {
37001
+ if (getHttpStatusCode(error48) === 401)
37002
+ throw error48;
37003
+ failedRepos++;
37004
+ if (isRateLimitError(error48)) {
37005
+ rateLimitFailures++;
37006
+ }
37007
+ warn(MODULE8, `Error fetching issues from ${repoFullName}:`, errorMessage(error48));
37008
+ }
37009
+ }
37010
+ const allReposFailed = failedRepos === repos.length && repos.length > 0;
37011
+ const rateLimitHit = rateLimitFailures > 0;
37012
+ if (allReposFailed) {
37013
+ warn(MODULE8, `All ${repos.length} repo(s) failed for ${priority} phase. This may indicate a systemic issue (rate limit, auth, network).`);
37014
+ }
37015
+ return { candidates, allReposFailed, rateLimitHit };
36902
37016
  }
36903
37017
  async function searchWithChunkedLabels(octokit, labels, reservedOps, buildQuery, perPage) {
36904
37018
  const labelChunks = chunkLabels(labels, reservedOps);
@@ -36954,50 +37068,9 @@ async function filterVetAndScore(vetter, items, filterIssues, excludedRepoSets,
36954
37068
  }
36955
37069
  return { candidates: starFiltered, allVetFailed, rateLimitHit };
36956
37070
  }
36957
- async function searchInRepos(octokit, vetter, repos, baseQualifiers, labels, maxResults, priority, filterFn) {
36958
- const candidates = [];
36959
- const batches = batchRepos(repos, BATCH_SIZE);
36960
- let failedBatches = 0;
36961
- let rateLimitFailures = 0;
36962
- for (let batchIdx = 0; batchIdx < batches.length; batchIdx++) {
36963
- const batch = batches[batchIdx];
36964
- if (candidates.length >= maxResults)
36965
- break;
36966
- if (batchIdx > 0)
36967
- await sleep(INTER_QUERY_DELAY_MS);
36968
- try {
36969
- const repoFilter = batch.map((r) => `repo:${r}`).join(" OR ");
36970
- const repoOps = batch.length - 1;
36971
- const perPage = Math.min(30, (maxResults - candidates.length) * 3);
36972
- const allItems = await searchWithChunkedLabels(octokit, labels, repoOps, (labelQ) => `${baseQualifiers} ${labelQ} (${repoFilter})`.replace(/ +/g, " ").trim(), perPage);
36973
- if (allItems.length > 0) {
36974
- const filtered = filterFn(allItems);
36975
- const remainingNeeded = maxResults - candidates.length;
36976
- const { candidates: vetted, rateLimitHit: vetRateLimitHit } = await vetter.vetIssuesParallel(filtered.slice(0, remainingNeeded * 2).map((i) => i.html_url), remainingNeeded, priority);
36977
- candidates.push(...vetted);
36978
- if (vetRateLimitHit)
36979
- rateLimitFailures++;
36980
- }
36981
- } catch (error48) {
36982
- failedBatches++;
36983
- if (isRateLimitError(error48)) {
36984
- rateLimitFailures++;
36985
- }
36986
- const batchReposStr = batch.join(", ");
36987
- warn(MODULE8, `Error searching issues in batch [${batchReposStr}]:`, errorMessage(error48));
36988
- }
36989
- }
36990
- const allBatchesFailed = failedBatches === batches.length && batches.length > 0;
36991
- const rateLimitHit = rateLimitFailures > 0;
36992
- if (allBatchesFailed) {
36993
- warn(MODULE8, `All ${batches.length} batch(es) failed for ${priority} phase. This may indicate a systemic issue (rate limit, auth, network).`);
36994
- }
36995
- return { candidates, allBatchesFailed, rateLimitHit };
36996
- }
36997
37071
 
36998
37072
  // ../core/dist/core/issue-discovery.js
36999
37073
  var MODULE9 = "issue-discovery";
37000
- var INTER_PHASE_DELAY_MS = 2e3;
37001
37074
  var LOW_BUDGET_THRESHOLD = 20;
37002
37075
  var CRITICAL_BUDGET_THRESHOLD = 10;
37003
37076
  function buildIssueFilter(config2) {
@@ -37029,24 +37102,25 @@ function buildIssueFilter(config2) {
37029
37102
  });
37030
37103
  };
37031
37104
  }
37032
- async function runPhase0(octokit, vetter, repos, baseQualifiers, maxResults, filterIssues) {
37105
+ async function runPhase0(octokit, vetter, repos, maxResults, filterIssues) {
37033
37106
  info(MODULE9, `Phase 0: Searching issues in ${repos.length} merged-PR repos (no label filter)...`);
37034
- const { candidates, allBatchesFailed, rateLimitHit } = await searchInRepos(octokit, vetter, repos, baseQualifiers, [], maxResults, "merged_pr", filterIssues);
37107
+ const { candidates, allReposFailed, rateLimitHit } = await fetchIssuesFromKnownRepos(octokit, vetter, repos, [], maxResults, "merged_pr", filterIssues);
37035
37108
  info(MODULE9, `Found ${candidates.length} candidates from merged-PR repos`);
37036
37109
  return {
37037
37110
  candidates,
37038
- error: allBatchesFailed ? "All merged-PR repo batches failed" : null,
37111
+ error: allReposFailed ? "All merged-PR repo fetches failed" : null,
37039
37112
  rateLimitHit
37040
37113
  };
37041
37114
  }
37042
- async function runPhase1(octokit, vetter, repos, baseQualifiers, labels, maxResults, filterIssues) {
37115
+ async function runPhase1(octokit, vetter, repos, labels, maxResults, filterIssues) {
37043
37116
  info(MODULE9, `Phase 1: Searching issues in ${repos.length} starred repos...`);
37044
37117
  const phase1Labels = labels.slice(0, 3);
37045
- const { candidates, allBatchesFailed, rateLimitHit } = await searchInRepos(octokit, vetter, repos.slice(0, 10), baseQualifiers, phase1Labels, maxResults, "starred", filterIssues);
37118
+ const reposToSearch = repos.slice(0, 10);
37119
+ const { candidates, allReposFailed, rateLimitHit } = await fetchIssuesFromKnownRepos(octokit, vetter, reposToSearch, phase1Labels, maxResults, "starred", filterIssues);
37046
37120
  info(MODULE9, `Found ${candidates.length} candidates from starred repos`);
37047
37121
  return {
37048
37122
  candidates,
37049
- error: allBatchesFailed ? "All starred repo batches failed" : null,
37123
+ error: allReposFailed ? "All starred repo fetches failed" : null,
37050
37124
  rateLimitHit
37051
37125
  };
37052
37126
  }
@@ -37112,8 +37186,32 @@ async function runPhase2(octokit, vetter, scopes, labels, configLabels, baseQual
37112
37186
  rateLimitHit
37113
37187
  };
37114
37188
  }
37115
- async function runPhase3(octokit, vetter, langQuery, minStars, projectCategories, maxResults, phase0RepoSet, starredRepoSet, existingCandidates, filterIssues) {
37189
+ async function runPhase3(octokit, vetter, langQuery, minStars, projectCategories, maxResults, phase0RepoSet, starredRepoSet, starredRepos, existingCandidates, filterIssues) {
37116
37190
  info(MODULE9, "Phase 3: Searching actively maintained repos...");
37191
+ const seenRepos = new Set(existingCandidates.map((c) => c.issue.repo));
37192
+ const eligibleStarred = starredRepos.filter((r) => !phase0RepoSet.has(r) && !seenRepos.has(r));
37193
+ if (eligibleStarred.length > 0) {
37194
+ info(MODULE9, `Phase 3: Checking ${eligibleStarred.length} starred repos via REST API...`);
37195
+ const restItems = await fetchIssuesFromMaintainedRepos(octokit, eligibleStarred.slice(0, 15), minStars, maxResults);
37196
+ if (restItems.length > 0) {
37197
+ try {
37198
+ const { candidates, allVetFailed, rateLimitHit: vetRateLimitHit } = await filterVetAndScore(vetter, restItems, filterIssues, [phase0RepoSet, seenRepos], maxResults, minStars, "Phase 3 (REST)");
37199
+ if (candidates.length > 0) {
37200
+ info(MODULE9, `Found ${candidates.length} candidates from maintained-repo REST search`);
37201
+ return {
37202
+ candidates,
37203
+ error: allVetFailed ? "all vetting failed" : null,
37204
+ rateLimitHit: vetRateLimitHit
37205
+ };
37206
+ }
37207
+ } catch (error48) {
37208
+ if (getHttpStatusCode(error48) === 401)
37209
+ throw error48;
37210
+ warn(MODULE9, `Phase 3 REST vetting failed, falling back to Search API:`, errorMessage(error48));
37211
+ }
37212
+ }
37213
+ }
37214
+ info(MODULE9, "Phase 3: Falling back to Search API...");
37117
37215
  const thirtyDaysAgo = /* @__PURE__ */ new Date();
37118
37216
  thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
37119
37217
  const pushedSince = thirtyDaysAgo.toISOString().split("T")[0];
@@ -37128,7 +37226,6 @@ async function runPhase3(octokit, vetter, langQuery, minStars, projectCategories
37128
37226
  per_page: maxResults * 3
37129
37227
  });
37130
37228
  info(MODULE9, `Found ${data.total_count} issues in maintained-repo search, processing top ${data.items.length}...`);
37131
- const seenRepos = new Set(existingCandidates.map((c) => c.issue.repo));
37132
37229
  const { candidates, allVetFailed, rateLimitHit: vetRateLimitHit } = await filterVetAndScore(vetter, data.items, filterIssues, [phase0RepoSet, starredRepoSet, seenRepos], maxResults, minStars, "Phase 3");
37133
37230
  info(MODULE9, `Found ${candidates.length} candidates from maintained-repo search`);
37134
37231
  return {
@@ -37204,6 +37301,7 @@ var IssueDiscovery = class {
37204
37301
  const labels = options.labels || (scopes ? buildEffectiveLabels(scopes, config2.labels) : config2.labels);
37205
37302
  const maxResults = options.maxResults || 10;
37206
37303
  const minStars = config2.minStars ?? 50;
37304
+ const interPhaseDelay = config2.interPhaseDelayMs ?? 3e4;
37207
37305
  const ALL_STRATEGIES = CONCRETE_STRATEGIES;
37208
37306
  const rawStrategies = options.strategies ?? config2.defaultStrategy ?? ["all"];
37209
37307
  const enabledStrategies = new Set(rawStrategies.includes("all") ? ALL_STRATEGIES : rawStrategies);
@@ -37264,7 +37362,7 @@ var IssueDiscovery = class {
37264
37362
  if (phase0Repos.length > 0 && enabledStrategies.has("merged")) {
37265
37363
  const remaining = maxResults - allCandidates.length;
37266
37364
  if (remaining > 0) {
37267
- const result = await runPhase0(this.octokit, this.vetter, phase0Repos, baseQualifiers, remaining, filterIssues);
37365
+ const result = await runPhase0(this.octokit, this.vetter, phase0Repos, remaining, filterIssues);
37268
37366
  allCandidates.push(...result.candidates);
37269
37367
  phaseErrors["0"] = result.error;
37270
37368
  if (result.rateLimitHit)
@@ -37273,12 +37371,15 @@ var IssueDiscovery = class {
37273
37371
  strategiesUsed.push("merged");
37274
37372
  }
37275
37373
  if (allCandidates.length < maxResults && starredRepos.length > 0 && searchBudget >= CRITICAL_BUDGET_THRESHOLD && enabledStrategies.has("starred")) {
37276
- await sleep(INTER_PHASE_DELAY_MS);
37374
+ if (interPhaseDelay > 0) {
37375
+ info(MODULE9, `Waiting ${(interPhaseDelay / 1e3).toFixed(0)}s between phases for rate limit management...`);
37376
+ await sleep(interPhaseDelay);
37377
+ }
37277
37378
  const reposToSearch = starredRepos.filter((r) => !phase0RepoSet.has(r));
37278
37379
  if (reposToSearch.length > 0) {
37279
37380
  const remaining = maxResults - allCandidates.length;
37280
37381
  if (remaining > 0) {
37281
- const result = await runPhase1(this.octokit, this.vetter, reposToSearch, baseQualifiers, labels, remaining, filterIssues);
37382
+ const result = await runPhase1(this.octokit, this.vetter, reposToSearch, labels, remaining, filterIssues);
37282
37383
  allCandidates.push(...result.candidates);
37283
37384
  phaseErrors["1"] = result.error;
37284
37385
  if (result.rateLimitHit)
@@ -37287,20 +37388,38 @@ var IssueDiscovery = class {
37287
37388
  }
37288
37389
  strategiesUsed.push("starred");
37289
37390
  }
37391
+ const broadDelay = config2.broadPhaseDelayMs ?? 9e4;
37392
+ const skipThreshold = config2.skipBroadWhenSufficientResults ?? 15;
37290
37393
  if (allCandidates.length < maxResults && searchBudget >= LOW_BUDGET_THRESHOLD && enabledStrategies.has("broad")) {
37291
- await sleep(INTER_PHASE_DELAY_MS);
37292
- const remaining = maxResults - allCandidates.length;
37293
- const result = await runPhase2(this.octokit, this.vetter, scopes, labels, config2.labels, baseQualifiers, remaining, minStars, phase0RepoSet, starredRepoSet, allCandidates, filterIssues);
37294
- allCandidates.push(...result.candidates);
37295
- phaseErrors["2"] = result.error;
37296
- if (result.rateLimitHit)
37297
- rateLimitHitDuringSearch = true;
37394
+ if (skipThreshold > 0 && allCandidates.length >= skipThreshold) {
37395
+ info(MODULE9, `Skipping broad search: already found ${allCandidates.length} candidates (threshold: ${skipThreshold})`);
37396
+ } else {
37397
+ if (interPhaseDelay > 0) {
37398
+ info(MODULE9, `Waiting ${(interPhaseDelay / 1e3).toFixed(0)}s between phases for rate limit management...`);
37399
+ await sleep(interPhaseDelay);
37400
+ }
37401
+ if (allCandidates.length > 0 && broadDelay > 0) {
37402
+ info(MODULE9, `Waiting ${(broadDelay / 1e3).toFixed(0)}s for rate limit cooldown before broad search...`);
37403
+ await sleep(broadDelay);
37404
+ } else if (allCandidates.length === 0) {
37405
+ info(MODULE9, `Skipping broad phase delay: no results from previous phases, proceeding immediately`);
37406
+ }
37407
+ const remaining = maxResults - allCandidates.length;
37408
+ const result = await runPhase2(this.octokit, this.vetter, scopes, labels, config2.labels, baseQualifiers, remaining, minStars, phase0RepoSet, starredRepoSet, allCandidates, filterIssues);
37409
+ allCandidates.push(...result.candidates);
37410
+ phaseErrors["2"] = result.error;
37411
+ if (result.rateLimitHit)
37412
+ rateLimitHitDuringSearch = true;
37413
+ }
37298
37414
  strategiesUsed.push("broad");
37299
37415
  }
37300
37416
  if (allCandidates.length < maxResults && searchBudget >= LOW_BUDGET_THRESHOLD && enabledStrategies.has("maintained")) {
37301
- await sleep(INTER_PHASE_DELAY_MS);
37417
+ if (interPhaseDelay > 0) {
37418
+ info(MODULE9, `Waiting ${(interPhaseDelay / 1e3).toFixed(0)}s between phases for rate limit management...`);
37419
+ await sleep(interPhaseDelay);
37420
+ }
37302
37421
  const remaining = maxResults - allCandidates.length;
37303
- const result = await runPhase3(this.octokit, this.vetter, langQuery, minStars, config2.projectCategories ?? [], remaining, phase0RepoSet, starredRepoSet, allCandidates, filterIssues);
37422
+ const result = await runPhase3(this.octokit, this.vetter, langQuery, minStars, config2.projectCategories ?? [], remaining, phase0RepoSet, starredRepoSet, starredRepos, allCandidates, filterIssues);
37304
37423
  allCandidates.push(...result.candidates);
37305
37424
  phaseErrors["3"] = result.error;
37306
37425
  if (result.rateLimitHit)
@@ -38313,7 +38432,10 @@ function registerTools(server, scout) {
38313
38432
  const NUMBER_KEYS = /* @__PURE__ */ new Set([
38314
38433
  "minStars",
38315
38434
  "maxIssueAgeDays",
38316
- "minRepoScoreThreshold"
38435
+ "minRepoScoreThreshold",
38436
+ "interPhaseDelayMs",
38437
+ "broadPhaseDelayMs",
38438
+ "skipBroadWhenSufficientResults"
38317
38439
  ]);
38318
38440
  const BOOLEAN_KEYS = /* @__PURE__ */ new Set(["includeDocIssues"]);
38319
38441
  server.tool(
package/dist/tools.js CHANGED
@@ -181,6 +181,9 @@ export function registerTools(server, scout) {
181
181
  "minStars",
182
182
  "maxIssueAgeDays",
183
183
  "minRepoScoreThreshold",
184
+ "interPhaseDelayMs",
185
+ "broadPhaseDelayMs",
186
+ "skipBroadWhenSufficientResults",
184
187
  ]);
185
188
  const BOOLEAN_KEYS = new Set(["includeDocIssues"]);
186
189
  server.tool("config-set", "Update an oss-scout configuration preference", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oss-scout/mcp",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "MCP server for oss-scout — exposes issue discovery and vetting as MCP tools",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,7 +18,7 @@
18
18
  "dependencies": {
19
19
  "@modelcontextprotocol/sdk": "^1.27.1",
20
20
  "zod": "^4.3.6",
21
- "@oss-scout/core": "^0.3.0"
21
+ "@oss-scout/core": "^0.4.0"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/node": "^25.5.0",