feat: read token from header in http/sse mode #89
10 README.md
10
README.md @@ -139,7 +139,10 @@ To configure the MCP server for Gitea, add the following to your MCP configurati | ||||
{ | ||||
"mcpServers": { | ||||
"gitea": { | ||||
"url": "http://localhost:8080/sse" | ||||
"url": "http://localhost:8080/sse", | ||||
"headers": { | ||||
"Authorization": "Bearer <your personal access token>" | ||||
} | ||||
} | ||||
} | ||||
} | ||||
@@ -151,7 +154,10 @@ To configure the MCP server for Gitea, add the following to your MCP configurati | ||||
{ | ||||
"mcpServers": { | ||||
"gitea": { | ||||
"url": "http://localhost:8080/mcp" | ||||
"url": "http://localhost:8080/mcp", | ||||
"headers": { | ||||
"Authorization": "Bearer <your personal access token>" | ||||
} | ||||
} | ||||
} | ||||
} | ||||
|
@@ -139,7 +139,10 @@ cp gitea-mcp /usr/local/bin/ | ||||
{ | ||||
"mcpServers": { | ||||
"gitea": { | ||||
"url": "http://localhost:8080/sse" | ||||
"url": "http://localhost:8080/sse", | ||||
"headers": { | ||||
"Authorization": "Bearer <your personal access token>" | ||||
} | ||||
} | ||||
} | ||||
} | ||||
@@ -151,7 +154,10 @@ cp gitea-mcp /usr/local/bin/ | ||||
{ | ||||
"mcpServers": { | ||||
"gitea": { | ||||
"url": "http://localhost:8080/mcp" | ||||
"url": "http://localhost:8080/mcp", | ||||
"headers": { | ||||
"Authorization": "Bearer <your personal access token>" | ||||
} | ||||
} | ||||
} | ||||
} | ||||
|
@@ -139,7 +139,10 @@ cp gitea-mcp /usr/local/bin/ | ||||
{ | ||||
"mcpServers": { | ||||
"gitea": { | ||||
"url": "http://localhost:8080/sse" | ||||
"url": "http://localhost:8080/sse", | ||||
"headers": { | ||||
"Authorization": "Bearer <your personal access token>" | ||||
} | ||||
} | ||||
} | ||||
} | ||||
@@ -151,7 +154,10 @@ cp gitea-mcp /usr/local/bin/ | ||||
{ | ||||
"mcpServers": { | ||||
"gitea": { | ||||
"url": "http://localhost:8080/mcp" | ||||
"url": "http://localhost:8080/mcp", | ||||
"headers": { | ||||
"Authorization": "Bearer <your personal access token>" | ||||
} | ||||
} | ||||
} | ||||
} | ||||
|
@@ -140,7 +140,11 @@ func GetIssueByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT | ||||
if !ok { | ||||
return to.ErrorResult(fmt.Errorf("index is required")) | ||||
} | ||||
issue, _, err := gitea.Client().GetIssue(owner, repo, int64(index)) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
issue, _, err := client.GetIssue(owner, repo, int64(index)) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get %v/%v/issue/%v err: %v", owner, repo, int64(index), err)) | ||||
} | ||||
@@ -177,7 +181,11 @@ func ListRepoIssuesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo | ||||
PageSize: int(pageSize), | ||||
}, | ||||
} | ||||
issues, _, err := gitea.Client().ListRepoIssues(owner, repo, opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
issues, _, err := client.ListRepoIssues(owner, repo, opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get %v/%v/issues err: %v", owner, repo, err)) | ||||
} | ||||
@@ -202,7 +210,11 @@ func CreateIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR | ||||
if !ok { | ||||
return to.ErrorResult(fmt.Errorf("body is required")) | ||||
} | ||||
issue, _, err := gitea.Client().CreateIssue(owner, repo, gitea_sdk.CreateIssueOption{ | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
issue, _, err := client.CreateIssue(owner, repo, gitea_sdk.CreateIssueOption{ | ||||
Title: title, | ||||
Body: body, | ||||
}) | ||||
@@ -234,7 +246,11 @@ func CreateIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca | ||||
opt := gitea_sdk.CreateIssueCommentOption{ | ||||
Body: body, | ||||
} | ||||
issueComment, _, err := gitea.Client().CreateIssueComment(owner, repo, int64(index), opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
issueComment, _, err := client.CreateIssueComment(owner, repo, int64(index), opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("create %v/%v/issue/%v/comment err: %v", owner, repo, int64(index), err)) | ||||
} | ||||
@@ -280,7 +296,11 @@ func EditIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes | ||||
opt.State = ptr.To(gitea_sdk.StateType(state)) | ||||
} | ||||
| ||||
issue, _, err := gitea.Client().EditIssue(owner, repo, int64(index), opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
issue, _, err := client.EditIssue(owner, repo, int64(index), opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("edit %v/%v/issue/%v err: %v", owner, repo, int64(index), err)) | ||||
} | ||||
@@ -309,7 +329,11 @@ func EditIssueCommentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call | ||||
opt := gitea_sdk.EditIssueCommentOption{ | ||||
Body: body, | ||||
} | ||||
issueComment, _, err := gitea.Client().EditIssueComment(owner, repo, int64(commentID), opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
issueComment, _, err := client.EditIssueComment(owner, repo, int64(commentID), opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("edit %v/%v/issues/comments/%v err: %v", owner, repo, int64(commentID), err)) | ||||
} | ||||
@@ -332,7 +356,11 @@ func GetIssueCommentsByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*m | ||||
return to.ErrorResult(fmt.Errorf("index is required")) | ||||
} | ||||
opt := gitea_sdk.ListIssueCommentOptions{} | ||||
issue, _, err := gitea.Client().ListIssueComments(owner, repo, int64(index), opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
issue, _, err := client.ListIssueComments(owner, repo, int64(index), opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get %v/%v/issues/%v/comments err: %v", owner, repo, int64(index), err)) | ||||
} | ||||
|
@@ -176,7 +176,11 @@ func ListRepoLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo | ||||
PageSize: int(pageSize), | ||||
}, | ||||
} | ||||
labels, _, err := gitea.Client().ListRepoLabels(owner, repo, opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
labels, _, err := client.ListRepoLabels(owner, repo, opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("list %v/%v/labels err: %v", owner, repo, err)) | ||||
} | ||||
@@ -198,7 +202,11 @@ func GetRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool | ||||
return to.ErrorResult(fmt.Errorf("label ID is required")) | ||||
} | ||||
| ||||
label, _, err := gitea.Client().GetRepoLabel(owner, repo, int64(id)) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
label, _, err := client.GetRepoLabel(owner, repo, int64(id)) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get %v/%v/label/%v err: %v", owner, repo, int64(id), err)) | ||||
} | ||||
@@ -231,7 +239,11 @@ func CreateRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT | ||||
Description: description, | ||||
} | ||||
| ||||
label, _, err := gitea.Client().CreateLabel(owner, repo, opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
label, _, err := client.CreateLabel(owner, repo, opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("create %v/%v/label err: %v", owner, repo, err)) | ||||
} | ||||
@@ -264,7 +276,11 @@ func EditRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo | ||||
opt.Description = ptr.To(description) | ||||
} | ||||
| ||||
label, _, err := gitea.Client().EditLabel(owner, repo, int64(id), opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
label, _, err := client.EditLabel(owner, repo, int64(id), opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("edit %v/%v/label/%v err: %v", owner, repo, int64(id), err)) | ||||
} | ||||
@@ -286,7 +302,11 @@ func DeleteRepoLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT | ||||
return to.ErrorResult(fmt.Errorf("label ID is required")) | ||||
} | ||||
| ||||
_, err := gitea.Client().DeleteLabel(owner, repo, int64(id)) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
_, err = client.DeleteLabel(owner, repo, int64(id)) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("delete %v/%v/label/%v err: %v", owner, repo, int64(id), err)) | ||||
} | ||||
@@ -324,7 +344,11 @@ func AddIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo | ||||
Labels: labels, | ||||
} | ||||
| ||||
issueLabels, _, err := gitea.Client().AddIssueLabels(owner, repo, int64(index), opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
issueLabels, _, err := client.AddIssueLabels(owner, repo, int64(index), opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("add labels to %v/%v/issue/%v err: %v", owner, repo, int64(index), err)) | ||||
} | ||||
@@ -362,7 +386,11 @@ func ReplaceIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca | ||||
Labels: labels, | ||||
} | ||||
| ||||
issueLabels, _, err := gitea.Client().ReplaceIssueLabels(owner, repo, int64(index), opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
issueLabels, _, err := client.ReplaceIssueLabels(owner, repo, int64(index), opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("replace labels on %v/%v/issue/%v err: %v", owner, repo, int64(index), err)) | ||||
} | ||||
@@ -384,7 +412,11 @@ func ClearIssueLabelsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call | ||||
return to.ErrorResult(fmt.Errorf("issue index is required")) | ||||
} | ||||
| ||||
_, err := gitea.Client().ClearIssueLabels(owner, repo, int64(index)) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
_, err = client.ClearIssueLabels(owner, repo, int64(index)) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("clear labels on %v/%v/issue/%v err: %v", owner, repo, int64(index), err)) | ||||
} | ||||
@@ -410,7 +442,11 @@ func RemoveIssueLabelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call | ||||
return to.ErrorResult(fmt.Errorf("label ID is required")) | ||||
} | ||||
| ||||
_, err := gitea.Client().DeleteIssueLabel(owner, repo, int64(index), int64(labelID)) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
_, err = client.DeleteIssueLabel(owner, repo, int64(index), int64(labelID)) | ||||
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)) | ||||
} | ||||
|
@@ -1,7 +1,10 @@ | ||||
package operation | ||||
| ||||
import ( | ||||
"context" | ||||
"fmt" | ||||
"net/http" | ||||
"strings" | ||||
"time" | ||||
| ||||
"gitea.com/gitea/gitea-mcp/operation/issue" | ||||
@@ -11,6 +14,7 @@ import ( | ||||
"gitea.com/gitea/gitea-mcp/operation/search" | ||||
"gitea.com/gitea/gitea-mcp/operation/user" | ||||
"gitea.com/gitea/gitea-mcp/operation/version" | ||||
mcpContext "gitea.com/gitea/gitea-mcp/pkg/context" | ||||
"gitea.com/gitea/gitea-mcp/pkg/flag" | ||||
"gitea.com/gitea/gitea-mcp/pkg/log" | ||||
| ||||
@@ -44,6 +48,20 @@ func RegisterTool(s *server.MCPServer) { | ||||
s.DeleteTools("") | ||||
} | ||||
| ||||
func getContextWithToken(ctx context.Context, r *http.Request) context.Context { | ||||
authHeader := r.Header.Get("Authorization") | ||||
if authHeader == "" { | ||||
return ctx | ||||
} | ||||
| ||||
parts := strings.Split(authHeader, " ") | ||||
if len(parts) != 2 || parts[0] != "Bearer" { | ||||
return ctx | ||||
} | ||||
| ||||
return context.WithValue(ctx, mcpContext.TokenContextKey, parts[1]) | ||||
} | ||||
| ||||
func Run() error { | ||||
mcpServer = newMCPServer(flag.Version) | ||||
RegisterTool(mcpServer) | ||||
@@ -57,6 +75,7 @@ func Run() error { | ||||
case "sse": | ||||
sseServer := server.NewSSEServer( | ||||
mcpServer, | ||||
server.WithSSEContextFunc(getContextWithToken), | ||||
) | ||||
log.Infof("Gitea MCP SSE server listening on :%d", flag.Port) | ||||
if err := sseServer.Start(fmt.Sprintf(":%d", flag.Port)); err != nil { | ||||
@@ -67,7 +86,7 @@ func Run() error { | ||||
mcpServer, | ||||
server.WithLogger(log.New()), | ||||
server.WithHeartbeatInterval(30*time.Second), | ||||
server.WithStateLess(true), | ||||
server.WithHTTPContextFunc(getContextWithToken), | ||||
) | ||||
log.Infof("Gitea MCP HTTP server listening on :%d", flag.Port) | ||||
if err := httpServer.Start(fmt.Sprintf(":%d", flag.Port)); err != nil { | ||||
|
@@ -84,7 +84,11 @@ func GetPullRequestByIndexFn(ctx context.Context, req mcp.CallToolRequest) (*mcp | ||||
if !ok { | ||||
return to.ErrorResult(fmt.Errorf("index is required")) | ||||
} | ||||
pr, _, err := gitea.Client().GetPullRequest(owner, repo, int64(index)) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
pr, _, err := client.GetPullRequest(owner, repo, int64(index)) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get %v/%v/pr/%v err: %v", owner, repo, int64(index), err)) | ||||
} | ||||
@@ -125,7 +129,11 @@ func ListRepoPullRequestsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp. | ||||
PageSize: int(pageSize), | ||||
}, | ||||
} | ||||
pullRequests, _, err := gitea.Client().ListRepoPullRequests(owner, repo, opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
pullRequests, _, err := client.ListRepoPullRequests(owner, repo, opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("list %v/%v/pull_requests err: %v", owner, repo, err)) | ||||
} | ||||
@@ -159,7 +167,11 @@ func CreatePullRequestFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal | ||||
if !ok { | ||||
return to.ErrorResult(fmt.Errorf("base is required")) | ||||
} | ||||
pr, _, err := gitea.Client().CreatePullRequest(owner, repo, gitea_sdk.CreatePullRequestOption{ | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
pr, _, err := client.CreatePullRequest(owner, repo, gitea_sdk.CreatePullRequestOption{ | ||||
Title: title, | ||||
Body: body, | ||||
Head: head, | ||||
|
@@ -76,7 +76,11 @@ func CreateBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool | ||||
} | ||||
oldBranch, _ := req.GetArguments()["old_branch"].(string) | ||||
| ||||
_, _, err := gitea.Client().CreateBranch(owner, repo, gitea_sdk.CreateBranchOption{ | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
_, _, err = client.CreateBranch(owner, repo, gitea_sdk.CreateBranchOption{ | ||||
BranchName: branch, | ||||
OldBranchName: oldBranch, | ||||
}) | ||||
@@ -101,7 +105,11 @@ func DeleteBranchFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool | ||||
if !ok { | ||||
return to.ErrorResult(fmt.Errorf("branch is required")) | ||||
} | ||||
_, _, err := gitea.Client().DeleteRepoBranch(owner, repo, branch) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
_, _, err = client.DeleteRepoBranch(owner, repo, branch) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("delete branch error: %v", err)) | ||||
} | ||||
@@ -125,7 +133,11 @@ func ListBranchesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool | ||||
PageSize: 100, | ||||
}, | ||||
} | ||||
branches, _, err := gitea.Client().ListRepoBranches(owner, repo, opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
branches, _, err := client.ListRepoBranches(owner, repo, opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("list branches error: %v", err)) | ||||
} | ||||
|
@@ -63,7 +63,11 @@ func ListRepoCommitsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT | ||||
SHA: sha, | ||||
Path: path, | ||||
} | ||||
commits, _, err := gitea.Client().ListRepoCommits(owner, repo, opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
commits, _, err := client.ListRepoCommits(owner, repo, opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("list repo commits err: %v", err)) | ||||
} | ||||
|
@@ -124,7 +124,11 @@ func GetFileContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo | ||||
if !ok { | ||||
return to.ErrorResult(fmt.Errorf("filePath is required")) | ||||
} | ||||
content, _, err := gitea.Client().GetContents(owner, repo, ref, filePath) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
content, _, err := client.GetContents(owner, repo, ref, filePath) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get file err: %v", err)) | ||||
} | ||||
@@ -184,7 +188,11 @@ func GetDirContentFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo | ||||
if !ok { | ||||
return to.ErrorResult(fmt.Errorf("filePath is required")) | ||||
} | ||||
content, _, err := gitea.Client().ListContents(owner, repo, ref, filePath) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
content, _, err := client.ListContents(owner, repo, ref, filePath) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get dir content err: %v", err)) | ||||
} | ||||
@@ -216,7 +224,11 @@ func CreateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe | ||||
}, | ||||
} | ||||
| ||||
_, _, err := gitea.Client().CreateFile(owner, repo, filePath, opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
_, _, err = client.CreateFile(owner, repo, filePath, opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("create file err: %v", err)) | ||||
} | ||||
@@ -253,7 +265,11 @@ func UpdateFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe | ||||
BranchName: branchName, | ||||
}, | ||||
} | ||||
_, _, err := gitea.Client().UpdateFile(owner, repo, filePath, opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
_, _, err = client.UpdateFile(owner, repo, filePath, opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("update file err: %v", err)) | ||||
} | ||||
@@ -287,7 +303,11 @@ func DeleteFileFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe | ||||
}, | ||||
SHA: sha, | ||||
} | ||||
_, err := gitea.Client().DeleteFile(owner, repo, filePath, opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
_, err = client.DeleteFile(owner, repo, filePath, opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("delete file err: %v", err)) | ||||
} | ||||
|
@@ -134,7 +134,11 @@ func CreateReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo | ||||
isPreRelease, _ := req.GetArguments()["is_pre_release"].(bool) | ||||
body, _ := req.GetArguments()["body"].(string) | ||||
| ||||
_, _, err := gitea.Client().CreateRelease(owner, repo, gitea_sdk.CreateReleaseOption{ | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
_, _, err = client.CreateRelease(owner, repo, gitea_sdk.CreateReleaseOption{ | ||||
TagName: tagName, | ||||
Target: target, | ||||
Title: title, | ||||
@@ -164,7 +168,11 @@ func DeleteReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo | ||||
return nil, fmt.Errorf("id is required") | ||||
} | ||||
| ||||
_, err := gitea.Client().DeleteRelease(owner, repo, int64(id)) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
_, err = client.DeleteRelease(owner, repo, int64(id)) | ||||
if err != nil { | ||||
return nil, fmt.Errorf("delete release error: %v", err) | ||||
} | ||||
@@ -187,7 +195,11 @@ func GetReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe | ||||
return nil, fmt.Errorf("id is required") | ||||
} | ||||
| ||||
release, _, err := gitea.Client().GetRelease(owner, repo, int64(id)) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
release, _, err := client.GetRelease(owner, repo, int64(id)) | ||||
if err != nil { | ||||
return nil, fmt.Errorf("get release error: %v", err) | ||||
} | ||||
@@ -206,7 +218,11 @@ func GetLatestReleaseFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.Call | ||||
return nil, fmt.Errorf("repo is required") | ||||
} | ||||
| ||||
release, _, err := gitea.Client().GetLatestRelease(owner, repo) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
release, _, err := client.GetLatestRelease(owner, repo) | ||||
if err != nil { | ||||
return nil, fmt.Errorf("get latest release error: %v", err) | ||||
} | ||||
@@ -237,7 +253,11 @@ func ListReleasesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool | ||||
page, _ := req.GetArguments()["page"].(float64) | ||||
pageSize, _ := req.GetArguments()["pageSize"].(float64) | ||||
| ||||
releases, _, err := gitea.Client().ListReleases(owner, repo, gitea_sdk.ListReleasesOptions{ | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
releases, _, err := client.ListReleases(owner, repo, gitea_sdk.ListReleasesOptions{ | ||||
ListOptions: gitea_sdk.ListOptions{ | ||||
Page: int(page), | ||||
PageSize: int(pageSize), | ||||
|
@@ -137,14 +137,17 @@ func CreateRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRe | ||||
} | ||||
| ||||
var repo *gitea_sdk.Repository | ||||
var err error | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
if organization != "" { | ||||
repo, _, err = gitea.Client().CreateOrgRepo(organization, opt) | ||||
repo, _, err = client.CreateOrgRepo(organization, opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("create organization repository '%s' in '%s' err: %v", name, organization, err)) | ||||
} | ||||
} else { | ||||
repo, _, err = gitea.Client().CreateRepo(opt) | ||||
repo, _, err = client.CreateRepo(opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("create repository '%s' err: %v", name, err)) | ||||
} | ||||
@@ -176,7 +179,11 @@ func ForkRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResu | ||||
Organization: organizationPtr, | ||||
Name: namePtr, | ||||
} | ||||
_, _, err := gitea.Client().CreateFork(user, repo, opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
_, _, err = client.CreateFork(user, repo, opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("fork repository error: %v", err)) | ||||
} | ||||
@@ -199,7 +206,11 @@ func ListMyReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR | ||||
PageSize: int(pageSize), | ||||
}, | ||||
} | ||||
repos, _, err := gitea.Client().ListMyRepos(opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
repos, _, err := client.ListMyRepos(opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("list my repositories error: %v", err)) | ||||
} | ||||
|
@@ -102,7 +102,11 @@ func CreateTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes | ||||
target, _ := req.GetArguments()["target"].(string) | ||||
message, _ := req.GetArguments()["message"].(string) | ||||
| ||||
_, _, err := gitea.Client().CreateTag(owner, repo, gitea_sdk.CreateTagOption{ | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
_, _, err = client.CreateTag(owner, repo, gitea_sdk.CreateTagOption{ | ||||
TagName: tagName, | ||||
Target: target, | ||||
Message: message, | ||||
@@ -129,7 +133,11 @@ func DeleteTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolRes | ||||
return nil, fmt.Errorf("tag_name is required") | ||||
} | ||||
| ||||
_, err := gitea.Client().DeleteTag(owner, repo, tagName) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
_, err = client.DeleteTag(owner, repo, tagName) | ||||
if err != nil { | ||||
return nil, fmt.Errorf("delete tag error: %v", err) | ||||
} | ||||
@@ -152,7 +160,11 @@ func GetTagFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult | ||||
return nil, fmt.Errorf("tag_name is required") | ||||
} | ||||
| ||||
tag, _, err := gitea.Client().GetTag(owner, repo, tagName) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
tag, _, err := client.GetTag(owner, repo, tagName) | ||||
if err != nil { | ||||
return nil, fmt.Errorf("get tag error: %v", err) | ||||
} | ||||
@@ -173,7 +185,11 @@ func ListTagsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResu | ||||
page, _ := req.GetArguments()["page"].(float64) | ||||
pageSize, _ := req.GetArguments()["pageSize"].(float64) | ||||
| ||||
tags, _, err := gitea.Client().ListRepoTags(owner, repo, gitea_sdk.ListRepoTagsOptions{ | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
tags, _, err := client.ListRepoTags(owner, repo, gitea_sdk.ListRepoTagsOptions{ | ||||
ListOptions: gitea_sdk.ListOptions{ | ||||
Page: int(page), | ||||
PageSize: int(pageSize), | ||||
|
@@ -94,7 +94,11 @@ func SearchUsersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR | ||||
PageSize: int(pageSize), | ||||
}, | ||||
} | ||||
users, _, err := gitea.Client().SearchUsers(opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
users, _, err := client.SearchUsers(opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("search users err: %v", err)) | ||||
} | ||||
@@ -128,7 +132,11 @@ func SearchOrgTeamsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo | ||||
PageSize: int(pageSize), | ||||
}, | ||||
} | ||||
teams, _, err := gitea.Client().SearchOrgTeams(org, &opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
teams, _, err := client.SearchOrgTeams(org, &opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("search organization teams error: %v", err)) | ||||
} | ||||
@@ -178,7 +186,11 @@ func SearchReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR | ||||
PageSize: int(pageSize), | ||||
}, | ||||
} | ||||
repos, _, err := gitea.Client().SearchRepos(opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
repos, _, err := client.SearchRepos(opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("search repos error: %v", err)) | ||||
} | ||||
|
@@ -79,7 +79,11 @@ func getIntArg(req mcp.CallToolRequest, name string, def int) int { | ||||
// 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") | ||||
user, _, err := gitea.Client().GetMyUserInfo() | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
user, _, err := client.GetMyUserInfo() | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get user info err: %v", err)) | ||||
} | ||||
@@ -100,7 +104,11 @@ func GetUserOrgsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR | ||||
PageSize: pageSize, | ||||
}, | ||||
} | ||||
orgs, _, err := gitea.Client().ListMyOrgs(opt) | ||||
client, err := gitea.ClientFromContext(ctx) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) | ||||
} | ||||
orgs, _, err := client.ListMyOrgs(opt) | ||||
if err != nil { | ||||
return to.ErrorResult(fmt.Errorf("get user orgs err: %v", err)) | ||||
} | ||||
|
7 pkg/context/context.go Normal file
7
pkg/context/context.go Normal file @@ -0,0 +1,7 @@ | ||||
package context | ||||
| ||||
type contextKey string | ||||
| ||||
const ( | ||||
TokenContextKey = contextKey("token") | ||||
) |
@@ -1,52 +1,47 @@ | ||||
package gitea | ||||
| ||||
import ( | ||||
"context" | ||||
"crypto/tls" | ||||
"fmt" | ||||
"net/http" | ||||
"sync" | ||||
| ||||
"gitea.com/gitea/gitea-mcp/pkg/flag" | ||||
"gitea.com/gitea/gitea-mcp/pkg/log" | ||||
| ||||
"code.gitea.io/sdk/gitea" | ||||
mcpContext "gitea.com/gitea/gitea-mcp/pkg/context" | ||||
"gitea.com/gitea/gitea-mcp/pkg/flag" | ||||
) | ||||
| ||||
var ( | ||||
client *gitea.Client | ||||
clientOnce sync.Once | ||||
) | ||||
func NewClient(token string) (*gitea.Client, error) { | ||||
httpClient := &http.Client{ | ||||
Transport: http.DefaultTransport, | ||||
} | ||||
| ||||
func Client() *gitea.Client { | ||||
clientOnce.Do(func() { | ||||
var err error | ||||
if client != nil { | ||||
return | ||||
opts := []gitea.ClientOption{ | ||||
gitea.SetToken(token), | ||||
} | ||||
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()) | ||||
} | ||||
client, err := gitea.NewClient(flag.Host, opts...) | ||||
if err != nil { | ||||
return nil, fmt.Errorf("create gitea client err: %w", err) | ||||
} | ||||
| ||||
httpClient := &http.Client{ | ||||
Transport: http.DefaultTransport, | ||||
} | ||||
| ||||
opts := []gitea.ClientOption{ | ||||
gitea.SetToken(flag.Token), | ||||
} | ||||
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()) | ||||
} | ||||
client, err = gitea.NewClient(flag.Host, opts...) | ||||
if err != nil { | ||||
log.Fatalf("create gitea client err: %v", err) | ||||
} | ||||
| ||||
// Set user agent for the client | ||||
client.SetUserAgent(fmt.Sprintf("gitea-mcp-server/%s", flag.Version)) | ||||
}) | ||||
return client | ||||
// Set user agent for the client | ||||
client.SetUserAgent(fmt.Sprintf("gitea-mcp-server/%s", flag.Version)) | ||||
return client, nil | ||||
} | ||||
| ||||
func ClientFromContext(ctx context.Context) (*gitea.Client, error) { | ||||
token, ok := ctx.Value(mcpContext.TokenContextKey).(string) | ||||
if !ok { | ||||
token = flag.Token | ||||
} | ||||
return NewClient(token) | ||||
} | ||||
|
Reference in New Issue
Block a user