diff --git a/pkg/github/code_scanning.go b/pkg/github/code_scanning.go index b33f32c1..93e7e0e5 100644 --- a/pkg/github/code_scanning.go +++ b/pkg/github/code_scanning.go @@ -16,6 +16,10 @@ import ( func GetCodeScanningAlert(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("get_code_scanning_alert", mcp.WithDescription(t("TOOL_GET_CODE_SCANNING_ALERT_DESCRIPTION", "Get details of a specific code scanning alert in a GitHub repository.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_GET_CODE_SCANNING_ALERT_USER_TITLE", "Get code scanning alert"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("The owner of the repository."), @@ -74,6 +78,10 @@ func GetCodeScanningAlert(getClient GetClientFn, t translations.TranslationHelpe func ListCodeScanningAlerts(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("list_code_scanning_alerts", mcp.WithDescription(t("TOOL_LIST_CODE_SCANNING_ALERTS_DESCRIPTION", "List code scanning alerts in a GitHub repository.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_LIST_CODE_SCANNING_ALERTS_USER_TITLE", "List code scanning alerts"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("The owner of the repository."), diff --git a/pkg/github/context_tools.go b/pkg/github/context_tools.go index 1c91d703..3511e23a 100644 --- a/pkg/github/context_tools.go +++ b/pkg/github/context_tools.go @@ -16,6 +16,10 @@ import ( func GetMe(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("get_me", mcp.WithDescription(t("TOOL_GET_ME_DESCRIPTION", "Get details of the authenticated GitHub user. Use this when a request include \"me\", \"my\"...")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_GET_ME_USER_TITLE", "Get my user profile"), + ReadOnlyHint: true, + }), mcp.WithString("reason", mcp.Description("Optional: reason the session was created"), ), diff --git a/pkg/github/dynamic_tools.go b/pkg/github/dynamic_tools.go index d4d5f27a..30dfd4a3 100644 --- a/pkg/github/dynamic_tools.go +++ b/pkg/github/dynamic_tools.go @@ -22,6 +22,11 @@ func ToolsetEnum(toolsetGroup *toolsets.ToolsetGroup) mcp.PropertyOption { func EnableToolset(s *server.MCPServer, toolsetGroup *toolsets.ToolsetGroup, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("enable_toolset", mcp.WithDescription(t("TOOL_ENABLE_TOOLSET_DESCRIPTION", "Enable one of the sets of tools the GitHub MCP server provides, use get_toolset_tools and list_available_toolsets first to see what this will enable")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_ENABLE_TOOLSET_USER_TITLE", "Enable a toolset"), + // Not modifying GitHub data so no need to show a warning + ReadOnlyHint: true, + }), mcp.WithString("toolset", mcp.Required(), mcp.Description("The name of the toolset to enable"), @@ -57,6 +62,10 @@ func EnableToolset(s *server.MCPServer, toolsetGroup *toolsets.ToolsetGroup, t t func ListAvailableToolsets(toolsetGroup *toolsets.ToolsetGroup, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("list_available_toolsets", mcp.WithDescription(t("TOOL_LIST_AVAILABLE_TOOLSETS_DESCRIPTION", "List all available toolsets this GitHub MCP server can offer, providing the enabled status of each. Use this when a task could be achieved with a GitHub tool and the currently available tools aren't enough. Call get_toolset_tools with these toolset names to discover specific tools you can call")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_LIST_AVAILABLE_TOOLSETS_USER_TITLE", "List available toolsets"), + ReadOnlyHint: true, + }), ), func(_ context.Context, _ mcp.CallToolRequest) (*mcp.CallToolResult, error) { // We need to convert the toolsetGroup back to a map for JSON serialization @@ -87,6 +96,10 @@ func ListAvailableToolsets(toolsetGroup *toolsets.ToolsetGroup, t translations.T func GetToolsetsTools(toolsetGroup *toolsets.ToolsetGroup, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("get_toolset_tools", mcp.WithDescription(t("TOOL_GET_TOOLSET_TOOLS_DESCRIPTION", "Lists all the capabilities that are enabled with the specified toolset, use this to get clarity on whether enabling a toolset would help you to complete a task")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_GET_TOOLSET_TOOLS_USER_TITLE", "List all tools in a toolset"), + ReadOnlyHint: true, + }), mcp.WithString("toolset", mcp.Required(), mcp.Description("The name of the toolset you want to get the tools for"), diff --git a/pkg/github/issues.go b/pkg/github/issues.go index 1324bd56..0fcc2502 100644 --- a/pkg/github/issues.go +++ b/pkg/github/issues.go @@ -17,7 +17,11 @@ import ( // GetIssue creates a tool to get details of a specific issue in a GitHub repository. func GetIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("get_issue", - mcp.WithDescription(t("TOOL_GET_ISSUE_DESCRIPTION", "Get details of a specific issue in a GitHub repository")), + mcp.WithDescription(t("TOOL_GET_ISSUE_DESCRIPTION", "Get details of a specific issue in a GitHub repository.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_GET_ISSUE_USER_TITLE", "Get issue details"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("The owner of the repository"), @@ -75,7 +79,11 @@ func GetIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (tool // AddIssueComment creates a tool to add a comment to an issue. func AddIssueComment(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("add_issue_comment", - mcp.WithDescription(t("TOOL_ADD_ISSUE_COMMENT_DESCRIPTION", "Add a comment to an existing issue")), + mcp.WithDescription(t("TOOL_ADD_ISSUE_COMMENT_DESCRIPTION", "Add a comment to a specific issue in a GitHub repository.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_ADD_ISSUE_COMMENT_USER_TITLE", "Add comment to issue"), + ReadOnlyHint: false, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -145,7 +153,11 @@ func AddIssueComment(getClient GetClientFn, t translations.TranslationHelperFunc // SearchIssues creates a tool to search for issues and pull requests. func SearchIssues(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("search_issues", - mcp.WithDescription(t("TOOL_SEARCH_ISSUES_DESCRIPTION", "Search for issues and pull requests across GitHub repositories")), + mcp.WithDescription(t("TOOL_SEARCH_ISSUES_DESCRIPTION", "Search for issues in GitHub repositories.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_SEARCH_ISSUES_USER_TITLE", "Search issues"), + ReadOnlyHint: true, + }), mcp.WithString("q", mcp.Required(), mcp.Description("Search query using GitHub issues search syntax"), @@ -229,7 +241,11 @@ func SearchIssues(getClient GetClientFn, t translations.TranslationHelperFunc) ( // CreateIssue creates a tool to create a new issue in a GitHub repository. func CreateIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("create_issue", - mcp.WithDescription(t("TOOL_CREATE_ISSUE_DESCRIPTION", "Create a new issue in a GitHub repository")), + mcp.WithDescription(t("TOOL_CREATE_ISSUE_DESCRIPTION", "Create a new issue in a GitHub repository.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_CREATE_ISSUE_USER_TITLE", "Open new issue"), + ReadOnlyHint: false, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -347,7 +363,11 @@ func CreateIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (t // ListIssues creates a tool to list and filter repository issues func ListIssues(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("list_issues", - mcp.WithDescription(t("TOOL_LIST_ISSUES_DESCRIPTION", "List issues in a GitHub repository with filtering options")), + mcp.WithDescription(t("TOOL_LIST_ISSUES_DESCRIPTION", "List issues in a GitHub repository.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_LIST_ISSUES_USER_TITLE", "List issues"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -465,7 +485,11 @@ func ListIssues(getClient GetClientFn, t translations.TranslationHelperFunc) (to // UpdateIssue creates a tool to update an existing issue in a GitHub repository. func UpdateIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("update_issue", - mcp.WithDescription(t("TOOL_UPDATE_ISSUE_DESCRIPTION", "Update an existing issue in a GitHub repository")), + mcp.WithDescription(t("TOOL_UPDATE_ISSUE_DESCRIPTION", "Update an existing issue in a GitHub repository.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_UPDATE_ISSUE_USER_TITLE", "Edit issue"), + ReadOnlyHint: false, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -607,7 +631,11 @@ func UpdateIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (t // GetIssueComments creates a tool to get comments for a GitHub issue. func GetIssueComments(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("get_issue_comments", - mcp.WithDescription(t("TOOL_GET_ISSUE_COMMENTS_DESCRIPTION", "Get comments for a GitHub issue")), + mcp.WithDescription(t("TOOL_GET_ISSUE_COMMENTS_DESCRIPTION", "Get comments for a specific issue in a GitHub repository.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_GET_ISSUE_COMMENTS_USER_TITLE", "Get issue comments"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), diff --git a/pkg/github/pullrequests.go b/pkg/github/pullrequests.go index 1ecd209e..9c8fca17 100644 --- a/pkg/github/pullrequests.go +++ b/pkg/github/pullrequests.go @@ -16,7 +16,11 @@ import ( // GetPullRequest creates a tool to get details of a specific pull request. func GetPullRequest(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("get_pull_request", - mcp.WithDescription(t("TOOL_GET_PULL_REQUEST_DESCRIPTION", "Get details of a specific pull request")), + mcp.WithDescription(t("TOOL_GET_PULL_REQUEST_DESCRIPTION", "Get details of a specific pull request in a GitHub repository.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_GET_PULL_REQUEST_USER_TITLE", "Get pull request details"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -74,7 +78,11 @@ func GetPullRequest(getClient GetClientFn, t translations.TranslationHelperFunc) // UpdatePullRequest creates a tool to update an existing pull request. func UpdatePullRequest(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler 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.WithDescription(t("TOOL_UPDATE_PULL_REQUEST_DESCRIPTION", "Update an existing pull request in a GitHub repository.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_UPDATE_PULL_REQUEST_USER_TITLE", "Edit pull request"), + ReadOnlyHint: false, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -191,7 +199,11 @@ func UpdatePullRequest(getClient GetClientFn, t translations.TranslationHelperFu // ListPullRequests creates a tool to list and filter repository pull requests. func ListPullRequests(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("list_pull_requests", - mcp.WithDescription(t("TOOL_LIST_PULL_REQUESTS_DESCRIPTION", "List and filter repository pull requests")), + mcp.WithDescription(t("TOOL_LIST_PULL_REQUESTS_DESCRIPTION", "List pull requests in a GitHub repository.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_LIST_PULL_REQUESTS_USER_TITLE", "List pull requests"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -296,7 +308,11 @@ func ListPullRequests(getClient GetClientFn, t translations.TranslationHelperFun // MergePullRequest creates a tool to merge a pull request. func MergePullRequest(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("merge_pull_request", - mcp.WithDescription(t("TOOL_MERGE_PULL_REQUEST_DESCRIPTION", "Merge a pull request")), + mcp.WithDescription(t("TOOL_MERGE_PULL_REQUEST_DESCRIPTION", "Merge a pull request in a GitHub repository.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_MERGE_PULL_REQUEST_USER_TITLE", "Merge pull request"), + ReadOnlyHint: false, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -381,7 +397,11 @@ func MergePullRequest(getClient GetClientFn, t translations.TranslationHelperFun // GetPullRequestFiles creates a tool to get the list of files changed in a pull request. func GetPullRequestFiles(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("get_pull_request_files", - mcp.WithDescription(t("TOOL_GET_PULL_REQUEST_FILES_DESCRIPTION", "Get the list of files changed in a pull request")), + mcp.WithDescription(t("TOOL_GET_PULL_REQUEST_FILES_DESCRIPTION", "Get the files changed in a specific pull request.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_GET_PULL_REQUEST_FILES_USER_TITLE", "Get pull request files"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -440,7 +460,11 @@ func GetPullRequestFiles(getClient GetClientFn, t translations.TranslationHelper // GetPullRequestStatus creates a tool to get the combined status of all status checks for a pull request. func GetPullRequestStatus(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("get_pull_request_status", - mcp.WithDescription(t("TOOL_GET_PULL_REQUEST_STATUS_DESCRIPTION", "Get the combined status of all status checks for a pull request")), + mcp.WithDescription(t("TOOL_GET_PULL_REQUEST_STATUS_DESCRIPTION", "Get the status of a specific pull request.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_GET_PULL_REQUEST_STATUS_USER_TITLE", "Get pull request status checks"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -513,7 +537,11 @@ func GetPullRequestStatus(getClient GetClientFn, t translations.TranslationHelpe // 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) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("update_pull_request_branch", - mcp.WithDescription(t("TOOL_UPDATE_PULL_REQUEST_BRANCH_DESCRIPTION", "Update a pull request branch with the latest changes from the base 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{ + Title: t("TOOL_UPDATE_PULL_REQUEST_BRANCH_USER_TITLE", "Update pull request branch"), + ReadOnlyHint: false, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -587,7 +615,11 @@ func UpdatePullRequestBranch(getClient GetClientFn, t translations.TranslationHe // GetPullRequestComments creates a tool to get the review comments on a pull request. func GetPullRequestComments(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("get_pull_request_comments", - mcp.WithDescription(t("TOOL_GET_PULL_REQUEST_COMMENTS_DESCRIPTION", "Get the review comments on a pull request")), + mcp.WithDescription(t("TOOL_GET_PULL_REQUEST_COMMENTS_DESCRIPTION", "Get comments for a specific pull request.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_GET_PULL_REQUEST_COMMENTS_USER_TITLE", "Get pull request comments"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -651,7 +683,11 @@ func GetPullRequestComments(getClient GetClientFn, t translations.TranslationHel // AddPullRequestReviewComment creates a tool to add a review comment to a pull request. func AddPullRequestReviewComment(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("add_pull_request_review_comment", - mcp.WithDescription(t("TOOL_ADD_PULL_REQUEST_COMMENT_DESCRIPTION", "Add a review comment to a pull request")), + mcp.WithDescription(t("TOOL_ADD_PULL_REQUEST_REVIEW_COMMENT_DESCRIPTION", "Add a review comment to a pull request.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_ADD_PULL_REQUEST_REVIEW_COMMENT_USER_TITLE", "Add review comment to pull request"), + ReadOnlyHint: false, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -821,7 +857,11 @@ func AddPullRequestReviewComment(getClient GetClientFn, t translations.Translati // GetPullRequestReviews creates a tool to get the reviews on a pull request. func GetPullRequestReviews(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("get_pull_request_reviews", - mcp.WithDescription(t("TOOL_GET_PULL_REQUEST_REVIEWS_DESCRIPTION", "Get the reviews on a pull request")), + mcp.WithDescription(t("TOOL_GET_PULL_REQUEST_REVIEWS_DESCRIPTION", "Get reviews for a specific pull request.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_GET_PULL_REQUEST_REVIEWS_USER_TITLE", "Get pull request reviews"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -879,7 +919,11 @@ func GetPullRequestReviews(getClient GetClientFn, t translations.TranslationHelp // CreatePullRequestReview creates a tool to submit a review on a pull request. func CreatePullRequestReview(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("create_pull_request_review", - mcp.WithDescription(t("TOOL_CREATE_PULL_REQUEST_REVIEW_DESCRIPTION", "Create a review on a pull request")), + mcp.WithDescription(t("TOOL_CREATE_PULL_REQUEST_REVIEW_DESCRIPTION", "Create a review for a pull request.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_CREATE_PULL_REQUEST_REVIEW_USER_TITLE", "Submit pull request review"), + ReadOnlyHint: false, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -1091,7 +1135,11 @@ func CreatePullRequestReview(getClient GetClientFn, t translations.TranslationHe // CreatePullRequest creates a tool to create a new pull request. func CreatePullRequest(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler 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.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: false, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go index 51948730..2ef328aa 100644 --- a/pkg/github/repositories.go +++ b/pkg/github/repositories.go @@ -16,6 +16,10 @@ import ( func GetCommit(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("get_commit", mcp.WithDescription(t("TOOL_GET_COMMITS_DESCRIPTION", "Get details for a commit from a GitHub repository")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_GET_COMMITS_USER_TITLE", "Get commit details"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -84,6 +88,10 @@ func GetCommit(getClient GetClientFn, t translations.TranslationHelperFunc) (too func ListCommits(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("list_commits", mcp.WithDescription(t("TOOL_LIST_COMMITS_DESCRIPTION", "Get list of commits of a branch in a GitHub repository")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_LIST_COMMITS_USER_TITLE", "List commits"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -154,6 +162,10 @@ func ListCommits(getClient GetClientFn, t translations.TranslationHelperFunc) (t func ListBranches(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("list_branches", mcp.WithDescription(t("TOOL_LIST_BRANCHES_DESCRIPTION", "List branches in a GitHub repository")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_LIST_BRANCHES_USER_TITLE", "List branches"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -217,6 +229,10 @@ func ListBranches(getClient GetClientFn, t translations.TranslationHelperFunc) ( func CreateOrUpdateFile(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("create_or_update_file", mcp.WithDescription(t("TOOL_CREATE_OR_UPDATE_FILE_DESCRIPTION", "Create or update a single file in a GitHub repository")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_CREATE_OR_UPDATE_FILE_USER_TITLE", "Create or update file"), + ReadOnlyHint: false, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner (username or organization)"), @@ -322,6 +338,10 @@ func CreateOrUpdateFile(getClient GetClientFn, t translations.TranslationHelperF func CreateRepository(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("create_repository", mcp.WithDescription(t("TOOL_CREATE_REPOSITORY_DESCRIPTION", "Create a new GitHub repository in your account")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_CREATE_REPOSITORY_USER_TITLE", "Create repository"), + ReadOnlyHint: false, + }), mcp.WithString("name", mcp.Required(), mcp.Description("Repository name"), @@ -392,6 +412,10 @@ func CreateRepository(getClient GetClientFn, t translations.TranslationHelperFun func GetFileContents(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("get_file_contents", mcp.WithDescription(t("TOOL_GET_FILE_CONTENTS_DESCRIPTION", "Get the contents of a file or directory from a GitHub repository")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_GET_FILE_CONTENTS_USER_TITLE", "Get file or directory contents"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner (username or organization)"), @@ -465,6 +489,10 @@ func GetFileContents(getClient GetClientFn, t translations.TranslationHelperFunc func ForkRepository(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("fork_repository", mcp.WithDescription(t("TOOL_FORK_REPOSITORY_DESCRIPTION", "Fork a GitHub repository to your account or specified organization")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_FORK_REPOSITORY_USER_TITLE", "Fork repository"), + ReadOnlyHint: false, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -532,6 +560,10 @@ func ForkRepository(getClient GetClientFn, t translations.TranslationHelperFunc) func CreateBranch(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("create_branch", mcp.WithDescription(t("TOOL_CREATE_BRANCH_DESCRIPTION", "Create a new branch in a GitHub repository")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_CREATE_BRANCH_USER_TITLE", "Create branch"), + ReadOnlyHint: false, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), @@ -580,7 +612,7 @@ func CreateBranch(getClient GetClientFn, t translations.TranslationHelperFunc) ( if err != nil { return nil, fmt.Errorf("failed to get repository: %w", err) } - defer func() { _ = resp.Body.Close() }() + defer resp.Body.Close() fromBranch = *repository.DefaultBranch } @@ -590,7 +622,7 @@ func CreateBranch(getClient GetClientFn, t translations.TranslationHelperFunc) ( if err != nil { return nil, fmt.Errorf("failed to get reference: %w", err) } - defer func() { _ = resp.Body.Close() }() + defer resp.Body.Close() // Create new branch newRef := &github.Reference{ @@ -602,7 +634,7 @@ func CreateBranch(getClient GetClientFn, t translations.TranslationHelperFunc) ( if err != nil { return nil, fmt.Errorf("failed to create branch: %w", err) } - defer func() { _ = resp.Body.Close() }() + defer resp.Body.Close() r, err := json.Marshal(createdRef) if err != nil { @@ -617,6 +649,10 @@ func CreateBranch(getClient GetClientFn, t translations.TranslationHelperFunc) ( func PushFiles(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("push_files", mcp.WithDescription(t("TOOL_PUSH_FILES_DESCRIPTION", "Push multiple files to a GitHub repository in a single commit")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_PUSH_FILES_USER_TITLE", "Push files to repository"), + ReadOnlyHint: false, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("Repository owner"), diff --git a/pkg/github/search.go b/pkg/github/search.go index dc85c177..86a4f431 100644 --- a/pkg/github/search.go +++ b/pkg/github/search.go @@ -16,6 +16,10 @@ import ( func SearchRepositories(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("search_repositories", mcp.WithDescription(t("TOOL_SEARCH_REPOSITORIES_DESCRIPTION", "Search for GitHub repositories")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_SEARCH_REPOSITORIES_USER_TITLE", "Search repositories"), + ReadOnlyHint: true, + }), mcp.WithString("query", mcp.Required(), mcp.Description("Search query"), @@ -70,6 +74,10 @@ func SearchRepositories(getClient GetClientFn, t translations.TranslationHelperF func SearchCode(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("search_code", mcp.WithDescription(t("TOOL_SEARCH_CODE_DESCRIPTION", "Search for code across GitHub repositories")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_SEARCH_CODE_USER_TITLE", "Search code"), + ReadOnlyHint: true, + }), mcp.WithString("q", mcp.Required(), mcp.Description("Search query using GitHub code search syntax"), @@ -142,6 +150,10 @@ func SearchCode(getClient GetClientFn, t translations.TranslationHelperFunc) (to func SearchUsers(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("search_users", mcp.WithDescription(t("TOOL_SEARCH_USERS_DESCRIPTION", "Search for GitHub users")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_SEARCH_USERS_USER_TITLE", "Search users"), + ReadOnlyHint: true, + }), mcp.WithString("q", mcp.Required(), mcp.Description("Search query using GitHub users search syntax"), diff --git a/pkg/github/secret_scanning.go b/pkg/github/secret_scanning.go index ee344061..cd0fd040 100644 --- a/pkg/github/secret_scanning.go +++ b/pkg/github/secret_scanning.go @@ -17,6 +17,10 @@ func GetSecretScanningAlert(getClient GetClientFn, t translations.TranslationHel return mcp.NewTool( "get_secret_scanning_alert", mcp.WithDescription(t("TOOL_GET_SECRET_SCANNING_ALERT_DESCRIPTION", "Get details of a specific secret scanning alert in a GitHub repository.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_GET_SECRET_SCANNING_ALERT_USER_TITLE", "Get secret scanning alert"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("The owner of the repository."), @@ -76,6 +80,10 @@ func ListSecretScanningAlerts(getClient GetClientFn, t translations.TranslationH return mcp.NewTool( "list_secret_scanning_alerts", mcp.WithDescription(t("TOOL_LIST_SECRET_SCANNING_ALERTS_DESCRIPTION", "List secret scanning alerts in a GitHub repository.")), + mcp.WithToolAnnotation(mcp.ToolAnnotation{ + Title: t("TOOL_LIST_SECRET_SCANNING_ALERTS_USER_TITLE", "List secret scanning alerts"), + ReadOnlyHint: true, + }), mcp.WithString("owner", mcp.Required(), mcp.Description("The owner of the repository."), diff --git a/pkg/github/tools.go b/pkg/github/tools.go index 35dabaef..1a4a3b4d 100644 --- a/pkg/github/tools.go +++ b/pkg/github/tools.go @@ -118,6 +118,7 @@ func InitDynamicToolset(s *server.MCPServer, tsg *toolsets.ToolsetGroup, t trans toolsets.NewServerTool(GetToolsetsTools(tsg, t)), toolsets.NewServerTool(EnableToolset(s, tsg, t)), ) + dynamicToolSelection.Enabled = true return dynamicToolSelection } diff --git a/pkg/toolsets/toolsets.go b/pkg/toolsets/toolsets.go index d4397fc9..b316aae3 100644 --- a/pkg/toolsets/toolsets.go +++ b/pkg/toolsets/toolsets.go @@ -58,6 +58,11 @@ func (t *Toolset) SetReadOnly() { func (t *Toolset) AddWriteTools(tools ...server.ServerTool) *Toolset { // Silently ignore if the toolset is read-only to avoid any breach of that contract + for _, tool := range tools { + if tool.Tool.Annotations.ReadOnlyHint { + panic(fmt.Sprintf("tool (%s) is incorrectly annotated as read-only", tool.Tool.Name)) + } + } if !t.readOnly { t.writeTools = append(t.writeTools, tools...) } @@ -65,6 +70,15 @@ func (t *Toolset) AddWriteTools(tools ...server.ServerTool) *Toolset { } func (t *Toolset) AddReadTools(tools ...server.ServerTool) *Toolset { + for _, tool := range tools { + if !tool.Tool.Annotations.ReadOnlyHint { + panic(fmt.Sprintf("tool (%s) must be annotated as read-only", tool.Tool.Name)) + } + tool.Tool.Annotations = mcp.ToolAnnotation{ + ReadOnlyHint: true, + Title: tool.Tool.Annotations.Title, + } + } t.readTools = append(t.readTools, tools...) return t }