Initial commit: Gitea MCP Server
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.com/gitea/gitea-mcp/pkg/errors"
|
||||
"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_sdk "code.gitea.io/sdk/gitea"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
const (
|
||||
CreateCommitStatusToolName = "create_commit_status"
|
||||
)
|
||||
|
||||
var (
|
||||
CreateCommitStatusTool = mcp.NewTool(
|
||||
CreateCommitStatusToolName,
|
||||
mcp.WithDescription("Create a commit status check for a repository. Adds a new status context to a commit without overwriting existing statuses."),
|
||||
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 (full 40-character SHA or short SHA)")),
|
||||
mcp.WithString("state", mcp.Required(), mcp.Description("status state: pending, success, error, or failure")),
|
||||
mcp.WithString("target_url", mcp.Description("URL with more details about the status (e.g., review environment link like https://review.lumbridgecorp.com)")),
|
||||
mcp.WithString("context", mcp.Description("status context identifier (e.g., 'ci/metal', 'ci/cloud-1', 'continuous-integration/jenkins')"), mcp.DefaultString("default")),
|
||||
mcp.WithString("description", mcp.Description("short description of the status")),
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
Tool.RegisterWrite(server.ServerTool{
|
||||
Tool: CreateCommitStatusTool,
|
||||
Handler: CreateCommitStatusFn,
|
||||
})
|
||||
}
|
||||
|
||||
// CreateCommitStatusFn creates a status check for a commit
|
||||
func CreateCommitStatusFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called CreateCommitStatusFn")
|
||||
args := req.GetArguments()
|
||||
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
repo, err := params.GetString(args, "repo")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
sha, err := params.GetString(args, "sha")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
state, err := params.GetString(args, "state")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
statusState, err := parseStatusState(state)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("invalid state '%s': must be one of pending, success, failure, error", state))
|
||||
}
|
||||
|
||||
targetURL, _ := args["target_url"].(string)
|
||||
context, _ := args["context"].(string)
|
||||
description, _ := args["description"].(string)
|
||||
|
||||
// Use default context if not provided
|
||||
if context == "" {
|
||||
context = "default"
|
||||
}
|
||||
|
||||
opt := gitea_sdk.CreateStatusOption{
|
||||
State: statusState,
|
||||
TargetURL: targetURL,
|
||||
Context: context,
|
||||
Description: description,
|
||||
}
|
||||
|
||||
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 {
|
||||
translatedErr := errors.TranslateError(err, map[string]string{
|
||||
"operation": "CreateCommitStatus",
|
||||
"owner": owner,
|
||||
"repo": repo,
|
||||
"sha": sha,
|
||||
"state": state,
|
||||
"context": context,
|
||||
})
|
||||
return to.ErrorResult(translatedErr)
|
||||
}
|
||||
|
||||
return to.TextResult(slimStatus(status))
|
||||
}
|
||||
|
||||
// parseStatusState converts a string state to gitea_sdk.StatusState
|
||||
func parseStatusState(state string) (gitea_sdk.StatusState, error) {
|
||||
switch state {
|
||||
case "pending":
|
||||
return gitea_sdk.StatusPending, nil
|
||||
case "success":
|
||||
return gitea_sdk.StatusSuccess, nil
|
||||
case "failure":
|
||||
return gitea_sdk.StatusFailure, nil
|
||||
case "error":
|
||||
return gitea_sdk.StatusError, nil
|
||||
default:
|
||||
return "", fmt.Errorf("invalid state: %s", state)
|
||||
}
|
||||
}
|
||||
|
||||
// slimStatus creates a slimmed down representation of a commit status
|
||||
func slimStatus(s *gitea_sdk.Status) map[string]any {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return map[string]any{
|
||||
"id": s.ID,
|
||||
"state": s.State,
|
||||
"target_url": s.TargetURL,
|
||||
"context": s.Context,
|
||||
"description": s.Description,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user