Simplify codebase (#195)

Net **-650 LOC** by removing duplication and dead noise. All tests pass.

### Duplication & helpers
- Extracted shared slim helpers (`UserLogin`, `UserLogins`, `LabelNames`, `BodyWithAttachments`, `UserDetail`, `Repo`/`Repos`, `Label`/`Labels`) into `pkg/slim`. Deleted the 4 copies that lived in `issue/`, `pull/`, `search/`, `repo/` (plus duplicate `slimUserDetail`/`slimRepo`/`slimLabels` across packages).
- Added `params.GetOptionalBoolPtr` and `params.GetOptionalStringPtr`. Replaced 18 awkward `new(localVar); if !ok { = nil }` patterns across `repo/`, `pull/`, `issue/`, `label/`, `milestone/`, `search/`.
- Extracted `pullRequestReviewerFn` for the 99%-identical `createPullRequestReviewerFn`/`deletePullRequestReviewerFn` pair.

### Dead code & noise
- Deleted **122** `log.Debugf("Called X")` narration lines (`zap.AddCaller` already records the caller) and pruned 19 unused `log` imports.
- Removed the unused `log.Logger` wrapper; the mcp-go server now uses `log.Default().Sugar()` directly (matches `util.Logger`).
- Deleted dead `s.DeleteTools("")` — confirmed no-op in mcp-go.
- Stripped WHAT-narration comments per project guidance.

### Correctness & consistency
- Fixed `log.Errorf(err.Error())` format-string bug in `pkg/to/to.go` — a `%` in the error would have been interpreted as a directive.
- Standardized `to.TextResult`/`to.ErrorResult` usage; `release.go`, `tag.go`, `branch.go` were bypassing the helpers in 9 sites (skipping the wrapper's debug/error logging).
- Made `params.GetString` reject empty strings; dropped 21 redundant `err != nil || x == ""` checks in `operation/actions/`.
- Replaced raw `args["org"].(string)` in `ListOrgReposFn` with `params.GetString` to match the rest of the codebase.

### Performance
- **Cached `*gitea.Client` by host+token via `sync.Map`** + shared `*http.Transport` via `sync.Once` for both SDK and raw REST paths. Eliminates the SDK's `/api/v1/version` preflight on every tool call and enables connection keep-alive across requests.
- Gated `to.TextResult` debug log behind `flag.Debug` to skip the `string(bytes)` allocation when debug is off.
- Hoisted `8192` and `60s` magic numbers in `pkg/gitea/rest.go` into named constants.

---
This PR was written with the help of Claude Opus 4.7

---------

Co-authored-by: silverwind <silv3rwind@gmail.com>
Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/195
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
This commit is contained in:
silverwind
2026-05-19 08:25:36 +00:00
committed by silverwind
parent e36137f5a1
commit 371a06403a
40 changed files with 640 additions and 1007 deletions
+1 -5
View File
@@ -6,7 +6,6 @@ import (
"gitea.com/gitea/gitea-mcp/pkg/annotation"
"gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params"
"gitea.com/gitea/gitea-mcp/pkg/to"
@@ -65,7 +64,6 @@ func init() {
}
func CreateBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called CreateBranchFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
@@ -93,11 +91,10 @@ func CreateBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
return to.ErrorResult(fmt.Errorf("create branch error: %v", err))
}
return mcp.NewToolResultText("Branch Created"), nil
return to.TextResult("Branch Created")
}
func DeleteBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called DeleteBranchFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
@@ -124,7 +121,6 @@ func DeleteBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
}
func ListBranchesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called ListBranchesFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
-3
View File
@@ -6,7 +6,6 @@ import (
"gitea.com/gitea/gitea-mcp/pkg/annotation"
"gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params"
"gitea.com/gitea/gitea-mcp/pkg/to"
@@ -53,7 +52,6 @@ func init() {
}
func ListRepoCommitsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called ListRepoCommitsFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
@@ -86,7 +84,6 @@ func ListRepoCommitsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
}
func GetCommitFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called GetCommitFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
-5
View File
@@ -10,7 +10,6 @@ import (
"gitea.com/gitea/gitea-mcp/pkg/annotation"
"gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params"
"gitea.com/gitea/gitea-mcp/pkg/to"
@@ -98,7 +97,6 @@ type ContentLine struct {
}
func GetFileContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called GetFileFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
@@ -162,7 +160,6 @@ func GetFileContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
}
func GetDirContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called GetDirContentFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
@@ -189,7 +186,6 @@ func GetDirContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
}
func CreateOrUpdateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called CreateOrUpdateFileFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
@@ -249,7 +245,6 @@ func CreateOrUpdateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
}
func DeleteFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called DeleteFileFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
+11 -28
View File
@@ -6,7 +6,6 @@ import (
"gitea.com/gitea/gitea-mcp/pkg/annotation"
"gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params"
"gitea.com/gitea/gitea-mcp/pkg/to"
@@ -97,7 +96,6 @@ func init() {
}
func CreateReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called CreateReleasesFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
@@ -136,14 +134,13 @@ func CreateReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
IsPrerelease: isPreRelease,
})
if err != nil {
return nil, fmt.Errorf("create release error: %v", err)
return to.ErrorResult(fmt.Errorf("create release error: %v", err))
}
return mcp.NewToolResultText("Release Created"), nil
return to.TextResult("Release Created")
}
func DeleteReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called DeleteReleaseFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
@@ -164,14 +161,13 @@ func DeleteReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
}
_, err = client.DeleteRelease(owner, repo, id)
if err != nil {
return nil, fmt.Errorf("delete release error: %v", err)
return to.ErrorResult(fmt.Errorf("delete release error: %v", err))
}
return to.TextResult("Release deleted successfully")
}
func GetReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called GetReleaseFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
@@ -192,14 +188,13 @@ func GetReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
}
release, _, err := client.GetRelease(owner, repo, id)
if err != nil {
return nil, fmt.Errorf("get release error: %v", err)
return to.ErrorResult(fmt.Errorf("get release error: %v", err))
}
return to.TextResult(slimRelease(release))
}
func GetLatestReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called GetLatestReleaseFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
@@ -216,14 +211,13 @@ func GetLatestReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
}
release, _, err := client.GetLatestRelease(owner, repo)
if err != nil {
return nil, fmt.Errorf("get latest release error: %v", err)
return to.ErrorResult(fmt.Errorf("get latest release error: %v", err))
}
return to.TextResult(slimRelease(release))
}
func ListReleasesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called ListReleasesFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
@@ -233,18 +227,7 @@ func ListReleasesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
if err != nil {
return to.ErrorResult(err)
}
var pIsDraft *bool
isDraft, ok := args["is_draft"].(bool)
if ok {
pIsDraft = new(isDraft)
}
var pIsPreRelease *bool
isPreRelease, ok := args["is_pre_release"].(bool)
if ok {
pIsPreRelease = new(isPreRelease)
}
page := params.GetOptionalInt(args, "page", 1)
pageSize := params.GetOptionalInt(args, "per_page", 20)
page, pageSize := params.GetPagination(args, 20)
client, err := gitea.ClientFromContext(ctx)
if err != nil {
@@ -252,14 +235,14 @@ func ListReleasesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
}
releases, _, err := client.ListReleases(owner, repo, gitea_sdk.ListReleasesOptions{
ListOptions: gitea_sdk.ListOptions{
Page: int(page),
PageSize: int(pageSize),
Page: page,
PageSize: pageSize,
},
IsDraft: pIsDraft,
IsPreRelease: pIsPreRelease,
IsDraft: params.GetOptionalBoolPtr(args, "is_draft"),
IsPreRelease: params.GetOptionalBoolPtr(args, "is_pre_release"),
})
if err != nil {
return nil, fmt.Errorf("list releases error: %v", err)
return to.ErrorResult(fmt.Errorf("list releases error: %v", err))
}
return to.TextResult(slimReleases(releases))
+8 -23
View File
@@ -2,13 +2,12 @@ package repo
import (
"context"
"errors"
"fmt"
"gitea.com/gitea/gitea-mcp/pkg/annotation"
"gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params"
"gitea.com/gitea/gitea-mcp/pkg/slim"
"gitea.com/gitea/gitea-mcp/pkg/to"
"gitea.com/gitea/gitea-mcp/pkg/tool"
@@ -90,7 +89,6 @@ func init() {
}
func CreateRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called CreateRepoFn")
args := req.GetArguments()
name, err := params.GetString(args, "name")
if err != nil {
@@ -140,11 +138,10 @@ func CreateRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
return to.ErrorResult(fmt.Errorf("create repository '%s' err: %v", name, err))
}
}
return to.TextResult(slimRepo(repo))
return to.TextResult(slim.Repo(repo))
}
func ForkRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called ForkRepoFn")
args := req.GetArguments()
user, err := params.GetString(args, "user")
if err != nil {
@@ -154,19 +151,9 @@ func ForkRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResu
if err != nil {
return to.ErrorResult(err)
}
organization, ok := args["organization"].(string)
organizationPtr := new(organization)
if !ok || organization == "" {
organizationPtr = nil
}
name, ok := args["name"].(string)
namePtr := new(name)
if !ok || name == "" {
namePtr = nil
}
opt := gitea_sdk.CreateForkOption{
Organization: organizationPtr,
Name: namePtr,
Organization: params.GetOptionalStringPtr(args, "organization"),
Name: params.GetOptionalStringPtr(args, "name"),
}
client, err := gitea.ClientFromContext(ctx)
if err != nil {
@@ -180,7 +167,6 @@ func ForkRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResu
}
func ListMyReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called ListMyReposFn")
page, pageSize := params.GetPagination(req.GetArguments(), 30)
opt := gitea_sdk.ListReposOptions{
ListOptions: gitea_sdk.ListOptions{
@@ -197,14 +183,13 @@ func ListMyReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
return to.ErrorResult(fmt.Errorf("list my repositories error: %v", err))
}
return to.TextResult(slimRepos(repos))
return to.TextResult(slim.Repos(repos))
}
func ListOrgReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called ListOrgReposFn")
org, ok := req.GetArguments()["org"].(string)
if !ok {
return to.ErrorResult(errors.New("organization name is required"))
org, err := params.GetString(req.GetArguments(), "org")
if err != nil {
return to.ErrorResult(err)
}
page, pageSize := params.GetPagination(req.GetArguments(), 100)
opt := gitea_sdk.ListOrgReposOptions{
+3 -48
View File
@@ -1,56 +1,11 @@
package repo
import (
"gitea.com/gitea/gitea-mcp/pkg/slim"
gitea_sdk "code.gitea.io/sdk/gitea"
)
func userLogin(u *gitea_sdk.User) string {
if u == nil {
return ""
}
return u.UserName
}
func slimRepo(r *gitea_sdk.Repository) map[string]any {
if r == nil {
return nil
}
m := map[string]any{
"id": r.ID,
"full_name": r.FullName,
"description": r.Description,
"html_url": r.HTMLURL,
"clone_url": r.CloneURL,
"ssh_url": r.SSHURL,
"default_branch": r.DefaultBranch,
"private": r.Private,
"fork": r.Fork,
"archived": r.Archived,
"language": r.Language,
"stars_count": r.Stars,
"forks_count": r.Forks,
"open_issues_count": r.OpenIssues,
"open_pr_counter": r.OpenPulls,
"created_at": r.Created,
"updated_at": r.Updated,
}
if r.Owner != nil {
m["owner"] = r.Owner.UserName
}
if len(r.Topics) > 0 {
m["topics"] = r.Topics
}
return m
}
func slimRepos(repos []*gitea_sdk.Repository) []map[string]any {
out := make([]map[string]any, 0, len(repos))
for _, r := range repos {
out = append(out, slimRepo(r))
}
return out
}
func slimBranch(b *gitea_sdk.Branch) map[string]any {
if b == nil {
return nil
@@ -144,7 +99,7 @@ func slimRelease(r *gitea_sdk.Release) map[string]any {
"draft": r.IsDraft,
"prerelease": r.IsPrerelease,
"html_url": r.HTMLURL,
"author": userLogin(r.Publisher),
"author": slim.UserLogin(r.Publisher),
"created_at": r.CreatedAt,
"published_at": r.PublishedAt,
}
-33
View File
@@ -6,39 +6,6 @@ import (
gitea_sdk "code.gitea.io/sdk/gitea"
)
func TestSlimRepo(t *testing.T) {
r := &gitea_sdk.Repository{
ID: 1,
FullName: "org/repo",
Description: "A test repo",
HTMLURL: "https://gitea.com/org/repo",
CloneURL: "https://gitea.com/org/repo.git",
SSHURL: "git@gitea.com:org/repo.git",
DefaultBranch: "main",
Private: false,
Fork: false,
Archived: false,
Language: "Go",
Stars: 10,
Forks: 2,
Owner: &gitea_sdk.User{UserName: "org"},
Topics: []string{"mcp", "gitea"},
}
m := slimRepo(r)
if m["full_name"] != "org/repo" {
t.Errorf("expected full_name org/repo, got %v", m["full_name"])
}
if m["owner"] != "org" {
t.Errorf("expected owner org, got %v", m["owner"])
}
topics := m["topics"].([]string)
if len(topics) != 2 {
t.Errorf("expected 2 topics, got %d", len(topics))
}
}
func TestSlimTag(t *testing.T) {
tag := &gitea_sdk.Tag{
Name: "v1.0.0",
+5 -10
View File
@@ -6,7 +6,6 @@ import (
"gitea.com/gitea/gitea-mcp/pkg/annotation"
"gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params"
"gitea.com/gitea/gitea-mcp/pkg/to"
@@ -79,7 +78,6 @@ func init() {
}
func CreateTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called CreateTagFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
@@ -106,14 +104,13 @@ func CreateTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
Message: message,
})
if err != nil {
return nil, fmt.Errorf("create tag error: %v", err)
return to.ErrorResult(fmt.Errorf("create tag error: %v", err))
}
return mcp.NewToolResultText("Tag Created"), nil
return to.TextResult("Tag Created")
}
func DeleteTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called DeleteTagFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
@@ -134,14 +131,13 @@ func DeleteTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
}
_, err = client.DeleteTag(owner, repo, tagName)
if err != nil {
return nil, fmt.Errorf("delete tag error: %v", err)
return to.ErrorResult(fmt.Errorf("delete tag error: %v", err))
}
return to.TextResult("Tag deleted")
}
func GetTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called GetTagFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
@@ -162,14 +158,13 @@ func GetTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult
}
tag, _, err := client.GetTag(owner, repo, tagName)
if err != nil {
return nil, fmt.Errorf("get tag error: %v", err)
return to.ErrorResult(fmt.Errorf("get tag error: %v", err))
}
return to.TextResult(slimTag(tag))
}
func ListTagsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called ListTagsFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {
@@ -193,7 +188,7 @@ func ListTagsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResu
},
})
if err != nil {
return nil, fmt.Errorf("list tags error: %v", err)
return to.ErrorResult(fmt.Errorf("list tags error: %v", err))
}
return to.TextResult(slimTags(tags))
-2
View File
@@ -6,7 +6,6 @@ import (
"gitea.com/gitea/gitea-mcp/pkg/annotation"
"gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params"
"gitea.com/gitea/gitea-mcp/pkg/to"
@@ -38,7 +37,6 @@ func init() {
}
func GetRepoTreeFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called GetRepoTreeFn")
args := req.GetArguments()
owner, err := params.GetString(args, "owner")
if err != nil {