diff --git a/pkg/github/pullrequests.go b/pkg/github/pullrequests.go index 69af69af7..60ae15a0a 100644 --- a/pkg/github/pullrequests.go +++ b/pkg/github/pullrequests.go @@ -1,5 +1,3 @@ -//go:build ignore - package github import ( @@ -9,28 +7,32 @@ import ( "io" "net/http" - "github.com/go-viper/mapstructure/v2" "github.com/google/go-github/v79/github" - "github.com/mark3labs/mcp-go/mcp" - "github.com/mark3labs/mcp-go/server" + "github.com/google/jsonschema-go/jsonschema" + "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" ghErrors "github.com/github/github-mcp-server/pkg/errors" "github.com/github/github-mcp-server/pkg/sanitize" "github.com/github/github-mcp-server/pkg/translations" + "github.com/github/github-mcp-server/pkg/utils" ) // GetPullRequest creates a tool to get details of a specific pull request. -func PullRequestRead(getClient GetClientFn, t translations.TranslationHelperFunc, flags FeatureFlags) (mcp.Tool, server.ToolHandlerFunc) { - return mcp.NewTool("pull_request_read", - mcp.WithDescription(t("TOOL_PULL_REQUEST_READ_DESCRIPTION", "Get information on a specific pull request in GitHub repository.")), - mcp.WithToolAnnotation(mcp.ToolAnnotation{ - Title: t("TOOL_GET_PULL_REQUEST_USER_TITLE", "Get details for a single pull request"), - ReadOnlyHint: ToBoolPtr(true), - }), - mcp.WithString("method", - mcp.Required(), - mcp.Description(`Action to specify what pull request data needs to be retrieved from GitHub. +func PullRequestRead(getClient GetClientFn, t translations.TranslationHelperFunc, flags FeatureFlags) (mcp.Tool, mcp.ToolHandlerFor[map[string]any, any]) { + tool := mcp.Tool{ + Name: "pull_request_read", + Description: t("TOOL_PULL_REQUEST_READ_DESCRIPTION", "Get information on a specific pull request in GitHub repository."), + Annotations: &mcp.ToolAnnotations{ + Title: t("TOOL_GET_PULL_REQUEST_USER_TITLE", "Get details for a single pull request"), + ReadOnlyHint: true, + }, + InputSchema: WithPagination(&jsonschema.Schema{ + Type: "object", + Properties: map[string]*jsonschema.Schema{ + "method": { + Type: "string", + Description: `Action to specify what pull request data needs to be retrieved from GitHub. Possible options: 1. get - Get details of a specific pull request. 2. get_diff - Get the diff of a pull request. @@ -38,73 +40,83 @@ Possible options: 4. get_files - Get the list of files changed in a pull request. Use with pagination parameters to control the number of results returned. 5. get_review_comments - Get the review comments on a pull request. They are comments made on a portion of the unified diff during a pull request review. Use with pagination parameters to control the number of results returned. 6. get_reviews - Get the reviews on a pull request. When asked for review comments, use get_review_comments method. - 7. get_comments - Get comments on a pull request. Use this if user doesn't specifically want review comments. Use with pagination parameters to control the number of results returned. -`), +`, + Enum: []any{"get", "get_diff", "get_status", "get_files", "get_review_comments", "get_reviews"}, + }, + "owner": { + Type: "string", + Description: "Repository owner", + }, + "repo": { + Type: "string", + Description: "Repository name", + }, + "pullNumber": { + Type: "number", + Description: "Pull request number", + }, + }, + Required: []string{"method", "owner", "repo", "pullNumber"}, + }), + } - mcp.Enum("get", "get_diff", "get_status", "get_files", "get_review_comments", "get_reviews", "get_comments"), - ), - mcp.WithString("owner", - mcp.Required(), - mcp.Description("Repository owner"), - ), - mcp.WithString("repo", - mcp.Required(), - mcp.Description("Repository name"), - ), - mcp.WithNumber("pullNumber", - mcp.Required(), - mcp.Description("Pull request number"), - ), - WithPagination(), - ), - func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - method, err := RequiredParam[string](request, "method") + handler := mcp.ToolHandlerFor[map[string]any, any]( + func(ctx context.Context, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { + method, err := RequiredParam[string](args, "method") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - owner, err := RequiredParam[string](request, "owner") + owner, err := RequiredParam[string](args, "owner") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - repo, err := RequiredParam[string](request, "repo") + repo, err := RequiredParam[string](args, "repo") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - pullNumber, err := RequiredInt(request, "pullNumber") + pullNumber, err := RequiredInt(args, "pullNumber") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - pagination, err := OptionalPaginationParams(request) + pagination, err := OptionalPaginationParams(args) if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } client, err := getClient(ctx) if err != nil { - return nil, fmt.Errorf("failed to get GitHub client: %w", err) + return nil, nil, fmt.Errorf("failed to get GitHub client: %w", err) } - switch method { + var result *mcp.CallToolResult + var handlerErr error + switch method { case "get": - return GetPullRequest(ctx, client, owner, repo, pullNumber) + result, handlerErr = GetPullRequest(ctx, client, owner, repo, pullNumber) case "get_diff": - return GetPullRequestDiff(ctx, client, owner, repo, pullNumber) + result, handlerErr = GetPullRequestDiff(ctx, client, owner, repo, pullNumber) case "get_status": - return GetPullRequestStatus(ctx, client, owner, repo, pullNumber) + result, handlerErr = GetPullRequestStatus(ctx, client, owner, repo, pullNumber) case "get_files": - return GetPullRequestFiles(ctx, client, owner, repo, pullNumber, pagination) + result, handlerErr = GetPullRequestFiles(ctx, client, owner, repo, pullNumber, pagination) case "get_review_comments": - return GetPullRequestReviewComments(ctx, client, owner, repo, pullNumber, pagination) + result, handlerErr = GetPullRequestReviewComments(ctx, client, owner, repo, pullNumber, pagination) case "get_reviews": - return GetPullRequestReviews(ctx, client, owner, repo, pullNumber) - case "get_comments": - return GetIssueComments(ctx, client, owner, repo, pullNumber, pagination, flags) + result, handlerErr = GetPullRequestReviews(ctx, client, owner, repo, pullNumber) + // TODO: Uncomment once issues.go is migrated + // case "get_comments": + // result, handlerErr = GetIssueComments(ctx, client, owner, repo, pullNumber, pagination, flags) default: - return nil, fmt.Errorf("unknown method: %s", method) + return nil, nil, fmt.Errorf("unknown method: %s", method) } - } + + return result, nil, handlerErr + }, + ) + + return tool, handler } func GetPullRequest(ctx context.Context, client *github.Client, owner, repo string, pullNumber int) (*mcp.CallToolResult, error) { @@ -123,7 +135,7 @@ func GetPullRequest(ctx context.Context, client *github.Client, owner, repo stri if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } - return mcp.NewToolResultError(fmt.Sprintf("failed to get pull request: %s", string(body))), nil + return utils.NewToolResultError(fmt.Sprintf("failed to get pull request: %s", string(body))), nil } // sanitize title/body on response @@ -141,7 +153,7 @@ func GetPullRequest(ctx context.Context, client *github.Client, owner, repo stri return nil, fmt.Errorf("failed to marshal response: %w", err) } - return mcp.NewToolResultText(string(r)), nil + return utils.NewToolResultText(string(r)), nil } func GetPullRequestDiff(ctx context.Context, client *github.Client, owner, repo string, pullNumber int) (*mcp.CallToolResult, error) { @@ -165,13 +177,13 @@ func GetPullRequestDiff(ctx context.Context, client *github.Client, owner, repo if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } - return mcp.NewToolResultError(fmt.Sprintf("failed to get pull request diff: %s", string(body))), nil + return utils.NewToolResultError(fmt.Sprintf("failed to get pull request diff: %s", string(body))), nil } defer func() { _ = resp.Body.Close() }() // Return the raw response - return mcp.NewToolResultText(string(raw)), nil + return utils.NewToolResultText(string(raw)), nil } func GetPullRequestStatus(ctx context.Context, client *github.Client, owner, repo string, pullNumber int) (*mcp.CallToolResult, error) { @@ -190,7 +202,7 @@ func GetPullRequestStatus(ctx context.Context, client *github.Client, owner, rep if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } - return mcp.NewToolResultError(fmt.Sprintf("failed to get pull request: %s", string(body))), nil + return utils.NewToolResultError(fmt.Sprintf("failed to get pull request: %s", string(body))), nil } // Get combined status for the head SHA @@ -209,7 +221,7 @@ func GetPullRequestStatus(ctx context.Context, client *github.Client, owner, rep if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } - return mcp.NewToolResultError(fmt.Sprintf("failed to get combined status: %s", string(body))), nil + return utils.NewToolResultError(fmt.Sprintf("failed to get combined status: %s", string(body))), nil } r, err := json.Marshal(status) @@ -217,7 +229,7 @@ func GetPullRequestStatus(ctx context.Context, client *github.Client, owner, rep return nil, fmt.Errorf("failed to marshal response: %w", err) } - return mcp.NewToolResultText(string(r)), nil + return utils.NewToolResultText(string(r)), nil } func GetPullRequestFiles(ctx context.Context, client *github.Client, owner, repo string, pullNumber int, pagination PaginationParams) (*mcp.CallToolResult, error) { @@ -240,7 +252,7 @@ func GetPullRequestFiles(ctx context.Context, client *github.Client, owner, repo if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } - return mcp.NewToolResultError(fmt.Sprintf("failed to get pull request files: %s", string(body))), nil + return utils.NewToolResultError(fmt.Sprintf("failed to get pull request files: %s", string(body))), nil } r, err := json.Marshal(files) @@ -248,7 +260,7 @@ func GetPullRequestFiles(ctx context.Context, client *github.Client, owner, repo return nil, fmt.Errorf("failed to marshal response: %w", err) } - return mcp.NewToolResultText(string(r)), nil + return utils.NewToolResultText(string(r)), nil } func GetPullRequestReviewComments(ctx context.Context, client *github.Client, owner, repo string, pullNumber int, pagination PaginationParams) (*mcp.CallToolResult, error) { @@ -274,7 +286,7 @@ func GetPullRequestReviewComments(ctx context.Context, client *github.Client, ow if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } - return mcp.NewToolResultError(fmt.Sprintf("failed to get pull request review comments: %s", string(body))), nil + return utils.NewToolResultError(fmt.Sprintf("failed to get pull request review comments: %s", string(body))), nil } r, err := json.Marshal(comments) @@ -282,7 +294,7 @@ func GetPullRequestReviewComments(ctx context.Context, client *github.Client, ow return nil, fmt.Errorf("failed to marshal response: %w", err) } - return mcp.NewToolResultText(string(r)), nil + return utils.NewToolResultText(string(r)), nil } func GetPullRequestReviews(ctx context.Context, client *github.Client, owner, repo string, pullNumber int) (*mcp.CallToolResult, error) { @@ -301,7 +313,7 @@ func GetPullRequestReviews(ctx context.Context, client *github.Client, owner, re if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } - return mcp.NewToolResultError(fmt.Sprintf("failed to get pull request reviews: %s", string(body))), nil + return utils.NewToolResultError(fmt.Sprintf("failed to get pull request reviews: %s", string(body))), nil } r, err := json.Marshal(reviews) @@ -309,82 +321,94 @@ func GetPullRequestReviews(ctx context.Context, client *github.Client, owner, re return nil, fmt.Errorf("failed to marshal response: %w", err) } - return mcp.NewToolResultText(string(r)), nil + return utils.NewToolResultText(string(r)), nil } // CreatePullRequest creates a tool to create a new pull request. -func CreatePullRequest(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) { - return mcp.NewTool("create_pull_request", - mcp.WithDescription(t("TOOL_CREATE_PULL_REQUEST_DESCRIPTION", "Create a new pull request in a GitHub repository.")), - mcp.WithToolAnnotation(mcp.ToolAnnotation{ - Title: t("TOOL_CREATE_PULL_REQUEST_USER_TITLE", "Open new pull request"), - ReadOnlyHint: ToBoolPtr(false), - }), - mcp.WithString("owner", - mcp.Required(), - mcp.Description("Repository owner"), - ), - mcp.WithString("repo", - mcp.Required(), - mcp.Description("Repository name"), - ), - mcp.WithString("title", - mcp.Required(), - mcp.Description("PR title"), - ), - mcp.WithString("body", - mcp.Description("PR description"), - ), - mcp.WithString("head", - mcp.Required(), - mcp.Description("Branch containing changes"), - ), - mcp.WithString("base", - mcp.Required(), - mcp.Description("Branch to merge into"), - ), - mcp.WithBoolean("draft", - mcp.Description("Create as draft PR"), - ), - mcp.WithBoolean("maintainer_can_modify", - mcp.Description("Allow maintainer edits"), - ), - ), - func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - owner, err := RequiredParam[string](request, "owner") +func CreatePullRequest(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Tool, mcp.ToolHandlerFor[map[string]any, any]) { + tool := mcp.Tool{ + Name: "create_pull_request", + Description: t("TOOL_CREATE_PULL_REQUEST_DESCRIPTION", "Create a new pull request in a GitHub repository."), + Annotations: &mcp.ToolAnnotations{ + Title: t("TOOL_CREATE_PULL_REQUEST_USER_TITLE", "Open new pull request"), + ReadOnlyHint: false, + }, + InputSchema: &jsonschema.Schema{ + Type: "object", + Properties: map[string]*jsonschema.Schema{ + "owner": { + Type: "string", + Description: "Repository owner", + }, + "repo": { + Type: "string", + Description: "Repository name", + }, + "title": { + Type: "string", + Description: "PR title", + }, + "body": { + Type: "string", + Description: "PR description", + }, + "head": { + Type: "string", + Description: "Branch containing changes", + }, + "base": { + Type: "string", + Description: "Branch to merge into", + }, + "draft": { + Type: "boolean", + Description: "Create as draft PR", + }, + "maintainer_can_modify": { + Type: "boolean", + Description: "Allow maintainer edits", + }, + }, + Required: []string{"owner", "repo", "title", "head", "base"}, + }, + } + + handler := mcp.ToolHandlerFor[map[string]any, any]( + func(ctx context.Context, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { + owner, err := RequiredParam[string](args, "owner") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - repo, err := RequiredParam[string](request, "repo") + repo, err := RequiredParam[string](args, "repo") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - title, err := RequiredParam[string](request, "title") + title, err := RequiredParam[string](args, "title") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - head, err := RequiredParam[string](request, "head") + head, err := RequiredParam[string](args, "head") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - base, err := RequiredParam[string](request, "base") + base, err := RequiredParam[string](args, "base") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - body, err := OptionalParam[string](request, "body") + body, err := OptionalParam[string](args, "body") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - draft, err := OptionalParam[bool](request, "draft") + draft, err := OptionalParam[bool](args, "draft") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - maintainerCanModify, err := OptionalParam[bool](request, "maintainer_can_modify") + maintainerCanModify, err := OptionalParam[bool](args, "maintainer_can_modify") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } newPR := &github.NewPullRequest{ @@ -402,7 +426,7 @@ func CreatePullRequest(getClient GetClientFn, t translations.TranslationHelperFu client, err := getClient(ctx) if err != nil { - return nil, fmt.Errorf("failed to get GitHub client: %w", err) + return nil, nil, fmt.Errorf("failed to get GitHub client: %w", err) } pr, resp, err := client.PullRequests.Create(ctx, owner, repo, newPR) if err != nil { @@ -410,16 +434,16 @@ func CreatePullRequest(getClient GetClientFn, t translations.TranslationHelperFu "failed to create pull request", resp, err, - ), nil + ), nil, nil } defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusCreated { body, err := io.ReadAll(resp.Body) if err != nil { - return nil, fmt.Errorf("failed to read response body: %w", err) + return nil, nil, fmt.Errorf("failed to read response body: %w", err) } - return mcp.NewToolResultError(fmt.Sprintf("failed to create pull request: %s", string(body))), nil + return utils.NewToolResultError(fmt.Sprintf("failed to create pull request: %s", string(body))), nil, nil } // Return minimal response with just essential information @@ -430,15 +454,20 @@ func CreatePullRequest(getClient GetClientFn, t translations.TranslationHelperFu r, err := json.Marshal(minimalResponse) if err != nil { - return nil, fmt.Errorf("failed to marshal response: %w", err) + return nil, nil, fmt.Errorf("failed to marshal response: %w", err) } - return mcp.NewToolResultText(string(r)), nil - } + return utils.NewToolResultText(string(r)), nil, nil + }, + ) + + return tool, handler } // UpdatePullRequest creates a tool to update an existing pull request. -func UpdatePullRequest(getClient GetClientFn, getGQLClient GetGQLClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) { +// TODO: MIGRATE THIS TOOL +/* +func UpdatePullRequest(getClient GetClientFn, getGQLClient GetGQLClientFn, t translations.TranslationHelperFunc) (mcp.Tool, mcp.ToolHandlerFor[map[string]any, any]) { 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{ @@ -483,27 +512,27 @@ func UpdatePullRequest(getClient GetClientFn, getGQLClient GetGQLClientFn, t tra }), ), ), - func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - owner, err := RequiredParam[string](request, "owner") + func(ctx context.Context, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { + owner, err := RequiredParam[string](args, "owner") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - repo, err := RequiredParam[string](request, "repo") + repo, err := RequiredParam[string](args, "repo") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - pullNumber, err := RequiredInt(request, "pullNumber") + pullNumber, err := RequiredInt(args, "pullNumber") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } // Check if draft parameter is provided - draftProvided := request.GetArguments()["draft"] != nil + draftProvided := args["draft"] != nil var draftValue bool if draftProvided { - draftValue, err = OptionalParam[bool](request, "draft") + draftValue, err = OptionalParam[bool](args, "draft") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } } @@ -511,50 +540,50 @@ func UpdatePullRequest(getClient GetClientFn, getGQLClient GetGQLClientFn, t tra update := &github.PullRequest{} restUpdateNeeded := false - if title, ok, err := OptionalParamOK[string](request, "title"); err != nil { - return mcp.NewToolResultError(err.Error()), nil + if title, ok, err := OptionalParamOK[string](args, "title"); err != nil { + return utils.NewToolResultError(err.Error()), nil, nil } else if ok { update.Title = github.Ptr(title) restUpdateNeeded = true } - if body, ok, err := OptionalParamOK[string](request, "body"); err != nil { - return mcp.NewToolResultError(err.Error()), nil + if body, ok, err := OptionalParamOK[string](args, "body"); err != nil { + return utils.NewToolResultError(err.Error()), nil, nil } else if ok { update.Body = github.Ptr(body) restUpdateNeeded = true } - if state, ok, err := OptionalParamOK[string](request, "state"); err != nil { - return mcp.NewToolResultError(err.Error()), nil + if state, ok, err := OptionalParamOK[string](args, "state"); err != nil { + return utils.NewToolResultError(err.Error()), nil, nil } else if ok { update.State = github.Ptr(state) restUpdateNeeded = true } - if base, ok, err := OptionalParamOK[string](request, "base"); err != nil { - return mcp.NewToolResultError(err.Error()), nil + if base, ok, err := OptionalParamOK[string](args, "base"); err != nil { + return utils.NewToolResultError(err.Error()), nil, nil } else if ok { update.Base = &github.PullRequestBranch{Ref: github.Ptr(base)} restUpdateNeeded = true } - if maintainerCanModify, ok, err := OptionalParamOK[bool](request, "maintainer_can_modify"); err != nil { - return mcp.NewToolResultError(err.Error()), nil + if maintainerCanModify, ok, err := OptionalParamOK[bool](args, "maintainer_can_modify"); err != nil { + return utils.NewToolResultError(err.Error()), nil, nil } else if ok { update.MaintainerCanModify = github.Ptr(maintainerCanModify) restUpdateNeeded = true } // Handle reviewers separately - reviewers, err := OptionalStringArrayParam(request, "reviewers") + reviewers, err := OptionalStringArrayParam(args, "reviewers") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } // If no updates, no draft change, and no reviewers, return error early if !restUpdateNeeded && !draftProvided && len(reviewers) == 0 { - return mcp.NewToolResultError("No update parameters provided."), nil + return utils.NewToolResultError("No update parameters provided."), nil, nil } // Handle REST API updates (title, body, state, base, maintainer_can_modify) @@ -570,7 +599,7 @@ func UpdatePullRequest(getClient GetClientFn, getGQLClient GetGQLClientFn, t tra "failed to update pull request", resp, err, - ), nil + ), nil, nil } defer func() { _ = resp.Body.Close() }() @@ -579,7 +608,7 @@ func UpdatePullRequest(getClient GetClientFn, getGQLClient GetGQLClientFn, t tra 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 + return utils.NewToolResultError(fmt.Sprintf("failed to update pull request: %s", string(body))), nil, nil } } @@ -605,7 +634,7 @@ func UpdatePullRequest(getClient GetClientFn, getGQLClient GetGQLClientFn, t tra "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 + return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "Failed to find pull request", err), nil, nil } currentIsDraft := bool(prQuery.Repository.PullRequest.IsDraft) @@ -626,7 +655,7 @@ func UpdatePullRequest(getClient GetClientFn, getGQLClient GetGQLClientFn, t tra PullRequestID: prQuery.Repository.PullRequest.ID, }, nil) if err != nil { - return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "Failed to convert pull request to draft", err), nil + return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "Failed to convert pull request to draft", err), nil, nil } } else { // Mark as ready for review @@ -643,7 +672,7 @@ func UpdatePullRequest(getClient GetClientFn, getGQLClient GetGQLClientFn, t tra PullRequestID: prQuery.Repository.PullRequest.ID, }, nil) if err != nil { - return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "Failed to mark pull request ready for review", err), nil + return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "Failed to mark pull request ready for review", err), nil, nil } } } @@ -666,7 +695,7 @@ func UpdatePullRequest(getClient GetClientFn, getGQLClient GetGQLClientFn, t tra "failed to request reviewers", resp, err, - ), nil + ), nil, nil } defer func() { if resp != nil && resp.Body != nil { @@ -679,7 +708,7 @@ func UpdatePullRequest(getClient GetClientFn, getGQLClient GetGQLClientFn, t tra if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } - return mcp.NewToolResultError(fmt.Sprintf("failed to request reviewers: %s", string(body))), nil + return utils.NewToolResultError(fmt.Sprintf("failed to request reviewers: %s", string(body))), nil, nil } } @@ -691,7 +720,7 @@ func UpdatePullRequest(getClient GetClientFn, getGQLClient GetGQLClientFn, t tra finalPR, resp, err := client.PullRequests.Get(ctx, owner, repo, pullNumber) if err != nil { - return ghErrors.NewGitHubAPIErrorResponse(ctx, "Failed to get pull request", resp, err), nil + return ghErrors.NewGitHubAPIErrorResponse(ctx, "Failed to get pull request", resp, err), nil, nil } defer func() { if resp != nil && resp.Body != nil { @@ -707,15 +736,19 @@ func UpdatePullRequest(getClient GetClientFn, getGQLClient GetGQLClientFn, t tra r, err := json.Marshal(minimalResponse) if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to marshal response: %v", err)), nil + return utils.NewToolResultError(fmt.Sprintf("Failed to marshal response: %v", err)), nil, nil } - return mcp.NewToolResultText(string(r)), nil + return utils.NewToolResultText(string(r)), nil, nil } } +*/ + // ListPullRequests creates a tool to list and filter repository pull requests. -func ListPullRequests(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) { +// TODO: MIGRATE THIS TOOL +/* +func ListPullRequests(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Tool, mcp.ToolHandlerFor[map[string]any, any]) { return mcp.NewTool("list_pull_requests", mcp.WithDescription(t("TOOL_LIST_PULL_REQUESTS_DESCRIPTION", "List pull requests in a GitHub repository. If the user specifies an author, then DO NOT use this tool and use the search_pull_requests tool instead.")), mcp.WithToolAnnotation(mcp.ToolAnnotation{ @@ -750,38 +783,38 @@ func ListPullRequests(getClient GetClientFn, t translations.TranslationHelperFun ), WithPagination(), ), - func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - owner, err := RequiredParam[string](request, "owner") + func(ctx context.Context, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { + owner, err := RequiredParam[string](args, "owner") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - repo, err := RequiredParam[string](request, "repo") + repo, err := RequiredParam[string](args, "repo") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - state, err := OptionalParam[string](request, "state") + state, err := OptionalParam[string](args, "state") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - head, err := OptionalParam[string](request, "head") + head, err := OptionalParam[string](args, "head") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - base, err := OptionalParam[string](request, "base") + base, err := OptionalParam[string](args, "base") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - sort, err := OptionalParam[string](request, "sort") + sort, err := OptionalParam[string](args, "sort") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - direction, err := OptionalParam[string](request, "direction") + direction, err := OptionalParam[string](args, "direction") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - pagination, err := OptionalPaginationParams(request) + pagination, err := OptionalPaginationParams(args) if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } opts := &github.PullRequestListOptions{ State: state, @@ -805,7 +838,7 @@ func ListPullRequests(getClient GetClientFn, t translations.TranslationHelperFun "failed to list pull requests", resp, err, - ), nil + ), nil, nil } defer func() { _ = resp.Body.Close() }() @@ -814,7 +847,7 @@ func ListPullRequests(getClient GetClientFn, t translations.TranslationHelperFun if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } - return mcp.NewToolResultError(fmt.Sprintf("failed to list pull requests: %s", string(body))), nil + return utils.NewToolResultError(fmt.Sprintf("failed to list pull requests: %s", string(body))), nil, nil } // sanitize title/body on each PR @@ -835,12 +868,16 @@ func ListPullRequests(getClient GetClientFn, t translations.TranslationHelperFun return nil, fmt.Errorf("failed to marshal response: %w", err) } - return mcp.NewToolResultText(string(r)), nil + return utils.NewToolResultText(string(r)), nil, nil } } +*/ + // MergePullRequest creates a tool to merge a pull request. -func MergePullRequest(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) { +// TODO: MIGRATE THIS TOOL +/* +func MergePullRequest(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Tool, mcp.ToolHandlerFor[map[string]any, any]) { return mcp.NewTool("merge_pull_request", mcp.WithDescription(t("TOOL_MERGE_PULL_REQUEST_DESCRIPTION", "Merge a pull request in a GitHub repository.")), mcp.WithToolAnnotation(mcp.ToolAnnotation{ @@ -870,30 +907,30 @@ func MergePullRequest(getClient GetClientFn, t translations.TranslationHelperFun mcp.Enum("merge", "squash", "rebase"), ), ), - func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - owner, err := RequiredParam[string](request, "owner") + func(ctx context.Context, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { + owner, err := RequiredParam[string](args, "owner") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - repo, err := RequiredParam[string](request, "repo") + repo, err := RequiredParam[string](args, "repo") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - pullNumber, err := RequiredInt(request, "pullNumber") + pullNumber, err := RequiredInt(args, "pullNumber") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - commitTitle, err := OptionalParam[string](request, "commit_title") + commitTitle, err := OptionalParam[string](args, "commit_title") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - commitMessage, err := OptionalParam[string](request, "commit_message") + commitMessage, err := OptionalParam[string](args, "commit_message") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - mergeMethod, err := OptionalParam[string](request, "merge_method") + mergeMethod, err := OptionalParam[string](args, "merge_method") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } options := &github.PullRequestOptions{ @@ -911,7 +948,7 @@ func MergePullRequest(getClient GetClientFn, t translations.TranslationHelperFun "failed to merge pull request", resp, err, - ), nil + ), nil, nil } defer func() { _ = resp.Body.Close() }() @@ -920,7 +957,7 @@ func MergePullRequest(getClient GetClientFn, t translations.TranslationHelperFun if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } - return mcp.NewToolResultError(fmt.Sprintf("failed to merge pull request: %s", string(body))), nil + return utils.NewToolResultError(fmt.Sprintf("failed to merge pull request: %s", string(body))), nil, nil } r, err := json.Marshal(result) @@ -928,11 +965,15 @@ func MergePullRequest(getClient GetClientFn, t translations.TranslationHelperFun return nil, fmt.Errorf("failed to marshal response: %w", err) } - return mcp.NewToolResultText(string(r)), nil + return utils.NewToolResultText(string(r)), nil, nil } } +*/ + // SearchPullRequests creates a tool to search for pull requests. +// TODO: MIGRATE THIS TOOL +/* func SearchPullRequests(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("search_pull_requests", mcp.WithDescription(t("TOOL_SEARCH_PULL_REQUESTS_DESCRIPTION", "Search for pull requests in GitHub repositories using issues search syntax already scoped to is:pr")), @@ -972,13 +1013,17 @@ func SearchPullRequests(getClient GetClientFn, t translations.TranslationHelperF ), WithPagination(), ), - func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + func(ctx context.Context, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { return searchHandler(ctx, getClient, request, "pr", "failed to search pull requests") } } +*/ + // UpdatePullRequestBranch creates a tool to update a pull request branch with the latest changes from the base branch. -func UpdatePullRequestBranch(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) { +// TODO: MIGRATE THIS TOOL +/* +func UpdatePullRequestBranch(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Tool, mcp.ToolHandlerFor[map[string]any, any]) { return mcp.NewTool("update_pull_request_branch", mcp.WithDescription(t("TOOL_UPDATE_PULL_REQUEST_BRANCH_DESCRIPTION", "Update the branch of a pull request with the latest changes from the base branch.")), mcp.WithToolAnnotation(mcp.ToolAnnotation{ @@ -1001,22 +1046,22 @@ func UpdatePullRequestBranch(getClient GetClientFn, t translations.TranslationHe mcp.Description("The expected SHA of the pull request's HEAD ref"), ), ), - func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - owner, err := RequiredParam[string](request, "owner") + func(ctx context.Context, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { + owner, err := RequiredParam[string](args, "owner") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - repo, err := RequiredParam[string](request, "repo") + repo, err := RequiredParam[string](args, "repo") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - pullNumber, err := RequiredInt(request, "pullNumber") + pullNumber, err := RequiredInt(args, "pullNumber") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - expectedHeadSHA, err := OptionalParam[string](request, "expectedHeadSha") + expectedHeadSHA, err := OptionalParam[string](args, "expectedHeadSha") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } opts := &github.PullRequestBranchUpdateOptions{} if expectedHeadSHA != "" { @@ -1032,13 +1077,13 @@ func UpdatePullRequestBranch(getClient GetClientFn, t translations.TranslationHe // Check if it's an acceptedError. An acceptedError indicates that the update is in progress, // and it's not a real error. if resp != nil && resp.StatusCode == http.StatusAccepted && isAcceptedError(err) { - return mcp.NewToolResultText("Pull request branch update is in progress"), nil + return utils.NewToolResultText("Pull request branch update is in progress"), nil, nil } return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to update pull request branch", resp, err, - ), nil + ), nil, nil } defer func() { _ = resp.Body.Close() }() @@ -1047,7 +1092,7 @@ func UpdatePullRequestBranch(getClient GetClientFn, t translations.TranslationHe if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } - return mcp.NewToolResultError(fmt.Sprintf("failed to update pull request branch: %s", string(body))), nil + return utils.NewToolResultError(fmt.Sprintf("failed to update pull request branch: %s", string(body))), nil, nil } r, err := json.Marshal(result) @@ -1055,9 +1100,11 @@ func UpdatePullRequestBranch(getClient GetClientFn, t translations.TranslationHe return nil, fmt.Errorf("failed to marshal response: %w", err) } - return mcp.NewToolResultText(string(r)), nil + return utils.NewToolResultText(string(r)), nil, nil } } +*/ + type PullRequestReviewWriteParams struct { Method string @@ -1069,7 +1116,9 @@ type PullRequestReviewWriteParams struct { CommitID *string } -func PullRequestReviewWrite(getGQLClient GetGQLClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) { +// TODO: MIGRATE THIS TOOL +/* +func PullRequestReviewWrite(getGQLClient GetGQLClientFn, t translations.TranslationHelperFunc) (mcp.Tool, mcp.ToolHandlerFor[map[string]any, any]) { return mcp.NewTool("pull_request_review_write", mcp.WithDescription(t("TOOL_PULL_REQUEST_REVIEW_WRITE_DESCRIPTION", `Create and/or submit, delete review of a pull request. @@ -1113,16 +1162,16 @@ Available methods: mcp.Description("SHA of commit to review"), ), ), - func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + func(ctx context.Context, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { var params PullRequestReviewWriteParams if err := mapstructure.Decode(request.Params.Arguments, ¶ms); err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } // Given our owner, repo and PR number, lookup the GQL ID of the PR. client, err := getGQLClient(ctx) if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("failed to get GitHub GQL client: %v", err)), nil + return utils.NewToolResultError(fmt.Sprintf("failed to get GitHub GQL client: %v", err)), nil, nil } switch params.Method { @@ -1133,10 +1182,12 @@ Available methods: case "delete_pending": return DeletePendingPullRequestReview(ctx, client, params) default: - return mcp.NewToolResultError(fmt.Sprintf("unknown method: %s", params.Method)), nil + return utils.NewToolResultError(fmt.Sprintf("unknown method: %s", params.Method)), nil, nil } } } +*/ + func CreatePullRequestReview(ctx context.Context, client *githubv4.Client, params PullRequestReviewWriteParams) (*mcp.CallToolResult, error) { var getPullRequestQuery struct { @@ -1184,16 +1235,16 @@ func CreatePullRequestReview(ctx context.Context, client *githubv4.Client, param addPullRequestReviewInput, nil, ); err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil } // Return nothing interesting, just indicate success for the time being. // In future, we may want to return the review ID, but for the moment, we're not leaking // API implementation details to the LLM. if params.Event == "" { - return mcp.NewToolResultText("pending pull request created"), nil + return utils.NewToolResultText("pending pull request created"), nil } - return mcp.NewToolResultText("pull request review submitted successfully"), nil + return utils.NewToolResultText("pull request review submitted successfully"), nil } func SubmitPendingPullRequestReview(ctx context.Context, client *githubv4.Client, params PullRequestReviewWriteParams) (*mcp.CallToolResult, error) { @@ -1241,13 +1292,13 @@ func SubmitPendingPullRequestReview(ctx context.Context, client *githubv4.Client // Validate there is one review and the state is pending if len(getLatestReviewForViewerQuery.Repository.PullRequest.Reviews.Nodes) == 0 { - return mcp.NewToolResultError("No pending review found for the viewer"), nil + return utils.NewToolResultError("No pending review found for the viewer"), nil } review := getLatestReviewForViewerQuery.Repository.PullRequest.Reviews.Nodes[0] if review.State != githubv4.PullRequestReviewStatePending { errText := fmt.Sprintf("The latest review, found at %s is not pending", review.URL) - return mcp.NewToolResultError(errText), nil + return utils.NewToolResultError(errText), nil } // Prepare the mutation @@ -1278,7 +1329,7 @@ func SubmitPendingPullRequestReview(ctx context.Context, client *githubv4.Client // Return nothing interesting, just indicate success for the time being. // In future, we may want to return the review ID, but for the moment, we're not leaking // API implementation details to the LLM. - return mcp.NewToolResultText("pending pull request review successfully submitted"), nil + return utils.NewToolResultText("pending pull request review successfully submitted"), nil } func DeletePendingPullRequestReview(ctx context.Context, client *githubv4.Client, params PullRequestReviewWriteParams) (*mcp.CallToolResult, error) { @@ -1326,13 +1377,13 @@ func DeletePendingPullRequestReview(ctx context.Context, client *githubv4.Client // Validate there is one review and the state is pending if len(getLatestReviewForViewerQuery.Repository.PullRequest.Reviews.Nodes) == 0 { - return mcp.NewToolResultError("No pending review found for the viewer"), nil + return utils.NewToolResultError("No pending review found for the viewer"), nil } review := getLatestReviewForViewerQuery.Repository.PullRequest.Reviews.Nodes[0] if review.State != githubv4.PullRequestReviewStatePending { errText := fmt.Sprintf("The latest review, found at %s is not pending", review.URL) - return mcp.NewToolResultError(errText), nil + return utils.NewToolResultError(errText), nil } // Prepare the mutation @@ -1352,17 +1403,19 @@ func DeletePendingPullRequestReview(ctx context.Context, client *githubv4.Client }, nil, ); err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil } // Return nothing interesting, just indicate success for the time being. // In future, we may want to return the review ID, but for the moment, we're not leaking // API implementation details to the LLM. - return mcp.NewToolResultText("pending pull request review successfully deleted"), nil + return utils.NewToolResultText("pending pull request review successfully deleted"), nil } // AddCommentToPendingReview creates a tool to add a comment to a pull request review. -func AddCommentToPendingReview(getGQLClient GetGQLClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) { +// TODO: MIGRATE THIS TOOL +/* +func AddCommentToPendingReview(getGQLClient GetGQLClientFn, t translations.TranslationHelperFunc) (mcp.Tool, mcp.ToolHandlerFor[map[string]any, any]) { return mcp.NewTool("add_comment_to_pending_review", mcp.WithDescription(t("TOOL_ADD_COMMENT_TO_PENDING_REVIEW_DESCRIPTION", "Add review comment to the requester's latest pending pull request review. A pending review needs to already exist to call this (check with the user if not sure).")), mcp.WithToolAnnotation(mcp.ToolAnnotation{ @@ -1418,7 +1471,7 @@ func AddCommentToPendingReview(getGQLClient GetGQLClientFn, t translations.Trans mcp.Enum("LEFT", "RIGHT"), ), ), - func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + func(ctx context.Context, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { var params struct { Owner string Repo string @@ -1432,7 +1485,7 @@ func AddCommentToPendingReview(getGQLClient GetGQLClientFn, t translations.Trans StartSide *string } if err := mapstructure.Decode(request.Params.Arguments, ¶ms); err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } client, err := getGQLClient(ctx) @@ -1451,7 +1504,7 @@ func AddCommentToPendingReview(getGQLClient GetGQLClientFn, t translations.Trans return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "failed to get current user", err, - ), nil + ), nil, nil } var getLatestReviewForViewerQuery struct { @@ -1479,18 +1532,18 @@ func AddCommentToPendingReview(getGQLClient GetGQLClientFn, t translations.Trans return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "failed to get latest review for current user", err, - ), nil + ), nil, nil } // Validate there is one review and the state is pending if len(getLatestReviewForViewerQuery.Repository.PullRequest.Reviews.Nodes) == 0 { - return mcp.NewToolResultError("No pending review found for the viewer"), nil + return utils.NewToolResultError("No pending review found for the viewer"), nil, nil } review := getLatestReviewForViewerQuery.Repository.PullRequest.Reviews.Nodes[0] if review.State != githubv4.PullRequestReviewStatePending { errText := fmt.Sprintf("The latest review, found at %s is not pending", review.URL) - return mcp.NewToolResultError(errText), nil + return utils.NewToolResultError(errText), nil, nil } // Then we can create a new review thread comment on the review. @@ -1517,20 +1570,24 @@ func AddCommentToPendingReview(getGQLClient GetGQLClientFn, t translations.Trans }, nil, ); err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } // Return nothing interesting, just indicate success for the time being. // In future, we may want to return the review ID, but for the moment, we're not leaking // API implementation details to the LLM. - return mcp.NewToolResultText("pull request review comment successfully added to pending review"), nil + return utils.NewToolResultText("pull request review comment successfully added to pending review"), nil, nil } } +*/ + // RequestCopilotReview creates a tool to request a Copilot review for a pull request. // Note that this tool will not work on GHES where this feature is unsupported. In future, we should not expose this // tool if the configured host does not support it. -func RequestCopilotReview(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) { +// TODO: MIGRATE THIS TOOL +/* +func RequestCopilotReview(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Tool, mcp.ToolHandlerFor[map[string]any, any]) { return mcp.NewTool("request_copilot_review", mcp.WithDescription(t("TOOL_REQUEST_COPILOT_REVIEW_DESCRIPTION", "Request a GitHub Copilot code review for a pull request. Use this for automated feedback on pull requests, usually before requesting a human reviewer.")), mcp.WithToolAnnotation(mcp.ToolAnnotation{ @@ -1550,25 +1607,25 @@ func RequestCopilotReview(getClient GetClientFn, t translations.TranslationHelpe mcp.Description("Pull request number"), ), ), - func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - owner, err := RequiredParam[string](request, "owner") + func(ctx context.Context, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { + owner, err := RequiredParam[string](args, "owner") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - repo, err := RequiredParam[string](request, "repo") + repo, err := RequiredParam[string](args, "repo") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } - pullNumber, err := RequiredInt(request, "pullNumber") + pullNumber, err := RequiredInt(args, "pullNumber") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } client, err := getClient(ctx) if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil, nil } _, resp, err := client.PullRequests.RequestReviewers( @@ -1586,7 +1643,7 @@ func RequestCopilotReview(getClient GetClientFn, t translations.TranslationHelpe "failed to request copilot review", resp, err, - ), nil + ), nil, nil } defer func() { _ = resp.Body.Close() }() @@ -1595,13 +1652,15 @@ func RequestCopilotReview(getClient GetClientFn, t translations.TranslationHelpe if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } - return mcp.NewToolResultError(fmt.Sprintf("failed to request copilot review: %s", string(body))), nil + return utils.NewToolResultError(fmt.Sprintf("failed to request copilot review: %s", string(body))), nil, nil } // Return nothing on success, as there's not much value in returning the Pull Request itself - return mcp.NewToolResultText(""), nil + return utils.NewToolResultText(""), nil, nil } } +*/ + // newGQLString like takes something that approximates a string (of which there are many types in shurcooL/githubv4) // and constructs a pointer to it, or nil if the string is empty. This is extremely useful because when we parse diff --git a/pkg/github/pullrequests_test.go b/pkg/github/pullrequests_test.go index 02eaadf32..347bce672 100644 --- a/pkg/github/pullrequests_test.go +++ b/pkg/github/pullrequests_test.go @@ -1,5 +1,3 @@ -//go:build ignore - package github import ( diff --git a/pkg/github/search_utils.go b/pkg/github/search_utils.go index 4a710236b..0ecfea7b0 100644 --- a/pkg/github/search_utils.go +++ b/pkg/github/search_utils.go @@ -1,5 +1,3 @@ -//go:build ignore - package github import ( @@ -11,7 +9,8 @@ import ( "regexp" "github.com/google/go-github/v79/github" - "github.com/mark3labs/mcp-go/mcp" + "github.com/github/github-mcp-server/pkg/utils" + "github.com/modelcontextprotocol/go-sdk/mcp" ) func hasFilter(query, filterType string) bool { @@ -40,44 +39,44 @@ func hasTypeFilter(query string) bool { func searchHandler( ctx context.Context, getClient GetClientFn, - request mcp.CallToolRequest, + args map[string]any, searchType string, errorPrefix string, ) (*mcp.CallToolResult, error) { - query, err := RequiredParam[string](request, "query") + query, err := RequiredParam[string](args, "query") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil } if !hasSpecificFilter(query, "is", searchType) { query = fmt.Sprintf("is:%s %s", searchType, query) } - owner, err := OptionalParam[string](request, "owner") + owner, err := OptionalParam[string](args, "owner") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil } - repo, err := OptionalParam[string](request, "repo") + repo, err := OptionalParam[string](args, "repo") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil } if owner != "" && repo != "" && !hasRepoFilter(query) { query = fmt.Sprintf("repo:%s/%s %s", owner, repo, query) } - sort, err := OptionalParam[string](request, "sort") + sort, err := OptionalParam[string](args, "sort") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil } - order, err := OptionalParam[string](request, "order") + order, err := OptionalParam[string](args, "order") if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil } - pagination, err := OptionalPaginationParams(request) + pagination, err := OptionalPaginationParams(args) if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return utils.NewToolResultError(err.Error()), nil } opts := &github.SearchOptions{ @@ -105,7 +104,7 @@ func searchHandler( if err != nil { return nil, fmt.Errorf("%s: failed to read response body: %w", errorPrefix, err) } - return mcp.NewToolResultError(fmt.Sprintf("%s: %s", errorPrefix, string(body))), nil + return utils.NewToolResultError(fmt.Sprintf("%s: %s", errorPrefix, string(body))), nil } r, err := json.Marshal(result) @@ -113,5 +112,5 @@ func searchHandler( return nil, fmt.Errorf("%s: failed to marshal response: %w", errorPrefix, err) } - return mcp.NewToolResultText(string(r)), nil + return utils.NewToolResultText(string(r)), nil }