chore: add golangci-lint, bump Go to 1.26, fix all lint issues (#133)

## Summary
- Add `.golangci.yml` with linter configuration matching the main gitea repo
- Add `lint`, `lint-fix`, `lint-go`, `lint-go-fix`, and `security-check` Makefile targets
- Add `tidy` Makefile target (extracts min Go version from `go.mod` for `-compat` flag)
- Bump minimum Go version to 1.26
- Update golangci-lint to v2.10.1
- Replace `golang/govulncheck-action` with `make security-check` in CI
- Add `make lint` step to CI
- Fix all lint issues across the codebase (formatting, `errors.New` vs `fmt.Errorf`, `any` vs `interface{}`, unused returns, stuttering names, Go 1.26 `new(expr)`, etc.)
- Remove unused `pkg/ptr` package (inlined by Go 1.26 `new(expr)`)
- Remove dead linter exclusions (staticcheck, gocritic, testifylint, dupl)

## Test plan
- [x] `make lint` passes
- [x] `go test ./...` passes
- [x] `make build` succeeds

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/133
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
This commit is contained in:
silverwind
2026-02-22 17:10:04 +00:00
committed by techknowlogick
parent 4d5fa3ab2c
commit 8728c04748
34 changed files with 727 additions and 675 deletions

View File

@@ -11,16 +11,9 @@ jobs:
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
with: with:
go-version-file: 'go.mod' go-version-file: 'go.mod'
- name: lint
run: make lint
- name: build - name: build
run: | run: make build
make build - name: security-check
run: make security-check
govulncheck_job:
runs-on: ubuntu-latest
name: Run govulncheck
steps:
- id: govulncheck
uses: golang/govulncheck-action@v1
with:
go-version-file: 'go.mod'
go-package: ./...

113
.golangci.yml Normal file
View File

@@ -0,0 +1,113 @@
version: "2"
output:
sort-order:
- file
linters:
default: none
enable:
- bidichk
- bodyclose
- depguard
- errcheck
- forbidigo
- gocheckcompilerdirectives
- gocritic
- govet
- ineffassign
- mirror
- modernize
- nakedret
- nilnil
- nolintlint
- perfsprint
- revive
- staticcheck
- testifylint
- unconvert
- unparam
- unused
- usestdlibvars
- usetesting
- wastedassign
settings:
depguard:
rules:
main:
deny:
- pkg: io/ioutil
desc: use os or io instead
- pkg: golang.org/x/exp
desc: it's experimental and unreliable
- pkg: github.com/pkg/errors
desc: use builtin errors package instead
nolintlint:
allow-unused: false
require-explanation: true
require-specific: true
gocritic:
enabled-checks:
- equalFold
disabled-checks: []
revive:
severity: error
rules:
- name: blank-imports
- name: constant-logical-expr
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: empty-lines
- name: error-return
- name: error-strings
- name: exported
- name: identical-branches
- name: if-return
- name: increment-decrement
- name: modifies-value-receiver
- name: package-comments
- name: redefines-builtin-id
- name: superfluous-else
- name: time-naming
- name: unexported-return
- name: var-declaration
- name: var-naming
disabled: true
staticcheck:
checks:
- all
testifylint: {}
usetesting:
os-temp-dir: true
perfsprint:
concat-loop: false
govet:
enable:
- nilness
- unusedwrite
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- errcheck
- staticcheck
- unparam
path: _test\.go
issues:
max-issues-per-linter: 0
max-same-issues: 0
formatters:
enable:
- gofmt
- gofumpt
settings:
gofumpt:
extra-rules: true
exclusions:
generated: lax
run:
timeout: 10m

View File

@@ -3,6 +3,10 @@ EXECUTABLE := gitea-mcp
VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//') VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
LDFLAGS := -X "main.Version=$(VERSION)" LDFLAGS := -X "main.Version=$(VERSION)"
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.10.1
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.9.2
.PHONY: help .PHONY: help
help: ## Print this help message. help: ## Print this help message.
@echo "Usage: make [target]" @echo "Usage: make [target]"
@@ -45,8 +49,29 @@ air: ## Install air for hot reload.
dev: air ## run the application with hot reload dev: air ## run the application with hot reload
air --build.cmd "make build" --build.bin ./gitea-mcp air --build.cmd "make build" --build.bin ./gitea-mcp
.PHONY: lint
lint: lint-go ## lint everything
.PHONY: lint-fix
lint-fix: lint-go-fix ## lint everything and fix issues
.PHONY: lint-go
lint-go: ## lint go files
$(GO) run $(GOLANGCI_LINT_PACKAGE) run
.PHONY: lint-go-fix
lint-go-fix: ## lint go files and fix issues
$(GO) run $(GOLANGCI_LINT_PACKAGE) run --fix
.PHONY: security-check
security-check: ## run security check
$(GO) run $(GOVULNCHECK_PACKAGE) -show color ./... || true
.PHONY: tidy
tidy: ## run go mod tidy
$(eval MIN_GO_VERSION := $(shell grep -Eo '^go\s+[0-9]+\.[0-9.]+' go.mod | cut -d' ' -f2))
$(GO) mod tidy -compat=$(MIN_GO_VERSION)
.PHONY: vendor .PHONY: vendor
vendor: ## tidy and verify module dependencies vendor: tidy ## tidy and verify module dependencies
@echo 'Tidying and verifying module dependencies...' $(GO) mod verify
go mod tidy
go mod verify

View File

@@ -99,12 +99,12 @@ func init() {
} }
func Execute() { func Execute() {
defer log.Default().Sync() defer log.Default().Sync() //nolint:errcheck // best-effort flush
if err := operation.Run(); err != nil { if err := operation.Run(); err != nil {
if err == context.Canceled { if err == context.Canceled {
log.Info("Server shutdown due to context cancellation") log.Info("Server shutdown due to context cancellation")
return return
} }
log.Fatalf("Run Gitea MCP Server Error: %v", err) log.Fatalf("Run Gitea MCP Server Error: %v", err) //nolint:gocritic // intentional exit after defer
} }
} }

2
go.mod
View File

@@ -1,6 +1,6 @@
module gitea.com/gitea/gitea-mcp module gitea.com/gitea/gitea-mcp
go 1.24.0 go 1.26.0
require ( require (
code.gitea.io/sdk/gitea v0.22.1 code.gitea.io/sdk/gitea v0.22.1

View File

@@ -5,9 +5,7 @@ import (
"gitea.com/gitea/gitea-mcp/pkg/flag" "gitea.com/gitea/gitea-mcp/pkg/flag"
) )
var ( var Version = "dev"
Version = "dev"
)
func init() { func init() {
flag.Version = Version flag.Version = Version

View File

@@ -6,5 +6,3 @@ import (
// Tool is the registry for all Actions-related MCP tools. // Tool is the registry for all Actions-related MCP tools.
var Tool = tool.New() var Tool = tool.New()

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"net/http"
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
@@ -67,7 +68,7 @@ func fetchJobLogBytes(ctx context.Context, owner, repo string, jobID int64) ([]b
} }
lastErr = err lastErr = err
var httpErr *gitea.HTTPError var httpErr *gitea.HTTPError
if errors.As(err, &httpErr) && (httpErr.StatusCode == 404 || httpErr.StatusCode == 405) { if errors.As(err, &httpErr) && (httpErr.StatusCode == http.StatusNotFound || httpErr.StatusCode == http.StatusMethodNotAllowed) {
continue continue
} }
return nil, p, err return nil, p, err
@@ -109,15 +110,15 @@ func GetRepoActionJobLogPreviewFn(ctx context.Context, req mcp.CallToolRequest)
log.Debugf("Called GetRepoActionJobLogPreviewFn") log.Debugf("Called GetRepoActionJobLogPreviewFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
jobIDFloat, ok := req.GetArguments()["job_id"].(float64) jobIDFloat, ok := req.GetArguments()["job_id"].(float64)
if !ok || jobIDFloat <= 0 { if !ok || jobIDFloat <= 0 {
return to.ErrorResult(fmt.Errorf("job_id is required")) return to.ErrorResult(errors.New("job_id is required"))
} }
tailLinesFloat, _ := req.GetArguments()["tail_lines"].(float64) tailLinesFloat, _ := req.GetArguments()["tail_lines"].(float64)
maxBytesFloat, _ := req.GetArguments()["max_bytes"].(float64) maxBytesFloat, _ := req.GetArguments()["max_bytes"].(float64)
@@ -140,13 +141,13 @@ func GetRepoActionJobLogPreviewFn(ctx context.Context, req mcp.CallToolRequest)
limited, truncated := limitBytes(tailed, maxBytes) limited, truncated := limitBytes(tailed, maxBytes)
return to.TextResult(map[string]any{ return to.TextResult(map[string]any{
"endpoint": usedPath, "endpoint": usedPath,
"job_id": jobID, "job_id": jobID,
"bytes": len(raw), "bytes": len(raw),
"tail_lines": tailLines, "tail_lines": tailLines,
"max_bytes": maxBytes, "max_bytes": maxBytes,
"truncated": truncated, "truncated": truncated,
"log": string(limited), "log": string(limited),
}) })
} }
@@ -154,15 +155,15 @@ func DownloadRepoActionJobLogFn(ctx context.Context, req mcp.CallToolRequest) (*
log.Debugf("Called DownloadRepoActionJobLogFn") log.Debugf("Called DownloadRepoActionJobLogFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
jobIDFloat, ok := req.GetArguments()["job_id"].(float64) jobIDFloat, ok := req.GetArguments()["job_id"].(float64)
if !ok || jobIDFloat <= 0 { if !ok || jobIDFloat <= 0 {
return to.ErrorResult(fmt.Errorf("job_id is required")) return to.ErrorResult(errors.New("job_id is required"))
} }
outputPath, _ := req.GetArguments()["output_path"].(string) outputPath, _ := req.GetArguments()["output_path"].(string)
jobID := int64(jobIDFloat) jobID := int64(jobIDFloat)
@@ -194,5 +195,3 @@ func DownloadRepoActionJobLogFn(ctx context.Context, req mcp.CallToolRequest) (*
"bytes": len(raw), "bytes": len(raw),
}) })
} }

View File

@@ -20,5 +20,3 @@ func TestLimitBytesKeepsTail(t *testing.T) {
t.Fatalf("limitBytes tail = %q, want %q", string(out), "6789") t.Fatalf("limitBytes tail = %q, want %q", string(out), "6789")
} }
} }

View File

@@ -4,7 +4,10 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"maps"
"net/http"
"net/url" "net/url"
"strconv"
"gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log" "gitea.com/gitea/gitea-mcp/pkg/log"
@@ -125,32 +128,32 @@ func init() {
Tool.RegisterRead(server.ServerTool{Tool: ListRepoActionRunJobsTool, Handler: ListRepoActionRunJobsFn}) Tool.RegisterRead(server.ServerTool{Tool: ListRepoActionRunJobsTool, Handler: ListRepoActionRunJobsFn})
} }
func doJSONWithFallback(ctx context.Context, method string, paths []string, query url.Values, body any, respOut any) (string, int, error) { func doJSONWithFallback(ctx context.Context, method string, paths []string, query url.Values, body, respOut any) error {
var lastErr error var lastErr error
for _, p := range paths { for _, p := range paths {
status, err := gitea.DoJSON(ctx, method, p, query, body, respOut) _, err := gitea.DoJSON(ctx, method, p, query, body, respOut)
if err == nil { if err == nil {
return p, status, nil return nil
} }
lastErr = err lastErr = err
var httpErr *gitea.HTTPError var httpErr *gitea.HTTPError
if errors.As(err, &httpErr) && (httpErr.StatusCode == 404 || httpErr.StatusCode == 405) { if errors.As(err, &httpErr) && (httpErr.StatusCode == http.StatusNotFound || httpErr.StatusCode == http.StatusMethodNotAllowed) {
continue continue
} }
return p, status, err return err
} }
return "", 0, lastErr return lastErr
} }
func ListRepoActionWorkflowsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { func ListRepoActionWorkflowsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called ListRepoActionWorkflowsFn") log.Debugf("Called ListRepoActionWorkflowsFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
page, _ := req.GetArguments()["page"].(float64) page, _ := req.GetArguments()["page"].(float64)
if page <= 0 { if page <= 0 {
@@ -161,11 +164,11 @@ func ListRepoActionWorkflowsFn(ctx context.Context, req mcp.CallToolRequest) (*m
pageSize = 50 pageSize = 50
} }
query := url.Values{} query := url.Values{}
query.Set("page", fmt.Sprintf("%d", int(page))) query.Set("page", strconv.Itoa(int(page)))
query.Set("limit", fmt.Sprintf("%d", int(pageSize))) query.Set("limit", strconv.Itoa(int(pageSize)))
var result any var result any
_, _, err := doJSONWithFallback(ctx, "GET", err := doJSONWithFallback(ctx, "GET",
[]string{ []string{
fmt.Sprintf("repos/%s/%s/actions/workflows", url.PathEscape(owner), url.PathEscape(repo)), fmt.Sprintf("repos/%s/%s/actions/workflows", url.PathEscape(owner), url.PathEscape(repo)),
}, },
@@ -181,19 +184,19 @@ func GetRepoActionWorkflowFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
log.Debugf("Called GetRepoActionWorkflowFn") log.Debugf("Called GetRepoActionWorkflowFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
workflowID, ok := req.GetArguments()["workflow_id"].(string) workflowID, ok := req.GetArguments()["workflow_id"].(string)
if !ok || workflowID == "" { if !ok || workflowID == "" {
return to.ErrorResult(fmt.Errorf("workflow_id is required")) return to.ErrorResult(errors.New("workflow_id is required"))
} }
var result any var result any
_, _, err := doJSONWithFallback(ctx, "GET", err := doJSONWithFallback(ctx, "GET",
[]string{ []string{
fmt.Sprintf("repos/%s/%s/actions/workflows/%s", url.PathEscape(owner), url.PathEscape(repo), url.PathEscape(workflowID)), fmt.Sprintf("repos/%s/%s/actions/workflows/%s", url.PathEscape(owner), url.PathEscape(repo), url.PathEscape(workflowID)),
}, },
@@ -209,30 +212,28 @@ func DispatchRepoActionWorkflowFn(ctx context.Context, req mcp.CallToolRequest)
log.Debugf("Called DispatchRepoActionWorkflowFn") log.Debugf("Called DispatchRepoActionWorkflowFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
workflowID, ok := req.GetArguments()["workflow_id"].(string) workflowID, ok := req.GetArguments()["workflow_id"].(string)
if !ok || workflowID == "" { if !ok || workflowID == "" {
return to.ErrorResult(fmt.Errorf("workflow_id is required")) return to.ErrorResult(errors.New("workflow_id is required"))
} }
ref, ok := req.GetArguments()["ref"].(string) ref, ok := req.GetArguments()["ref"].(string)
if !ok || ref == "" { if !ok || ref == "" {
return to.ErrorResult(fmt.Errorf("ref is required")) return to.ErrorResult(errors.New("ref is required"))
} }
var inputs map[string]any var inputs map[string]any
if raw, exists := req.GetArguments()["inputs"]; exists { if raw, exists := req.GetArguments()["inputs"]; exists {
if m, ok := raw.(map[string]any); ok { if m, ok := raw.(map[string]any); ok {
inputs = m inputs = m
} else if m, ok := raw.(map[string]interface{}); ok { } else if m, ok := raw.(map[string]any); ok {
inputs = make(map[string]any, len(m)) inputs = make(map[string]any, len(m))
for k, v := range m { maps.Copy(inputs, m)
inputs[k] = v
}
} }
} }
@@ -243,7 +244,7 @@ func DispatchRepoActionWorkflowFn(ctx context.Context, req mcp.CallToolRequest)
body["inputs"] = inputs body["inputs"] = inputs
} }
_, _, err := doJSONWithFallback(ctx, "POST", err := doJSONWithFallback(ctx, "POST",
[]string{ []string{
fmt.Sprintf("repos/%s/%s/actions/workflows/%s/dispatches", url.PathEscape(owner), url.PathEscape(repo), url.PathEscape(workflowID)), fmt.Sprintf("repos/%s/%s/actions/workflows/%s/dispatches", url.PathEscape(owner), url.PathEscape(repo), url.PathEscape(workflowID)),
fmt.Sprintf("repos/%s/%s/actions/workflows/%s/dispatch", url.PathEscape(owner), url.PathEscape(repo), url.PathEscape(workflowID)), fmt.Sprintf("repos/%s/%s/actions/workflows/%s/dispatch", url.PathEscape(owner), url.PathEscape(repo), url.PathEscape(workflowID)),
@@ -252,7 +253,7 @@ func DispatchRepoActionWorkflowFn(ctx context.Context, req mcp.CallToolRequest)
) )
if err != nil { if err != nil {
var httpErr *gitea.HTTPError var httpErr *gitea.HTTPError
if errors.As(err, &httpErr) && (httpErr.StatusCode == 404 || httpErr.StatusCode == 405) { if errors.As(err, &httpErr) && (httpErr.StatusCode == http.StatusNotFound || httpErr.StatusCode == http.StatusMethodNotAllowed) {
return to.ErrorResult(fmt.Errorf("workflow dispatch not supported on this Gitea version (endpoint returned %d). Check https://docs.gitea.com/api/1.24/ for available Actions endpoints", httpErr.StatusCode)) return to.ErrorResult(fmt.Errorf("workflow dispatch not supported on this Gitea version (endpoint returned %d). Check https://docs.gitea.com/api/1.24/ for available Actions endpoints", httpErr.StatusCode))
} }
return to.ErrorResult(fmt.Errorf("dispatch action workflow err: %v", err)) return to.ErrorResult(fmt.Errorf("dispatch action workflow err: %v", err))
@@ -264,11 +265,11 @@ func ListRepoActionRunsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
log.Debugf("Called ListRepoActionRunsFn") log.Debugf("Called ListRepoActionRunsFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
page, _ := req.GetArguments()["page"].(float64) page, _ := req.GetArguments()["page"].(float64)
if page <= 0 { if page <= 0 {
@@ -281,14 +282,14 @@ func ListRepoActionRunsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
statusFilter, _ := req.GetArguments()["status"].(string) statusFilter, _ := req.GetArguments()["status"].(string)
query := url.Values{} query := url.Values{}
query.Set("page", fmt.Sprintf("%d", int(page))) query.Set("page", strconv.Itoa(int(page)))
query.Set("limit", fmt.Sprintf("%d", int(pageSize))) query.Set("limit", strconv.Itoa(int(pageSize)))
if statusFilter != "" { if statusFilter != "" {
query.Set("status", statusFilter) query.Set("status", statusFilter)
} }
var result any var result any
_, _, err := doJSONWithFallback(ctx, "GET", err := doJSONWithFallback(ctx, "GET",
[]string{ []string{
fmt.Sprintf("repos/%s/%s/actions/runs", url.PathEscape(owner), url.PathEscape(repo)), fmt.Sprintf("repos/%s/%s/actions/runs", url.PathEscape(owner), url.PathEscape(repo)),
}, },
@@ -304,19 +305,19 @@ func GetRepoActionRunFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
log.Debugf("Called GetRepoActionRunFn") log.Debugf("Called GetRepoActionRunFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
runID, ok := req.GetArguments()["run_id"].(float64) runID, ok := req.GetArguments()["run_id"].(float64)
if !ok || runID <= 0 { if !ok || runID <= 0 {
return to.ErrorResult(fmt.Errorf("run_id is required")) return to.ErrorResult(errors.New("run_id is required"))
} }
var result any var result any
_, _, err := doJSONWithFallback(ctx, "GET", err := doJSONWithFallback(ctx, "GET",
[]string{ []string{
fmt.Sprintf("repos/%s/%s/actions/runs/%d", url.PathEscape(owner), url.PathEscape(repo), int64(runID)), fmt.Sprintf("repos/%s/%s/actions/runs/%d", url.PathEscape(owner), url.PathEscape(repo), int64(runID)),
}, },
@@ -332,18 +333,18 @@ func CancelRepoActionRunFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.C
log.Debugf("Called CancelRepoActionRunFn") log.Debugf("Called CancelRepoActionRunFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
runID, ok := req.GetArguments()["run_id"].(float64) runID, ok := req.GetArguments()["run_id"].(float64)
if !ok || runID <= 0 { if !ok || runID <= 0 {
return to.ErrorResult(fmt.Errorf("run_id is required")) return to.ErrorResult(errors.New("run_id is required"))
} }
_, _, err := doJSONWithFallback(ctx, "POST", err := doJSONWithFallback(ctx, "POST",
[]string{ []string{
fmt.Sprintf("repos/%s/%s/actions/runs/%d/cancel", url.PathEscape(owner), url.PathEscape(repo), int64(runID)), fmt.Sprintf("repos/%s/%s/actions/runs/%d/cancel", url.PathEscape(owner), url.PathEscape(repo), int64(runID)),
}, },
@@ -359,18 +360,18 @@ func RerunRepoActionRunFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
log.Debugf("Called RerunRepoActionRunFn") log.Debugf("Called RerunRepoActionRunFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
runID, ok := req.GetArguments()["run_id"].(float64) runID, ok := req.GetArguments()["run_id"].(float64)
if !ok || runID <= 0 { if !ok || runID <= 0 {
return to.ErrorResult(fmt.Errorf("run_id is required")) return to.ErrorResult(errors.New("run_id is required"))
} }
_, _, err := doJSONWithFallback(ctx, "POST", err := doJSONWithFallback(ctx, "POST",
[]string{ []string{
fmt.Sprintf("repos/%s/%s/actions/runs/%d/rerun", url.PathEscape(owner), url.PathEscape(repo), int64(runID)), fmt.Sprintf("repos/%s/%s/actions/runs/%d/rerun", url.PathEscape(owner), url.PathEscape(repo), int64(runID)),
fmt.Sprintf("repos/%s/%s/actions/runs/%d/rerun-failed-jobs", url.PathEscape(owner), url.PathEscape(repo), int64(runID)), fmt.Sprintf("repos/%s/%s/actions/runs/%d/rerun-failed-jobs", url.PathEscape(owner), url.PathEscape(repo), int64(runID)),
@@ -379,7 +380,7 @@ func RerunRepoActionRunFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
) )
if err != nil { if err != nil {
var httpErr *gitea.HTTPError var httpErr *gitea.HTTPError
if errors.As(err, &httpErr) && (httpErr.StatusCode == 404 || httpErr.StatusCode == 405) { if errors.As(err, &httpErr) && (httpErr.StatusCode == http.StatusNotFound || httpErr.StatusCode == http.StatusMethodNotAllowed) {
return to.ErrorResult(fmt.Errorf("workflow rerun not supported on this Gitea version (endpoint returned %d). Check https://docs.gitea.com/api/1.24/ for available Actions endpoints", httpErr.StatusCode)) return to.ErrorResult(fmt.Errorf("workflow rerun not supported on this Gitea version (endpoint returned %d). Check https://docs.gitea.com/api/1.24/ for available Actions endpoints", httpErr.StatusCode))
} }
return to.ErrorResult(fmt.Errorf("rerun action run err: %v", err)) return to.ErrorResult(fmt.Errorf("rerun action run err: %v", err))
@@ -391,11 +392,11 @@ func ListRepoActionJobsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
log.Debugf("Called ListRepoActionJobsFn") log.Debugf("Called ListRepoActionJobsFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
page, _ := req.GetArguments()["page"].(float64) page, _ := req.GetArguments()["page"].(float64)
if page <= 0 { if page <= 0 {
@@ -408,14 +409,14 @@ func ListRepoActionJobsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
statusFilter, _ := req.GetArguments()["status"].(string) statusFilter, _ := req.GetArguments()["status"].(string)
query := url.Values{} query := url.Values{}
query.Set("page", fmt.Sprintf("%d", int(page))) query.Set("page", strconv.Itoa(int(page)))
query.Set("limit", fmt.Sprintf("%d", int(pageSize))) query.Set("limit", strconv.Itoa(int(pageSize)))
if statusFilter != "" { if statusFilter != "" {
query.Set("status", statusFilter) query.Set("status", statusFilter)
} }
var result any var result any
_, _, err := doJSONWithFallback(ctx, "GET", err := doJSONWithFallback(ctx, "GET",
[]string{ []string{
fmt.Sprintf("repos/%s/%s/actions/jobs", url.PathEscape(owner), url.PathEscape(repo)), fmt.Sprintf("repos/%s/%s/actions/jobs", url.PathEscape(owner), url.PathEscape(repo)),
}, },
@@ -431,15 +432,15 @@ func ListRepoActionRunJobsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
log.Debugf("Called ListRepoActionRunJobsFn") log.Debugf("Called ListRepoActionRunJobsFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
runID, ok := req.GetArguments()["run_id"].(float64) runID, ok := req.GetArguments()["run_id"].(float64)
if !ok || runID <= 0 { if !ok || runID <= 0 {
return to.ErrorResult(fmt.Errorf("run_id is required")) return to.ErrorResult(errors.New("run_id is required"))
} }
page, _ := req.GetArguments()["page"].(float64) page, _ := req.GetArguments()["page"].(float64)
if page <= 0 { if page <= 0 {
@@ -451,11 +452,11 @@ func ListRepoActionRunJobsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
} }
query := url.Values{} query := url.Values{}
query.Set("page", fmt.Sprintf("%d", int(page))) query.Set("page", strconv.Itoa(int(page)))
query.Set("limit", fmt.Sprintf("%d", int(pageSize))) query.Set("limit", strconv.Itoa(int(pageSize)))
var result any var result any
_, _, err := doJSONWithFallback(ctx, "GET", err := doJSONWithFallback(ctx, "GET",
[]string{ []string{
fmt.Sprintf("repos/%s/%s/actions/runs/%d/jobs", url.PathEscape(owner), url.PathEscape(repo), int64(runID)), fmt.Sprintf("repos/%s/%s/actions/runs/%d/jobs", url.PathEscape(owner), url.PathEscape(repo), int64(runID)),
}, },

View File

@@ -2,6 +2,7 @@ package actions
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"net/url" "net/url"
"time" "time"
@@ -27,7 +28,7 @@ const (
type secretMeta struct { type secretMeta struct {
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
CreatedAt time.Time `json:"created_at,omitempty"` CreatedAt time.Time `json:"created_at,omitzero"`
} }
var ( var (
@@ -97,11 +98,11 @@ func ListRepoActionSecretsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
log.Debugf("Called ListRepoActionSecretsFn") log.Debugf("Called ListRepoActionSecretsFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
page, _ := req.GetArguments()["page"].(float64) page, _ := req.GetArguments()["page"].(float64)
if page <= 0 { if page <= 0 {
@@ -142,19 +143,19 @@ func UpsertRepoActionSecretFn(ctx context.Context, req mcp.CallToolRequest) (*mc
log.Debugf("Called UpsertRepoActionSecretFn") log.Debugf("Called UpsertRepoActionSecretFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
name, ok := req.GetArguments()["name"].(string) name, ok := req.GetArguments()["name"].(string)
if !ok || name == "" { if !ok || name == "" {
return to.ErrorResult(fmt.Errorf("name is required")) return to.ErrorResult(errors.New("name is required"))
} }
data, ok := req.GetArguments()["data"].(string) data, ok := req.GetArguments()["data"].(string)
if !ok || data == "" { if !ok || data == "" {
return to.ErrorResult(fmt.Errorf("data is required")) return to.ErrorResult(errors.New("data is required"))
} }
description, _ := req.GetArguments()["description"].(string) description, _ := req.GetArguments()["description"].(string)
@@ -177,15 +178,15 @@ func DeleteRepoActionSecretFn(ctx context.Context, req mcp.CallToolRequest) (*mc
log.Debugf("Called DeleteRepoActionSecretFn") log.Debugf("Called DeleteRepoActionSecretFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
secretName, ok := req.GetArguments()["secretName"].(string) secretName, ok := req.GetArguments()["secretName"].(string)
if !ok || secretName == "" { if !ok || secretName == "" {
return to.ErrorResult(fmt.Errorf("secretName is required")) return to.ErrorResult(errors.New("secretName is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -203,7 +204,7 @@ func ListOrgActionSecretsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.
log.Debugf("Called ListOrgActionSecretsFn") log.Debugf("Called ListOrgActionSecretsFn")
org, ok := req.GetArguments()["org"].(string) org, ok := req.GetArguments()["org"].(string)
if !ok || org == "" { if !ok || org == "" {
return to.ErrorResult(fmt.Errorf("org is required")) return to.ErrorResult(errors.New("org is required"))
} }
page, _ := req.GetArguments()["page"].(float64) page, _ := req.GetArguments()["page"].(float64)
if page <= 0 { if page <= 0 {
@@ -244,15 +245,15 @@ func UpsertOrgActionSecretFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
log.Debugf("Called UpsertOrgActionSecretFn") log.Debugf("Called UpsertOrgActionSecretFn")
org, ok := req.GetArguments()["org"].(string) org, ok := req.GetArguments()["org"].(string)
if !ok || org == "" { if !ok || org == "" {
return to.ErrorResult(fmt.Errorf("org is required")) return to.ErrorResult(errors.New("org is required"))
} }
name, ok := req.GetArguments()["name"].(string) name, ok := req.GetArguments()["name"].(string)
if !ok || name == "" { if !ok || name == "" {
return to.ErrorResult(fmt.Errorf("name is required")) return to.ErrorResult(errors.New("name is required"))
} }
data, ok := req.GetArguments()["data"].(string) data, ok := req.GetArguments()["data"].(string)
if !ok || data == "" { if !ok || data == "" {
return to.ErrorResult(fmt.Errorf("data is required")) return to.ErrorResult(errors.New("data is required"))
} }
description, _ := req.GetArguments()["description"].(string) description, _ := req.GetArguments()["description"].(string)
@@ -275,11 +276,11 @@ func DeleteOrgActionSecretFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
log.Debugf("Called DeleteOrgActionSecretFn") log.Debugf("Called DeleteOrgActionSecretFn")
org, ok := req.GetArguments()["org"].(string) org, ok := req.GetArguments()["org"].(string)
if !ok || org == "" { if !ok || org == "" {
return to.ErrorResult(fmt.Errorf("org is required")) return to.ErrorResult(errors.New("org is required"))
} }
secretName, ok := req.GetArguments()["secretName"].(string) secretName, ok := req.GetArguments()["secretName"].(string)
if !ok || secretName == "" { if !ok || secretName == "" {
return to.ErrorResult(fmt.Errorf("secretName is required")) return to.ErrorResult(errors.New("secretName is required"))
} }
escapedOrg := url.PathEscape(org) escapedOrg := url.PathEscape(org)

View File

@@ -2,8 +2,10 @@ package actions
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"net/url" "net/url"
"strconv"
"gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log" "gitea.com/gitea/gitea-mcp/pkg/log"
@@ -21,11 +23,11 @@ const (
UpdateRepoActionVariableToolName = "update_repo_action_variable" UpdateRepoActionVariableToolName = "update_repo_action_variable"
DeleteRepoActionVariableToolName = "delete_repo_action_variable" DeleteRepoActionVariableToolName = "delete_repo_action_variable"
ListOrgActionVariablesToolName = "list_org_action_variables" ListOrgActionVariablesToolName = "list_org_action_variables"
GetOrgActionVariableToolName = "get_org_action_variable" GetOrgActionVariableToolName = "get_org_action_variable"
CreateOrgActionVariableToolName = "create_org_action_variable" CreateOrgActionVariableToolName = "create_org_action_variable"
UpdateOrgActionVariableToolName = "update_org_action_variable" UpdateOrgActionVariableToolName = "update_org_action_variable"
DeleteOrgActionVariableToolName = "delete_org_action_variable" DeleteOrgActionVariableToolName = "delete_org_action_variable"
) )
var ( var (
@@ -131,11 +133,11 @@ func ListRepoActionVariablesFn(ctx context.Context, req mcp.CallToolRequest) (*m
log.Debugf("Called ListRepoActionVariablesFn") log.Debugf("Called ListRepoActionVariablesFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
page, _ := req.GetArguments()["page"].(float64) page, _ := req.GetArguments()["page"].(float64)
if page <= 0 { if page <= 0 {
@@ -147,8 +149,8 @@ func ListRepoActionVariablesFn(ctx context.Context, req mcp.CallToolRequest) (*m
} }
query := url.Values{} query := url.Values{}
query.Set("page", fmt.Sprintf("%d", int(page))) query.Set("page", strconv.Itoa(int(page)))
query.Set("limit", fmt.Sprintf("%d", int(pageSize))) query.Set("limit", strconv.Itoa(int(pageSize)))
var result any var result any
_, err := gitea.DoJSON(ctx, "GET", fmt.Sprintf("repos/%s/%s/actions/variables", url.PathEscape(owner), url.PathEscape(repo)), query, nil, &result) _, err := gitea.DoJSON(ctx, "GET", fmt.Sprintf("repos/%s/%s/actions/variables", url.PathEscape(owner), url.PathEscape(repo)), query, nil, &result)
@@ -162,15 +164,15 @@ func GetRepoActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
log.Debugf("Called GetRepoActionVariableFn") log.Debugf("Called GetRepoActionVariableFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
name, ok := req.GetArguments()["name"].(string) name, ok := req.GetArguments()["name"].(string)
if !ok || name == "" { if !ok || name == "" {
return to.ErrorResult(fmt.Errorf("name is required")) return to.ErrorResult(errors.New("name is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -188,19 +190,19 @@ func CreateRepoActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*
log.Debugf("Called CreateRepoActionVariableFn") log.Debugf("Called CreateRepoActionVariableFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
name, ok := req.GetArguments()["name"].(string) name, ok := req.GetArguments()["name"].(string)
if !ok || name == "" { if !ok || name == "" {
return to.ErrorResult(fmt.Errorf("name is required")) return to.ErrorResult(errors.New("name is required"))
} }
value, ok := req.GetArguments()["value"].(string) value, ok := req.GetArguments()["value"].(string)
if !ok || value == "" { if !ok || value == "" {
return to.ErrorResult(fmt.Errorf("value is required")) return to.ErrorResult(errors.New("value is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -218,19 +220,19 @@ func UpdateRepoActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*
log.Debugf("Called UpdateRepoActionVariableFn") log.Debugf("Called UpdateRepoActionVariableFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
name, ok := req.GetArguments()["name"].(string) name, ok := req.GetArguments()["name"].(string)
if !ok || name == "" { if !ok || name == "" {
return to.ErrorResult(fmt.Errorf("name is required")) return to.ErrorResult(errors.New("name is required"))
} }
value, ok := req.GetArguments()["value"].(string) value, ok := req.GetArguments()["value"].(string)
if !ok || value == "" { if !ok || value == "" {
return to.ErrorResult(fmt.Errorf("value is required")) return to.ErrorResult(errors.New("value is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -248,15 +250,15 @@ func DeleteRepoActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*
log.Debugf("Called DeleteRepoActionVariableFn") log.Debugf("Called DeleteRepoActionVariableFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok || owner == "" { if !ok || owner == "" {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok || repo == "" { if !ok || repo == "" {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
name, ok := req.GetArguments()["name"].(string) name, ok := req.GetArguments()["name"].(string)
if !ok || name == "" { if !ok || name == "" {
return to.ErrorResult(fmt.Errorf("name is required")) return to.ErrorResult(errors.New("name is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -274,7 +276,7 @@ func ListOrgActionVariablesFn(ctx context.Context, req mcp.CallToolRequest) (*mc
log.Debugf("Called ListOrgActionVariablesFn") log.Debugf("Called ListOrgActionVariablesFn")
org, ok := req.GetArguments()["org"].(string) org, ok := req.GetArguments()["org"].(string)
if !ok || org == "" { if !ok || org == "" {
return to.ErrorResult(fmt.Errorf("org is required")) return to.ErrorResult(errors.New("org is required"))
} }
page, _ := req.GetArguments()["page"].(float64) page, _ := req.GetArguments()["page"].(float64)
if page <= 0 { if page <= 0 {
@@ -302,11 +304,11 @@ func GetOrgActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.
log.Debugf("Called GetOrgActionVariableFn") log.Debugf("Called GetOrgActionVariableFn")
org, ok := req.GetArguments()["org"].(string) org, ok := req.GetArguments()["org"].(string)
if !ok || org == "" { if !ok || org == "" {
return to.ErrorResult(fmt.Errorf("org is required")) return to.ErrorResult(errors.New("org is required"))
} }
name, ok := req.GetArguments()["name"].(string) name, ok := req.GetArguments()["name"].(string)
if !ok || name == "" { if !ok || name == "" {
return to.ErrorResult(fmt.Errorf("name is required")) return to.ErrorResult(errors.New("name is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -324,15 +326,15 @@ func CreateOrgActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*m
log.Debugf("Called CreateOrgActionVariableFn") log.Debugf("Called CreateOrgActionVariableFn")
org, ok := req.GetArguments()["org"].(string) org, ok := req.GetArguments()["org"].(string)
if !ok || org == "" { if !ok || org == "" {
return to.ErrorResult(fmt.Errorf("org is required")) return to.ErrorResult(errors.New("org is required"))
} }
name, ok := req.GetArguments()["name"].(string) name, ok := req.GetArguments()["name"].(string)
if !ok || name == "" { if !ok || name == "" {
return to.ErrorResult(fmt.Errorf("name is required")) return to.ErrorResult(errors.New("name is required"))
} }
value, ok := req.GetArguments()["value"].(string) value, ok := req.GetArguments()["value"].(string)
if !ok || value == "" { if !ok || value == "" {
return to.ErrorResult(fmt.Errorf("value is required")) return to.ErrorResult(errors.New("value is required"))
} }
description, _ := req.GetArguments()["description"].(string) description, _ := req.GetArguments()["description"].(string)
@@ -355,15 +357,15 @@ func UpdateOrgActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*m
log.Debugf("Called UpdateOrgActionVariableFn") log.Debugf("Called UpdateOrgActionVariableFn")
org, ok := req.GetArguments()["org"].(string) org, ok := req.GetArguments()["org"].(string)
if !ok || org == "" { if !ok || org == "" {
return to.ErrorResult(fmt.Errorf("org is required")) return to.ErrorResult(errors.New("org is required"))
} }
name, ok := req.GetArguments()["name"].(string) name, ok := req.GetArguments()["name"].(string)
if !ok || name == "" { if !ok || name == "" {
return to.ErrorResult(fmt.Errorf("name is required")) return to.ErrorResult(errors.New("name is required"))
} }
value, ok := req.GetArguments()["value"].(string) value, ok := req.GetArguments()["value"].(string)
if !ok || value == "" { if !ok || value == "" {
return to.ErrorResult(fmt.Errorf("value is required")) return to.ErrorResult(errors.New("value is required"))
} }
description, _ := req.GetArguments()["description"].(string) description, _ := req.GetArguments()["description"].(string)
@@ -385,11 +387,11 @@ func DeleteOrgActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*m
log.Debugf("Called DeleteOrgActionVariableFn") log.Debugf("Called DeleteOrgActionVariableFn")
org, ok := req.GetArguments()["org"].(string) org, ok := req.GetArguments()["org"].(string)
if !ok || org == "" { if !ok || org == "" {
return to.ErrorResult(fmt.Errorf("org is required")) return to.ErrorResult(errors.New("org is required"))
} }
name, ok := req.GetArguments()["name"].(string) name, ok := req.GetArguments()["name"].(string)
if !ok || name == "" { if !ok || name == "" {
return to.ErrorResult(fmt.Errorf("name is required")) return to.ErrorResult(errors.New("name is required"))
} }
_, err := gitea.DoJSON(ctx, "DELETE", fmt.Sprintf("orgs/%s/actions/variables/%s", url.PathEscape(org), url.PathEscape(name)), nil, nil, nil) _, err := gitea.DoJSON(ctx, "DELETE", fmt.Sprintf("orgs/%s/actions/variables/%s", url.PathEscape(org), url.PathEscape(name)), nil, nil, nil)
@@ -398,5 +400,3 @@ func DeleteOrgActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*m
} }
return to.TextResult(map[string]any{"message": "variable deleted"}) return to.TextResult(map[string]any{"message": "variable deleted"})
} }

View File

@@ -2,12 +2,12 @@ package issue
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log" "gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params" "gitea.com/gitea/gitea-mcp/pkg/params"
"gitea.com/gitea/gitea-mcp/pkg/ptr"
"gitea.com/gitea/gitea-mcp/pkg/to" "gitea.com/gitea/gitea-mcp/pkg/to"
"gitea.com/gitea/gitea-mcp/pkg/tool" "gitea.com/gitea/gitea-mcp/pkg/tool"
@@ -73,7 +73,7 @@ var (
mcp.WithNumber("index", mcp.Required(), mcp.Description("repository issue index")), mcp.WithNumber("index", mcp.Required(), mcp.Description("repository issue index")),
mcp.WithString("title", mcp.Description("issue title"), mcp.DefaultString("")), mcp.WithString("title", mcp.Description("issue title"), mcp.DefaultString("")),
mcp.WithString("body", mcp.Description("issue body content")), mcp.WithString("body", mcp.Description("issue body content")),
mcp.WithArray("assignees", mcp.Description("usernames to assign to this issue"), mcp.Items(map[string]interface{}{"type": "string"})), mcp.WithArray("assignees", mcp.Description("usernames to assign to this issue"), mcp.Items(map[string]any{"type": "string"})),
mcp.WithNumber("milestone", mcp.Description("milestone number")), mcp.WithNumber("milestone", mcp.Description("milestone number")),
mcp.WithString("state", mcp.Description("issue state, one of open, closed, all")), mcp.WithString("state", mcp.Description("issue state, one of open, closed, all")),
) )
@@ -131,11 +131,11 @@ func GetIssueByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
log.Debugf("Called GetIssueByIndexFn") log.Debugf("Called GetIssueByIndexFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -157,11 +157,11 @@ func ListRepoIssuesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
log.Debugf("Called ListIssuesFn") log.Debugf("Called ListIssuesFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
state, ok := req.GetArguments()["state"].(string) state, ok := req.GetArguments()["state"].(string)
if !ok { if !ok {
@@ -197,19 +197,19 @@ func CreateIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
log.Debugf("Called CreateIssueFn") log.Debugf("Called CreateIssueFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
title, ok := req.GetArguments()["title"].(string) title, ok := req.GetArguments()["title"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("title is required")) return to.ErrorResult(errors.New("title is required"))
} }
body, ok := req.GetArguments()["body"].(string) body, ok := req.GetArguments()["body"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("body is required")) return to.ErrorResult(errors.New("body is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
if err != nil { if err != nil {
@@ -230,11 +230,11 @@ func CreateIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
log.Debugf("Called CreateIssueCommentFn") log.Debugf("Called CreateIssueCommentFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -242,7 +242,7 @@ func CreateIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
} }
body, ok := req.GetArguments()["body"].(string) body, ok := req.GetArguments()["body"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("body is required")) return to.ErrorResult(errors.New("body is required"))
} }
opt := gitea_sdk.CreateIssueCommentOption{ opt := gitea_sdk.CreateIssueCommentOption{
Body: body, Body: body,
@@ -263,11 +263,11 @@ func EditIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
log.Debugf("Called EditIssueFn") log.Debugf("Called EditIssueFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -282,11 +282,11 @@ func EditIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
} }
body, ok := req.GetArguments()["body"].(string) body, ok := req.GetArguments()["body"].(string)
if ok { if ok {
opt.Body = ptr.To(body) opt.Body = new(body)
} }
var assignees []string var assignees []string
if assigneesArg, exists := req.GetArguments()["assignees"]; exists { if assigneesArg, exists := req.GetArguments()["assignees"]; exists {
if assigneesSlice, ok := assigneesArg.([]interface{}); ok { if assigneesSlice, ok := assigneesArg.([]any); ok {
for _, assignee := range assigneesSlice { for _, assignee := range assigneesSlice {
if assigneeStr, ok := assignee.(string); ok { if assigneeStr, ok := assignee.(string); ok {
assignees = append(assignees, assigneeStr) assignees = append(assignees, assigneeStr)
@@ -297,11 +297,11 @@ func EditIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
opt.Assignees = assignees opt.Assignees = assignees
milestone, ok := req.GetArguments()["milestone"].(float64) milestone, ok := req.GetArguments()["milestone"].(float64)
if ok { if ok {
opt.Milestone = ptr.To(int64(milestone)) opt.Milestone = new(int64(milestone))
} }
state, ok := req.GetArguments()["state"].(string) state, ok := req.GetArguments()["state"].(string)
if ok { if ok {
opt.State = ptr.To(gitea_sdk.StateType(state)) opt.State = new(gitea_sdk.StateType(state))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -320,19 +320,19 @@ func EditIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
log.Debugf("Called EditIssueCommentFn") log.Debugf("Called EditIssueCommentFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
commentID, ok := req.GetArguments()["commentID"].(float64) commentID, ok := req.GetArguments()["commentID"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("comment ID is required")) return to.ErrorResult(errors.New("comment ID is required"))
} }
body, ok := req.GetArguments()["body"].(string) body, ok := req.GetArguments()["body"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("body is required")) return to.ErrorResult(errors.New("body is required"))
} }
opt := gitea_sdk.EditIssueCommentOption{ opt := gitea_sdk.EditIssueCommentOption{
Body: body, Body: body,
@@ -353,11 +353,11 @@ func GetIssueCommentsByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*m
log.Debugf("Called GetIssueCommentsByIndexFn") log.Debugf("Called GetIssueCommentsByIndexFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {

View File

@@ -2,12 +2,12 @@ package label
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log" "gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params" "gitea.com/gitea/gitea-mcp/pkg/params"
"gitea.com/gitea/gitea-mcp/pkg/ptr"
"gitea.com/gitea/gitea-mcp/pkg/to" "gitea.com/gitea/gitea-mcp/pkg/to"
"gitea.com/gitea/gitea-mcp/pkg/tool" "gitea.com/gitea/gitea-mcp/pkg/tool"
@@ -28,10 +28,10 @@ const (
ReplaceIssueLabelsToolName = "replace_issue_labels" ReplaceIssueLabelsToolName = "replace_issue_labels"
ClearIssueLabelsToolName = "clear_issue_labels" ClearIssueLabelsToolName = "clear_issue_labels"
RemoveIssueLabelToolName = "remove_issue_label" RemoveIssueLabelToolName = "remove_issue_label"
ListOrgLabelsToolName = "list_org_labels" ListOrgLabelsToolName = "list_org_labels"
CreateOrgLabelToolName = "create_org_label" CreateOrgLabelToolName = "create_org_label"
EditOrgLabelToolName = "edit_org_label" EditOrgLabelToolName = "edit_org_label"
DeleteOrgLabelToolName = "delete_org_label" DeleteOrgLabelToolName = "delete_org_label"
) )
var ( var (
@@ -87,7 +87,7 @@ var (
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithNumber("index", mcp.Required(), mcp.Description("issue index")), mcp.WithNumber("index", mcp.Required(), mcp.Description("issue index")),
mcp.WithArray("labels", mcp.Required(), mcp.Description("array of label IDs to add"), mcp.Items(map[string]interface{}{"type": "number"})), mcp.WithArray("labels", mcp.Required(), mcp.Description("array of label IDs to add"), mcp.Items(map[string]any{"type": "number"})),
) )
ReplaceIssueLabelsTool = mcp.NewTool( ReplaceIssueLabelsTool = mcp.NewTool(
@@ -96,7 +96,7 @@ var (
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithNumber("index", mcp.Required(), mcp.Description("issue index")), mcp.WithNumber("index", mcp.Required(), mcp.Description("issue index")),
mcp.WithArray("labels", mcp.Required(), mcp.Description("array of label IDs to replace with"), mcp.Items(map[string]interface{}{"type": "number"})), mcp.WithArray("labels", mcp.Required(), mcp.Description("array of label IDs to replace with"), mcp.Items(map[string]any{"type": "number"})),
) )
ClearIssueLabelsTool = mcp.NewTool( ClearIssueLabelsTool = mcp.NewTool(
@@ -116,42 +116,41 @@ var (
mcp.WithNumber("label_id", mcp.Required(), mcp.Description("label ID to remove")), mcp.WithNumber("label_id", mcp.Required(), mcp.Description("label ID to remove")),
) )
ListOrgLabelsTool = mcp.NewTool( ListOrgLabelsTool = mcp.NewTool(
ListOrgLabelsToolName, ListOrgLabelsToolName,
mcp.WithDescription("Lists labels defined at organization level"), mcp.WithDescription("Lists labels defined at organization level"),
mcp.WithString("org", mcp.Required(), mcp.Description("organization name")), mcp.WithString("org", mcp.Required(), mcp.Description("organization name")),
mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)), mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)),
mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)), mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)),
) )
CreateOrgLabelTool = mcp.NewTool( CreateOrgLabelTool = mcp.NewTool(
CreateOrgLabelToolName, CreateOrgLabelToolName,
mcp.WithDescription("Creates a new label for an organization"), mcp.WithDescription("Creates a new label for an organization"),
mcp.WithString("org", mcp.Required(), mcp.Description("organization name")), mcp.WithString("org", mcp.Required(), mcp.Description("organization name")),
mcp.WithString("name", mcp.Required(), mcp.Description("label name")), mcp.WithString("name", mcp.Required(), mcp.Description("label name")),
mcp.WithString("color", mcp.Required(), mcp.Description("label color (hex code, e.g., #RRGGBB)")), mcp.WithString("color", mcp.Required(), mcp.Description("label color (hex code, e.g., #RRGGBB)")),
mcp.WithString("description", mcp.Description("label description")), mcp.WithString("description", mcp.Description("label description")),
mcp.WithBoolean("exclusive", mcp.Description("whether the label is exclusive"), mcp.DefaultBool(false)), mcp.WithBoolean("exclusive", mcp.Description("whether the label is exclusive"), mcp.DefaultBool(false)),
) )
EditOrgLabelTool = mcp.NewTool( EditOrgLabelTool = mcp.NewTool(
EditOrgLabelToolName, EditOrgLabelToolName,
mcp.WithDescription("Edits an existing organization label"), mcp.WithDescription("Edits an existing organization label"),
mcp.WithString("org", mcp.Required(), mcp.Description("organization name")), mcp.WithString("org", mcp.Required(), mcp.Description("organization name")),
mcp.WithNumber("id", mcp.Required(), mcp.Description("label ID")), mcp.WithNumber("id", mcp.Required(), mcp.Description("label ID")),
mcp.WithString("name", mcp.Description("new label name")), mcp.WithString("name", mcp.Description("new label name")),
mcp.WithString("color", mcp.Description("new label color (hex code, e.g., #RRGGBB)")), mcp.WithString("color", mcp.Description("new label color (hex code, e.g., #RRGGBB)")),
mcp.WithString("description", mcp.Description("new label description")), mcp.WithString("description", mcp.Description("new label description")),
mcp.WithBoolean("exclusive", mcp.Description("whether the label is exclusive")), mcp.WithBoolean("exclusive", mcp.Description("whether the label is exclusive")),
) )
DeleteOrgLabelTool = mcp.NewTool(
DeleteOrgLabelToolName,
mcp.WithDescription("Deletes an organization label by ID"),
mcp.WithString("org", mcp.Required(), mcp.Description("organization name")),
mcp.WithNumber("id", mcp.Required(), mcp.Description("label ID")),
)
DeleteOrgLabelTool = mcp.NewTool(
DeleteOrgLabelToolName,
mcp.WithDescription("Deletes an organization label by ID"),
mcp.WithString("org", mcp.Required(), mcp.Description("organization name")),
mcp.WithNumber("id", mcp.Required(), mcp.Description("label ID")),
)
) )
func init() { func init() {
@@ -191,33 +190,33 @@ func init() {
Tool: RemoveIssueLabelTool, Tool: RemoveIssueLabelTool,
Handler: RemoveIssueLabelFn, Handler: RemoveIssueLabelFn,
}) })
Tool.RegisterRead(server.ServerTool{ Tool.RegisterRead(server.ServerTool{
Tool: ListOrgLabelsTool, Tool: ListOrgLabelsTool,
Handler: ListOrgLabelsFn, Handler: ListOrgLabelsFn,
}) })
Tool.RegisterWrite(server.ServerTool{ Tool.RegisterWrite(server.ServerTool{
Tool: CreateOrgLabelTool, Tool: CreateOrgLabelTool,
Handler: CreateOrgLabelFn, Handler: CreateOrgLabelFn,
}) })
Tool.RegisterWrite(server.ServerTool{ Tool.RegisterWrite(server.ServerTool{
Tool: EditOrgLabelTool, Tool: EditOrgLabelTool,
Handler: EditOrgLabelFn, Handler: EditOrgLabelFn,
}) })
Tool.RegisterWrite(server.ServerTool{ Tool.RegisterWrite(server.ServerTool{
Tool: DeleteOrgLabelTool, Tool: DeleteOrgLabelTool,
Handler: DeleteOrgLabelFn, Handler: DeleteOrgLabelFn,
}) })
} }
func ListRepoLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { func ListRepoLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called ListRepoLabelsFn") log.Debugf("Called ListRepoLabelsFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
page, ok := req.GetArguments()["page"].(float64) page, ok := req.GetArguments()["page"].(float64)
if !ok { if !ok {
@@ -249,15 +248,15 @@ func GetRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
log.Debugf("Called GetRepoLabelFn") log.Debugf("Called GetRepoLabelFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
id, ok := req.GetArguments()["id"].(float64) id, ok := req.GetArguments()["id"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("label ID is required")) return to.ErrorResult(errors.New("label ID is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -275,19 +274,19 @@ func CreateRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
log.Debugf("Called CreateRepoLabelFn") log.Debugf("Called CreateRepoLabelFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
name, ok := req.GetArguments()["name"].(string) name, ok := req.GetArguments()["name"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("name is required")) return to.ErrorResult(errors.New("name is required"))
} }
color, ok := req.GetArguments()["color"].(string) color, ok := req.GetArguments()["color"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("color is required")) return to.ErrorResult(errors.New("color is required"))
} }
description, _ := req.GetArguments()["description"].(string) // Optional description, _ := req.GetArguments()["description"].(string) // Optional
@@ -312,26 +311,26 @@ func EditRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
log.Debugf("Called EditRepoLabelFn") log.Debugf("Called EditRepoLabelFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
id, ok := req.GetArguments()["id"].(float64) id, ok := req.GetArguments()["id"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("label ID is required")) return to.ErrorResult(errors.New("label ID is required"))
} }
opt := gitea_sdk.EditLabelOption{} opt := gitea_sdk.EditLabelOption{}
if name, ok := req.GetArguments()["name"].(string); ok { if name, ok := req.GetArguments()["name"].(string); ok {
opt.Name = ptr.To(name) opt.Name = new(name)
} }
if color, ok := req.GetArguments()["color"].(string); ok { if color, ok := req.GetArguments()["color"].(string); ok {
opt.Color = ptr.To(color) opt.Color = new(color)
} }
if description, ok := req.GetArguments()["description"].(string); ok { if description, ok := req.GetArguments()["description"].(string); ok {
opt.Description = ptr.To(description) opt.Description = new(description)
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -349,15 +348,15 @@ func DeleteRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
log.Debugf("Called DeleteRepoLabelFn") log.Debugf("Called DeleteRepoLabelFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
id, ok := req.GetArguments()["id"].(float64) id, ok := req.GetArguments()["id"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("label ID is required")) return to.ErrorResult(errors.New("label ID is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -375,26 +374,26 @@ func AddIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
log.Debugf("Called AddIssueLabelsFn") log.Debugf("Called AddIssueLabelsFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
return to.ErrorResult(err) return to.ErrorResult(err)
} }
labelsRaw, ok := req.GetArguments()["labels"].([]interface{}) labelsRaw, ok := req.GetArguments()["labels"].([]any)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("labels (array of IDs) is required")) return to.ErrorResult(errors.New("labels (array of IDs) is required"))
} }
var labels []int64 var labels []int64
for _, l := range labelsRaw { for _, l := range labelsRaw {
if labelID, ok := l.(float64); ok { if labelID, ok := l.(float64); ok {
labels = append(labels, int64(labelID)) labels = append(labels, int64(labelID))
} else { } else {
return to.ErrorResult(fmt.Errorf("invalid label ID in labels array")) return to.ErrorResult(errors.New("invalid label ID in labels array"))
} }
} }
@@ -417,26 +416,26 @@ func ReplaceIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
log.Debugf("Called ReplaceIssueLabelsFn") log.Debugf("Called ReplaceIssueLabelsFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
return to.ErrorResult(err) return to.ErrorResult(err)
} }
labelsRaw, ok := req.GetArguments()["labels"].([]interface{}) labelsRaw, ok := req.GetArguments()["labels"].([]any)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("labels (array of IDs) is required")) return to.ErrorResult(errors.New("labels (array of IDs) is required"))
} }
var labels []int64 var labels []int64
for _, l := range labelsRaw { for _, l := range labelsRaw {
if labelID, ok := l.(float64); ok { if labelID, ok := l.(float64); ok {
labels = append(labels, int64(labelID)) labels = append(labels, int64(labelID))
} else { } else {
return to.ErrorResult(fmt.Errorf("invalid label ID in labels array")) return to.ErrorResult(errors.New("invalid label ID in labels array"))
} }
} }
@@ -459,11 +458,11 @@ func ClearIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
log.Debugf("Called ClearIssueLabelsFn") log.Debugf("Called ClearIssueLabelsFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -485,11 +484,11 @@ func RemoveIssueLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
log.Debugf("Called RemoveIssueLabelFn") log.Debugf("Called RemoveIssueLabelFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -497,7 +496,7 @@ func RemoveIssueLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
} }
labelID, ok := req.GetArguments()["label_id"].(float64) labelID, ok := req.GetArguments()["label_id"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("label ID is required")) return to.ErrorResult(errors.New("label ID is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -512,126 +511,126 @@ func RemoveIssueLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
} }
func ListOrgLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { func ListOrgLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called ListOrgLabelsFn") log.Debugf("Called ListOrgLabelsFn")
org, ok := req.GetArguments()["org"].(string) org, ok := req.GetArguments()["org"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("org is required")) return to.ErrorResult(errors.New("org is required"))
} }
page, ok := req.GetArguments()["page"].(float64) page, ok := req.GetArguments()["page"].(float64)
if !ok { if !ok {
page = 1 page = 1
} }
pageSize, ok := req.GetArguments()["pageSize"].(float64) pageSize, ok := req.GetArguments()["pageSize"].(float64)
if !ok { if !ok {
pageSize = 100 pageSize = 100
} }
opt := gitea_sdk.ListOrgLabelsOptions{ opt := gitea_sdk.ListOrgLabelsOptions{
ListOptions: gitea_sdk.ListOptions{ ListOptions: gitea_sdk.ListOptions{
Page: int(page), Page: int(page),
PageSize: int(pageSize), PageSize: int(pageSize),
}, },
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
if err != nil { if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
} }
labels, _, err := client.ListOrgLabels(org, opt) labels, _, err := client.ListOrgLabels(org, opt)
if err != nil { if err != nil {
return to.ErrorResult(fmt.Errorf("list %v/labels err: %v", org, err)) return to.ErrorResult(fmt.Errorf("list %v/labels err: %v", org, err))
} }
return to.TextResult(labels) return to.TextResult(labels)
} }
func CreateOrgLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { func CreateOrgLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called CreateOrgLabelFn") log.Debugf("Called CreateOrgLabelFn")
org, ok := req.GetArguments()["org"].(string) org, ok := req.GetArguments()["org"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("org is required")) return to.ErrorResult(errors.New("org is required"))
} }
name, ok := req.GetArguments()["name"].(string) name, ok := req.GetArguments()["name"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("name is required")) return to.ErrorResult(errors.New("name is required"))
} }
color, ok := req.GetArguments()["color"].(string) color, ok := req.GetArguments()["color"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("color is required")) return to.ErrorResult(errors.New("color is required"))
} }
description, _ := req.GetArguments()["description"].(string) description, _ := req.GetArguments()["description"].(string)
exclusive, _ := req.GetArguments()["exclusive"].(bool) exclusive, _ := req.GetArguments()["exclusive"].(bool)
opt := gitea_sdk.CreateOrgLabelOption{ opt := gitea_sdk.CreateOrgLabelOption{
Name: name, Name: name,
Color: color, Color: color,
Description: description, Description: description,
Exclusive: exclusive, Exclusive: exclusive,
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
if err != nil { if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
} }
label, _, err := client.CreateOrgLabel(org, opt) label, _, err := client.CreateOrgLabel(org, opt)
if err != nil { if err != nil {
return to.ErrorResult(fmt.Errorf("create %v/labels err: %v", org, err)) return to.ErrorResult(fmt.Errorf("create %v/labels err: %v", org, err))
} }
return to.TextResult(label) return to.TextResult(label)
} }
func EditOrgLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { func EditOrgLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called EditOrgLabelFn") log.Debugf("Called EditOrgLabelFn")
org, ok := req.GetArguments()["org"].(string) org, ok := req.GetArguments()["org"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("org is required")) return to.ErrorResult(errors.New("org is required"))
} }
id, ok := req.GetArguments()["id"].(float64) id, ok := req.GetArguments()["id"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("label ID is required")) return to.ErrorResult(errors.New("label ID is required"))
} }
opt := gitea_sdk.EditOrgLabelOption{} opt := gitea_sdk.EditOrgLabelOption{}
if name, ok := req.GetArguments()["name"].(string); ok { if name, ok := req.GetArguments()["name"].(string); ok {
opt.Name = ptr.To(name) opt.Name = new(name)
} }
if color, ok := req.GetArguments()["color"].(string); ok { if color, ok := req.GetArguments()["color"].(string); ok {
opt.Color = ptr.To(color) opt.Color = new(color)
} }
if description, ok := req.GetArguments()["description"].(string); ok { if description, ok := req.GetArguments()["description"].(string); ok {
opt.Description = ptr.To(description) opt.Description = new(description)
} }
if exclusive, ok := req.GetArguments()["exclusive"].(bool); ok { if exclusive, ok := req.GetArguments()["exclusive"].(bool); ok {
opt.Exclusive = ptr.To(exclusive) opt.Exclusive = new(exclusive)
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
if err != nil { if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
} }
label, _, err := client.EditOrgLabel(org, int64(id), opt) label, _, err := client.EditOrgLabel(org, int64(id), opt)
if err != nil { if err != nil {
return to.ErrorResult(fmt.Errorf("edit %v/labels/%v err: %v", org, int64(id), err)) return to.ErrorResult(fmt.Errorf("edit %v/labels/%v err: %v", org, int64(id), err))
} }
return to.TextResult(label) return to.TextResult(label)
} }
func DeleteOrgLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { func DeleteOrgLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called DeleteOrgLabelFn") log.Debugf("Called DeleteOrgLabelFn")
org, ok := req.GetArguments()["org"].(string) org, ok := req.GetArguments()["org"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("org is required")) return to.ErrorResult(errors.New("org is required"))
} }
id, ok := req.GetArguments()["id"].(float64) id, ok := req.GetArguments()["id"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("label ID is required")) return to.ErrorResult(errors.New("label ID is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
if err != nil { if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
} }
_, err = client.DeleteOrgLabel(org, int64(id)) _, err = client.DeleteOrgLabel(org, int64(id))
if err != nil { if err != nil {
return to.ErrorResult(fmt.Errorf("delete %v/labels/%v err: %v", org, int64(id), err)) return to.ErrorResult(fmt.Errorf("delete %v/labels/%v err: %v", org, int64(id), err))
} }
return to.TextResult("Label deleted successfully") return to.TextResult("Label deleted successfully")
} }

View File

@@ -2,11 +2,11 @@ package milestone
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log" "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/to"
"gitea.com/gitea/gitea-mcp/pkg/tool" "gitea.com/gitea/gitea-mcp/pkg/tool"
@@ -103,15 +103,15 @@ func GetMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
log.Debugf("Called GetMilestoneFn") log.Debugf("Called GetMilestoneFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
id, ok := req.GetArguments()["id"].(float64) id, ok := req.GetArguments()["id"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("id is required")) return to.ErrorResult(errors.New("id is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
if err != nil { if err != nil {
@@ -129,11 +129,11 @@ func ListMilestonesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
log.Debugf("Called ListMilestonesFn") log.Debugf("Called ListMilestonesFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
state, ok := req.GetArguments()["state"].(string) state, ok := req.GetArguments()["state"].(string)
if !ok { if !ok {
@@ -174,15 +174,15 @@ func CreateMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
log.Debugf("Called CreateMilestoneFn") log.Debugf("Called CreateMilestoneFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
title, ok := req.GetArguments()["title"].(string) title, ok := req.GetArguments()["title"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("title is required")) return to.ErrorResult(errors.New("title is required"))
} }
opt := gitea_sdk.CreateMilestoneOption{ opt := gitea_sdk.CreateMilestoneOption{
@@ -210,15 +210,15 @@ func EditMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
log.Debugf("Called EditMilestoneFn") log.Debugf("Called EditMilestoneFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
id, ok := req.GetArguments()["id"].(float64) id, ok := req.GetArguments()["id"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("id is required")) return to.ErrorResult(errors.New("id is required"))
} }
opt := gitea_sdk.EditMilestoneOption{} opt := gitea_sdk.EditMilestoneOption{}
@@ -229,11 +229,11 @@ func EditMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
} }
description, ok := req.GetArguments()["description"].(string) description, ok := req.GetArguments()["description"].(string)
if ok { if ok {
opt.Description = ptr.To(description) opt.Description = new(description)
} }
state, ok := req.GetArguments()["state"].(string) state, ok := req.GetArguments()["state"].(string)
if ok { if ok {
opt.State = ptr.To(gitea_sdk.StateType(state)) opt.State = new(gitea_sdk.StateType(state))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -252,15 +252,15 @@ func DeleteMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
log.Debugf("Called DeleteMilestoneFn") log.Debugf("Called DeleteMilestoneFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
id, ok := req.GetArguments()["id"].(float64) id, ok := req.GetArguments()["id"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("id is required")) return to.ErrorResult(errors.New("id is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
if err != nil { if err != nil {

View File

@@ -10,14 +10,14 @@ import (
"syscall" "syscall"
"time" "time"
"gitea.com/gitea/gitea-mcp/operation/actions"
"gitea.com/gitea/gitea-mcp/operation/issue" "gitea.com/gitea/gitea-mcp/operation/issue"
"gitea.com/gitea/gitea-mcp/operation/label" "gitea.com/gitea/gitea-mcp/operation/label"
"gitea.com/gitea/gitea-mcp/operation/milestone" "gitea.com/gitea/gitea-mcp/operation/milestone"
"gitea.com/gitea/gitea-mcp/operation/timetracking"
"gitea.com/gitea/gitea-mcp/operation/actions"
"gitea.com/gitea/gitea-mcp/operation/pull" "gitea.com/gitea/gitea-mcp/operation/pull"
"gitea.com/gitea/gitea-mcp/operation/repo" "gitea.com/gitea/gitea-mcp/operation/repo"
"gitea.com/gitea/gitea-mcp/operation/search" "gitea.com/gitea/gitea-mcp/operation/search"
"gitea.com/gitea/gitea-mcp/operation/timetracking"
"gitea.com/gitea/gitea-mcp/operation/user" "gitea.com/gitea/gitea-mcp/operation/user"
"gitea.com/gitea/gitea-mcp/operation/version" "gitea.com/gitea/gitea-mcp/operation/version"
"gitea.com/gitea/gitea-mcp/operation/wiki" "gitea.com/gitea/gitea-mcp/operation/wiki"

View File

@@ -2,12 +2,12 @@ package pull
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log" "gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params" "gitea.com/gitea/gitea-mcp/pkg/params"
"gitea.com/gitea/gitea-mcp/pkg/ptr"
"gitea.com/gitea/gitea-mcp/pkg/to" "gitea.com/gitea/gitea-mcp/pkg/to"
"gitea.com/gitea/gitea-mcp/pkg/tool" "gitea.com/gitea/gitea-mcp/pkg/tool"
@@ -83,8 +83,8 @@ var (
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithNumber("index", mcp.Required(), mcp.Description("pull request index")), mcp.WithNumber("index", mcp.Required(), mcp.Description("pull request index")),
mcp.WithArray("reviewers", mcp.Description("list of reviewer usernames"), mcp.Items(map[string]interface{}{"type": "string"})), mcp.WithArray("reviewers", mcp.Description("list of reviewer usernames"), mcp.Items(map[string]any{"type": "string"})),
mcp.WithArray("team_reviewers", mcp.Description("list of team reviewer names"), mcp.Items(map[string]interface{}{"type": "string"})), mcp.WithArray("team_reviewers", mcp.Description("list of team reviewer names"), mcp.Items(map[string]any{"type": "string"})),
) )
DeletePullRequestReviewerTool = mcp.NewTool( DeletePullRequestReviewerTool = mcp.NewTool(
@@ -93,8 +93,8 @@ var (
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithNumber("index", mcp.Required(), mcp.Description("pull request index")), mcp.WithNumber("index", mcp.Required(), mcp.Description("pull request index")),
mcp.WithArray("reviewers", mcp.Description("list of reviewer usernames to remove"), mcp.Items(map[string]interface{}{"type": "string"})), mcp.WithArray("reviewers", mcp.Description("list of reviewer usernames to remove"), mcp.Items(map[string]any{"type": "string"})),
mcp.WithArray("team_reviewers", mcp.Description("list of team reviewer names to remove"), mcp.Items(map[string]interface{}{"type": "string"})), mcp.WithArray("team_reviewers", mcp.Description("list of team reviewer names to remove"), mcp.Items(map[string]any{"type": "string"})),
) )
ListPullRequestReviewsTool = mcp.NewTool( ListPullRequestReviewsTool = mcp.NewTool(
@@ -134,13 +134,13 @@ var (
mcp.WithString("state", mcp.Description("review state"), mcp.Enum("APPROVED", "REQUEST_CHANGES", "COMMENT", "PENDING")), mcp.WithString("state", mcp.Description("review state"), mcp.Enum("APPROVED", "REQUEST_CHANGES", "COMMENT", "PENDING")),
mcp.WithString("body", mcp.Description("review body/comment")), mcp.WithString("body", mcp.Description("review body/comment")),
mcp.WithString("commit_id", mcp.Description("commit SHA to review")), mcp.WithString("commit_id", mcp.Description("commit SHA to review")),
mcp.WithArray("comments", mcp.Description("inline review comments (objects with path, body, old_line_num, new_line_num)"), mcp.Items(map[string]interface{}{ mcp.WithArray("comments", mcp.Description("inline review comments (objects with path, body, old_line_num, new_line_num)"), mcp.Items(map[string]any{
"type": "object", "type": "object",
"properties": map[string]interface{}{ "properties": map[string]any{
"path": map[string]interface{}{"type": "string", "description": "file path to comment on"}, "path": map[string]any{"type": "string", "description": "file path to comment on"},
"body": map[string]interface{}{"type": "string", "description": "comment body"}, "body": map[string]any{"type": "string", "description": "comment body"},
"old_line_num": map[string]interface{}{"type": "number", "description": "line number in the old file (for deletions/changes)"}, "old_line_num": map[string]any{"type": "number", "description": "line number in the old file (for deletions/changes)"},
"new_line_num": map[string]interface{}{"type": "number", "description": "line number in the new file (for additions/changes)"}, "new_line_num": map[string]any{"type": "number", "description": "line number in the new file (for additions/changes)"},
}, },
})), })),
) )
@@ -196,7 +196,7 @@ var (
mcp.WithString("body", mcp.Description("pull request body content")), mcp.WithString("body", mcp.Description("pull request body content")),
mcp.WithString("base", mcp.Description("pull request base branch")), mcp.WithString("base", mcp.Description("pull request base branch")),
mcp.WithString("assignee", mcp.Description("username to assign")), mcp.WithString("assignee", mcp.Description("username to assign")),
mcp.WithArray("assignees", mcp.Description("usernames to assign"), mcp.Items(map[string]interface{}{"type": "string"})), mcp.WithArray("assignees", mcp.Description("usernames to assign"), mcp.Items(map[string]any{"type": "string"})),
mcp.WithNumber("milestone", mcp.Description("milestone number")), mcp.WithNumber("milestone", mcp.Description("milestone number")),
mcp.WithString("state", mcp.Description("pull request state"), mcp.Enum("open", "closed")), mcp.WithString("state", mcp.Description("pull request state"), mcp.Enum("open", "closed")),
mcp.WithBoolean("allow_maintainer_edit", mcp.Description("allow maintainer to edit the pull request")), mcp.WithBoolean("allow_maintainer_edit", mcp.Description("allow maintainer to edit the pull request")),
@@ -270,11 +270,11 @@ func GetPullRequestByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
log.Debugf("Called GetPullRequestByIndexFn") log.Debugf("Called GetPullRequestByIndexFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -296,11 +296,11 @@ func GetPullRequestDiffFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
log.Debugf("Called GetPullRequestDiffFn") log.Debugf("Called GetPullRequestDiffFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -319,7 +319,7 @@ func GetPullRequestDiffFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
return to.ErrorResult(fmt.Errorf("get %v/%v/pr/%v diff err: %v", owner, repo, index, err)) return to.ErrorResult(fmt.Errorf("get %v/%v/pr/%v diff err: %v", owner, repo, index, err))
} }
result := map[string]interface{}{ result := map[string]any{
"diff": string(diffBytes), "diff": string(diffBytes),
"binary": binary, "binary": binary,
"index": index, "index": index,
@@ -333,11 +333,11 @@ func ListRepoPullRequestsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.
log.Debugf("Called ListRepoPullRequests") log.Debugf("Called ListRepoPullRequests")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
state, _ := req.GetArguments()["state"].(string) state, _ := req.GetArguments()["state"].(string)
sort, ok := req.GetArguments()["sort"].(string) sort, ok := req.GetArguments()["sort"].(string)
@@ -378,27 +378,27 @@ func CreatePullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
log.Debugf("Called CreatePullRequestFn") log.Debugf("Called CreatePullRequestFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
title, ok := req.GetArguments()["title"].(string) title, ok := req.GetArguments()["title"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("title is required")) return to.ErrorResult(errors.New("title is required"))
} }
body, ok := req.GetArguments()["body"].(string) body, ok := req.GetArguments()["body"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("body is required")) return to.ErrorResult(errors.New("body is required"))
} }
head, ok := req.GetArguments()["head"].(string) head, ok := req.GetArguments()["head"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("head is required")) return to.ErrorResult(errors.New("head is required"))
} }
base, ok := req.GetArguments()["base"].(string) base, ok := req.GetArguments()["base"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("base is required")) return to.ErrorResult(errors.New("base is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
if err != nil { if err != nil {
@@ -421,11 +421,11 @@ func CreatePullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (
log.Debugf("Called CreatePullRequestReviewerFn") log.Debugf("Called CreatePullRequestReviewerFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -434,7 +434,7 @@ func CreatePullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (
var reviewers []string var reviewers []string
if reviewersArg, exists := req.GetArguments()["reviewers"]; exists { if reviewersArg, exists := req.GetArguments()["reviewers"]; exists {
if reviewersSlice, ok := reviewersArg.([]interface{}); ok { if reviewersSlice, ok := reviewersArg.([]any); ok {
for _, reviewer := range reviewersSlice { for _, reviewer := range reviewersSlice {
if reviewerStr, ok := reviewer.(string); ok { if reviewerStr, ok := reviewer.(string); ok {
reviewers = append(reviewers, reviewerStr) reviewers = append(reviewers, reviewerStr)
@@ -445,7 +445,7 @@ func CreatePullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (
var teamReviewers []string var teamReviewers []string
if teamReviewersArg, exists := req.GetArguments()["team_reviewers"]; exists { if teamReviewersArg, exists := req.GetArguments()["team_reviewers"]; exists {
if teamReviewersSlice, ok := teamReviewersArg.([]interface{}); ok { if teamReviewersSlice, ok := teamReviewersArg.([]any); ok {
for _, teamReviewer := range teamReviewersSlice { for _, teamReviewer := range teamReviewersSlice {
if teamReviewerStr, ok := teamReviewer.(string); ok { if teamReviewerStr, ok := teamReviewer.(string); ok {
teamReviewers = append(teamReviewers, teamReviewerStr) teamReviewers = append(teamReviewers, teamReviewerStr)
@@ -468,7 +468,7 @@ func CreatePullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (
} }
// Return a success message instead of the Response object which contains non-serializable functions // Return a success message instead of the Response object which contains non-serializable functions
successMsg := map[string]interface{}{ successMsg := map[string]any{
"message": "Successfully created review requests", "message": "Successfully created review requests",
"reviewers": reviewers, "reviewers": reviewers,
"team_reviewers": teamReviewers, "team_reviewers": teamReviewers,
@@ -483,11 +483,11 @@ func DeletePullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (
log.Debugf("Called DeletePullRequestReviewerFn") log.Debugf("Called DeletePullRequestReviewerFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -496,7 +496,7 @@ func DeletePullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (
var reviewers []string var reviewers []string
if reviewersArg, exists := req.GetArguments()["reviewers"]; exists { if reviewersArg, exists := req.GetArguments()["reviewers"]; exists {
if reviewersSlice, ok := reviewersArg.([]interface{}); ok { if reviewersSlice, ok := reviewersArg.([]any); ok {
for _, reviewer := range reviewersSlice { for _, reviewer := range reviewersSlice {
if reviewerStr, ok := reviewer.(string); ok { if reviewerStr, ok := reviewer.(string); ok {
reviewers = append(reviewers, reviewerStr) reviewers = append(reviewers, reviewerStr)
@@ -507,7 +507,7 @@ func DeletePullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (
var teamReviewers []string var teamReviewers []string
if teamReviewersArg, exists := req.GetArguments()["team_reviewers"]; exists { if teamReviewersArg, exists := req.GetArguments()["team_reviewers"]; exists {
if teamReviewersSlice, ok := teamReviewersArg.([]interface{}); ok { if teamReviewersSlice, ok := teamReviewersArg.([]any); ok {
for _, teamReviewer := range teamReviewersSlice { for _, teamReviewer := range teamReviewersSlice {
if teamReviewerStr, ok := teamReviewer.(string); ok { if teamReviewerStr, ok := teamReviewer.(string); ok {
teamReviewers = append(teamReviewers, teamReviewerStr) teamReviewers = append(teamReviewers, teamReviewerStr)
@@ -529,7 +529,7 @@ func DeletePullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (
return to.ErrorResult(fmt.Errorf("delete review requests for %v/%v/pr/%v err: %v", owner, repo, index, err)) return to.ErrorResult(fmt.Errorf("delete review requests for %v/%v/pr/%v err: %v", owner, repo, index, err))
} }
successMsg := map[string]interface{}{ successMsg := map[string]any{
"message": "Successfully deleted review requests", "message": "Successfully deleted review requests",
"reviewers": reviewers, "reviewers": reviewers,
"team_reviewers": teamReviewers, "team_reviewers": teamReviewers,
@@ -544,11 +544,11 @@ func ListPullRequestReviewsFn(ctx context.Context, req mcp.CallToolRequest) (*mc
log.Debugf("Called ListPullRequestReviewsFn") log.Debugf("Called ListPullRequestReviewsFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -585,11 +585,11 @@ func GetPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.
log.Debugf("Called GetPullRequestReviewFn") log.Debugf("Called GetPullRequestReviewFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -597,7 +597,7 @@ func GetPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.
} }
reviewID, ok := req.GetArguments()["review_id"].(float64) reviewID, ok := req.GetArguments()["review_id"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("review_id is required")) return to.ErrorResult(errors.New("review_id is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -617,11 +617,11 @@ func ListPullRequestReviewCommentsFn(ctx context.Context, req mcp.CallToolReques
log.Debugf("Called ListPullRequestReviewCommentsFn") log.Debugf("Called ListPullRequestReviewCommentsFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -629,7 +629,7 @@ func ListPullRequestReviewCommentsFn(ctx context.Context, req mcp.CallToolReques
} }
reviewID, ok := req.GetArguments()["review_id"].(float64) reviewID, ok := req.GetArguments()["review_id"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("review_id is required")) return to.ErrorResult(errors.New("review_id is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -649,11 +649,11 @@ func CreatePullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
log.Debugf("Called CreatePullRequestReviewFn") log.Debugf("Called CreatePullRequestReviewFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -674,9 +674,9 @@ func CreatePullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
// Parse inline comments // Parse inline comments
if commentsArg, exists := req.GetArguments()["comments"]; exists { if commentsArg, exists := req.GetArguments()["comments"]; exists {
if commentsSlice, ok := commentsArg.([]interface{}); ok { if commentsSlice, ok := commentsArg.([]any); ok {
for _, comment := range commentsSlice { for _, comment := range commentsSlice {
if commentMap, ok := comment.(map[string]interface{}); ok { if commentMap, ok := comment.(map[string]any); ok {
reviewComment := gitea_sdk.CreatePullReviewComment{} reviewComment := gitea_sdk.CreatePullReviewComment{}
if path, ok := commentMap["path"].(string); ok { if path, ok := commentMap["path"].(string); ok {
reviewComment.Path = path reviewComment.Path = path
@@ -713,11 +713,11 @@ func SubmitPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
log.Debugf("Called SubmitPullRequestReviewFn") log.Debugf("Called SubmitPullRequestReviewFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -725,11 +725,11 @@ func SubmitPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
} }
reviewID, ok := req.GetArguments()["review_id"].(float64) reviewID, ok := req.GetArguments()["review_id"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("review_id is required")) return to.ErrorResult(errors.New("review_id is required"))
} }
state, ok := req.GetArguments()["state"].(string) state, ok := req.GetArguments()["state"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("state is required")) return to.ErrorResult(errors.New("state is required"))
} }
opt := gitea_sdk.SubmitPullReviewOptions{ opt := gitea_sdk.SubmitPullReviewOptions{
@@ -756,11 +756,11 @@ func DeletePullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
log.Debugf("Called DeletePullRequestReviewFn") log.Debugf("Called DeletePullRequestReviewFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -768,7 +768,7 @@ func DeletePullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
} }
reviewID, ok := req.GetArguments()["review_id"].(float64) reviewID, ok := req.GetArguments()["review_id"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("review_id is required")) return to.ErrorResult(errors.New("review_id is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -781,7 +781,7 @@ func DeletePullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
return to.ErrorResult(fmt.Errorf("delete review %v for %v/%v/pr/%v err: %v", int64(reviewID), owner, repo, index, err)) return to.ErrorResult(fmt.Errorf("delete review %v for %v/%v/pr/%v err: %v", int64(reviewID), owner, repo, index, err))
} }
successMsg := map[string]interface{}{ successMsg := map[string]any{
"message": "Successfully deleted review", "message": "Successfully deleted review",
"review_id": int64(reviewID), "review_id": int64(reviewID),
"pr_index": index, "pr_index": index,
@@ -795,11 +795,11 @@ func DismissPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*
log.Debugf("Called DismissPullRequestReviewFn") log.Debugf("Called DismissPullRequestReviewFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -807,7 +807,7 @@ func DismissPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*
} }
reviewID, ok := req.GetArguments()["review_id"].(float64) reviewID, ok := req.GetArguments()["review_id"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("review_id is required")) return to.ErrorResult(errors.New("review_id is required"))
} }
opt := gitea_sdk.DismissPullReviewOptions{} opt := gitea_sdk.DismissPullReviewOptions{}
@@ -825,7 +825,7 @@ func DismissPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*
return to.ErrorResult(fmt.Errorf("dismiss review %v for %v/%v/pr/%v err: %v", int64(reviewID), owner, repo, index, err)) return to.ErrorResult(fmt.Errorf("dismiss review %v for %v/%v/pr/%v err: %v", int64(reviewID), owner, repo, index, err))
} }
successMsg := map[string]interface{}{ successMsg := map[string]any{
"message": "Successfully dismissed review", "message": "Successfully dismissed review",
"review_id": int64(reviewID), "review_id": int64(reviewID),
"pr_index": index, "pr_index": index,
@@ -839,15 +839,15 @@ func MergePullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
log.Debugf("Called MergePullRequestFn") log.Debugf("Called MergePullRequestFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, ok := req.GetArguments()["index"].(float64) index, ok := req.GetArguments()["index"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("index is required")) return to.ErrorResult(errors.New("index is required"))
} }
mergeStyle := "merge" mergeStyle := "merge"
@@ -889,7 +889,7 @@ func MergePullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
return to.ErrorResult(fmt.Errorf("merge %v/%v/pr/%v returned merged=false", owner, repo, int64(index))) return to.ErrorResult(fmt.Errorf("merge %v/%v/pr/%v returned merged=false", owner, repo, int64(index)))
} }
successMsg := map[string]interface{}{ successMsg := map[string]any{
"merged": merged, "merged": merged,
"pr_index": int64(index), "pr_index": int64(index),
"repository": fmt.Sprintf("%s/%s", owner, repo), "repository": fmt.Sprintf("%s/%s", owner, repo),
@@ -904,15 +904,15 @@ func EditPullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
log.Debugf("Called EditPullRequestFn") log.Debugf("Called EditPullRequestFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, ok := req.GetArguments()["index"].(float64) index, ok := req.GetArguments()["index"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("index is required")) return to.ErrorResult(errors.New("index is required"))
} }
opt := gitea_sdk.EditPullRequestOption{} opt := gitea_sdk.EditPullRequestOption{}
@@ -921,7 +921,7 @@ func EditPullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
opt.Title = title opt.Title = title
} }
if body, ok := req.GetArguments()["body"].(string); ok { if body, ok := req.GetArguments()["body"].(string); ok {
opt.Body = ptr.To(body) opt.Body = new(body)
} }
if base, ok := req.GetArguments()["base"].(string); ok { if base, ok := req.GetArguments()["base"].(string); ok {
opt.Base = base opt.Base = base
@@ -930,7 +930,7 @@ func EditPullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
opt.Assignee = assignee opt.Assignee = assignee
} }
if assigneesArg, exists := req.GetArguments()["assignees"]; exists { if assigneesArg, exists := req.GetArguments()["assignees"]; exists {
if assigneesSlice, ok := assigneesArg.([]interface{}); ok { if assigneesSlice, ok := assigneesArg.([]any); ok {
var assignees []string var assignees []string
for _, a := range assigneesSlice { for _, a := range assigneesSlice {
if s, ok := a.(string); ok { if s, ok := a.(string); ok {
@@ -944,10 +944,10 @@ func EditPullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
opt.Milestone = int64(milestone) opt.Milestone = int64(milestone)
} }
if state, ok := req.GetArguments()["state"].(string); ok { if state, ok := req.GetArguments()["state"].(string); ok {
opt.State = ptr.To(gitea_sdk.StateType(state)) opt.State = new(gitea_sdk.StateType(state))
} }
if allowMaintainerEdit, ok := req.GetArguments()["allow_maintainer_edit"].(bool); ok { if allowMaintainerEdit, ok := req.GetArguments()["allow_maintainer_edit"].(bool); ok {
opt.AllowMaintainerEdit = ptr.To(allowMaintainerEdit) opt.AllowMaintainerEdit = new(allowMaintainerEdit)
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)

View File

@@ -21,10 +21,10 @@ func TestEditPullRequestFn(t *testing.T) {
) )
var ( var (
mu sync.Mutex mu sync.Mutex
gotMethod string gotMethod string
gotPath string gotPath string
gotBody map[string]any gotBody map[string]any
) )
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -44,7 +44,7 @@ func TestEditPullRequestFn(t *testing.T) {
gotBody = body gotBody = body
mu.Unlock() mu.Unlock()
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(fmt.Sprintf(`{"number":%d,"title":"%s","state":"open"}`, index, body["title"]))) _, _ = w.Write(fmt.Appendf(nil, `{"number":%d,"title":"%s","state":"open"}`, index, body["title"]))
default: default:
http.NotFound(w, r) http.NotFound(w, r)
} }

View File

@@ -2,6 +2,7 @@ package repo
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea"
@@ -64,15 +65,15 @@ func CreateBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
log.Debugf("Called CreateBranchFn") log.Debugf("Called CreateBranchFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
branch, ok := req.GetArguments()["branch"].(string) branch, ok := req.GetArguments()["branch"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("branch is required")) return to.ErrorResult(errors.New("branch is required"))
} }
oldBranch, _ := req.GetArguments()["old_branch"].(string) oldBranch, _ := req.GetArguments()["old_branch"].(string)
@@ -95,15 +96,15 @@ func DeleteBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
log.Debugf("Called DeleteBranchFn") log.Debugf("Called DeleteBranchFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
branch, ok := req.GetArguments()["branch"].(string) branch, ok := req.GetArguments()["branch"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("branch is required")) return to.ErrorResult(errors.New("branch is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
if err != nil { if err != nil {
@@ -121,11 +122,11 @@ func ListBranchesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
log.Debugf("Called ListBranchesFn") log.Debugf("Called ListBranchesFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
opt := gitea_sdk.ListRepoBranchesOptions{ opt := gitea_sdk.ListRepoBranchesOptions{
ListOptions: gitea_sdk.ListOptions{ ListOptions: gitea_sdk.ListOptions{

View File

@@ -2,6 +2,7 @@ package repo
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea"
@@ -39,19 +40,19 @@ func ListRepoCommitsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
log.Debugf("Called ListRepoCommitsFn") log.Debugf("Called ListRepoCommitsFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
page, ok := req.GetArguments()["page"].(float64) page, ok := req.GetArguments()["page"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("page is required")) return to.ErrorResult(errors.New("page is required"))
} }
pageSize, ok := req.GetArguments()["page_size"].(float64) pageSize, ok := req.GetArguments()["page_size"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("page_size is required")) return to.ErrorResult(errors.New("page_size is required"))
} }
sha, _ := req.GetArguments()["sha"].(string) sha, _ := req.GetArguments()["sha"].(string)
path, _ := req.GetArguments()["path"].(string) path, _ := req.GetArguments()["path"].(string)

View File

@@ -6,6 +6,7 @@ import (
"context" "context"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea"
@@ -113,16 +114,16 @@ func GetFileContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
log.Debugf("Called GetFileFn") log.Debugf("Called GetFileFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
ref, _ := req.GetArguments()["ref"].(string) ref, _ := req.GetArguments()["ref"].(string)
filePath, ok := req.GetArguments()["filePath"].(string) filePath, ok := req.GetArguments()["filePath"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("filePath is required")) return to.ErrorResult(errors.New("filePath is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
if err != nil { if err != nil {
@@ -151,7 +152,6 @@ func GetFileContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
LineNumber: line, LineNumber: line,
Content: scanner.Text(), Content: scanner.Text(),
}) })
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
return to.ErrorResult(fmt.Errorf("scan content err: %v", err)) return to.ErrorResult(fmt.Errorf("scan content err: %v", err))
@@ -177,16 +177,16 @@ func GetDirContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
log.Debugf("Called GetDirContentFn") log.Debugf("Called GetDirContentFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
ref, _ := req.GetArguments()["ref"].(string) ref, _ := req.GetArguments()["ref"].(string)
filePath, ok := req.GetArguments()["filePath"].(string) filePath, ok := req.GetArguments()["filePath"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("filePath is required")) return to.ErrorResult(errors.New("filePath is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
if err != nil { if err != nil {
@@ -203,15 +203,15 @@ func CreateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
log.Debugf("Called CreateFileFn") log.Debugf("Called CreateFileFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
filePath, ok := req.GetArguments()["filePath"].(string) filePath, ok := req.GetArguments()["filePath"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("filePath is required")) return to.ErrorResult(errors.New("filePath is required"))
} }
content, _ := req.GetArguments()["content"].(string) content, _ := req.GetArguments()["content"].(string)
message, _ := req.GetArguments()["message"].(string) message, _ := req.GetArguments()["message"].(string)
@@ -239,19 +239,19 @@ func UpdateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
log.Debugf("Called UpdateFileFn") log.Debugf("Called UpdateFileFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
filePath, ok := req.GetArguments()["filePath"].(string) filePath, ok := req.GetArguments()["filePath"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("filePath is required")) return to.ErrorResult(errors.New("filePath is required"))
} }
sha, ok := req.GetArguments()["sha"].(string) sha, ok := req.GetArguments()["sha"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("sha is required")) return to.ErrorResult(errors.New("sha is required"))
} }
content, _ := req.GetArguments()["content"].(string) content, _ := req.GetArguments()["content"].(string)
message, _ := req.GetArguments()["message"].(string) message, _ := req.GetArguments()["message"].(string)
@@ -280,21 +280,21 @@ func DeleteFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
log.Debugf("Called DeleteFileFn") log.Debugf("Called DeleteFileFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
filePath, ok := req.GetArguments()["filePath"].(string) filePath, ok := req.GetArguments()["filePath"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("filePath is required")) return to.ErrorResult(errors.New("filePath is required"))
} }
message, _ := req.GetArguments()["message"].(string) message, _ := req.GetArguments()["message"].(string)
branchName, _ := req.GetArguments()["branch_name"].(string) branchName, _ := req.GetArguments()["branch_name"].(string)
sha, ok := req.GetArguments()["sha"].(string) sha, ok := req.GetArguments()["sha"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("sha is required")) return to.ErrorResult(errors.New("sha is required"))
} }
opt := gitea_sdk.DeleteFileOptions{ opt := gitea_sdk.DeleteFileOptions{
FileOptions: gitea_sdk.FileOptions{ FileOptions: gitea_sdk.FileOptions{

View File

@@ -2,12 +2,12 @@ package repo
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"time" "time"
"gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log" "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/to"
gitea_sdk "code.gitea.io/sdk/gitea" gitea_sdk "code.gitea.io/sdk/gitea"
@@ -112,23 +112,23 @@ func CreateReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
log.Debugf("Called CreateReleasesFn") log.Debugf("Called CreateReleasesFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return nil, errors.New("owner is required")
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return nil, errors.New("repo is required")
} }
tagName, ok := req.GetArguments()["tag_name"].(string) tagName, ok := req.GetArguments()["tag_name"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("tag_name is required") return nil, errors.New("tag_name is required")
} }
target, ok := req.GetArguments()["target"].(string) target, ok := req.GetArguments()["target"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("target is required") return nil, errors.New("target is required")
} }
title, ok := req.GetArguments()["title"].(string) title, ok := req.GetArguments()["title"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("title is required") return nil, errors.New("title is required")
} }
isDraft, _ := req.GetArguments()["is_draft"].(bool) isDraft, _ := req.GetArguments()["is_draft"].(bool)
isPreRelease, _ := req.GetArguments()["is_pre_release"].(bool) isPreRelease, _ := req.GetArguments()["is_pre_release"].(bool)
@@ -157,15 +157,15 @@ func DeleteReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
log.Debugf("Called DeleteReleaseFn") log.Debugf("Called DeleteReleaseFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return nil, errors.New("owner is required")
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return nil, errors.New("repo is required")
} }
id, ok := req.GetArguments()["id"].(float64) id, ok := req.GetArguments()["id"].(float64)
if !ok { if !ok {
return nil, fmt.Errorf("id is required") return nil, errors.New("id is required")
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -184,15 +184,15 @@ func GetReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
log.Debugf("Called GetReleaseFn") log.Debugf("Called GetReleaseFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return nil, errors.New("owner is required")
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return nil, errors.New("repo is required")
} }
id, ok := req.GetArguments()["id"].(float64) id, ok := req.GetArguments()["id"].(float64)
if !ok { if !ok {
return nil, fmt.Errorf("id is required") return nil, errors.New("id is required")
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -211,11 +211,11 @@ func GetLatestReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
log.Debugf("Called GetLatestReleaseFn") log.Debugf("Called GetLatestReleaseFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return nil, errors.New("owner is required")
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return nil, errors.New("repo is required")
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -234,21 +234,21 @@ func ListReleasesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
log.Debugf("Called ListReleasesFn") log.Debugf("Called ListReleasesFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return nil, errors.New("owner is required")
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return nil, errors.New("repo is required")
} }
var pIsDraft *bool var pIsDraft *bool
isDraft, ok := req.GetArguments()["is_draft"].(bool) isDraft, ok := req.GetArguments()["is_draft"].(bool)
if ok { if ok {
pIsDraft = ptr.To(isDraft) pIsDraft = new(isDraft)
} }
var pIsPreRelease *bool var pIsPreRelease *bool
isPreRelease, ok := req.GetArguments()["is_pre_release"].(bool) isPreRelease, ok := req.GetArguments()["is_pre_release"].(bool)
if ok { if ok {
pIsPreRelease = ptr.To(isPreRelease) pIsPreRelease = new(isPreRelease)
} }
page, _ := req.GetArguments()["page"].(float64) page, _ := req.GetArguments()["page"].(float64)
pageSize, _ := req.GetArguments()["pageSize"].(float64) pageSize, _ := req.GetArguments()["pageSize"].(float64)

View File

@@ -7,7 +7,6 @@ import (
"gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log" "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/to"
"gitea.com/gitea/gitea-mcp/pkg/tool" "gitea.com/gitea/gitea-mcp/pkg/tool"
@@ -166,12 +165,12 @@ func ForkRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResu
return to.ErrorResult(errors.New("repository name is required")) return to.ErrorResult(errors.New("repository name is required"))
} }
organization, ok := req.GetArguments()["organization"].(string) organization, ok := req.GetArguments()["organization"].(string)
organizationPtr := ptr.To(organization) organizationPtr := new(organization)
if !ok || organization == "" { if !ok || organization == "" {
organizationPtr = nil organizationPtr = nil
} }
name, ok := req.GetArguments()["name"].(string) name, ok := req.GetArguments()["name"].(string)
namePtr := ptr.To(name) namePtr := new(name)
if !ok || name == "" { if !ok || name == "" {
namePtr = nil namePtr = nil
} }

View File

@@ -2,6 +2,7 @@ package repo
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea"
@@ -89,15 +90,15 @@ func CreateTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
log.Debugf("Called CreateTagFn") log.Debugf("Called CreateTagFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return nil, errors.New("owner is required")
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return nil, errors.New("repo is required")
} }
tagName, ok := req.GetArguments()["tag_name"].(string) tagName, ok := req.GetArguments()["tag_name"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("tag_name is required") return nil, errors.New("tag_name is required")
} }
target, _ := req.GetArguments()["target"].(string) target, _ := req.GetArguments()["target"].(string)
message, _ := req.GetArguments()["message"].(string) message, _ := req.GetArguments()["message"].(string)
@@ -122,15 +123,15 @@ func DeleteTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
log.Debugf("Called DeleteTagFn") log.Debugf("Called DeleteTagFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return nil, errors.New("owner is required")
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return nil, errors.New("repo is required")
} }
tagName, ok := req.GetArguments()["tag_name"].(string) tagName, ok := req.GetArguments()["tag_name"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("tag_name is required") return nil, errors.New("tag_name is required")
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -149,15 +150,15 @@ func GetTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult
log.Debugf("Called GetTagFn") log.Debugf("Called GetTagFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return nil, errors.New("owner is required")
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return nil, errors.New("repo is required")
} }
tagName, ok := req.GetArguments()["tag_name"].(string) tagName, ok := req.GetArguments()["tag_name"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("tag_name is required") return nil, errors.New("tag_name is required")
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
@@ -176,11 +177,11 @@ func ListTagsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResu
log.Debugf("Called ListTagsFn") log.Debugf("Called ListTagsFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("owner is required") return nil, errors.New("owner is required")
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("repo is required") return nil, errors.New("repo is required")
} }
page, _ := req.GetArguments()["page"].(float64) page, _ := req.GetArguments()["page"].(float64)
pageSize, _ := req.GetArguments()["pageSize"].(float64) pageSize, _ := req.GetArguments()["pageSize"].(float64)

View File

@@ -2,11 +2,11 @@ package search
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log" "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/to"
"gitea.com/gitea/gitea-mcp/pkg/tool" "gitea.com/gitea/gitea-mcp/pkg/tool"
@@ -61,23 +61,23 @@ var (
func init() { func init() {
Tool.RegisterRead(server.ServerTool{ Tool.RegisterRead(server.ServerTool{
Tool: SearchUsersTool, Tool: SearchUsersTool,
Handler: SearchUsersFn, Handler: UsersFn,
}) })
Tool.RegisterRead(server.ServerTool{ Tool.RegisterRead(server.ServerTool{
Tool: SearOrgTeamsTool, Tool: SearOrgTeamsTool,
Handler: SearchOrgTeamsFn, Handler: OrgTeamsFn,
}) })
Tool.RegisterRead(server.ServerTool{ Tool.RegisterRead(server.ServerTool{
Tool: SearchReposTool, Tool: SearchReposTool,
Handler: SearchReposFn, Handler: ReposFn,
}) })
} }
func SearchUsersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { func UsersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called SearchUsersFn") log.Debugf("Called UsersFn")
keyword, ok := req.GetArguments()["keyword"].(string) keyword, ok := req.GetArguments()["keyword"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("keyword is required")) return to.ErrorResult(errors.New("keyword is required"))
} }
page, ok := req.GetArguments()["page"].(float64) page, ok := req.GetArguments()["page"].(float64)
if !ok { if !ok {
@@ -105,15 +105,15 @@ func SearchUsersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
return to.TextResult(users) return to.TextResult(users)
} }
func SearchOrgTeamsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { func OrgTeamsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called SearchOrgTeamsFn") log.Debugf("Called OrgTeamsFn")
org, ok := req.GetArguments()["org"].(string) org, ok := req.GetArguments()["org"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("organization is required")) return to.ErrorResult(errors.New("organization is required"))
} }
query, ok := req.GetArguments()["query"].(string) query, ok := req.GetArguments()["query"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("query is required")) return to.ErrorResult(errors.New("query is required"))
} }
includeDescription, _ := req.GetArguments()["includeDescription"].(bool) includeDescription, _ := req.GetArguments()["includeDescription"].(bool)
page, ok := req.GetArguments()["page"].(float64) page, ok := req.GetArguments()["page"].(float64)
@@ -143,11 +143,11 @@ func SearchOrgTeamsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
return to.TextResult(teams) return to.TextResult(teams)
} }
func SearchReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { func ReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called SearchReposFn") log.Debugf("Called ReposFn")
keyword, ok := req.GetArguments()["keyword"].(string) keyword, ok := req.GetArguments()["keyword"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("keyword is required")) return to.ErrorResult(errors.New("keyword is required"))
} }
keywordIsTopic, _ := req.GetArguments()["keywordIsTopic"].(bool) keywordIsTopic, _ := req.GetArguments()["keywordIsTopic"].(bool)
keywordInDescription, _ := req.GetArguments()["keywordInDescription"].(bool) keywordInDescription, _ := req.GetArguments()["keywordInDescription"].(bool)
@@ -155,12 +155,12 @@ func SearchReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
var pIsPrivate *bool var pIsPrivate *bool
isPrivate, ok := req.GetArguments()["isPrivate"].(bool) isPrivate, ok := req.GetArguments()["isPrivate"].(bool)
if ok { if ok {
pIsPrivate = ptr.To(isPrivate) pIsPrivate = new(isPrivate)
} }
var pIsArchived *bool var pIsArchived *bool
isArchived, ok := req.GetArguments()["isArchived"].(bool) isArchived, ok := req.GetArguments()["isArchived"].(bool)
if ok { if ok {
pIsArchived = ptr.To(isArchived) pIsArchived = new(isArchived)
} }
sort, _ := req.GetArguments()["sort"].(string) sort, _ := req.GetArguments()["sort"].(string)
order, _ := req.GetArguments()["order"].(string) order, _ := req.GetArguments()["order"].(string)

View File

@@ -3,6 +3,7 @@ package timetracking
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
gitea_sdk "code.gitea.io/sdk/gitea" gitea_sdk "code.gitea.io/sdk/gitea"
@@ -129,11 +130,11 @@ func StartStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
log.Debugf("Called StartStopwatchFn") log.Debugf("Called StartStopwatchFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -154,11 +155,11 @@ func StopStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
log.Debugf("Called StopStopwatchFn") log.Debugf("Called StopStopwatchFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -179,11 +180,11 @@ func DeleteStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
log.Debugf("Called DeleteStopwatchFn") log.Debugf("Called DeleteStopwatchFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -222,11 +223,11 @@ func ListTrackedTimesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
log.Debugf("Called ListTrackedTimesFn") log.Debugf("Called ListTrackedTimesFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -264,11 +265,11 @@ func AddTrackedTimeFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
log.Debugf("Called AddTrackedTimeFn") log.Debugf("Called AddTrackedTimeFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
if err != nil { if err != nil {
@@ -277,7 +278,7 @@ func AddTrackedTimeFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
timeSeconds, ok := req.GetArguments()["time"].(float64) timeSeconds, ok := req.GetArguments()["time"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("time is required")) return to.ErrorResult(errors.New("time is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
if err != nil { if err != nil {
@@ -296,11 +297,11 @@ func DeleteTrackedTimeFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
log.Debugf("Called DeleteTrackedTimeFn") log.Debugf("Called DeleteTrackedTimeFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
index, err := params.GetIndex(req.GetArguments(), "index") index, err := params.GetIndex(req.GetArguments(), "index")
@@ -309,7 +310,7 @@ func DeleteTrackedTimeFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
} }
id, ok := req.GetArguments()["id"].(float64) id, ok := req.GetArguments()["id"].(float64)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("id is required")) return to.ErrorResult(errors.New("id is required"))
} }
client, err := gitea.ClientFromContext(ctx) client, err := gitea.ClientFromContext(ctx)
if err != nil { if err != nil {
@@ -326,11 +327,11 @@ func ListRepoTimesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
log.Debugf("Called ListRepoTimesFn") log.Debugf("Called ListRepoTimesFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
page, ok := req.GetArguments()["page"].(float64) page, ok := req.GetArguments()["page"].(float64)

View File

@@ -2,6 +2,7 @@ package wiki
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"net/url" "net/url"
@@ -110,11 +111,11 @@ func ListWikiPagesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
log.Debugf("Called ListWikiPagesFn") log.Debugf("Called ListWikiPagesFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
// Use direct HTTP request because SDK does not support yet wikis // Use direct HTTP request because SDK does not support yet wikis
@@ -131,15 +132,15 @@ func GetWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
log.Debugf("Called GetWikiPageFn") log.Debugf("Called GetWikiPageFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
pageName, ok := req.GetArguments()["pageName"].(string) pageName, ok := req.GetArguments()["pageName"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("pageName is required")) return to.ErrorResult(errors.New("pageName is required"))
} }
var result any var result any
@@ -155,15 +156,15 @@ func GetWikiRevisionsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
log.Debugf("Called GetWikiRevisionsFn") log.Debugf("Called GetWikiRevisionsFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
pageName, ok := req.GetArguments()["pageName"].(string) pageName, ok := req.GetArguments()["pageName"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("pageName is required")) return to.ErrorResult(errors.New("pageName is required"))
} }
var result any var result any
@@ -179,19 +180,19 @@ func CreateWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
log.Debugf("Called CreateWikiPageFn") log.Debugf("Called CreateWikiPageFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
title, ok := req.GetArguments()["title"].(string) title, ok := req.GetArguments()["title"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("title is required")) return to.ErrorResult(errors.New("title is required"))
} }
contentBase64, ok := req.GetArguments()["content_base64"].(string) contentBase64, ok := req.GetArguments()["content_base64"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("content_base64 is required")) return to.ErrorResult(errors.New("content_base64 is required"))
} }
message, _ := req.GetArguments()["message"].(string) message, _ := req.GetArguments()["message"].(string)
@@ -218,19 +219,19 @@ func UpdateWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
log.Debugf("Called UpdateWikiPageFn") log.Debugf("Called UpdateWikiPageFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
pageName, ok := req.GetArguments()["pageName"].(string) pageName, ok := req.GetArguments()["pageName"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("pageName is required")) return to.ErrorResult(errors.New("pageName is required"))
} }
contentBase64, ok := req.GetArguments()["content_base64"].(string) contentBase64, ok := req.GetArguments()["content_base64"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("content_base64 is required")) return to.ErrorResult(errors.New("content_base64 is required"))
} }
requestBody := map[string]string{ requestBody := map[string]string{
@@ -264,15 +265,15 @@ func DeleteWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
log.Debugf("Called DeleteWikiPageFn") log.Debugf("Called DeleteWikiPageFn")
owner, ok := req.GetArguments()["owner"].(string) owner, ok := req.GetArguments()["owner"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("owner is required")) return to.ErrorResult(errors.New("owner is required"))
} }
repo, ok := req.GetArguments()["repo"].(string) repo, ok := req.GetArguments()["repo"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("repo is required")) return to.ErrorResult(errors.New("repo is required"))
} }
pageName, ok := req.GetArguments()["pageName"].(string) pageName, ok := req.GetArguments()["pageName"].(string)
if !ok { if !ok {
return to.ErrorResult(fmt.Errorf("pageName is required")) return to.ErrorResult(errors.New("pageName is required"))
} }
_, err := gitea.DoJSON(ctx, "DELETE", fmt.Sprintf("repos/%s/%s/wiki/page/%s", url.QueryEscape(owner), url.QueryEscape(repo), url.QueryEscape(pageName)), nil, nil, nil) _, err := gitea.DoJSON(ctx, "DELETE", fmt.Sprintf("repos/%s/%s/wiki/page/%s", url.QueryEscape(owner), url.QueryEscape(repo), url.QueryEscape(pageName)), nil, nil, nil)

View File

@@ -34,7 +34,7 @@ func NewClient(token string) (*gitea.Client, error) {
} }
// Set user agent for the client // Set user agent for the client
client.SetUserAgent(fmt.Sprintf("gitea-mcp-server/%s", flag.Version)) client.SetUserAgent("gitea-mcp-server/" + flag.Version)
return client, nil return client, nil
} }

View File

@@ -5,6 +5,7 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@@ -40,7 +41,7 @@ func tokenFromContext(ctx context.Context) string {
func newRESTHTTPClient() *http.Client { func newRESTHTTPClient() *http.Client {
transport := http.DefaultTransport.(*http.Transport).Clone() transport := http.DefaultTransport.(*http.Transport).Clone()
if flag.Insecure { if flag.Insecure {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} //nolint:gosec transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} //nolint:gosec // user-requested insecure mode
} }
return &http.Client{ return &http.Client{
Transport: transport, Transport: transport,
@@ -51,7 +52,7 @@ func newRESTHTTPClient() *http.Client {
func buildAPIURL(path string, query url.Values) (string, error) { func buildAPIURL(path string, query url.Values) (string, error) {
host := strings.TrimRight(flag.Host, "/") host := strings.TrimRight(flag.Host, "/")
if host == "" { if host == "" {
return "", fmt.Errorf("gitea host is empty") return "", errors.New("gitea host is empty")
} }
p := strings.TrimLeft(path, "/") p := strings.TrimLeft(path, "/")
u, err := url.Parse(fmt.Sprintf("%s/api/v1/%s", host, p)) u, err := url.Parse(fmt.Sprintf("%s/api/v1/%s", host, p))
@@ -66,7 +67,7 @@ func buildAPIURL(path string, query url.Values) (string, error) {
// DoJSON performs an API request and decodes a JSON response into respOut (if non-nil). // DoJSON performs an API request and decodes a JSON response into respOut (if non-nil).
// It returns the HTTP status code. // It returns the HTTP status code.
func DoJSON(ctx context.Context, method, path string, query url.Values, body any, respOut any) (int, error) { func DoJSON(ctx context.Context, method, path string, query url.Values, body, respOut any) (int, error) {
var bodyReader io.Reader var bodyReader io.Reader
if body != nil { if body != nil {
b, err := json.Marshal(body) b, err := json.Marshal(body)
@@ -87,7 +88,7 @@ func DoJSON(ctx context.Context, method, path string, query url.Values, body any
token := tokenFromContext(ctx) token := tokenFromContext(ctx)
if token != "" { if token != "" {
req.Header.Set("Authorization", fmt.Sprintf("token %s", token)) req.Header.Set("Authorization", "token "+token)
} }
req.Header.Set("Accept", "application/json") req.Header.Set("Accept", "application/json")
if body != nil { if body != nil {
@@ -107,7 +108,7 @@ func DoJSON(ctx context.Context, method, path string, query url.Values, body any
} }
if respOut == nil { if respOut == nil {
io.Copy(io.Discard, resp.Body) // best-effort _, _ = io.Copy(io.Discard, resp.Body) // best-effort
return resp.StatusCode, nil return resp.StatusCode, nil
} }
@@ -140,7 +141,7 @@ func DoBytes(ctx context.Context, method, path string, query url.Values, body an
token := tokenFromContext(ctx) token := tokenFromContext(ctx)
if token != "" { if token != "" {
req.Header.Set("Authorization", fmt.Sprintf("token %s", token)) req.Header.Set("Authorization", "token "+token)
} }
if accept != "" { if accept != "" {
req.Header.Set("Accept", accept) req.Header.Set("Accept", accept)
@@ -171,5 +172,3 @@ func DoBytes(ctx context.Context, method, path string, query url.Values, body an
return respBytes, resp.StatusCode, nil return respBytes, resp.StatusCode, nil
} }

View File

@@ -28,5 +28,3 @@ func TestTokenFromContext(t *testing.T) {
} }
}) })
} }

View File

@@ -1,7 +1,6 @@
package log package log
import ( import (
"fmt"
"os" "os"
"sync" "sync"
"time" "time"
@@ -35,14 +34,14 @@ func Default() *zap.Logger {
home = os.TempDir() home = os.TempDir()
} }
logDir := fmt.Sprintf("%s/.gitea-mcp", home) logDir := home + "/.gitea-mcp"
if err := os.MkdirAll(logDir, 0o700); err != nil { if err := os.MkdirAll(logDir, 0o700); err != nil {
// Fallback to temp directory if creation fails // Fallback to temp directory if creation fails
logDir = os.TempDir() logDir = os.TempDir()
} }
wss = append(wss, zapcore.AddSync(&lumberjack.Logger{ wss = append(wss, zapcore.AddSync(&lumberjack.Logger{
Filename: fmt.Sprintf("%s/gitea-mcp.log", logDir), Filename: logDir + "/gitea-mcp.log",
MaxSize: 100, MaxSize: 100,
MaxBackups: 10, MaxBackups: 10,
MaxAge: 30, MaxAge: 30,

View File

@@ -9,7 +9,7 @@ import (
// It accepts both numeric (float64 from JSON) and string representations. // It accepts both numeric (float64 from JSON) and string representations.
// This provides better UX for LLM callers that may naturally use strings // This provides better UX for LLM callers that may naturally use strings
// for identifiers like issue/PR numbers. // for identifiers like issue/PR numbers.
func GetIndex(args map[string]interface{}, key string) (int64, error) { func GetIndex(args map[string]any, key string) (int64, error) {
val, exists := args[key] val, exists := args[key]
if !exists { if !exists {
return 0, fmt.Errorf("%s is required", key) return 0, fmt.Errorf("%s is required", key)

View File

@@ -8,7 +8,7 @@ import (
func TestGetIndex(t *testing.T) { func TestGetIndex(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
args map[string]interface{} args map[string]any
key string key string
wantIndex int64 wantIndex int64
wantErr bool wantErr bool
@@ -16,63 +16,63 @@ func TestGetIndex(t *testing.T) {
}{ }{
{ {
name: "valid float64", name: "valid float64",
args: map[string]interface{}{"index": float64(123)}, args: map[string]any{"index": float64(123)},
key: "index", key: "index",
wantIndex: 123, wantIndex: 123,
wantErr: false, wantErr: false,
}, },
{ {
name: "valid string", name: "valid string",
args: map[string]interface{}{"index": "456"}, args: map[string]any{"index": "456"},
key: "index", key: "index",
wantIndex: 456, wantIndex: 456,
wantErr: false, wantErr: false,
}, },
{ {
name: "valid string with large number", name: "valid string with large number",
args: map[string]interface{}{"index": "999999"}, args: map[string]any{"index": "999999"},
key: "index", key: "index",
wantIndex: 999999, wantIndex: 999999,
wantErr: false, wantErr: false,
}, },
{ {
name: "missing parameter", name: "missing parameter",
args: map[string]interface{}{}, args: map[string]any{},
key: "index", key: "index",
wantErr: true, wantErr: true,
errMsg: "index is required", errMsg: "index is required",
}, },
{ {
name: "invalid string (not a number)", name: "invalid string (not a number)",
args: map[string]interface{}{"index": "abc"}, args: map[string]any{"index": "abc"},
key: "index", key: "index",
wantErr: true, wantErr: true,
errMsg: "must be a valid integer", errMsg: "must be a valid integer",
}, },
{ {
name: "invalid string (decimal)", name: "invalid string (decimal)",
args: map[string]interface{}{"index": "12.34"}, args: map[string]any{"index": "12.34"},
key: "index", key: "index",
wantErr: true, wantErr: true,
errMsg: "must be a valid integer", errMsg: "must be a valid integer",
}, },
{ {
name: "invalid type (bool)", name: "invalid type (bool)",
args: map[string]interface{}{"index": true}, args: map[string]any{"index": true},
key: "index", key: "index",
wantErr: true, wantErr: true,
errMsg: "must be a number or numeric string", errMsg: "must be a number or numeric string",
}, },
{ {
name: "invalid type (map)", name: "invalid type (map)",
args: map[string]interface{}{"index": map[string]string{"foo": "bar"}}, args: map[string]any{"index": map[string]string{"foo": "bar"}},
key: "index", key: "index",
wantErr: true, wantErr: true,
errMsg: "must be a number or numeric string", errMsg: "must be a number or numeric string",
}, },
{ {
name: "custom key name", name: "custom key name",
args: map[string]interface{}{"pr_index": "789"}, args: map[string]any{"pr_index": "789"},
key: "pr_index", key: "pr_index",
wantIndex: 789, wantIndex: 789,
wantErr: false, wantErr: false,

View File

@@ -1,73 +0,0 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ptr
import (
"fmt"
"reflect"
)
// AllPtrFieldsNil tests whether all pointer fields in a struct are nil. This is useful when,
// for example, an API struct is handled by plugins which need to distinguish
// "no plugin accepted this spec" from "this spec is empty".
//
// This function is only valid for structs and pointers to structs. Any other
// type will cause a panic. Passing a typed nil pointer will return true.
func AllPtrFieldsNil(obj interface{}) bool {
v := reflect.ValueOf(obj)
if !v.IsValid() {
panic(fmt.Sprintf("reflect.ValueOf() produced a non-valid Value for %#v", obj))
}
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return true
}
v = v.Elem()
}
for i := 0; i < v.NumField(); i++ {
if v.Field(i).Kind() == reflect.Ptr && !v.Field(i).IsNil() {
return false
}
}
return true
}
// To returns a pointer to the given value.
func To[T any](v T) *T {
return &v
}
// Deref dereferences ptr and returns the value it points to if no nil, or else
// returns def.
func Deref[T any](ptr *T, def T) T {
if ptr != nil {
return *ptr
}
return def
}
// Equal returns true if both arguments are nil or both arguments
// dereference to the same value.
func Equal[T comparable](a, b *T) bool {
if (a == nil) != (b == nil) {
return false
}
if a == nil {
return true
}
return *a == *b
}