Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1034,7 +1034,8 @@ The following sets of tools are available:
- **Required OAuth Scopes**: `read:project`
- **Accepted OAuth Scopes**: `project`, `read:project`
- `field_id`: The field's ID. Required for 'get_project_field' method. (number, optional)
- `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)
- `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)
- `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)
- `item_id`: The item's ID. Required for 'get_project_item' method. (number, optional)
- `method`: The method to execute (string, required)
- `owner`: The owner (user or organization login). The name is not case sensitive. (string, optional)
Expand All @@ -1047,7 +1048,8 @@ The following sets of tools are available:
- **Accepted OAuth Scopes**: `project`, `read:project`
- `after`: Forward pagination cursor from previous pageInfo.nextCursor. (string, optional)
- `before`: Backward pagination cursor from previous pageInfo.prevCursor (rare). (string, optional)
- `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)
- `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)
- `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)
- `method`: The action to perform (string, required)
- `owner`: The owner (user or organization login). The name is not case sensitive. (string, required)
- `owner_type`: Owner type (user or org). If not provided, will automatically try both. (string, optional)
Expand All @@ -1059,10 +1061,10 @@ The following sets of tools are available:
- **Required OAuth Scopes**: `project`
- `body`: The body of the status update (markdown). Used for 'create_project_status_update' method. (string, optional)
- `field_name`: The name of the iteration field (e.g. 'Sprint'). Required for 'create_iteration_field' method. (string, optional)
- `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)
- `item_id`: The project item ID. Required for 'update_project_item' and 'delete_project_item' methods. (number, optional)
- `item_owner`: The owner (user or organization) of the repository containing the issue or pull request. Required for 'add_project_item' method. (string, optional)
- `item_repo`: The name of the repository containing the issue or pull request. Required for 'add_project_item' method. (string, optional)
- `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)
- `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)
- `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)
- `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)
- `item_type`: The item's type, either issue or pull_request. Required for 'add_project_item' method. (string, optional)
- `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)
- `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)
Expand All @@ -1075,7 +1077,7 @@ The following sets of tools are available:
- `status`: The status of the project. Used for 'create_project_status_update' method. (string, optional)
- `target_date`: The target date of the status update in YYYY-MM-DD format. Used for 'create_project_status_update' method. (string, optional)
- `title`: The project title. Required for 'create_project' method. (string, optional)
- `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)
- `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)

</details>

Expand Down
46 changes: 46 additions & 0 deletions pkg/errors/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package errors

import (
"context"
"encoding/json"
stderrors "errors"
"fmt"
"net/http"
Expand Down Expand Up @@ -218,3 +219,48 @@ func NewGitHubAPIStatusErrorResponse(ctx context.Context, message string, resp *
err := fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
return NewGitHubAPIErrorResponse(ctx, message, resp, err)
}

// StructuredResolutionError is a machine-readable error returned by name-resolution
// helpers (e.g. resolving a project field or single-select option by name). Agents
// can parse the JSON body to self-correct without re-prompting.
//
// Kind values:
// - "field_not_found" — no project field matches the supplied name
// - "field_ambiguous" — more than one project field shares the supplied name
// - "option_not_found" — no option on the resolved single-select field matches
// - "option_ambiguous" — duplicate option names on the resolved field
// - "item_not_in_project" — the issue/PR exists but is not an item on the project
// - "wrong_field_type" — the named field is not the data type the caller expected
type StructuredResolutionError struct {
Kind string `json:"error"`
Name string `json:"name,omitempty"`
Field string `json:"field,omitempty"`
Candidates []any `json:"candidates,omitempty"`
Hint string `json:"hint,omitempty"`
}

// Error implements the error interface; the message is the JSON body so that the
// downstream tool result also carries the structured payload as plain text.
func (e *StructuredResolutionError) Error() string {
b, err := json.Marshal(e)
if err != nil {
return fmt.Sprintf(`{"error":%q,"name":%q}`, e.Kind, e.Name)
}
return string(b)
}

// NewStructuredResolutionError constructs a StructuredResolutionError.
func NewStructuredResolutionError(kind, name, hint string, candidates []any) *StructuredResolutionError {
return &StructuredResolutionError{
Kind: kind,
Name: name,
Hint: hint,
Candidates: candidates,
}
}

// NewStructuredResolutionErrorResponse returns an mcp.CallToolResult whose text body
// is the JSON-serialised StructuredResolutionError, suitable for agent self-correction.
func NewStructuredResolutionErrorResponse(err *StructuredResolutionError) *mcp.CallToolResult {
return utils.NewToolResultError(err.Error())
}
9 changes: 8 additions & 1 deletion pkg/github/__toolsnaps__/projects_get.snap
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@
"description": "The field's ID. Required for 'get_project_field' method.",
"type": "number"
},
"field_names": {
"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.",
"items": {
"type": "string"
},
"type": "array"
},
"fields": {
"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.",
"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.",
"items": {
"type": "string"
},
Expand Down
9 changes: 8 additions & 1 deletion pkg/github/__toolsnaps__/projects_list.snap
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,15 @@
"description": "Backward pagination cursor from previous pageInfo.prevCursor (rare).",
"type": "string"
},
"field_names": {
"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.",
"items": {
"type": "string"
},
"type": "array"
},
"fields": {
"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.",
"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.",
"items": {
"type": "string"
},
Expand Down
10 changes: 5 additions & 5 deletions pkg/github/__toolsnaps__/projects_write.snap
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@
"type": "string"
},
"issue_number": {
"description": "The issue number (use when item_type is 'issue' for 'add_project_item' method). Provide either issue_number or pull_request_number.",
"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).",
"type": "number"
},
"item_id": {
"description": "The project item ID. Required for 'update_project_item' and 'delete_project_item' methods.",
"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.",
"type": "number"
},
"item_owner": {
"description": "The owner (user or organization) of the repository containing the issue or pull request. Required for 'add_project_item' method.",
"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.",
"type": "string"
},
"item_repo": {
"description": "The name of the repository containing the issue or pull request. Required for 'add_project_item' method.",
"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.",
"type": "string"
},
"item_type": {
Expand Down Expand Up @@ -125,7 +125,7 @@
"type": "string"
},
"updated_field": {
"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.",
"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.",
"type": "object"
}
},
Expand Down
Loading
Loading