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:
+84
-106
@@ -2,7 +2,6 @@ package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@@ -10,7 +9,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"
|
||||
|
||||
@@ -133,17 +131,14 @@ func configWriteFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
|
||||
}
|
||||
}
|
||||
|
||||
// Secret functions
|
||||
|
||||
func listRepoActionSecretsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listRepoActionSecretsFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
page, pageSize := params.GetPagination(req.GetArguments(), 30)
|
||||
|
||||
@@ -163,22 +158,21 @@ func listRepoActionSecretsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
|
||||
}
|
||||
|
||||
func upsertRepoActionSecretFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called upsertRepoActionSecretFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
name, err := params.GetString(req.GetArguments(), "name")
|
||||
if err != nil || name == "" {
|
||||
return to.ErrorResult(errors.New("name is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
data, err := params.GetString(req.GetArguments(), "data")
|
||||
if err != nil || data == "" {
|
||||
return to.ErrorResult(errors.New("data is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
description, _ := req.GetArguments()["description"].(string)
|
||||
|
||||
@@ -198,18 +192,17 @@ func upsertRepoActionSecretFn(ctx context.Context, req mcp.CallToolRequest) (*mc
|
||||
}
|
||||
|
||||
func deleteRepoActionSecretFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called deleteRepoActionSecretFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
name, err := params.GetString(req.GetArguments(), "name")
|
||||
if err != nil || name == "" {
|
||||
return to.ErrorResult(errors.New("name is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
@@ -224,10 +217,9 @@ func deleteRepoActionSecretFn(ctx context.Context, req mcp.CallToolRequest) (*mc
|
||||
}
|
||||
|
||||
func listOrgActionSecretsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listOrgActionSecretsFn")
|
||||
org, err := params.GetString(req.GetArguments(), "org")
|
||||
if err != nil || org == "" {
|
||||
return to.ErrorResult(errors.New("org is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
page, pageSize := params.GetPagination(req.GetArguments(), 30)
|
||||
|
||||
@@ -247,18 +239,17 @@ func listOrgActionSecretsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.
|
||||
}
|
||||
|
||||
func upsertOrgActionSecretFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called upsertOrgActionSecretFn")
|
||||
org, err := params.GetString(req.GetArguments(), "org")
|
||||
if err != nil || org == "" {
|
||||
return to.ErrorResult(errors.New("org is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
name, err := params.GetString(req.GetArguments(), "name")
|
||||
if err != nil || name == "" {
|
||||
return to.ErrorResult(errors.New("name is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
data, err := params.GetString(req.GetArguments(), "data")
|
||||
if err != nil || data == "" {
|
||||
return to.ErrorResult(errors.New("data is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
description, _ := req.GetArguments()["description"].(string)
|
||||
|
||||
@@ -278,14 +269,13 @@ func upsertOrgActionSecretFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
|
||||
}
|
||||
|
||||
func deleteOrgActionSecretFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called deleteOrgActionSecretFn")
|
||||
org, err := params.GetString(req.GetArguments(), "org")
|
||||
if err != nil || org == "" {
|
||||
return to.ErrorResult(errors.New("org is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
name, err := params.GetString(req.GetArguments(), "name")
|
||||
if err != nil || name == "" {
|
||||
return to.ErrorResult(errors.New("name is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
escapedOrg := url.PathEscape(org)
|
||||
@@ -297,17 +287,14 @@ func deleteOrgActionSecretFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
|
||||
return to.TextResult(map[string]any{"message": "secret deleted"})
|
||||
}
|
||||
|
||||
// Variable functions
|
||||
|
||||
func listRepoActionVariablesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listRepoActionVariablesFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
page, pageSize := params.GetPagination(req.GetArguments(), 30)
|
||||
|
||||
@@ -324,18 +311,17 @@ func listRepoActionVariablesFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
||||
}
|
||||
|
||||
func getRepoActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getRepoActionVariableFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
name, err := params.GetString(req.GetArguments(), "name")
|
||||
if err != nil || name == "" {
|
||||
return to.ErrorResult(errors.New("name is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
@@ -350,22 +336,21 @@ func getRepoActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
|
||||
}
|
||||
|
||||
func createRepoActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called createRepoActionVariableFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
name, err := params.GetString(req.GetArguments(), "name")
|
||||
if err != nil || name == "" {
|
||||
return to.ErrorResult(errors.New("name is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
value, err := params.GetString(req.GetArguments(), "value")
|
||||
if err != nil || value == "" {
|
||||
return to.ErrorResult(errors.New("value is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
@@ -380,22 +365,21 @@ func createRepoActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*
|
||||
}
|
||||
|
||||
func updateRepoActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called updateRepoActionVariableFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
name, err := params.GetString(req.GetArguments(), "name")
|
||||
if err != nil || name == "" {
|
||||
return to.ErrorResult(errors.New("name is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
value, err := params.GetString(req.GetArguments(), "value")
|
||||
if err != nil || value == "" {
|
||||
return to.ErrorResult(errors.New("value is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
@@ -410,18 +394,17 @@ func updateRepoActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*
|
||||
}
|
||||
|
||||
func deleteRepoActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called deleteRepoActionVariableFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
name, err := params.GetString(req.GetArguments(), "name")
|
||||
if err != nil || name == "" {
|
||||
return to.ErrorResult(errors.New("name is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
@@ -436,10 +419,9 @@ func deleteRepoActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*
|
||||
}
|
||||
|
||||
func listOrgActionVariablesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listOrgActionVariablesFn")
|
||||
org, err := params.GetString(req.GetArguments(), "org")
|
||||
if err != nil || org == "" {
|
||||
return to.ErrorResult(errors.New("org is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
page, pageSize := params.GetPagination(req.GetArguments(), 30)
|
||||
|
||||
@@ -457,14 +439,13 @@ func listOrgActionVariablesFn(ctx context.Context, req mcp.CallToolRequest) (*mc
|
||||
}
|
||||
|
||||
func getOrgActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getOrgActionVariableFn")
|
||||
org, err := params.GetString(req.GetArguments(), "org")
|
||||
if err != nil || org == "" {
|
||||
return to.ErrorResult(errors.New("org is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
name, err := params.GetString(req.GetArguments(), "name")
|
||||
if err != nil || name == "" {
|
||||
return to.ErrorResult(errors.New("name is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
@@ -479,18 +460,17 @@ func getOrgActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.
|
||||
}
|
||||
|
||||
func createOrgActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called createOrgActionVariableFn")
|
||||
org, err := params.GetString(req.GetArguments(), "org")
|
||||
if err != nil || org == "" {
|
||||
return to.ErrorResult(errors.New("org is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
name, err := params.GetString(req.GetArguments(), "name")
|
||||
if err != nil || name == "" {
|
||||
return to.ErrorResult(errors.New("name is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
value, err := params.GetString(req.GetArguments(), "value")
|
||||
if err != nil || value == "" {
|
||||
return to.ErrorResult(errors.New("value is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
description, _ := req.GetArguments()["description"].(string)
|
||||
|
||||
@@ -510,18 +490,17 @@ func createOrgActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
||||
}
|
||||
|
||||
func updateOrgActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called updateOrgActionVariableFn")
|
||||
org, err := params.GetString(req.GetArguments(), "org")
|
||||
if err != nil || org == "" {
|
||||
return to.ErrorResult(errors.New("org is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
name, err := params.GetString(req.GetArguments(), "name")
|
||||
if err != nil || name == "" {
|
||||
return to.ErrorResult(errors.New("name is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
value, err := params.GetString(req.GetArguments(), "value")
|
||||
if err != nil || value == "" {
|
||||
return to.ErrorResult(errors.New("value is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
description, _ := req.GetArguments()["description"].(string)
|
||||
|
||||
@@ -540,14 +519,13 @@ func updateOrgActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
||||
}
|
||||
|
||||
func deleteOrgActionVariableFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called deleteOrgActionVariableFn")
|
||||
org, err := params.GetString(req.GetArguments(), "org")
|
||||
if err != nil || org == "" {
|
||||
return to.ErrorResult(errors.New("org is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
name, err := params.GetString(req.GetArguments(), "name")
|
||||
if err != nil || name == "" {
|
||||
return to.ErrorResult(errors.New("name is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
_, err = gitea.DoJSON(ctx, "DELETE", fmt.Sprintf("orgs/%s/actions/variables/%s", url.PathEscape(org), url.PathEscape(name)), nil, nil, nil)
|
||||
|
||||
+42
-56
@@ -12,7 +12,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"
|
||||
|
||||
@@ -125,14 +124,13 @@ func doJSONWithFallback(ctx context.Context, method string, paths []string, quer
|
||||
}
|
||||
|
||||
func listRepoActionWorkflowsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listRepoActionWorkflowsFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
page, pageSize := params.GetPagination(req.GetArguments(), 30)
|
||||
query := url.Values{}
|
||||
@@ -153,18 +151,17 @@ func listRepoActionWorkflowsFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
||||
}
|
||||
|
||||
func getRepoActionWorkflowFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getRepoActionWorkflowFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
workflowID, err := params.GetString(req.GetArguments(), "workflow_id")
|
||||
if err != nil || workflowID == "" {
|
||||
return to.ErrorResult(errors.New("workflow_id is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
var result any
|
||||
@@ -181,22 +178,21 @@ func getRepoActionWorkflowFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
|
||||
}
|
||||
|
||||
func dispatchRepoActionWorkflowFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called dispatchRepoActionWorkflowFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
workflowID, err := params.GetString(req.GetArguments(), "workflow_id")
|
||||
if err != nil || workflowID == "" {
|
||||
return to.ErrorResult(errors.New("workflow_id is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
ref, err := params.GetString(req.GetArguments(), "ref")
|
||||
if err != nil || ref == "" {
|
||||
return to.ErrorResult(errors.New("ref is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
var inputs map[string]any
|
||||
@@ -231,14 +227,13 @@ func dispatchRepoActionWorkflowFn(ctx context.Context, req mcp.CallToolRequest)
|
||||
}
|
||||
|
||||
func listRepoActionRunsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listRepoActionRunsFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
page, pageSize := params.GetPagination(req.GetArguments(), 30)
|
||||
statusFilter, _ := req.GetArguments()["status"].(string)
|
||||
@@ -264,14 +259,13 @@ func listRepoActionRunsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
|
||||
}
|
||||
|
||||
func getRepoActionRunFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getRepoActionRunFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
runID, err := params.GetIndex(req.GetArguments(), "run_id")
|
||||
if err != nil || runID <= 0 {
|
||||
@@ -292,14 +286,13 @@ func getRepoActionRunFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
|
||||
}
|
||||
|
||||
func cancelRepoActionRunFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called cancelRepoActionRunFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
runID, err := params.GetIndex(req.GetArguments(), "run_id")
|
||||
if err != nil || runID <= 0 {
|
||||
@@ -319,14 +312,13 @@ func cancelRepoActionRunFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.C
|
||||
}
|
||||
|
||||
func rerunRepoActionRunFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called rerunRepoActionRunFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
runID, err := params.GetIndex(req.GetArguments(), "run_id")
|
||||
if err != nil || runID <= 0 {
|
||||
@@ -351,14 +343,13 @@ func rerunRepoActionRunFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
|
||||
}
|
||||
|
||||
func listRepoActionJobsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listRepoActionJobsFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
page, pageSize := params.GetPagination(req.GetArguments(), 30)
|
||||
statusFilter, _ := req.GetArguments()["status"].(string)
|
||||
@@ -384,14 +375,13 @@ func listRepoActionJobsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
|
||||
}
|
||||
|
||||
func listRepoActionRunJobsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listRepoActionRunJobsFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil || owner == "" {
|
||||
return to.ErrorResult(errors.New("owner is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(req.GetArguments(), "repo")
|
||||
if err != nil || repo == "" {
|
||||
return to.ErrorResult(errors.New("repo is required"))
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
runID, err := params.GetIndex(req.GetArguments(), "run_id")
|
||||
if err != nil || runID <= 0 {
|
||||
@@ -416,8 +406,6 @@ func listRepoActionRunJobsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
|
||||
return to.TextResult(slimActionJobs(result))
|
||||
}
|
||||
|
||||
// Log functions (merged from logs.go)
|
||||
|
||||
func logPaths(owner, repo string, jobID int64) []string {
|
||||
return []string{
|
||||
fmt.Sprintf("repos/%s/%s/actions/jobs/%d/logs", url.PathEscape(owner), url.PathEscape(repo), jobID),
|
||||
@@ -473,7 +461,6 @@ func limitBytes(data []byte, maxBytes int) ([]byte, bool) {
|
||||
}
|
||||
|
||||
func getRepoActionJobLogPreviewFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getRepoActionJobLogPreviewFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -508,7 +495,6 @@ func getRepoActionJobLogPreviewFn(ctx context.Context, req mcp.CallToolRequest)
|
||||
}
|
||||
|
||||
func downloadRepoActionJobLogFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called downloadRepoActionJobLogFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
|
||||
+20
-41
@@ -7,8 +7,8 @@ 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/slim"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||
|
||||
@@ -145,7 +145,6 @@ func issueWriteFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
|
||||
}
|
||||
|
||||
func getIssueByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getIssueByIndexFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -164,12 +163,11 @@ func getIssueByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
|
||||
return to.ErrorResult(fmt.Errorf("get %v/%v/issue/%v err: %v", owner, repo, index, err))
|
||||
}
|
||||
m := slimIssue(&issue.Issue)
|
||||
m["body"] = bodyWithAttachments(issue.Body, issue.Assets)
|
||||
m["body"] = slim.BodyWithAttachments(issue.Body, issue.Assets)
|
||||
return to.TextResult(m)
|
||||
}
|
||||
|
||||
func listRepoIssuesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called ListIssuesFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -210,7 +208,6 @@ func listRepoIssuesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
||||
}
|
||||
|
||||
func createIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called createIssueFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -257,7 +254,6 @@ func createIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
|
||||
}
|
||||
|
||||
func createIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called createIssueCommentFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -290,7 +286,6 @@ func createIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
|
||||
}
|
||||
|
||||
func editIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called editIssueFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -304,32 +299,25 @@ func editIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
opt := gitea_sdk.EditIssueOption{}
|
||||
|
||||
title, ok := req.GetArguments()["title"].(string)
|
||||
if ok {
|
||||
args := req.GetArguments()
|
||||
opt := gitea_sdk.EditIssueOption{
|
||||
Body: params.GetPresentStringPtr(args, "body"),
|
||||
Ref: params.GetPresentStringPtr(args, "ref"),
|
||||
Assignees: params.GetStringSlice(args, "assignees"),
|
||||
Deadline: params.GetOptionalTime(args, "deadline"),
|
||||
RemoveDeadline: params.GetOptionalBoolPtr(args, "remove_deadline"),
|
||||
}
|
||||
if title, ok := args["title"].(string); ok {
|
||||
opt.Title = title
|
||||
}
|
||||
body, ok := req.GetArguments()["body"].(string)
|
||||
if ok {
|
||||
opt.Body = new(body)
|
||||
}
|
||||
opt.Assignees = params.GetStringSlice(req.GetArguments(), "assignees")
|
||||
if val, exists := req.GetArguments()["milestone"]; exists {
|
||||
if val, exists := args["milestone"]; exists {
|
||||
if milestone, ok := params.ToInt64(val); ok {
|
||||
opt.Milestone = new(milestone)
|
||||
opt.Milestone = &milestone
|
||||
}
|
||||
}
|
||||
state, ok := req.GetArguments()["state"].(string)
|
||||
if ok {
|
||||
opt.State = new(gitea_sdk.StateType(state))
|
||||
}
|
||||
if ref, ok := req.GetArguments()["ref"].(string); ok {
|
||||
opt.Ref = &ref
|
||||
}
|
||||
opt.Deadline = params.GetOptionalTime(req.GetArguments(), "deadline")
|
||||
if removeDeadline, ok := req.GetArguments()["remove_deadline"].(bool); ok {
|
||||
opt.RemoveDeadline = &removeDeadline
|
||||
if state, ok := args["state"].(string); ok {
|
||||
s := gitea_sdk.StateType(state)
|
||||
opt.State = &s
|
||||
}
|
||||
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
@@ -345,7 +333,6 @@ func editIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
|
||||
}
|
||||
|
||||
func editIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called editIssueCommentFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -378,7 +365,6 @@ func editIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
|
||||
}
|
||||
|
||||
func getIssueCommentsByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getIssueCommentsByIndexFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -399,14 +385,13 @@ func getIssueCommentsByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
||||
out := make([]map[string]any, 0, len(comments))
|
||||
for i := range comments {
|
||||
m := slimComment(&comments[i].Comment)
|
||||
m["body"] = bodyWithAttachments(comments[i].Body, comments[i].Assets)
|
||||
m["body"] = slim.BodyWithAttachments(comments[i].Body, comments[i].Assets)
|
||||
out = append(out, m)
|
||||
}
|
||||
return to.TextResult(out)
|
||||
}
|
||||
|
||||
func getIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getIssueLabelsFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -428,13 +413,10 @@ func getIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get %v/%v/issues/%v/labels err: %v", owner, repo, index, err))
|
||||
}
|
||||
return to.TextResult(slimLabels(labels))
|
||||
return to.TextResult(slim.Labels(labels))
|
||||
}
|
||||
|
||||
// Issue label operations (moved from label package)
|
||||
|
||||
func addIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called addIssueLabelsFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -460,11 +442,10 @@ func addIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("add labels to %v/%v/issue/%v err: %v", owner, repo, index, err))
|
||||
}
|
||||
return to.TextResult(slimLabels(issueLabels))
|
||||
return to.TextResult(slim.Labels(issueLabels))
|
||||
}
|
||||
|
||||
func replaceIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called replaceIssueLabelsFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -490,11 +471,10 @@ func replaceIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("replace labels on %v/%v/issue/%v err: %v", owner, repo, index, err))
|
||||
}
|
||||
return to.TextResult(slimLabels(issueLabels))
|
||||
return to.TextResult(slim.Labels(issueLabels))
|
||||
}
|
||||
|
||||
func clearIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called clearIssueLabelsFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -520,7 +500,6 @@ func clearIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
|
||||
}
|
||||
|
||||
func removeIssueLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called removeIssueLabelFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
|
||||
+7
-76
@@ -1,63 +1,11 @@
|
||||
package issue
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"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 userLogins(users []*gitea_sdk.User) []string {
|
||||
if len(users) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, len(users))
|
||||
for _, u := range users {
|
||||
if u != nil {
|
||||
out = append(out, u.UserName)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func labelNames(labels []*gitea_sdk.Label) []string {
|
||||
if len(labels) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, len(labels))
|
||||
for _, l := range labels {
|
||||
if l != nil {
|
||||
out = append(out, l.Name)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func bodyWithAttachments(body string, atts []*gitea_sdk.Attachment) string {
|
||||
links := make([]string, 0, len(atts))
|
||||
for _, a := range atts {
|
||||
if a == nil || a.DownloadURL == "" {
|
||||
continue
|
||||
}
|
||||
links = append(links, fmt.Sprintf("[%s](%s)", a.Name, a.DownloadURL))
|
||||
}
|
||||
if len(links) == 0 {
|
||||
return body
|
||||
}
|
||||
joined := strings.Join(links, "\n")
|
||||
if body == "" {
|
||||
return joined
|
||||
}
|
||||
return body + "\n\n" + joined
|
||||
}
|
||||
|
||||
func slimIssue(i *gitea_sdk.Issue) map[string]any {
|
||||
if i == nil {
|
||||
return nil
|
||||
@@ -68,15 +16,15 @@ func slimIssue(i *gitea_sdk.Issue) map[string]any {
|
||||
"body": i.Body,
|
||||
"state": i.State,
|
||||
"html_url": i.HTMLURL,
|
||||
"user": userLogin(i.Poster),
|
||||
"labels": labelNames(i.Labels),
|
||||
"user": slim.UserLogin(i.Poster),
|
||||
"labels": slim.LabelNames(i.Labels),
|
||||
"comments": i.Comments,
|
||||
"created_at": i.Created,
|
||||
"updated_at": i.Updated,
|
||||
"closed_at": i.Closed,
|
||||
}
|
||||
if len(i.Assignees) > 0 {
|
||||
m["assignees"] = userLogins(i.Assignees)
|
||||
m["assignees"] = slim.UserLogins(i.Assignees)
|
||||
}
|
||||
if i.Milestone != nil {
|
||||
m["milestone"] = map[string]any{
|
||||
@@ -107,13 +55,13 @@ func slimIssues(issues []*gitea_sdk.Issue) []map[string]any {
|
||||
"title": i.Title,
|
||||
"state": i.State,
|
||||
"html_url": i.HTMLURL,
|
||||
"user": userLogin(i.Poster),
|
||||
"user": slim.UserLogin(i.Poster),
|
||||
"comments": i.Comments,
|
||||
"created_at": i.Created,
|
||||
"updated_at": i.Updated,
|
||||
}
|
||||
if len(i.Labels) > 0 {
|
||||
m["labels"] = labelNames(i.Labels)
|
||||
m["labels"] = slim.LabelNames(i.Labels)
|
||||
}
|
||||
if i.Ref != "" {
|
||||
m["ref"] = i.Ref
|
||||
@@ -133,26 +81,9 @@ func slimComment(c *gitea_sdk.Comment) map[string]any {
|
||||
return map[string]any{
|
||||
"id": c.ID,
|
||||
"body": c.Body,
|
||||
"user": userLogin(c.Poster),
|
||||
"user": slim.UserLogin(c.Poster),
|
||||
"html_url": c.HTMLURL,
|
||||
"created_at": c.Created,
|
||||
"updated_at": c.Updated,
|
||||
}
|
||||
}
|
||||
|
||||
func slimLabels(labels []*gitea_sdk.Label) []map[string]any {
|
||||
out := make([]map[string]any, 0, len(labels))
|
||||
for _, l := range labels {
|
||||
if l == nil {
|
||||
continue
|
||||
}
|
||||
out = append(out, map[string]any{
|
||||
"id": l.ID,
|
||||
"name": l.Name,
|
||||
"color": l.Color,
|
||||
"description": l.Description,
|
||||
"exclusive": l.Exclusive,
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -40,29 +40,6 @@ func TestSlimIssue(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBodyWithAttachments(t *testing.T) {
|
||||
atts := []*gitea_sdk.Attachment{
|
||||
{Name: "shot.png", DownloadURL: "https://example/shot.png"},
|
||||
{Name: "log.txt", DownloadURL: "https://example/log.txt"},
|
||||
}
|
||||
got := bodyWithAttachments("see attached", atts)
|
||||
want := "see attached\n\n[shot.png](https://example/shot.png)\n[log.txt](https://example/log.txt)"
|
||||
if got != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
|
||||
if got := bodyWithAttachments("only body", nil); got != "only body" {
|
||||
t.Errorf("nil attachments should return body unchanged, got %q", got)
|
||||
}
|
||||
if got := bodyWithAttachments("", atts); got != "[shot.png](https://example/shot.png)\n[log.txt](https://example/log.txt)" {
|
||||
t.Errorf("empty body should drop separator, got %q", got)
|
||||
}
|
||||
skipped := []*gitea_sdk.Attachment{nil, {Name: "noop", DownloadURL: ""}}
|
||||
if got := bodyWithAttachments("body", skipped); got != "body" {
|
||||
t.Errorf("nil/empty-URL attachments should be skipped, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSlimIssues_ListIsSlimmer(t *testing.T) {
|
||||
i := &gitea_sdk.Issue{
|
||||
Index: 1,
|
||||
|
||||
+20
-41
@@ -6,8 +6,8 @@ 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/slim"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||
|
||||
@@ -108,7 +108,6 @@ func labelWriteFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
|
||||
}
|
||||
|
||||
func listRepoLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listRepoLabelsFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -133,11 +132,10 @@ func listRepoLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list %v/%v/labels err: %v", owner, repo, err))
|
||||
}
|
||||
return to.TextResult(slimLabels(labels))
|
||||
return to.TextResult(slim.Labels(labels))
|
||||
}
|
||||
|
||||
func getRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getRepoLabelFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -159,11 +157,10 @@ func getRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get %v/%v/label/%v err: %v", owner, repo, id, err))
|
||||
}
|
||||
return to.TextResult(slimLabel(label))
|
||||
return to.TextResult(slim.Label(label))
|
||||
}
|
||||
|
||||
func createRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called createRepoLabelFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -199,11 +196,10 @@ func createRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("create %v/%v/label err: %v", owner, repo, err))
|
||||
}
|
||||
return to.TextResult(slimLabel(label))
|
||||
return to.TextResult(slim.Label(label))
|
||||
}
|
||||
|
||||
func editRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called editRepoLabelFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -217,18 +213,12 @@ func editRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
opt := gitea_sdk.EditLabelOption{}
|
||||
if name, ok := req.GetArguments()["name"].(string); ok {
|
||||
opt.Name = new(name)
|
||||
}
|
||||
if color, ok := req.GetArguments()["color"].(string); ok {
|
||||
opt.Color = new(color)
|
||||
}
|
||||
if description, ok := req.GetArguments()["description"].(string); ok {
|
||||
opt.Description = new(description)
|
||||
}
|
||||
if isArchived, ok := req.GetArguments()["is_archived"].(bool); ok {
|
||||
opt.IsArchived = &isArchived
|
||||
args := req.GetArguments()
|
||||
opt := gitea_sdk.EditLabelOption{
|
||||
Name: params.GetOptionalStringPtr(args, "name"),
|
||||
Color: params.GetOptionalStringPtr(args, "color"),
|
||||
Description: params.GetPresentStringPtr(args, "description"),
|
||||
IsArchived: params.GetOptionalBoolPtr(args, "is_archived"),
|
||||
}
|
||||
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
@@ -239,11 +229,10 @@ func editRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("edit %v/%v/label/%v err: %v", owner, repo, id, err))
|
||||
}
|
||||
return to.TextResult(slimLabel(label))
|
||||
return to.TextResult(slim.Label(label))
|
||||
}
|
||||
|
||||
func deleteRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called deleteRepoLabelFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -269,7 +258,6 @@ func deleteRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
|
||||
}
|
||||
|
||||
func listOrgLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listOrgLabelsFn")
|
||||
org, err := params.GetString(req.GetArguments(), "org")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -290,11 +278,10 @@ func listOrgLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list %v/labels err: %v", org, err))
|
||||
}
|
||||
return to.TextResult(slimLabels(labels))
|
||||
return to.TextResult(slim.Labels(labels))
|
||||
}
|
||||
|
||||
func createOrgLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called createOrgLabelFn")
|
||||
org, err := params.GetString(req.GetArguments(), "org")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -325,11 +312,10 @@ func createOrgLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("create %v/labels err: %v", org, err))
|
||||
}
|
||||
return to.TextResult(slimLabel(label))
|
||||
return to.TextResult(slim.Label(label))
|
||||
}
|
||||
|
||||
func editOrgLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called editOrgLabelFn")
|
||||
org, err := params.GetString(req.GetArguments(), "org")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -339,18 +325,12 @@ func editOrgLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
opt := gitea_sdk.EditOrgLabelOption{}
|
||||
if name, ok := req.GetArguments()["name"].(string); ok {
|
||||
opt.Name = new(name)
|
||||
}
|
||||
if color, ok := req.GetArguments()["color"].(string); ok {
|
||||
opt.Color = new(color)
|
||||
}
|
||||
if description, ok := req.GetArguments()["description"].(string); ok {
|
||||
opt.Description = new(description)
|
||||
}
|
||||
if exclusive, ok := req.GetArguments()["exclusive"].(bool); ok {
|
||||
opt.Exclusive = new(exclusive)
|
||||
args := req.GetArguments()
|
||||
opt := gitea_sdk.EditOrgLabelOption{
|
||||
Name: params.GetOptionalStringPtr(args, "name"),
|
||||
Color: params.GetOptionalStringPtr(args, "color"),
|
||||
Description: params.GetPresentStringPtr(args, "description"),
|
||||
Exclusive: params.GetOptionalBoolPtr(args, "exclusive"),
|
||||
}
|
||||
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
@@ -361,11 +341,10 @@ func editOrgLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("edit %v/labels/%v err: %v", org, id, err))
|
||||
}
|
||||
return to.TextResult(slimLabel(label))
|
||||
return to.TextResult(slim.Label(label))
|
||||
}
|
||||
|
||||
func deleteOrgLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called deleteOrgLabelFn")
|
||||
org, err := params.GetString(req.GetArguments(), "org")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
|
||||
@@ -1,26 +1 @@
|
||||
package label
|
||||
|
||||
import (
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
)
|
||||
|
||||
func slimLabel(l *gitea_sdk.Label) map[string]any {
|
||||
if l == nil {
|
||||
return nil
|
||||
}
|
||||
return map[string]any{
|
||||
"id": l.ID,
|
||||
"name": l.Name,
|
||||
"color": l.Color,
|
||||
"description": l.Description,
|
||||
"exclusive": l.Exclusive,
|
||||
}
|
||||
}
|
||||
|
||||
func slimLabels(labels []*gitea_sdk.Label) []map[string]any {
|
||||
out := make([]map[string]any, 0, len(labels))
|
||||
for _, l := range labels {
|
||||
out = append(out, slimLabel(l))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package label
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
)
|
||||
|
||||
func TestSlimLabel(t *testing.T) {
|
||||
l := &gitea_sdk.Label{
|
||||
ID: 1,
|
||||
Name: "bug",
|
||||
Color: "#d73a4a",
|
||||
Description: "Something isn't working",
|
||||
Exclusive: false,
|
||||
}
|
||||
|
||||
m := slimLabel(l)
|
||||
if m["name"] != "bug" {
|
||||
t.Errorf("expected name bug, got %v", m["name"])
|
||||
}
|
||||
if m["color"] != "#d73a4a" {
|
||||
t.Errorf("expected color, got %v", m["color"])
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||
@@ -99,7 +98,6 @@ func milestoneWriteFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
||||
}
|
||||
|
||||
func getMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getMilestoneFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -125,7 +123,6 @@ func getMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
|
||||
}
|
||||
|
||||
func listMilestonesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listMilestonesFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -157,7 +154,6 @@ func listMilestonesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
||||
}
|
||||
|
||||
func createMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called createMilestoneFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -194,7 +190,6 @@ func createMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
|
||||
}
|
||||
|
||||
func editMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called editMilestoneFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -208,21 +203,18 @@ func editMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
opt := gitea_sdk.EditMilestoneOption{}
|
||||
|
||||
title, ok := req.GetArguments()["title"].(string)
|
||||
if ok {
|
||||
args := req.GetArguments()
|
||||
opt := gitea_sdk.EditMilestoneOption{
|
||||
Description: params.GetPresentStringPtr(args, "description"),
|
||||
Deadline: params.GetOptionalTime(args, "due_on"),
|
||||
}
|
||||
if title, ok := args["title"].(string); ok {
|
||||
opt.Title = title
|
||||
}
|
||||
description, ok := req.GetArguments()["description"].(string)
|
||||
if ok {
|
||||
opt.Description = new(description)
|
||||
if state, ok := args["state"].(string); ok {
|
||||
s := gitea_sdk.StateType(state)
|
||||
opt.State = &s
|
||||
}
|
||||
state, ok := req.GetArguments()["state"].(string)
|
||||
if ok {
|
||||
opt.State = new(gitea_sdk.StateType(state))
|
||||
}
|
||||
opt.Deadline = params.GetOptionalTime(req.GetArguments(), "due_on")
|
||||
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
@@ -237,7 +229,6 @@ func editMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
|
||||
}
|
||||
|
||||
func deleteMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called deleteMilestoneFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
|
||||
@@ -7,7 +7,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"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||
@@ -97,7 +96,6 @@ func notificationWriteFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
|
||||
}
|
||||
|
||||
func listNotificationsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listNotificationsFn")
|
||||
args := req.GetArguments()
|
||||
page, pageSize := params.GetPagination(args, 30)
|
||||
opt := gitea_sdk.ListNotificationOptions{
|
||||
@@ -142,7 +140,6 @@ func listNotificationsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
|
||||
}
|
||||
|
||||
func getNotificationFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getNotificationFn")
|
||||
id, err := params.GetIndex(req.GetArguments(), "id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -159,7 +156,6 @@ func getNotificationFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
|
||||
}
|
||||
|
||||
func markNotificationReadFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called markNotificationReadFn")
|
||||
id, err := params.GetIndex(req.GetArguments(), "id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -179,7 +175,6 @@ func markNotificationReadFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.
|
||||
}
|
||||
|
||||
func markAllNotificationsReadFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called markAllNotificationsReadFn")
|
||||
args := req.GetArguments()
|
||||
lastReadAt := time.Now()
|
||||
if t := params.GetOptionalTime(args, "last_read_at"); t != nil {
|
||||
|
||||
@@ -46,7 +46,6 @@ func RegisterTool(s *server.MCPServer) {
|
||||
for _, t := range domainTools {
|
||||
s.AddTools(t.Tools()...)
|
||||
}
|
||||
s.DeleteTools("")
|
||||
tool.WarnUnmatchedAllowedTools(domainTools...)
|
||||
}
|
||||
|
||||
@@ -97,7 +96,7 @@ func Run() error {
|
||||
case "http":
|
||||
httpServer := server.NewStreamableHTTPServer(
|
||||
mcpServer,
|
||||
server.WithLogger(log.New()),
|
||||
server.WithLogger(log.Default().Sugar()),
|
||||
server.WithHeartbeatInterval(30*time.Second),
|
||||
server.WithHTTPContextFunc(getContextWithToken),
|
||||
)
|
||||
|
||||
@@ -9,7 +9,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"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||
@@ -110,7 +109,6 @@ func escapePackageName(name string) string {
|
||||
}
|
||||
|
||||
func listPackagesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listPackagesFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -138,7 +136,6 @@ func listPackagesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
|
||||
}
|
||||
|
||||
func listPackageVersionsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listPackageVersionsFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -168,7 +165,6 @@ func listPackageVersionsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.C
|
||||
}
|
||||
|
||||
func getPackageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getPackageFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -197,7 +193,6 @@ func getPackageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe
|
||||
}
|
||||
|
||||
func deletePackageVersionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called deletePackageVersionFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
|
||||
+20
-77
@@ -10,6 +10,7 @@ import (
|
||||
"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"
|
||||
|
||||
@@ -261,7 +262,6 @@ func pullRequestReviewWriteFn(ctx context.Context, req mcp.CallToolRequest) (*mc
|
||||
}
|
||||
|
||||
func getPullRequestByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getPullRequestByIndexFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -293,12 +293,11 @@ func getPullRequestByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
|
||||
}
|
||||
|
||||
m := slimPullRequest(pr)
|
||||
m["body"] = bodyWithAttachments(pr.Body, assets)
|
||||
m["body"] = slim.BodyWithAttachments(pr.Body, assets)
|
||||
return to.TextResult(m)
|
||||
}
|
||||
|
||||
func getPullRequestDiffFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getPullRequestDiffFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -329,7 +328,6 @@ func getPullRequestDiffFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
|
||||
}
|
||||
|
||||
func listRepoPullRequestsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called ListRepoPullRequests")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -387,7 +385,6 @@ func applyDraftPrefix(title string, isDraft bool) string {
|
||||
}
|
||||
|
||||
func createPullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called createPullRequestFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -440,8 +437,9 @@ func createPullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
|
||||
return to.TextResult(slimPullRequest(pr))
|
||||
}
|
||||
|
||||
func createPullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called createPullRequestReviewerFn")
|
||||
type reviewerOp func(client *gitea_sdk.Client, owner, repo string, index int64, opt gitea_sdk.PullReviewRequestOptions) (*gitea_sdk.Response, error)
|
||||
|
||||
func pullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest, verb string, op reviewerOp) (*mcp.CallToolResult, error) {
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -464,70 +462,31 @@ func createPullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
|
||||
_, err = client.CreateReviewRequests(owner, repo, index, gitea_sdk.PullReviewRequestOptions{
|
||||
if _, err := op(client, owner, repo, index, gitea_sdk.PullReviewRequestOptions{
|
||||
Reviewers: reviewers,
|
||||
TeamReviewers: teamReviewers,
|
||||
})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("create review requests for %v/%v/pr/%v err: %v", owner, repo, index, err))
|
||||
}); err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("%s review requests for %v/%v/pr/%v err: %v", verb, owner, repo, index, err))
|
||||
}
|
||||
|
||||
successMsg := map[string]any{
|
||||
"message": "Successfully created review requests",
|
||||
return to.TextResult(map[string]any{
|
||||
"message": fmt.Sprintf("Successfully %sd review requests", verb),
|
||||
"reviewers": reviewers,
|
||||
"team_reviewers": teamReviewers,
|
||||
"pr_index": index,
|
||||
"repository": fmt.Sprintf("%s/%s", owner, repo),
|
||||
})
|
||||
}
|
||||
|
||||
return to.TextResult(successMsg)
|
||||
func createPullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
return pullRequestReviewerFn(ctx, req, "create", (*gitea_sdk.Client).CreateReviewRequests)
|
||||
}
|
||||
|
||||
func deletePullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called deletePullRequestReviewerFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
repo, err := params.GetString(args, "repo")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
index, err := params.GetIndex(args, "pull_number")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
|
||||
reviewers := params.GetStringSlice(args, "reviewers")
|
||||
teamReviewers := params.GetStringSlice(args, "team_reviewers")
|
||||
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
|
||||
_, err = client.DeleteReviewRequests(owner, repo, index, gitea_sdk.PullReviewRequestOptions{
|
||||
Reviewers: reviewers,
|
||||
TeamReviewers: teamReviewers,
|
||||
})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("delete review requests for %v/%v/pr/%v err: %v", owner, repo, index, err))
|
||||
}
|
||||
|
||||
successMsg := map[string]any{
|
||||
"message": "Successfully deleted review requests",
|
||||
"reviewers": reviewers,
|
||||
"team_reviewers": teamReviewers,
|
||||
"pr_index": index,
|
||||
"repository": fmt.Sprintf("%s/%s", owner, repo),
|
||||
}
|
||||
|
||||
return to.TextResult(successMsg)
|
||||
return pullRequestReviewerFn(ctx, req, "delete", (*gitea_sdk.Client).DeleteReviewRequests)
|
||||
}
|
||||
|
||||
func listPullRequestReviewsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listPullRequestReviewsFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -562,7 +521,6 @@ func listPullRequestReviewsFn(ctx context.Context, req mcp.CallToolRequest) (*mc
|
||||
}
|
||||
|
||||
func getPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getPullRequestReviewFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -595,7 +553,6 @@ func getPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.
|
||||
}
|
||||
|
||||
func listPullRequestReviewCommentsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listPullRequestReviewCommentsFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -628,7 +585,6 @@ func listPullRequestReviewCommentsFn(ctx context.Context, req mcp.CallToolReques
|
||||
}
|
||||
|
||||
func createPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called createPullRequestReviewFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -693,7 +649,6 @@ func createPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
||||
}
|
||||
|
||||
func submitPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called submitPullRequestReviewFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -737,7 +692,6 @@ func submitPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
||||
}
|
||||
|
||||
func deletePullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called deletePullRequestReviewFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -777,7 +731,6 @@ func deletePullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
||||
}
|
||||
|
||||
func dismissPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called dismissPullRequestReviewFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -822,7 +775,6 @@ func dismissPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*
|
||||
}
|
||||
|
||||
func mergePullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called mergePullRequestFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -886,7 +838,6 @@ func mergePullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
|
||||
}
|
||||
|
||||
func editPullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called editPullRequestFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -922,9 +873,10 @@ func editPullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
|
||||
}
|
||||
opt.Title = applyDraftPrefix(opt.Title, draft)
|
||||
}
|
||||
if body, ok := args["body"].(string); ok {
|
||||
opt.Body = new(body)
|
||||
}
|
||||
opt.Body = params.GetPresentStringPtr(args, "body")
|
||||
opt.AllowMaintainerEdit = params.GetOptionalBoolPtr(args, "allow_maintainer_edit")
|
||||
opt.RemoveDeadline = params.GetOptionalBoolPtr(args, "remove_deadline")
|
||||
opt.Deadline = params.GetOptionalTime(args, "deadline")
|
||||
if base, ok := args["base"].(string); ok {
|
||||
opt.Base = base
|
||||
}
|
||||
@@ -940,18 +892,12 @@ func editPullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
|
||||
}
|
||||
}
|
||||
if state, ok := args["state"].(string); ok {
|
||||
opt.State = new(gitea_sdk.StateType(state))
|
||||
}
|
||||
if allowMaintainerEdit, ok := args["allow_maintainer_edit"].(bool); ok {
|
||||
opt.AllowMaintainerEdit = new(allowMaintainerEdit)
|
||||
s := gitea_sdk.StateType(state)
|
||||
opt.State = &s
|
||||
}
|
||||
if labelIDs, err := params.GetInt64Slice(args, "labels"); err == nil {
|
||||
opt.Labels = labelIDs
|
||||
}
|
||||
opt.Deadline = params.GetOptionalTime(args, "deadline")
|
||||
if removeDeadline, ok := args["remove_deadline"].(bool); ok {
|
||||
opt.RemoveDeadline = &removeDeadline
|
||||
}
|
||||
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
@@ -967,7 +913,6 @@ func editPullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
|
||||
}
|
||||
|
||||
func updatePullRequestBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called updatePullRequestBranchFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -990,7 +935,6 @@ func updatePullRequestBranchFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
||||
}
|
||||
|
||||
func getPullRequestFilesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getPullRequestFilesFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -1019,7 +963,6 @@ func getPullRequestFilesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.C
|
||||
}
|
||||
|
||||
func getPullRequestStatusFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getPullRequestStatusFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
|
||||
+9
-61
@@ -1,63 +1,11 @@
|
||||
package pull
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/slim"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
)
|
||||
|
||||
func bodyWithAttachments(body string, atts []*gitea_sdk.Attachment) string {
|
||||
links := make([]string, 0, len(atts))
|
||||
for _, a := range atts {
|
||||
if a == nil || a.DownloadURL == "" {
|
||||
continue
|
||||
}
|
||||
links = append(links, fmt.Sprintf("[%s](%s)", a.Name, a.DownloadURL))
|
||||
}
|
||||
if len(links) == 0 {
|
||||
return body
|
||||
}
|
||||
joined := strings.Join(links, "\n")
|
||||
if body == "" {
|
||||
return joined
|
||||
}
|
||||
return body + "\n\n" + joined
|
||||
}
|
||||
|
||||
func userLogin(u *gitea_sdk.User) string {
|
||||
if u == nil {
|
||||
return ""
|
||||
}
|
||||
return u.UserName
|
||||
}
|
||||
|
||||
func userLogins(users []*gitea_sdk.User) []string {
|
||||
if len(users) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, len(users))
|
||||
for _, u := range users {
|
||||
if u != nil {
|
||||
out = append(out, u.UserName)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func labelNames(labels []*gitea_sdk.Label) []string {
|
||||
if len(labels) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, len(labels))
|
||||
for _, l := range labels {
|
||||
if l != nil {
|
||||
out = append(out, l.Name)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func repoRef(r *gitea_sdk.Repository) map[string]any {
|
||||
if r == nil {
|
||||
return nil
|
||||
@@ -81,8 +29,8 @@ func slimPullRequest(pr *gitea_sdk.PullRequest) map[string]any {
|
||||
"merged": pr.HasMerged,
|
||||
"mergeable": pr.Mergeable,
|
||||
"html_url": pr.HTMLURL,
|
||||
"user": userLogin(pr.Poster),
|
||||
"labels": labelNames(pr.Labels),
|
||||
"user": slim.UserLogin(pr.Poster),
|
||||
"labels": slim.LabelNames(pr.Labels),
|
||||
"comments": pr.Comments,
|
||||
"created_at": pr.Created,
|
||||
"updated_at": pr.Updated,
|
||||
@@ -91,7 +39,7 @@ func slimPullRequest(pr *gitea_sdk.PullRequest) map[string]any {
|
||||
if pr.HasMerged {
|
||||
m["merged_at"] = pr.Merged
|
||||
m["merge_commit_sha"] = pr.MergedCommitID
|
||||
m["merged_by"] = userLogin(pr.MergedBy)
|
||||
m["merged_by"] = slim.UserLogin(pr.MergedBy)
|
||||
}
|
||||
if pr.Head != nil {
|
||||
head := map[string]any{"ref": pr.Head.Ref, "sha": pr.Head.Sha}
|
||||
@@ -117,7 +65,7 @@ func slimPullRequest(pr *gitea_sdk.PullRequest) map[string]any {
|
||||
m["changed_files"] = *pr.ChangedFiles
|
||||
}
|
||||
if len(pr.Assignees) > 0 {
|
||||
m["assignees"] = userLogins(pr.Assignees)
|
||||
m["assignees"] = slim.UserLogins(pr.Assignees)
|
||||
}
|
||||
if pr.Milestone != nil {
|
||||
m["milestone"] = pr.Milestone.Title
|
||||
@@ -141,7 +89,7 @@ func slimPullRequests(prs []*gitea_sdk.PullRequest) []map[string]any {
|
||||
"draft": pr.Draft,
|
||||
"merged": pr.HasMerged,
|
||||
"html_url": pr.HTMLURL,
|
||||
"user": userLogin(pr.Poster),
|
||||
"user": slim.UserLogin(pr.Poster),
|
||||
"created_at": pr.Created,
|
||||
"updated_at": pr.Updated,
|
||||
}
|
||||
@@ -152,7 +100,7 @@ func slimPullRequests(prs []*gitea_sdk.PullRequest) []map[string]any {
|
||||
m["base"] = pr.Base.Ref
|
||||
}
|
||||
if len(pr.Labels) > 0 {
|
||||
m["labels"] = labelNames(pr.Labels)
|
||||
m["labels"] = slim.LabelNames(pr.Labels)
|
||||
}
|
||||
out = append(out, m)
|
||||
}
|
||||
@@ -167,7 +115,7 @@ func slimReview(r *gitea_sdk.PullReview) map[string]any {
|
||||
"id": r.ID,
|
||||
"state": r.State,
|
||||
"body": r.Body,
|
||||
"user": userLogin(r.Reviewer),
|
||||
"user": slim.UserLogin(r.Reviewer),
|
||||
"comments_count": r.CodeCommentsCount,
|
||||
"submitted_at": r.Submitted,
|
||||
"html_url": r.HTMLURL,
|
||||
@@ -196,7 +144,7 @@ func slimReviewComment(c *gitea_sdk.PullReviewComment) map[string]any {
|
||||
"position": c.LineNum,
|
||||
"old_position": c.OldLineNum,
|
||||
"diff_hunk": c.DiffHunk,
|
||||
"user": userLogin(c.Reviewer),
|
||||
"user": slim.UserLogin(c.Reviewer),
|
||||
"html_url": c.HTMLURL,
|
||||
"created_at": c.Created,
|
||||
"updated_at": c.Updated,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
@@ -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))
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+11
-25
@@ -7,8 +7,8 @@ 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/slim"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||
|
||||
@@ -94,7 +94,6 @@ func init() {
|
||||
}
|
||||
|
||||
func UsersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called UsersFn")
|
||||
keyword, err := params.GetString(req.GetArguments(), "query")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -119,7 +118,6 @@ func UsersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult,
|
||||
}
|
||||
|
||||
func OrgTeamsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called OrgTeamsFn")
|
||||
org, err := params.GetString(req.GetArguments(), "org")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -150,34 +148,23 @@ func OrgTeamsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResu
|
||||
}
|
||||
|
||||
func ReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called ReposFn")
|
||||
keyword, err := params.GetString(req.GetArguments(), "query")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
keywordIsTopic, _ := req.GetArguments()["keywordIsTopic"].(bool)
|
||||
keywordInDescription, _ := req.GetArguments()["keywordInDescription"].(bool)
|
||||
ownerID := params.GetOptionalInt(req.GetArguments(), "ownerID", 0)
|
||||
var pIsPrivate *bool
|
||||
isPrivate, ok := req.GetArguments()["isPrivate"].(bool)
|
||||
if ok {
|
||||
pIsPrivate = new(isPrivate)
|
||||
}
|
||||
var pIsArchived *bool
|
||||
isArchived, ok := req.GetArguments()["isArchived"].(bool)
|
||||
if ok {
|
||||
pIsArchived = new(isArchived)
|
||||
}
|
||||
sort, _ := req.GetArguments()["sort"].(string)
|
||||
order, _ := req.GetArguments()["order"].(string)
|
||||
page, pageSize := params.GetPagination(req.GetArguments(), 30)
|
||||
args := req.GetArguments()
|
||||
keywordIsTopic, _ := args["keywordIsTopic"].(bool)
|
||||
keywordInDescription, _ := args["keywordInDescription"].(bool)
|
||||
sort, _ := args["sort"].(string)
|
||||
order, _ := args["order"].(string)
|
||||
page, pageSize := params.GetPagination(args, 30)
|
||||
opt := gitea_sdk.SearchRepoOptions{
|
||||
Keyword: keyword,
|
||||
KeywordIsTopic: keywordIsTopic,
|
||||
KeywordInDescription: keywordInDescription,
|
||||
OwnerID: ownerID,
|
||||
IsPrivate: pIsPrivate,
|
||||
IsArchived: pIsArchived,
|
||||
OwnerID: params.GetOptionalInt(args, "ownerID", 0),
|
||||
IsPrivate: params.GetOptionalBoolPtr(args, "isPrivate"),
|
||||
IsArchived: params.GetOptionalBoolPtr(args, "isArchived"),
|
||||
Sort: sort,
|
||||
Order: order,
|
||||
ListOptions: gitea_sdk.ListOptions{
|
||||
@@ -193,11 +180,10 @@ func ReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult,
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("search repos error: %v", err))
|
||||
}
|
||||
return to.TextResult(slimRepos(repos))
|
||||
return to.TextResult(slim.Repos(repos))
|
||||
}
|
||||
|
||||
func IssuesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called IssuesFn")
|
||||
args := req.GetArguments()
|
||||
query, err := params.GetString(args, "query")
|
||||
if err != nil {
|
||||
|
||||
@@ -1,28 +1,15 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"gitea.com/gitea/gitea-mcp/pkg/slim"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
)
|
||||
|
||||
func slimUserDetail(u *gitea_sdk.User) map[string]any {
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
return map[string]any{
|
||||
"id": u.ID,
|
||||
"login": u.UserName,
|
||||
"full_name": u.FullName,
|
||||
"email": u.Email,
|
||||
"avatar_url": u.AvatarURL,
|
||||
"html_url": u.HTMLURL,
|
||||
"is_admin": u.IsAdmin,
|
||||
}
|
||||
}
|
||||
|
||||
func slimUserDetails(users []*gitea_sdk.User) []map[string]any {
|
||||
out := make([]map[string]any, 0, len(users))
|
||||
for _, u := range users {
|
||||
out = append(out, slimUserDetail(u))
|
||||
out = append(out, slim.UserDetail(u))
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -47,66 +34,6 @@ func slimTeams(teams []*gitea_sdk.Team) []map[string]any {
|
||||
return out
|
||||
}
|
||||
|
||||
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 userLogin(u *gitea_sdk.User) string {
|
||||
if u == nil {
|
||||
return ""
|
||||
}
|
||||
return u.UserName
|
||||
}
|
||||
|
||||
func labelNames(labels []*gitea_sdk.Label) []string {
|
||||
if len(labels) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, len(labels))
|
||||
for _, l := range labels {
|
||||
if l != nil {
|
||||
out = append(out, l.Name)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func slimIssues(issues []*gitea_sdk.Issue) []map[string]any {
|
||||
out := make([]map[string]any, 0, len(issues))
|
||||
for _, i := range issues {
|
||||
@@ -118,13 +45,13 @@ func slimIssues(issues []*gitea_sdk.Issue) []map[string]any {
|
||||
"title": i.Title,
|
||||
"state": i.State,
|
||||
"html_url": i.HTMLURL,
|
||||
"user": userLogin(i.Poster),
|
||||
"user": slim.UserLogin(i.Poster),
|
||||
"comments": i.Comments,
|
||||
"created_at": i.Created,
|
||||
"updated_at": i.Updated,
|
||||
}
|
||||
if len(i.Labels) > 0 {
|
||||
m["labels"] = labelNames(i.Labels)
|
||||
m["labels"] = slim.LabelNames(i.Labels)
|
||||
}
|
||||
if i.Repository != nil {
|
||||
m["repository"] = i.Repository.FullName
|
||||
|
||||
@@ -7,7 +7,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"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||
@@ -95,10 +94,7 @@ func writeFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult,
|
||||
}
|
||||
}
|
||||
|
||||
// Stopwatch handler functions
|
||||
|
||||
func startStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called startStopwatchFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -123,7 +119,6 @@ func startStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
||||
}
|
||||
|
||||
func stopStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called stopStopwatchFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -148,7 +143,6 @@ func stopStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
|
||||
}
|
||||
|
||||
func deleteStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called deleteStopwatchFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -173,7 +167,6 @@ func deleteStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
|
||||
}
|
||||
|
||||
func getMyStopwatchesFn(ctx context.Context, _ mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getMyStopwatchesFn")
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
@@ -188,10 +181,7 @@ func getMyStopwatchesFn(ctx context.Context, _ mcp.CallToolRequest) (*mcp.CallTo
|
||||
return to.TextResult(slimStopWatches(stopwatches))
|
||||
}
|
||||
|
||||
// Tracked time handler functions
|
||||
|
||||
func listTrackedTimesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listTrackedTimesFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -226,7 +216,6 @@ func listTrackedTimesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
|
||||
}
|
||||
|
||||
func addTrackedTimeFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called addTrackedTimeFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -258,7 +247,6 @@ func addTrackedTimeFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
||||
}
|
||||
|
||||
func deleteTrackedTimeFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called deleteTrackedTimeFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -288,7 +276,6 @@ func deleteTrackedTimeFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
|
||||
}
|
||||
|
||||
func listRepoTimesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listRepoTimesFn")
|
||||
owner, err := params.GetString(req.GetArguments(), "owner")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
@@ -319,7 +306,6 @@ func listRepoTimesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
|
||||
}
|
||||
|
||||
func getMyTimesFn(ctx context.Context, _ mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getMyTimesFn")
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
|
||||
@@ -4,21 +4,6 @@ import (
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
)
|
||||
|
||||
func slimUserDetail(u *gitea_sdk.User) map[string]any {
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
return map[string]any{
|
||||
"id": u.ID,
|
||||
"login": u.UserName,
|
||||
"full_name": u.FullName,
|
||||
"email": u.Email,
|
||||
"avatar_url": u.AvatarURL,
|
||||
"html_url": u.HTMLURL,
|
||||
"is_admin": u.IsAdmin,
|
||||
}
|
||||
}
|
||||
|
||||
func slimOrg(o *gitea_sdk.Organization) map[string]any {
|
||||
if o == nil {
|
||||
return nil
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
)
|
||||
|
||||
func TestSlimUserDetail(t *testing.T) {
|
||||
u := &gitea_sdk.User{
|
||||
ID: 42,
|
||||
UserName: "alice",
|
||||
FullName: "Alice Smith",
|
||||
Email: "alice@example.com",
|
||||
AvatarURL: "https://gitea.com/avatars/42",
|
||||
HTMLURL: "https://gitea.com/alice",
|
||||
IsAdmin: true,
|
||||
}
|
||||
m := slimUserDetail(u)
|
||||
|
||||
if m["id"] != int64(42) {
|
||||
t.Errorf("expected id 42, got %v", m["id"])
|
||||
}
|
||||
if m["login"] != "alice" {
|
||||
t.Errorf("expected login alice, got %v", m["login"])
|
||||
}
|
||||
if m["full_name"] != "Alice Smith" {
|
||||
t.Errorf("expected full_name Alice Smith, got %v", m["full_name"])
|
||||
}
|
||||
if m["is_admin"] != true {
|
||||
t.Errorf("expected is_admin true, got %v", m["is_admin"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestSlimUserDetail_Nil(t *testing.T) {
|
||||
if m := slimUserDetail(nil); m != nil {
|
||||
t.Errorf("expected nil for nil user, got %v", m)
|
||||
}
|
||||
}
|
||||
+7
-39
@@ -6,8 +6,8 @@ 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/slim"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||
|
||||
@@ -17,62 +17,34 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// GetMyUserInfoToolName is the unique tool name used for MCP registration and lookup of the get_me command.
|
||||
GetMyUserInfoToolName = "get_me"
|
||||
// GetUserOrgsToolName is the unique tool name used for MCP registration and lookup of the get_user_orgs command.
|
||||
GetUserOrgsToolName = "get_user_orgs"
|
||||
|
||||
// defaultPage is the default starting page number used for paginated organization listings.
|
||||
defaultPage = 1
|
||||
// defaultPageSize is the default number of organizations per page for paginated queries.
|
||||
defaultPageSize = 30
|
||||
)
|
||||
|
||||
// Tool is the MCP tool manager instance for registering all MCP tools in this package.
|
||||
var Tool = tool.New()
|
||||
|
||||
var (
|
||||
// GetMyUserInfoTool is the MCP tool for retrieving the current user's info.
|
||||
// It is registered with a specific name and a description string.
|
||||
GetMyUserInfoTool = mcp.NewTool(
|
||||
GetMyUserInfoToolName,
|
||||
mcp.WithDescription("Get current user"),
|
||||
mcp.WithToolAnnotation(annotation.ReadOnly("Get current user information")),
|
||||
)
|
||||
|
||||
// GetUserOrgsTool is the MCP tool for listing organizations for the authenticated user.
|
||||
// It supports pagination via "page" and "per_page" arguments with default values specified above.
|
||||
GetUserOrgsTool = mcp.NewTool(
|
||||
GetUserOrgsToolName,
|
||||
mcp.WithDescription("List current user's organizations"),
|
||||
mcp.WithToolAnnotation(annotation.ReadOnly("Get user organizations")),
|
||||
mcp.WithNumber("page", mcp.Description(params.PageDesc), mcp.DefaultNumber(defaultPage)),
|
||||
mcp.WithNumber("per_page", mcp.Description(params.PaginationDesc), mcp.DefaultNumber(defaultPageSize)),
|
||||
mcp.WithNumber("page", mcp.Description(params.PageDesc), mcp.DefaultNumber(1)),
|
||||
mcp.WithNumber("per_page", mcp.Description(params.PaginationDesc), mcp.DefaultNumber(30)),
|
||||
)
|
||||
)
|
||||
|
||||
// init registers all MCP tools in Tool at package initialization.
|
||||
// This function ensures the handler functions are registered before server usage.
|
||||
func init() {
|
||||
registerTools()
|
||||
Tool.RegisterRead(server.ServerTool{Tool: GetMyUserInfoTool, Handler: GetUserInfoFn})
|
||||
Tool.RegisterRead(server.ServerTool{Tool: GetUserOrgsTool, Handler: GetUserOrgsFn})
|
||||
}
|
||||
|
||||
// registerTools registers all local MCP tool definitions and their handler functions.
|
||||
// To add new functionality, append your tool/handler pair to the tools slice below.
|
||||
func registerTools() {
|
||||
tools := []server.ServerTool{
|
||||
{Tool: GetMyUserInfoTool, Handler: GetUserInfoFn},
|
||||
{Tool: GetUserOrgsTool, Handler: GetUserOrgsFn},
|
||||
}
|
||||
for _, t := range tools {
|
||||
Tool.RegisterRead(t)
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserInfoFn is the handler for "get_me" MCP tool requests.
|
||||
// Logs invocation, fetches current user info from gitea, wraps result for MCP.
|
||||
func GetUserInfoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("[User] Called GetUserInfoFn")
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
@@ -81,15 +53,11 @@ func GetUserInfoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get user info err: %v", err))
|
||||
}
|
||||
return to.TextResult(slimUserDetail(user))
|
||||
return to.TextResult(slim.UserDetail(user))
|
||||
}
|
||||
|
||||
// GetUserOrgsFn is the handler for "get_user_orgs" MCP tool requests.
|
||||
// Logs invocation, pulls validated pagination arguments from request,
|
||||
// performs Gitea organization listing, and wraps the result for MCP.
|
||||
func GetUserOrgsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("[User] Called GetUserOrgsFn")
|
||||
page, pageSize := params.GetPagination(req.GetArguments(), defaultPageSize)
|
||||
page, pageSize := params.GetPagination(req.GetArguments(), 30)
|
||||
|
||||
opt := gitea_sdk.ListOrgsOptions{
|
||||
ListOptions: gitea_sdk.ListOptions{
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"gitea.com/gitea/gitea-mcp/pkg/annotation"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/flag"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/log"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||
|
||||
@@ -33,7 +32,6 @@ func init() {
|
||||
}
|
||||
|
||||
func GetGiteaMCPServerVersionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called GetGiteaMCPServerVersionFn")
|
||||
version := flag.Version
|
||||
if version == "" {
|
||||
version = "dev"
|
||||
|
||||
@@ -8,7 +8,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"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||
@@ -95,7 +94,6 @@ func wikiWriteFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
|
||||
}
|
||||
|
||||
func listWikiPagesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called listWikiPagesFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -116,7 +114,6 @@ func listWikiPagesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
|
||||
}
|
||||
|
||||
func getWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getWikiPageFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -141,7 +138,6 @@ func getWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
|
||||
}
|
||||
|
||||
func getWikiRevisionsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called getWikiRevisionsFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -166,7 +162,6 @@ func getWikiRevisionsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
|
||||
}
|
||||
|
||||
func createWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called createWikiPageFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -206,7 +201,6 @@ func createWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
||||
}
|
||||
|
||||
func updateWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called updateWikiPageFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
@@ -252,7 +246,6 @@ func updateWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
||||
}
|
||||
|
||||
func deleteWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
log.Debugf("Called deleteWikiPageFn")
|
||||
args := req.GetArguments()
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
// Package annotation provides shared MCP tool annotation helpers.
|
||||
package annotation
|
||||
|
||||
import "github.com/mark3labs/mcp-go/mcp"
|
||||
|
||||
// ReadOnly returns a ToolAnnotation for read-only tools.
|
||||
func ReadOnly(title string) mcp.ToolAnnotation {
|
||||
t := true
|
||||
return mcp.ToolAnnotation{Title: title, ReadOnlyHint: &t}
|
||||
}
|
||||
|
||||
// Write returns a ToolAnnotation for write tools.
|
||||
func Write(title string) mcp.ToolAnnotation {
|
||||
f := false
|
||||
return mcp.ToolAnnotation{Title: title, ReadOnlyHint: &f}
|
||||
}
|
||||
|
||||
// Destructive returns a ToolAnnotation for destructive write tools.
|
||||
func Destructive(title string) mcp.ToolAnnotation {
|
||||
f, t := false, true
|
||||
return mcp.ToolAnnotation{Title: title, ReadOnlyHint: &f, DestructiveHint: &t}
|
||||
|
||||
+32
-13
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
mcpContext "gitea.com/gitea/gitea-mcp/pkg/context"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/flag"
|
||||
@@ -13,21 +14,39 @@ import (
|
||||
"code.gitea.io/sdk/gitea"
|
||||
)
|
||||
|
||||
func NewClient(token string) (*gitea.Client, error) {
|
||||
httpClient := &http.Client{
|
||||
Transport: http.DefaultTransport,
|
||||
CheckRedirect: checkRedirect,
|
||||
var (
|
||||
clientCache sync.Map // token -> *gitea.Client
|
||||
sharedTransOnce sync.Once
|
||||
sharedTrans *http.Transport
|
||||
)
|
||||
|
||||
func sharedTransport() *http.Transport {
|
||||
sharedTransOnce.Do(func() {
|
||||
sharedTrans = http.DefaultTransport.(*http.Transport).Clone()
|
||||
if flag.Insecure {
|
||||
sharedTrans.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} //nolint:gosec // user-requested insecure mode
|
||||
}
|
||||
})
|
||||
return sharedTrans
|
||||
}
|
||||
|
||||
// NewClient returns a cached *gitea.Client keyed by host+token. The SDK's per-client
|
||||
// version cache and the shared transport let us reuse keep-alive connections
|
||||
// and avoid the SDK's /api/v1/version preflight on every tool call.
|
||||
func NewClient(token string) (*gitea.Client, error) {
|
||||
key := flag.Host + "\x00" + token
|
||||
if v, ok := clientCache.Load(key); ok {
|
||||
return v.(*gitea.Client), nil
|
||||
}
|
||||
|
||||
httpClient := &http.Client{
|
||||
Transport: sharedTransport(),
|
||||
CheckRedirect: checkRedirect,
|
||||
}
|
||||
opts := []gitea.ClientOption{
|
||||
gitea.SetToken(token),
|
||||
gitea.SetHTTPClient(httpClient),
|
||||
}
|
||||
if flag.Insecure {
|
||||
httpClient.Transport.(*http.Transport).TLSClientConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
}
|
||||
opts = append(opts, gitea.SetHTTPClient(httpClient))
|
||||
if flag.Debug {
|
||||
opts = append(opts, gitea.SetDebugMode())
|
||||
}
|
||||
@@ -35,10 +54,10 @@ func NewClient(token string) (*gitea.Client, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create gitea client err: %w", err)
|
||||
}
|
||||
|
||||
// Set user agent for the client
|
||||
client.SetUserAgent("gitea-mcp-server/" + flag.Version)
|
||||
return client, nil
|
||||
|
||||
actual, _ := clientCache.LoadOrStore(key, client)
|
||||
return actual.(*gitea.Client), nil
|
||||
}
|
||||
|
||||
// checkRedirect prevents Go from silently changing mutating requests (POST, PATCH, etc.)
|
||||
|
||||
+23
-14
@@ -3,7 +3,6 @@ package gitea
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -11,12 +10,18 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
mcpContext "gitea.com/gitea/gitea-mcp/pkg/context"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/flag"
|
||||
)
|
||||
|
||||
const (
|
||||
httpClientTimeout = 60 * time.Second
|
||||
errBodySnippetSize = 8192
|
||||
)
|
||||
|
||||
type HTTPError struct {
|
||||
StatusCode int
|
||||
Body string
|
||||
@@ -38,16 +43,20 @@ func tokenFromContext(ctx context.Context) string {
|
||||
return flag.Token
|
||||
}
|
||||
|
||||
func newRESTHTTPClient() *http.Client {
|
||||
transport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
if flag.Insecure {
|
||||
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} //nolint:gosec // user-requested insecure mode
|
||||
}
|
||||
return &http.Client{
|
||||
Transport: transport,
|
||||
Timeout: 60 * time.Second,
|
||||
var (
|
||||
restClientOnce sync.Once
|
||||
restClient *http.Client
|
||||
)
|
||||
|
||||
func restHTTPClient() *http.Client {
|
||||
restClientOnce.Do(func() {
|
||||
restClient = &http.Client{
|
||||
Transport: sharedTransport(),
|
||||
Timeout: httpClientTimeout,
|
||||
CheckRedirect: checkRedirect,
|
||||
}
|
||||
})
|
||||
return restClient
|
||||
}
|
||||
|
||||
func buildAPIURL(path string, query url.Values) (string, error) {
|
||||
@@ -96,7 +105,7 @@ func DoJSON(ctx context.Context, method, path string, query url.Values, body, re
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
client := newRESTHTTPClient()
|
||||
client := restHTTPClient()
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("do request: %w", err)
|
||||
@@ -104,7 +113,7 @@ func DoJSON(ctx context.Context, method, path string, query url.Values, body, re
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
bodySnippet, _ := io.ReadAll(io.LimitReader(resp.Body, 8192))
|
||||
bodySnippet, _ := io.ReadAll(io.LimitReader(resp.Body, errBodySnippetSize))
|
||||
return resp.StatusCode, &HTTPError{StatusCode: resp.StatusCode, Body: strings.TrimSpace(string(bodySnippet))}
|
||||
}
|
||||
|
||||
@@ -151,7 +160,7 @@ func DoBytes(ctx context.Context, method, path string, query url.Values, body an
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
client := newRESTHTTPClient()
|
||||
client := restHTTPClient()
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("do request: %w", err)
|
||||
@@ -165,8 +174,8 @@ func DoBytes(ctx context.Context, method, path string, query url.Values, body an
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
bodySnippet := respBytes
|
||||
if len(bodySnippet) > 8192 {
|
||||
bodySnippet = bodySnippet[:8192]
|
||||
if len(bodySnippet) > errBodySnippetSize {
|
||||
bodySnippet = bodySnippet[:errBodySnippetSize]
|
||||
}
|
||||
return nil, resp.StatusCode, &HTTPError{StatusCode: resp.StatusCode, Body: strings.TrimSpace(string(bodySnippet))}
|
||||
}
|
||||
|
||||
@@ -79,24 +79,6 @@ func SetDefault(logger *zap.Logger) {
|
||||
}
|
||||
}
|
||||
|
||||
func New() *Logger {
|
||||
return &Logger{
|
||||
defaultLogger: Default(),
|
||||
}
|
||||
}
|
||||
|
||||
type Logger struct {
|
||||
defaultLogger *zap.Logger
|
||||
}
|
||||
|
||||
func (l *Logger) Infof(msg string, args ...any) {
|
||||
l.defaultLogger.Sugar().Infof(msg, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Errorf(msg string, args ...any) {
|
||||
l.defaultLogger.Sugar().Errorf(msg, args...)
|
||||
}
|
||||
|
||||
func Debug(msg string, fields ...zap.Field) {
|
||||
Default().Debug(msg, fields...)
|
||||
}
|
||||
|
||||
+33
-16
@@ -16,16 +16,15 @@ const (
|
||||
PaginationDesc = "results per page"
|
||||
)
|
||||
|
||||
// GetString extracts a required string parameter from MCP tool arguments.
|
||||
// GetString extracts a required string parameter. Empty strings are treated as missing.
|
||||
func GetString(args map[string]any, key string) (string, error) {
|
||||
val, ok := args[key].(string)
|
||||
if !ok {
|
||||
if !ok || val == "" {
|
||||
return "", fmt.Errorf("%s is required", key)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// GetOptionalString extracts an optional string parameter with a default value.
|
||||
func GetOptionalString(args map[string]any, key, defaultVal string) string {
|
||||
if val, ok := args[key].(string); ok {
|
||||
return val
|
||||
@@ -33,7 +32,6 @@ func GetOptionalString(args map[string]any, key, defaultVal string) string {
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
// GetStringSlice extracts an optional string slice parameter from MCP tool arguments.
|
||||
func GetStringSlice(args map[string]any, key string) []string {
|
||||
val, ok := args[key]
|
||||
if !ok {
|
||||
@@ -52,13 +50,11 @@ func GetStringSlice(args map[string]any, key string) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// GetPagination extracts page and per_page parameters, returning them as ints.
|
||||
func GetPagination(args map[string]any, defaultPageSize int64) (page, pageSize int) {
|
||||
return int(GetOptionalInt(args, "page", 1)), int(GetOptionalInt(args, "per_page", defaultPageSize))
|
||||
}
|
||||
|
||||
// ToInt64 converts a value to int64, accepting both float64 (JSON number) and
|
||||
// string representations. Returns false if the value cannot be converted.
|
||||
// ToInt64 accepts float64 (JSON number) and string representations.
|
||||
func ToInt64(val any) (int64, bool) {
|
||||
switch v := val.(type) {
|
||||
case float64:
|
||||
@@ -74,10 +70,8 @@ func ToInt64(val any) (int64, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetIndex extracts a required integer parameter from MCP tool arguments.
|
||||
// It accepts both numeric (float64 from JSON) and string representations.
|
||||
// This provides better UX for LLM callers that may naturally use strings
|
||||
// for identifiers like issue/PR numbers.
|
||||
// GetIndex extracts a required integer. Accepts numeric or string forms — LLM callers
|
||||
// often pass identifiers like issue/PR numbers as strings.
|
||||
func GetIndex(args map[string]any, key string) (int64, error) {
|
||||
val, exists := args[key]
|
||||
if !exists {
|
||||
@@ -95,7 +89,6 @@ func GetIndex(args map[string]any, key string) (int64, error) {
|
||||
return 0, fmt.Errorf("%s must be a number or numeric string", key)
|
||||
}
|
||||
|
||||
// GetInt64Slice extracts a required int64 slice parameter from MCP tool arguments.
|
||||
func GetInt64Slice(args map[string]any, key string) ([]int64, error) {
|
||||
raw, ok := args[key].([]any)
|
||||
if !ok {
|
||||
@@ -112,7 +105,7 @@ func GetInt64Slice(args map[string]any, key string) ([]int64, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GetOptionalTime extracts an optional RFC3339 timestamp parameter, returning nil if missing or unparseable.
|
||||
// GetOptionalTime parses RFC3339, returning nil if missing or unparseable.
|
||||
func GetOptionalTime(args map[string]any, key string) *time.Time {
|
||||
val, ok := args[key].(string)
|
||||
if !ok {
|
||||
@@ -124,9 +117,6 @@ func GetOptionalTime(args map[string]any, key string) *time.Time {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetOptionalInt extracts an optional integer parameter from MCP tool arguments.
|
||||
// Returns defaultVal if the key is missing or the value cannot be parsed.
|
||||
// Accepts both float64 (JSON number) and string representations.
|
||||
func GetOptionalInt(args map[string]any, key string, defaultVal int64) int64 {
|
||||
val, exists := args[key]
|
||||
if !exists {
|
||||
@@ -137,3 +127,30 @@ func GetOptionalInt(args map[string]any, key string, defaultVal int64) int64 {
|
||||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
// GetOptionalBoolPtr is for SDK fields where nil/false/true are distinct (e.g. "no change" vs "set to false").
|
||||
func GetOptionalBoolPtr(args map[string]any, key string) *bool {
|
||||
if v, ok := args[key].(bool); ok {
|
||||
return &v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetOptionalStringPtr returns nil when the key is missing OR the value is an empty string.
|
||||
// Use this for create/fork-style fields where "" is meaningless (e.g. fork target name).
|
||||
func GetOptionalStringPtr(args map[string]any, key string) *string {
|
||||
if v, ok := args[key].(string); ok && v != "" {
|
||||
return &v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPresentStringPtr returns &v whenever the key is present as a string, including "".
|
||||
// Use this for PATCH-style fields where the SDK distinguishes "no change" (nil) from
|
||||
// "set to empty" (&""), e.g. clearing an issue body or label description.
|
||||
func GetPresentStringPtr(args map[string]any, key string) *string {
|
||||
if v, ok := args[key].(string); ok {
|
||||
return &v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -73,6 +73,42 @@ func TestGetOptionalInt(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOptionalStringPtr(t *testing.T) {
|
||||
if p := GetOptionalStringPtr(map[string]any{}, "k"); p != nil {
|
||||
t.Errorf("missing key: got %v, want nil", p)
|
||||
}
|
||||
if p := GetOptionalStringPtr(map[string]any{"k": ""}, "k"); p != nil {
|
||||
t.Errorf("empty string: got %v, want nil", p)
|
||||
}
|
||||
if p := GetOptionalStringPtr(map[string]any{"k": 42}, "k"); p != nil {
|
||||
t.Errorf("non-string: got %v, want nil", p)
|
||||
}
|
||||
if p := GetOptionalStringPtr(map[string]any{"k": nil}, "k"); p != nil {
|
||||
t.Errorf("nil value (JSON null): got %v, want nil", p)
|
||||
}
|
||||
if p := GetOptionalStringPtr(map[string]any{"k": "x"}, "k"); p == nil || *p != "x" {
|
||||
t.Errorf("non-empty: got %v, want &\"x\"", p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPresentStringPtr(t *testing.T) {
|
||||
if p := GetPresentStringPtr(map[string]any{}, "k"); p != nil {
|
||||
t.Errorf("missing key: got %v, want nil", p)
|
||||
}
|
||||
if p := GetPresentStringPtr(map[string]any{"k": 42}, "k"); p != nil {
|
||||
t.Errorf("non-string: got %v, want nil", p)
|
||||
}
|
||||
if p := GetPresentStringPtr(map[string]any{"k": nil}, "k"); p != nil {
|
||||
t.Errorf("nil value (JSON null): got %v, want nil", p)
|
||||
}
|
||||
if p := GetPresentStringPtr(map[string]any{"k": ""}, "k"); p == nil || *p != "" {
|
||||
t.Errorf("empty string: got %v, want &\"\"", p)
|
||||
}
|
||||
if p := GetPresentStringPtr(map[string]any{"k": "x"}, "k"); p == nil || *p != "x" {
|
||||
t.Errorf("non-empty: got %v, want &\"x\"", p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIndex(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package slim
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
)
|
||||
|
||||
func UserLogin(u *gitea_sdk.User) string {
|
||||
if u == nil {
|
||||
return ""
|
||||
}
|
||||
return u.UserName
|
||||
}
|
||||
|
||||
func UserLogins(users []*gitea_sdk.User) []string {
|
||||
if len(users) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, len(users))
|
||||
for _, u := range users {
|
||||
if u != nil {
|
||||
out = append(out, u.UserName)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func LabelNames(labels []*gitea_sdk.Label) []string {
|
||||
if len(labels) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, len(labels))
|
||||
for _, l := range labels {
|
||||
if l != nil {
|
||||
out = append(out, l.Name)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func BodyWithAttachments(body string, atts []*gitea_sdk.Attachment) string {
|
||||
links := make([]string, 0, len(atts))
|
||||
for _, a := range atts {
|
||||
if a == nil || a.DownloadURL == "" {
|
||||
continue
|
||||
}
|
||||
links = append(links, fmt.Sprintf("[%s](%s)", a.Name, a.DownloadURL))
|
||||
}
|
||||
if len(links) == 0 {
|
||||
return body
|
||||
}
|
||||
joined := strings.Join(links, "\n")
|
||||
if body == "" {
|
||||
return joined
|
||||
}
|
||||
return body + "\n\n" + joined
|
||||
}
|
||||
|
||||
func UserDetail(u *gitea_sdk.User) map[string]any {
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
return map[string]any{
|
||||
"id": u.ID,
|
||||
"login": u.UserName,
|
||||
"full_name": u.FullName,
|
||||
"email": u.Email,
|
||||
"avatar_url": u.AvatarURL,
|
||||
"html_url": u.HTMLURL,
|
||||
"is_admin": u.IsAdmin,
|
||||
}
|
||||
}
|
||||
|
||||
func Repo(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 Repos(repos []*gitea_sdk.Repository) []map[string]any {
|
||||
out := make([]map[string]any, 0, len(repos))
|
||||
for _, r := range repos {
|
||||
out = append(out, Repo(r))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func Label(l *gitea_sdk.Label) map[string]any {
|
||||
if l == nil {
|
||||
return nil
|
||||
}
|
||||
return map[string]any{
|
||||
"id": l.ID,
|
||||
"name": l.Name,
|
||||
"color": l.Color,
|
||||
"description": l.Description,
|
||||
"exclusive": l.Exclusive,
|
||||
}
|
||||
}
|
||||
|
||||
func Labels(labels []*gitea_sdk.Label) []map[string]any {
|
||||
out := make([]map[string]any, 0, len(labels))
|
||||
for _, l := range labels {
|
||||
out = append(out, Label(l))
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package slim
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
)
|
||||
|
||||
func TestUserDetail(t *testing.T) {
|
||||
u := &gitea_sdk.User{
|
||||
ID: 42,
|
||||
UserName: "alice",
|
||||
FullName: "Alice Smith",
|
||||
Email: "alice@example.com",
|
||||
AvatarURL: "https://gitea.com/avatars/42",
|
||||
HTMLURL: "https://gitea.com/alice",
|
||||
IsAdmin: true,
|
||||
}
|
||||
m := UserDetail(u)
|
||||
|
||||
if m["id"] != int64(42) {
|
||||
t.Errorf("expected id 42, got %v", m["id"])
|
||||
}
|
||||
if m["login"] != "alice" {
|
||||
t.Errorf("expected login alice, got %v", m["login"])
|
||||
}
|
||||
if m["full_name"] != "Alice Smith" {
|
||||
t.Errorf("expected full_name Alice Smith, got %v", m["full_name"])
|
||||
}
|
||||
if m["is_admin"] != true {
|
||||
t.Errorf("expected is_admin true, got %v", m["is_admin"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserDetail_Nil(t *testing.T) {
|
||||
if m := UserDetail(nil); m != nil {
|
||||
t.Errorf("expected nil for nil user, got %v", m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLabel(t *testing.T) {
|
||||
l := &gitea_sdk.Label{
|
||||
ID: 1,
|
||||
Name: "bug",
|
||||
Color: "#d73a4a",
|
||||
Description: "Something isn't working",
|
||||
Exclusive: false,
|
||||
}
|
||||
|
||||
m := Label(l)
|
||||
if m["name"] != "bug" {
|
||||
t.Errorf("expected name bug, got %v", m["name"])
|
||||
}
|
||||
if m["color"] != "#d73a4a" {
|
||||
t.Errorf("expected color, got %v", m["color"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepo(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",
|
||||
Language: "Go",
|
||||
Stars: 10,
|
||||
Forks: 2,
|
||||
Owner: &gitea_sdk.User{UserName: "org"},
|
||||
Topics: []string{"mcp", "gitea"},
|
||||
}
|
||||
|
||||
m := Repo(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 TestBodyWithAttachments(t *testing.T) {
|
||||
atts := []*gitea_sdk.Attachment{
|
||||
{Name: "shot.png", DownloadURL: "https://example/shot.png"},
|
||||
{Name: "log.txt", DownloadURL: "https://example/log.txt"},
|
||||
}
|
||||
got := BodyWithAttachments("see attached", atts)
|
||||
want := "see attached\n\n[shot.png](https://example/shot.png)\n[log.txt](https://example/log.txt)"
|
||||
if got != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
|
||||
if got := BodyWithAttachments("only body", nil); got != "only body" {
|
||||
t.Errorf("nil attachments should return body unchanged, got %q", got)
|
||||
}
|
||||
if got := BodyWithAttachments("", atts); got != "[shot.png](https://example/shot.png)\n[log.txt](https://example/log.txt)" {
|
||||
t.Errorf("empty body should drop separator, got %q", got)
|
||||
}
|
||||
skipped := []*gitea_sdk.Attachment{nil, {Name: "noop", DownloadURL: ""}}
|
||||
if got := BodyWithAttachments("body", skipped); got != "body" {
|
||||
t.Errorf("nil/empty-URL attachments should be skipped, got %q", got)
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"gitea.com/gitea/gitea-mcp/pkg/flag"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/log"
|
||||
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
@@ -14,11 +15,13 @@ func TextResult(v any) (*mcp.CallToolResult, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal result err: %v", err)
|
||||
}
|
||||
if flag.Debug {
|
||||
log.Debugf("Text Result: %s", string(resultBytes))
|
||||
}
|
||||
return mcp.NewToolResultText(string(resultBytes)), nil
|
||||
}
|
||||
|
||||
func ErrorResult(err error) (*mcp.CallToolResult, error) {
|
||||
log.Errorf(err.Error())
|
||||
log.Errorf("%s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user