feat: expand MCP tool coverage from 93 to 299 tools
Some checks failed
release-nightly / release-image (push) Failing after 2m17s
Some checks failed
release-nightly / release-image (push) Failing after 2m17s
Fork the official gitea-mcp and massively extend API coverage: - Organization: orgs, members, teams, hooks, blocks, activity (34 tools) - User: profile, followers, keys, emails, repos, blocks (30 tools) - Repository: collaborators, webhooks, branch/tag protection, deploy keys, topics, git refs/trees/notes, commit status, stars/watchers, forks, transfers, mirrors, templates (53 tools) - Issue: reactions, pins, subscriptions, timeline, templates (16 tools) - Notifications: list, check, read, repo-scoped (7 tools) - Settings: API, attachment, repo, UI settings (4 tools) - Packages: list, get, delete, files, latest, link/unlink (7 tools) - Miscellaneous: server version, gitignore/label/license templates, markdown/markup rendering, node info, signing keys (12 tools) - Admin: user/org/repo CRUD, system webhooks, cron tasks, unadopted repos, emails, badges (23 tools) Module path updated to git.lethalbits.com/lethalbits/gitea-mcp.
This commit is contained in:
@@ -5,9 +5,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/log"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
|
||||
226
operation/repo/branch_protection.go
Normal file
226
operation/repo/branch_protection.go
Normal file
@@ -0,0 +1,226 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/params"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
const (
|
||||
ListBranchProtectionsToolName = "list_branch_protections"
|
||||
GetBranchProtectionToolName = "get_branch_protection"
|
||||
CreateBranchProtectionToolName = "create_branch_protection"
|
||||
EditBranchProtectionToolName = "edit_branch_protection"
|
||||
DeleteBranchProtectionToolName = "delete_branch_protection"
|
||||
)
|
||||
|
||||
var (
|
||||
ListBranchProtectionsTool = mcp.NewTool(
|
||||
ListBranchProtectionsToolName,
|
||||
mcp.WithDescription("List branch protections for a repository"),
|
||||
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.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)),
|
||||
)
|
||||
|
||||
GetBranchProtectionTool = mcp.NewTool(
|
||||
GetBranchProtectionToolName,
|
||||
mcp.WithDescription("Get a branch protection rule by name"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("name", mcp.Required(), mcp.Description("branch protection rule name")),
|
||||
)
|
||||
|
||||
CreateBranchProtectionTool = mcp.NewTool(
|
||||
CreateBranchProtectionToolName,
|
||||
mcp.WithDescription("Create a branch protection rule"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("rule_name", mcp.Required(), mcp.Description("rule name (glob pattern for branch matching)")),
|
||||
mcp.WithBoolean("enable_push", mcp.Description("enable push to protected branch")),
|
||||
mcp.WithBoolean("enable_merge_whitelist", mcp.Description("enable merge whitelist")),
|
||||
mcp.WithBoolean("enable_status_check", mcp.Description("enable status check")),
|
||||
mcp.WithNumber("required_approvals", mcp.Description("number of required approvals")),
|
||||
mcp.WithBoolean("block_on_rejected_reviews", mcp.Description("block merge on rejected reviews")),
|
||||
mcp.WithBoolean("dismiss_stale_approvals", mcp.Description("dismiss stale approvals on new commits")),
|
||||
)
|
||||
|
||||
EditBranchProtectionTool = mcp.NewTool(
|
||||
EditBranchProtectionToolName,
|
||||
mcp.WithDescription("Edit a branch protection rule"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("name", mcp.Required(), mcp.Description("branch protection rule name")),
|
||||
mcp.WithBoolean("enable_push", mcp.Description("enable push to protected branch")),
|
||||
mcp.WithBoolean("enable_merge_whitelist", mcp.Description("enable merge whitelist")),
|
||||
mcp.WithBoolean("enable_status_check", mcp.Description("enable status check")),
|
||||
mcp.WithNumber("required_approvals", mcp.Description("number of required approvals")),
|
||||
mcp.WithBoolean("block_on_rejected_reviews", mcp.Description("block merge on rejected reviews")),
|
||||
mcp.WithBoolean("dismiss_stale_approvals", mcp.Description("dismiss stale approvals on new commits")),
|
||||
)
|
||||
|
||||
DeleteBranchProtectionTool = mcp.NewTool(
|
||||
DeleteBranchProtectionToolName,
|
||||
mcp.WithDescription("Delete a branch protection rule"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("name", mcp.Required(), mcp.Description("branch protection rule name")),
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
Tool.RegisterRead(server.ServerTool{Tool: ListBranchProtectionsTool, Handler: ListBranchProtectionsFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: GetBranchProtectionTool, Handler: GetBranchProtectionFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: CreateBranchProtectionTool, Handler: CreateBranchProtectionFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: EditBranchProtectionTool, Handler: EditBranchProtectionFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: DeleteBranchProtectionTool, Handler: DeleteBranchProtectionFn})
|
||||
}
|
||||
|
||||
func ListBranchProtectionsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called ListBranchProtectionsFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
page := params.GetOptionalInt(req.GetArguments(), "page", 1)
|
||||
pageSize := params.GetOptionalInt(req.GetArguments(), "pageSize", 100)
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
bps, _, err := client.ListBranchProtections(owner, repo, gitea_sdk.ListBranchProtectionsOptions{
|
||||
ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)},
|
||||
})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list branch protections err: %v", err))
|
||||
}
|
||||
return to.TextResult(bps)
|
||||
}
|
||||
|
||||
func GetBranchProtectionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called GetBranchProtectionFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
name, _ := req.GetArguments()["name"].(string)
|
||||
if owner == "" || repo == "" || name == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and name are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
bp, _, err := client.GetBranchProtection(owner, repo, name)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get branch protection err: %v", err))
|
||||
}
|
||||
return to.TextResult(bp)
|
||||
}
|
||||
|
||||
func CreateBranchProtectionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called CreateBranchProtectionFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
ruleName, _ := req.GetArguments()["rule_name"].(string)
|
||||
if owner == "" || repo == "" || ruleName == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and rule_name are required"))
|
||||
}
|
||||
opt := gitea_sdk.CreateBranchProtectionOption{
|
||||
RuleName: ruleName,
|
||||
}
|
||||
if v, ok := req.GetArguments()["enable_push"].(bool); ok {
|
||||
opt.EnablePush = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["enable_merge_whitelist"].(bool); ok {
|
||||
opt.EnableMergeWhitelist = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["enable_status_check"].(bool); ok {
|
||||
opt.EnableStatusCheck = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["required_approvals"].(float64); ok {
|
||||
opt.RequiredApprovals = int64(v)
|
||||
}
|
||||
if v, ok := req.GetArguments()["block_on_rejected_reviews"].(bool); ok {
|
||||
opt.BlockOnRejectedReviews = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["dismiss_stale_approvals"].(bool); ok {
|
||||
opt.DismissStaleApprovals = v
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
bp, _, err := client.CreateBranchProtection(owner, repo, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("create branch protection err: %v", err))
|
||||
}
|
||||
return to.TextResult(bp)
|
||||
}
|
||||
|
||||
func EditBranchProtectionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called EditBranchProtectionFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
name, _ := req.GetArguments()["name"].(string)
|
||||
if owner == "" || repo == "" || name == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and name are required"))
|
||||
}
|
||||
opt := gitea_sdk.EditBranchProtectionOption{}
|
||||
if v, ok := req.GetArguments()["enable_push"].(bool); ok {
|
||||
opt.EnablePush = &v
|
||||
}
|
||||
if v, ok := req.GetArguments()["enable_merge_whitelist"].(bool); ok {
|
||||
opt.EnableMergeWhitelist = &v
|
||||
}
|
||||
if v, ok := req.GetArguments()["enable_status_check"].(bool); ok {
|
||||
opt.EnableStatusCheck = &v
|
||||
}
|
||||
if v, ok := req.GetArguments()["required_approvals"].(float64); ok {
|
||||
approvals := int64(v)
|
||||
opt.RequiredApprovals = &approvals
|
||||
}
|
||||
if v, ok := req.GetArguments()["block_on_rejected_reviews"].(bool); ok {
|
||||
opt.BlockOnRejectedReviews = &v
|
||||
}
|
||||
if v, ok := req.GetArguments()["dismiss_stale_approvals"].(bool); ok {
|
||||
opt.DismissStaleApprovals = &v
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
bp, _, err := client.EditBranchProtection(owner, repo, name, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("edit branch protection err: %v", err))
|
||||
}
|
||||
return to.TextResult(bp)
|
||||
}
|
||||
|
||||
func DeleteBranchProtectionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called DeleteBranchProtectionFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
name, _ := req.GetArguments()["name"].(string)
|
||||
if owner == "" || repo == "" || name == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and name are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
_, err = client.DeleteBranchProtection(owner, repo, name)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("delete branch protection err: %v", err))
|
||||
}
|
||||
return to.TextResult(map[string]string{"status": "deleted"})
|
||||
}
|
||||
233
operation/repo/collaborator.go
Normal file
233
operation/repo/collaborator.go
Normal file
@@ -0,0 +1,233 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/params"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
const (
|
||||
ListCollaboratorsToolName = "list_collaborators"
|
||||
IsCollaboratorToolName = "is_collaborator"
|
||||
CollaboratorPermissionToolName = "collaborator_permission"
|
||||
AddCollaboratorToolName = "add_collaborator"
|
||||
DeleteCollaboratorToolName = "delete_collaborator"
|
||||
GetReviewersToolName = "get_reviewers"
|
||||
GetAssigneesToolName = "get_assignees"
|
||||
)
|
||||
|
||||
var (
|
||||
ListCollaboratorsTool = mcp.NewTool(
|
||||
ListCollaboratorsToolName,
|
||||
mcp.WithDescription("List a repository's collaborators"),
|
||||
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.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)),
|
||||
)
|
||||
|
||||
IsCollaboratorTool = mcp.NewTool(
|
||||
IsCollaboratorToolName,
|
||||
mcp.WithDescription("Check if a user is a collaborator of a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("collaborator", mcp.Required(), mcp.Description("username to check")),
|
||||
)
|
||||
|
||||
CollaboratorPermissionTool = mcp.NewTool(
|
||||
CollaboratorPermissionToolName,
|
||||
mcp.WithDescription("Get a collaborator's permission level on a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("collaborator", mcp.Required(), mcp.Description("collaborator username")),
|
||||
)
|
||||
|
||||
AddCollaboratorTool = mcp.NewTool(
|
||||
AddCollaboratorToolName,
|
||||
mcp.WithDescription("Add a collaborator to a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("collaborator", mcp.Required(), mcp.Description("username to add")),
|
||||
mcp.WithString("permission", mcp.Description("permission level: read, write, admin (default: write)")),
|
||||
)
|
||||
|
||||
DeleteCollaboratorTool = mcp.NewTool(
|
||||
DeleteCollaboratorToolName,
|
||||
mcp.WithDescription("Remove a collaborator from a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("collaborator", mcp.Required(), mcp.Description("username to remove")),
|
||||
)
|
||||
|
||||
GetReviewersTool = mcp.NewTool(
|
||||
GetReviewersToolName,
|
||||
mcp.WithDescription("Get the list of users who can review PRs in a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
)
|
||||
|
||||
GetAssigneesTool = mcp.NewTool(
|
||||
GetAssigneesToolName,
|
||||
mcp.WithDescription("Get the list of users who can be assigned to issues in a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
Tool.RegisterRead(server.ServerTool{Tool: ListCollaboratorsTool, Handler: ListCollaboratorsFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: IsCollaboratorTool, Handler: IsCollaboratorFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: CollaboratorPermissionTool, Handler: CollaboratorPermissionFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: GetReviewersTool, Handler: GetReviewersFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: GetAssigneesTool, Handler: GetAssigneesFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: AddCollaboratorTool, Handler: AddCollaboratorFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: DeleteCollaboratorTool, Handler: DeleteCollaboratorFn})
|
||||
}
|
||||
|
||||
func ListCollaboratorsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called ListCollaboratorsFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
page := params.GetOptionalInt(req.GetArguments(), "page", 1)
|
||||
pageSize := params.GetOptionalInt(req.GetArguments(), "pageSize", 100)
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
users, _, err := client.ListCollaborators(owner, repo, gitea_sdk.ListCollaboratorsOptions{
|
||||
ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)},
|
||||
})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list collaborators err: %v", err))
|
||||
}
|
||||
return to.TextResult(users)
|
||||
}
|
||||
|
||||
func IsCollaboratorFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called IsCollaboratorFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
collaborator, _ := req.GetArguments()["collaborator"].(string)
|
||||
if owner == "" || repo == "" || collaborator == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and collaborator are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
isCollab, _, err := client.IsCollaborator(owner, repo, collaborator)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("check collaborator err: %v", err))
|
||||
}
|
||||
return to.TextResult(map[string]any{"collaborator": collaborator, "is_collaborator": isCollab})
|
||||
}
|
||||
|
||||
func CollaboratorPermissionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called CollaboratorPermissionFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
collaborator, _ := req.GetArguments()["collaborator"].(string)
|
||||
if owner == "" || repo == "" || collaborator == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and collaborator are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
perm, _, err := client.CollaboratorPermission(owner, repo, collaborator)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get collaborator permission err: %v", err))
|
||||
}
|
||||
return to.TextResult(perm)
|
||||
}
|
||||
|
||||
func AddCollaboratorFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called AddCollaboratorFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
collaborator, _ := req.GetArguments()["collaborator"].(string)
|
||||
if owner == "" || repo == "" || collaborator == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and collaborator are required"))
|
||||
}
|
||||
opt := gitea_sdk.AddCollaboratorOption{}
|
||||
if v, ok := req.GetArguments()["permission"].(string); ok {
|
||||
perm := gitea_sdk.AccessMode(v)
|
||||
opt.Permission = &perm
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
_, err = client.AddCollaborator(owner, repo, collaborator, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("add collaborator err: %v", err))
|
||||
}
|
||||
return to.TextResult(map[string]string{"status": "added", "collaborator": collaborator})
|
||||
}
|
||||
|
||||
func DeleteCollaboratorFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called DeleteCollaboratorFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
collaborator, _ := req.GetArguments()["collaborator"].(string)
|
||||
if owner == "" || repo == "" || collaborator == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and collaborator are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
_, err = client.DeleteCollaborator(owner, repo, collaborator)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("delete collaborator err: %v", err))
|
||||
}
|
||||
return to.TextResult(map[string]string{"status": "removed", "collaborator": collaborator})
|
||||
}
|
||||
|
||||
func GetReviewersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called GetReviewersFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
users, _, err := client.GetReviewers(owner, repo)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get reviewers err: %v", err))
|
||||
}
|
||||
return to.TextResult(users)
|
||||
}
|
||||
|
||||
func GetAssigneesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called GetAssigneesFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
users, _, err := client.GetAssignees(owner, repo)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get assignees err: %v", err))
|
||||
}
|
||||
return to.TextResult(users)
|
||||
}
|
||||
@@ -5,10 +5,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/log"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/params"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/params"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
|
||||
161
operation/repo/deploy_key.go
Normal file
161
operation/repo/deploy_key.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/params"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
const (
|
||||
ListDeployKeysToolName = "list_deploy_keys"
|
||||
GetDeployKeyToolName = "get_deploy_key"
|
||||
CreateDeployKeyToolName = "create_deploy_key"
|
||||
DeleteDeployKeyToolName = "delete_deploy_key"
|
||||
)
|
||||
|
||||
var (
|
||||
ListDeployKeysTool = mcp.NewTool(
|
||||
ListDeployKeysToolName,
|
||||
mcp.WithDescription("List a repository's deploy keys"),
|
||||
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.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)),
|
||||
)
|
||||
|
||||
GetDeployKeyTool = mcp.NewTool(
|
||||
GetDeployKeyToolName,
|
||||
mcp.WithDescription("Get a deploy key by ID"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithNumber("key_id", mcp.Required(), mcp.Description("deploy key ID")),
|
||||
)
|
||||
|
||||
CreateDeployKeyTool = mcp.NewTool(
|
||||
CreateDeployKeyToolName,
|
||||
mcp.WithDescription("Add a deploy key to a repository"),
|
||||
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("key title")),
|
||||
mcp.WithString("key", mcp.Required(), mcp.Description("public key content")),
|
||||
mcp.WithBoolean("read_only", mcp.Description("whether the key has read-only access (default: true)")),
|
||||
)
|
||||
|
||||
DeleteDeployKeyTool = mcp.NewTool(
|
||||
DeleteDeployKeyToolName,
|
||||
mcp.WithDescription("Remove a deploy key from a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithNumber("key_id", mcp.Required(), mcp.Description("deploy key ID")),
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
Tool.RegisterRead(server.ServerTool{Tool: ListDeployKeysTool, Handler: ListDeployKeysFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: GetDeployKeyTool, Handler: GetDeployKeyFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: CreateDeployKeyTool, Handler: CreateDeployKeyFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: DeleteDeployKeyTool, Handler: DeleteDeployKeyFn})
|
||||
}
|
||||
|
||||
func ListDeployKeysFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called ListDeployKeysFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
page := params.GetOptionalInt(req.GetArguments(), "page", 1)
|
||||
pageSize := params.GetOptionalInt(req.GetArguments(), "pageSize", 100)
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
keys, _, err := client.ListDeployKeys(owner, repo, gitea_sdk.ListDeployKeysOptions{
|
||||
ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)},
|
||||
})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list deploy keys err: %v", err))
|
||||
}
|
||||
return to.TextResult(keys)
|
||||
}
|
||||
|
||||
func GetDeployKeyFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called GetDeployKeyFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
keyID, err := params.GetIndex(req.GetArguments(), "key_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
key, _, err := client.GetDeployKey(owner, repo, keyID)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get deploy key err: %v", err))
|
||||
}
|
||||
return to.TextResult(key)
|
||||
}
|
||||
|
||||
func CreateDeployKeyFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called CreateDeployKeyFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
title, _ := req.GetArguments()["title"].(string)
|
||||
key, _ := req.GetArguments()["key"].(string)
|
||||
if owner == "" || repo == "" || title == "" || key == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, title, and key are required"))
|
||||
}
|
||||
opt := gitea_sdk.CreateKeyOption{
|
||||
Title: title,
|
||||
Key: key,
|
||||
ReadOnly: true,
|
||||
}
|
||||
if v, ok := req.GetArguments()["read_only"].(bool); ok {
|
||||
opt.ReadOnly = v
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
dk, _, err := client.CreateDeployKey(owner, repo, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("create deploy key err: %v", err))
|
||||
}
|
||||
return to.TextResult(dk)
|
||||
}
|
||||
|
||||
func DeleteDeployKeyFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called DeleteDeployKeyFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
keyID, err := params.GetIndex(req.GetArguments(), "key_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
_, err = client.DeleteDeployKey(owner, repo, keyID)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("delete deploy key err: %v", err))
|
||||
}
|
||||
return to.TextResult(map[string]string{"status": "deleted"})
|
||||
}
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/log"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
|
||||
175
operation/repo/git.go
Normal file
175
operation/repo/git.go
Normal file
@@ -0,0 +1,175 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
const (
|
||||
GetRepoRefToolName = "get_repo_ref"
|
||||
ListAllGitRefsToolName = "list_all_git_refs"
|
||||
GetTreeToolName = "get_tree"
|
||||
GetRepoNoteToolName = "get_repo_note"
|
||||
CompareCommitsToolName = "compare_commits"
|
||||
)
|
||||
|
||||
var (
|
||||
GetRepoRefTool = mcp.NewTool(
|
||||
GetRepoRefToolName,
|
||||
mcp.WithDescription("Get a git reference from a repository"),
|
||||
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("git reference (e.g., refs/heads/main, refs/tags/v1.0)")),
|
||||
)
|
||||
|
||||
ListAllGitRefsTool = mcp.NewTool(
|
||||
ListAllGitRefsToolName,
|
||||
mcp.WithDescription("List all git references in a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
)
|
||||
|
||||
GetTreeTool = mcp.NewTool(
|
||||
GetTreeToolName,
|
||||
mcp.WithDescription("Get the tree of a repository at a given SHA"),
|
||||
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("SHA of the tree")),
|
||||
mcp.WithBoolean("recursive", mcp.Description("show all items recursively")),
|
||||
)
|
||||
|
||||
GetRepoNoteTool = mcp.NewTool(
|
||||
GetRepoNoteToolName,
|
||||
mcp.WithDescription("Get a git note for a commit"),
|
||||
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")),
|
||||
)
|
||||
|
||||
CompareCommitsTool = mcp.NewTool(
|
||||
CompareCommitsToolName,
|
||||
mcp.WithDescription("Compare two commits in a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("base", mcp.Required(), mcp.Description("base commit SHA or branch")),
|
||||
mcp.WithString("head", mcp.Required(), mcp.Description("head commit SHA or branch")),
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
Tool.RegisterRead(server.ServerTool{Tool: GetRepoRefTool, Handler: GetRepoRefFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: ListAllGitRefsTool, Handler: ListAllGitRefsFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: GetTreeTool, Handler: GetTreeFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: GetRepoNoteTool, Handler: GetRepoNoteFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: CompareCommitsTool, Handler: CompareCommitsFn})
|
||||
}
|
||||
|
||||
func GetRepoRefFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called GetRepoRefFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
ref, _ := req.GetArguments()["ref"].(string)
|
||||
if owner == "" || repo == "" || ref == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and ref are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
reference, _, err := client.GetRepoRef(owner, repo, ref)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get repo ref err: %v", err))
|
||||
}
|
||||
return to.TextResult(reference)
|
||||
}
|
||||
|
||||
func ListAllGitRefsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called ListAllGitRefsFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
refs, _, err := client.ListAllGitRefs(owner, repo)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list git refs err: %v", err))
|
||||
}
|
||||
return to.TextResult(refs)
|
||||
}
|
||||
|
||||
func GetTreeFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called GetTreeFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
sha, _ := req.GetArguments()["sha"].(string)
|
||||
if owner == "" || repo == "" || sha == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and sha are required"))
|
||||
}
|
||||
opt := gitea_sdk.ListTreeOptions{
|
||||
Ref: sha,
|
||||
}
|
||||
if v, ok := req.GetArguments()["recursive"].(bool); ok && v {
|
||||
opt.Recursive = true
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
tree, _, err := client.GetTrees(owner, repo, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get tree err: %v", err))
|
||||
}
|
||||
return to.TextResult(tree)
|
||||
}
|
||||
|
||||
func GetRepoNoteFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called GetRepoNoteFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
sha, _ := req.GetArguments()["sha"].(string)
|
||||
if owner == "" || repo == "" || sha == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and sha are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
note, _, err := client.GetRepoNote(owner, repo, sha, gitea_sdk.GetRepoNoteOptions{})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get repo note err: %v", err))
|
||||
}
|
||||
return to.TextResult(note)
|
||||
}
|
||||
|
||||
func CompareCommitsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called CompareCommitsFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
base, _ := req.GetArguments()["base"].(string)
|
||||
head, _ := req.GetArguments()["head"].(string)
|
||||
if owner == "" || repo == "" || base == "" || head == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, base, and head are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
compare, _, err := client.CompareCommits(owner, repo, base, head)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("compare commits err: %v", err))
|
||||
}
|
||||
return to.TextResult(compare)
|
||||
}
|
||||
227
operation/repo/hook.go
Normal file
227
operation/repo/hook.go
Normal file
@@ -0,0 +1,227 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/params"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
const (
|
||||
ListRepoHooksToolName = "list_repo_hooks"
|
||||
GetRepoHookToolName = "get_repo_hook"
|
||||
CreateRepoHookToolName = "create_repo_hook"
|
||||
EditRepoHookToolName = "edit_repo_hook"
|
||||
DeleteRepoHookToolName = "delete_repo_hook"
|
||||
)
|
||||
|
||||
var (
|
||||
ListRepoHooksTool = mcp.NewTool(
|
||||
ListRepoHooksToolName,
|
||||
mcp.WithDescription("List a repository's webhooks"),
|
||||
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.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)),
|
||||
)
|
||||
|
||||
GetRepoHookTool = mcp.NewTool(
|
||||
GetRepoHookToolName,
|
||||
mcp.WithDescription("Get a repository webhook by ID"),
|
||||
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("webhook ID")),
|
||||
)
|
||||
|
||||
CreateRepoHookTool = mcp.NewTool(
|
||||
CreateRepoHookToolName,
|
||||
mcp.WithDescription("Create a webhook for a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("type", mcp.Required(), mcp.Description("hook type: gitea, gogs, slack, discord, dingtalk, telegram, msteams, feishu, wechatwork, packagist")),
|
||||
mcp.WithString("url", mcp.Required(), mcp.Description("target URL for the webhook")),
|
||||
mcp.WithString("content_type", mcp.Description("content type: json or form (default: json)")),
|
||||
mcp.WithString("secret", mcp.Description("webhook secret")),
|
||||
mcp.WithBoolean("active", mcp.Description("whether the webhook is active (default: true)")),
|
||||
)
|
||||
|
||||
EditRepoHookTool = mcp.NewTool(
|
||||
EditRepoHookToolName,
|
||||
mcp.WithDescription("Edit a repository webhook"),
|
||||
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("webhook ID")),
|
||||
mcp.WithString("url", mcp.Description("target URL")),
|
||||
mcp.WithString("content_type", mcp.Description("content type: json or form")),
|
||||
mcp.WithString("secret", mcp.Description("webhook secret")),
|
||||
mcp.WithBoolean("active", mcp.Description("whether the webhook is active")),
|
||||
)
|
||||
|
||||
DeleteRepoHookTool = mcp.NewTool(
|
||||
DeleteRepoHookToolName,
|
||||
mcp.WithDescription("Delete a repository webhook"),
|
||||
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("webhook ID")),
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
Tool.RegisterRead(server.ServerTool{Tool: ListRepoHooksTool, Handler: ListRepoHooksFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: GetRepoHookTool, Handler: GetRepoHookFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: CreateRepoHookTool, Handler: CreateRepoHookFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: EditRepoHookTool, Handler: EditRepoHookFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: DeleteRepoHookTool, Handler: DeleteRepoHookFn})
|
||||
}
|
||||
|
||||
func ListRepoHooksFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called ListRepoHooksFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
page := params.GetOptionalInt(req.GetArguments(), "page", 1)
|
||||
pageSize := params.GetOptionalInt(req.GetArguments(), "pageSize", 100)
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
hooks, _, err := client.ListRepoHooks(owner, repo, gitea_sdk.ListHooksOptions{
|
||||
ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)},
|
||||
})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list repo hooks err: %v", err))
|
||||
}
|
||||
return to.TextResult(hooks)
|
||||
}
|
||||
|
||||
func GetRepoHookFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called GetRepoHookFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
id, err := params.GetIndex(req.GetArguments(), "id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
hook, _, err := client.GetRepoHook(owner, repo, id)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get repo hook err: %v", err))
|
||||
}
|
||||
return to.TextResult(hook)
|
||||
}
|
||||
|
||||
func CreateRepoHookFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called CreateRepoHookFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
hookType, _ := req.GetArguments()["type"].(string)
|
||||
url, _ := req.GetArguments()["url"].(string)
|
||||
if owner == "" || repo == "" || hookType == "" || url == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, type, and url are required"))
|
||||
}
|
||||
contentType := "json"
|
||||
if v, ok := req.GetArguments()["content_type"].(string); ok {
|
||||
contentType = v
|
||||
}
|
||||
config := map[string]string{
|
||||
"url": url,
|
||||
"content_type": contentType,
|
||||
}
|
||||
if v, ok := req.GetArguments()["secret"].(string); ok {
|
||||
config["secret"] = v
|
||||
}
|
||||
opt := gitea_sdk.CreateHookOption{
|
||||
Type: gitea_sdk.HookType(hookType),
|
||||
Config: config,
|
||||
Active: true,
|
||||
}
|
||||
if v, ok := req.GetArguments()["active"].(bool); ok {
|
||||
opt.Active = v
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
hook, _, err := client.CreateRepoHook(owner, repo, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("create repo hook err: %v", err))
|
||||
}
|
||||
return to.TextResult(hook)
|
||||
}
|
||||
|
||||
func EditRepoHookFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called EditRepoHookFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
id, err := params.GetIndex(req.GetArguments(), "id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
opt := gitea_sdk.EditHookOption{}
|
||||
config := map[string]string{}
|
||||
if v, ok := req.GetArguments()["url"].(string); ok {
|
||||
config["url"] = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["content_type"].(string); ok {
|
||||
config["content_type"] = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["secret"].(string); ok {
|
||||
config["secret"] = v
|
||||
}
|
||||
if len(config) > 0 {
|
||||
opt.Config = config
|
||||
}
|
||||
if v, ok := req.GetArguments()["active"].(bool); ok {
|
||||
opt.Active = &v
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
_, err = client.EditRepoHook(owner, repo, id, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("edit repo hook err: %v", err))
|
||||
}
|
||||
return to.TextResult(map[string]string{"status": "updated"})
|
||||
}
|
||||
|
||||
func DeleteRepoHookFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called DeleteRepoHookFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
id, err := params.GetIndex(req.GetArguments(), "id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
_, err = client.DeleteRepoHook(owner, repo, id)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("delete repo hook err: %v", err))
|
||||
}
|
||||
return to.TextResult(map[string]string{"status": "deleted"})
|
||||
}
|
||||
156
operation/repo/mirror.go
Normal file
156
operation/repo/mirror.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
const (
|
||||
CreatePushMirrorToolName = "create_push_mirror"
|
||||
ListPushMirrorsToolName = "list_push_mirrors"
|
||||
GetPushMirrorByRemoteNameToolName = "get_push_mirror"
|
||||
DeletePushMirrorToolName = "delete_push_mirror"
|
||||
)
|
||||
|
||||
var (
|
||||
CreatePushMirrorTool = mcp.NewTool(
|
||||
CreatePushMirrorToolName,
|
||||
mcp.WithDescription("Create a push mirror for a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("remote_address", mcp.Required(), mcp.Description("remote git URL to push to")),
|
||||
mcp.WithString("remote_username", mcp.Description("remote username for authentication")),
|
||||
mcp.WithString("remote_password", mcp.Description("remote password/token for authentication")),
|
||||
mcp.WithString("interval", mcp.Description("sync interval (e.g., 8h0m0s)")),
|
||||
mcp.WithBoolean("sync_on_commit", mcp.Description("sync on commit")),
|
||||
)
|
||||
|
||||
ListPushMirrorsTool = mcp.NewTool(
|
||||
ListPushMirrorsToolName,
|
||||
mcp.WithDescription("List push mirrors for a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
)
|
||||
|
||||
GetPushMirrorByRemoteNameTool = mcp.NewTool(
|
||||
GetPushMirrorByRemoteNameToolName,
|
||||
mcp.WithDescription("Get a push mirror by remote name"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("remote_name", mcp.Required(), mcp.Description("remote name")),
|
||||
)
|
||||
|
||||
DeletePushMirrorTool = mcp.NewTool(
|
||||
DeletePushMirrorToolName,
|
||||
mcp.WithDescription("Delete a push mirror by remote name"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("remote_name", mcp.Required(), mcp.Description("remote name to delete")),
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
Tool.RegisterRead(server.ServerTool{Tool: ListPushMirrorsTool, Handler: ListPushMirrorsFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: GetPushMirrorByRemoteNameTool, Handler: GetPushMirrorByRemoteNameFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: CreatePushMirrorTool, Handler: CreatePushMirrorFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: DeletePushMirrorTool, Handler: DeletePushMirrorFn})
|
||||
}
|
||||
|
||||
func CreatePushMirrorFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called CreatePushMirrorFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
remoteAddr, _ := req.GetArguments()["remote_address"].(string)
|
||||
if owner == "" || repo == "" || remoteAddr == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and remote_address are required"))
|
||||
}
|
||||
opt := gitea_sdk.CreatePushMirrorOption{
|
||||
RemoteAddress: remoteAddr,
|
||||
}
|
||||
if v, ok := req.GetArguments()["remote_username"].(string); ok {
|
||||
opt.RemoteUsername = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["remote_password"].(string); ok {
|
||||
opt.RemotePassword = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["interval"].(string); ok {
|
||||
opt.Interval = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["sync_on_commit"].(bool); ok {
|
||||
opt.SyncONCommit = v
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
mirror, _, err := client.PushMirrors(owner, repo, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("create push mirror err: %v", err))
|
||||
}
|
||||
return to.TextResult(mirror)
|
||||
}
|
||||
|
||||
func ListPushMirrorsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called ListPushMirrorsFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
mirrors, _, err := client.ListPushMirrors(owner, repo, gitea_sdk.ListOptions{})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list push mirrors err: %v", err))
|
||||
}
|
||||
return to.TextResult(mirrors)
|
||||
}
|
||||
|
||||
func GetPushMirrorByRemoteNameFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called GetPushMirrorByRemoteNameFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
remoteName, _ := req.GetArguments()["remote_name"].(string)
|
||||
if owner == "" || repo == "" || remoteName == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and remote_name are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
mirror, _, err := client.GetPushMirrorByRemoteName(owner, repo, remoteName)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get push mirror err: %v", err))
|
||||
}
|
||||
return to.TextResult(mirror)
|
||||
}
|
||||
|
||||
func DeletePushMirrorFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called DeletePushMirrorFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
remoteName, _ := req.GetArguments()["remote_name"].(string)
|
||||
if owner == "" || repo == "" || remoteName == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and remote_name are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
_, err = client.DeletePushMirror(owner, repo, remoteName)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("delete push mirror err: %v", err))
|
||||
}
|
||||
return to.TextResult(map[string]string{"status": "deleted"})
|
||||
}
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/log"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/params"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/params"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
|
||||
@@ -5,11 +5,11 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/log"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/params"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/params"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/tool"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
|
||||
216
operation/repo/star_watch.go
Normal file
216
operation/repo/star_watch.go
Normal file
@@ -0,0 +1,216 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/params"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
const (
|
||||
ListStargazersToolName = "list_stargazers"
|
||||
ListForksToolName = "list_forks"
|
||||
GetWatchedReposToolName = "get_watched_repos"
|
||||
GetMyWatchedReposToolName = "get_my_watched_repos"
|
||||
CheckRepoWatchToolName = "check_repo_watch"
|
||||
WatchRepoToolName = "watch_repo"
|
||||
UnWatchRepoToolName = "unwatch_repo"
|
||||
)
|
||||
|
||||
var (
|
||||
ListStargazersTool = mcp.NewTool(
|
||||
ListStargazersToolName,
|
||||
mcp.WithDescription("List a repository's stargazers"),
|
||||
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.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)),
|
||||
)
|
||||
|
||||
ListForksTool = mcp.NewTool(
|
||||
ListForksToolName,
|
||||
mcp.WithDescription("List a repository's forks"),
|
||||
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.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)),
|
||||
)
|
||||
|
||||
GetWatchedReposTool = mcp.NewTool(
|
||||
GetWatchedReposToolName,
|
||||
mcp.WithDescription("List repositories watched by a user"),
|
||||
mcp.WithString("username", mcp.Required(), mcp.Description("username")),
|
||||
)
|
||||
|
||||
GetMyWatchedReposTool = mcp.NewTool(
|
||||
GetMyWatchedReposToolName,
|
||||
mcp.WithDescription("List repositories watched by the authenticated user"),
|
||||
)
|
||||
|
||||
CheckRepoWatchTool = mcp.NewTool(
|
||||
CheckRepoWatchToolName,
|
||||
mcp.WithDescription("Check if the authenticated user is watching a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
)
|
||||
|
||||
WatchRepoTool = mcp.NewTool(
|
||||
WatchRepoToolName,
|
||||
mcp.WithDescription("Watch a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
)
|
||||
|
||||
UnWatchRepoTool = mcp.NewTool(
|
||||
UnWatchRepoToolName,
|
||||
mcp.WithDescription("Unwatch a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
Tool.RegisterRead(server.ServerTool{Tool: ListStargazersTool, Handler: ListStargazersFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: ListForksTool, Handler: ListForksFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: GetWatchedReposTool, Handler: GetWatchedReposFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: GetMyWatchedReposTool, Handler: GetMyWatchedReposFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: CheckRepoWatchTool, Handler: CheckRepoWatchFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: WatchRepoTool, Handler: WatchRepoFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: UnWatchRepoTool, Handler: UnWatchRepoFn})
|
||||
}
|
||||
|
||||
func ListStargazersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called ListStargazersFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
page := params.GetOptionalInt(req.GetArguments(), "page", 1)
|
||||
pageSize := params.GetOptionalInt(req.GetArguments(), "pageSize", 100)
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
users, _, err := client.ListRepoStargazers(owner, repo, gitea_sdk.ListStargazersOptions{
|
||||
ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)},
|
||||
})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list stargazers err: %v", err))
|
||||
}
|
||||
return to.TextResult(users)
|
||||
}
|
||||
|
||||
func ListForksFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called ListForksFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
page := params.GetOptionalInt(req.GetArguments(), "page", 1)
|
||||
pageSize := params.GetOptionalInt(req.GetArguments(), "pageSize", 100)
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
repos, _, err := client.ListForks(owner, repo, gitea_sdk.ListForksOptions{
|
||||
ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)},
|
||||
})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list forks err: %v", err))
|
||||
}
|
||||
return to.TextResult(repos)
|
||||
}
|
||||
|
||||
func GetWatchedReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called GetWatchedReposFn")
|
||||
username, _ := req.GetArguments()["username"].(string)
|
||||
if username == "" {
|
||||
return to.ErrorResult(errors.New("username is required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
repos, _, err := client.GetWatchedRepos(username)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get watched repos err: %v", err))
|
||||
}
|
||||
return to.TextResult(repos)
|
||||
}
|
||||
|
||||
func GetMyWatchedReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called GetMyWatchedReposFn")
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
repos, _, err := client.GetMyWatchedRepos()
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get my watched repos err: %v", err))
|
||||
}
|
||||
return to.TextResult(repos)
|
||||
}
|
||||
|
||||
func CheckRepoWatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called CheckRepoWatchFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
watching, _, err := client.CheckRepoWatch(owner, repo)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("check repo watch err: %v", err))
|
||||
}
|
||||
return to.TextResult(map[string]any{"watching": watching})
|
||||
}
|
||||
|
||||
func WatchRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called WatchRepoFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
_, err = client.WatchRepo(owner, repo)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("watch repo err: %v", err))
|
||||
}
|
||||
return to.TextResult(map[string]string{"status": "watching"})
|
||||
}
|
||||
|
||||
func UnWatchRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called UnWatchRepoFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
_, err = client.UnWatchRepo(owner, repo)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("unwatch repo err: %v", err))
|
||||
}
|
||||
return to.TextResult(map[string]string{"status": "unwatched"})
|
||||
}
|
||||
134
operation/repo/status.go
Normal file
134
operation/repo/status.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/params"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
const (
|
||||
CreateStatusToolName = "create_commit_status"
|
||||
ListStatusesToolName = "list_commit_statuses"
|
||||
GetCombinedStatusToolName = "get_combined_status"
|
||||
)
|
||||
|
||||
var (
|
||||
CreateStatusTool = mcp.NewTool(
|
||||
CreateStatusToolName,
|
||||
mcp.WithDescription("Create a commit status"),
|
||||
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")),
|
||||
mcp.WithString("state", mcp.Required(), mcp.Description("status state: pending, success, error, failure, warning")),
|
||||
mcp.WithString("target_url", mcp.Description("URL for status details")),
|
||||
mcp.WithString("description", mcp.Description("status description")),
|
||||
mcp.WithString("context", mcp.Description("status context (e.g., ci/build)")),
|
||||
)
|
||||
|
||||
ListStatusesTool = mcp.NewTool(
|
||||
ListStatusesToolName,
|
||||
mcp.WithDescription("List commit statuses for a ref"),
|
||||
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("commit SHA, branch, or tag")),
|
||||
mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)),
|
||||
mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)),
|
||||
)
|
||||
|
||||
GetCombinedStatusTool = mcp.NewTool(
|
||||
GetCombinedStatusToolName,
|
||||
mcp.WithDescription("Get the combined status for a ref"),
|
||||
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("commit SHA, branch, or tag")),
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
Tool.RegisterRead(server.ServerTool{Tool: ListStatusesTool, Handler: ListStatusesFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: GetCombinedStatusTool, Handler: GetCombinedStatusFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: CreateStatusTool, Handler: CreateStatusFn})
|
||||
}
|
||||
|
||||
func CreateStatusFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called CreateStatusFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
sha, _ := req.GetArguments()["sha"].(string)
|
||||
state, _ := req.GetArguments()["state"].(string)
|
||||
if owner == "" || repo == "" || sha == "" || state == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, sha, and state are required"))
|
||||
}
|
||||
opt := gitea_sdk.CreateStatusOption{
|
||||
State: gitea_sdk.StatusState(state),
|
||||
}
|
||||
if v, ok := req.GetArguments()["target_url"].(string); ok {
|
||||
opt.TargetURL = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["description"].(string); ok {
|
||||
opt.Description = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["context"].(string); ok {
|
||||
opt.Context = v
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
status, _, err := client.CreateStatus(owner, repo, sha, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("create status err: %v", err))
|
||||
}
|
||||
return to.TextResult(status)
|
||||
}
|
||||
|
||||
func ListStatusesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called ListStatusesFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
ref, _ := req.GetArguments()["ref"].(string)
|
||||
if owner == "" || repo == "" || ref == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and ref are required"))
|
||||
}
|
||||
page := params.GetOptionalInt(req.GetArguments(), "page", 1)
|
||||
pageSize := params.GetOptionalInt(req.GetArguments(), "pageSize", 100)
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
statuses, _, err := client.ListStatuses(owner, repo, ref, gitea_sdk.ListStatusesOption{
|
||||
ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)},
|
||||
})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list statuses err: %v", err))
|
||||
}
|
||||
return to.TextResult(statuses)
|
||||
}
|
||||
|
||||
func GetCombinedStatusFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called GetCombinedStatusFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
ref, _ := req.GetArguments()["ref"].(string)
|
||||
if owner == "" || repo == "" || ref == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and ref are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
combined, _, err := client.GetCombinedStatus(owner, repo, ref)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get combined status err: %v", err))
|
||||
}
|
||||
return to.TextResult(combined)
|
||||
}
|
||||
@@ -5,10 +5,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/log"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/params"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/params"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
|
||||
200
operation/repo/tag_protection.go
Normal file
200
operation/repo/tag_protection.go
Normal file
@@ -0,0 +1,200 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/params"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
const (
|
||||
ListTagProtectionsToolName = "list_tag_protections"
|
||||
GetTagProtectionToolName = "get_tag_protection"
|
||||
CreateTagProtectionToolName = "create_tag_protection"
|
||||
EditTagProtectionToolName = "edit_tag_protection"
|
||||
DeleteTagProtectionToolName = "delete_tag_protection"
|
||||
)
|
||||
|
||||
var (
|
||||
ListTagProtectionsTool = mcp.NewTool(
|
||||
ListTagProtectionsToolName,
|
||||
mcp.WithDescription("List tag protections for a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
)
|
||||
|
||||
GetTagProtectionTool = mcp.NewTool(
|
||||
GetTagProtectionToolName,
|
||||
mcp.WithDescription("Get a tag protection rule by ID"),
|
||||
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("tag protection ID")),
|
||||
)
|
||||
|
||||
CreateTagProtectionTool = mcp.NewTool(
|
||||
CreateTagProtectionToolName,
|
||||
mcp.WithDescription("Create a tag protection rule"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("name_pattern", mcp.Required(), mcp.Description("glob pattern for tag name matching")),
|
||||
mcp.WithString("whitelist_usernames", mcp.Description("comma-separated list of allowed usernames")),
|
||||
mcp.WithString("whitelist_teams", mcp.Description("comma-separated list of allowed team names")),
|
||||
)
|
||||
|
||||
EditTagProtectionTool = mcp.NewTool(
|
||||
EditTagProtectionToolName,
|
||||
mcp.WithDescription("Edit a tag protection rule"),
|
||||
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("tag protection ID")),
|
||||
mcp.WithString("name_pattern", mcp.Description("glob pattern for tag name matching")),
|
||||
mcp.WithString("whitelist_usernames", mcp.Description("comma-separated list of allowed usernames")),
|
||||
mcp.WithString("whitelist_teams", mcp.Description("comma-separated list of allowed team names")),
|
||||
)
|
||||
|
||||
DeleteTagProtectionTool = mcp.NewTool(
|
||||
DeleteTagProtectionToolName,
|
||||
mcp.WithDescription("Delete a tag protection rule"),
|
||||
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("tag protection ID")),
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
Tool.RegisterRead(server.ServerTool{Tool: ListTagProtectionsTool, Handler: ListTagProtectionsFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: GetTagProtectionTool, Handler: GetTagProtectionFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: CreateTagProtectionTool, Handler: CreateTagProtectionFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: EditTagProtectionTool, Handler: EditTagProtectionFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: DeleteTagProtectionTool, Handler: DeleteTagProtectionFn})
|
||||
}
|
||||
|
||||
func ListTagProtectionsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called ListTagProtectionsFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
tps, _, err := client.ListTagProtection(owner, repo, gitea_sdk.ListRepoTagProtectionsOptions{})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list tag protections err: %v", err))
|
||||
}
|
||||
return to.TextResult(tps)
|
||||
}
|
||||
|
||||
func GetTagProtectionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called GetTagProtectionFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
id, err := params.GetIndex(req.GetArguments(), "id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
tp, _, err := client.GetTagProtection(owner, repo, id)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get tag protection err: %v", err))
|
||||
}
|
||||
return to.TextResult(tp)
|
||||
}
|
||||
|
||||
func CreateTagProtectionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called CreateTagProtectionFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
namePattern, _ := req.GetArguments()["name_pattern"].(string)
|
||||
if owner == "" || repo == "" || namePattern == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and name_pattern are required"))
|
||||
}
|
||||
opt := gitea_sdk.CreateTagProtectionOption{
|
||||
NamePattern: namePattern,
|
||||
}
|
||||
if v, ok := req.GetArguments()["whitelist_usernames"].(string); ok && v != "" {
|
||||
opt.WhitelistUsernames = splitAndTrim(v)
|
||||
}
|
||||
if v, ok := req.GetArguments()["whitelist_teams"].(string); ok && v != "" {
|
||||
opt.WhitelistTeams = splitAndTrim(v)
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
tp, _, err := client.CreateTagProtection(owner, repo, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("create tag protection err: %v", err))
|
||||
}
|
||||
return to.TextResult(tp)
|
||||
}
|
||||
|
||||
func EditTagProtectionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called EditTagProtectionFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
id, err := params.GetIndex(req.GetArguments(), "id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
opt := gitea_sdk.EditTagProtectionOption{}
|
||||
if v, ok := req.GetArguments()["name_pattern"].(string); ok {
|
||||
opt.NamePattern = &v
|
||||
}
|
||||
if v, ok := req.GetArguments()["whitelist_usernames"].(string); ok {
|
||||
opt.WhitelistUsernames = splitAndTrim(v)
|
||||
}
|
||||
if v, ok := req.GetArguments()["whitelist_teams"].(string); ok {
|
||||
opt.WhitelistTeams = splitAndTrim(v)
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
tp, _, err := client.EditTagProtection(owner, repo, id, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("edit tag protection err: %v", err))
|
||||
}
|
||||
return to.TextResult(tp)
|
||||
}
|
||||
|
||||
func DeleteTagProtectionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called DeleteTagProtectionFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
id, err := params.GetIndex(req.GetArguments(), "id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
_, err = client.DeleteTagProtection(owner, repo, id)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("delete tag protection err: %v", err))
|
||||
}
|
||||
return to.TextResult(map[string]string{"status": "deleted"})
|
||||
}
|
||||
157
operation/repo/topic.go
Normal file
157
operation/repo/topic.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
const (
|
||||
ListRepoTopicsToolName = "list_repo_topics"
|
||||
SetRepoTopicsToolName = "set_repo_topics"
|
||||
AddRepoTopicToolName = "add_repo_topic"
|
||||
DeleteRepoTopicToolName = "delete_repo_topic"
|
||||
)
|
||||
|
||||
var (
|
||||
ListRepoTopicsTool = mcp.NewTool(
|
||||
ListRepoTopicsToolName,
|
||||
mcp.WithDescription("List a repository's topics"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
)
|
||||
|
||||
SetRepoTopicsTool = mcp.NewTool(
|
||||
SetRepoTopicsToolName,
|
||||
mcp.WithDescription("Replace all topics of a repository with a new list"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("topics", mcp.Required(), mcp.Description("comma-separated list of topics")),
|
||||
)
|
||||
|
||||
AddRepoTopicTool = mcp.NewTool(
|
||||
AddRepoTopicToolName,
|
||||
mcp.WithDescription("Add a topic to a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("topic", mcp.Required(), mcp.Description("topic name to add")),
|
||||
)
|
||||
|
||||
DeleteRepoTopicTool = mcp.NewTool(
|
||||
DeleteRepoTopicToolName,
|
||||
mcp.WithDescription("Remove a topic from a repository"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("topic", mcp.Required(), mcp.Description("topic name to remove")),
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
Tool.RegisterRead(server.ServerTool{Tool: ListRepoTopicsTool, Handler: ListRepoTopicsFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: SetRepoTopicsTool, Handler: SetRepoTopicsFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: AddRepoTopicTool, Handler: AddRepoTopicFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: DeleteRepoTopicTool, Handler: DeleteRepoTopicFn})
|
||||
}
|
||||
|
||||
func ListRepoTopicsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called ListRepoTopicsFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
topics, _, err := client.ListRepoTopics(owner, repo, gitea_sdk.ListRepoTopicsOptions{})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list repo topics err: %v", err))
|
||||
}
|
||||
return to.TextResult(topics)
|
||||
}
|
||||
|
||||
func SetRepoTopicsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called SetRepoTopicsFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
topicsStr, _ := req.GetArguments()["topics"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
var topics []string
|
||||
if topicsStr != "" {
|
||||
for _, t := range splitAndTrim(topicsStr) {
|
||||
if t != "" {
|
||||
topics = append(topics, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
_, err = client.SetRepoTopics(owner, repo, topics)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("set repo topics err: %v", err))
|
||||
}
|
||||
return to.TextResult(map[string]any{"status": "updated", "topics": topics})
|
||||
}
|
||||
|
||||
func AddRepoTopicFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called AddRepoTopicFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
topic, _ := req.GetArguments()["topic"].(string)
|
||||
if owner == "" || repo == "" || topic == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and topic are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
_, err = client.AddRepoTopic(owner, repo, topic)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("add repo topic err: %v", err))
|
||||
}
|
||||
return to.TextResult(map[string]string{"status": "added", "topic": topic})
|
||||
}
|
||||
|
||||
func DeleteRepoTopicFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called DeleteRepoTopicFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
topic, _ := req.GetArguments()["topic"].(string)
|
||||
if owner == "" || repo == "" || topic == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and topic are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
_, err = client.DeleteRepoTopic(owner, repo, topic)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("delete repo topic err: %v", err))
|
||||
}
|
||||
return to.TextResult(map[string]string{"status": "deleted", "topic": topic})
|
||||
}
|
||||
|
||||
func splitAndTrim(s string) []string {
|
||||
var result []string
|
||||
for _, part := range strings.Split(s, ",") {
|
||||
trimmed := strings.TrimSpace(part)
|
||||
if trimmed != "" {
|
||||
result = append(result, trimmed)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
167
operation/repo/transfer.go
Normal file
167
operation/repo/transfer.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
|
||||
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
const (
|
||||
TransferRepoToolName = "transfer_repo"
|
||||
AcceptRepoTransferToolName = "accept_repo_transfer"
|
||||
RejectRepoTransferToolName = "reject_repo_transfer"
|
||||
CreateFromTemplateToolName = "create_repo_from_template"
|
||||
)
|
||||
|
||||
var (
|
||||
TransferRepoTool = mcp.NewTool(
|
||||
TransferRepoToolName,
|
||||
mcp.WithDescription("Transfer a repository to a new owner"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("current repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
mcp.WithString("new_owner", mcp.Required(), mcp.Description("new owner username or org")),
|
||||
)
|
||||
|
||||
AcceptRepoTransferTool = mcp.NewTool(
|
||||
AcceptRepoTransferToolName,
|
||||
mcp.WithDescription("Accept a pending repository transfer"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
)
|
||||
|
||||
RejectRepoTransferTool = mcp.NewTool(
|
||||
RejectRepoTransferToolName,
|
||||
mcp.WithDescription("Reject a pending repository transfer"),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||
)
|
||||
|
||||
CreateFromTemplateTool = mcp.NewTool(
|
||||
CreateFromTemplateToolName,
|
||||
mcp.WithDescription("Create a repository from a template"),
|
||||
mcp.WithString("template_owner", mcp.Required(), mcp.Description("template repository owner")),
|
||||
mcp.WithString("template_repo", mcp.Required(), mcp.Description("template repository name")),
|
||||
mcp.WithString("name", mcp.Required(), mcp.Description("name for the new repository")),
|
||||
mcp.WithString("owner", mcp.Required(), mcp.Description("owner of the new repository (user or org)")),
|
||||
mcp.WithString("description", mcp.Description("description for the new repository")),
|
||||
mcp.WithBoolean("private", mcp.Description("whether the new repository is private")),
|
||||
mcp.WithBoolean("git_content", mcp.Description("copy git content from template (default: true)")),
|
||||
mcp.WithBoolean("topics", mcp.Description("copy topics from template")),
|
||||
mcp.WithBoolean("labels", mcp.Description("copy labels from template")),
|
||||
mcp.WithBoolean("webhooks", mcp.Description("copy webhooks from template")),
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: TransferRepoTool, Handler: TransferRepoFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: AcceptRepoTransferTool, Handler: AcceptRepoTransferFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: RejectRepoTransferTool, Handler: RejectRepoTransferFn})
|
||||
Tool.RegisterWrite(server.ServerTool{Tool: CreateFromTemplateTool, Handler: CreateFromTemplateFn})
|
||||
}
|
||||
|
||||
func TransferRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called TransferRepoFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
newOwner, _ := req.GetArguments()["new_owner"].(string)
|
||||
if owner == "" || repo == "" || newOwner == "" {
|
||||
return to.ErrorResult(errors.New("owner, repo, and new_owner are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
repository, _, err := client.TransferRepo(owner, repo, gitea_sdk.TransferRepoOption{
|
||||
NewOwner: newOwner,
|
||||
})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("transfer repo err: %v", err))
|
||||
}
|
||||
return to.TextResult(repository)
|
||||
}
|
||||
|
||||
func AcceptRepoTransferFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called AcceptRepoTransferFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
repository, _, err := client.AcceptRepoTransfer(owner, repo)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("accept repo transfer err: %v", err))
|
||||
}
|
||||
return to.TextResult(repository)
|
||||
}
|
||||
|
||||
func RejectRepoTransferFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called RejectRepoTransferFn")
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
repo, _ := req.GetArguments()["repo"].(string)
|
||||
if owner == "" || repo == "" {
|
||||
return to.ErrorResult(errors.New("owner and repo are required"))
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
repository, _, err := client.RejectRepoTransfer(owner, repo)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("reject repo transfer err: %v", err))
|
||||
}
|
||||
return to.TextResult(repository)
|
||||
}
|
||||
|
||||
func CreateFromTemplateFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called CreateFromTemplateFn")
|
||||
templateOwner, _ := req.GetArguments()["template_owner"].(string)
|
||||
templateRepo, _ := req.GetArguments()["template_repo"].(string)
|
||||
name, _ := req.GetArguments()["name"].(string)
|
||||
owner, _ := req.GetArguments()["owner"].(string)
|
||||
if templateOwner == "" || templateRepo == "" || name == "" || owner == "" {
|
||||
return to.ErrorResult(errors.New("template_owner, template_repo, name, and owner are required"))
|
||||
}
|
||||
opt := gitea_sdk.CreateRepoFromTemplateOption{
|
||||
Name: name,
|
||||
Owner: owner,
|
||||
}
|
||||
if v, ok := req.GetArguments()["description"].(string); ok {
|
||||
opt.Description = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["private"].(bool); ok {
|
||||
opt.Private = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["git_content"].(bool); ok {
|
||||
opt.GitContent = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["topics"].(bool); ok {
|
||||
opt.Topics = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["labels"].(bool); ok {
|
||||
opt.Labels = v
|
||||
}
|
||||
if v, ok := req.GetArguments()["webhooks"].(bool); ok {
|
||||
opt.Webhooks = v
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
repository, _, err := client.CreateRepoFromTemplate(templateOwner, templateRepo, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("create from template err: %v", err))
|
||||
}
|
||||
return to.TextResult(repository)
|
||||
}
|
||||
Reference in New Issue
Block a user