pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/github/github-mcp-server/commit/13e7f10fc8ddff977e02043aec4574d9c8ded8ec

tps://github.githubassets.com/assets/global-d18f184ea1a06a2c.css" /> Add custom field filtering to list_issues (#2480) · github/github-mcp-server@13e7f10 · GitHub
Skip to content

Commit 13e7f10

Browse files
Add custom field filtering to list_issues (#2480)
* Add custom field filtering to list_issues * Flatten schema * add repo fields flag * test fix --------- Co-authored-by: Sam Morrow <sammorrowdrums@github.com>
1 parent 8f6050a commit 13e7f10

5 files changed

Lines changed: 755 additions & 133 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,7 @@ The following sets of tools are available:
885885
- **Required OAuth Scopes**: `repo`
886886
- `after`: Cursor for pagination. Use the endCursor from the previous page's PageInfo for GraphQL APIs. (string, optional)
887887
- `direction`: Order direction. If provided, the 'orderBy' also needs to be provided. (string, optional)
888+
- `field_filters`: Filter by custom issue field values. Each entry takes a field_name and a value; the server looks up the field and coerces the value to its type (single-select option name, text, number, or YYYY-MM-DD date). (object[], optional)
888889
- `labels`: Filter by labels (string[], optional)
889890
- `orderBy`: Order issues by field. If provided, the 'direction' also needs to be provided. (string, optional)
890891
- `owner`: Repository owner (string, required)

pkg/github/__toolsnaps__/list_issues.snap

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,27 @@
1818
],
1919
"type": "string"
2020
},
21+
"field_filters": {
22+
"description": "Filter by custom issue field values. Each entry takes a field_name and a value; the server looks up the field and coerces the value to its type (single-select option name, text, number, or YYYY-MM-DD date).",
23+
"items": {
24+
"properties": {
25+
"field_name": {
26+
"description": "Name of the custom field (e.g. \"Priority\"). Case-insensitive.",
27+
"type": "string"
28+
},
29+
"value": {
30+
"description": "Value to filter on. For single-select fields, the option name (e.g. \"P1\"). For dates, YYYY-MM-DD. For numbers, the numeric value as a string. For text, the text value.",
31+
"type": "string"
32+
}
33+
},
34+
"required": [
35+
"field_name",
36+
"value"
37+
],
38+
"type": "object"
39+
},
40+
"type": "array"
41+
},
2142
"labels": {
2243
"description": "Filter by labels",
2344
"items": {

pkg/github/issue_fields.go

Lines changed: 88 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -136,81 +136,9 @@ func ListIssueFields(t translations.TranslationHelperFunc) inventory.ServerTool
136136
return utils.NewToolResultErrorFromErr("failed to get GitHub GraphQL client", err), nil, nil
137137
}
138138

139-
ctxWithFeatures := ghcontext.WithGraphQLFeatures(ctx, "issue_fields", "repo_issue_fields")
140-
var nodes []issueFieldNode
141-
if repo != "" {
142-
var query issueFieldsRepoQuery
143-
vars := map[string]any{
144-
"owner": githubv4.String(owner),
145-
"name": githubv4.String(repo),
146-
}
147-
if err := gqlClient.Query(ctxWithFeatures, &query, vars); err != nil {
148-
return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "failed to list issue fields", err), nil, nil
149-
}
150-
nodes = query.Repository.IssueFields.Nodes
151-
} else {
152-
var query issueFieldsOrgQuery
153-
vars := map[string]any{
154-
"login": githubv4.String(owner),
155-
}
156-
if err := gqlClient.Query(ctxWithFeatures, &query, vars); err != nil {
157-
return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "failed to list issue fields", err), nil, nil
158-
}
159-
nodes = query.Organization.IssueFields.Nodes
160-
}
161-
162-
fields := make([]IssueField, 0, len(nodes))
163-
for _, node := range nodes {
164-
var f IssueField
165-
// Read from the fragment matching __typename; the other fragments are zero-valued.
166-
switch string(node.TypeName) {
167-
case "IssueFieldSingleSelect":
168-
opts := make([]IssueSingleSelectFieldOption, 0, len(node.IssueFieldSingleSelect.Options))
169-
for _, o := range node.IssueFieldSingleSelect.Options {
170-
opts = append(opts, IssueSingleSelectFieldOption{
171-
ID: fmt.Sprintf("%v", o.ID),
172-
Name: string(o.Name),
173-
Description: string(o.Description),
174-
Color: string(o.Color),
175-
Priority: o.Priority,
176-
})
177-
}
178-
f = IssueField{
179-
ID: fmt.Sprintf("%v", node.IssueFieldSingleSelect.ID),
180-
Name: string(node.IssueFieldSingleSelect.Name),
181-
Description: string(node.IssueFieldSingleSelect.Description),
182-
DataType: string(node.IssueFieldSingleSelect.DataType),
183-
Visibility: string(node.IssueFieldSingleSelect.Visibility),
184-
Options: opts,
185-
}
186-
case "IssueFieldText":
187-
f = IssueField{
188-
ID: fmt.Sprintf("%v", node.IssueFieldText.ID),
189-
Name: string(node.IssueFieldText.Name),
190-
Description: string(node.IssueFieldText.Description),
191-
DataType: string(node.IssueFieldText.DataType),
192-
Visibility: string(node.IssueFieldText.Visibility),
193-
}
194-
case "IssueFieldNumber":
195-
f = IssueField{
196-
ID: fmt.Sprintf("%v", node.IssueFieldNumber.ID),
197-
Name: string(node.IssueFieldNumber.Name),
198-
Description: string(node.IssueFieldNumber.Description),
199-
DataType: string(node.IssueFieldNumber.DataType),
200-
Visibility: string(node.IssueFieldNumber.Visibility),
201-
}
202-
case "IssueFieldDate":
203-
f = IssueField{
204-
ID: fmt.Sprintf("%v", node.IssueFieldDate.ID),
205-
Name: string(node.IssueFieldDate.Name),
206-
Description: string(node.IssueFieldDate.Description),
207-
DataType: string(node.IssueFieldDate.DataType),
208-
Visibility: string(node.IssueFieldDate.Visibility),
209-
}
210-
default:
211-
continue
212-
}
213-
fields = append(fields, f)
139+
fields, err := fetchIssueFields(ctx, gqlClient, owner, repo)
140+
if err != nil {
141+
return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "failed to list issue fields", err), nil, nil
214142
}
215143

216144
r, err := json.Marshal(fields)
@@ -221,3 +149,88 @@ func ListIssueFields(t translations.TranslationHelperFunc) inventory.ServerTool
221149
return utils.NewToolResultText(string(r)), nil, nil
222150
})
223151
}
152+
153+
// fetchIssueFields returns the issue field definitions for the given owner.
154+
// If repo is provided, fields are scoped to that repository (inherited from its
155+
// organization); otherwise fields are returned directly from the organization.
156+
func fetchIssueFields(ctx context.Context, gqlClient *githubv4.Client, owner, repo string) ([]IssueField, error) {
157+
ctxWithFeatures := ghcontext.WithGraphQLFeatures(ctx, "issue_fields", "repo_issue_fields")
158+
if repo != "" {
159+
var query issueFieldsRepoQuery
160+
vars := map[string]any{
161+
"owner": githubv4.String(owner),
162+
"name": githubv4.String(repo),
163+
}
164+
if err := gqlClient.Query(ctxWithFeatures, &query, vars); err != nil {
165+
return nil, err
166+
}
167+
return issueFieldsFromNodes(query.Repository.IssueFields.Nodes), nil
168+
}
169+
170+
var query issueFieldsOrgQuery
171+
vars := map[string]any{
172+
"login": githubv4.String(owner),
173+
}
174+
if err := gqlClient.Query(ctxWithFeatures, &query, vars); err != nil {
175+
return nil, err
176+
}
177+
return issueFieldsFromNodes(query.Organization.IssueFields.Nodes), nil
178+
}
179+
180+
// issueFieldsFromNodes converts GraphQL issue field union nodes into IssueField values.
181+
// Read from the fragment matching __typename; the other fragments are zero-valued.
182+
func issueFieldsFromNodes(nodes []issueFieldNode) []IssueField {
183+
fields := make([]IssueField, 0, len(nodes))
184+
for _, node := range nodes {
185+
var f IssueField
186+
switch string(node.TypeName) {
187+
case "IssueFieldSingleSelect":
188+
opts := make([]IssueSingleSelectFieldOption, 0, len(node.IssueFieldSingleSelect.Options))
189+
for _, o := range node.IssueFieldSingleSelect.Options {
190+
opts = append(opts, IssueSingleSelectFieldOption{
191+
ID: fmt.Sprintf("%v", o.ID),
192+
Name: string(o.Name),
193+
Description: string(o.Description),
194+
Color: string(o.Color),
195+
Priority: o.Priority,
196+
})
197+
}
198+
f = IssueField{
199+
ID: fmt.Sprintf("%v", node.IssueFieldSingleSelect.ID),
200+
Name: string(node.IssueFieldSingleSelect.Name),
201+
Description: string(node.IssueFieldSingleSelect.Description),
202+
DataType: string(node.IssueFieldSingleSelect.DataType),
203+
Visibility: string(node.IssueFieldSingleSelect.Visibility),
204+
Options: opts,
205+
}
206+
case "IssueFieldText":
207+
f = IssueField{
208+
ID: fmt.Sprintf("%v", node.IssueFieldText.ID),
209+
Name: string(node.IssueFieldText.Name),
210+
Description: string(node.IssueFieldText.Description),
211+
DataType: string(node.IssueFieldText.DataType),
212+
Visibility: string(node.IssueFieldText.Visibility),
213+
}
214+
case "IssueFieldNumber":
215+
f = IssueField{
216+
ID: fmt.Sprintf("%v", node.IssueFieldNumber.ID),
217+
Name: string(node.IssueFieldNumber.Name),
218+
Description: string(node.IssueFieldNumber.Description),
219+
DataType: string(node.IssueFieldNumber.DataType),
220+
Visibility: string(node.IssueFieldNumber.Visibility),
221+
}
222+
case "IssueFieldDate":
223+
f = IssueField{
224+
ID: fmt.Sprintf("%v", node.IssueFieldDate.ID),
225+
Name: string(node.IssueFieldDate.Name),
226+
Description: string(node.IssueFieldDate.Description),
227+
DataType: string(node.IssueFieldDate.DataType),
228+
Visibility: string(node.IssueFieldDate.Visibility),
229+
}
230+
default:
231+
continue
232+
}
233+
fields = append(fields, f)
234+
}
235+
return fields
236+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy