Add tool annotations and PR close/reopen support (#174)

Add MCP `ToolAnnotation` metadata (Title, ReadOnlyHint, DestructiveHint)
to all registered tools so MCP hosts (VS Code, Claude, Cursor) get
accurate per-tool hints. A shared `pkg/annotation` package exposes
`ReadOnly`, `Write`, and `Destructive` helpers for consistency.

Add `close` and `reopen` methods to `pull_request_write` so PR state
can be toggled without going through the generic `update` path.

Co-Authored-By: silverwind <me@silverwind.io>
Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>
This commit is contained in:
unpossible
2026-05-10 11:25:22 +02:00
committed by silverwind
parent 4c45b42cb5
commit 329a97d5d2
21 changed files with 296 additions and 2 deletions
+4
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"gitea.com/gitea/gitea-mcp/pkg/annotation"
"gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params"
@@ -24,6 +25,7 @@ var (
CreateBranchTool = mcp.NewTool(
CreateBranchToolName,
mcp.WithDescription("Create branch"),
mcp.WithToolAnnotation(annotation.Write("Create a new branch")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("branch", mcp.Required(), mcp.Description("Name of the branch to create")),
@@ -33,6 +35,7 @@ var (
DeleteBranchTool = mcp.NewTool(
DeleteBranchToolName,
mcp.WithDescription("Delete branch"),
mcp.WithToolAnnotation(annotation.Destructive("Delete a branch")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("branch", mcp.Required(), mcp.Description("Name of the branch to delete")),
@@ -41,6 +44,7 @@ var (
ListBranchesTool = mcp.NewTool(
ListBranchesToolName,
mcp.WithDescription("List branches"),
mcp.WithToolAnnotation(annotation.ReadOnly("List repository branches")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)),
+3
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"gitea.com/gitea/gitea-mcp/pkg/annotation"
"gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params"
@@ -23,6 +24,7 @@ var (
ListRepoCommitsTool = mcp.NewTool(
ListRepoCommitsToolName,
mcp.WithDescription("List repository commits"),
mcp.WithToolAnnotation(annotation.ReadOnly("List repository commits")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("sha", mcp.Description("SHA or branch to start listing commits from")),
@@ -34,6 +36,7 @@ var (
GetCommitTool = mcp.NewTool(
GetCommitToolName,
mcp.WithDescription("Get details of a specific commit"),
mcp.WithToolAnnotation(annotation.ReadOnly("Get commit details")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("sha", mcp.Required(), mcp.Description("commit SHA")),
+5
View File
@@ -8,6 +8,7 @@ import (
"encoding/json"
"fmt"
"gitea.com/gitea/gitea-mcp/pkg/annotation"
"gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params"
@@ -29,6 +30,7 @@ var (
GetFileContentTool = mcp.NewTool(
GetFileToolName,
mcp.WithDescription("Get file Content and Metadata"),
mcp.WithToolAnnotation(annotation.ReadOnly("Get file content")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("ref", mcp.Required(), mcp.Description("ref can be branch/tag/commit")),
@@ -39,6 +41,7 @@ var (
GetDirContentTool = mcp.NewTool(
GetDirToolName,
mcp.WithDescription("Get a list of entries in a directory"),
mcp.WithToolAnnotation(annotation.ReadOnly("Get directory contents")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("ref", mcp.Required(), mcp.Description("ref can be branch/tag/commit")),
@@ -48,6 +51,7 @@ var (
CreateOrUpdateFileTool = mcp.NewTool(
CreateOrUpdateFileToolName,
mcp.WithDescription("Create or update a file. If sha is provided, updates the existing file; otherwise creates a new file."),
mcp.WithToolAnnotation(annotation.Write("Create or update a file")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("filePath", mcp.Required(), mcp.Description("file path")),
@@ -61,6 +65,7 @@ var (
DeleteFileTool = mcp.NewTool(
DeleteFileToolName,
mcp.WithDescription("Delete file"),
mcp.WithToolAnnotation(annotation.Destructive("Delete a file")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("filePath", mcp.Required(), mcp.Description("file path")),
+6
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"gitea.com/gitea/gitea-mcp/pkg/annotation"
"gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params"
@@ -26,6 +27,7 @@ var (
CreateReleaseTool = mcp.NewTool(
CreateReleaseToolName,
mcp.WithDescription("Create release"),
mcp.WithToolAnnotation(annotation.Write("Create a release")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("tag_name", mcp.Required(), mcp.Description("tag name")),
@@ -39,6 +41,7 @@ var (
DeleteReleaseTool = mcp.NewTool(
DeleteReleaseToolName,
mcp.WithDescription("Delete release"),
mcp.WithToolAnnotation(annotation.Destructive("Delete a release")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithNumber("id", mcp.Required(), mcp.Description("release id")),
@@ -47,6 +50,7 @@ var (
GetReleaseTool = mcp.NewTool(
GetReleaseToolName,
mcp.WithDescription("Get release"),
mcp.WithToolAnnotation(annotation.ReadOnly("Get release details")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithNumber("id", mcp.Required(), mcp.Description("release id")),
@@ -55,6 +59,7 @@ var (
GetLatestReleaseTool = mcp.NewTool(
GetLatestReleaseToolName,
mcp.WithDescription("Get latest release"),
mcp.WithToolAnnotation(annotation.ReadOnly("Get latest release")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
)
@@ -62,6 +67,7 @@ var (
ListReleasesTool = mcp.NewTool(
ListReleasesToolName,
mcp.WithDescription("List releases"),
mcp.WithToolAnnotation(annotation.ReadOnly("List releases")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithBoolean("is_draft", mcp.Description("Whether the release is draft"), mcp.DefaultBool(false)),
+5
View File
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"gitea.com/gitea/gitea-mcp/pkg/annotation"
"gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params"
@@ -29,6 +30,7 @@ var (
CreateRepoTool = mcp.NewTool(
CreateRepoToolName,
mcp.WithDescription("Create repository in personal account or organization"),
mcp.WithToolAnnotation(annotation.Write("Create a new repository")),
mcp.WithString("name", mcp.Required(), mcp.Description("Name of the repository to create")),
mcp.WithString("description", mcp.Description("Description of the repository to create")),
mcp.WithBoolean("private", mcp.Description("Whether the repository is private")),
@@ -47,6 +49,7 @@ var (
ForkRepoTool = mcp.NewTool(
ForkRepoToolName,
mcp.WithDescription("Fork repository"),
mcp.WithToolAnnotation(annotation.Write("Fork a repository")),
mcp.WithString("user", mcp.Required(), mcp.Description("User name of the repository to fork")),
mcp.WithString("repo", mcp.Required(), mcp.Description("Repository name to fork")),
mcp.WithString("organization", mcp.Description("Organization name to fork")),
@@ -56,6 +59,7 @@ var (
ListMyReposTool = mcp.NewTool(
ListMyReposToolName,
mcp.WithDescription("List my repositories"),
mcp.WithToolAnnotation(annotation.ReadOnly("List my repositories")),
mcp.WithNumber("page", mcp.Required(), mcp.Description("Page number"), mcp.DefaultNumber(1), mcp.Min(1)),
mcp.WithNumber("perPage", mcp.Required(), mcp.Description("results per page (may be capped by the server's MAX_RESPONSE_ITEMS setting, default 50)"), mcp.DefaultNumber(30), mcp.Min(1)),
)
@@ -63,6 +67,7 @@ var (
ListOrgReposTool = mcp.NewTool(
ListOrgReposToolName,
mcp.WithDescription("List repositories of an organization"),
mcp.WithToolAnnotation(annotation.ReadOnly("List organization repositories")),
mcp.WithString("org", mcp.Required(), mcp.Description("Organization name")),
mcp.WithNumber("page", mcp.Required(), mcp.Description("Page number"), mcp.DefaultNumber(1), mcp.Min(1)),
mcp.WithNumber("pageSize", mcp.Required(), mcp.Description("Page size number"), mcp.DefaultNumber(100), mcp.Min(1)),
+5
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"gitea.com/gitea/gitea-mcp/pkg/annotation"
"gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params"
@@ -25,6 +26,7 @@ var (
CreateTagTool = mcp.NewTool(
CreateTagToolName,
mcp.WithDescription("Create tag"),
mcp.WithToolAnnotation(annotation.Write("Create a tag")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("tag_name", mcp.Required(), mcp.Description("tag name")),
@@ -35,6 +37,7 @@ var (
DeleteTagTool = mcp.NewTool(
DeleteTagToolName,
mcp.WithDescription("Delete tag"),
mcp.WithToolAnnotation(annotation.Destructive("Delete a tag")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("tag_name", mcp.Required(), mcp.Description("tag name")),
@@ -43,6 +46,7 @@ var (
GetTagTool = mcp.NewTool(
GetTagToolName,
mcp.WithDescription("Get tag"),
mcp.WithToolAnnotation(annotation.ReadOnly("Get tag details")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("tag_name", mcp.Required(), mcp.Description("tag name")),
@@ -51,6 +55,7 @@ var (
ListTagsTool = mcp.NewTool(
ListTagsToolName,
mcp.WithDescription("List tags"),
mcp.WithToolAnnotation(annotation.ReadOnly("List tags")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1), mcp.Min(1)),
+2
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"gitea.com/gitea/gitea-mcp/pkg/annotation"
"gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params"
@@ -21,6 +22,7 @@ const (
var GetRepoTreeTool = mcp.NewTool(
GetRepoTreeToolName,
mcp.WithDescription("Get the file tree of a repository"),
mcp.WithToolAnnotation(annotation.ReadOnly("Get repository file tree")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("tree_sha", mcp.Required(), mcp.Description("SHA, branch name, or tag name")),