Skip to content

feat(mcp): add hosted opportunity finder - #2699

Closed
Helios531 wants to merge 1 commit into
JSONbored:mainfrom
Helios531:feat/mcp-find-opportunities-2308
Closed

feat(mcp): add hosted opportunity finder#2699
Helios531 wants to merge 1 commit into
JSONbored:mainfrom
Helios531:feat/mcp-find-opportunities-2308

Conversation

@Helios531

Copy link
Copy Markdown
Contributor

Summary

Adds gittensory_find_opportunities, a hosted MCP tool that ranks contributor opportunities from cached repo/issue metadata only — no source upload, no GitHub writes. Given targets (owner/repo) and/or a searchQuery, plus an optional miner goalSpec and limit, it scores open issues with the shared rankOpportunities engine and returns the top-ranked issues with their per-signal scores.

  • Inputs: targets (≤25 owner/repo), searchQuery (≤200 chars), goalSpec (MinerGoalSpec), limit (1–25, default 10). Requires at least one of targets or searchQuery.
  • Signals per issue: potential, feasibility, laneFit, freshness, dupRisk, ranked via @jsonbored/gittensory-engine.
  • Output: { source: "cached_metadata", searchedRepositories, candidateCount, opportunities[], warnings? }; each opportunity carries owner/repo/issueNumber/title/rankScore/laneFit/freshness/dupRisk/aiPolicyAllowed.
  • Safety built in: skips repos that aren't cached / registered / accessible to the caller (surfaced as warnings), drops claimed or duplicate issues (dupRisk >= 1), sanitizes titles via sanitizePublicComment, and returns an empty result when goalSpec.minerEnabled is false.

Reinstates the previously-removed gittensory_find_opportunities tool name with this metadata-only implementation.

Related issue

Closes: #2308

Change Type (select all that apply)

  • New feature (hosted MCP tool)
  • Tests
  • Bug fix
  • Refactor / cleanup
  • Docs

Real behavior proof

Driven end-to-end through a connected MCP test client (the tool itself is not mocked):

  • Ranking + filtering — seeds cached repo octo/demo with four issues; verifies it dedupes repeated targets, ranks the maintainer-authored bug above the question issue, drops the duplicate-labeled and already-claimed (Closes #3) issues, falls back to neutral freshness on an unparsable updated_at, and leaks no hotkey/coldkey/wallet/payout/reward.
  • Search + access controlsearchQuery matches only registered, accessible cached repos; excludes a private repo the caller can't read (with a warning), warns on unregistered/missing targets, applies issueDiscoveryPolicy: "discouraged"
    (laneFit 0.35), returns empty for minerEnabled:false, and errors targets_or_search_query_required when neither targets nor searchQuery is given.
  • Inventory + schema — the tool appears in the live MCP tool inventory and exposes the expected output-schema properties.

@Helios531
Helios531 requested a review from JSONbored as a code owner July 3, 2026 06:31
@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

⚠️ JUnit XML file not found

The CLI was unable to find any JUnit XML files to upload.
For more help, visit our troubleshooting guide.

@loopover-orb

loopover-orb Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Caution

🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥

🛑 Gittensory review result - reject/close recommended

Review updated: 2026-07-03 08:30:42 UTC

3 files · 1 AI reviewer · 1 blocker · readiness 76/100 · CI failing · blocked

🛑 Suggested Action - Reject/Close

  • AI reviewers agree on a likely critical defect: src/mcp/server.ts:2291 leaks inaccessible repository names through search warnings because `listRepositories()` adds registered repos before `canAccessRepo`, so a caller allowed only one repo can still receive a warning naming another private repo
  • filter unauthorized repos silently or return only an aggregate skipped count. — Resolve the flagged defect, or override if the AI reviewers are mistaken, then re-run the gate.

Review summary
This change wires a new hosted MCP opportunity finder and covers the happy paths for target lookup, search, disabled goals, schema discovery, and basic filtering. The overall structure is coherent, but the current implementation is not safe to ship because the search path exposes inaccessible repository names and the duplicate-risk heuristic turns unrelated open PR volume into a hard drop for every issue in a busy repo.

Blockers

  • src/mcp/server.ts:2291 leaks inaccessible repository names through search warnings because `listRepositories()` adds registered repos before `canAccessRepo`, so a caller allowed only one repo can still receive a warning naming another private repo; filter unauthorized repos silently or return only an aggregate skipped count.
  • src/mcp/server.ts:1139 computes `dupRisk` for unlinked issues as `openPullRequests.length / 10`, and src/mcp/server.ts:2305 drops every candidate with `dupRisk >= 1`, so any repository with 10 unrelated open PRs returns zero opportunities even when the issues are unclaimed.
Nits — 5 non-blocking
  • src/mcp/server.ts:2275 applies the 50-repository search cap before access filtering or any match work, which makes `searchQuery` results depend on repository listing order and can skip eligible accessible repositories without a warning.
  • src/mcp/server.ts:1118 accepts `wantedPaths`, `blockedPaths`, and `maxConcurrentClaims` in `goalSpec` but the finder never uses them, so either narrow the schema or document which goal fields affect this metadata-only tool.
  • src/mcp/server.ts:2301 calls `Date.now()` once per issue; capture it once before scanning so all candidates in one response are scored against the same timestamp.
  • src/mcp/server.ts:2312 reports `candidateCount` after rank-score filtering, not the number of metadata candidates considered, so the field name is easy for clients to misread.
  • In src/mcp/server.ts, run `canAccessRepo` before adding search-discovered repositories to the result set and replace per-repo denied warnings with a generic skipped count.

Why this is blocked

  • src/mcp/server.ts:2291 leaks inaccessible repository names through search warnings because `listRepositories()` adds registered repos before `canAccessRepo`, so a caller allowed only one repo can still receive a warning naming another private repo; filter unauthorized repos silently or return only an aggregate skipped count.

CI checks failing

  • validate
  • validate-code
Signal Result Evidence
Code review ❌ 1 blocker 1 reviewer
Linked issue ✅ Linked #3
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (draft PR; 1 linked issue).
Validation posture ❌ 5/25 Preflight is holding this PR: the review lane is unavailable, so it is not ready for automated review.
Contributor workload ✅ 10/10 Author activity: 131 registered-repo PR(s), 80 merged, 3 issue(s).
Contributor context ✅ Confirmed Gittensor contributor Helios531; Gittensor profile; 131 PR(s), 3 issue(s).
Gate result ❌ Blocking Repo-configured hard blocker found.
Review context
  • Author: Helios531
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository registration is not available in the local Gittensory cache.
  • Public profile languages: not available
  • Official Gittensor activity: 131 PR(s), 3 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Await review-lane availability.
  • Mark ready when done.
  • Refresh registry data or choose a registered active repo.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.

  • Re-run Gittensory review

@loopover-orb loopover-orb Bot added gittensor gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. labels Jul 3, 2026
Comment thread src/mcp/server.ts
continue;
}
if (!(await this.canAccessRepo(repository.fullName))) {
warnings.push(`Skipping ${repository.fullName}: caller cannot access cached repository metadata.`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Search mode leaks inaccessible cached repository names in warnings

When searchQuery is provided, the tool adds all registered repos to the scan set before access checks and then reveals their exact full names in warnings to unauthorized callers.

Move the access check before adding search-discovered repositories, or avoid naming inaccessible repos in warnings.

AI prompt
Check if this security scanner issue is valid. If so, understand the root cause and fix it. If appropriate, update or add tests. Keep the change focused and preserve intended behavior.

<file name="src/mcp/server.ts">
<violation number="1" location="src/mcp/server.ts:2294">
<priority>medium</priority>
<title>Search mode leaks inaccessible cached repository names in warnings</title>
<evidence>In the findOpportunities method, when a searchQuery is provided, the code fetches all registered repositories via listRepositories(this.env), adds them to repositoriesByName without verifying caller access, and later emits a warning containing the repository fullName when the caller lacks access: warnings.push(`Skipping ${repository.fullName}: caller cannot access cached repository metadata.`). This allows an unauthorized caller to probe for the existence and exact names of private or restricted cached repositories.</evidence>
<recommendation>Move the access check (this.canAccessRepo) before adding search-discovered repositories into the scan set, or change the warning to a generic count-based message that does not include repository names the caller is not authorized to see.</recommendation>
</violation>
</file>

@superagent-security superagent-security Bot added the pr:flagged PR flagged for review by security analysis. label Jul 3, 2026
@Helios531
Helios531 marked this pull request as draft July 3, 2026 07:19
@Helios531
Helios531 force-pushed the feat/mcp-find-opportunities-2308 branch from 16bb70b to 6a8a637 Compare July 3, 2026 08:31

@JSONbored JSONbored left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failing review, closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. pr:flagged PR flagged for review by security analysis.

Development

Successfully merging this pull request may close these issues.

feat(mcp): add gittensory_find_opportunities tool to the hosted /mcp Worker

2 participants