This commit addresses the review feedback on PR #138: - Fixed a privilege escalation vulnerability where failed ephemeral client creation would silently fall back to the global singleton token. - Refactored forgejo.Client(ctx) to return (*forgejo.Client, error). - Updated all 100+ tool handlers to properly handle client initialization errors. - Improved token extraction to handle schemes (token/bearer) case-insensitively. - Enhanced Unit Tests (pkg/forgejo/forgejo_test.go) to verify that different concurrent requests with different tokens correctly use their respective tokens in the Authorization header. - Updated README and demos to reflect case-insensitivity support.
141 lines
4.4 KiB
Go
141 lines
4.4 KiB
Go
//go:build wiki
|
|
|
|
package wiki
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"codeberg.org/goern/forgejo-mcp/v2/operation/params"
|
|
"codeberg.org/goern/forgejo-mcp/v2/pkg/forgejo"
|
|
"codeberg.org/goern/forgejo-mcp/v2/pkg/log"
|
|
"codeberg.org/goern/forgejo-mcp/v2/pkg/to"
|
|
|
|
forgejo_sdk "codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v3"
|
|
"github.com/mark3labs/mcp-go/mcp"
|
|
"github.com/mark3labs/mcp-go/server"
|
|
)
|
|
|
|
const (
|
|
ListWikiPagesToolName = "list_wiki_pages"
|
|
CreateWikiPageToolName = "create_wiki_page"
|
|
UpdateWikiPageToolName = "update_wiki_page"
|
|
)
|
|
|
|
var (
|
|
ListWikiPagesTool = mcp.NewTool(
|
|
ListWikiPagesToolName,
|
|
mcp.WithDescription("List wiki pages"),
|
|
mcp.WithString("owner", mcp.Required(), mcp.Description(params.Owner)),
|
|
mcp.WithString("repo", mcp.Required(), mcp.Description(params.Repo)),
|
|
)
|
|
|
|
CreateWikiPageTool = mcp.NewTool(
|
|
CreateWikiPageToolName,
|
|
mcp.WithDescription("Create wiki page"),
|
|
mcp.WithString("owner", mcp.Required(), mcp.Description(params.Owner)),
|
|
mcp.WithString("repo", mcp.Required(), mcp.Description(params.Repo)),
|
|
mcp.WithString("title", mcp.Required(), mcp.Description(params.WikiTitle)),
|
|
mcp.WithString("content", mcp.Required(), mcp.Description(params.WikiContent)),
|
|
mcp.WithString("message", mcp.Description(params.Message)),
|
|
)
|
|
|
|
UpdateWikiPageTool = mcp.NewTool(
|
|
UpdateWikiPageToolName,
|
|
mcp.WithDescription("Update wiki page"),
|
|
mcp.WithString("owner", mcp.Required(), mcp.Description(params.Owner)),
|
|
mcp.WithString("repo", mcp.Required(), mcp.Description(params.Repo)),
|
|
mcp.WithString("page_name", mcp.Required(), mcp.Description(params.WikiPage)),
|
|
mcp.WithString("title", mcp.Description(params.WikiTitle)),
|
|
mcp.WithString("content", mcp.Required(), mcp.Description(params.WikiContent)),
|
|
mcp.WithString("message", mcp.Description(params.Message)),
|
|
)
|
|
)
|
|
|
|
func RegisterTool(s *server.MCPServer) {
|
|
s.AddTool(ListWikiPagesTool, ListWikiPagesFn)
|
|
s.AddTool(CreateWikiPageTool, CreateWikiPageFn)
|
|
s.AddTool(UpdateWikiPageTool, UpdateWikiPageFn)
|
|
}
|
|
|
|
func ListWikiPagesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called ListWikiPagesFn")
|
|
owner, _ := req.GetArguments()["owner"].(string)
|
|
repo, _ := req.GetArguments()["repo"].(string)
|
|
|
|
client, err := forgejo.Client(ctx)
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
wikiPages, _, err := client.ListWikiPages(owner, repo)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("list wiki pages err: %v", err))
|
|
}
|
|
return to.TextResult(wikiPages)
|
|
}
|
|
|
|
func CreateWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called CreateWikiPageFn")
|
|
owner, _ := req.GetArguments()["owner"].(string)
|
|
repo, _ := req.GetArguments()["repo"].(string)
|
|
title, _ := req.GetArguments()["title"].(string)
|
|
content, _ := req.GetArguments()["content"].(string)
|
|
message, _ := req.GetArguments()["message"].(string)
|
|
|
|
// Use default commit message if not provided
|
|
if message == "" {
|
|
message = fmt.Sprintf("Create wiki page '%s'", title)
|
|
}
|
|
|
|
opt := forgejo_sdk.CreateWikiPageOption{
|
|
Title: title,
|
|
Content: content,
|
|
Message: message,
|
|
}
|
|
|
|
client, err := forgejo.Client(ctx)
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
wikiPage, _, err := client.CreateWikiPage(owner, repo, opt)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("create wiki page err: %v", err))
|
|
}
|
|
return to.TextResult(wikiPage)
|
|
}
|
|
|
|
func UpdateWikiPageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called UpdateWikiPageFn")
|
|
owner, _ := req.GetArguments()["owner"].(string)
|
|
repo, _ := req.GetArguments()["repo"].(string)
|
|
pageName, _ := req.GetArguments()["page_name"].(string)
|
|
title, titleProvided := req.GetArguments()["title"].(string)
|
|
content, _ := req.GetArguments()["content"].(string)
|
|
message, _ := req.GetArguments()["message"].(string)
|
|
|
|
// If title is not provided, use the current page name
|
|
if !titleProvided || title == "" {
|
|
title = pageName
|
|
}
|
|
|
|
// Use default commit message if not provided
|
|
if message == "" {
|
|
message = fmt.Sprintf("Update wiki page '%s'", pageName)
|
|
}
|
|
|
|
opt := forgejo_sdk.EditWikiPageOption{
|
|
Title: title,
|
|
Content: content,
|
|
Message: message,
|
|
}
|
|
|
|
client, err := forgejo.Client(ctx)
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
wikiPage, _, err := client.EditWikiPage(owner, repo, pageName, opt)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("update wiki page err: %v", err))
|
|
}
|
|
return to.TextResult(wikiPage)
|
|
}
|