@@ -26,38 +26,10 @@ func (r *MCPReader) FindPortfolioOverlaps(ctx context.Context, in mcpserver.Find
2626 return mcpserver.FindPortfolioOverlapsOutput {}, err
2727 }
2828 out := mcpserver.FindPortfolioOverlapsOutput {Status : "complete" , Items : make ([]mcpserver.BatchItem [mcpserver.PortfolioOverlapOutput ], len (in .Candidates ))}
29- var candidates []corpus.PortfolioSubject
30- var candidateIndexes []int
31- for i , candidate := range in .Candidates {
32- item := mcpserver.BatchItem [mcpserver.PortfolioOverlapOutput ]{Key : candidate .Kind + ":" + candidate .Ref }
33- if ! validPortfolioSubjectInput (candidate ) {
34- item .Status , item .Reason , item .Message = "failed" , "invalid_candidate" , "kind must be opportunity, workspace, or pull_request and ref must be a valid local ID"
35- out .Status , out .Items [i ] = "partial" , item
36- continue
37- }
38- candidates = append (candidates , corpus.PortfolioSubject {Kind : candidate .Kind , Ref : strings .TrimSpace (candidate .Ref )})
39- candidateIndexes = append (candidateIndexes , i )
40- }
41- var prIDs []int64
42- missingPullRequests := false
43- for _ , ref := range in .PullRequests {
44- repo , err := c .GetRepository (ctx , ref .Owner , ref .Repo )
45- if err != nil {
46- return mcpserver.FindPortfolioOverlapsOutput {}, err
47- }
48- if repo == nil {
49- missingPullRequests = true
50- continue
51- }
52- thread , err := c .GetThreadByNumber (ctx , repo .ID , ref .Number )
53- if err != nil {
54- return mcpserver.FindPortfolioOverlapsOutput {}, err
55- }
56- if thread == nil || thread .Kind != corpus .ThreadKindPullRequest {
57- missingPullRequests = true
58- continue
59- }
60- prIDs = append (prIDs , thread .ID )
29+ candidates , candidateIndexes := collectPortfolioCandidates (in .Candidates , & out )
30+ prIDs , missingPullRequests , err := resolvePortfolioPullRequests (ctx , c , in .PullRequests )
31+ if err != nil {
32+ return mcpserver.FindPortfolioOverlapsOutput {}, err
6133 }
6234 if len (candidates ) == 0 {
6335 return out , nil
@@ -75,18 +47,7 @@ func (r *MCPReader) FindPortfolioOverlaps(ctx context.Context, in mcpserver.Find
7547 }
7648 for resultIndex , result := range results {
7749 i := candidateIndexes [resultIndex ]
78- value := mcpserver.PortfolioOverlapOutput {Candidate : mcpserver.PortfolioSubjectInput {Kind : result .Candidate .Kind , Ref : result .Candidate .Ref }, Status : result .Status , Coverage : result .Coverage }
79- for _ , match := range result .Matches {
80- converted := mcpserver.PortfolioOverlapMatchOutput {PullRequestThreadID : match .PullRequestThreadID }
81- for _ , evidence := range match .Evidence {
82- item := mcpserver.PortfolioOverlapEvidenceOutput {Kind : evidence .Kind , Value : evidence .Value , Score : evidence .Score }
83- for _ , ref := range evidence .SourceObservationRefs {
84- item .SourceRefs = append (item .SourceRefs , ref .Kind + ":" + strconv .FormatInt (ref .ID , 10 ))
85- }
86- converted .Evidence = append (converted .Evidence , item )
87- }
88- value .Matches = append (value .Matches , converted )
89- }
50+ value := portfolioOverlapOutput (result )
9051 batch := mcpserver.BatchItem [mcpserver.PortfolioOverlapOutput ]{Key : result .Candidate .Kind + ":" + result .Candidate .Ref , Status : "complete" , Value : & value }
9152 if missingPullRequests {
9253 out .Status = "partial"
@@ -100,6 +61,63 @@ func (r *MCPReader) FindPortfolioOverlaps(ctx context.Context, in mcpserver.Find
10061 return out , nil
10162}
10263
64+ func collectPortfolioCandidates (inputs []mcpserver.PortfolioSubjectInput , out * mcpserver.FindPortfolioOverlapsOutput ) ([]corpus.PortfolioSubject , []int ) {
65+ var candidates []corpus.PortfolioSubject
66+ var indexes []int
67+ for i , candidate := range inputs {
68+ item := mcpserver.BatchItem [mcpserver.PortfolioOverlapOutput ]{Key : candidate .Kind + ":" + candidate .Ref }
69+ if ! validPortfolioSubjectInput (candidate ) {
70+ item .Status , item .Reason , item .Message = "failed" , "invalid_candidate" , "kind must be opportunity, workspace, or pull_request and ref must be a valid local ID"
71+ out .Status , out .Items [i ] = "partial" , item
72+ continue
73+ }
74+ candidates = append (candidates , corpus.PortfolioSubject {Kind : candidate .Kind , Ref : strings .TrimSpace (candidate .Ref )})
75+ indexes = append (indexes , i )
76+ }
77+ return candidates , indexes
78+ }
79+
80+ func resolvePortfolioPullRequests (ctx context.Context , c * corpus.Corpus , refs []mcpserver.ThreadRef ) ([]int64 , bool , error ) {
81+ var ids []int64
82+ missing := false
83+ for _ , ref := range refs {
84+ repo , err := c .GetRepository (ctx , ref .Owner , ref .Repo )
85+ if err != nil {
86+ return nil , false , err
87+ }
88+ if repo == nil {
89+ missing = true
90+ continue
91+ }
92+ thread , err := c .GetThreadByNumber (ctx , repo .ID , ref .Number )
93+ if err != nil {
94+ return nil , false , err
95+ }
96+ if thread == nil || thread .Kind != corpus .ThreadKindPullRequest {
97+ missing = true
98+ continue
99+ }
100+ ids = append (ids , thread .ID )
101+ }
102+ return ids , missing , nil
103+ }
104+
105+ func portfolioOverlapOutput (result corpus.PortfolioOverlapResult ) mcpserver.PortfolioOverlapOutput {
106+ value := mcpserver.PortfolioOverlapOutput {Candidate : mcpserver.PortfolioSubjectInput {Kind : result .Candidate .Kind , Ref : result .Candidate .Ref }, Status : result .Status , Coverage : result .Coverage }
107+ for _ , match := range result .Matches {
108+ converted := mcpserver.PortfolioOverlapMatchOutput {PullRequestThreadID : match .PullRequestThreadID }
109+ for _ , evidence := range match .Evidence {
110+ item := mcpserver.PortfolioOverlapEvidenceOutput {Kind : evidence .Kind , Value : evidence .Value , Score : evidence .Score }
111+ for _ , ref := range evidence .SourceObservationRefs {
112+ item .SourceRefs = append (item .SourceRefs , ref .Kind + ":" + strconv .FormatInt (ref .ID , 10 ))
113+ }
114+ converted .Evidence = append (converted .Evidence , item )
115+ }
116+ value .Matches = append (value .Matches , converted )
117+ }
118+ return value
119+ }
120+
103121func validPortfolioSubjectInput (candidate mcpserver.PortfolioSubjectInput ) bool {
104122 ref := strings .TrimSpace (candidate .Ref )
105123 if ref == "" {
0 commit comments