Files
Martin Høst Normark 60a5391d67 Add tools for sub-issue endpoint (#470)
* Create 'add sub-issue' tool

* Fix hardcoded API host

* Create 'list sub-issues' tool

* Create 'remove sub-issue' tool

* Fix Test_GetIssue mock data - add missing User field

The assertion was already checking User.Login but the mock was incomplete

* Create 'reprioritize sub-issue' tool

* fixes

* use go github pck to add sub-issues

* Update to use go github package

* update description

* update to use go github v73

* lint, docs

* refactor: tests to use go-github-mock

* add toolsnaps

* make RemoveSubIssue use NewGitHubAPIErrorResponse, update docstring

* Always include SHA in get_file_contents responses (#676)

* fix: Add SHA to get_file_contents while preserving MCP behavior (#595)

Enhance get_file_contents to include SHA information without changing
the existing MCP server response format.

Changes:
- Add Contents API call to retrieve SHA before fetching raw content
- Include SHA in resourceURI (repo://owner/repo/sha/{SHA}/contents/path)
- Add SHA to success messages
- Update tests to verify SHA inclusion
- Maintain original behavior: text files return raw text, binaries return base64

This preserves backward compatibility while providing SHA information
for better file versioning support.

Closes #595

* fix: Improve error handling for Contents API response

Ensure response body is properly closed even when an error occurs by moving
the defer statement before the error check. This prevents potential resource
leaks when the Contents API returns an error with a non-nil response.

Changes:
- Move defer respContents.Body.Close() before error checking
- Rename errContents to err for consistency
- Add nil check for respContents before attempting to close body

This follows Go best practices for handling HTTP responses and prevents
potential goroutine/memory leaks.

* revert changes to resource URI

* use GraphQL API to get file SHA

* refactor: mock GQL client instead of getFileSHA function to follow conventions

* lint

* revert GraphQL

---------

Co-authored-by: LuluBeatson <lulubeatson@github.com>

* Reorganize README, add dedicated install guides, include policies and governance info for the github server (#695)

* Refactor README and add host installation guides, governance docs

- Reorganized README for clarity and navigation
- Added dedicated installation guides for Claude, Cursor, Windsurf, JetBrains, and more
- Clarified contribution guidelines and approval criteria
- Added policies and governance documentation

* Update README.md

* Update README with configuration section for remote GitHub MCP Server

* Update MCP access policy description in README

Removing coding agent from the policy note, as the GitHub server is unaffected by this policy

* Update configuration steps for GitHub Copilot in JetBrains IDEs...

...to reflect changes in accessing settings and configuring MCP.

* Update install-other-copilot-ides.md

* Update Eclipse MCP support version and configuration steps...

...for GitHub Copilot plugin in installation guide.

* Update docs/installation-guides/install-cursor.md

* Update docs/installation-guides/install-windsurf.md

* Apply suggestion from @tonytrg

* Apply suggestion from @tonytrg

* Apply suggestion from @tonytrg

* Apply suggestion from @tonytrg

* Apply suggestion from @tonytrg

* Apply suggestion from @tonytrg

* Apply suggestion from @tonytrg

---------

Co-authored-by: Tony Truong <tonytrg@github.com>

* fix: shorten long tool name for adding pr review comments (#697)

* shorten tool name

* update function name to match tool name

* adjust wording of descriptions

* Update installation guide for GitHub MCP Server (#699)

* Update installation guide for GitHub MCP Server

Removed reference to GitHub.com in the installation guide. The GitHub server is available to Coding Agent by default, without installation needed.

* Rename section to 'Install in Other MCP Hosts'

Updating title for consistency and adding a link to the "other Copilot IDEs" install guide.

* Revise installation guide for Cursor MCP setup

Updated installation guide for Cursor with steps clarified, remote server installation, and one-click install deeplinks to open Cursor and add the github server to the config file.

* fix: make mcpcurl support "integer" type (#688)

- FYI:https://json-schema.org/understanding-json-schema/reference/numeric#integer

* Added installation instructions for mcpcurl (#719)

* Added installation instructions for mcpcurl

* Update cmd/mcpcurl/README.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Add pagination support to GraphQL-based tools (#683)

* initial pagination for `ListDiscussions`

* redo category id var cast

* add GraphQL pagination support for discussion comments and categories

* remove pageinfo returns

* fix out ref for linter

* update docs

* move to unified pagination for consensus on params

* update docs

* refactor pagination handling

* update docs

* linter fix

* conv rest to gql params for safe lint

* add nolint

* add error handling for perPage value in ToGraphQLParams

* refactor pagination error handling

* unified params for rest andn graphql and rennamed to be uniform for golang

* add 'after' for pagination

* update docs

* Update pkg/github/discussions.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update pkg/github/discussions.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update pkg/github/discussions_test.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* update default page size const

* reduce default pagination size from 100 to 30 in discussion tests

* update pagination for reverse and total

* update pagination to remove from discussions

* updated README

* improve the `ToGraphQLParams` function

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* shorten param descriptions

* fix: resp nil check in error handling in RemoveSubIssue function

---------

Co-authored-by: LuluBeatson <lulubeatson@github.com>
Co-authored-by: tommaso-moro <tommaso-moro@github.com>
2025-07-22 09:24:46 +01:00

210 lines
9.1 KiB
Go

package github
import (
"context"
"github.com/github/github-mcp-server/pkg/raw"
"github.com/github/github-mcp-server/pkg/toolsets"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v73/github"
"github.com/mark3labs/mcp-go/server"
"github.com/shurcooL/githubv4"
)
type GetClientFn func(context.Context) (*github.Client, error)
type GetGQLClientFn func(context.Context) (*githubv4.Client, error)
var DefaultTools = []string{"all"}
func DefaultToolsetGroup(readOnly bool, getClient GetClientFn, getGQLClient GetGQLClientFn, getRawClient raw.GetRawClientFn, t translations.TranslationHelperFunc) *toolsets.ToolsetGroup {
tsg := toolsets.NewToolsetGroup(readOnly)
// Define all available features with their default state (disabled)
// Create toolsets
repos := toolsets.NewToolset("repos", "GitHub Repository related tools").
AddReadTools(
toolsets.NewServerTool(SearchRepositories(getClient, t)),
toolsets.NewServerTool(GetFileContents(getClient, getRawClient, t)),
toolsets.NewServerTool(ListCommits(getClient, t)),
toolsets.NewServerTool(SearchCode(getClient, t)),
toolsets.NewServerTool(GetCommit(getClient, t)),
toolsets.NewServerTool(ListBranches(getClient, t)),
toolsets.NewServerTool(ListTags(getClient, t)),
toolsets.NewServerTool(GetTag(getClient, t)),
).
AddWriteTools(
toolsets.NewServerTool(CreateOrUpdateFile(getClient, t)),
toolsets.NewServerTool(CreateRepository(getClient, t)),
toolsets.NewServerTool(ForkRepository(getClient, t)),
toolsets.NewServerTool(CreateBranch(getClient, t)),
toolsets.NewServerTool(PushFiles(getClient, t)),
toolsets.NewServerTool(DeleteFile(getClient, t)),
).
AddResourceTemplates(
toolsets.NewServerResourceTemplate(GetRepositoryResourceContent(getClient, getRawClient, t)),
toolsets.NewServerResourceTemplate(GetRepositoryResourceBranchContent(getClient, getRawClient, t)),
toolsets.NewServerResourceTemplate(GetRepositoryResourceCommitContent(getClient, getRawClient, t)),
toolsets.NewServerResourceTemplate(GetRepositoryResourceTagContent(getClient, getRawClient, t)),
toolsets.NewServerResourceTemplate(GetRepositoryResourcePrContent(getClient, getRawClient, t)),
)
issues := toolsets.NewToolset("issues", "GitHub Issues related tools").
AddReadTools(
toolsets.NewServerTool(GetIssue(getClient, t)),
toolsets.NewServerTool(SearchIssues(getClient, t)),
toolsets.NewServerTool(ListIssues(getClient, t)),
toolsets.NewServerTool(GetIssueComments(getClient, t)),
toolsets.NewServerTool(ListSubIssues(getClient, t)),
).
AddWriteTools(
toolsets.NewServerTool(CreateIssue(getClient, t)),
toolsets.NewServerTool(AddIssueComment(getClient, t)),
toolsets.NewServerTool(UpdateIssue(getClient, t)),
toolsets.NewServerTool(AssignCopilotToIssue(getGQLClient, t)),
toolsets.NewServerTool(AddSubIssue(getClient, t)),
toolsets.NewServerTool(RemoveSubIssue(getClient, t)),
toolsets.NewServerTool(ReprioritizeSubIssue(getClient, t)),
).AddPrompts(toolsets.NewServerPrompt(AssignCodingAgentPrompt(t)))
users := toolsets.NewToolset("users", "GitHub User related tools").
AddReadTools(
toolsets.NewServerTool(SearchUsers(getClient, t)),
)
orgs := toolsets.NewToolset("orgs", "GitHub Organization related tools").
AddReadTools(
toolsets.NewServerTool(SearchOrgs(getClient, t)),
)
pullRequests := toolsets.NewToolset("pull_requests", "GitHub Pull Request related tools").
AddReadTools(
toolsets.NewServerTool(GetPullRequest(getClient, t)),
toolsets.NewServerTool(ListPullRequests(getClient, t)),
toolsets.NewServerTool(GetPullRequestFiles(getClient, t)),
toolsets.NewServerTool(SearchPullRequests(getClient, t)),
toolsets.NewServerTool(GetPullRequestStatus(getClient, t)),
toolsets.NewServerTool(GetPullRequestComments(getClient, t)),
toolsets.NewServerTool(GetPullRequestReviews(getClient, t)),
toolsets.NewServerTool(GetPullRequestDiff(getClient, t)),
).
AddWriteTools(
toolsets.NewServerTool(MergePullRequest(getClient, t)),
toolsets.NewServerTool(UpdatePullRequestBranch(getClient, t)),
toolsets.NewServerTool(CreatePullRequest(getClient, t)),
toolsets.NewServerTool(UpdatePullRequest(getClient, t)),
toolsets.NewServerTool(RequestCopilotReview(getClient, t)),
// Reviews
toolsets.NewServerTool(CreateAndSubmitPullRequestReview(getGQLClient, t)),
toolsets.NewServerTool(CreatePendingPullRequestReview(getGQLClient, t)),
toolsets.NewServerTool(AddCommentToPendingReview(getGQLClient, t)),
toolsets.NewServerTool(SubmitPendingPullRequestReview(getGQLClient, t)),
toolsets.NewServerTool(DeletePendingPullRequestReview(getGQLClient, t)),
)
codeSecurity := toolsets.NewToolset("code_security", "Code security related tools, such as GitHub Code Scanning").
AddReadTools(
toolsets.NewServerTool(GetCodeScanningAlert(getClient, t)),
toolsets.NewServerTool(ListCodeScanningAlerts(getClient, t)),
)
secretProtection := toolsets.NewToolset("secret_protection", "Secret protection related tools, such as GitHub Secret Scanning").
AddReadTools(
toolsets.NewServerTool(GetSecretScanningAlert(getClient, t)),
toolsets.NewServerTool(ListSecretScanningAlerts(getClient, t)),
)
dependabot := toolsets.NewToolset("dependabot", "Dependabot tools").
AddReadTools(
toolsets.NewServerTool(GetDependabotAlert(getClient, t)),
toolsets.NewServerTool(ListDependabotAlerts(getClient, t)),
)
notifications := toolsets.NewToolset("notifications", "GitHub Notifications related tools").
AddReadTools(
toolsets.NewServerTool(ListNotifications(getClient, t)),
toolsets.NewServerTool(GetNotificationDetails(getClient, t)),
).
AddWriteTools(
toolsets.NewServerTool(DismissNotification(getClient, t)),
toolsets.NewServerTool(MarkAllNotificationsRead(getClient, t)),
toolsets.NewServerTool(ManageNotificationSubscription(getClient, t)),
toolsets.NewServerTool(ManageRepositoryNotificationSubscription(getClient, t)),
)
discussions := toolsets.NewToolset("discussions", "GitHub Discussions related tools").
AddReadTools(
toolsets.NewServerTool(ListDiscussions(getGQLClient, t)),
toolsets.NewServerTool(GetDiscussion(getGQLClient, t)),
toolsets.NewServerTool(GetDiscussionComments(getGQLClient, t)),
toolsets.NewServerTool(ListDiscussionCategories(getGQLClient, t)),
)
actions := toolsets.NewToolset("actions", "GitHub Actions workflows and CI/CD operations").
AddReadTools(
toolsets.NewServerTool(ListWorkflows(getClient, t)),
toolsets.NewServerTool(ListWorkflowRuns(getClient, t)),
toolsets.NewServerTool(GetWorkflowRun(getClient, t)),
toolsets.NewServerTool(GetWorkflowRunLogs(getClient, t)),
toolsets.NewServerTool(ListWorkflowJobs(getClient, t)),
toolsets.NewServerTool(GetJobLogs(getClient, t)),
toolsets.NewServerTool(ListWorkflowRunArtifacts(getClient, t)),
toolsets.NewServerTool(DownloadWorkflowRunArtifact(getClient, t)),
toolsets.NewServerTool(GetWorkflowRunUsage(getClient, t)),
).
AddWriteTools(
toolsets.NewServerTool(RunWorkflow(getClient, t)),
toolsets.NewServerTool(RerunWorkflowRun(getClient, t)),
toolsets.NewServerTool(RerunFailedJobs(getClient, t)),
toolsets.NewServerTool(CancelWorkflowRun(getClient, t)),
toolsets.NewServerTool(DeleteWorkflowRunLogs(getClient, t)),
)
// Keep experiments alive so the system doesn't error out when it's always enabled
experiments := toolsets.NewToolset("experiments", "Experimental features that are not considered stable yet")
contextTools := toolsets.NewToolset("context", "Tools that provide context about the current user and GitHub context you are operating in").
AddReadTools(
toolsets.NewServerTool(GetMe(getClient, t)),
)
// Add toolsets to the group
tsg.AddToolset(contextTools)
tsg.AddToolset(repos)
tsg.AddToolset(issues)
tsg.AddToolset(orgs)
tsg.AddToolset(users)
tsg.AddToolset(pullRequests)
tsg.AddToolset(actions)
tsg.AddToolset(codeSecurity)
tsg.AddToolset(secretProtection)
tsg.AddToolset(dependabot)
tsg.AddToolset(notifications)
tsg.AddToolset(experiments)
tsg.AddToolset(discussions)
return tsg
}
// InitDynamicToolset creates a dynamic toolset that can be used to enable other toolsets, and so requires the server and toolset group as arguments
func InitDynamicToolset(s *server.MCPServer, tsg *toolsets.ToolsetGroup, t translations.TranslationHelperFunc) *toolsets.Toolset {
// Create a new dynamic toolset
// Need to add the dynamic toolset last so it can be used to enable other toolsets
dynamicToolSelection := toolsets.NewToolset("dynamic", "Discover GitHub MCP tools that can help achieve tasks by enabling additional sets of tools, you can control the enablement of any toolset to access its tools when this toolset is enabled.").
AddReadTools(
toolsets.NewServerTool(ListAvailableToolsets(tsg, t)),
toolsets.NewServerTool(GetToolsetsTools(tsg, t)),
toolsets.NewServerTool(EnableToolset(s, tsg, t)),
)
dynamicToolSelection.Enabled = true
return dynamicToolSelection
}
// ToBoolPtr converts a bool to a *bool pointer.
func ToBoolPtr(b bool) *bool {
return &b
}
// ToStringPtr converts a string to a *string pointer.
// Returns nil if the string is empty.
func ToStringPtr(s string) *string {
if s == "" {
return nil
}
return &s
}