@@ -40,6 +40,7 @@ import {
4040 listBountiesByRepo ,
4141 getContributorEvidence ,
4242 getLatestRepoGithubTotalsSnapshot ,
43+ getLatestUpstreamRulesetSnapshot ,
4344 getInstallation ,
4445 getIssue ,
4546 getPendingAgentAction ,
@@ -1407,6 +1408,23 @@ const upstreamDriftOutputSchema = {
14071408 reports : z . unknown ( ) . optional ( ) ,
14081409} ;
14091410
1411+ // Public upstream ruleset snapshot (#7807) — same shape the REST route returns (or the not-found error body).
1412+ const upstreamRulesetOutputSchema = {
1413+ id : z . string ( ) . optional ( ) ,
1414+ sourceRepo : z . string ( ) . optional ( ) ,
1415+ sourceRef : z . string ( ) . optional ( ) ,
1416+ commitSha : z . string ( ) . nullable ( ) . optional ( ) ,
1417+ sourceSnapshotIds : z . unknown ( ) . optional ( ) ,
1418+ activeModel : z . string ( ) . optional ( ) ,
1419+ registryRepoCount : z . number ( ) . optional ( ) ,
1420+ totalEmissionShare : z . number ( ) . optional ( ) ,
1421+ semanticHash : z . string ( ) . optional ( ) ,
1422+ payload : z . unknown ( ) . optional ( ) ,
1423+ warnings : z . unknown ( ) . optional ( ) ,
1424+ generatedAt : z . string ( ) . optional ( ) ,
1425+ error : z . string ( ) . optional ( ) ,
1426+ } ;
1427+
14101428const localStatusOutputSchema = {
14111429 apiAvailable : z . boolean ( ) . optional ( ) ,
14121430 sourceUploadDefault : z . boolean ( ) . optional ( ) ,
@@ -1836,6 +1854,7 @@ export const MCP_TOOL_CATEGORIES: Record<string, McpToolCategory> = {
18361854 loopover_get_bounty_advisory : "discovery" ,
18371855 loopover_get_registry_changes : "utility" ,
18381856 loopover_get_upstream_drift : "utility" ,
1857+ loopover_get_upstream_ruleset : "utility" ,
18391858 loopover_get_issue_quality : "maintainer" ,
18401859 loopover_get_pr_reviewability : "review" ,
18411860 loopover_validate_linked_issue : "discovery" ,
@@ -2336,6 +2355,17 @@ export class LoopoverMcp {
23362355 async ( ) => this . toolResult ( await this . getUpstreamDrift ( ) ) ,
23372356 ) ;
23382357
2358+ register (
2359+ "loopover_get_upstream_ruleset" ,
2360+ {
2361+ description :
2362+ "Return the latest cached upstream Gittensor ruleset snapshot (public static discovery data). No input; returns not-found when no snapshot exists yet." ,
2363+ inputSchema : { } ,
2364+ outputSchema : upstreamRulesetOutputSchema ,
2365+ } ,
2366+ async ( ) => this . toolResult ( await this . getUpstreamRuleset ( ) ) ,
2367+ ) ;
2368+
23392369 register (
23402370 "loopover_get_issue_quality" ,
23412371 {
@@ -4008,6 +4038,23 @@ export class LoopoverMcp {
40084038 } ;
40094039 }
40104040
4041+ // #7807 — public raw ruleset snapshot (distinct from getUpstreamDrift's status+reports payload).
4042+ // Mirrors GET /v1/upstream/ruleset: return the snapshot when present; otherwise a normal not-found
4043+ // result (never throw), matching the REST route's upstream_ruleset_not_found body.
4044+ private async getUpstreamRuleset ( ) : Promise < ToolPayload > {
4045+ const ruleset = await getLatestUpstreamRulesetSnapshot ( this . env ) ;
4046+ if ( ! ruleset ) {
4047+ return {
4048+ summary : "LoopOver has no upstream ruleset snapshot yet." ,
4049+ data : { error : "upstream_ruleset_not_found" } ,
4050+ } ;
4051+ }
4052+ return {
4053+ summary : `LoopOver upstream ruleset snapshot ${ ruleset . id } (${ ruleset . activeModel } ).` ,
4054+ data : ruleset as unknown as Record < string , unknown > ,
4055+ } ;
4056+ }
4057+
40114058 private async preflightPr ( input : z . infer < z . ZodObject < typeof preflightShape > > ) : Promise < ToolPayload > {
40124059 await this . requireRepoAccess ( input . repoFullName ) ;
40134060 const [ repo , issues , pullRequests , bounties , issueQuality ] = await Promise . all ( [
0 commit comments