Skip to content

Commit 7c3f8c7

Browse files
committed
Let agents address Project fields, single-select options, and item field values by name through the GitHub MCP server
1 parent 9430064 commit 7c3f8c7

8 files changed

Lines changed: 1099 additions & 40 deletions

File tree

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,7 +1034,8 @@ The following sets of tools are available:
10341034
- **Required OAuth Scopes**: `read:project`
10351035
- **Accepted OAuth Scopes**: `project`, `read:project`
10361036
- `field_id`: The field's ID. Required for 'get_project_field' method. (number, optional)
1037-
- `fields`: Specific list of field IDs to include in the response when getting a project item (e.g. ["102589", "985201", "169875"]). If not provided, only the title field is included. Only used for 'get_project_item' method. (string[], optional)
1037+
- `field_names`: Specific list of field names to include in the response when getting a project item (e.g. ["Status", "Priority"]). Resolved server-side to field IDs — pass this instead of 'fields' when you only know the human-readable names. Only used for 'get_project_item' method. (string[], optional)
1038+
- `fields`: Specific list of field IDs to include in the response when getting a project item (e.g. ["102589", "985201", "169875"]). If neither 'fields' nor 'field_names' is provided, only the title field is included. Only used for 'get_project_item' method. (string[], optional)
10381039
- `item_id`: The item's ID. Required for 'get_project_item' method. (number, optional)
10391040
- `method`: The method to execute (string, required)
10401041
- `owner`: The owner (user or organization login). The name is not case sensitive. (string, optional)
@@ -1047,7 +1048,8 @@ The following sets of tools are available:
10471048
- **Accepted OAuth Scopes**: `project`, `read:project`
10481049
- `after`: Forward pagination cursor from previous pageInfo.nextCursor. (string, optional)
10491050
- `before`: Backward pagination cursor from previous pageInfo.prevCursor (rare). (string, optional)
1050-
- `fields`: Field IDs to include when listing project items (e.g. ["102589", "985201"]). CRITICAL: Always provide to get field values. Without this, only titles returned. Only used for 'list_project_items' method. (string[], optional)
1051+
- `field_names`: Field names to include when listing project items (e.g. ["Status", "Priority"]). Resolved server-side to field IDs — pass this instead of 'fields' when you only know the human-readable names. Names that fail to resolve return a structured error. Only used for 'list_project_items' method. (string[], optional)
1052+
- `fields`: Field IDs to include when listing project items (e.g. ["102589", "985201"]). CRITICAL: Always provide to get field values. Without this (and without 'field_names'), only titles returned. Only used for 'list_project_items' method. (string[], optional)
10511053
- `method`: The action to perform (string, required)
10521054
- `owner`: The owner (user or organization login). The name is not case sensitive. (string, required)
10531055
- `owner_type`: Owner type (user or org). If not provided, will automatically try both. (string, optional)
@@ -1059,10 +1061,10 @@ The following sets of tools are available:
10591061
- **Required OAuth Scopes**: `project`
10601062
- `body`: The body of the status update (markdown). Used for 'create_project_status_update' method. (string, optional)
10611063
- `field_name`: The name of the iteration field (e.g. 'Sprint'). Required for 'create_iteration_field' method. (string, optional)
1062-
- `issue_number`: The issue number (use when item_type is 'issue' for 'add_project_item' method). Provide either issue_number or pull_request_number. (number, optional)
1063-
- `item_id`: The project item ID. Required for 'update_project_item' and 'delete_project_item' methods. (number, optional)
1064-
- `item_owner`: The owner (user or organization) of the repository containing the issue or pull request. Required for 'add_project_item' method. (string, optional)
1065-
- `item_repo`: The name of the repository containing the issue or pull request. Required for 'add_project_item' method. (string, optional)
1064+
- `issue_number`: The issue number. Required for 'add_project_item' when item_type is 'issue'. Also accepted by 'update_project_item' to resolve the item by issue number (combine with item_owner and item_repo). (number, optional)
1065+
- `item_id`: The project item ID. Required for 'delete_project_item'. For 'update_project_item', provide either item_id, or (item_owner + item_repo + issue_number) to resolve the item by issue. (number, optional)
1066+
- `item_owner`: The owner (user or organization) of the repository containing the issue or pull request. Required for 'add_project_item' method. Also accepted by 'update_project_item' when resolving the item by issue number. (string, optional)
1067+
- `item_repo`: The name of the repository containing the issue or pull request. Required for 'add_project_item' method. Also accepted by 'update_project_item' when resolving the item by issue number. (string, optional)
10661068
- `item_type`: The item's type, either issue or pull_request. Required for 'add_project_item' method. (string, optional)
10671069
- `iteration_duration`: Duration in days for iterations of the field (e.g. 7 for weekly, 14 for bi-weekly). Required for 'create_iteration_field' method. (number, optional)
10681070
- `iterations`: Custom iterations for 'create_iteration_field' method. Only set this when you need iterations with varying durations, breaks between them, or specific titles. Otherwise omit it: GitHub auto-creates three iterations of 'iteration_duration' days starting on 'start_date', which is the right choice for most cases. (object[], optional)
@@ -1075,7 +1077,7 @@ The following sets of tools are available:
10751077
- `status`: The status of the project. Used for 'create_project_status_update' method. (string, optional)
10761078
- `target_date`: The target date of the status update in YYYY-MM-DD format. Used for 'create_project_status_update' method. (string, optional)
10771079
- `title`: The project title. Required for 'create_project' method. (string, optional)
1078-
- `updated_field`: Object consisting of the ID of the project field to update and the new value for the field. To clear the field, set value to null. Example: {"id": 123456, "value": "New Value"}. Required for 'update_project_item' method. (object, optional)
1080+
- `updated_field`: Object describing the field to update and its new value. Required for 'update_project_item'. Two shapes are accepted: (1) by ID — {"id": 123456, "value": "..."}; (2) by name — {"name": "Status", "value": "In Progress"}. For single-select fields, the value may be the option name (resolved server-side) or the option ID. Set value to null to clear the field. (object, optional)
10791081

10801082
</details>
10811083

pkg/errors/error.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package errors
22

33
import (
44
"context"
5+
"encoding/json"
56
stderrors "errors"
67
"fmt"
78
"net/http"
@@ -218,3 +219,48 @@ func NewGitHubAPIStatusErrorResponse(ctx context.Context, message string, resp *
218219
err := fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
219220
return NewGitHubAPIErrorResponse(ctx, message, resp, err)
220221
}
222+
223+
// StructuredResolutionError is a machine-readable error returned by name-resolution
224+
// helpers (e.g. resolving a project field or single-select option by name). Agents
225+
// can parse the JSON body to self-correct without re-prompting.
226+
//
227+
// Kind values:
228+
// - "field_not_found" — no project field matches the supplied name
229+
// - "field_ambiguous" — more than one project field shares the supplied name
230+
// - "option_not_found" — no option on the resolved single-select field matches
231+
// - "option_ambiguous" — duplicate option names on the resolved field
232+
// - "item_not_in_project" — the issue/PR exists but is not an item on the project
233+
// - "wrong_field_type" — the named field is not the data type the caller expected
234+
type StructuredResolutionError struct {
235+
Kind string `json:"error"`
236+
Name string `json:"name,omitempty"`
237+
Field string `json:"field,omitempty"`
238+
Candidates []any `json:"candidates,omitempty"`
239+
Hint string `json:"hint,omitempty"`
240+
}
241+
242+
// Error implements the error interface; the message is the JSON body so that the
243+
// downstream tool result also carries the structured payload as plain text.
244+
func (e *StructuredResolutionError) Error() string {
245+
b, err := json.Marshal(e)
246+
if err != nil {
247+
return fmt.Sprintf(`{"error":%q,"name":%q}`, e.Kind, e.Name)
248+
}
249+
return string(b)
250+
}
251+
252+
// NewStructuredResolutionError constructs a StructuredResolutionError.
253+
func NewStructuredResolutionError(kind, name, hint string, candidates []any) *StructuredResolutionError {
254+
return &StructuredResolutionError{
255+
Kind: kind,
256+
Name: name,
257+
Hint: hint,
258+
Candidates: candidates,
259+
}
260+
}
261+
262+
// NewStructuredResolutionErrorResponse returns an mcp.CallToolResult whose text body
263+
// is the JSON-serialised StructuredResolutionError, suitable for agent self-correction.
264+
func NewStructuredResolutionErrorResponse(err *StructuredResolutionError) *mcp.CallToolResult {
265+
return utils.NewToolResultError(err.Error())
266+
}

pkg/github/__toolsnaps__/projects_get.snap

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,15 @@
1010
"description": "The field's ID. Required for 'get_project_field' method.",
1111
"type": "number"
1212
},
13+
"field_names": {
14+
"description": "Specific list of field names to include in the response when getting a project item (e.g. [\"Status\", \"Priority\"]). Resolved server-side to field IDs — pass this instead of 'fields' when you only know the human-readable names. Only used for 'get_project_item' method.",
15+
"items": {
16+
"type": "string"
17+
},
18+
"type": "array"
19+
},
1320
"fields": {
14-
"description": "Specific list of field IDs to include in the response when getting a project item (e.g. [\"102589\", \"985201\", \"169875\"]). If not provided, only the title field is included. Only used for 'get_project_item' method.",
21+
"description": "Specific list of field IDs to include in the response when getting a project item (e.g. [\"102589\", \"985201\", \"169875\"]). If neither 'fields' nor 'field_names' is provided, only the title field is included. Only used for 'get_project_item' method.",
1522
"items": {
1623
"type": "string"
1724
},

pkg/github/__toolsnaps__/projects_list.snap

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,15 @@
1414
"description": "Backward pagination cursor from previous pageInfo.prevCursor (rare).",
1515
"type": "string"
1616
},
17+
"field_names": {
18+
"description": "Field names to include when listing project items (e.g. [\"Status\", \"Priority\"]). Resolved server-side to field IDs — pass this instead of 'fields' when you only know the human-readable names. Names that fail to resolve return a structured error. Only used for 'list_project_items' method.",
19+
"items": {
20+
"type": "string"
21+
},
22+
"type": "array"
23+
},
1724
"fields": {
18-
"description": "Field IDs to include when listing project items (e.g. [\"102589\", \"985201\"]). CRITICAL: Always provide to get field values. Without this, only titles returned. Only used for 'list_project_items' method.",
25+
"description": "Field IDs to include when listing project items (e.g. [\"102589\", \"985201\"]). CRITICAL: Always provide to get field values. Without this (and without 'field_names'), only titles returned. Only used for 'list_project_items' method.",
1926
"items": {
2027
"type": "string"
2128
},

pkg/github/__toolsnaps__/projects_write.snap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,19 @@
1515
"type": "string"
1616
},
1717
"issue_number": {
18-
"description": "The issue number (use when item_type is 'issue' for 'add_project_item' method). Provide either issue_number or pull_request_number.",
18+
"description": "The issue number. Required for 'add_project_item' when item_type is 'issue'. Also accepted by 'update_project_item' to resolve the item by issue number (combine with item_owner and item_repo).",
1919
"type": "number"
2020
},
2121
"item_id": {
22-
"description": "The project item ID. Required for 'update_project_item' and 'delete_project_item' methods.",
22+
"description": "The project item ID. Required for 'delete_project_item'. For 'update_project_item', provide either item_id, or (item_owner + item_repo + issue_number) to resolve the item by issue.",
2323
"type": "number"
2424
},
2525
"item_owner": {
26-
"description": "The owner (user or organization) of the repository containing the issue or pull request. Required for 'add_project_item' method.",
26+
"description": "The owner (user or organization) of the repository containing the issue or pull request. Required for 'add_project_item' method. Also accepted by 'update_project_item' when resolving the item by issue number.",
2727
"type": "string"
2828
},
2929
"item_repo": {
30-
"description": "The name of the repository containing the issue or pull request. Required for 'add_project_item' method.",
30+
"description": "The name of the repository containing the issue or pull request. Required for 'add_project_item' method. Also accepted by 'update_project_item' when resolving the item by issue number.",
3131
"type": "string"
3232
},
3333
"item_type": {
@@ -125,7 +125,7 @@
125125
"type": "string"
126126
},
127127
"updated_field": {
128-
"description": "Object consisting of the ID of the project field to update and the new value for the field. To clear the field, set value to null. Example: {\"id\": 123456, \"value\": \"New Value\"}. Required for 'update_project_item' method.",
128+
"description": "Object describing the field to update and its new value. Required for 'update_project_item'. Two shapes are accepted: (1) by ID — {\"id\": 123456, \"value\": \"...\"}; (2) by name — {\"name\": \"Status\", \"value\": \"In Progress\"}. For single-select fields, the value may be the option name (resolved server-side) or the option ID. Set value to null to clear the field.",
129129
"type": "object"
130130
}
131131
},

0 commit comments

Comments
 (0)