mirror of
				https://gitea.com/gitea/gitea-mcp.git
				synced 2025-11-04 04:11:50 +00:00 
			
		
		
		
	Compare commits
	
		
			5 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					de311344cd | ||
| 
						 | 
					d7addd56c4 | ||
| 
						 | 
					dc3e120e97 | ||
| 
						 | 
					f33b04a3df | ||
| 
						 | 
					ba07925969 | 
@@ -66,6 +66,8 @@ jobs:
 | 
			
		||||
            linux/amd64
 | 
			
		||||
            linux/arm64
 | 
			
		||||
          push: true
 | 
			
		||||
          build-args: |
 | 
			
		||||
            VERSION=${{ steps.meta.outputs.REPO_VERSION }}
 | 
			
		||||
          tags: |
 | 
			
		||||
            ${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}-server:${{ steps.meta.outputs.REPO_VERSION }}
 | 
			
		||||
            ${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}-server:${{ env.DOCKER_LATEST }}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							@@ -139,7 +139,10 @@ To configure the MCP server for Gitea, add the following to your MCP configurati
 | 
			
		||||
{
 | 
			
		||||
  "mcpServers": {
 | 
			
		||||
    "gitea": {
 | 
			
		||||
      "url": "http://localhost:8080/sse"
 | 
			
		||||
      "url": "http://localhost:8080/sse",
 | 
			
		||||
      "headers": {
 | 
			
		||||
        "Authorization": "Bearer <your personal access token>"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -151,7 +154,10 @@ To configure the MCP server for Gitea, add the following to your MCP configurati
 | 
			
		||||
{
 | 
			
		||||
  "mcpServers": {
 | 
			
		||||
    "gitea": {
 | 
			
		||||
      "url": "http://localhost:8080/mcp"
 | 
			
		||||
      "url": "http://localhost:8080/mcp",
 | 
			
		||||
      "headers": {
 | 
			
		||||
        "Authorization": "Bearer <your personal access token>"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -139,7 +139,10 @@ cp gitea-mcp /usr/local/bin/
 | 
			
		||||
{
 | 
			
		||||
  "mcpServers": {
 | 
			
		||||
    "gitea": {
 | 
			
		||||
      "url": "http://localhost:8080/sse"
 | 
			
		||||
      "url": "http://localhost:8080/sse",
 | 
			
		||||
      "headers": {
 | 
			
		||||
        "Authorization": "Bearer <your personal access token>"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -151,7 +154,10 @@ cp gitea-mcp /usr/local/bin/
 | 
			
		||||
{
 | 
			
		||||
  "mcpServers": {
 | 
			
		||||
    "gitea": {
 | 
			
		||||
      "url": "http://localhost:8080/mcp"
 | 
			
		||||
      "url": "http://localhost:8080/mcp",
 | 
			
		||||
      "headers": {
 | 
			
		||||
        "Authorization": "Bearer <your personal access token>"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -139,7 +139,10 @@ cp gitea-mcp /usr/local/bin/
 | 
			
		||||
{
 | 
			
		||||
  "mcpServers": {
 | 
			
		||||
    "gitea": {
 | 
			
		||||
      "url": "http://localhost:8080/sse"
 | 
			
		||||
      "url": "http://localhost:8080/sse",
 | 
			
		||||
      "headers": {
 | 
			
		||||
        "Authorization": "Bearer <your personal access token>"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -151,7 +154,10 @@ cp gitea-mcp /usr/local/bin/
 | 
			
		||||
{
 | 
			
		||||
  "mcpServers": {
 | 
			
		||||
    "gitea": {
 | 
			
		||||
      "url": "http://localhost:8080/mcp"
 | 
			
		||||
      "url": "http://localhost:8080/mcp",
 | 
			
		||||
      "headers": {
 | 
			
		||||
        "Authorization": "Bearer <your personal access token>"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -140,7 +140,11 @@ func GetIssueByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("index is required"))
 | 
			
		||||
	}
 | 
			
		||||
	issue, _, err := gitea.Client().GetIssue(owner, repo, int64(index))
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	issue, _, err := client.GetIssue(owner, repo, int64(index))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -177,7 +181,11 @@ func ListRepoIssuesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
 | 
			
		||||
			PageSize: int(pageSize),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	issues, _, err := gitea.Client().ListRepoIssues(owner, repo, opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	issues, _, err := client.ListRepoIssues(owner, repo, opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get %v/%v/issues err: %v", owner, repo, err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -202,7 +210,11 @@ func CreateIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("body is required"))
 | 
			
		||||
	}
 | 
			
		||||
	issue, _, err := gitea.Client().CreateIssue(owner, repo, gitea_sdk.CreateIssueOption{
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	issue, _, err := client.CreateIssue(owner, repo, gitea_sdk.CreateIssueOption{
 | 
			
		||||
		Title: title,
 | 
			
		||||
		Body:  body,
 | 
			
		||||
	})
 | 
			
		||||
@@ -234,7 +246,11 @@ func CreateIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
 | 
			
		||||
	opt := gitea_sdk.CreateIssueCommentOption{
 | 
			
		||||
		Body: body,
 | 
			
		||||
	}
 | 
			
		||||
	issueComment, _, err := gitea.Client().CreateIssueComment(owner, repo, int64(index), opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	issueComment, _, err := client.CreateIssueComment(owner, repo, int64(index), opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("create %v/%v/issue/%v/comment err: %v", owner, repo, int64(index), err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -280,7 +296,11 @@ func EditIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
 | 
			
		||||
		opt.State = ptr.To(gitea_sdk.StateType(state))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	issue, _, err := gitea.Client().EditIssue(owner, repo, int64(index), opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	issue, _, err := client.EditIssue(owner, repo, int64(index), opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("edit %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -309,7 +329,11 @@ func EditIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
 | 
			
		||||
	opt := gitea_sdk.EditIssueCommentOption{
 | 
			
		||||
		Body: body,
 | 
			
		||||
	}
 | 
			
		||||
	issueComment, _, err := gitea.Client().EditIssueComment(owner, repo, int64(commentID), opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	issueComment, _, err := client.EditIssueComment(owner, repo, int64(commentID), opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("edit %v/%v/issues/comments/%v err: %v", owner, repo, int64(commentID), err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -332,7 +356,11 @@ func GetIssueCommentsByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*m
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("index is required"))
 | 
			
		||||
	}
 | 
			
		||||
	opt := gitea_sdk.ListIssueCommentOptions{}
 | 
			
		||||
	issue, _, err := gitea.Client().ListIssueComments(owner, repo, int64(index), opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	issue, _, err := client.ListIssueComments(owner, repo, int64(index), opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get %v/%v/issues/%v/comments err: %v", owner, repo, int64(index), err))
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -176,7 +176,11 @@ func ListRepoLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
 | 
			
		||||
			PageSize: int(pageSize),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	labels, _, err := gitea.Client().ListRepoLabels(owner, repo, opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	labels, _, err := client.ListRepoLabels(owner, repo, opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("list %v/%v/labels err: %v", owner, repo, err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -198,7 +202,11 @@ func GetRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("label ID is required"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	label, _, err := gitea.Client().GetRepoLabel(owner, repo, int64(id))
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	label, _, err := client.GetRepoLabel(owner, repo, int64(id))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get %v/%v/label/%v err: %v", owner, repo, int64(id), err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -231,7 +239,11 @@ func CreateRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
 | 
			
		||||
		Description: description,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	label, _, err := gitea.Client().CreateLabel(owner, repo, opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	label, _, err := client.CreateLabel(owner, repo, opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("create %v/%v/label err: %v", owner, repo, err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -264,7 +276,11 @@ func EditRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
 | 
			
		||||
		opt.Description = ptr.To(description)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	label, _, err := gitea.Client().EditLabel(owner, repo, int64(id), opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	label, _, err := client.EditLabel(owner, repo, int64(id), opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("edit %v/%v/label/%v err: %v", owner, repo, int64(id), err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -286,7 +302,11 @@ func DeleteRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("label ID is required"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := gitea.Client().DeleteLabel(owner, repo, int64(id))
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	_, err = client.DeleteLabel(owner, repo, int64(id))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("delete %v/%v/label/%v err: %v", owner, repo, int64(id), err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -324,7 +344,11 @@ func AddIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
 | 
			
		||||
		Labels: labels,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	issueLabels, _, err := gitea.Client().AddIssueLabels(owner, repo, int64(index), opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	issueLabels, _, err := client.AddIssueLabels(owner, repo, int64(index), opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("add labels to %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -362,7 +386,11 @@ func ReplaceIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
 | 
			
		||||
		Labels: labels,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	issueLabels, _, err := gitea.Client().ReplaceIssueLabels(owner, repo, int64(index), opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	issueLabels, _, err := client.ReplaceIssueLabels(owner, repo, int64(index), opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("replace labels on %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -384,7 +412,11 @@ func ClearIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("issue index is required"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := gitea.Client().ClearIssueLabels(owner, repo, int64(index))
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	_, err = client.ClearIssueLabels(owner, repo, int64(index))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("clear labels on %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -410,7 +442,11 @@ func RemoveIssueLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("label ID is required"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := gitea.Client().DeleteIssueLabel(owner, repo, int64(index), int64(labelID))
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	_, err = client.DeleteIssueLabel(owner, repo, int64(index), int64(labelID))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("remove label %v from %v/%v/issue/%v err: %v", int64(labelID), owner, repo, int64(index), err))
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,10 @@
 | 
			
		||||
package operation
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"gitea.com/gitea/gitea-mcp/operation/issue"
 | 
			
		||||
@@ -11,6 +14,7 @@ import (
 | 
			
		||||
	"gitea.com/gitea/gitea-mcp/operation/search"
 | 
			
		||||
	"gitea.com/gitea/gitea-mcp/operation/user"
 | 
			
		||||
	"gitea.com/gitea/gitea-mcp/operation/version"
 | 
			
		||||
	mcpContext "gitea.com/gitea/gitea-mcp/pkg/context"
 | 
			
		||||
	"gitea.com/gitea/gitea-mcp/pkg/flag"
 | 
			
		||||
	"gitea.com/gitea/gitea-mcp/pkg/log"
 | 
			
		||||
 | 
			
		||||
@@ -44,6 +48,20 @@ func RegisterTool(s *server.MCPServer) {
 | 
			
		||||
	s.DeleteTools("")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getContextWithToken(ctx context.Context, r *http.Request) context.Context {
 | 
			
		||||
	authHeader := r.Header.Get("Authorization")
 | 
			
		||||
	if authHeader == "" {
 | 
			
		||||
		return ctx
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parts := strings.Split(authHeader, " ")
 | 
			
		||||
	if len(parts) != 2 || parts[0] != "Bearer" {
 | 
			
		||||
		return ctx
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return context.WithValue(ctx, mcpContext.TokenContextKey, parts[1])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Run() error {
 | 
			
		||||
	mcpServer = newMCPServer(flag.Version)
 | 
			
		||||
	RegisterTool(mcpServer)
 | 
			
		||||
@@ -57,6 +75,7 @@ func Run() error {
 | 
			
		||||
	case "sse":
 | 
			
		||||
		sseServer := server.NewSSEServer(
 | 
			
		||||
			mcpServer,
 | 
			
		||||
			server.WithSSEContextFunc(getContextWithToken),
 | 
			
		||||
		)
 | 
			
		||||
		log.Infof("Gitea MCP SSE server listening on :%d", flag.Port)
 | 
			
		||||
		if err := sseServer.Start(fmt.Sprintf(":%d", flag.Port)); err != nil {
 | 
			
		||||
@@ -67,7 +86,7 @@ func Run() error {
 | 
			
		||||
			mcpServer,
 | 
			
		||||
			server.WithLogger(log.New()),
 | 
			
		||||
			server.WithHeartbeatInterval(30*time.Second),
 | 
			
		||||
			server.WithStateLess(true),
 | 
			
		||||
			server.WithHTTPContextFunc(getContextWithToken),
 | 
			
		||||
		)
 | 
			
		||||
		log.Infof("Gitea MCP HTTP server listening on :%d", flag.Port)
 | 
			
		||||
		if err := httpServer.Start(fmt.Sprintf(":%d", flag.Port)); err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -84,7 +84,11 @@ func GetPullRequestByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("index is required"))
 | 
			
		||||
	}
 | 
			
		||||
	pr, _, err := gitea.Client().GetPullRequest(owner, repo, int64(index))
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	pr, _, err := client.GetPullRequest(owner, repo, int64(index))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get %v/%v/pr/%v err: %v", owner, repo, int64(index), err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -125,7 +129,11 @@ func ListRepoPullRequestsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.
 | 
			
		||||
			PageSize: int(pageSize),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	pullRequests, _, err := gitea.Client().ListRepoPullRequests(owner, repo, opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	pullRequests, _, err := client.ListRepoPullRequests(owner, repo, opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("list %v/%v/pull_requests err: %v", owner, repo, err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -159,7 +167,11 @@ func CreatePullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("base is required"))
 | 
			
		||||
	}
 | 
			
		||||
	pr, _, err := gitea.Client().CreatePullRequest(owner, repo, gitea_sdk.CreatePullRequestOption{
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	pr, _, err := client.CreatePullRequest(owner, repo, gitea_sdk.CreatePullRequestOption{
 | 
			
		||||
		Title: title,
 | 
			
		||||
		Body:  body,
 | 
			
		||||
		Head:  head,
 | 
			
		||||
 
 | 
			
		||||
@@ -76,7 +76,11 @@ func CreateBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 | 
			
		||||
	}
 | 
			
		||||
	oldBranch, _ := req.GetArguments()["old_branch"].(string)
 | 
			
		||||
 | 
			
		||||
	_, _, err := gitea.Client().CreateBranch(owner, repo, gitea_sdk.CreateBranchOption{
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err = client.CreateBranch(owner, repo, gitea_sdk.CreateBranchOption{
 | 
			
		||||
		BranchName:    branch,
 | 
			
		||||
		OldBranchName: oldBranch,
 | 
			
		||||
	})
 | 
			
		||||
@@ -101,7 +105,11 @@ func DeleteBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("branch is required"))
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err := gitea.Client().DeleteRepoBranch(owner, repo, branch)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err = client.DeleteRepoBranch(owner, repo, branch)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("delete branch error: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -125,7 +133,11 @@ func ListBranchesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 | 
			
		||||
			PageSize: 100,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	branches, _, err := gitea.Client().ListRepoBranches(owner, repo, opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	branches, _, err := client.ListRepoBranches(owner, repo, opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("list branches error: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,11 @@ func ListRepoCommitsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
 | 
			
		||||
		SHA:  sha,
 | 
			
		||||
		Path: path,
 | 
			
		||||
	}
 | 
			
		||||
	commits, _, err := gitea.Client().ListRepoCommits(owner, repo, opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	commits, _, err := client.ListRepoCommits(owner, repo, opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("list repo commits err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ var (
 | 
			
		||||
		mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
 | 
			
		||||
		mcp.WithString("filePath", mcp.Required(), mcp.Description("file path")),
 | 
			
		||||
		mcp.WithString("sha", mcp.Required(), mcp.Description("sha is the SHA for the file that already exists")),
 | 
			
		||||
		mcp.WithString("content", mcp.Required(), mcp.Description("file content, base64 encoded")),
 | 
			
		||||
		mcp.WithString("content", mcp.Required(), mcp.Description("file content")),
 | 
			
		||||
		mcp.WithString("message", mcp.Required(), mcp.Description("commit message")),
 | 
			
		||||
		mcp.WithString("branch_name", mcp.Required(), mcp.Description("branch name")),
 | 
			
		||||
	)
 | 
			
		||||
@@ -124,7 +124,11 @@ func GetFileContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("filePath is required"))
 | 
			
		||||
	}
 | 
			
		||||
	content, _, err := gitea.Client().GetContents(owner, repo, ref, filePath)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	content, _, err := client.GetContents(owner, repo, ref, filePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get file err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -184,7 +188,11 @@ func GetDirContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("filePath is required"))
 | 
			
		||||
	}
 | 
			
		||||
	content, _, err := gitea.Client().ListContents(owner, repo, ref, filePath)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	content, _, err := client.ListContents(owner, repo, ref, filePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get dir content err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -216,7 +224,11 @@ func CreateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, _, err := gitea.Client().CreateFile(owner, repo, filePath, opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err = client.CreateFile(owner, repo, filePath, opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("create file err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -253,7 +265,11 @@ func UpdateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
 | 
			
		||||
			BranchName: branchName,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err := gitea.Client().UpdateFile(owner, repo, filePath, opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err = client.UpdateFile(owner, repo, filePath, opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("update file err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -287,7 +303,11 @@ func DeleteFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
 | 
			
		||||
		},
 | 
			
		||||
		SHA: sha,
 | 
			
		||||
	}
 | 
			
		||||
	_, err := gitea.Client().DeleteFile(owner, repo, filePath, opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	_, err = client.DeleteFile(owner, repo, filePath, opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("delete file err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -134,7 +134,11 @@ func CreateReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
 | 
			
		||||
	isPreRelease, _ := req.GetArguments()["is_pre_release"].(bool)
 | 
			
		||||
	body, _ := req.GetArguments()["body"].(string)
 | 
			
		||||
 | 
			
		||||
	_, _, err := gitea.Client().CreateRelease(owner, repo, gitea_sdk.CreateReleaseOption{
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err = client.CreateRelease(owner, repo, gitea_sdk.CreateReleaseOption{
 | 
			
		||||
		TagName:      tagName,
 | 
			
		||||
		Target:       target,
 | 
			
		||||
		Title:        title,
 | 
			
		||||
@@ -164,7 +168,11 @@ func DeleteReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
 | 
			
		||||
		return nil, fmt.Errorf("id is required")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := gitea.Client().DeleteRelease(owner, repo, int64(id))
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	_, err = client.DeleteRelease(owner, repo, int64(id))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("delete release error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -187,7 +195,11 @@ func GetReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
 | 
			
		||||
		return nil, fmt.Errorf("id is required")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	release, _, err := gitea.Client().GetRelease(owner, repo, int64(id))
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	release, _, err := client.GetRelease(owner, repo, int64(id))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("get release error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -206,7 +218,11 @@ func GetLatestReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
 | 
			
		||||
		return nil, fmt.Errorf("repo is required")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	release, _, err := gitea.Client().GetLatestRelease(owner, repo)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	release, _, err := client.GetLatestRelease(owner, repo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("get latest release error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -237,7 +253,11 @@ func ListReleasesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 | 
			
		||||
	page, _ := req.GetArguments()["page"].(float64)
 | 
			
		||||
	pageSize, _ := req.GetArguments()["pageSize"].(float64)
 | 
			
		||||
 | 
			
		||||
	releases, _, err := gitea.Client().ListReleases(owner, repo, gitea_sdk.ListReleasesOptions{
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	releases, _, err := client.ListReleases(owner, repo, gitea_sdk.ListReleasesOptions{
 | 
			
		||||
		ListOptions: gitea_sdk.ListOptions{
 | 
			
		||||
			Page:     int(page),
 | 
			
		||||
			PageSize: int(pageSize),
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ const (
 | 
			
		||||
var (
 | 
			
		||||
	CreateRepoTool = mcp.NewTool(
 | 
			
		||||
		CreateRepoToolName,
 | 
			
		||||
		mcp.WithDescription("Create repository"),
 | 
			
		||||
		mcp.WithDescription("Create repository in personal account or organization"),
 | 
			
		||||
		mcp.WithString("name", mcp.Required(), mcp.Description("Name of the repository to create")),
 | 
			
		||||
		mcp.WithString("description", mcp.Description("Description of the repository to create")),
 | 
			
		||||
		mcp.WithBoolean("private", mcp.Description("Whether the repository is private")),
 | 
			
		||||
@@ -38,6 +38,7 @@ var (
 | 
			
		||||
		mcp.WithString("license", mcp.Description("License to use")),
 | 
			
		||||
		mcp.WithString("readme", mcp.Description("Readme of the repository to create")),
 | 
			
		||||
		mcp.WithString("default_branch", mcp.Description("DefaultBranch of the repository (used when initializes and in template)")),
 | 
			
		||||
		mcp.WithString("organization", mcp.Description("Organization name to create repository in (optional - defaults to personal account)")),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	ForkRepoTool = mcp.NewTool(
 | 
			
		||||
@@ -120,6 +121,7 @@ func CreateRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
 | 
			
		||||
	license, _ := req.GetArguments()["license"].(string)
 | 
			
		||||
	readme, _ := req.GetArguments()["readme"].(string)
 | 
			
		||||
	defaultBranch, _ := req.GetArguments()["default_branch"].(string)
 | 
			
		||||
	organization, _ := req.GetArguments()["organization"].(string)
 | 
			
		||||
 | 
			
		||||
	opt := gitea_sdk.CreateRepoOption{
 | 
			
		||||
		Name:          name,
 | 
			
		||||
@@ -133,9 +135,22 @@ func CreateRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
 | 
			
		||||
		Readme:        readme,
 | 
			
		||||
		DefaultBranch: defaultBranch,
 | 
			
		||||
	}
 | 
			
		||||
	repo, _, err := gitea.Client().CreateRepo(opt)
 | 
			
		||||
 | 
			
		||||
	var repo *gitea_sdk.Repository
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("create repo err: %v", err))
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	if organization != "" {
 | 
			
		||||
		repo, _, err = client.CreateOrgRepo(organization, opt)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return to.ErrorResult(fmt.Errorf("create organization repository '%s' in '%s' err: %v", name, organization, err))
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		repo, _, err = client.CreateRepo(opt)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return to.ErrorResult(fmt.Errorf("create repository '%s' err: %v", name, err))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return to.TextResult(repo)
 | 
			
		||||
}
 | 
			
		||||
@@ -164,7 +179,11 @@ func ForkRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResu
 | 
			
		||||
		Organization: organizationPtr,
 | 
			
		||||
		Name:         namePtr,
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err := gitea.Client().CreateFork(user, repo, opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err = client.CreateFork(user, repo, opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("fork repository error: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -187,7 +206,11 @@ func ListMyReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
 | 
			
		||||
			PageSize: int(pageSize),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	repos, _, err := gitea.Client().ListMyRepos(opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	repos, _, err := client.ListMyRepos(opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("list my repositories error: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -102,7 +102,11 @@ func CreateTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
 | 
			
		||||
	target, _ := req.GetArguments()["target"].(string)
 | 
			
		||||
	message, _ := req.GetArguments()["message"].(string)
 | 
			
		||||
 | 
			
		||||
	_, _, err := gitea.Client().CreateTag(owner, repo, gitea_sdk.CreateTagOption{
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err = client.CreateTag(owner, repo, gitea_sdk.CreateTagOption{
 | 
			
		||||
		TagName: tagName,
 | 
			
		||||
		Target:  target,
 | 
			
		||||
		Message: message,
 | 
			
		||||
@@ -129,7 +133,11 @@ func DeleteTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
 | 
			
		||||
		return nil, fmt.Errorf("tag_name is required")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := gitea.Client().DeleteTag(owner, repo, tagName)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	_, err = client.DeleteTag(owner, repo, tagName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("delete tag error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -152,7 +160,11 @@ func GetTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult
 | 
			
		||||
		return nil, fmt.Errorf("tag_name is required")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tag, _, err := gitea.Client().GetTag(owner, repo, tagName)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	tag, _, err := client.GetTag(owner, repo, tagName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("get tag error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -173,7 +185,11 @@ func ListTagsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResu
 | 
			
		||||
	page, _ := req.GetArguments()["page"].(float64)
 | 
			
		||||
	pageSize, _ := req.GetArguments()["pageSize"].(float64)
 | 
			
		||||
 | 
			
		||||
	tags, _, err := gitea.Client().ListRepoTags(owner, repo, gitea_sdk.ListRepoTagsOptions{
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	tags, _, err := client.ListRepoTags(owner, repo, gitea_sdk.ListRepoTagsOptions{
 | 
			
		||||
		ListOptions: gitea_sdk.ListOptions{
 | 
			
		||||
			Page:     int(page),
 | 
			
		||||
			PageSize: int(pageSize),
 | 
			
		||||
 
 | 
			
		||||
@@ -94,7 +94,11 @@ func SearchUsersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
 | 
			
		||||
			PageSize: int(pageSize),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	users, _, err := gitea.Client().SearchUsers(opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	users, _, err := client.SearchUsers(opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("search users err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -128,7 +132,11 @@ func SearchOrgTeamsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
 | 
			
		||||
			PageSize: int(pageSize),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	teams, _, err := gitea.Client().SearchOrgTeams(org, &opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	teams, _, err := client.SearchOrgTeams(org, &opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("search organization teams error: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -178,7 +186,11 @@ func SearchReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
 | 
			
		||||
			PageSize: int(pageSize),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	repos, _, err := gitea.Client().SearchRepos(opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	repos, _, err := client.SearchRepos(opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("search repos error: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -79,7 +79,11 @@ func getIntArg(req mcp.CallToolRequest, name string, def int) int {
 | 
			
		||||
// Logs invocation, fetches current user info from gitea, wraps result for MCP.
 | 
			
		||||
func GetUserInfoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 | 
			
		||||
	log.Debugf("[User] Called GetUserInfoFn")
 | 
			
		||||
	user, _, err := gitea.Client().GetMyUserInfo()
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	user, _, err := client.GetMyUserInfo()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get user info err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
@@ -100,7 +104,11 @@ func GetUserOrgsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
 | 
			
		||||
			PageSize: pageSize,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	orgs, _, err := gitea.Client().ListMyOrgs(opt)
 | 
			
		||||
	client, err := gitea.ClientFromContext(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	orgs, _, err := client.ListMyOrgs(opt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return to.ErrorResult(fmt.Errorf("get user orgs err: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								pkg/context/context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								pkg/context/context.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
package context
 | 
			
		||||
 | 
			
		||||
type contextKey string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	TokenContextKey = contextKey("token")
 | 
			
		||||
)
 | 
			
		||||
@@ -1,52 +1,47 @@
 | 
			
		||||
package gitea
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"gitea.com/gitea/gitea-mcp/pkg/flag"
 | 
			
		||||
	"gitea.com/gitea/gitea-mcp/pkg/log"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/sdk/gitea"
 | 
			
		||||
	mcpContext "gitea.com/gitea/gitea-mcp/pkg/context"
 | 
			
		||||
	"gitea.com/gitea/gitea-mcp/pkg/flag"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	client     *gitea.Client
 | 
			
		||||
	clientOnce sync.Once
 | 
			
		||||
)
 | 
			
		||||
func NewClient(token string) (*gitea.Client, error) {
 | 
			
		||||
	httpClient := &http.Client{
 | 
			
		||||
		Transport: http.DefaultTransport,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
func Client() *gitea.Client {
 | 
			
		||||
	clientOnce.Do(func() {
 | 
			
		||||
		var err error
 | 
			
		||||
		if client != nil {
 | 
			
		||||
			return
 | 
			
		||||
	opts := []gitea.ClientOption{
 | 
			
		||||
		gitea.SetToken(token),
 | 
			
		||||
	}
 | 
			
		||||
	if flag.Insecure {
 | 
			
		||||
		httpClient.Transport.(*http.Transport).TLSClientConfig = &tls.Config{
 | 
			
		||||
			InsecureSkipVerify: true,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	opts = append(opts, gitea.SetHTTPClient(httpClient))
 | 
			
		||||
	if flag.Debug {
 | 
			
		||||
		opts = append(opts, gitea.SetDebugMode())
 | 
			
		||||
	}
 | 
			
		||||
	client, err := gitea.NewClient(flag.Host, opts...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("create gitea client err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		httpClient := &http.Client{
 | 
			
		||||
			Transport: http.DefaultTransport,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		opts := []gitea.ClientOption{
 | 
			
		||||
			gitea.SetToken(flag.Token),
 | 
			
		||||
		}
 | 
			
		||||
		if flag.Insecure {
 | 
			
		||||
			httpClient.Transport.(*http.Transport).TLSClientConfig = &tls.Config{
 | 
			
		||||
				InsecureSkipVerify: true,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		opts = append(opts, gitea.SetHTTPClient(httpClient))
 | 
			
		||||
		if flag.Debug {
 | 
			
		||||
			opts = append(opts, gitea.SetDebugMode())
 | 
			
		||||
		}
 | 
			
		||||
		client, err = gitea.NewClient(flag.Host, opts...)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatalf("create gitea client err: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Set user agent for the client
 | 
			
		||||
		client.SetUserAgent(fmt.Sprintf("gitea-mcp-server/%s", flag.Version))
 | 
			
		||||
	})
 | 
			
		||||
	return client
 | 
			
		||||
	// Set user agent for the client
 | 
			
		||||
	client.SetUserAgent(fmt.Sprintf("gitea-mcp-server/%s", flag.Version))
 | 
			
		||||
	return client, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ClientFromContext(ctx context.Context) (*gitea.Client, error) {
 | 
			
		||||
	token, ok := ctx.Value(mcpContext.TokenContextKey).(string)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		token = flag.Token
 | 
			
		||||
	}
 | 
			
		||||
	return NewClient(token)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user