Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Add updating draft state toupdate_pull_request tool#774

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
mattdholloway merged 6 commits intomainfrommark-pr-ready-for-review-2
Jul 29, 2025
Merged
Show file tree
Hide file tree
Changes fromall 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
1 change: 1 addition & 0 deletionsREADME.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -736,6 +736,7 @@ The following sets of tools are available (all are on by default):
- **update_pull_request** - Edit pull request
- `base`: New base branch name (string, optional)
- `body`: New description (string, optional)
- `draft`: Mark pull request as draft (true) or ready for review (false) (boolean, optional)
- `maintainer_can_modify`: Allow maintainer edits (boolean, optional)
- `owner`: Repository owner (string, required)
- `pullNumber`: Pull request number to update (number, required)
Expand Down
4 changes: 4 additions & 0 deletionspkg/github/__toolsnaps__/update_pull_request.snap
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,6 +14,10 @@
"description": "New description",
"type": "string"
},
"draft": {
"description": "Mark pull request as draft (true) or ready for review (false)",
"type": "boolean"
},
"maintainer_can_modify": {
"description": "Allow maintainer edits",
"type": "boolean"
Expand Down
146 changes: 120 additions & 26 deletionspkg/github/pullrequests.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -203,7 +203,7 @@ func CreatePullRequest(getClient GetClientFn, t translations.TranslationHelperFu
}

// UpdatePullRequest creates a tool to update an existing pull request.
func UpdatePullRequest(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) {
func UpdatePullRequest(getClient GetClientFn,getGQLClient GetGQLClientFn,t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) {
return mcp.NewTool("update_pull_request",
mcp.WithDescription(t("TOOL_UPDATE_PULL_REQUEST_DESCRIPTION", "Update an existing pull request in a GitHub repository.")),
mcp.WithToolAnnotation(mcp.ToolAnnotation{
Expand DownExpand Up@@ -232,6 +232,9 @@ func UpdatePullRequest(getClient GetClientFn, t translations.TranslationHelperFu
mcp.Description("New state"),
mcp.Enum("open", "closed"),
),
mcp.WithBoolean("draft",
mcp.Description("Mark pull request as draft (true) or ready for review (false)"),
),
mcp.WithString("base",
mcp.Description("New base branch name"),
),
Expand All@@ -253,74 +256,165 @@ func UpdatePullRequest(getClient GetClientFn, t translations.TranslationHelperFu
return mcp.NewToolResultError(err.Error()), nil
}

// Build the update struct only with provided fields
draftProvided := request.GetArguments()["draft"] != nil
var draftValue bool
if draftProvided {
draftValue, err = OptionalParam[bool](request, "draft")
if err != nil {
return nil, err
}
}

update := &github.PullRequest{}
updateNeeded := false
restUpdateNeeded := false

if title, ok, err := OptionalParamOK[string](request, "title"); err != nil {
return mcp.NewToolResultError(err.Error()), nil
} else if ok {
update.Title = github.Ptr(title)
updateNeeded = true
restUpdateNeeded = true
}

if body, ok, err := OptionalParamOK[string](request, "body"); err != nil {
return mcp.NewToolResultError(err.Error()), nil
} else if ok {
update.Body = github.Ptr(body)
updateNeeded = true
restUpdateNeeded = true
}

if state, ok, err := OptionalParamOK[string](request, "state"); err != nil {
return mcp.NewToolResultError(err.Error()), nil
} else if ok {
update.State = github.Ptr(state)
updateNeeded = true
restUpdateNeeded = true
}

if base, ok, err := OptionalParamOK[string](request, "base"); err != nil {
return mcp.NewToolResultError(err.Error()), nil
} else if ok {
update.Base = &github.PullRequestBranch{Ref: github.Ptr(base)}
updateNeeded = true
restUpdateNeeded = true
}

if maintainerCanModify, ok, err := OptionalParamOK[bool](request, "maintainer_can_modify"); err != nil {
return mcp.NewToolResultError(err.Error()), nil
} else if ok {
update.MaintainerCanModify = github.Ptr(maintainerCanModify)
updateNeeded = true
restUpdateNeeded = true
}

if !updateNeeded {
if !restUpdateNeeded && !draftProvided {
return mcp.NewToolResultError("No update parameters provided."), nil
}

if restUpdateNeeded {
client, err := getClient(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
}

_, resp, err := client.PullRequests.Edit(ctx, owner, repo, pullNumber, update)
if err != nil {
return ghErrors.NewGitHubAPIErrorResponse(ctx,
"failed to update pull request",
resp,
err,
), nil
}
defer func() { _ = resp.Body.Close() }()

if resp.StatusCode != http.StatusOK {
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}
return mcp.NewToolResultError(fmt.Sprintf("failed to update pull request: %s", string(body))), nil
}
}

if draftProvided {
gqlClient, err := getGQLClient(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get GitHub GraphQL client: %w", err)
}

var prQuery struct {
Repository struct {
PullRequest struct {
ID githubv4.ID
IsDraft githubv4.Boolean
} `graphql:"pullRequest(number: $prNum)"`
} `graphql:"repository(owner: $owner, name: $repo)"`
}

err = gqlClient.Query(ctx, &prQuery, map[string]interface{}{
"owner": githubv4.String(owner),
"repo": githubv4.String(repo),
"prNum": githubv4.Int(pullNumber), // #nosec G115 - pull request numbers are always small positive integers
})
if err != nil {
return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "Failed to find pull request", err), nil
}

currentIsDraft := bool(prQuery.Repository.PullRequest.IsDraft)

if currentIsDraft != draftValue {
if draftValue {
// Convert to draft
var mutation struct {
ConvertPullRequestToDraft struct {
PullRequest struct {
ID githubv4.ID
IsDraft githubv4.Boolean
}
} `graphql:"convertPullRequestToDraft(input: $input)"`
}

err = gqlClient.Mutate(ctx, &mutation, githubv4.ConvertPullRequestToDraftInput{
PullRequestID: prQuery.Repository.PullRequest.ID,
}, nil)
if err != nil {
return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "Failed to convert pull request to draft", err), nil
}
} else {
// Mark as ready for review
var mutation struct {
MarkPullRequestReadyForReview struct {
PullRequest struct {
ID githubv4.ID
IsDraft githubv4.Boolean
}
} `graphql:"markPullRequestReadyForReview(input: $input)"`
}

err = gqlClient.Mutate(ctx, &mutation, githubv4.MarkPullRequestReadyForReviewInput{
PullRequestID: prQuery.Repository.PullRequest.ID,
}, nil)
if err != nil {
return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "Failed to mark pull request ready for review", err), nil
}
}
}
}

client, err := getClient(ctx)
if err != nil {
return nil,fmt.Errorf("failed to get GitHub client: %w",err)
return nil, err

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The REST client is retrieved twice in this function - once for the conditional update and again at the end. Consider reusing the client instance or restructuring the code to avoid duplicate client retrieval.

Suggested change
returnnil,err
finalPR,resp,err:=client.REST.PullRequests.Get(ctx,owner,repo,pullNumber)
iferr!=nil {
returnghErrors.NewGitHubAPIErrorResponse(ctx,"Failed to get pull request",resp,err),nil

Copilot uses AI. Check for mistakes.
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The REST client is retrieved twice in this function (lines 311 and 400). Consider retrieving it once at the beginning and reusing it to improve efficiency and reduce duplication.

Suggested change
}
// Reuse the client retrieved earlier

Copilot uses AI. Check for mistakes.
pr, resp, err := client.PullRequests.Edit(ctx, owner, repo, pullNumber, update)

finalPR, resp, err := client.PullRequests.Get(ctx, owner, repo, pullNumber)
if err != nil {
return ghErrors.NewGitHubAPIErrorResponse(ctx,
"failed to update pull request",
resp,
err,
), nil
return ghErrors.NewGitHubAPIErrorResponse(ctx, "Failed to get pull request", resp, err), nil
}
defer func() { _ = resp.Body.Close() }()

if resp.StatusCode != http.StatusOK {
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
defer func() {
if resp != nil && resp.Body != nil {
_ = resp.Body.Close()
}
return mcp.NewToolResultError(fmt.Sprintf("failed to update pull request: %s", string(body))), nil
}
}()

r, err := json.Marshal(pr)
r, err := json.Marshal(finalPR)
if err != nil {
returnnil,fmt.Errorf("failed to marshal response: %w", err)
returnmcp.NewToolResultError(fmt.Sprintf("Failed to marshal response: %v", err)), nil
}

return mcp.NewToolResultText(string(r)), nil
Expand Down
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp