forgejo-mcp/operation/repo/resources_repo.go
Christoph Görn 0c177af796
fix: 🐛 map parse errors via MapForgejoError in repo resource
repoResourceHandler wrapped parse errors with fmt.Errorf, losing
the *resource.ResourceError type. Align with the pattern used by
sibling resource handlers (commit, issue, pr).

Note: unrecognized parse-error strings still surface as -32603
internal-error until forgejo-mcp-wgs lifts them to -32602.

Closes forgejo-mcp-s2g.
Ref PR #172 review https://codeberg.org/goern/forgejo-mcp/pulls/172#issuecomment-16022165
2026-05-28 16:01:42 +02:00

115 lines
3.3 KiB
Go

package repo
import (
"context"
"encoding/json"
"fmt"
"codeberg.org/goern/forgejo-mcp/v2/operation/resource"
"codeberg.org/goern/forgejo-mcp/v2/pkg/forgejo"
"codeberg.org/goern/forgejo-mcp/v2/pkg/log"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
const repoResourceURITemplate = "forgejo://repo/{owner}/{repo}"
// repoResourcePayload is the counts-only repository summary returned as JSON.
type repoResourcePayload struct {
Owner string `json:"owner"`
Name string `json:"name"`
FullName string `json:"full_name"`
Description string `json:"description,omitempty"`
HTMLURL string `json:"html_url"`
DefaultBranch string `json:"default_branch"`
Fork bool `json:"fork"`
Archived bool `json:"archived"`
Private bool `json:"private"`
StarsCount int `json:"stars_count"`
ForksCount int `json:"forks_count"`
WatchersCount int `json:"watchers_count"`
OpenIssuesCount int `json:"open_issues_count"`
OpenPRCount int `json:"open_pr_count"`
Size int `json:"size"`
HasIssues bool `json:"has_issues"`
HasWiki bool `json:"has_wiki"`
HasPullRequests bool `json:"has_pull_requests"`
}
// RegisterRepoResource registers the forgejo://repo/{owner}/{repo} resource template.
func RegisterRepoResource(s *server.MCPServer) {
resource.RegisterTemplate(
s,
repoResourceURITemplate,
"Forgejo Repository",
repoResourceHandler,
mcp.WithTemplateDescription(
"Repository overview by name. Returns immutable identity (owner, name) plus "+
"mutable counts (stars, forks, open issues, open PRs). No embedded lists. "+
"URI: forgejo://repo/{owner}/{repo}.",
),
mcp.WithTemplateMIMEType("application/json"),
)
log.Debug("Registered repo resource template")
}
func repoResourceHandler(ctx context.Context, req mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
uri := req.Params.URI
params, err := resource.ParseRepo(uri)
if err != nil {
return nil, resource.MapForgejoError(uri, err)
}
client, err := forgejo.Client(ctx)
if err != nil {
return nil, fmt.Errorf("forgejo client: %w", err)
}
r, resp, err := client.GetRepo(params.Owner, params.Repo)
if err != nil {
if resp != nil {
return nil, resource.MapForgejoError(uri, fmt.Errorf("%d %s", resp.StatusCode, err.Error()))
}
return nil, resource.MapForgejoError(uri, err)
}
ownerLogin := params.Owner
if r.Owner != nil {
ownerLogin = r.Owner.UserName
}
payload := repoResourcePayload{
Owner: ownerLogin,
Name: r.Name,
FullName: r.FullName,
Description: r.Description,
HTMLURL: r.HTMLURL,
DefaultBranch: r.DefaultBranch,
Fork: r.Fork,
Archived: r.Archived,
Private: r.Private,
StarsCount: r.Stars,
ForksCount: r.Forks,
WatchersCount: r.Watchers,
OpenIssuesCount: r.OpenIssues,
OpenPRCount: r.OpenPulls,
Size: r.Size,
HasIssues: r.HasIssues,
HasWiki: r.HasWiki,
HasPullRequests: r.HasPullRequests,
}
jsonBytes, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("marshal repo payload: %w", err)
}
return []mcp.ResourceContents{
mcp.TextResourceContents{
URI: uri,
MIMEType: "application/json",
Text: string(jsonBytes),
},
}, nil
}