Context
loopover_get_eligibility_plan (src/mcp/server.ts:2358-2367) derives a structured eligibility plan (is the branch/PR eligible now, public-safe blockers, cleanup paths) from a ScorePreviewResult via deriveEligibilityPlan (src/services/eligibility-plan.ts:109). Its handler (getEligibilityPlan, src/mcp/server.ts:3956-3972) fetches the exact same repo/snapshot/contributor-evidence data as previewScore/loopover_preview_local_pr_score, builds the same ScorePreviewResult via buildScorePreview(...), then applies deriveEligibilityPlan(preview) before returning.
This is structurally identical to how loopover_explain_score_breakdown is built: its handler also fetches the same repo/snapshot/evidence data, calls buildScorePreview(...), and then applies a second pure derivation (explainScoreBreakdown(preview)) on top. loopover_explain_score_breakdown has a full REST + CLI mirror pair:
POST /v1/scoring/explain-breakdown (src/api/routes.ts:2108-2124) — same input schema (scorePreviewSchema) as POST /v1/scoring/preview, same repo/snapshot/evidence fetch, then explainScoreBreakdown(preview) instead of makeScorePreviewRecord(...).
registerStdioTool("loopover_explain_score_breakdown", ...) in packages/loopover-mcp/bin/loopover-mcp.js:1512-1557, which assembles a scoring-preview request body from local branch/diff metadata and calls apiPost("/v1/scoring/explain-breakdown", body).
loopover_get_eligibility_plan has neither: there is no /v1/scoring/eligibility-plan (or equivalent) REST route, and no CLI mirror in packages/loopover-mcp/bin/loopover-mcp.js at all — grepping the CLI source for "eligibility" turns up only the unrelated loopover_post_eligibility_comment write-tool. A contributor using the local CLI currently cannot get an eligibility-plan verdict at all, despite already being able to fetch both the raw score preview (loopover_preview_local_pr_score → /v1/scoring/preview) and its breakdown explanation (loopover_explain_score_breakdown → /v1/scoring/explain-breakdown) through the CLI today.
Requirements
- Add
POST /v1/scoring/eligibility-plan to src/api/routes.ts, placed immediately after /v1/scoring/explain-breakdown (src/api/routes.ts:2108-2124) and structured identically to it: parse the body with the same scorePreviewSchema, call requireContributorAccess when contributorLogin is supplied (matching /v1/scoring/preview's own conditional check — loopover_get_eligibility_plan's own MCP handler only gates on contributorLogin being present, same as previewScore, not explainScoreBreakdown's unconditional-required version), fetch repo/snapshot/evidence/contributorIssues the same way, build scoreInput/preview via buildScorePreview(...), then return c.json(deriveEligibilityPlan(preview)). Reuse deriveEligibilityPlan from src/services/eligibility-plan.ts as-is; do not reimplement it in the route.
- In
packages/loopover-mcp/bin/loopover-mcp.js, add a registerStdioTool("loopover_get_eligibility_plan", ...) block immediately after the existing loopover_explain_score_breakdown block (packages/loopover-mcp/bin/loopover-mcp.js:1512-1558), reusing the exact same localScoreShape input schema and the exact same local-branch-metadata-to-request-body assembly logic loopover_explain_score_breakdown's handler already contains (workspace resolution, collectLocalDiff, buildBranchAnalysisPayload, estimated source lines) — the only difference should be the final call: apiPost("/v1/scoring/eligibility-plan", body) instead of apiPost("/v1/scoring/explain-breakdown", body). Do not duplicate that assembly logic by copy-pasting a second, drifting copy — factor the shared body-building into a helper both stdio tools call, OR (if a helper extraction is out of scope for this issue) copy the block verbatim and note in the PR description that a follow-up should de-duplicate it.
- Add a matching
STDIO_TOOL_DESCRIPTORS entry (category "discovery", matching loopover_get_eligibility_plan: "discovery" in the server's own MCP_TOOL_CATEGORIES).
Deliverables
Test Coverage Requirements
This touches src/api/routes.ts and packages/loopover-mcp/bin/loopover-mcp.js, both under src/**/packages/** — the repo's 99%+ Codecov patch gate applies in full. Every new branch (eligible/ineligible plans, contributorLogin present/absent) must be covered by new tests.
Expected Outcome
A contributor using the local loopover-mcp CLI can get an eligibility-plan verdict (eligible now / blockers / cleanup paths) the same way they can already get a raw score preview or a score breakdown explanation — closing the one tool in that trio that currently has no REST route or CLI mirror.
Links & Resources
src/mcp/server.ts:2358-2367 (existing MCP tool) and :3956-3972 (getEligibilityPlan handler, the exact logic to mirror server-side in the new route)
src/services/eligibility-plan.ts:109 (deriveEligibilityPlan, reuse as-is)
src/api/routes.ts:2086-2124 (/v1/scoring/preview and /v1/scoring/explain-breakdown, the two routes to model the new one on)
packages/loopover-mcp/bin/loopover-mcp.js:1503-1558 (loopover_explain_score_breakdown stdio tool, the CLI-mirror pattern — and its body-assembly logic — to copy)
test/unit/mcp-cli-plan-scorer-tools.test.ts (existing CLI-mirror test precedent)
test/unit/mcp-eligibility-plan.test.ts (existing server-side test to extend/mirror at the route level)
Context
loopover_get_eligibility_plan(src/mcp/server.ts:2358-2367) derives a structured eligibility plan (is the branch/PR eligible now, public-safe blockers, cleanup paths) from aScorePreviewResultviaderiveEligibilityPlan(src/services/eligibility-plan.ts:109). Its handler (getEligibilityPlan,src/mcp/server.ts:3956-3972) fetches the exact same repo/snapshot/contributor-evidence data aspreviewScore/loopover_preview_local_pr_score, builds the sameScorePreviewResultviabuildScorePreview(...), then appliesderiveEligibilityPlan(preview)before returning.This is structurally identical to how
loopover_explain_score_breakdownis built: its handler also fetches the same repo/snapshot/evidence data, callsbuildScorePreview(...), and then applies a second pure derivation (explainScoreBreakdown(preview)) on top.loopover_explain_score_breakdownhas a full REST + CLI mirror pair:POST /v1/scoring/explain-breakdown(src/api/routes.ts:2108-2124) — same input schema (scorePreviewSchema) asPOST /v1/scoring/preview, same repo/snapshot/evidence fetch, thenexplainScoreBreakdown(preview)instead ofmakeScorePreviewRecord(...).registerStdioTool("loopover_explain_score_breakdown", ...)inpackages/loopover-mcp/bin/loopover-mcp.js:1512-1557, which assembles a scoring-preview request body from local branch/diff metadata and callsapiPost("/v1/scoring/explain-breakdown", body).loopover_get_eligibility_planhas neither: there is no/v1/scoring/eligibility-plan(or equivalent) REST route, and no CLI mirror inpackages/loopover-mcp/bin/loopover-mcp.jsat all — grepping the CLI source for "eligibility" turns up only the unrelatedloopover_post_eligibility_commentwrite-tool. A contributor using the local CLI currently cannot get an eligibility-plan verdict at all, despite already being able to fetch both the raw score preview (loopover_preview_local_pr_score→/v1/scoring/preview) and its breakdown explanation (loopover_explain_score_breakdown→/v1/scoring/explain-breakdown) through the CLI today.Requirements
POST /v1/scoring/eligibility-plantosrc/api/routes.ts, placed immediately after/v1/scoring/explain-breakdown(src/api/routes.ts:2108-2124) and structured identically to it: parse the body with the samescorePreviewSchema, callrequireContributorAccesswhencontributorLoginis supplied (matching/v1/scoring/preview's own conditional check —loopover_get_eligibility_plan's own MCP handler only gates oncontributorLoginbeing present, same aspreviewScore, notexplainScoreBreakdown's unconditional-required version), fetchrepo/snapshot/evidence/contributorIssuesthe same way, buildscoreInput/previewviabuildScorePreview(...), then returnc.json(deriveEligibilityPlan(preview)). ReusederiveEligibilityPlanfromsrc/services/eligibility-plan.tsas-is; do not reimplement it in the route.packages/loopover-mcp/bin/loopover-mcp.js, add aregisterStdioTool("loopover_get_eligibility_plan", ...)block immediately after the existingloopover_explain_score_breakdownblock (packages/loopover-mcp/bin/loopover-mcp.js:1512-1558), reusing the exact samelocalScoreShapeinput schema and the exact same local-branch-metadata-to-request-body assembly logicloopover_explain_score_breakdown's handler already contains (workspace resolution,collectLocalDiff,buildBranchAnalysisPayload, estimated source lines) — the only difference should be the final call:apiPost("/v1/scoring/eligibility-plan", body)instead ofapiPost("/v1/scoring/explain-breakdown", body). Do not duplicate that assembly logic by copy-pasting a second, drifting copy — factor the shared body-building into a helper both stdio tools call, OR (if a helper extraction is out of scope for this issue) copy the block verbatim and note in the PR description that a follow-up should de-duplicate it.STDIO_TOOL_DESCRIPTORSentry (category"discovery", matchingloopover_get_eligibility_plan: "discovery"in the server's ownMCP_TOOL_CATEGORIES).Deliverables
POST /v1/scoring/eligibility-planroute added tosrc/api/routes.tsloopover_get_eligibility_planstdio tool registered inpackages/loopover-mcp/bin/loopover-mcp.js, with aSTDIO_TOOL_DESCRIPTORSentrycontributorLogin-optional path, following the existing/v1/scoring/explain-breakdownroute testsapiPostcall, followingtest/unit/mcp-cli-plan-scorer-tools.test.ts's existing coverage ofloopover_explain_score_breakdownTest Coverage Requirements
This touches
src/api/routes.tsandpackages/loopover-mcp/bin/loopover-mcp.js, both undersrc/**/packages/**— the repo's 99%+ Codecov patch gate applies in full. Every new branch (eligible/ineligible plans, contributorLogin present/absent) must be covered by new tests.Expected Outcome
A contributor using the local
loopover-mcpCLI can get an eligibility-plan verdict (eligible now / blockers / cleanup paths) the same way they can already get a raw score preview or a score breakdown explanation — closing the one tool in that trio that currently has no REST route or CLI mirror.Links & Resources
src/mcp/server.ts:2358-2367(existing MCP tool) and:3956-3972(getEligibilityPlanhandler, the exact logic to mirror server-side in the new route)src/services/eligibility-plan.ts:109(deriveEligibilityPlan, reuse as-is)src/api/routes.ts:2086-2124(/v1/scoring/previewand/v1/scoring/explain-breakdown, the two routes to model the new one on)packages/loopover-mcp/bin/loopover-mcp.js:1503-1558(loopover_explain_score_breakdownstdio tool, the CLI-mirror pattern — and its body-assembly logic — to copy)test/unit/mcp-cli-plan-scorer-tools.test.ts(existing CLI-mirror test precedent)test/unit/mcp-eligibility-plan.test.ts(existing server-side test to extend/mirror at the route level)