diff --git a/Dockerfile b/Dockerfile index 417e513..e047015 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,8 @@ RUN CGO_ENABLED=0 go build -ldflags="-s -w -X main.Version=${VERSION}" -o gitea- # Final stage FROM debian:bullseye-slim +ENV GITEA_MODE stdio + WORKDIR /app # Install ca-certificates for HTTPS requests @@ -34,4 +36,4 @@ COPY --from=builder --chown=1000:1000 /app/gitea-mcp . # Use the non-root user USER gitea-mcp -CMD ["/app/gitea-mcp", "-t", "stdio"] \ No newline at end of file +CMD ["/app/gitea-mcp"] \ No newline at end of file diff --git a/cmd/cmd.go b/cmd/cmd.go index dccb93f..f46ad29 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -11,23 +11,20 @@ import ( ) var ( - transport string - host string - port int - token string - - debug *bool + host string + port int + token string ) func init() { flag.StringVar( - &transport, + &flagPkg.Mode, "t", "stdio", "Transport type (stdio or sse)", ) flag.StringVar( - &transport, + &flagPkg.Mode, "transport", "stdio", "Transport type (stdio or sse)", @@ -50,14 +47,17 @@ func init() { "", "Your personal access token", ) - flag.BoolFunc( + flag.BoolVar( + &flagPkg.ReadOnly, + "read-only", + false, + "Read-only mode", + ) + flag.BoolVar( + &flagPkg.Debug, "d", + false, "debug mode (If -d flag is provided, debug mode will be enabled by default)", - func(string) error { - debug = new(bool) - *debug = true - return nil - }, ) flag.BoolVar( &flagPkg.Insecure, @@ -80,13 +80,16 @@ func init() { flagPkg.Token = os.Getenv("GITEA_ACCESS_TOKEN") } - flagPkg.Mode = transport - - if debug != nil && *debug { - flagPkg.Debug = *debug + if os.Getenv("GITEA_MODE") != "" { + flagPkg.Mode = os.Getenv("GITEA_MODE") } - if debug != nil && !*debug { - flagPkg.Debug = os.Getenv("GITEA_DEBUG") == "true" + + if os.Getenv("GITEA_READONLY") == "true" { + flagPkg.ReadOnly = true + } + + if os.Getenv("GITEA_DEBUG") == "true" { + flagPkg.Debug = true } // Set insecure mode based on environment variable @@ -95,9 +98,9 @@ func init() { } } -func Execute(version string) { +func Execute() { defer log.Default().Sync() - if err := operation.Run(transport, version); err != nil { + if err := operation.Run(); err != nil { if err == context.Canceled { log.Info("Server shutdown due to context cancellation") return diff --git a/go.mod b/go.mod index c7e978b..aa170cf 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.0 require ( code.gitea.io/sdk/gitea v0.21.0 - github.com/mark3labs/mcp-go v0.18.0 + github.com/mark3labs/mcp-go v0.22.0 go.uber.org/zap v1.27.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) @@ -15,8 +15,9 @@ require ( github.com/go-fed/httpsig v1.1.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect + github.com/spf13/cast v1.7.1 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.36.0 // indirect + golang.org/x/crypto v0.37.0 // indirect golang.org/x/sys v0.32.0 // indirect ) diff --git a/go.sum b/go.sum index ad970f3..5016e84 100644 --- a/go.sum +++ b/go.sum @@ -6,16 +6,28 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/mark3labs/mcp-go v0.18.0 h1:YuhgIVjNlTG2ZOwmrkORWyPTp0dz1opPEqvsPtySXao= -github.com/mark3labs/mcp-go v0.18.0/go.mod h1:KmJndYv7GIgcPVwEKJjNcbhVQ+hJGJhrCCB/9xITzpE= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mark3labs/mcp-go v0.22.0 h1:cCEBWi4Yy9Kio+OW1hWIyi4WLsSr+RBBK6FI5tj+b7I= +github.com/mark3labs/mcp-go v0.22.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= @@ -29,8 +41,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -39,8 +51,8 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/main.go b/main.go index 16d7b9d..860ee00 100644 --- a/main.go +++ b/main.go @@ -2,12 +2,17 @@ package main import ( "gitea.com/gitea/gitea-mcp/cmd" + "gitea.com/gitea/gitea-mcp/pkg/flag" ) var ( Version = "dev" ) -func main() { - cmd.Execute(Version) +func init() { + flag.Version = Version +} + +func main() { + cmd.Execute() } diff --git a/operation/issue/issue.go b/operation/issue/issue.go index 1fc7f90..ea259d6 100644 --- a/operation/issue/issue.go +++ b/operation/issue/issue.go @@ -8,12 +8,15 @@ import ( "gitea.com/gitea/gitea-mcp/pkg/log" "gitea.com/gitea/gitea-mcp/pkg/ptr" "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 ( GetIssueByIndexToolName = "get_issue_by_index" ListRepoIssuesToolName = "list_repo_issues" @@ -71,12 +74,27 @@ var ( ) ) -func RegisterTool(s *server.MCPServer) { - s.AddTool(GetIssueByIndexTool, GetIssueByIndexFn) - s.AddTool(ListRepoIssuesTool, ListRepoIssuesFn) - s.AddTool(CreateIssueTool, CreateIssueFn) - s.AddTool(CreateIssueCommentTool, CreateIssueCommentFn) - s.AddTool(EditIssueTool, EditIssueFn) +func init() { + Tool.RegisterRead(server.ServerTool{ + Tool: GetIssueByIndexTool, + Handler: GetIssueByIndexFn, + }) + Tool.RegisterRead(server.ServerTool{ + Tool: ListRepoIssuesTool, + Handler: ListRepoIssuesFn, + }) + Tool.RegisterWrite(server.ServerTool{ + Tool: CreateIssueTool, + Handler: CreateIssueFn, + }) + Tool.RegisterWrite(server.ServerTool{ + Tool: CreateIssueCommentTool, + Handler: CreateIssueCommentFn, + }) + Tool.RegisterWrite(server.ServerTool{ + Tool: EditIssueTool, + Handler: EditIssueFn, + }) } func GetIssueByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { diff --git a/operation/operation.go b/operation/operation.go index 800d416..9510ea9 100644 --- a/operation/operation.go +++ b/operation/operation.go @@ -21,29 +21,30 @@ var ( func RegisterTool(s *server.MCPServer) { // User Tool - user.RegisterTool(s) + s.AddTools(user.Tool.Tools()...) // Repo Tool - repo.RegisterTool(s) + s.AddTools(repo.Tool.Tools()...) // Issue Tool - issue.RegisterTool(s) + s.AddTools(issue.Tool.Tools()...) // Pull Tool - pull.RegisterTool(s) + s.AddTools(pull.Tool.Tools()...) // Search Tool - search.RegisterTool(s) + s.AddTools(search.Tool.Tools()...) // Version Tool - version.RegisterTool(s) + s.AddTools(version.Tool.Tools()...) + + s.DeleteTools("") } -func Run(transport, version string) error { - flag.Version = version - mcpServer = newMCPServer(version) +func Run() error { + mcpServer = newMCPServer(flag.Version) RegisterTool(mcpServer) - switch transport { + switch flag.Mode { case "stdio": if err := server.ServeStdio(mcpServer); err != nil { return err @@ -55,7 +56,7 @@ func Run(transport, version string) error { return err } default: - return fmt.Errorf("invalid transport type: %s. Must be 'stdio' or 'sse'", transport) + return fmt.Errorf("invalid transport type: %s. Must be 'stdio' or 'sse'", flag.Mode) } return nil } @@ -64,6 +65,7 @@ func newMCPServer(version string) *server.MCPServer { return server.NewMCPServer( "Gitea MCP Server", version, + server.WithToolCapabilities(true), server.WithLogging(), ) } diff --git a/operation/pull/pull.go b/operation/pull/pull.go index 07bc9b3..4f32af3 100644 --- a/operation/pull/pull.go +++ b/operation/pull/pull.go @@ -7,12 +7,15 @@ import ( "gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/log" "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 ( GetPullRequestByIndexToolName = "get_pull_request_by_index" ListRepoPullRequestsToolName = "list_repo_pull_requests" @@ -52,10 +55,19 @@ var ( ) ) -func RegisterTool(s *server.MCPServer) { - s.AddTool(GetPullRequestByIndexTool, GetPullRequestByIndexFn) - s.AddTool(ListRepoPullRequestsTool, ListRepoPullRequestsFn) - s.AddTool(CreatePullRequestTool, CreatePullRequestFn) +func init() { + Tool.RegisterRead(server.ServerTool{ + Tool: GetPullRequestByIndexTool, + Handler: GetPullRequestByIndexFn, + }) + Tool.RegisterRead(server.ServerTool{ + Tool: ListRepoPullRequestsTool, + Handler: ListRepoPullRequestsFn, + }) + Tool.RegisterWrite(server.ServerTool{ + Tool: CreatePullRequestTool, + Handler: CreatePullRequestFn, + }) } func GetPullRequestByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { diff --git a/operation/repo/branch.go b/operation/repo/branch.go index b3ab8e0..c2b3b7f 100644 --- a/operation/repo/branch.go +++ b/operation/repo/branch.go @@ -10,6 +10,7 @@ import ( gitea_sdk "code.gitea.io/sdk/gitea" "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" ) const ( @@ -44,6 +45,21 @@ var ( ) ) +func init() { + Tool.RegisterWrite(server.ServerTool{ + Tool: CreateBranchTool, + Handler: CreateBranchFn, + }) + Tool.RegisterWrite(server.ServerTool{ + Tool: DeleteBranchTool, + Handler: DeleteBranchFn, + }) + Tool.RegisterRead(server.ServerTool{ + Tool: ListBranchesTool, + Handler: ListBranchesFn, + }) +} + func CreateBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called CreateBranchFn") owner, ok := req.Params.Arguments["owner"].(string) diff --git a/operation/repo/commit.go b/operation/repo/commit.go index 6665a46..9c9d0e8 100644 --- a/operation/repo/commit.go +++ b/operation/repo/commit.go @@ -10,6 +10,7 @@ import ( gitea_sdk "code.gitea.io/sdk/gitea" "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" ) const ( @@ -29,6 +30,13 @@ var ( ) ) +func init() { + Tool.RegisterRead(server.ServerTool{ + Tool: ListRepoCommitsTool, + Handler: ListRepoCommitsFn, + }) +} + func ListRepoCommitsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called ListRepoCommitsFn") owner, ok := req.Params.Arguments["owner"].(string) diff --git a/operation/repo/file.go b/operation/repo/file.go index e16289b..09b55b8 100644 --- a/operation/repo/file.go +++ b/operation/repo/file.go @@ -11,6 +11,7 @@ import ( gitea_sdk "code.gitea.io/sdk/gitea" "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" ) const ( @@ -66,6 +67,25 @@ var ( ) ) +func init() { + Tool.RegisterRead(server.ServerTool{ + Tool: GetFileContentTool, + Handler: GetFileContentFn, + }) + Tool.RegisterWrite(server.ServerTool{ + Tool: CreateFileTool, + Handler: CreateFileFn, + }) + Tool.RegisterWrite(server.ServerTool{ + Tool: UpdateFileTool, + Handler: UpdateFileFn, + }) + Tool.RegisterWrite(server.ServerTool{ + Tool: DeleteFileTool, + Handler: DeleteFileFn, + }) +} + func GetFileContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called GetFileFn") owner, ok := req.Params.Arguments["owner"].(string) diff --git a/operation/repo/release.go b/operation/repo/release.go index 2e0f341..f077fd8 100644 --- a/operation/repo/release.go +++ b/operation/repo/release.go @@ -5,12 +5,14 @@ import ( "fmt" "time" - gitea_sdk "code.gitea.io/sdk/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/log" "gitea.com/gitea/gitea-mcp/pkg/ptr" "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 ( @@ -69,6 +71,29 @@ var ( ) ) +func init() { + Tool.RegisterWrite(server.ServerTool{ + Tool: CreateReleaseTool, + Handler: CreateReleaseFn, + }) + Tool.RegisterWrite(server.ServerTool{ + Tool: DeleteReleaseTool, + Handler: DeleteReleaseFn, + }) + Tool.RegisterRead(server.ServerTool{ + Tool: GetReleaseTool, + Handler: GetReleaseFn, + }) + Tool.RegisterRead(server.ServerTool{ + Tool: GetLatestReleaseTool, + Handler: GetLatestReleaseFn, + }) + Tool.RegisterRead(server.ServerTool{ + Tool: ListReleasesTool, + Handler: ListReleasesFn, + }) +} + // To avoid return too many tokens, we need to provide at least information as possible // llm can call get release to get more information type ListReleaseResult struct { diff --git a/operation/repo/repo.go b/operation/repo/repo.go index 13cefc2..91c2215 100644 --- a/operation/repo/repo.go +++ b/operation/repo/repo.go @@ -9,12 +9,15 @@ import ( "gitea.com/gitea/gitea-mcp/pkg/log" "gitea.com/gitea/gitea-mcp/pkg/ptr" "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 ( CreateRepoToolName = "create_repo" ForkRepoToolName = "fork_repo" @@ -54,6 +57,21 @@ var ( ) ) +func init() { + Tool.RegisterWrite(server.ServerTool{ + Tool: CreateRepoTool, + Handler: CreateRepoFn, + }) + Tool.RegisterWrite(server.ServerTool{ + Tool: ForkRepoTool, + Handler: ForkRepoFn, + }) + Tool.RegisterRead(server.ServerTool{ + Tool: ListMyReposTool, + Handler: ListMyReposFn, + }) +} + func RegisterTool(s *server.MCPServer) { s.AddTool(CreateRepoTool, CreateRepoFn) s.AddTool(ForkRepoTool, ForkRepoFn) diff --git a/operation/repo/tag.go b/operation/repo/tag.go index 763e872..6024c04 100644 --- a/operation/repo/tag.go +++ b/operation/repo/tag.go @@ -4,11 +4,13 @@ import ( "context" "fmt" - gitea_sdk "code.gitea.io/sdk/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/log" "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 ( @@ -55,6 +57,25 @@ var ( ) ) +func init() { + Tool.RegisterWrite(server.ServerTool{ + Tool: CreateTagTool, + Handler: CreateTagFn, + }) + Tool.RegisterWrite(server.ServerTool{ + Tool: DeleteTagTool, + Handler: DeleteTagFn, + }) + Tool.RegisterRead(server.ServerTool{ + Tool: GetTagTool, + Handler: GetTagFn, + }) + Tool.RegisterRead(server.ServerTool{ + Tool: ListTagsTool, + Handler: ListTagsFn, + }) +} + // To avoid return too many tokens, we need to provide at least information as possible // llm can call get tag to get more information type ListTagResult struct { diff --git a/operation/search/search.go b/operation/search/search.go index 2db6a3f..c0e5e2a 100644 --- a/operation/search/search.go +++ b/operation/search/search.go @@ -8,12 +8,15 @@ import ( "gitea.com/gitea/gitea-mcp/pkg/log" "gitea.com/gitea/gitea-mcp/pkg/ptr" "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 ( SearchUsersToolName = "search_users" SearchOrgTeamsToolName = "search_org_teams" @@ -55,10 +58,19 @@ var ( ) ) -func RegisterTool(s *server.MCPServer) { - s.AddTool(SearchUsersTool, SearchUsersFn) - s.AddTool(SearOrgTeamsTool, SearchOrgTeamsFn) - s.AddTool(SearchReposTool, SearchReposFn) +func init() { + Tool.RegisterRead(server.ServerTool{ + Tool: SearchUsersTool, + Handler: SearchUsersFn, + }) + Tool.RegisterRead(server.ServerTool{ + Tool: SearOrgTeamsTool, + Handler: SearchOrgTeamsFn, + }) + Tool.RegisterRead(server.ServerTool{ + Tool: SearchReposTool, + Handler: SearchReposFn, + }) } func SearchUsersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { diff --git a/operation/user/user.go b/operation/user/user.go index 8aa4923..a100ab3 100644 --- a/operation/user/user.go +++ b/operation/user/user.go @@ -7,6 +7,7 @@ import ( "gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/log" "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" @@ -18,6 +19,8 @@ const ( GetUserOrgsToolName = "get_user_orgs" ) +var Tool = tool.New() + var ( GetMyUserInfoTool = mcp.NewTool( GetMyUserInfoToolName, @@ -32,9 +35,16 @@ var ( ) ) -func RegisterTool(s *server.MCPServer) { - s.AddTool(GetMyUserInfoTool, GetUserInfoFn) - s.AddTool(GetUserOrgsTool, GetUserOrgsFn) +func init() { + Tool.RegisterRead(server.ServerTool{ + Tool: GetMyUserInfoTool, + Handler: GetUserInfoFn, + }) + + Tool.RegisterRead(server.ServerTool{ + Tool: GetUserOrgsTool, + Handler: GetUserOrgsFn, + }) } func GetUserInfoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { diff --git a/operation/version/version.go b/operation/version/version.go index 2da70ab..431c0a3 100644 --- a/operation/version/version.go +++ b/operation/version/version.go @@ -7,11 +7,14 @@ import ( "gitea.com/gitea/gitea-mcp/pkg/flag" "gitea.com/gitea/gitea-mcp/pkg/log" "gitea.com/gitea/gitea-mcp/pkg/to" + "gitea.com/gitea/gitea-mcp/pkg/tool" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) +var Tool = tool.New() + const ( GetGiteaMCPServerVersion = "get_gitea_mcp_server_version" ) @@ -23,8 +26,11 @@ var ( ) ) -func RegisterTool(s *server.MCPServer) { - s.AddTool(GetGiteaMCPServerVersionTool, GetGiteaMCPServerVersionFn) +func init() { + Tool.RegisterRead(server.ServerTool{ + Tool: GetGiteaMCPServerVersionTool, + Handler: GetGiteaMCPServerVersionFn, + }) } func GetGiteaMCPServerVersionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { diff --git a/pkg/flag/flag.go b/pkg/flag/flag.go index 0c6d4a6..9ebffa1 100644 --- a/pkg/flag/flag.go +++ b/pkg/flag/flag.go @@ -8,5 +8,6 @@ var ( Mode string Insecure bool + ReadOnly bool Debug bool ) diff --git a/pkg/tool/tool.go b/pkg/tool/tool.go new file mode 100644 index 0000000..384d3f1 --- /dev/null +++ b/pkg/tool/tool.go @@ -0,0 +1,37 @@ +package tool + +import ( + "gitea.com/gitea/gitea-mcp/pkg/flag" + "github.com/mark3labs/mcp-go/server" +) + +type Tool struct { + write []server.ServerTool + read []server.ServerTool +} + +func New() *Tool { + return &Tool{ + write: make([]server.ServerTool, 100), + read: make([]server.ServerTool, 100), + } +} + +func (t *Tool) RegisterWrite(s server.ServerTool) { + t.write = append(t.write, s) +} + +func (t *Tool) RegisterRead(s server.ServerTool) { + t.read = append(t.read, s) +} + +func (t *Tool) Tools() []server.ServerTool { + tools := make([]server.ServerTool, 0, len(t.write)+len(t.read)) + if flag.ReadOnly { + tools = append(tools, t.read...) + return tools + } + tools = append(tools, t.write...) + tools = append(tools, t.read...) + return tools +}