@@ -1793,6 +1793,11 @@ func issueWriteHasNonFormParams(args map[string]any) bool {
17931793 return false
17941794}
17951795
1796+ func issueWriteHasNonNilParam (args map [string ]any , key string ) bool {
1797+ value , ok := args [key ]
1798+ return ok && value != nil
1799+ }
1800+
17961801// IssueWrite is the FeatureFlagIssueFields-enabled variant of issue_write
17971802// (with the issue_fields parameter). LegacyIssueWrite is served when the flag
17981803// is off. Both register under the tool name "issue_write"; exactly one is
@@ -1972,12 +1977,14 @@ Options are:
19721977 if err != nil {
19731978 return utils .NewToolResultError (err .Error ()), nil , nil
19741979 }
1980+ assigneesProvided := issueWriteHasNonNilParam (args , "assignees" )
19751981
19761982 // Get labels
19771983 labels , err := OptionalStringArrayParam (args , "labels" )
19781984 if err != nil {
19791985 return utils .NewToolResultError (err .Error ()), nil , nil
19801986 }
1987+ labelsProvided := issueWriteHasNonNilParam (args , "labels" )
19811988
19821989 // Get optional milestone
19831990 milestone , err := OptionalIntParam (args , "milestone" )
@@ -2049,7 +2056,10 @@ Options are:
20492056 if err != nil {
20502057 return utils .NewToolResultError (err .Error ()), nil , nil
20512058 }
2052- result , err := UpdateIssue (ctx , client , gqlClient , owner , repo , issueNumber , title , body , assignees , labels , milestoneNum , issueType , issueFieldValues , fieldIDsToDelete , state , stateReason , duplicateOf )
2059+ result , err := UpdateIssue (ctx , client , gqlClient , owner , repo , issueNumber , title , body , assignees , labels , milestoneNum , issueType , issueFieldValues , fieldIDsToDelete , state , stateReason , duplicateOf , UpdateIssueOptions {
2060+ AssigneesProvided : assigneesProvided ,
2061+ LabelsProvided : labelsProvided ,
2062+ })
20532063 return result , nil , err
20542064 default :
20552065 return utils .NewToolResultError ("invalid method, must be either 'create' or 'update'" ), nil , nil
@@ -2204,12 +2214,14 @@ Options are:
22042214 if err != nil {
22052215 return utils .NewToolResultError (err .Error ()), nil , nil
22062216 }
2217+ assigneesProvided := issueWriteHasNonNilParam (args , "assignees" )
22072218
22082219 // Get labels
22092220 labels , err := OptionalStringArrayParam (args , "labels" )
22102221 if err != nil {
22112222 return utils .NewToolResultError (err .Error ()), nil , nil
22122223 }
2224+ labelsProvided := issueWriteHasNonNilParam (args , "labels" )
22132225
22142226 // Get optional milestone
22152227 milestone , err := OptionalIntParam (args , "milestone" )
@@ -2266,7 +2278,10 @@ Options are:
22662278 if err != nil {
22672279 return utils .NewToolResultError (err .Error ()), nil , nil
22682280 }
2269- result , err := UpdateIssue (ctx , client , gqlClient , owner , repo , issueNumber , title , body , assignees , labels , milestoneNum , issueType , nil , nil , state , stateReason , duplicateOf )
2281+ result , err := UpdateIssue (ctx , client , gqlClient , owner , repo , issueNumber , title , body , assignees , labels , milestoneNum , issueType , nil , nil , state , stateReason , duplicateOf , UpdateIssueOptions {
2282+ AssigneesProvided : assigneesProvided ,
2283+ LabelsProvided : labelsProvided ,
2284+ })
22702285 return result , nil , err
22712286 default :
22722287 return utils .NewToolResultError ("invalid method, must be either 'create' or 'update'" ), nil , nil
@@ -2330,7 +2345,24 @@ func CreateIssue(ctx context.Context, client *github.Client, owner string, repo
23302345 return utils .NewToolResultText (string (r )), nil
23312346}
23322347
2333- func UpdateIssue (ctx context.Context , client * github.Client , gqlClient * githubv4.Client , owner string , repo string , issueNumber int , title string , body string , assignees []string , labels []string , milestoneNum int , issueType string , issueFieldValues []* github.IssueRequestFieldValue , fieldIDsToDelete []int64 , state string , stateReason string , duplicateOf int ) (* mcp.CallToolResult , error ) {
2348+ // UpdateIssueOptions controls which optional fields are included in an issue update request.
2349+ type UpdateIssueOptions struct {
2350+ // AssigneesProvided sends the assignees field even when the slice is empty.
2351+ AssigneesProvided bool
2352+ // LabelsProvided sends the labels field even when the slice is empty.
2353+ LabelsProvided bool
2354+ }
2355+
2356+ func UpdateIssue (ctx context.Context , client * github.Client , gqlClient * githubv4.Client , owner string , repo string , issueNumber int , title string , body string , assignees []string , labels []string , milestoneNum int , issueType string , issueFieldValues []* github.IssueRequestFieldValue , fieldIDsToDelete []int64 , state string , stateReason string , duplicateOf int , opts ... UpdateIssueOptions ) (* mcp.CallToolResult , error ) {
2357+ updateOptions := UpdateIssueOptions {
2358+ AssigneesProvided : len (assignees ) > 0 ,
2359+ LabelsProvided : len (labels ) > 0 ,
2360+ }
2361+ for _ , opt := range opts {
2362+ updateOptions .AssigneesProvided = updateOptions .AssigneesProvided || opt .AssigneesProvided
2363+ updateOptions .LabelsProvided = updateOptions .LabelsProvided || opt .LabelsProvided
2364+ }
2365+
23342366 // Create the issue request with only provided fields
23352367 issueRequest := & github.IssueRequest {}
23362368
@@ -2343,11 +2375,11 @@ func UpdateIssue(ctx context.Context, client *github.Client, gqlClient *githubv4
23432375 issueRequest .Body = github .Ptr (body )
23442376 }
23452377
2346- if len ( labels ) > 0 {
2378+ if updateOptions . LabelsProvided {
23472379 issueRequest .Labels = & labels
23482380 }
23492381
2350- if len ( assignees ) > 0 {
2382+ if updateOptions . AssigneesProvided {
23512383 issueRequest .Assignees = & assignees
23522384 }
23532385
0 commit comments