diff --git a/pkg/github/issues.go b/pkg/github/issues.go index 9d51aeb50..1e65a4961 100644 --- a/pkg/github/issues.go +++ b/pkg/github/issues.go @@ -240,6 +240,9 @@ func CreateIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (t mcp.WithNumber("milestone", mcp.Description("Milestone number"), ), + mcp.WithString("type", + mcp.Description("Type of this issue"), + ), ), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { owner, err := RequiredParam[string](request, "owner") @@ -284,6 +287,12 @@ func CreateIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (t milestoneNum = &milestone } + // Get optional type + issueType, err := OptionalParam[string](request, "type") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil + } + // Create the issue request issueRequest := &github.IssueRequest{ Title: github.Ptr(title), @@ -291,6 +300,7 @@ func CreateIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (t Assignees: &assignees, Labels: &labels, Milestone: milestoneNum, + Type: &issueType, } client, err := getClient(ctx) @@ -491,6 +501,9 @@ func UpdateIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (t mcp.WithNumber("milestone", mcp.Description("New milestone number"), ), + mcp.WithString("type", + mcp.Description("New issue type"), + ), ), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { owner, err := RequiredParam[string](request, "owner") @@ -561,6 +574,15 @@ func UpdateIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (t issueRequest.Milestone = &milestoneNum } + // Get issue type + issueType, err := OptionalParam[string](request, "type") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil + } + if issueType != "" { + issueRequest.Type = github.Ptr(issueType) + } + client, err := getClient(ctx) if err != nil { return nil, fmt.Errorf("failed to get GitHub client: %w", err) diff --git a/pkg/github/issues_test.go b/pkg/github/issues_test.go index a6facbe2f..9d37525b1 100644 --- a/pkg/github/issues_test.go +++ b/pkg/github/issues_test.go @@ -480,6 +480,7 @@ func Test_CreateIssue(t *testing.T) { assert.Contains(t, tool.InputSchema.Properties, "assignees") assert.Contains(t, tool.InputSchema.Properties, "labels") assert.Contains(t, tool.InputSchema.Properties, "milestone") + assert.Contains(t, tool.InputSchema.Properties, "type") assert.ElementsMatch(t, tool.InputSchema.Required, []string{"owner", "repo", "title"}) // Setup mock issue for success case @@ -492,6 +493,7 @@ func Test_CreateIssue(t *testing.T) { Assignees: []*github.User{{Login: github.Ptr("user1")}, {Login: github.Ptr("user2")}}, Labels: []*github.Label{{Name: github.Ptr("bug")}, {Name: github.Ptr("help wanted")}}, Milestone: &github.Milestone{Number: github.Ptr(5)}, + Type: &github.IssueType{Name: github.Ptr("Bug")}, } tests := []struct { @@ -513,6 +515,7 @@ func Test_CreateIssue(t *testing.T) { "labels": []any{"bug", "help wanted"}, "assignees": []any{"user1", "user2"}, "milestone": float64(5), + "type": "Bug", }).andThen( mockResponse(t, http.StatusCreated, mockIssue), ), @@ -526,6 +529,7 @@ func Test_CreateIssue(t *testing.T) { "assignees": []any{"user1", "user2"}, "labels": []any{"bug", "help wanted"}, "milestone": float64(5), + "type": "Bug", }, expectError: false, expectedIssue: mockIssue, @@ -621,6 +625,10 @@ func Test_CreateIssue(t *testing.T) { assert.Equal(t, *tc.expectedIssue.Body, *returnedIssue.Body) } + if tc.expectedIssue.Type != nil { + assert.Equal(t, *tc.expectedIssue.Type.Name, *returnedIssue.Type.Name) + } + // Check assignees if expected if len(tc.expectedIssue.Assignees) > 0 { assert.Equal(t, len(tc.expectedIssue.Assignees), len(returnedIssue.Assignees)) @@ -834,6 +842,7 @@ func Test_UpdateIssue(t *testing.T) { assert.Contains(t, tool.InputSchema.Properties, "labels") assert.Contains(t, tool.InputSchema.Properties, "assignees") assert.Contains(t, tool.InputSchema.Properties, "milestone") + assert.Contains(t, tool.InputSchema.Properties, "type") assert.ElementsMatch(t, tool.InputSchema.Required, []string{"owner", "repo", "issue_number"}) // Setup mock issue for success case @@ -846,6 +855,7 @@ func Test_UpdateIssue(t *testing.T) { Assignees: []*github.User{{Login: github.Ptr("assignee1")}, {Login: github.Ptr("assignee2")}}, Labels: []*github.Label{{Name: github.Ptr("bug")}, {Name: github.Ptr("priority")}}, Milestone: &github.Milestone{Number: github.Ptr(5)}, + Type: &github.IssueType{Name: github.Ptr("Bug")}, } tests := []struct { @@ -868,6 +878,7 @@ func Test_UpdateIssue(t *testing.T) { "labels": []any{"bug", "priority"}, "assignees": []any{"assignee1", "assignee2"}, "milestone": float64(5), + "type": "Bug", }).andThen( mockResponse(t, http.StatusOK, mockIssue), ), @@ -883,6 +894,7 @@ func Test_UpdateIssue(t *testing.T) { "labels": []any{"bug", "priority"}, "assignees": []any{"assignee1", "assignee2"}, "milestone": float64(5), + "type": "Bug", }, expectError: false, expectedIssue: mockIssue, @@ -894,9 +906,10 @@ func Test_UpdateIssue(t *testing.T) { mock.PatchReposIssuesByOwnerByRepoByIssueNumber, mockResponse(t, http.StatusOK, &github.Issue{ Number: github.Ptr(123), - Title: github.Ptr("Only Title Updated"), + Title: github.Ptr("Updated Issue Title"), HTMLURL: github.Ptr("https://github.com/owner/repo/issues/123"), State: github.Ptr("open"), + Type: &github.IssueType{Name: github.Ptr("Feature")}, }), ), ), @@ -904,14 +917,16 @@ func Test_UpdateIssue(t *testing.T) { "owner": "owner", "repo": "repo", "issue_number": float64(123), - "title": "Only Title Updated", + "title": "Updated Issue Title", + "type": "Feature", }, expectError: false, expectedIssue: &github.Issue{ Number: github.Ptr(123), - Title: github.Ptr("Only Title Updated"), + Title: github.Ptr("Updated Issue Title"), HTMLURL: github.Ptr("https://github.com/owner/repo/issues/123"), State: github.Ptr("open"), + Type: &github.IssueType{Name: github.Ptr("Feature")}, }, }, { @@ -1000,6 +1015,10 @@ func Test_UpdateIssue(t *testing.T) { assert.Equal(t, *tc.expectedIssue.Body, *returnedIssue.Body) } + if tc.expectedIssue.Type != nil { + assert.Equal(t, *tc.expectedIssue.Type.Name, *returnedIssue.Type.Name) + } + // Check assignees if expected if len(tc.expectedIssue.Assignees) > 0 { assert.Len(t, returnedIssue.Assignees, len(tc.expectedIssue.Assignees))