@@ -18,22 +18,7 @@ import (
18
18
"github.com/shurcooL/githubv4"
19
19
)
20
20
21
- var ListIssuesQuery struct {
22
- Repository struct {
23
- Issues struct {
24
- Nodes []IssueFragment `graphql:"nodes"`
25
- PageInfo struct {
26
- HasNextPage githubv4.Boolean
27
- HasPreviousPage githubv4.Boolean
28
- StartCursor githubv4.String
29
- EndCursor githubv4.String
30
- }
31
- TotalCount int
32
- } `graphql:"issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction})"`
33
- } `graphql:"repository(owner: $owner, name: $repo)"`
34
- }
35
-
36
- // NodeFragment represents a fragment of an issue node in the GraphQL API.
21
+ // IssueFragment represents a fragment of an issue node in the GraphQL API.
37
22
type IssueFragment struct {
38
23
Number githubv4.Int
39
24
Title githubv4.String
@@ -55,11 +40,57 @@ type IssueFragment struct {
55
40
} `graphql:"labels(first: 10)"`
56
41
}
57
42
43
+ // Common interface for all issue query types
44
+ type IssueQueryResult interface {
45
+ GetIssueFragment () IssueQueryFragment
46
+ }
47
+
48
+ type IssueQueryFragment struct {
49
+ Nodes []IssueFragment `graphql:"nodes"`
50
+ PageInfo struct {
51
+ HasNextPage githubv4.Boolean
52
+ HasPreviousPage githubv4.Boolean
53
+ StartCursor githubv4.String
54
+ EndCursor githubv4.String
55
+ }
56
+ TotalCount int
57
+ }
58
+
59
+ // ListIssuesQuery is the root query structure for fetching issues with optional label filtering.
60
+ type ListIssuesQueryType struct {
61
+ Repository struct {
62
+ Issues IssueQueryFragment `graphql:"issues(first: $first, after: $after, labels: $labels, states: $states, orderBy: {field: $orderBy, direction: $direction})"`
63
+ } `graphql:"repository(owner: $owner, name: $repo)"`
64
+ }
65
+
66
+ // ListIssuesQueryNoLabels is the query structure for fetching issues without label filtering.
67
+ type ListIssuesQueryNoLabelsType struct {
68
+ Repository struct {
69
+ Issues IssueQueryFragment `graphql:"issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction})"`
70
+ } `graphql:"repository(owner: $owner, name: $repo)"`
71
+ }
72
+
73
+ // Implement the interface for both query types
74
+ func (q * ListIssuesQueryType ) GetIssueFragment () IssueQueryFragment {
75
+ return q .Repository .Issues
76
+ }
77
+
78
+ func (q * ListIssuesQueryNoLabelsType ) GetIssueFragment () IssueQueryFragment {
79
+ return q .Repository .Issues
80
+ }
81
+
82
+ func getIssueQueryType (hasLabels bool ) any {
83
+ if hasLabels {
84
+ return & ListIssuesQueryType {}
85
+ }
86
+ return & ListIssuesQueryNoLabelsType {}
87
+ }
88
+
58
89
func fragmentToIssue (fragment IssueFragment ) * github.Issue {
59
90
// Convert GraphQL labels to GitHub API labels format
60
- var labels []* github.Label
91
+ var foundLabels []* github.Label
61
92
for _ , labelNode := range fragment .Labels .Nodes {
62
- labels = append (labels , & github.Label {
93
+ foundLabels = append (foundLabels , & github.Label {
63
94
Name : github .Ptr (string (labelNode .Name )),
64
95
NodeID : github .Ptr (string (labelNode .Id )),
65
96
Description : github .Ptr (string (labelNode .Description )),
@@ -77,7 +108,7 @@ func fragmentToIssue(fragment IssueFragment) *github.Issue {
77
108
State : github .Ptr (string (fragment .State )),
78
109
ID : github .Ptr (fragment .DatabaseID ),
79
110
Body : github .Ptr (string (fragment .Body )),
80
- Labels : labels ,
111
+ Labels : foundLabels ,
81
112
}
82
113
}
83
114
@@ -850,6 +881,11 @@ func ListIssues(getGQLClient GetGQLClientFn, t translations.TranslationHelperFun
850
881
return mcp .NewToolResultError (err .Error ()), nil
851
882
}
852
883
884
+ //If labels is empty, default to nil for gql query
885
+ if len (labels ) == 0 {
886
+ labels = nil
887
+ }
888
+
853
889
orderBy , err := OptionalParam [string ](request , "orderBy" )
854
890
if err != nil {
855
891
return mcp .NewToolResultError (err .Error ()), nil
@@ -868,18 +904,19 @@ func ListIssues(getGQLClient GetGQLClientFn, t translations.TranslationHelperFun
868
904
direction = "DESC"
869
905
}
870
906
871
- since , err := OptionalParam [string ](request , "since" )
872
- if err != nil {
873
- return mcp .NewToolResultError (err .Error ()), nil
874
- }
907
+ // since, err := OptionalParam[string](request, "since")
908
+ // if err != nil {
909
+ // return mcp.NewToolResultError(err.Error()), nil
910
+ // }
911
+
912
+ // var sinceTime time.Time
913
+ // if since != "" {
914
+ // sinceTime, err = parseISOTimestamp(since)
915
+ // if err != nil {
916
+ // return mcp.NewToolResultError(fmt.Sprintf("failed to list issues: %s", err.Error())), nil
917
+ // }
918
+ // }
875
919
876
- var sinceTime time.Time
877
- if since != "" {
878
- sinceTime , err = parseISOTimestamp (since )
879
- if err != nil {
880
- return mcp .NewToolResultError (fmt .Sprintf ("failed to list issues: %s" , err .Error ())), nil
881
- }
882
- }
883
920
// Get pagination parameters and convert to GraphQL format
884
921
pagination , err := OptionalCursorPaginationParams (request )
885
922
if err != nil {
@@ -926,54 +963,52 @@ func ListIssues(getGQLClient GetGQLClientFn, t translations.TranslationHelperFun
926
963
vars ["after" ] = (* githubv4 .String )(nil )
927
964
}
928
965
929
- if err := client .Query (ctx , & ListIssuesQuery , vars ); err != nil {
966
+ // Choose the appropriate query based on whether labels are specified
967
+ hasLabels := len (labels ) > 0
968
+
969
+ if hasLabels {
970
+ // Use query with labels filtering - convert string labels to githubv4.String slice
971
+ labelStrings := make ([]githubv4.String , len (labels ))
972
+ for i , label := range labels {
973
+ labelStrings [i ] = githubv4 .String (label )
974
+ }
975
+ vars ["labels" ] = labelStrings
976
+ }
977
+
978
+ issueQuery := getIssueQueryType (hasLabels )
979
+ if err := client .Query (ctx , issueQuery , vars ); err != nil {
930
980
return mcp .NewToolResultError (err .Error ()), nil
931
981
}
932
982
933
- //We must filter based on labels after fetching all issues
983
+ // Extract and convert all issue nodes using the common interface
934
984
var issues []* github.Issue
935
- for _ , issue := range ListIssuesQuery .Repository .Issues .Nodes {
936
- var issueLabels []string
937
- for _ , label := range issue .Labels .Nodes {
938
- issueLabels = append (issueLabels , string (label .Name ))
939
- }
940
-
941
- // Filter by since date if specified
942
- if ! sinceTime .IsZero () && issue .CreatedAt .Time .Before (sinceTime ) {
943
- continue // Skip issues created before the since date
944
- }
985
+ var pageInfo struct {
986
+ HasNextPage githubv4.Boolean
987
+ HasPreviousPage githubv4.Boolean
988
+ StartCursor githubv4.String
989
+ EndCursor githubv4.String
990
+ }
991
+ var totalCount int
945
992
946
- // Filter by labels if specified
947
- if len (labels ) > 0 {
948
- hasMatchingLabel := false
949
- for _ , requestedLabel := range labels {
950
- for _ , issueLabel := range issueLabels {
951
- if strings .EqualFold (requestedLabel , issueLabel ) {
952
- hasMatchingLabel = true
953
- break
954
- }
955
- }
956
- if hasMatchingLabel {
957
- break
958
- }
959
- }
960
- if ! hasMatchingLabel {
961
- continue // Skip this issue as it doesn't match any requested labels
962
- }
993
+ if queryResult , ok := issueQuery .(IssueQueryResult ); ok {
994
+ fragment := queryResult .GetIssueFragment ()
995
+ for _ , issue := range fragment .Nodes {
996
+ issues = append (issues , fragmentToIssue (issue ))
963
997
}
964
- issues = append (issues , fragmentToIssue (issue ))
998
+ pageInfo = fragment .PageInfo
999
+ totalCount = fragment .TotalCount
965
1000
}
966
1001
967
1002
// Create response with issues
968
1003
response := map [string ]interface {}{
969
1004
"issues" : issues ,
970
1005
"pageInfo" : map [string ]interface {}{
971
- "hasNextPage" : ListIssuesQuery . Repository . Issues . PageInfo .HasNextPage ,
972
- "hasPreviousPage" : ListIssuesQuery . Repository . Issues . PageInfo .HasPreviousPage ,
973
- "startCursor" : string (ListIssuesQuery . Repository . Issues . PageInfo .StartCursor ),
974
- "endCursor" : string (ListIssuesQuery . Repository . Issues . PageInfo .EndCursor ),
1006
+ "hasNextPage" : pageInfo .HasNextPage ,
1007
+ "hasPreviousPage" : pageInfo .HasPreviousPage ,
1008
+ "startCursor" : string (pageInfo .StartCursor ),
1009
+ "endCursor" : string (pageInfo .EndCursor ),
975
1010
},
976
- "totalCount" : ListIssuesQuery . Repository . Issues . TotalCount ,
1011
+ "totalCount" : totalCount ,
977
1012
}
978
1013
out , err := json .Marshal (response )
979
1014
if err != nil {
0 commit comments