mirror of
https://gitea.com/gitea/gitea-mcp.git
synced 2026-02-27 17:15:13 +00:00
Compare commits
3 Commits
feat/accep
...
v0.8.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d5fa3ab2c | ||
|
|
21e4e1b42b | ||
|
|
4aacfe348a |
@@ -221,6 +221,7 @@ The Gitea MCP Server supports the following tools:
|
|||||||
| submit_pull_request_review | Pull Request | Submit a pending review |
|
| submit_pull_request_review | Pull Request | Submit a pending review |
|
||||||
| delete_pull_request_review | Pull Request | Delete a review |
|
| delete_pull_request_review | Pull Request | Delete a review |
|
||||||
| dismiss_pull_request_review | Pull Request | Dismiss a review with optional message |
|
| dismiss_pull_request_review | Pull Request | Dismiss a review with optional message |
|
||||||
|
| merge_pull_request | Pull Request | Merge a pull request |
|
||||||
| search_users | User | Search for users |
|
| search_users | User | Search for users |
|
||||||
| search_org_teams | Organization | Search for teams in an organization |
|
| search_org_teams | Organization | Search for teams in an organization |
|
||||||
| list_org_labels | Organization | List labels defined at organization level |
|
| list_org_labels | Organization | List labels defined at organization level |
|
||||||
|
|||||||
@@ -220,6 +220,7 @@ Gitea MCP 服务器支持以下工具:
|
|||||||
| submit_pull_request_review | 拉取请求 | 提交待处理的审查 |
|
| submit_pull_request_review | 拉取请求 | 提交待处理的审查 |
|
||||||
| delete_pull_request_review | 拉取请求 | 删除审查 |
|
| delete_pull_request_review | 拉取请求 | 删除审查 |
|
||||||
| dismiss_pull_request_review | 拉取请求 | 驳回审查(可附消息) |
|
| dismiss_pull_request_review | 拉取请求 | 驳回审查(可附消息) |
|
||||||
|
| merge_pull_request | 拉取请求 | 合并拉取请求 |
|
||||||
| search_users | 用户 | 搜索用户 |
|
| search_users | 用户 | 搜索用户 |
|
||||||
| search_org_teams | 组织 | 搜索组织团队 |
|
| search_org_teams | 组织 | 搜索组织团队 |
|
||||||
| list_org_labels | 组织 | 列出组织标签 |
|
| list_org_labels | 组织 | 列出组织标签 |
|
||||||
|
|||||||
@@ -220,6 +220,7 @@ Gitea MCP 伺服器支援以下工具:
|
|||||||
| submit_pull_request_review | 拉取請求 | 提交待處理的審查 |
|
| submit_pull_request_review | 拉取請求 | 提交待處理的審查 |
|
||||||
| delete_pull_request_review | 拉取請求 | 刪除審查 |
|
| delete_pull_request_review | 拉取請求 | 刪除審查 |
|
||||||
| dismiss_pull_request_review | 拉取請求 | 駁回審查(可附訊息) |
|
| dismiss_pull_request_review | 拉取請求 | 駁回審查(可附訊息) |
|
||||||
|
| merge_pull_request | 拉取請求 | 合併拉取請求 |
|
||||||
| search_users | 用戶 | 搜尋用戶 |
|
| search_users | 用戶 | 搜尋用戶 |
|
||||||
| search_org_teams | 組織 | 搜尋組織團隊 |
|
| search_org_teams | 組織 | 搜尋組織團隊 |
|
||||||
| list_org_labels | 組織 | 列出組織標籤 |
|
| list_org_labels | 組織 | 列出組織標籤 |
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/log"
|
"gitea.com/gitea/gitea-mcp/pkg/log"
|
||||||
|
"gitea.com/gitea/gitea-mcp/pkg/params"
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/ptr"
|
"gitea.com/gitea/gitea-mcp/pkg/ptr"
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||||
@@ -136,17 +137,17 @@ func GetIssueByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
client, err := gitea.ClientFromContext(ctx)
|
client, err := gitea.ClientFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
issue, _, err := client.GetIssue(owner, repo, int64(index))
|
issue, _, err := client.GetIssue(owner, repo, index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("get %v/%v/issue/%v err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return to.TextResult(issue)
|
return to.TextResult(issue)
|
||||||
@@ -235,9 +236,9 @@ func CreateIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
body, ok := req.GetArguments()["body"].(string)
|
body, ok := req.GetArguments()["body"].(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -250,9 +251,9 @@ func CreateIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
issueComment, _, err := client.CreateIssueComment(owner, repo, int64(index), opt)
|
issueComment, _, err := client.CreateIssueComment(owner, repo, index, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("create %v/%v/issue/%v/comment err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("create %v/%v/issue/%v/comment err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return to.TextResult(issueComment)
|
return to.TextResult(issueComment)
|
||||||
@@ -268,9 +269,9 @@ func EditIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
opt := gitea_sdk.EditIssueOption{}
|
opt := gitea_sdk.EditIssueOption{}
|
||||||
@@ -307,9 +308,9 @@ func EditIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
issue, _, err := client.EditIssue(owner, repo, int64(index), opt)
|
issue, _, err := client.EditIssue(owner, repo, index, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("edit %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("edit %v/%v/issue/%v err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return to.TextResult(issue)
|
return to.TextResult(issue)
|
||||||
@@ -358,18 +359,18 @@ func GetIssueCommentsByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
opt := gitea_sdk.ListIssueCommentOptions{}
|
opt := gitea_sdk.ListIssueCommentOptions{}
|
||||||
client, err := gitea.ClientFromContext(ctx)
|
client, err := gitea.ClientFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
issue, _, err := client.ListIssueComments(owner, repo, int64(index), opt)
|
issue, _, err := client.ListIssueComments(owner, repo, index, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get %v/%v/issues/%v/comments err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("get %v/%v/issues/%v/comments err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return to.TextResult(issue)
|
return to.TextResult(issue)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/log"
|
"gitea.com/gitea/gitea-mcp/pkg/log"
|
||||||
|
"gitea.com/gitea/gitea-mcp/pkg/params"
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/ptr"
|
"gitea.com/gitea/gitea-mcp/pkg/ptr"
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||||
@@ -380,9 +381,9 @@ func AddIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("issue index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
labelsRaw, ok := req.GetArguments()["labels"].([]interface{})
|
labelsRaw, ok := req.GetArguments()["labels"].([]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -405,9 +406,9 @@ func AddIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
issueLabels, _, err := client.AddIssueLabels(owner, repo, int64(index), opt)
|
issueLabels, _, err := client.AddIssueLabels(owner, repo, index, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("add labels to %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("add labels to %v/%v/issue/%v err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
return to.TextResult(issueLabels)
|
return to.TextResult(issueLabels)
|
||||||
}
|
}
|
||||||
@@ -422,9 +423,9 @@ func ReplaceIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("issue index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
labelsRaw, ok := req.GetArguments()["labels"].([]interface{})
|
labelsRaw, ok := req.GetArguments()["labels"].([]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -447,9 +448,9 @@ func ReplaceIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
issueLabels, _, err := client.ReplaceIssueLabels(owner, repo, int64(index), opt)
|
issueLabels, _, err := client.ReplaceIssueLabels(owner, repo, index, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("replace labels on %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("replace labels on %v/%v/issue/%v err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
return to.TextResult(issueLabels)
|
return to.TextResult(issueLabels)
|
||||||
}
|
}
|
||||||
@@ -464,18 +465,18 @@ func ClearIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("issue index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := gitea.ClientFromContext(ctx)
|
client, err := gitea.ClientFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
_, err = client.ClearIssueLabels(owner, repo, int64(index))
|
_, err = client.ClearIssueLabels(owner, repo, index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("clear labels on %v/%v/issue/%v err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("clear labels on %v/%v/issue/%v err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
return to.TextResult("Labels cleared successfully")
|
return to.TextResult("Labels cleared successfully")
|
||||||
}
|
}
|
||||||
@@ -490,9 +491,9 @@ func RemoveIssueLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("issue index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
labelID, ok := req.GetArguments()["label_id"].(float64)
|
labelID, ok := req.GetArguments()["label_id"].(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -503,9 +504,9 @@ func RemoveIssueLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
_, err = client.DeleteIssueLabel(owner, repo, int64(index), int64(labelID))
|
_, err = client.DeleteIssueLabel(owner, repo, index, int64(labelID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("remove label %v from %v/%v/issue/%v err: %v", int64(labelID), owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("remove label %v from %v/%v/issue/%v err: %v", int64(labelID), owner, repo, index, err))
|
||||||
}
|
}
|
||||||
return to.TextResult("Label removed successfully")
|
return to.TextResult("Label removed successfully")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
|
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/log"
|
"gitea.com/gitea/gitea-mcp/pkg/log"
|
||||||
|
"gitea.com/gitea/gitea-mcp/pkg/params"
|
||||||
|
"gitea.com/gitea/gitea-mcp/pkg/ptr"
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||||
|
|
||||||
@@ -30,6 +32,8 @@ const (
|
|||||||
SubmitPullRequestReviewToolName = "submit_pull_request_review"
|
SubmitPullRequestReviewToolName = "submit_pull_request_review"
|
||||||
DeletePullRequestReviewToolName = "delete_pull_request_review"
|
DeletePullRequestReviewToolName = "delete_pull_request_review"
|
||||||
DismissPullRequestReviewToolName = "dismiss_pull_request_review"
|
DismissPullRequestReviewToolName = "dismiss_pull_request_review"
|
||||||
|
MergePullRequestToolName = "merge_pull_request"
|
||||||
|
EditPullRequestToolName = "edit_pull_request"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -170,6 +174,33 @@ var (
|
|||||||
mcp.WithNumber("review_id", mcp.Required(), mcp.Description("review ID")),
|
mcp.WithNumber("review_id", mcp.Required(), mcp.Description("review ID")),
|
||||||
mcp.WithString("message", mcp.Description("dismissal reason")),
|
mcp.WithString("message", mcp.Description("dismissal reason")),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
MergePullRequestTool = mcp.NewTool(
|
||||||
|
MergePullRequestToolName,
|
||||||
|
mcp.WithDescription("merge a pull request"),
|
||||||
|
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||||
|
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||||
|
mcp.WithNumber("index", mcp.Required(), mcp.Description("pull request index")),
|
||||||
|
mcp.WithString("merge_style", mcp.Description("merge style: merge, rebase, rebase-merge, squash, fast-forward-only"), mcp.Enum("merge", "rebase", "rebase-merge", "squash", "fast-forward-only"), mcp.DefaultString("merge")),
|
||||||
|
mcp.WithString("message", mcp.Description("custom merge commit message (optional)")),
|
||||||
|
mcp.WithBoolean("delete_branch", mcp.Description("delete the branch after merge"), mcp.DefaultBool(false)),
|
||||||
|
)
|
||||||
|
|
||||||
|
EditPullRequestTool = mcp.NewTool(
|
||||||
|
EditPullRequestToolName,
|
||||||
|
mcp.WithDescription("edit a pull request"),
|
||||||
|
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
||||||
|
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
||||||
|
mcp.WithNumber("index", mcp.Required(), mcp.Description("pull request index")),
|
||||||
|
mcp.WithString("title", mcp.Description("pull request title")),
|
||||||
|
mcp.WithString("body", mcp.Description("pull request body content")),
|
||||||
|
mcp.WithString("base", mcp.Description("pull request base branch")),
|
||||||
|
mcp.WithString("assignee", mcp.Description("username to assign")),
|
||||||
|
mcp.WithArray("assignees", mcp.Description("usernames to assign"), mcp.Items(map[string]interface{}{"type": "string"})),
|
||||||
|
mcp.WithNumber("milestone", mcp.Description("milestone number")),
|
||||||
|
mcp.WithString("state", mcp.Description("pull request state"), mcp.Enum("open", "closed")),
|
||||||
|
mcp.WithBoolean("allow_maintainer_edit", mcp.Description("allow maintainer to edit the pull request")),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -225,6 +256,14 @@ func init() {
|
|||||||
Tool: DismissPullRequestReviewTool,
|
Tool: DismissPullRequestReviewTool,
|
||||||
Handler: DismissPullRequestReviewFn,
|
Handler: DismissPullRequestReviewFn,
|
||||||
})
|
})
|
||||||
|
Tool.RegisterWrite(server.ServerTool{
|
||||||
|
Tool: MergePullRequestTool,
|
||||||
|
Handler: MergePullRequestFn,
|
||||||
|
})
|
||||||
|
Tool.RegisterWrite(server.ServerTool{
|
||||||
|
Tool: EditPullRequestTool,
|
||||||
|
Handler: EditPullRequestFn,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPullRequestByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
func GetPullRequestByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||||
@@ -237,17 +276,17 @@ func GetPullRequestByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
client, err := gitea.ClientFromContext(ctx)
|
client, err := gitea.ClientFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
pr, _, err := client.GetPullRequest(owner, repo, int64(index))
|
pr, _, err := client.GetPullRequest(owner, repo, index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get %v/%v/pr/%v err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("get %v/%v/pr/%v err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return to.TextResult(pr)
|
return to.TextResult(pr)
|
||||||
@@ -263,9 +302,9 @@ func GetPullRequestDiffFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
binary, _ := req.GetArguments()["binary"].(bool)
|
binary, _ := req.GetArguments()["binary"].(bool)
|
||||||
|
|
||||||
@@ -273,17 +312,17 @@ func GetPullRequestDiffFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
diffBytes, _, err := client.GetPullRequestDiff(owner, repo, int64(index), gitea_sdk.PullRequestDiffOptions{
|
diffBytes, _, err := client.GetPullRequestDiff(owner, repo, index, gitea_sdk.PullRequestDiffOptions{
|
||||||
Binary: binary,
|
Binary: binary,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get %v/%v/pr/%v diff err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("get %v/%v/pr/%v diff err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
result := map[string]interface{}{
|
result := map[string]interface{}{
|
||||||
"diff": string(diffBytes),
|
"diff": string(diffBytes),
|
||||||
"binary": binary,
|
"binary": binary,
|
||||||
"index": int64(index),
|
"index": index,
|
||||||
"repo": repo,
|
"repo": repo,
|
||||||
"owner": owner,
|
"owner": owner,
|
||||||
}
|
}
|
||||||
@@ -388,9 +427,9 @@ func CreatePullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var reviewers []string
|
var reviewers []string
|
||||||
@@ -420,12 +459,12 @@ func CreatePullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (
|
|||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = client.CreateReviewRequests(owner, repo, int64(index), gitea_sdk.PullReviewRequestOptions{
|
_, err = client.CreateReviewRequests(owner, repo, index, gitea_sdk.PullReviewRequestOptions{
|
||||||
Reviewers: reviewers,
|
Reviewers: reviewers,
|
||||||
TeamReviewers: teamReviewers,
|
TeamReviewers: teamReviewers,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("create review requests for %v/%v/pr/%v err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("create review requests for %v/%v/pr/%v err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a success message instead of the Response object which contains non-serializable functions
|
// Return a success message instead of the Response object which contains non-serializable functions
|
||||||
@@ -433,7 +472,7 @@ func CreatePullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (
|
|||||||
"message": "Successfully created review requests",
|
"message": "Successfully created review requests",
|
||||||
"reviewers": reviewers,
|
"reviewers": reviewers,
|
||||||
"team_reviewers": teamReviewers,
|
"team_reviewers": teamReviewers,
|
||||||
"pr_index": int64(index),
|
"pr_index": index,
|
||||||
"repository": fmt.Sprintf("%s/%s", owner, repo),
|
"repository": fmt.Sprintf("%s/%s", owner, repo),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,9 +489,9 @@ func DeletePullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var reviewers []string
|
var reviewers []string
|
||||||
@@ -482,19 +521,19 @@ func DeletePullRequestReviewerFn(ctx context.Context, req mcp.CallToolRequest) (
|
|||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = client.DeleteReviewRequests(owner, repo, int64(index), gitea_sdk.PullReviewRequestOptions{
|
_, err = client.DeleteReviewRequests(owner, repo, index, gitea_sdk.PullReviewRequestOptions{
|
||||||
Reviewers: reviewers,
|
Reviewers: reviewers,
|
||||||
TeamReviewers: teamReviewers,
|
TeamReviewers: teamReviewers,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("delete review requests for %v/%v/pr/%v err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("delete review requests for %v/%v/pr/%v err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
successMsg := map[string]interface{}{
|
successMsg := map[string]interface{}{
|
||||||
"message": "Successfully deleted review requests",
|
"message": "Successfully deleted review requests",
|
||||||
"reviewers": reviewers,
|
"reviewers": reviewers,
|
||||||
"team_reviewers": teamReviewers,
|
"team_reviewers": teamReviewers,
|
||||||
"pr_index": int64(index),
|
"pr_index": index,
|
||||||
"repository": fmt.Sprintf("%s/%s", owner, repo),
|
"repository": fmt.Sprintf("%s/%s", owner, repo),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,9 +550,9 @@ func ListPullRequestReviewsFn(ctx context.Context, req mcp.CallToolRequest) (*mc
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
page, ok := req.GetArguments()["page"].(float64)
|
page, ok := req.GetArguments()["page"].(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -529,14 +568,14 @@ func ListPullRequestReviewsFn(ctx context.Context, req mcp.CallToolRequest) (*mc
|
|||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
reviews, _, err := client.ListPullReviews(owner, repo, int64(index), gitea_sdk.ListPullReviewsOptions{
|
reviews, _, err := client.ListPullReviews(owner, repo, index, gitea_sdk.ListPullReviewsOptions{
|
||||||
ListOptions: gitea_sdk.ListOptions{
|
ListOptions: gitea_sdk.ListOptions{
|
||||||
Page: int(page),
|
Page: int(page),
|
||||||
PageSize: int(pageSize),
|
PageSize: int(pageSize),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("list reviews for %v/%v/pr/%v err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("list reviews for %v/%v/pr/%v err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return to.TextResult(reviews)
|
return to.TextResult(reviews)
|
||||||
@@ -552,9 +591,9 @@ func GetPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
reviewID, ok := req.GetArguments()["review_id"].(float64)
|
reviewID, ok := req.GetArguments()["review_id"].(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -566,9 +605,9 @@ func GetPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.
|
|||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
review, _, err := client.GetPullReview(owner, repo, int64(index), int64(reviewID))
|
review, _, err := client.GetPullReview(owner, repo, index, int64(reviewID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get review %v for %v/%v/pr/%v err: %v", int64(reviewID), owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("get review %v for %v/%v/pr/%v err: %v", int64(reviewID), owner, repo, index, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return to.TextResult(review)
|
return to.TextResult(review)
|
||||||
@@ -584,9 +623,9 @@ func ListPullRequestReviewCommentsFn(ctx context.Context, req mcp.CallToolReques
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
reviewID, ok := req.GetArguments()["review_id"].(float64)
|
reviewID, ok := req.GetArguments()["review_id"].(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -598,9 +637,9 @@ func ListPullRequestReviewCommentsFn(ctx context.Context, req mcp.CallToolReques
|
|||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
comments, _, err := client.ListPullReviewComments(owner, repo, int64(index), int64(reviewID))
|
comments, _, err := client.ListPullReviewComments(owner, repo, index, int64(reviewID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("list review comments for review %v on %v/%v/pr/%v err: %v", int64(reviewID), owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("list review comments for review %v on %v/%v/pr/%v err: %v", int64(reviewID), owner, repo, index, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return to.TextResult(comments)
|
return to.TextResult(comments)
|
||||||
@@ -616,9 +655,9 @@ func CreatePullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
opt := gitea_sdk.CreatePullReviewOptions{}
|
opt := gitea_sdk.CreatePullReviewOptions{}
|
||||||
@@ -662,9 +701,9 @@ func CreatePullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
|||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
review, _, err := client.CreatePullReview(owner, repo, int64(index), opt)
|
review, _, err := client.CreatePullReview(owner, repo, index, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("create review for %v/%v/pr/%v err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("create review for %v/%v/pr/%v err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return to.TextResult(review)
|
return to.TextResult(review)
|
||||||
@@ -680,9 +719,9 @@ func SubmitPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
reviewID, ok := req.GetArguments()["review_id"].(float64)
|
reviewID, ok := req.GetArguments()["review_id"].(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -705,9 +744,9 @@ func SubmitPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
|||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
review, _, err := client.SubmitPullReview(owner, repo, int64(index), int64(reviewID), opt)
|
review, _, err := client.SubmitPullReview(owner, repo, index, int64(reviewID), opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("submit review %v for %v/%v/pr/%v err: %v", int64(reviewID), owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("submit review %v for %v/%v/pr/%v err: %v", int64(reviewID), owner, repo, index, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return to.TextResult(review)
|
return to.TextResult(review)
|
||||||
@@ -723,9 +762,9 @@ func DeletePullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
reviewID, ok := req.GetArguments()["review_id"].(float64)
|
reviewID, ok := req.GetArguments()["review_id"].(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -737,15 +776,15 @@ func DeletePullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*m
|
|||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = client.DeletePullReview(owner, repo, int64(index), int64(reviewID))
|
_, err = client.DeletePullReview(owner, repo, index, int64(reviewID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("delete review %v for %v/%v/pr/%v err: %v", int64(reviewID), owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("delete review %v for %v/%v/pr/%v err: %v", int64(reviewID), owner, repo, index, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
successMsg := map[string]interface{}{
|
successMsg := map[string]interface{}{
|
||||||
"message": "Successfully deleted review",
|
"message": "Successfully deleted review",
|
||||||
"review_id": int64(reviewID),
|
"review_id": int64(reviewID),
|
||||||
"pr_index": int64(index),
|
"pr_index": index,
|
||||||
"repository": fmt.Sprintf("%s/%s", owner, repo),
|
"repository": fmt.Sprintf("%s/%s", owner, repo),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -762,9 +801,9 @@ func DismissPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
reviewID, ok := req.GetArguments()["review_id"].(float64)
|
reviewID, ok := req.GetArguments()["review_id"].(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -781,17 +820,145 @@ func DismissPullRequestReviewFn(ctx context.Context, req mcp.CallToolRequest) (*
|
|||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = client.DismissPullReview(owner, repo, int64(index), int64(reviewID), opt)
|
_, err = client.DismissPullReview(owner, repo, index, int64(reviewID), opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("dismiss review %v for %v/%v/pr/%v err: %v", int64(reviewID), owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("dismiss review %v for %v/%v/pr/%v err: %v", int64(reviewID), owner, repo, index, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
successMsg := map[string]interface{}{
|
successMsg := map[string]interface{}{
|
||||||
"message": "Successfully dismissed review",
|
"message": "Successfully dismissed review",
|
||||||
"review_id": int64(reviewID),
|
"review_id": int64(reviewID),
|
||||||
"pr_index": int64(index),
|
"pr_index": index,
|
||||||
"repository": fmt.Sprintf("%s/%s", owner, repo),
|
"repository": fmt.Sprintf("%s/%s", owner, repo),
|
||||||
}
|
}
|
||||||
|
|
||||||
return to.TextResult(successMsg)
|
return to.TextResult(successMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MergePullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||||
|
log.Debugf("Called MergePullRequestFn")
|
||||||
|
owner, ok := req.GetArguments()["owner"].(string)
|
||||||
|
if !ok {
|
||||||
|
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||||
|
}
|
||||||
|
repo, ok := req.GetArguments()["repo"].(string)
|
||||||
|
if !ok {
|
||||||
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
|
}
|
||||||
|
index, ok := req.GetArguments()["index"].(float64)
|
||||||
|
if !ok {
|
||||||
|
return to.ErrorResult(fmt.Errorf("index is required"))
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeStyle := "merge"
|
||||||
|
if style, exists := req.GetArguments()["merge_style"].(string); exists && style != "" {
|
||||||
|
mergeStyle = style
|
||||||
|
}
|
||||||
|
|
||||||
|
message := ""
|
||||||
|
if msg, exists := req.GetArguments()["message"].(string); exists {
|
||||||
|
message = msg
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteBranch := false
|
||||||
|
if del, exists := req.GetArguments()["delete_branch"].(bool); exists {
|
||||||
|
deleteBranch = del
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := gitea.ClientFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
opt := gitea_sdk.MergePullRequestOption{
|
||||||
|
Style: gitea_sdk.MergeStyle(mergeStyle),
|
||||||
|
Message: message,
|
||||||
|
DeleteBranchAfterMerge: deleteBranch,
|
||||||
|
}
|
||||||
|
|
||||||
|
merged, resp, err := client.MergePullRequest(owner, repo, int64(index), opt)
|
||||||
|
if err != nil {
|
||||||
|
return to.ErrorResult(fmt.Errorf("merge %v/%v/pr/%v err: %v", owner, repo, int64(index), err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !merged && resp != nil && resp.StatusCode >= 400 {
|
||||||
|
return to.ErrorResult(fmt.Errorf("merge %v/%v/pr/%v failed: HTTP %d %s", owner, repo, int64(index), resp.StatusCode, resp.Status))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !merged {
|
||||||
|
return to.ErrorResult(fmt.Errorf("merge %v/%v/pr/%v returned merged=false", owner, repo, int64(index)))
|
||||||
|
}
|
||||||
|
|
||||||
|
successMsg := map[string]interface{}{
|
||||||
|
"merged": merged,
|
||||||
|
"pr_index": int64(index),
|
||||||
|
"repository": fmt.Sprintf("%s/%s", owner, repo),
|
||||||
|
"merge_style": mergeStyle,
|
||||||
|
"branch_deleted": deleteBranch,
|
||||||
|
}
|
||||||
|
|
||||||
|
return to.TextResult(successMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EditPullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||||
|
log.Debugf("Called EditPullRequestFn")
|
||||||
|
owner, ok := req.GetArguments()["owner"].(string)
|
||||||
|
if !ok {
|
||||||
|
return to.ErrorResult(fmt.Errorf("owner is required"))
|
||||||
|
}
|
||||||
|
repo, ok := req.GetArguments()["repo"].(string)
|
||||||
|
if !ok {
|
||||||
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
|
}
|
||||||
|
index, ok := req.GetArguments()["index"].(float64)
|
||||||
|
if !ok {
|
||||||
|
return to.ErrorResult(fmt.Errorf("index is required"))
|
||||||
|
}
|
||||||
|
|
||||||
|
opt := gitea_sdk.EditPullRequestOption{}
|
||||||
|
|
||||||
|
if title, ok := req.GetArguments()["title"].(string); ok {
|
||||||
|
opt.Title = title
|
||||||
|
}
|
||||||
|
if body, ok := req.GetArguments()["body"].(string); ok {
|
||||||
|
opt.Body = ptr.To(body)
|
||||||
|
}
|
||||||
|
if base, ok := req.GetArguments()["base"].(string); ok {
|
||||||
|
opt.Base = base
|
||||||
|
}
|
||||||
|
if assignee, ok := req.GetArguments()["assignee"].(string); ok {
|
||||||
|
opt.Assignee = assignee
|
||||||
|
}
|
||||||
|
if assigneesArg, exists := req.GetArguments()["assignees"]; exists {
|
||||||
|
if assigneesSlice, ok := assigneesArg.([]interface{}); ok {
|
||||||
|
var assignees []string
|
||||||
|
for _, a := range assigneesSlice {
|
||||||
|
if s, ok := a.(string); ok {
|
||||||
|
assignees = append(assignees, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opt.Assignees = assignees
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if milestone, ok := req.GetArguments()["milestone"].(float64); ok {
|
||||||
|
opt.Milestone = int64(milestone)
|
||||||
|
}
|
||||||
|
if state, ok := req.GetArguments()["state"].(string); ok {
|
||||||
|
opt.State = ptr.To(gitea_sdk.StateType(state))
|
||||||
|
}
|
||||||
|
if allowMaintainerEdit, ok := req.GetArguments()["allow_maintainer_edit"].(bool); ok {
|
||||||
|
opt.AllowMaintainerEdit = ptr.To(allowMaintainerEdit)
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := gitea.ClientFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
pr, _, err := client.EditPullRequest(owner, repo, int64(index), opt)
|
||||||
|
if err != nil {
|
||||||
|
return to.ErrorResult(fmt.Errorf("edit %v/%v/pr/%v err: %v", owner, repo, int64(index), err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return to.TextResult(pr)
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,6 +13,110 @@ import (
|
|||||||
"github.com/mark3labs/mcp-go/mcp"
|
"github.com/mark3labs/mcp-go/mcp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestEditPullRequestFn(t *testing.T) {
|
||||||
|
const (
|
||||||
|
owner = "octo"
|
||||||
|
repo = "demo"
|
||||||
|
index = 7
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mu sync.Mutex
|
||||||
|
gotMethod string
|
||||||
|
gotPath string
|
||||||
|
gotBody map[string]any
|
||||||
|
)
|
||||||
|
|
||||||
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch r.URL.Path {
|
||||||
|
case "/api/v1/version":
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_, _ = w.Write([]byte(`{"version":"1.12.0"}`))
|
||||||
|
case fmt.Sprintf("/api/v1/repos/%s/%s", owner, repo):
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_, _ = w.Write([]byte(`{"private":false}`))
|
||||||
|
case fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner, repo, index):
|
||||||
|
mu.Lock()
|
||||||
|
gotMethod = r.Method
|
||||||
|
gotPath = r.URL.Path
|
||||||
|
var body map[string]any
|
||||||
|
_ = json.NewDecoder(r.Body).Decode(&body)
|
||||||
|
gotBody = body
|
||||||
|
mu.Unlock()
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_, _ = w.Write([]byte(fmt.Sprintf(`{"number":%d,"title":"%s","state":"open"}`, index, body["title"])))
|
||||||
|
default:
|
||||||
|
http.NotFound(w, r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
server := httptest.NewServer(handler)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
origHost := flag.Host
|
||||||
|
origToken := flag.Token
|
||||||
|
origVersion := flag.Version
|
||||||
|
flag.Host = server.URL
|
||||||
|
flag.Token = ""
|
||||||
|
flag.Version = "test"
|
||||||
|
defer func() {
|
||||||
|
flag.Host = origHost
|
||||||
|
flag.Token = origToken
|
||||||
|
flag.Version = origVersion
|
||||||
|
}()
|
||||||
|
|
||||||
|
req := mcp.CallToolRequest{
|
||||||
|
Params: mcp.CallToolParams{
|
||||||
|
Arguments: map[string]any{
|
||||||
|
"owner": owner,
|
||||||
|
"repo": repo,
|
||||||
|
"index": float64(index),
|
||||||
|
"title": "WIP: my feature",
|
||||||
|
"state": "open",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := EditPullRequestFn(context.Background(), req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("EditPullRequestFn() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
if gotMethod != http.MethodPatch {
|
||||||
|
t.Fatalf("expected PATCH request, got %s", gotMethod)
|
||||||
|
}
|
||||||
|
if gotPath != fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner, repo, index) {
|
||||||
|
t.Fatalf("unexpected path: %s", gotPath)
|
||||||
|
}
|
||||||
|
if gotBody["title"] != "WIP: my feature" {
|
||||||
|
t.Fatalf("expected title 'WIP: my feature', got %v", gotBody["title"])
|
||||||
|
}
|
||||||
|
if gotBody["state"] != "open" {
|
||||||
|
t.Fatalf("expected state 'open', got %v", gotBody["state"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result.Content) == 0 {
|
||||||
|
t.Fatalf("expected content in result")
|
||||||
|
}
|
||||||
|
textContent, ok := mcp.AsTextContent(result.Content[0])
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected text content, got %T", result.Content[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsed struct {
|
||||||
|
Result map[string]any `json:"Result"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(textContent.Text), &parsed); err != nil {
|
||||||
|
t.Fatalf("unmarshal result text: %v", err)
|
||||||
|
}
|
||||||
|
if got := parsed.Result["title"].(string); got != "WIP: my feature" {
|
||||||
|
t.Fatalf("result title = %q, want %q", got, "WIP: my feature")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetPullRequestDiffFn(t *testing.T) {
|
func TestGetPullRequestDiffFn(t *testing.T) {
|
||||||
const (
|
const (
|
||||||
owner = "octo"
|
owner = "octo"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/log"
|
"gitea.com/gitea/gitea-mcp/pkg/log"
|
||||||
|
"gitea.com/gitea/gitea-mcp/pkg/params"
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||||
|
|
||||||
@@ -134,19 +135,19 @@ func StartStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
client, err := gitea.ClientFromContext(ctx)
|
client, err := gitea.ClientFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
_, err = client.StartIssueStopWatch(owner, repo, int64(index))
|
_, err = client.StartIssueStopWatch(owner, repo, index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("start stopwatch on %s/%s#%d err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("start stopwatch on %s/%s#%d err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
return to.TextResult(fmt.Sprintf("Stopwatch started on issue %s/%s#%d", owner, repo, int64(index)))
|
return to.TextResult(fmt.Sprintf("Stopwatch started on issue %s/%s#%d", owner, repo, index))
|
||||||
}
|
}
|
||||||
|
|
||||||
func StopStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
func StopStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||||
@@ -159,19 +160,19 @@ func StopStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
client, err := gitea.ClientFromContext(ctx)
|
client, err := gitea.ClientFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
_, err = client.StopIssueStopWatch(owner, repo, int64(index))
|
_, err = client.StopIssueStopWatch(owner, repo, index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("stop stopwatch on %s/%s#%d err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("stop stopwatch on %s/%s#%d err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
return to.TextResult(fmt.Sprintf("Stopwatch stopped on issue %s/%s#%d - time recorded", owner, repo, int64(index)))
|
return to.TextResult(fmt.Sprintf("Stopwatch stopped on issue %s/%s#%d - time recorded", owner, repo, index))
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
func DeleteStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||||
@@ -184,19 +185,19 @@ func DeleteStopwatchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
client, err := gitea.ClientFromContext(ctx)
|
client, err := gitea.ClientFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
_, err = client.DeleteIssueStopwatch(owner, repo, int64(index))
|
_, err = client.DeleteIssueStopwatch(owner, repo, index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("delete stopwatch on %s/%s#%d err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("delete stopwatch on %s/%s#%d err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
return to.TextResult(fmt.Sprintf("Stopwatch deleted/cancelled on issue %s/%s#%d", owner, repo, int64(index)))
|
return to.TextResult(fmt.Sprintf("Stopwatch deleted/cancelled on issue %s/%s#%d", owner, repo, index))
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMyStopwatchesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
func GetMyStopwatchesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||||
@@ -227,9 +228,9 @@ func ListTrackedTimesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
page, ok := req.GetArguments()["page"].(float64)
|
page, ok := req.GetArguments()["page"].(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -244,17 +245,17 @@ func ListTrackedTimesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call
|
|||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
times, _, err := client.ListIssueTrackedTimes(owner, repo, int64(index), gitea_sdk.ListTrackedTimesOptions{
|
times, _, err := client.ListIssueTrackedTimes(owner, repo, index, gitea_sdk.ListTrackedTimesOptions{
|
||||||
ListOptions: gitea_sdk.ListOptions{
|
ListOptions: gitea_sdk.ListOptions{
|
||||||
Page: int(page),
|
Page: int(page),
|
||||||
PageSize: int(pageSize),
|
PageSize: int(pageSize),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("list tracked times for %s/%s#%d err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("list tracked times for %s/%s#%d err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
if len(times) == 0 {
|
if len(times) == 0 {
|
||||||
return to.TextResult(fmt.Sprintf("No tracked times for issue %s/%s#%d", owner, repo, int64(index)))
|
return to.TextResult(fmt.Sprintf("No tracked times for issue %s/%s#%d", owner, repo, index))
|
||||||
}
|
}
|
||||||
return to.TextResult(times)
|
return to.TextResult(times)
|
||||||
}
|
}
|
||||||
@@ -269,9 +270,9 @@ func AddTrackedTimeFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
|||||||
if !ok {
|
if !ok {
|
||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
timeSeconds, ok := req.GetArguments()["time"].(float64)
|
timeSeconds, ok := req.GetArguments()["time"].(float64)
|
||||||
@@ -282,11 +283,11 @@ func AddTrackedTimeFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
trackedTime, _, err := client.AddTime(owner, repo, int64(index), gitea_sdk.AddTimeOption{
|
trackedTime, _, err := client.AddTime(owner, repo, index, gitea_sdk.AddTimeOption{
|
||||||
Time: int64(timeSeconds),
|
Time: int64(timeSeconds),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("add tracked time to %s/%s#%d err: %v", owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("add tracked time to %s/%s#%d err: %v", owner, repo, index, err))
|
||||||
}
|
}
|
||||||
return to.TextResult(trackedTime)
|
return to.TextResult(trackedTime)
|
||||||
}
|
}
|
||||||
@@ -302,9 +303,9 @@ func DeleteTrackedTimeFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
|
|||||||
return to.ErrorResult(fmt.Errorf("repo is required"))
|
return to.ErrorResult(fmt.Errorf("repo is required"))
|
||||||
}
|
}
|
||||||
|
|
||||||
index, ok := req.GetArguments()["index"].(float64)
|
index, err := params.GetIndex(req.GetArguments(), "index")
|
||||||
if !ok {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("index is required"))
|
return to.ErrorResult(err)
|
||||||
}
|
}
|
||||||
id, ok := req.GetArguments()["id"].(float64)
|
id, ok := req.GetArguments()["id"].(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -314,11 +315,11 @@ func DeleteTrackedTimeFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||||
}
|
}
|
||||||
_, err = client.DeleteTime(owner, repo, int64(index), int64(id))
|
_, err = client.DeleteTime(owner, repo, index, int64(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return to.ErrorResult(fmt.Errorf("delete tracked time %d from %s/%s#%d err: %v", int64(id), owner, repo, int64(index), err))
|
return to.ErrorResult(fmt.Errorf("delete tracked time %d from %s/%s#%d err: %v", int64(id), owner, repo, index, err))
|
||||||
}
|
}
|
||||||
return to.TextResult(fmt.Sprintf("Tracked time entry %d deleted from issue %s/%s#%d", int64(id), owner, repo, int64(index)))
|
return to.TextResult(fmt.Sprintf("Tracked time entry %d deleted from issue %s/%s#%d", int64(id), owner, repo, index))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListRepoTimesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
func ListRepoTimesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||||
|
|||||||
33
pkg/params/params.go
Normal file
33
pkg/params/params.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package params
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetIndex extracts an index 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.
|
||||||
|
func GetIndex(args map[string]interface{}, key string) (int64, error) {
|
||||||
|
val, exists := args[key]
|
||||||
|
if !exists {
|
||||||
|
return 0, fmt.Errorf("%s is required", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try float64 (JSON number type)
|
||||||
|
if f, ok := val.(float64); ok {
|
||||||
|
return int64(f), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try string and parse to integer
|
||||||
|
if s, ok := val.(string); ok {
|
||||||
|
i, err := strconv.ParseInt(s, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("%s must be a valid integer (got %q)", key, s)
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, fmt.Errorf("%s must be a number or numeric string", key)
|
||||||
|
}
|
||||||
104
pkg/params/params_test.go
Normal file
104
pkg/params/params_test.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package params
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetIndex(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args map[string]interface{}
|
||||||
|
key string
|
||||||
|
wantIndex int64
|
||||||
|
wantErr bool
|
||||||
|
errMsg string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid float64",
|
||||||
|
args: map[string]interface{}{"index": float64(123)},
|
||||||
|
key: "index",
|
||||||
|
wantIndex: 123,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid string",
|
||||||
|
args: map[string]interface{}{"index": "456"},
|
||||||
|
key: "index",
|
||||||
|
wantIndex: 456,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid string with large number",
|
||||||
|
args: map[string]interface{}{"index": "999999"},
|
||||||
|
key: "index",
|
||||||
|
wantIndex: 999999,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing parameter",
|
||||||
|
args: map[string]interface{}{},
|
||||||
|
key: "index",
|
||||||
|
wantErr: true,
|
||||||
|
errMsg: "index is required",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid string (not a number)",
|
||||||
|
args: map[string]interface{}{"index": "abc"},
|
||||||
|
key: "index",
|
||||||
|
wantErr: true,
|
||||||
|
errMsg: "must be a valid integer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid string (decimal)",
|
||||||
|
args: map[string]interface{}{"index": "12.34"},
|
||||||
|
key: "index",
|
||||||
|
wantErr: true,
|
||||||
|
errMsg: "must be a valid integer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid type (bool)",
|
||||||
|
args: map[string]interface{}{"index": true},
|
||||||
|
key: "index",
|
||||||
|
wantErr: true,
|
||||||
|
errMsg: "must be a number or numeric string",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid type (map)",
|
||||||
|
args: map[string]interface{}{"index": map[string]string{"foo": "bar"}},
|
||||||
|
key: "index",
|
||||||
|
wantErr: true,
|
||||||
|
errMsg: "must be a number or numeric string",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom key name",
|
||||||
|
args: map[string]interface{}{"pr_index": "789"},
|
||||||
|
key: "pr_index",
|
||||||
|
wantIndex: 789,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
gotIndex, err := GetIndex(tt.args, tt.key)
|
||||||
|
if tt.wantErr {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("GetIndex() expected error but got nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tt.errMsg != "" && !strings.Contains(err.Error(), tt.errMsg) {
|
||||||
|
t.Errorf("GetIndex() error = %v, want error containing %q", err, tt.errMsg)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("GetIndex() unexpected error = %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if gotIndex != tt.wantIndex {
|
||||||
|
t.Errorf("GetIndex() = %v, want %v", gotIndex, tt.wantIndex)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user