Files

228 lines
6.8 KiB
Go

package stars
import (
"context"
"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"
gitea_sdk "code.gitea.io/sdk/gitea"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
var Tool = tool.New()
const (
StarsReadToolName = "stars_read"
StarsWriteToolName = "stars_write"
)
var (
StarsReadTool = mcp.NewTool(
StarsReadToolName,
mcp.WithDescription("Read stars information. Use method 'list_stargazers' to list repo stargazers, 'list_starred' to list user's starred repos, 'my_starred' for your starred repos, 'check' to check if user starred a repo."),
mcp.WithString("method", mcp.Required(), mcp.Description("operation to perform"), mcp.Enum("list_stargazers", "list_starred", "my_starred", "check")),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("user", mcp.Description("username (for 'list_starred', 'check')")),
)
StarsWriteTool = mcp.NewTool(
StarsWriteToolName,
mcp.WithDescription("Star or unstar a repository."),
mcp.WithString("method", mcp.Required(), mcp.Description("operation to perform"), mcp.Enum("star", "unstar")),
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: StarsReadTool,
Handler: starsReadFn,
})
Tool.RegisterWrite(server.ServerTool{
Tool: StarsWriteTool,
Handler: starsWriteFn,
})
}
func starsReadFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
method, err := params.GetString(req.GetArguments(), "method")
if err != nil {
return to.ErrorResult(err)
}
switch method {
case "list_stargazers":
return listStargazersFn(ctx, req)
case "list_starred":
return listStarredFn(ctx, req)
case "my_starred":
return myStarredFn(ctx, req)
case "check":
return checkStarFn(ctx, req)
default:
return to.ErrorResult(fmt.Errorf("unknown method: %s", method))
}
}
func starsWriteFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
method, err := params.GetString(req.GetArguments(), "method")
if err != nil {
return to.ErrorResult(err)
}
switch method {
case "star":
return starRepoFn(ctx, req)
case "unstar":
return unstarRepoFn(ctx, req)
default:
return to.ErrorResult(fmt.Errorf("unknown method: %s", method))
}
}
func listStargazersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called listStargazersFn")
owner, err := params.GetString(req.GetArguments(), "owner")
if err != nil {
return to.ErrorResult(err)
}
repo, err := params.GetString(req.GetArguments(), "repo")
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))
}
users, _, err := client.ListRepoStargazers(owner, repo, gitea_sdk.ListStargazersOptions{})
if err != nil {
return to.ErrorResult(fmt.Errorf("list stargazers err: %v", err))
}
return to.TextResult(slimUsers(users))
}
func listStarredFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called listStarredFn")
user, err := params.GetString(req.GetArguments(), "user")
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))
}
repos, _, err := client.GetStarredRepos(user)
if err != nil {
return to.ErrorResult(fmt.Errorf("list starred repos err: %v", err))
}
return to.TextResult(slimRepos(repos))
}
func myStarredFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called myStarredFn")
client, err := gitea.ClientFromContext(ctx)
if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
}
repos, _, err := client.GetMyStarredRepos()
if err != nil {
return to.ErrorResult(fmt.Errorf("list my starred repos err: %v", err))
}
return to.TextResult(slimRepos(repos))
}
func checkStarFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called checkStarFn")
owner, err := params.GetString(req.GetArguments(), "owner")
if err != nil {
return to.ErrorResult(err)
}
repo, err := params.GetString(req.GetArguments(), "repo")
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))
}
starred, _, err := client.IsRepoStarring(owner, repo)
if err != nil {
return to.ErrorResult(fmt.Errorf("check star err: %v", err))
}
return to.TextResult(map[string]any{"starred": starred})
}
func starRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called starRepoFn")
owner, err := params.GetString(req.GetArguments(), "owner")
if err != nil {
return to.ErrorResult(err)
}
repo, err := params.GetString(req.GetArguments(), "repo")
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.StarRepo(owner, repo)
if err != nil {
return to.ErrorResult(fmt.Errorf("star repo err: %v", err))
}
return to.TextResult("Repository starred successfully")
}
func unstarRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called unstarRepoFn")
owner, err := params.GetString(req.GetArguments(), "owner")
if err != nil {
return to.ErrorResult(err)
}
repo, err := params.GetString(req.GetArguments(), "repo")
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.UnStarRepo(owner, repo)
if err != nil {
return to.ErrorResult(fmt.Errorf("unstar repo err: %v", err))
}
return to.TextResult("Repository unstarred successfully")
}
func slimUsers(users []*gitea_sdk.User) []map[string]any {
out := make([]map[string]any, 0, len(users))
for _, u := range users {
out = append(out, map[string]any{
"id": u.ID,
"login": u.UserName,
"full_name": u.FullName,
"avatar_url": u.AvatarURL,
})
}
return out
}
func slimRepos(repos []*gitea_sdk.Repository) []map[string]any {
out := make([]map[string]any, 0, len(repos))
for _, r := range repos {
out = append(out, map[string]any{
"id": r.ID,
"name": r.Name,
"full_name": r.FullName,
"private": r.Private,
})
}
return out
}