Skip to content

Commit f5f644c

Browse files
committed
Document mixed issue type clear routing
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea8faa5c-7f26-4e2d-bf9c-6f0b5f173e8c
1 parent 45cac89 commit f5f644c

2 files changed

Lines changed: 88 additions & 0 deletions

File tree

pkg/github/issues.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2050,6 +2050,8 @@ var issueWriteFormParams = map[string]struct{}{
20502050
func shouldIssueWriteDeferToForm(ctx context.Context, deps ToolDependencies, req *mcp.CallToolRequest, args map[string]any) bool {
20512051
issueType, issueTypeProvided, err := OptionalParamOK[string](args, "type")
20522052
explicitTypeClear := err == nil && issueTypeProvided && issueType == "" && args["method"] == "update"
2053+
// The form cannot preserve an empty type, so execute the complete call
2054+
// directly to avoid dropping the clear or any co-submitted values.
20532055
return !explicitTypeClear && shouldDeferToForm(ctx, deps, req, args, issueWriteFormParams)
20542056
}
20552057

pkg/github/issues_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2939,6 +2939,92 @@ func TestIssueWriteClearTypeBypassesMCPAppForm(t *testing.T) {
29392939
require.Contains(t, textContent.Text, "https://github.com/owner/repo/issues/123")
29402940
}
29412941

2942+
func TestIssueWriteClearTypeBypassesMCPAppFormWithStateChange(t *testing.T) {
2943+
var gotRequestBody []byte
2944+
var readErr error
2945+
client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
2946+
PatchReposIssuesByOwnerByRepoByIssueNumber: func(w http.ResponseWriter, r *http.Request) {
2947+
gotRequestBody, readErr = io.ReadAll(r.Body)
2948+
w.WriteHeader(http.StatusOK)
2949+
_, _ = w.Write([]byte(`{"number":123,"html_url":"https://github.com/owner/repo/issues/123"}`))
2950+
},
2951+
}))
2952+
issueID := githubv4.ID("I_kwDOA0xdyM50BPaO")
2953+
stateReason := IssueClosedStateReasonCompleted
2954+
gqlClient := githubv4.NewClient(githubv4mock.NewMockedHTTPClient(
2955+
githubv4mock.NewQueryMatcher(
2956+
struct {
2957+
Repository struct {
2958+
Issue struct {
2959+
ID githubv4.ID
2960+
} `graphql:"issue(number: $issueNumber)"`
2961+
} `graphql:"repository(owner: $owner, name: $repo)"`
2962+
}{},
2963+
map[string]any{
2964+
"owner": githubv4.String("owner"),
2965+
"repo": githubv4.String("repo"),
2966+
"issueNumber": githubv4.Int(123),
2967+
},
2968+
githubv4mock.DataResponse(map[string]any{
2969+
"repository": map[string]any{
2970+
"issue": map[string]any{"id": issueID},
2971+
},
2972+
}),
2973+
),
2974+
githubv4mock.NewMutationMatcher(
2975+
struct {
2976+
CloseIssue struct {
2977+
Issue struct {
2978+
ID githubv4.ID
2979+
Number githubv4.Int
2980+
URL githubv4.String
2981+
State githubv4.String
2982+
}
2983+
} `graphql:"closeIssue(input: $input)"`
2984+
}{},
2985+
CloseIssueInput{
2986+
IssueID: issueID,
2987+
StateReason: &stateReason,
2988+
},
2989+
nil,
2990+
githubv4mock.DataResponse(map[string]any{
2991+
"closeIssue": map[string]any{
2992+
"issue": map[string]any{
2993+
"id": issueID,
2994+
"number": 123,
2995+
"url": "https://github.com/owner/repo/issues/123",
2996+
"state": "CLOSED",
2997+
},
2998+
},
2999+
}),
3000+
),
3001+
))
3002+
deps := BaseDeps{
3003+
Client: client,
3004+
GQLClient: gqlClient,
3005+
featureChecker: featureCheckerFor(MCPAppsFeatureFlag),
3006+
}
3007+
serverTool := IssueWrite(translations.NullTranslationHelper)
3008+
handler := serverTool.Handler(deps)
3009+
request := createMCPRequestWithSession(t, ClientNameVSCodeInsiders, true, map[string]any{
3010+
"method": "update",
3011+
"owner": "owner",
3012+
"repo": "repo",
3013+
"issue_number": float64(123),
3014+
"type": "",
3015+
"state": "closed",
3016+
"state_reason": "completed",
3017+
})
3018+
3019+
result, err := handler(ContextWithDeps(context.Background(), deps), &request)
3020+
require.NoError(t, err)
3021+
require.False(t, result.IsError)
3022+
require.NoError(t, readErr)
3023+
require.JSONEq(t, `{"type":null}`, string(gotRequestBody))
3024+
textContent := getTextResult(t, result)
3025+
require.Contains(t, textContent.Text, "https://github.com/owner/repo/issues/123")
3026+
}
3027+
29423028
func Test_UpdateIssue(t *testing.T) {
29433029
// Verify tool definition
29443030
serverTool := IssueWrite(translations.NullTranslationHelper)

0 commit comments

Comments
 (0)