@@ -10,6 +10,7 @@ import (
1010 "time"
1111
1212 ghErrors "github.com/github/github-mcp-server/pkg/errors"
13+ "github.com/github/github-mcp-server/pkg/ifc"
1314 "github.com/github/github-mcp-server/pkg/inventory"
1415 "github.com/github/github-mcp-server/pkg/sanitize"
1516 "github.com/github/github-mcp-server/pkg/scopes"
@@ -130,6 +131,7 @@ type IssueFragment struct {
130131// Common interface for all issue query types
131132type IssueQueryResult interface {
132133 GetIssueFragment () IssueQueryFragment
134+ GetIsPrivate () bool
133135}
134136
135137type IssueQueryFragment struct {
@@ -146,28 +148,32 @@ type IssueQueryFragment struct {
146148// ListIssuesQuery is the root query structure for fetching issues with optional label filtering.
147149type ListIssuesQuery struct {
148150 Repository struct {
149- Issues IssueQueryFragment `graphql:"issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction})"`
151+ Issues IssueQueryFragment `graphql:"issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction})"`
152+ IsPrivate githubv4.Boolean
150153 } `graphql:"repository(owner: $owner, name: $repo)"`
151154}
152155
153156// ListIssuesQueryTypeWithLabels is the query structure for fetching issues with optional label filtering.
154157type ListIssuesQueryTypeWithLabels struct {
155158 Repository struct {
156- Issues IssueQueryFragment `graphql:"issues(first: $first, after: $after, labels: $labels, states: $states, orderBy: {field: $orderBy, direction: $direction})"`
159+ Issues IssueQueryFragment `graphql:"issues(first: $first, after: $after, labels: $labels, states: $states, orderBy: {field: $orderBy, direction: $direction})"`
160+ IsPrivate githubv4.Boolean
157161 } `graphql:"repository(owner: $owner, name: $repo)"`
158162}
159163
160164// ListIssuesQueryWithSince is the query structure for fetching issues without label filtering but with since filtering.
161165type ListIssuesQueryWithSince struct {
162166 Repository struct {
163- Issues IssueQueryFragment `graphql:"issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction}, filterBy: {since: $since})"`
167+ Issues IssueQueryFragment `graphql:"issues(first: $first, after: $after, states: $states, orderBy: {field: $orderBy, direction: $direction}, filterBy: {since: $since})"`
168+ IsPrivate githubv4.Boolean
164169 } `graphql:"repository(owner: $owner, name: $repo)"`
165170}
166171
167172// ListIssuesQueryTypeWithLabelsWithSince is the query structure for fetching issues with both label and since filtering.
168173type ListIssuesQueryTypeWithLabelsWithSince struct {
169174 Repository struct {
170- Issues IssueQueryFragment `graphql:"issues(first: $first, after: $after, labels: $labels, states: $states, orderBy: {field: $orderBy, direction: $direction}, filterBy: {since: $since})"`
175+ Issues IssueQueryFragment `graphql:"issues(first: $first, after: $after, labels: $labels, states: $states, orderBy: {field: $orderBy, direction: $direction}, filterBy: {since: $since})"`
176+ IsPrivate githubv4.Boolean
171177 } `graphql:"repository(owner: $owner, name: $repo)"`
172178}
173179
@@ -176,18 +182,28 @@ func (q *ListIssuesQueryTypeWithLabels) GetIssueFragment() IssueQueryFragment {
176182 return q .Repository .Issues
177183}
178184
185+ func (q * ListIssuesQueryTypeWithLabels ) GetIsPrivate () bool { return bool (q .Repository .IsPrivate ) }
186+
179187func (q * ListIssuesQuery ) GetIssueFragment () IssueQueryFragment {
180188 return q .Repository .Issues
181189}
182190
191+ func (q * ListIssuesQuery ) GetIsPrivate () bool { return bool (q .Repository .IsPrivate ) }
192+
183193func (q * ListIssuesQueryWithSince ) GetIssueFragment () IssueQueryFragment {
184194 return q .Repository .Issues
185195}
186196
197+ func (q * ListIssuesQueryWithSince ) GetIsPrivate () bool { return bool (q .Repository .IsPrivate ) }
198+
187199func (q * ListIssuesQueryTypeWithLabelsWithSince ) GetIssueFragment () IssueQueryFragment {
188200 return q .Repository .Issues
189201}
190202
203+ func (q * ListIssuesQueryTypeWithLabelsWithSince ) GetIsPrivate () bool {
204+ return bool (q .Repository .IsPrivate )
205+ }
206+
191207func getIssueQueryType (hasLabels bool , hasSince bool ) any {
192208 switch {
193209 case hasLabels && hasSince :
@@ -1846,11 +1862,35 @@ func ListIssues(t translations.TranslationHelperFunc) inventory.ServerTool {
18461862 }
18471863
18481864 var resp MinimalIssuesResponse
1865+ var isPrivate bool
18491866 if queryResult , ok := issueQuery .(IssueQueryResult ); ok {
18501867 resp = convertToMinimalIssuesResponse (queryResult .GetIssueFragment ())
1868+ isPrivate = queryResult .GetIsPrivate ()
18511869 }
18521870
1853- return MarshalledTextResult (resp ), nil , nil
1871+ result := MarshalledTextResult (resp )
1872+ if deps .GetFlags (ctx ).InsidersMode {
1873+ if result .Meta == nil {
1874+ result .Meta = mcp.Meta {}
1875+ }
1876+ var readers []string
1877+ if isPrivate {
1878+ restClient , err := deps .GetClient (ctx )
1879+ if err == nil {
1880+ if collaborators , err := FetchRepoCollaborators (ctx , restClient , owner , repo ); err == nil {
1881+ readers = collaborators
1882+ }
1883+ }
1884+ // Fall back to the repository owner so the reader set is
1885+ // never empty for a private repository even if the
1886+ // collaborators lookup fails.
1887+ if len (readers ) == 0 {
1888+ readers = []string {owner }
1889+ }
1890+ }
1891+ result .Meta ["ifc" ] = ifc .LabelListIssues (isPrivate , readers )
1892+ }
1893+ return result , nil , nil
18541894 })
18551895}
18561896
0 commit comments