package user import ( "context" "errors" "fmt" "git.lethalbits.com/lethalbits/gitea-mcp-extended/pkg/gitea" "git.lethalbits.com/lethalbits/gitea-mcp-extended/pkg/log" "git.lethalbits.com/lethalbits/gitea-mcp-extended/pkg/params" "git.lethalbits.com/lethalbits/gitea-mcp-extended/pkg/to" gitea_sdk "code.gitea.io/sdk/gitea" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) const ( ListMyPublicKeysToolName = "list_my_public_keys" ListUserPublicKeysToolName = "list_user_public_keys" GetPublicKeyToolName = "get_public_key" CreatePublicKeyToolName = "create_public_key" DeletePublicKeyToolName = "delete_public_key" ) var ( ListMyPublicKeysTool = mcp.NewTool( ListMyPublicKeysToolName, mcp.WithDescription("List the authenticated user's SSH public keys"), mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)), mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)), ) ListUserPublicKeysTool = mcp.NewTool( ListUserPublicKeysToolName, mcp.WithDescription("List a user's SSH public keys"), mcp.WithString("username", mcp.Required(), mcp.Description("username")), mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)), mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)), ) GetPublicKeyTool = mcp.NewTool( GetPublicKeyToolName, mcp.WithDescription("Get an SSH public key by ID"), mcp.WithNumber("id", mcp.Required(), mcp.Description("key ID")), ) CreatePublicKeyTool = mcp.NewTool( CreatePublicKeyToolName, mcp.WithDescription("Create an SSH public key for the authenticated user"), mcp.WithString("title", mcp.Required(), mcp.Description("key title")), mcp.WithString("key", mcp.Required(), mcp.Description("SSH public key content")), mcp.WithBoolean("read_only", mcp.Description("whether the key is read-only")), ) DeletePublicKeyTool = mcp.NewTool( DeletePublicKeyToolName, mcp.WithDescription("Delete an SSH public key"), mcp.WithNumber("id", mcp.Required(), mcp.Description("key ID")), ) ) func init() { Tool.RegisterRead(server.ServerTool{Tool: ListMyPublicKeysTool, Handler: ListMyPublicKeysFn}) Tool.RegisterRead(server.ServerTool{Tool: ListUserPublicKeysTool, Handler: ListUserPublicKeysFn}) Tool.RegisterRead(server.ServerTool{Tool: GetPublicKeyTool, Handler: GetPublicKeyFn}) Tool.RegisterWrite(server.ServerTool{Tool: CreatePublicKeyTool, Handler: CreatePublicKeyFn}) Tool.RegisterWrite(server.ServerTool{Tool: DeletePublicKeyTool, Handler: DeletePublicKeyFn}) } func ListMyPublicKeysFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called ListMyPublicKeysFn") 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.ListMyPublicKeys(gitea_sdk.ListPublicKeysOptions{ ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)}, }) if err != nil { return to.ErrorResult(fmt.Errorf("list my public keys err: %v", err)) } return to.TextResult(keys) } func ListUserPublicKeysFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called ListUserPublicKeysFn") username, ok := req.GetArguments()["username"].(string) if !ok { return to.ErrorResult(errors.New("username is 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.ListPublicKeys(username, gitea_sdk.ListPublicKeysOptions{ ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)}, }) if err != nil { return to.ErrorResult(fmt.Errorf("list %s public keys err: %v", username, err)) } return to.TextResult(keys) } func GetPublicKeyFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called GetPublicKeyFn") 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)) } key, _, err := client.GetPublicKey(id) if err != nil { return to.ErrorResult(fmt.Errorf("get public key %d err: %v", id, err)) } return to.TextResult(key) } func CreatePublicKeyFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called CreatePublicKeyFn") title, ok := req.GetArguments()["title"].(string) if !ok { return to.ErrorResult(errors.New("title is required")) } key, ok := req.GetArguments()["key"].(string) if !ok { return to.ErrorResult(errors.New("key is required")) } opt := gitea_sdk.CreateKeyOption{ Title: title, Key: key, } 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)) } pubKey, _, err := client.CreatePublicKey(opt) if err != nil { return to.ErrorResult(fmt.Errorf("create public key err: %v", err)) } return to.TextResult(pubKey) } func DeletePublicKeyFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called DeletePublicKeyFn") 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.DeletePublicKey(id) if err != nil { return to.ErrorResult(fmt.Errorf("delete public key %d err: %v", id, err)) } return to.TextResult(map[string]any{"status": "deleted", "key_id": id}) }