58d2feb54a
Allows connecting to multiple Gitea instances from a single MCP server.
Set GITEA_SERVERS as a JSON object mapping names to {host, token, insecure}
configs. Every tool gets an optional "server" parameter, and a new
"list_servers" tool is added to discover available connections.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
78 lines
2.2 KiB
Go
78 lines
2.2 KiB
Go
package gitea
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"code.gitea.io/sdk/gitea"
|
|
mcpContext "gitea.com/gitea/gitea-mcp/pkg/context"
|
|
"gitea.com/gitea/gitea-mcp/pkg/flag"
|
|
)
|
|
|
|
func newClientForHost(host, token string, insecure bool) (*gitea.Client, error) {
|
|
httpClient := &http.Client{
|
|
Transport: http.DefaultTransport.(*http.Transport).Clone(),
|
|
CheckRedirect: checkRedirect,
|
|
}
|
|
|
|
opts := []gitea.ClientOption{
|
|
gitea.SetToken(token),
|
|
}
|
|
if 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(host, opts...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("create gitea client err: %w", err)
|
|
}
|
|
|
|
client.SetUserAgent("gitea-mcp-server/" + flag.Version)
|
|
return client, nil
|
|
}
|
|
|
|
// NewClient creates a Gitea client using the default host and the given token.
|
|
func NewClient(token string) (*gitea.Client, error) {
|
|
return newClientForHost(flag.Host, token, flag.Insecure)
|
|
}
|
|
|
|
// checkRedirect prevents Go from silently changing mutating requests (POST, PATCH, etc.)
|
|
// to GET when following 301/302/303 redirects, which would drop the request body and
|
|
// make writes appear to succeed when they didn't.
|
|
func checkRedirect(_ *http.Request, via []*http.Request) error {
|
|
if len(via) >= 10 {
|
|
return errors.New("stopped after 10 redirects")
|
|
}
|
|
if via[0].Method != http.MethodGet && via[0].Method != http.MethodHead {
|
|
return http.ErrUseLastResponse
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ClientFromContext creates a Gitea client using connection details from the
|
|
// context (set by the multi-server wrapper) or falling back to global defaults.
|
|
func ClientFromContext(ctx context.Context) (*gitea.Client, error) {
|
|
host := flag.Host
|
|
token := flag.Token
|
|
insecure := flag.Insecure
|
|
|
|
if h, ok := ctx.Value(mcpContext.HostContextKey).(string); ok && h != "" {
|
|
host = h
|
|
}
|
|
if t, ok := ctx.Value(mcpContext.TokenContextKey).(string); ok && t != "" {
|
|
token = t
|
|
}
|
|
if i, ok := ctx.Value(mcpContext.InsecureContextKey).(bool); ok {
|
|
insecure = i
|
|
}
|
|
return newClientForHost(host, token, insecure)
|
|
}
|